2016-01-27 11:41:24 -08:00
|
|
|
// Licensed to the .NET Foundation under one or more agreements.
|
|
|
|
|
// The .NET Foundation licenses this file to you under the MIT license.
|
2016-01-13 16:38:34 -08:00
|
|
|
|
|
|
|
|
#include "common.h"
|
|
|
|
|
|
|
|
|
|
#include "disassembler.h"
|
|
|
|
|
#include "dllimport.h"
|
|
|
|
|
|
|
|
|
|
#if USE_DISASSEMBLER
|
|
|
|
|
|
|
|
|
|
// TODO: Which contracts should be used where? Currently, everything is using LIMITED_METHOD_CONTRACT.
|
|
|
|
|
|
|
|
|
|
#if USE_COREDISTOOLS_DISASSEMBLER
|
|
|
|
|
HMODULE Disassembler::s_libraryHandle = nullptr;
|
2016-03-22 17:17:58 -07:00
|
|
|
InitDisasm_t *Disassembler::External_InitDisasm = nullptr;
|
|
|
|
|
FinishDisasm_t *Disassembler::External_FinishDisasm = nullptr;
|
|
|
|
|
DisasmInstruction_t *Disassembler::External_DisasmInstruction = nullptr;
|
2016-01-13 16:38:34 -08:00
|
|
|
#endif // USE_COREDISTOOLS_DISASSEMBLER
|
|
|
|
|
|
|
|
|
|
Disassembler::ExternalDisassembler *Disassembler::s_availableExternalDisassembler = nullptr;
|
|
|
|
|
|
2020-01-31 19:36:44 -05:00
|
|
|
#if defined(TARGET_AMD64) || defined(TARGET_X86)
|
2016-01-13 16:38:34 -08:00
|
|
|
// static
|
|
|
|
|
bool Disassembler::IsRexPrefix(UINT8 potentialRexByte)
|
|
|
|
|
{
|
|
|
|
|
LIMITED_METHOD_CONTRACT;
|
|
|
|
|
|
2020-01-31 19:36:44 -05:00
|
|
|
#ifdef TARGET_AMD64
|
2016-01-13 16:38:34 -08:00
|
|
|
return (potentialRexByte & 0xf0) == REX_PREFIX_BASE;
|
2020-01-31 19:36:44 -05:00
|
|
|
#else // !TARGET_AMD64
|
2016-01-13 16:38:34 -08:00
|
|
|
return false;
|
2020-01-31 19:36:44 -05:00
|
|
|
#endif // TARGET_AMD64
|
2016-01-13 16:38:34 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
UINT8 Disassembler::DecodeModFromModRm(UINT8 modRm)
|
|
|
|
|
{
|
|
|
|
|
LIMITED_METHOD_CONTRACT;
|
|
|
|
|
return modRm >> 6;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
UINT8 Disassembler::DecodeRegOrOpCodeFromModRm(UINT8 modRm)
|
|
|
|
|
{
|
|
|
|
|
LIMITED_METHOD_CONTRACT;
|
|
|
|
|
return (modRm >> 3) & 0x7;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
UINT8 Disassembler::DecodeRmFromModRm(UINT8 modRm)
|
|
|
|
|
{
|
|
|
|
|
LIMITED_METHOD_CONTRACT;
|
|
|
|
|
return modRm & 0x7;
|
|
|
|
|
}
|
2020-01-31 19:36:44 -05:00
|
|
|
#endif // defined(TARGET_AMD64) || defined(TARGET_X86)
|
2016-01-13 16:38:34 -08:00
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
bool Disassembler::IsAvailable()
|
|
|
|
|
{
|
|
|
|
|
LIMITED_METHOD_CONTRACT;
|
|
|
|
|
|
|
|
|
|
#if USE_COREDISTOOLS_DISASSEMBLER
|
|
|
|
|
return s_libraryHandle != nullptr;
|
|
|
|
|
#else // !USE_COREDISTOOLS_DISASSEMBLER
|
|
|
|
|
return true;
|
|
|
|
|
#endif // USE_COREDISTOOLS_DISASSEMBLER
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-30 10:06:28 -08:00
|
|
|
#if _DEBUG
|
2022-05-16 09:53:31 -07:00
|
|
|
#define DISPLAYERROR(FMT, ...) printf(FMT, __VA_ARGS__)
|
2018-11-30 10:06:28 -08:00
|
|
|
#else
|
|
|
|
|
#define DISPLAYERROR(FMT, ...) (void)0
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
namespace
|
|
|
|
|
{
|
|
|
|
|
HMODULE LoadCoreDisToolsModule(PathString &libPath)
|
|
|
|
|
{
|
|
|
|
|
LIMITED_METHOD_CONTRACT;
|
|
|
|
|
|
2019-01-14 18:41:00 -08:00
|
|
|
LPCWSTR sysDirectory = GetInternalSystemDirectory();
|
2018-11-30 10:06:28 -08:00
|
|
|
LPCWSTR libFileName = MAKEDLLNAME(W("coredistools"));
|
2019-01-14 18:41:00 -08:00
|
|
|
|
|
|
|
|
// Look for the coredistools module next to the clr binary
|
2022-05-31 15:59:00 -07:00
|
|
|
libPath.Append(sysDirectory);
|
|
|
|
|
libPath.Append(libFileName);
|
2018-11-30 10:06:28 -08:00
|
|
|
|
|
|
|
|
LPCWSTR libraryName = libPath.GetUnicode();
|
2019-01-14 18:41:00 -08:00
|
|
|
return CLRLoadLibrary(libraryName);
|
2018-11-30 10:06:28 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-13 16:38:34 -08:00
|
|
|
void Disassembler::StaticInitialize()
|
|
|
|
|
{
|
|
|
|
|
LIMITED_METHOD_CONTRACT;
|
|
|
|
|
|
|
|
|
|
#if USE_COREDISTOOLS_DISASSEMBLER
|
2016-01-25 15:24:47 -08:00
|
|
|
_ASSERTE(!IsAvailable());
|
|
|
|
|
|
2016-02-20 12:44:13 -08:00
|
|
|
PathString libPath;
|
2018-11-30 10:06:28 -08:00
|
|
|
HMODULE libraryHandle = LoadCoreDisToolsModule(libPath);
|
|
|
|
|
if (libraryHandle == nullptr)
|
2016-02-20 12:44:13 -08:00
|
|
|
return;
|
|
|
|
|
|
2018-11-30 10:06:28 -08:00
|
|
|
External_InitDisasm =
|
|
|
|
|
reinterpret_cast<decltype(External_InitDisasm)>(GetProcAddress(libraryHandle, "InitDisasm"));
|
|
|
|
|
if (External_InitDisasm == nullptr)
|
2016-01-13 16:38:34 -08:00
|
|
|
{
|
2022-05-16 09:53:31 -07:00
|
|
|
DISPLAYERROR("GetProcAddress failed for coredistools function 'InitDisasm': error %u\n", GetLastError());
|
2016-01-13 16:38:34 -08:00
|
|
|
return;
|
2018-11-30 10:06:28 -08:00
|
|
|
}
|
2016-01-13 16:38:34 -08:00
|
|
|
|
2018-11-30 10:06:28 -08:00
|
|
|
External_DisasmInstruction =
|
|
|
|
|
reinterpret_cast<decltype(External_DisasmInstruction)>(GetProcAddress(libraryHandle, "DisasmInstruction"));
|
|
|
|
|
if (External_DisasmInstruction == nullptr)
|
|
|
|
|
{
|
2022-05-16 09:53:31 -07:00
|
|
|
DISPLAYERROR("GetProcAddress failed for coredistools function 'DisasmInstruction': error %u\n", GetLastError());
|
2018-11-30 10:06:28 -08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
External_FinishDisasm =
|
|
|
|
|
reinterpret_cast<decltype(External_FinishDisasm)>(GetProcAddress(libraryHandle, "FinishDisasm"));
|
|
|
|
|
if (External_FinishDisasm == nullptr)
|
|
|
|
|
{
|
2022-05-16 09:53:31 -07:00
|
|
|
DISPLAYERROR("GetProcAddress failed for coredistools function 'FinishDisasm': error %u\n", GetLastError());
|
2018-11-30 10:06:28 -08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set this last to indicate successful load of the library and all exports
|
|
|
|
|
s_libraryHandle = libraryHandle;
|
|
|
|
|
_ASSERTE(IsAvailable());
|
2016-01-13 16:38:34 -08:00
|
|
|
#endif // USE_COREDISTOOLS_DISASSEMBLER
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
void Disassembler::StaticClose()
|
|
|
|
|
{
|
|
|
|
|
LIMITED_METHOD_CONTRACT;
|
|
|
|
|
|
|
|
|
|
if (!IsAvailable())
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if USE_COREDISTOOLS_DISASSEMBLER
|
|
|
|
|
CLRFreeLibrary(s_libraryHandle);
|
|
|
|
|
s_libraryHandle = nullptr;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Disassembler::Disassembler()
|
|
|
|
|
{
|
|
|
|
|
LIMITED_METHOD_CONTRACT;
|
|
|
|
|
_ASSERTE(IsAvailable());
|
|
|
|
|
|
|
|
|
|
// TODO: Is it ok to save and reuse an instance of the LLVM-based disassembler? It may later be used from a different
|
|
|
|
|
// thread, and it may be deleted from a different thread than the one from which it was created.
|
|
|
|
|
|
|
|
|
|
// Try to get an external disassembler that is already available for use before creating one
|
|
|
|
|
ExternalDisassembler *externalDisassembler =
|
2022-06-15 16:22:25 -07:00
|
|
|
InterlockedExchangeT(&s_availableExternalDisassembler, static_cast<ExternalDisassembler *>(nullptr));
|
2016-01-13 16:38:34 -08:00
|
|
|
if (externalDisassembler == nullptr)
|
|
|
|
|
{
|
|
|
|
|
#if USE_COREDISTOOLS_DISASSEMBLER
|
|
|
|
|
// First parameter:
|
|
|
|
|
// - Empty string for the current architecture
|
|
|
|
|
// - A string of the form "x86_64-pc-win32"
|
|
|
|
|
externalDisassembler = External_InitDisasm(Target_Host);
|
|
|
|
|
#elif USE_MSVC_DISASSEMBLER
|
2020-01-31 19:36:44 -05:00
|
|
|
#ifdef TARGET_X86
|
2016-01-13 16:38:34 -08:00
|
|
|
externalDisassembler = ExternalDisassembler::PdisNew(ExternalDisassembler::distX86);
|
2020-01-31 19:36:44 -05:00
|
|
|
#elif defined(TARGET_AMD64)
|
2016-01-13 16:38:34 -08:00
|
|
|
externalDisassembler = ExternalDisassembler::PdisNew(ExternalDisassembler::distX8664);
|
2020-01-31 19:36:44 -05:00
|
|
|
#endif // defined(TARGET_X86) || defined(TARGET_AMD64)
|
2016-01-13 16:38:34 -08:00
|
|
|
#endif // USE_COREDISTOOLS_DISASSEMBLER || USE_MSVC_DISASSEMBLER
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_ASSERTE(externalDisassembler != nullptr);
|
|
|
|
|
m_externalDisassembler = externalDisassembler;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Disassembler::~Disassembler()
|
|
|
|
|
{
|
|
|
|
|
LIMITED_METHOD_CONTRACT;
|
|
|
|
|
_ASSERTE(IsAvailable());
|
|
|
|
|
|
|
|
|
|
// Save the external disassembler for future use. We only save one instance, so delete a previously saved one.
|
|
|
|
|
ExternalDisassembler *externalDisassemblerToDelete =
|
2022-06-15 16:22:25 -07:00
|
|
|
InterlockedExchangeT(&s_availableExternalDisassembler, m_externalDisassembler);
|
2016-01-13 16:38:34 -08:00
|
|
|
if (externalDisassemblerToDelete == nullptr)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if USE_COREDISTOOLS_DISASSEMBLER
|
|
|
|
|
External_FinishDisasm(externalDisassemblerToDelete);
|
|
|
|
|
#elif USE_MSVC_DISASSEMBLER
|
|
|
|
|
delete externalDisassemblerToDelete;
|
|
|
|
|
#endif // USE_COREDISTOOLS_DISASSEMBLER || USE_MSVC_DISASSEMBLER
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SIZE_T Disassembler::DisassembleInstruction(const UINT8 *code, SIZE_T codeLength, InstructionType *instructionTypeRef) const
|
|
|
|
|
{
|
|
|
|
|
LIMITED_METHOD_CONTRACT;
|
|
|
|
|
_ASSERTE(IsAvailable());
|
|
|
|
|
|
|
|
|
|
#if USE_COREDISTOOLS_DISASSEMBLER
|
2016-03-22 17:17:58 -07:00
|
|
|
SIZE_T instructionLength = External_DisasmInstruction(m_externalDisassembler, code, code, codeLength);
|
2016-01-13 16:38:34 -08:00
|
|
|
#elif USE_MSVC_DISASSEMBLER
|
|
|
|
|
SIZE_T instructionLength =
|
|
|
|
|
m_externalDisassembler->CbDisassemble(reinterpret_cast<ExternalDisassembler::ADDR>(code), code, codeLength);
|
|
|
|
|
#endif // USE_COREDISTOOLS_DISASSEMBLER || USE_MSVC_DISASSEMBLER
|
|
|
|
|
_ASSERTE(instructionLength <= codeLength);
|
|
|
|
|
|
|
|
|
|
if (instructionTypeRef != nullptr)
|
|
|
|
|
{
|
|
|
|
|
if (instructionLength == 0)
|
|
|
|
|
{
|
|
|
|
|
*instructionTypeRef = InstructionType::Unknown;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
#if USE_COREDISTOOLS_DISASSEMBLER
|
|
|
|
|
*instructionTypeRef = DetermineInstructionType(code, instructionLength);
|
|
|
|
|
#elif USE_MSVC_DISASSEMBLER
|
|
|
|
|
*instructionTypeRef = DetermineInstructionType(m_externalDisassembler->Trmt());
|
|
|
|
|
#endif // USE_COREDISTOOLS_DISASSEMBLER || USE_MSVC_DISASSEMBLER
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return instructionLength;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// static
|
|
|
|
|
InstructionType Disassembler::DetermineInstructionType(
|
|
|
|
|
#if USE_COREDISTOOLS_DISASSEMBLER
|
|
|
|
|
const UINT8 *instructionCode, SIZE_T instructionCodeLength
|
|
|
|
|
#elif USE_MSVC_DISASSEMBLER
|
|
|
|
|
ExternalDisassembler::TRMT terminationType
|
|
|
|
|
#endif // USE_COREDISTOOLS_DISASSEMBLER || USE_MSVC_DISASSEMBLER
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
LIMITED_METHOD_CONTRACT;
|
|
|
|
|
|
|
|
|
|
#if USE_COREDISTOOLS_DISASSEMBLER
|
|
|
|
|
_ASSERTE(instructionCodeLength != 0);
|
|
|
|
|
|
|
|
|
|
SIZE_T i = 0;
|
|
|
|
|
if (Disassembler::IsRexPrefix(instructionCode[i]))
|
|
|
|
|
{
|
|
|
|
|
++i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (instructionCode[i])
|
|
|
|
|
{
|
|
|
|
|
case 0xe8: // call near rel
|
2020-01-31 19:36:44 -05:00
|
|
|
#ifdef TARGET_X86
|
2016-01-13 16:38:34 -08:00
|
|
|
case 0x9a: // call far ptr
|
2020-01-31 19:36:44 -05:00
|
|
|
#endif // TARGET_X86
|
2016-01-13 16:38:34 -08:00
|
|
|
return InstructionType::Call_DirectUnconditional;
|
|
|
|
|
|
|
|
|
|
case 0xff:
|
|
|
|
|
++i;
|
|
|
|
|
if (i >= instructionCodeLength)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (Disassembler::DecodeRegOrOpCodeFromModRm(instructionCode[i]))
|
|
|
|
|
{
|
|
|
|
|
case 2: // call near r/m
|
|
|
|
|
case 3: // call far m
|
|
|
|
|
return InstructionType::Call_IndirectUnconditional;
|
|
|
|
|
|
|
|
|
|
case 4: // jmp near r/m
|
|
|
|
|
case 5: // jmp far m
|
|
|
|
|
return InstructionType::Branch_IndirectUnconditional;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
#elif USE_MSVC_DISASSEMBLER
|
|
|
|
|
switch (terminationType)
|
|
|
|
|
{
|
|
|
|
|
case ExternalDisassembler::trmtCall:
|
|
|
|
|
return InstructionType::Call_DirectUnconditional;
|
|
|
|
|
|
|
|
|
|
case ExternalDisassembler::trmtCallInd:
|
|
|
|
|
return InstructionType::Call_IndirectUnconditional;
|
|
|
|
|
|
|
|
|
|
case ExternalDisassembler::trmtBraInd:
|
|
|
|
|
return InstructionType::Branch_IndirectUnconditional;
|
|
|
|
|
}
|
|
|
|
|
#endif // USE_COREDISTOOLS_DISASSEMBLER || USE_MSVC_DISASSEMBLER
|
|
|
|
|
|
|
|
|
|
return InstructionType::Unknown;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif // USE_DISASSEMBLER
|