mirror of https://github.com/x64dbg/zydis
Improved formatter performance
Added symbol resolver example
This commit is contained in:
parent
9c52df0fe4
commit
11cb62c1ed
|
@ -40,10 +40,17 @@ using namespace Disassembler;
|
||||||
|
|
||||||
void testDecodingAndFormatting(uintptr_t baseAddress, PIMAGE_NT_HEADERS ntHeaders)
|
void testDecodingAndFormatting(uintptr_t baseAddress, PIMAGE_NT_HEADERS ntHeaders)
|
||||||
{
|
{
|
||||||
|
uint32_t sizeTotal = 0;
|
||||||
VXInstructionInfo info;
|
VXInstructionInfo info;
|
||||||
VXInstructionDecoder decoder;
|
VXInstructionDecoder decoder;
|
||||||
VXIntelInstructionFormatter formatter;
|
VXIntelInstructionFormatter formatter;
|
||||||
|
#ifdef _M_X64
|
||||||
|
decoder.setDisassemblerMode(VXDisassemblerMode::M32BIT);
|
||||||
|
#else
|
||||||
decoder.setDisassemblerMode(VXDisassemblerMode::M64BIT);
|
decoder.setDisassemblerMode(VXDisassemblerMode::M64BIT);
|
||||||
|
#endif
|
||||||
|
while (sizeTotal < 1024 * 1024 * 50)
|
||||||
|
{
|
||||||
PIMAGE_SECTION_HEADER sectionHeader =
|
PIMAGE_SECTION_HEADER sectionHeader =
|
||||||
reinterpret_cast<PIMAGE_SECTION_HEADER>(
|
reinterpret_cast<PIMAGE_SECTION_HEADER>(
|
||||||
reinterpret_cast<uintptr_t>(ntHeaders) + sizeof(IMAGE_NT_HEADERS)
|
reinterpret_cast<uintptr_t>(ntHeaders) + sizeof(IMAGE_NT_HEADERS)
|
||||||
|
@ -53,7 +60,6 @@ void testDecodingAndFormatting(uintptr_t baseAddress, PIMAGE_NT_HEADERS ntHeader
|
||||||
{
|
{
|
||||||
if (sectionHeader->Characteristics & IMAGE_SCN_CNT_CODE)
|
if (sectionHeader->Characteristics & IMAGE_SCN_CNT_CODE)
|
||||||
{
|
{
|
||||||
std::cout << sectionHeader->SizeOfRawData / 1024 << " KiB" << std::endl;
|
|
||||||
VXMemoryDataSource input(reinterpret_cast<const void*>(
|
VXMemoryDataSource input(reinterpret_cast<const void*>(
|
||||||
baseAddress + sectionHeader->VirtualAddress), sectionHeader->SizeOfRawData);
|
baseAddress + sectionHeader->VirtualAddress), sectionHeader->SizeOfRawData);
|
||||||
decoder.setDataSource(&input);
|
decoder.setDataSource(&input);
|
||||||
|
@ -62,10 +68,12 @@ void testDecodingAndFormatting(uintptr_t baseAddress, PIMAGE_NT_HEADERS ntHeader
|
||||||
{
|
{
|
||||||
formatter.formatInstruction(info);
|
formatter.formatInstruction(info);
|
||||||
}
|
}
|
||||||
|
sizeTotal += sectionHeader->SizeOfRawData;
|
||||||
}
|
}
|
||||||
sectionHeader++;
|
sectionHeader++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int _tmain(int argc, _TCHAR* argv[])
|
int _tmain(int argc, _TCHAR* argv[])
|
||||||
{
|
{
|
||||||
|
@ -87,8 +95,8 @@ int _tmain(int argc, _TCHAR* argv[])
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
double pcFrequency = 0.0;
|
double pcFrequency;
|
||||||
uint64_t pcStart = 0;
|
uint64_t pcStart;
|
||||||
LARGE_INTEGER li;
|
LARGE_INTEGER li;
|
||||||
|
|
||||||
// Start the performance counter
|
// Start the performance counter
|
||||||
|
|
|
@ -30,9 +30,160 @@
|
||||||
|
|
||||||
**************************************************************************************************/
|
**************************************************************************************************/
|
||||||
#include <tchar.h>
|
#include <tchar.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <string>
|
||||||
|
#include "VXDisassembler.h"
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
using namespace Verteron;
|
||||||
|
using namespace Disassembler;
|
||||||
|
|
||||||
int _tmain(int argc, _TCHAR* argv[])
|
int _tmain(int argc, _TCHAR* argv[])
|
||||||
{
|
{
|
||||||
// TODO:
|
// Find module base in memory
|
||||||
|
void *moduleBase = GetModuleHandle(L"kernel32.dll");
|
||||||
|
uintptr_t baseAddress = reinterpret_cast<uintptr_t>(moduleBase);
|
||||||
|
// Parse PE headers
|
||||||
|
PIMAGE_DOS_HEADER dosHeader = static_cast<PIMAGE_DOS_HEADER>(moduleBase);
|
||||||
|
if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
PIMAGE_NT_HEADERS ntHeaders =
|
||||||
|
reinterpret_cast<PIMAGE_NT_HEADERS>(baseAddress + dosHeader->e_lfanew);
|
||||||
|
if (ntHeaders->Signature != IMAGE_NT_SIGNATURE)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
// Initialize disassembler
|
||||||
|
VXInstructionInfo info;
|
||||||
|
VXInstructionDecoder decoder;
|
||||||
|
VXExactSymbolResolver resolver;
|
||||||
|
VXIntelInstructionFormatter formatter;
|
||||||
|
decoder.setDisassemblerMode(VXDisassemblerMode::M64BIT);
|
||||||
|
formatter.setSymbolResolver(&resolver);
|
||||||
|
// Initialize output stream
|
||||||
|
std::ofstream out;
|
||||||
|
out.open(".\\output.txt");
|
||||||
|
// Find all call and jump targets
|
||||||
|
uint64_t subCount = 0;
|
||||||
|
uint64_t locCount = 0;
|
||||||
|
PIMAGE_SECTION_HEADER sectionHeader =
|
||||||
|
reinterpret_cast<PIMAGE_SECTION_HEADER>(
|
||||||
|
reinterpret_cast<uintptr_t>(ntHeaders) + sizeof(IMAGE_NT_HEADERS)
|
||||||
|
+ ntHeaders->FileHeader.SizeOfOptionalHeader - sizeof(IMAGE_OPTIONAL_HEADER));
|
||||||
|
for (unsigned int i = 0; i < ntHeaders->FileHeader.NumberOfSections; ++i)
|
||||||
|
{
|
||||||
|
if (sectionHeader->Characteristics & IMAGE_SCN_CNT_CODE)
|
||||||
|
{
|
||||||
|
VXMemoryDataSource input(reinterpret_cast<const void*>(
|
||||||
|
baseAddress + sectionHeader->VirtualAddress), sectionHeader->SizeOfRawData);
|
||||||
|
decoder.setDataSource(&input);
|
||||||
|
decoder.setInstructionPointer(baseAddress + sectionHeader->VirtualAddress);
|
||||||
|
while (decoder.decodeInstruction(info))
|
||||||
|
{
|
||||||
|
// Skip invalid instructions and non-relative instructions
|
||||||
|
if ((info.flags & IF_ERROR_MASK) || !(info.flags & IF_RELATIVE))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
switch (info.mnemonic)
|
||||||
|
{
|
||||||
|
case VXInstructionMnemonic::CALL:
|
||||||
|
resolver.setSymbol(VDECalcAbsoluteTarget(info, info.operand[0]),
|
||||||
|
std::string("sub_" + std::to_string(subCount)).c_str());
|
||||||
|
subCount++;
|
||||||
|
break;
|
||||||
|
case VXInstructionMnemonic::JMP:
|
||||||
|
case VXInstructionMnemonic::JO:
|
||||||
|
case VXInstructionMnemonic::JNO:
|
||||||
|
case VXInstructionMnemonic::JB:
|
||||||
|
case VXInstructionMnemonic::JNB:
|
||||||
|
case VXInstructionMnemonic::JE:
|
||||||
|
case VXInstructionMnemonic::JNE:
|
||||||
|
case VXInstructionMnemonic::JBE:
|
||||||
|
case VXInstructionMnemonic::JA:
|
||||||
|
case VXInstructionMnemonic::JS:
|
||||||
|
case VXInstructionMnemonic::JNS:
|
||||||
|
case VXInstructionMnemonic::JP:
|
||||||
|
case VXInstructionMnemonic::JNP:
|
||||||
|
case VXInstructionMnemonic::JL:
|
||||||
|
case VXInstructionMnemonic::JGE:
|
||||||
|
case VXInstructionMnemonic::JLE:
|
||||||
|
case VXInstructionMnemonic::JG:
|
||||||
|
case VXInstructionMnemonic::JCXZ:
|
||||||
|
case VXInstructionMnemonic::JECXZ:
|
||||||
|
case VXInstructionMnemonic::JRCXZ:
|
||||||
|
resolver.setSymbol(VDECalcAbsoluteTarget(info, info.operand[0]),
|
||||||
|
std::string("loc_" + std::to_string(locCount)).c_str());
|
||||||
|
locCount++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sectionHeader++;
|
||||||
|
}
|
||||||
|
// Add entry point symbol
|
||||||
|
resolver.setSymbol(baseAddress + ntHeaders->OptionalHeader.AddressOfEntryPoint, "EntryPoint");
|
||||||
|
// Add exported symbols
|
||||||
|
if (ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress > 0)
|
||||||
|
{
|
||||||
|
PIMAGE_EXPORT_DIRECTORY exports =
|
||||||
|
reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(reinterpret_cast<LPBYTE>(baseAddress) +
|
||||||
|
ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
|
||||||
|
PDWORD address =
|
||||||
|
reinterpret_cast<PDWORD>(reinterpret_cast<LPBYTE>(baseAddress) +
|
||||||
|
exports->AddressOfFunctions);
|
||||||
|
PDWORD name =
|
||||||
|
reinterpret_cast<PDWORD>(reinterpret_cast<LPBYTE>(baseAddress) +
|
||||||
|
exports->AddressOfNames);
|
||||||
|
PWORD ordinal =
|
||||||
|
reinterpret_cast<PWORD>(reinterpret_cast<LPBYTE>(baseAddress) +
|
||||||
|
exports->AddressOfNameOrdinals);
|
||||||
|
for(unsigned int i = 0; i < exports->NumberOfNames; ++i)
|
||||||
|
{
|
||||||
|
resolver.setSymbol(baseAddress + address[ordinal[i]],
|
||||||
|
reinterpret_cast<char*>(baseAddress) + name[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Disassemble
|
||||||
|
sectionHeader =
|
||||||
|
reinterpret_cast<PIMAGE_SECTION_HEADER>(
|
||||||
|
reinterpret_cast<uintptr_t>(ntHeaders) + sizeof(IMAGE_NT_HEADERS)
|
||||||
|
+ ntHeaders->FileHeader.SizeOfOptionalHeader - sizeof(IMAGE_OPTIONAL_HEADER));
|
||||||
|
for (unsigned int i = 0; i < ntHeaders->FileHeader.NumberOfSections; ++i)
|
||||||
|
{
|
||||||
|
if (sectionHeader->Characteristics & IMAGE_SCN_CNT_CODE)
|
||||||
|
{
|
||||||
|
VXMemoryDataSource input(reinterpret_cast<const void*>(
|
||||||
|
baseAddress + sectionHeader->VirtualAddress), sectionHeader->SizeOfRawData);
|
||||||
|
decoder.setDataSource(&input);
|
||||||
|
decoder.setInstructionPointer(baseAddress + sectionHeader->VirtualAddress);
|
||||||
|
while (decoder.decodeInstruction(info))
|
||||||
|
{
|
||||||
|
uint64_t offset;
|
||||||
|
const char *symbol = resolver.resolveSymbol(info, info.instrAddress, offset);
|
||||||
|
if (symbol)
|
||||||
|
{
|
||||||
|
out << symbol << ": " << std::endl;
|
||||||
|
}
|
||||||
|
out << " " << std::hex << std::setw(16) << std::setfill('0')
|
||||||
|
<< info.instrAddress << " ";
|
||||||
|
if (info.flags & IF_ERROR_MASK)
|
||||||
|
{
|
||||||
|
out << "db " << std::hex << std::setw(2) << std::setfill('0')
|
||||||
|
<< static_cast<int>(info.data[0]) << std::endl;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
out << formatter.formatInstruction(info) << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sectionHeader++;
|
||||||
|
}
|
||||||
|
out.close();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,13 +96,14 @@ const char* VXBaseInstructionFormatter::m_registerStrings[] =
|
||||||
"rip"
|
"rip"
|
||||||
};
|
};
|
||||||
|
|
||||||
void VXBaseInstructionFormatter::internalFormatInstruction(VXInstructionInfo const& info)
|
void VXBaseInstructionFormatter::internalFormatInstruction(const VXInstructionInfo &info)
|
||||||
{
|
{
|
||||||
// Nothing to do here
|
// Nothing to do here
|
||||||
}
|
}
|
||||||
|
|
||||||
VXBaseInstructionFormatter::VXBaseInstructionFormatter()
|
VXBaseInstructionFormatter::VXBaseInstructionFormatter()
|
||||||
: m_symbolResolver(nullptr)
|
: m_symbolResolver(nullptr)
|
||||||
|
, m_outputStringLen(0)
|
||||||
, m_uppercase(false)
|
, m_uppercase(false)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -110,6 +111,7 @@ VXBaseInstructionFormatter::VXBaseInstructionFormatter()
|
||||||
|
|
||||||
VXBaseInstructionFormatter::VXBaseInstructionFormatter(VXBaseSymbolResolver *symbolResolver)
|
VXBaseInstructionFormatter::VXBaseInstructionFormatter(VXBaseSymbolResolver *symbolResolver)
|
||||||
: m_symbolResolver(symbolResolver)
|
: m_symbolResolver(symbolResolver)
|
||||||
|
, m_outputStringLen(0)
|
||||||
, m_uppercase(false)
|
, m_uppercase(false)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -137,7 +139,7 @@ VXBaseInstructionFormatter::~VXBaseInstructionFormatter()
|
||||||
|
|
||||||
void VXBaseInstructionFormatter::outputClear()
|
void VXBaseInstructionFormatter::outputClear()
|
||||||
{
|
{
|
||||||
m_outputBuffer.clear();
|
m_outputStringLen = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char const* VXBaseInstructionFormatter::outputString()
|
char const* VXBaseInstructionFormatter::outputString()
|
||||||
|
@ -149,25 +151,25 @@ void VXBaseInstructionFormatter::outputAppend(char const *text)
|
||||||
{
|
{
|
||||||
// Get the string length including the null-terminator char
|
// Get the string length including the null-terminator char
|
||||||
size_t strLen = strlen(text) + 1;
|
size_t strLen = strlen(text) + 1;
|
||||||
// Get the buffer capacity and size
|
// Get the buffer size
|
||||||
size_t bufCap = m_outputBuffer.capacity();
|
|
||||||
size_t bufLen = m_outputBuffer.size();
|
size_t bufLen = m_outputBuffer.size();
|
||||||
// Decrease the offset by one, to exclude already existing null-terminator chars in the
|
// Decrease the offset by one, to exclude already existing null-terminator chars in the
|
||||||
// output buffer
|
// output buffer
|
||||||
size_t offset = (bufLen) ? bufLen - 1 : 0;
|
size_t offset = (m_outputStringLen) ? m_outputStringLen - 1 : 0;
|
||||||
// Resize capacity of the output buffer on demand and add some extra space to improve the
|
// Resize capacity of the output buffer on demand and add some extra space to improve the
|
||||||
// performance
|
// performance
|
||||||
if (bufCap <= (bufLen + strLen))
|
if (bufLen <= (m_outputStringLen + strLen))
|
||||||
{
|
{
|
||||||
m_outputBuffer.reserve(bufCap + strLen + 256);
|
m_outputBuffer.resize(bufLen + strLen + 512);
|
||||||
}
|
}
|
||||||
// Append the text
|
// Write the text to the output buffer
|
||||||
m_outputBuffer.resize(offset + strLen);
|
|
||||||
memcpy(&m_outputBuffer[offset], text, strLen);
|
memcpy(&m_outputBuffer[offset], text, strLen);
|
||||||
|
// Increase the string length
|
||||||
|
m_outputStringLen = offset + strLen;
|
||||||
// Convert to uppercase
|
// Convert to uppercase
|
||||||
if (m_uppercase)
|
if (m_uppercase)
|
||||||
{
|
{
|
||||||
for (size_t i = offset; i < m_outputBuffer.size() - 1; ++i)
|
for (size_t i = offset; i < m_outputStringLen - 1; ++i)
|
||||||
{
|
{
|
||||||
m_outputBuffer[i] = toupper(m_outputBuffer[i]);
|
m_outputBuffer[i] = toupper(m_outputBuffer[i]);
|
||||||
}
|
}
|
||||||
|
@ -178,34 +180,41 @@ void VXBaseInstructionFormatter::outputAppendFormatted(char const *format, ...)
|
||||||
{
|
{
|
||||||
va_list arguments;
|
va_list arguments;
|
||||||
va_start(arguments, format);
|
va_start(arguments, format);
|
||||||
// Get the string length including the null-terminator char
|
// Get the buffer size
|
||||||
size_t strLen = _vscprintf(format, arguments) + 1;
|
|
||||||
// Get the buffer capacity and size
|
|
||||||
size_t bufCap = m_outputBuffer.capacity();
|
|
||||||
size_t bufLen = m_outputBuffer.size();
|
size_t bufLen = m_outputBuffer.size();
|
||||||
// Decrease the offset by one, to exclude already existing null-terminator chars in the
|
// Decrease the offset by one, to exclude already existing null-terminator chars in the
|
||||||
// output buffer
|
// output buffer
|
||||||
size_t offset = (bufLen) ? bufLen - 1 : 0;
|
size_t offset = (m_outputStringLen) ? m_outputStringLen - 1 : 0;
|
||||||
if (strLen > 1)
|
// Resize the output buffer on demand and add some extra space to improve the performance
|
||||||
|
if ((bufLen - m_outputStringLen) < 256)
|
||||||
{
|
{
|
||||||
// Resize capacity of the output buffer on demand and add some extra space to improve the
|
bufLen = bufLen + 512;
|
||||||
// performance
|
m_outputBuffer.resize(bufLen);
|
||||||
if (bufCap < (bufLen + strLen))
|
|
||||||
{
|
|
||||||
m_outputBuffer.reserve(bufCap + strLen + 256);
|
|
||||||
}
|
}
|
||||||
// Append the formatted text
|
int strLen = 0;
|
||||||
m_outputBuffer.resize(offset + strLen);
|
do
|
||||||
vsnprintf_s(&m_outputBuffer[offset], strLen, strLen, format, arguments);
|
{
|
||||||
|
// If the formatted text did not fit in the output buffer, resize it, and try again
|
||||||
|
if (strLen < 0)
|
||||||
|
{
|
||||||
|
m_outputBuffer.resize(bufLen + 512);
|
||||||
|
return outputAppendFormatted(format, arguments);
|
||||||
|
}
|
||||||
|
// Write the formatted text to the output buffer
|
||||||
|
assert((bufLen - offset) > 0);
|
||||||
|
strLen =
|
||||||
|
vsnprintf_s(&m_outputBuffer[offset], bufLen - offset, _TRUNCATE, format, arguments);
|
||||||
|
} while (strLen < 0);
|
||||||
|
// Increase the string length
|
||||||
|
m_outputStringLen = offset + strLen + 1;
|
||||||
// Convert to uppercase
|
// Convert to uppercase
|
||||||
if (m_uppercase)
|
if (m_uppercase)
|
||||||
{
|
{
|
||||||
for (size_t i = offset; i < m_outputBuffer.size() - 1; ++i)
|
for (size_t i = offset; i < m_outputStringLen - 1; ++i)
|
||||||
{
|
{
|
||||||
m_outputBuffer[i] = toupper(m_outputBuffer[i]);
|
m_outputBuffer[i] = toupper(m_outputBuffer[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
va_end(arguments);
|
va_end(arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,7 @@ private:
|
||||||
static const char *m_registerStrings[];
|
static const char *m_registerStrings[];
|
||||||
VXBaseSymbolResolver *m_symbolResolver;
|
VXBaseSymbolResolver *m_symbolResolver;
|
||||||
std::vector<char> m_outputBuffer;
|
std::vector<char> m_outputBuffer;
|
||||||
|
size_t m_outputStringLen;
|
||||||
bool m_uppercase;
|
bool m_uppercase;
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -59,13 +59,18 @@ const char* VXExactSymbolResolver::resolveSymbol(const VXInstructionInfo &info,
|
||||||
uint64_t &offset)
|
uint64_t &offset)
|
||||||
{
|
{
|
||||||
std::unordered_map<uint64_t, std::string>::const_iterator iterator = m_symbolMap.find(address);
|
std::unordered_map<uint64_t, std::string>::const_iterator iterator = m_symbolMap.find(address);
|
||||||
return (iterator == m_symbolMap.end()) ? nullptr : iterator->second.c_str();
|
if (iterator != m_symbolMap.end())
|
||||||
|
{
|
||||||
|
offset = 0;
|
||||||
|
return iterator->second.c_str();
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VXExactSymbolResolver::containsSymbol(uint64_t address) const
|
bool VXExactSymbolResolver::containsSymbol(uint64_t address) const
|
||||||
{
|
{
|
||||||
std::unordered_map<uint64_t, std::string>::const_iterator iterator = m_symbolMap.find(address);
|
std::unordered_map<uint64_t, std::string>::const_iterator iterator = m_symbolMap.find(address);
|
||||||
return (iterator == m_symbolMap.end()) ? false : true;
|
return (iterator != m_symbolMap.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
void VXExactSymbolResolver::setSymbol(uint64_t address, const char* name)
|
void VXExactSymbolResolver::setSymbol(uint64_t address, const char* name)
|
||||||
|
|
Loading…
Reference in New Issue