diff --git a/CMakeLists.txt b/CMakeLists.txt index 31ff690b..c5d6f9bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -306,13 +306,5 @@ if (ZYDIS_BUILD_TOOLS AND NOT ZYAN_NO_LIBC) zyan_maybe_enable_wpo("ZydisInfo") _maybe_set_emscripten_cfg("ZydisInfo") install(TARGETS "ZydisInfo" RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) - - add_executable("ZydisPE" "tools/ZydisPE.c") - target_link_libraries("ZydisPE" "Zydis") - set_target_properties ("ZydisPE" PROPERTIES FOLDER "Tools") - target_compile_definitions("ZydisPE" PRIVATE "_CRT_SECURE_NO_WARNINGS") - zyan_set_common_flags("ZydisPE") - zyan_maybe_enable_wpo("ZydisPE") - _maybe_set_emscripten_cfg("ZydisPE") endif () endif () diff --git a/tools/ZydisPE.c b/tools/ZydisPE.c deleted file mode 100644 index 8208094d..00000000 --- a/tools/ZydisPE.c +++ /dev/null @@ -1,1188 +0,0 @@ -/*************************************************************************************************** - - Zyan Disassembler Library (Zydis) - - Original Author : Florian Bernd - - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - -***************************************************************************************************/ - -/** - * @file - * Disassembles a given PE file. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -// TODO: Add buffer overread checks -// TODO: Use platform specific file mapping routines instead of `fopen`/`malloc` - -/* ============================================================================================== */ -/* String constants */ -/* ============================================================================================== */ - -static const ZyanStringView STR_DOT = ZYAN_DEFINE_STRING_VIEW("."); -static const ZyanStringView STR_ENTRY_POINT = ZYAN_DEFINE_STRING_VIEW("EntryPoint"); - -/* ============================================================================================== */ -/* Status codes */ -/* ============================================================================================== */ - -/* ---------------------------------------------------------------------------------------------- */ -/* Module IDs */ -/* ---------------------------------------------------------------------------------------------- */ - -/** - * The zydis PE tool module id. - */ -#define ZYAN_MODULE_ZYDIS_PE 0x101 - -/* ---------------------------------------------------------------------------------------------- */ -/* Status codes */ -/* ---------------------------------------------------------------------------------------------- */ - -/** - * The signature of the PE-files DOS header field is invalid. - */ -#define ZYDIS_STATUS_INVALID_DOS_SIGNATURE \ - ZYAN_MAKE_STATUS(1, ZYAN_MODULE_ZYDIS_PE, 0x00) - -/** - * The signature of the PE-files NT headers field is invalid. - */ -#define ZYDIS_STATUS_INVALID_NT_SIGNATURE \ - ZYAN_MAKE_STATUS(1, ZYAN_MODULE_ZYDIS_PE, 0x01) - -/** - * The architecture of the assembly code contained in the PE-file is not supported. - */ -#define ZYDIS_STATUS_UNSUPPORTED_ARCHITECTURE \ - ZYAN_MAKE_STATUS(1, ZYAN_MODULE_ZYDIS_PE, 0x02) - -/* ---------------------------------------------------------------------------------------------- */ - -/* ============================================================================================== */ -/* PE stuff from `Windows.h` */ -/* ============================================================================================== */ - -/* ---------------------------------------------------------------------------------------------- */ -/* Constants */ -/* ---------------------------------------------------------------------------------------------- */ - -#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ -#define IMAGE_NT_SIGNATURE 0x00004550 // PE00 -#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x010B -#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x020B - -#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 -#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 -#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 - -#define IMAGE_FILE_MACHINE_I386 0x014C -#define IMAGE_FILE_MACHINE_IA64 0x0200 -#define IMAGE_FILE_MACHINE_AMD64 0x8664 - -#define IMAGE_SCN_CNT_CODE 0x00000020 - -#define IMAGE_IMPORT_BY_ORDINAL32 0x80000000 -#define IMAGE_IMPORT_BY_ORDINAL64 0x8000000000000000 - -/* ---------------------------------------------------------------------------------------------- */ -/* Enums and types */ -/* ---------------------------------------------------------------------------------------------- */ - -typedef struct IMAGE_DOS_HEADER_ -{ - ZyanU16 e_magic; - ZyanU16 e_cblp; - ZyanU16 e_cp; - ZyanU16 e_crlc; - ZyanU16 e_cparhdr; - ZyanU16 e_minalloc; - ZyanU16 e_maxalloc; - ZyanU16 e_ss; - ZyanU16 e_sp; - ZyanU16 e_csum; - ZyanU16 e_ip; - ZyanU16 e_cs; - ZyanU16 e_lfarlc; - ZyanU16 e_ovno; - ZyanU16 e_res[4]; - ZyanU16 e_oemid; - ZyanU16 e_oeminfo; - ZyanU16 e_res2[10]; - ZyanU32 e_lfanew; -} IMAGE_DOS_HEADER; - -typedef struct IMAGE_FILE_HEADER_ -{ - ZyanU16 Machine; - ZyanU16 NumberOfSections; - ZyanU32 TimeDateStamp; - ZyanU32 PointerToSymbolTable; - ZyanU32 NumberOfSymbols; - ZyanU16 SizeOfOptionalHeader; - ZyanU16 Characteristics; -} IMAGE_FILE_HEADER; - -typedef struct IMAGE_DATA_DIRECTORY_ -{ - ZyanU32 VirtualAddress; - ZyanU32 Size; -} IMAGE_DATA_DIRECTORY; - -typedef struct IMAGE_OPTIONAL_HEADER32_ -{ - ZyanU16 Magic; - ZyanU8 MajorLinkerVersion; - ZyanU8 MinorLinkerVersion; - ZyanU32 SizeOfCode; - ZyanU32 SizeOfInitializedData; - ZyanU32 SizeOfUninitializedData; - ZyanU32 AddressOfEntryPoint; - ZyanU32 BaseOfCode; - ZyanU32 BaseOfData; - ZyanU32 ImageBase; - ZyanU32 SectionAlignment; - ZyanU32 FileAlignment; - ZyanU16 MajorOperatingSystemVersion; - ZyanU16 MinorOperatingSystemVersion; - ZyanU16 MajorImageVersion; - ZyanU16 MinorImageVersion; - ZyanU16 MajorSubsystemVersion; - ZyanU16 MinorSubsystemVersion; - ZyanU32 Win32VersionValue; - ZyanU32 SizeOfImage; - ZyanU32 SizeOfHeaders; - ZyanU32 CheckSum; - ZyanU16 Subsystem; - ZyanU16 DllCharacteristics; - ZyanU32 SizeOfStackReserve; - ZyanU32 SizeOfStackCommit; - ZyanU32 SizeOfHeapReserve; - ZyanU32 SizeOfHeapCommit; - ZyanU32 LoaderFlags; - ZyanU32 NumberOfRvaAndSizes; - IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; -} IMAGE_OPTIONAL_HEADER32; - -typedef struct IMAGE_NT_HEADERS32_ -{ - ZyanU32 Signature; - IMAGE_FILE_HEADER FileHeader; - IMAGE_OPTIONAL_HEADER32 OptionalHeader; -} IMAGE_NT_HEADERS32; - -typedef struct IMAGE_OPTIONAL_HEADER64_ -{ - ZyanU16 Magic; - ZyanU8 MajorLinkerVersion; - ZyanU8 MinorLinkerVersion; - ZyanU32 SizeOfCode; - ZyanU32 SizeOfInitializedData; - ZyanU32 SizeOfUninitializedData; - ZyanU32 AddressOfEntryPoint; - ZyanU32 BaseOfCode; - ZyanU64 ImageBase; - ZyanU32 SectionAlignment; - ZyanU32 FileAlignment; - ZyanU16 MajorOperatingSystemVersion; - ZyanU16 MinorOperatingSystemVersion; - ZyanU16 MajorImageVersion; - ZyanU16 MinorImageVersion; - ZyanU16 MajorSubsystemVersion; - ZyanU16 MinorSubsystemVersion; - ZyanU32 Win32VersionValue; - ZyanU32 SizeOfImage; - ZyanU32 SizeOfHeaders; - ZyanU32 CheckSum; - ZyanU16 Subsystem; - ZyanU16 DllCharacteristics; - ZyanU64 SizeOfStackReserve; - ZyanU64 SizeOfStackCommit; - ZyanU64 SizeOfHeapReserve; - ZyanU64 SizeOfHeapCommit; - ZyanU32 LoaderFlags; - ZyanU32 NumberOfRvaAndSizes; - IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; -} IMAGE_OPTIONAL_HEADER64; - -typedef struct IMAGE_NT_HEADERS64_ -{ - ZyanU32 Signature; - IMAGE_FILE_HEADER FileHeader; - IMAGE_OPTIONAL_HEADER64 OptionalHeader; -} IMAGE_NT_HEADERS64; - -#define IMAGE_SIZEOF_SHORT_NAME 8 - -typedef struct IMAGE_SECTION_HEADER_ { - ZyanU8 Name[IMAGE_SIZEOF_SHORT_NAME]; - union - { - ZyanU32 PhysicalAddress; - ZyanU32 VirtualSize; - } Misc; - ZyanU32 VirtualAddress; - ZyanU32 SizeOfRawData; - ZyanU32 PointerToRawData; - ZyanU32 PointerToRelocations; - ZyanU32 PointerToLinenumbers; - ZyanU16 NumberOfRelocations; - ZyanU16 NumberOfLinenumbers; - ZyanU32 Characteristics; -} IMAGE_SECTION_HEADER; - -typedef struct IMAGE_EXPORT_DIRECTORY_ -{ - ZyanU32 Characteristics; - ZyanU32 TimeDateStamp; - ZyanU16 MajorVersion; - ZyanU16 MinorVersion; - ZyanU32 Name; - ZyanU32 Base; - ZyanU32 NumberOfFunctions; - ZyanU32 NumberOfNames; - ZyanU32 AddressOfFunctions; - ZyanU32 AddressOfNames; - ZyanU32 AddressOfNameOrdinals; -} IMAGE_EXPORT_DIRECTORY; - -typedef struct IMAGE_IMPORT_DESCRIPTOR_ -{ - union - { - ZyanU32 Characteristics; - ZyanU32 OriginalFirstThunk; - } u1; - ZyanU32 TimeDateStamp; - ZyanU32 ForwarderChain; - ZyanU32 Name; - ZyanU32 FirstThunk; -} IMAGE_IMPORT_DESCRIPTOR; - -typedef struct IMAGE_THUNK_DATA32_ -{ - union - { - ZyanU32 ForwarderString; - ZyanU32 Function; - ZyanU32 Ordinal; - ZyanU32 AddressOfData; - } u1; -} IMAGE_THUNK_DATA32; - -#pragma pack(push, 8) - -typedef struct IMAGE_THUNK_DATA64_ -{ - union - { - ZyanU64 ForwarderString; - ZyanU64 Function; - ZyanU64 Ordinal; - ZyanU64 AddressOfData; - } u1; -} IMAGE_THUNK_DATA64; - -#pragma pack(pop) - -typedef struct IMAGE_IMPORT_BY_NAME_ -{ - ZyanU16 Hint; - char Name[1]; -} IMAGE_IMPORT_BY_NAME; - -/* ---------------------------------------------------------------------------------------------- */ -/* Macros */ -/* ---------------------------------------------------------------------------------------------- */ - -#define IMAGE_FIRST_SECTION(nt_headers) \ - ((IMAGE_SECTION_HEADER*)((ZyanUPointer)(nt_headers) \ - + offsetof(IMAGE_NT_HEADERS32, OptionalHeader) \ - + ((nt_headers))->FileHeader.SizeOfOptionalHeader)) - -/* ---------------------------------------------------------------------------------------------- */ - -/* ============================================================================================== */ -/* PE Context */ -/* ============================================================================================== */ - -/* ---------------------------------------------------------------------------------------------- */ -/* Enums and types */ -/* ---------------------------------------------------------------------------------------------- */ - -/** - * Defines the `ZydisPESymbol` struct. - */ -typedef struct ZydisPESymbol_ -{ - /** - * The virtual address of the symbol. - */ - ZyanU64 address; - /** - * The module string. - */ - ZyanString module_name; - /** - * The symbol string. - */ - ZyanString symbol_name; -} ZydisPESymbol; - -/** - * Defines the `ZydisPEContext` struct. - */ -typedef struct ZydisPEContext_ -{ - /** - * The memory that contains the mapped PE-file. - */ - const void* base; - /** - * The size of the memory mapped PE-file. - */ - ZyanUSize size; - /** - * A vector that contains the addresses and names of all symbols. - */ - ZyanVector symbols; - /** - * The desired image-base of the PE-file. - */ - ZyanU64 image_base; - /** - * A vector that contains all string instances that need to be destroyed. - */ - ZyanVector unique_strings; -} ZydisPEContext; - -/* ---------------------------------------------------------------------------------------------- */ -/* Functions */ -/* ---------------------------------------------------------------------------------------------- */ -/** - * A comparison function for the `ZydisPESymbol` that uses the `address` field as key value. - * - * @param left A pointer to the first element. - * @param right A pointer to the second element. - * - * @return Returns values in the following range: - * `left == right -> result == 0` - * `left < right -> result < 0` - * `left > right -> result > 0` - */ -static ZyanI32 CompareSymbol(const ZydisPESymbol* left, const ZydisPESymbol* right) -{ - ZYAN_ASSERT(left); - ZYAN_ASSERT(right); - - if (left->address < right->address) - { - return -1; - } - if (left->address > right->address) - { - return 1; - } - return 0; -} - -/* ---------------------------------------------------------------------------------------------- */ - -/** - * Returns a pointer to the section header of the section that contains the given `rva`. - * - * @param base A pointer to the memory that contains the mapped PE-file. - * @param rva The relative virtual address. - * - * @return A pointer to a `IMAGE_SECTION_HEADER` struct, or `ZYAN_NULL`. - */ -static const IMAGE_SECTION_HEADER* GetSectionByRVA(const void* base, ZyanU64 rva) -{ - ZYAN_ASSERT(base); - - const IMAGE_DOS_HEADER* dos_header = (const IMAGE_DOS_HEADER*)base; - ZYAN_ASSERT(dos_header->e_magic == IMAGE_DOS_SIGNATURE); - const IMAGE_NT_HEADERS32* nt_headers = - (const IMAGE_NT_HEADERS32*)((ZyanU8*)dos_header + dos_header->e_lfanew); - ZYAN_ASSERT(nt_headers->Signature == IMAGE_NT_SIGNATURE); - - const IMAGE_SECTION_HEADER* section = IMAGE_FIRST_SECTION(nt_headers); - for (ZyanU16 i = 0; i < nt_headers->FileHeader.NumberOfSections; ++i, ++section) - { - ZyanU32 size = section->SizeOfRawData; - if (section->Misc.VirtualSize > 0) - { - size = ZYAN_MIN(section->Misc.VirtualSize, size); - } - size = ZYAN_ALIGN_UP(size, nt_headers->OptionalHeader.FileAlignment); - - if ((rva >= section->VirtualAddress) && (rva < (section->VirtualAddress + size))) - { - return section; - } - } - - return ZYAN_NULL; -} - -/** - * Converts a relative virtual address to file offset. - * - * @param base A pointer to the memory mapped PE-file. - * @param rva The relative virtual-address. - * - * @return The address in file mapping corresponding to `rva` or `ZYAN_NULL`. - */ -const void* RVAToFileOffset(const void* base, ZyanU64 rva) -{ - ZYAN_ASSERT(base); - - const IMAGE_SECTION_HEADER* section = GetSectionByRVA(base, rva); - - if (!section) - { - return ZYAN_NULL; - } - - return (void*)((ZyanU8*)base + section->PointerToRawData + (rva - section->VirtualAddress)); -} - -/* ---------------------------------------------------------------------------------------------- */ - -/** - * Finalizes the given `ZydisPEContext` struct. - * - * @param context A pointer to the `ZydisPEContext` struct. - * - * @return A zycore status code. - */ -static ZyanStatus ZydisPEContextFinalize(ZydisPEContext* context) -{ - ZyanUSize size; - ZYAN_CHECK(ZyanVectorGetSize(&context->unique_strings, &size)); - - for (ZyanUSize i = 0; i < size; ++i) - { - ZyanString* string; - ZYAN_CHECK(ZyanVectorGetPointerMutable(&context->unique_strings, i, (void**)&string)); - ZYAN_CHECK(ZyanStringDestroy(string)); - } - - ZYAN_CHECK(ZyanVectorDestroy(&context->symbols)); - return ZyanVectorDestroy(&context->unique_strings); -} - -/** - * Initializes the given `ZydisPEContext` struct. - * - * @param context A pointer to the `ZydisPEContext` struct. - * @param base A pointer to the memory that contains the mapped PE-file. - * @param size The size of the memory mapped PE-file. - * - * @return A zycore status code. - */ -static ZyanStatus ZydisPEContextInit(ZydisPEContext* context, const void* base, ZyanUSize size) -{ - ZYAN_ASSERT(context); - ZYAN_ASSERT(base); - ZYAN_ASSERT(size); - - context->base = base; - context->size = size; - - ZyanStatus status; - ZYAN_CHECK(status = ZyanVectorInit(&context->symbols, sizeof(ZydisPESymbol), 256, ZYAN_NULL)); - if (!ZYAN_SUCCESS(status = ZyanVectorInit(&context->unique_strings, sizeof(ZyanString), 256, ZYAN_NULL))) - { - ZyanVectorDestroy(&context->symbols); - return status; - } - - const IMAGE_DOS_HEADER* dos_header = (const IMAGE_DOS_HEADER*)base; - ZYAN_ASSERT(dos_header->e_magic == IMAGE_DOS_SIGNATURE); - const IMAGE_NT_HEADERS32* nt_headers_temp = - (const IMAGE_NT_HEADERS32*)((ZyanU8*)dos_header + dos_header->e_lfanew); - ZYAN_ASSERT(nt_headers_temp->Signature == IMAGE_NT_SIGNATURE); - - // Parse symbols - - switch (nt_headers_temp->OptionalHeader.Magic) - { - case IMAGE_NT_OPTIONAL_HDR32_MAGIC: - { - const IMAGE_NT_HEADERS32* nt_headers = - (const IMAGE_NT_HEADERS32*)((ZyanU8*)dos_header + dos_header->e_lfanew); - context->image_base = nt_headers->OptionalHeader.ImageBase; - - // Entry point - ZydisPESymbol element; - const ZyanUPointer entry_point = nt_headers->OptionalHeader.AddressOfEntryPoint; - element.address = entry_point; - if (!ZYAN_SUCCESS((status = - ZyanStringDuplicate(&element.symbol_name, &STR_ENTRY_POINT, 0))) || - !ZYAN_SUCCESS((status = - ZyanVectorPushBack(&context->symbols, &element)))) - { - goto FatalError; - } - - // Exports - if (nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress) - { - const IMAGE_EXPORT_DIRECTORY* export_directory = - (const IMAGE_EXPORT_DIRECTORY*)RVAToFileOffset(base, - nt_headers->OptionalHeader.DataDirectory[ - IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); - - ZyanStringView module_name; - if (!ZYAN_SUCCESS(status = ZyanStringViewInsideBuffer(&module_name, - (char*)RVAToFileOffset(base, export_directory->Name))) || - !ZYAN_SUCCESS(status = - ZyanStringDuplicate(&element.module_name, &module_name, 0))) - { - goto FatalError; - } - // Remove file-extension - ZyanISize index; - if (!ZYAN_SUCCESS(status = ZyanStringRPos(&module_name, &STR_DOT, &index))) - { - goto FatalError; - } - if (index >= 0) - { - if (!ZYAN_SUCCESS(status = ZyanStringTruncate(&element.module_name, index)) || - !ZYAN_SUCCESS(status = - ZyanVectorPushBack(&context->unique_strings, &element.module_name))) - { - goto FatalError; - } - } else - if (!ZYAN_SUCCESS(status = - ZyanVectorPushBack(&context->unique_strings, &element.module_name))) - { - goto FatalError; - } - - const ZyanU32* export_addresses = - (const ZyanU32*)RVAToFileOffset(base, - export_directory->AddressOfFunctions); - const ZyanU32* export_names = - (const ZyanU32*)RVAToFileOffset(base, - export_directory->AddressOfNames); - for (ZyanU32 i = 0; i < export_directory->NumberOfFunctions; ++i) - { - element.address = export_addresses[i]; - ZyanStringView symbol_name; - if (!ZYAN_SUCCESS((status = - ZyanStringViewInsideBuffer(&symbol_name, - (const char*)RVAToFileOffset(base, export_names[i])))) || - !ZYAN_SUCCESS((status = - ZyanStringDuplicate(&element.symbol_name, &symbol_name, 0)))) - { - goto FatalError; - } - - ZyanUSize found_index; - if (!ZYAN_SUCCESS((status = - ZyanVectorBinarySearch(&context->symbols, &element, &found_index, - (ZyanComparison)&CompareSymbol))) || - !ZYAN_SUCCESS((status = - ZyanVectorInsert(&context->symbols, found_index, &element)))) - { - goto FatalError; - } - } - } - - // Imports - if (nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress) - { - const IMAGE_IMPORT_DESCRIPTOR* descriptor = - (const IMAGE_IMPORT_DESCRIPTOR*)RVAToFileOffset(base, - nt_headers->OptionalHeader.DataDirectory[ - IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); - while (descriptor->u1.OriginalFirstThunk) - { - ZyanStringView module_name; - if (!ZYAN_SUCCESS(status = ZyanStringViewInsideBuffer(&module_name, - (const char*)RVAToFileOffset(base, descriptor->Name))) || - !ZYAN_SUCCESS(status = ZyanStringDuplicate( - &element.module_name, &module_name, 0))) - { - goto FatalError; - } - // Remove file-extension - ZyanISize index; - if (!ZYAN_SUCCESS(status = ZyanStringRPos(&module_name, &STR_DOT, &index))) - { - goto FatalError; - } - if (index >= 0) - { - if (!ZYAN_SUCCESS(status = ZyanStringTruncate(&element.module_name, index)) || - !ZYAN_SUCCESS(status = - ZyanVectorPushBack(&context->unique_strings, &element.module_name))) - { - goto FatalError; - } - } else - if (!ZYAN_SUCCESS(status = - ZyanVectorPushBack(&context->unique_strings, &element.module_name))) - { - goto FatalError; - } - - const IMAGE_THUNK_DATA32* original_thunk = - (const IMAGE_THUNK_DATA32*)RVAToFileOffset(base, - descriptor->u1.OriginalFirstThunk); - element.address = descriptor->FirstThunk; - - ZyanUSize found_index; - if (!ZYAN_SUCCESS((status = - ZyanVectorBinarySearch(&context->symbols, &element, &found_index, - (ZyanComparison)&CompareSymbol)))) - { - goto FatalError; - } - ZYAN_ASSERT(status == ZYAN_STATUS_FALSE); - - while (original_thunk->u1.ForwarderString) - { - if (!(original_thunk->u1.Ordinal & IMAGE_IMPORT_BY_ORDINAL32)) - { - const IMAGE_IMPORT_BY_NAME* by_name = - (const IMAGE_IMPORT_BY_NAME*)RVAToFileOffset(base, - original_thunk->u1.AddressOfData); - - ZyanStringView symbol_name; - if (!ZYAN_SUCCESS((status = - ZyanStringViewInsideBuffer( - &symbol_name, (const char*)by_name->Name))) || - !ZYAN_SUCCESS((status = - ZyanStringDuplicate(&element.symbol_name, &symbol_name, 0)))) - { - goto FatalError; - } - } - - if (!ZYAN_SUCCESS((status = - ZyanVectorInsert(&context->symbols, found_index, &element)))) - { - goto FatalError; - } - - element.address += sizeof(IMAGE_THUNK_DATA32); - ++original_thunk; - ++found_index; - } - ++descriptor; - } - } - - break; - } - case IMAGE_NT_OPTIONAL_HDR64_MAGIC: - { - const IMAGE_NT_HEADERS64* nt_headers = - (const IMAGE_NT_HEADERS64*)((ZyanU8*)dos_header + dos_header->e_lfanew); - context->image_base = nt_headers->OptionalHeader.ImageBase; - - // Entry point. - ZydisPESymbol element; - const ZyanUPointer entry_point = nt_headers->OptionalHeader.AddressOfEntryPoint; - element.address = entry_point; - if (!ZYAN_SUCCESS((status = - ZyanStringDuplicate(&element.symbol_name, &STR_ENTRY_POINT, 0))) || - !ZYAN_SUCCESS((status = - ZyanVectorPushBack(&context->symbols, &element)))) - { - goto FatalError; - } - - // Exports - if (nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress) - { - const IMAGE_EXPORT_DIRECTORY* export_directory = - (const IMAGE_EXPORT_DIRECTORY*)RVAToFileOffset(base, - nt_headers->OptionalHeader.DataDirectory[ - IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); - - ZyanStringView module_name; - if (!ZYAN_SUCCESS((status = - ZyanStringViewInsideBuffer(&module_name, - (const char*)RVAToFileOffset(base, export_directory->Name)))) || - !ZYAN_SUCCESS((status = - ZyanStringDuplicate(&element.module_name, &module_name, 0)))) - { - goto FatalError; - } - // TODO: Implement - /*for (ZyanUSize i = element.module_name.length - 1; i >= 0; --i) - { - if (element.module_name.buffer[i] == '.') - { - element.module_name.length = i; - break; - } - }*/ - - const ZyanU32* export_addresses = - (const ZyanU32*)RVAToFileOffset(base, - export_directory->AddressOfFunctions); - const ZyanU32* export_names = - (const ZyanU32*)RVAToFileOffset(base, - export_directory->AddressOfNames); - for (ZyanU32 i = 0; i < export_directory->NumberOfFunctions; ++i) - { - element.address = export_addresses[i]; - ZyanStringView symbol_name; - if (!ZYAN_SUCCESS((status = - ZyanStringViewInsideBuffer(&symbol_name, - (const char*)RVAToFileOffset(base, export_names[i])))) || - !ZYAN_SUCCESS((status = - ZyanStringDuplicate(&element.symbol_name, &symbol_name, 0)))) - { - goto FatalError; - } - - ZyanUSize found_index; - if (!ZYAN_SUCCESS((status = - ZyanVectorBinarySearch(&context->symbols, &element, &found_index, - (ZyanComparison)&CompareSymbol))) || - !ZYAN_SUCCESS((status = - ZyanVectorInsert(&context->symbols, found_index, &element)))) - { - goto FatalError; - } - } - } - - // Imports - if (nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress) - { - const IMAGE_IMPORT_DESCRIPTOR* descriptor = - (const IMAGE_IMPORT_DESCRIPTOR*)RVAToFileOffset(base, - nt_headers->OptionalHeader.DataDirectory[ - IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); - while (descriptor->u1.OriginalFirstThunk) - { - ZyanStringView module_name; - if (!ZYAN_SUCCESS((status = - ZyanStringViewInsideBuffer(&module_name, - (const char*)RVAToFileOffset(base, descriptor->Name)))) || - !ZYAN_SUCCESS((status = - ZyanStringDuplicate(&element.module_name, &module_name, 0)))) - { - goto FatalError; - } - // TODO: Implement - /*for (ZyanUSize i = element.module_name.length - 1; i >= 0; --i) - { - if (element.module_name.buffer[i] == '.') - { - element.module_name.length = i; - break; - } - }*/ - - const IMAGE_THUNK_DATA64* original_thunk = - (const IMAGE_THUNK_DATA64*)RVAToFileOffset(base, - descriptor->u1.OriginalFirstThunk); - element.address = descriptor->FirstThunk; - - ZyanUSize found_index; - if (!ZYAN_SUCCESS((status = - ZyanVectorBinarySearch(&context->symbols, &element, &found_index, - (ZyanComparison)&CompareSymbol)))) - { - goto FatalError; - } - ZYAN_ASSERT(status == ZYAN_STATUS_FALSE); - - while (original_thunk->u1.ForwarderString) - { - if (!(original_thunk->u1.Ordinal & IMAGE_IMPORT_BY_ORDINAL64)) - { - const IMAGE_IMPORT_BY_NAME* by_name = - (const IMAGE_IMPORT_BY_NAME*)RVAToFileOffset(base, - original_thunk->u1.AddressOfData); - - ZyanStringView symbol_name; - if (!ZYAN_SUCCESS((status = ZyanStringViewInsideBuffer( - &symbol_name, (const char*)by_name->Name))) || - !ZYAN_SUCCESS((status = - ZyanStringDuplicate(&element.symbol_name, &symbol_name, 0)))) - { - goto FatalError; - } - } - - if (!ZYAN_SUCCESS((status = - ZyanVectorInsert(&context->symbols, found_index, &element)))) - { - goto FatalError; - } - - element.address += sizeof(IMAGE_THUNK_DATA64); - ++original_thunk; - ++found_index; - } - ++descriptor; - } - } - - break; - } - default: - ZYAN_UNREACHABLE; - } - - return ZYAN_STATUS_SUCCESS; - -FatalError: - ZydisPEContextFinalize(context); - return status; -} - -/* ---------------------------------------------------------------------------------------------- */ - -/* ============================================================================================== */ -/* Disassembler */ -/* ============================================================================================== */ - -/* ---------------------------------------------------------------------------------------------- */ -/* Callbacks */ -/* ---------------------------------------------------------------------------------------------- */ - -ZydisFormatterFunc default_print_address_abs; -ZydisFormatterFunc default_print_address_rel; - -static ZyanStatus ZydisFormatterPrintAddress(const ZydisFormatter* formatter, - ZydisFormatterBuffer* buffer, ZydisFormatterContext* context, ZyanU64 address, ZyanBool is_abs) -{ - const ZydisPEContext* data = (const ZydisPEContext*)context->user_data; - ZYAN_ASSERT(data); - - ZydisPESymbol symbol; - symbol.address = address - data->image_base; - - ZyanStatus status; - ZyanUSize found_index; - ZYAN_CHECK((status = - ZyanVectorBinarySearch(&data->symbols, &symbol, &found_index, - (ZyanComparison)&CompareSymbol))); - - ZyanString* string; - ZYAN_CHECK(ZydisFormatterBufferGetString(buffer, &string)); - - if (status == ZYAN_STATUS_TRUE) - { - const ZydisPESymbol* element; - ZYAN_CHECK(ZyanVectorGetPointer(&data->symbols, found_index, (const void**)&element)); - ZyanUSize index; - ZyanUSize count; - ZYAN_CHECK(ZyanStringGetSize(string, &index)); - ZYAN_CHECK(ZyanStringGetSize(&element->module_name, &count)); - ZYAN_CHECK(ZyanStringAppend(string, ZYAN_STRING_TO_VIEW(&element->module_name))); - ZYAN_CHECK(ZyanStringToLowerCaseEx(string, index, count)); - ZYAN_CHECK(ZyanStringAppend(string, &STR_DOT)); - return ZyanStringAppend(string, ZYAN_STRING_TO_VIEW(&element->symbol_name)); - } - - // Default address printing - ZydisFormatterFunc fn = is_abs ? default_print_address_abs : default_print_address_rel; - return fn(formatter, buffer, context); -} - -static ZyanStatus ZydisFormatterPrintAddressABS(const ZydisFormatter* formatter, - ZydisFormatterBuffer* buffer, ZydisFormatterContext* context) -{ - ZyanU64 address; - ZYAN_CHECK(ZydisCalcAbsoluteAddress(context->instruction, context->operand, - context->runtime_address, &address)); - - return ZydisFormatterPrintAddress(formatter, buffer, context, address, ZYAN_TRUE); -} - -static ZyanStatus ZydisFormatterPrintAddressREL(const ZydisFormatter* formatter, - ZydisFormatterBuffer* buffer, ZydisFormatterContext* context) -{ - ZyanU64 address; - ZYAN_CHECK(ZydisCalcAbsoluteAddress(context->instruction, context->operand, 0, &address)); - - return ZydisFormatterPrintAddress(formatter, buffer, context, address, ZYAN_FALSE); -} - -/* ---------------------------------------------------------------------------------------------- */ -/* Disassembler */ -/* ---------------------------------------------------------------------------------------------- */ - -/** - * Disassembles a mapped PE-file and prints the output to `stdout`. - * Automatically resolves exports and imports. - * - * base A pointer to the `ZydisPEContext` struct. - */ -static ZyanStatus DisassembleMappedPEFile(const ZydisPEContext* context) -{ - ZYAN_ASSERT(context); - - const IMAGE_DOS_HEADER* dos_header = (const IMAGE_DOS_HEADER*)context->base; - ZYAN_ASSERT(dos_header->e_magic == IMAGE_DOS_SIGNATURE); - const IMAGE_NT_HEADERS32* nt_headers = - (const IMAGE_NT_HEADERS32*)((ZyanU8*)dos_header + dos_header->e_lfanew); - ZYAN_ASSERT(nt_headers->Signature == IMAGE_NT_SIGNATURE); - - ZyanStatus status; - - // Initialize decoder - ZydisMachineMode machine_mode; - ZydisStackWidth stack_width; - switch (nt_headers->FileHeader.Machine) - { - case IMAGE_FILE_MACHINE_I386: - machine_mode = ZYDIS_MACHINE_MODE_LONG_COMPAT_32; - stack_width = ZYDIS_STACK_WIDTH_32; - break; - case IMAGE_FILE_MACHINE_IA64: - case IMAGE_FILE_MACHINE_AMD64: - machine_mode = ZYDIS_MACHINE_MODE_LONG_64; - stack_width = ZYDIS_STACK_WIDTH_64; - break; - default: - ZYAN_UNREACHABLE; - } - ZydisDecoder decoder; - if (!ZYAN_SUCCESS((status = ZydisDecoderInit(&decoder, machine_mode, stack_width)))) - { - fputs("Failed to initialize instruction-decoder\n", stderr); - return status; - } - - // Initialize formatter - default_print_address_abs = (ZydisFormatterFunc)&ZydisFormatterPrintAddressABS; - default_print_address_rel = (ZydisFormatterFunc)&ZydisFormatterPrintAddressREL; - ZydisFormatter formatter; - if (!ZYAN_SUCCESS((status = ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL))) || - !ZYAN_SUCCESS((status = ZydisFormatterSetProperty(&formatter, - ZYDIS_FORMATTER_PROP_FORCE_SEGMENT, ZYAN_TRUE))) || - !ZYAN_SUCCESS((status = ZydisFormatterSetProperty(&formatter, - ZYDIS_FORMATTER_PROP_FORCE_SIZE, ZYAN_TRUE))) || - !ZYAN_SUCCESS((status = ZydisFormatterSetHook(&formatter, - ZYDIS_FORMATTER_FUNC_PRINT_ADDRESS_ABS, (const void**)&default_print_address_abs))) || - !ZYAN_SUCCESS((status = ZydisFormatterSetHook(&formatter, - ZYDIS_FORMATTER_FUNC_PRINT_ADDRESS_REL, (const void**)&default_print_address_rel)))) - { - fputs("Failed to initialize instruction-formatter\n", stderr); - return status; - } - - // Disassemble all code sections - ZydisDecodedInstruction instruction; - const IMAGE_SECTION_HEADER* section_header = IMAGE_FIRST_SECTION(nt_headers); - for (ZyanU16 i = 0; i < nt_headers->FileHeader.NumberOfSections; ++i) - { - if (!(section_header->Characteristics & IMAGE_SCN_CNT_CODE)) - { - continue; - } - - const ZyanU8* buffer = (ZyanU8*)context->base + section_header->PointerToRawData; - const ZyanUSize buffer_size = section_header->SizeOfRawData; - const ZyanU64 read_offset_base = context->image_base + section_header->VirtualAddress; - - ZyanUSize read_offset = 0; - while ((status = ZydisDecoderDecodeBuffer(&decoder, buffer + read_offset, - buffer_size - read_offset, &instruction)) != ZYDIS_STATUS_NO_MORE_DATA) - { - const ZyanU64 runtime_address = read_offset_base + read_offset; - - ZydisPESymbol symbol; - symbol.address = runtime_address - context->image_base; - - ZyanUSize found_index; - ZYAN_CHECK((status = - ZyanVectorBinarySearch(&context->symbols, &symbol, &found_index, - (ZyanComparison)&CompareSymbol))); - if (status == ZYAN_STATUS_TRUE) - { - const ZydisPESymbol* element; - ZYAN_CHECK(ZyanVectorGetPointer(&context->symbols, found_index, - (const void**)&element)); - const char* string; - ZYAN_CHECK(ZyanStringGetData(&element->symbol_name, &string)); - printf("\n%s:\n", string); - } - - switch (instruction.machine_mode) - { - case ZYDIS_MACHINE_MODE_LONG_COMPAT_32: - printf("%08" PRIX32 " ", (ZyanU32)runtime_address); - break; - case ZYDIS_MACHINE_MODE_LONG_64: - printf("%016" PRIX64 " ", (ZyanU64)runtime_address); - break; - default: - ZYAN_UNREACHABLE; - } - for (int j = 0; j < instruction.length; ++j) - { - printf("%02X ", buffer[read_offset + j]); - } - for (int j = instruction.length; j < 15; ++j) - { - printf(" "); - } - - if (ZYAN_SUCCESS(status)) - { - read_offset += instruction.length; - - char format_buffer[256]; - if (!ZYAN_SUCCESS((status = - ZydisFormatterFormatInstructionEx(&formatter, &instruction, format_buffer, - sizeof(format_buffer), runtime_address, (void*)context)))) - { - fputs("Failed to format instruction\n", stderr); - return status; - } - printf(" %s\n", &format_buffer[0]); - } else - { - printf(" db %02x\n", buffer[read_offset++]); - } - } - - ++section_header; - } - - return ZYAN_STATUS_SUCCESS; -} - -/* ---------------------------------------------------------------------------------------------- */ - -/* ============================================================================================== */ -/* Entry point */ -/* ============================================================================================== */ - -int main(int argc, char** argv) -{ - - if (argc != 2) - { - fprintf(stderr, "Usage: %s \n", (argc > 0 ? argv[0] : "ZydisPE")); - return ZYAN_STATUS_INVALID_ARGUMENT; - } - - // Map PE-file to memory - FILE* file = fopen(argv[1], "rb"); - if (!file) - { - fprintf(stderr, "Could not open file \"%s\": %s\n", argv[1], strerror(errno)); - return ZYAN_STATUS_BAD_SYSTEMCALL; - } - - fseek(file, 0L, SEEK_END); - const long size = ftell(file); - void* buffer = ZYAN_MALLOC(size); - if (!buffer) - { - fprintf(stderr, "Failed to allocate %" PRIu64 " bytes on the heap\n", (ZyanU64)size); - fclose(file); - return ZYAN_STATUS_NOT_ENOUGH_MEMORY; - } - - rewind(file); - if (fread(buffer, 1, size, file) != (ZyanUSize)size) - { - fprintf(stderr, - "Could not read %" PRIu64 " bytes from file \"%s\"\n", (ZyanU64)size, argv[1]); - ZYAN_FREE(buffer); - fclose(file); - return ZYAN_STATUS_BAD_SYSTEMCALL; - } - - // Validate PE file - const IMAGE_DOS_HEADER* dos_header = (const IMAGE_DOS_HEADER*)buffer; - if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) - { - fputs("Invalid file signature (DOS header)\n", stderr); - return ZYDIS_STATUS_INVALID_DOS_SIGNATURE; - } - - const IMAGE_NT_HEADERS32* nt_headers = - (const IMAGE_NT_HEADERS32*)((ZyanU8*)dos_header + dos_header->e_lfanew); - if (nt_headers->Signature != IMAGE_NT_SIGNATURE) - { - fputs("Invalid file signature (NT headers)\n", stderr); - return ZYDIS_STATUS_INVALID_NT_SIGNATURE; - } - - switch (nt_headers->FileHeader.Machine) - { - case IMAGE_FILE_MACHINE_I386: - case IMAGE_FILE_MACHINE_IA64: - case IMAGE_FILE_MACHINE_AMD64: - break; - default: - fputs("Unsupported architecture\n", stderr); - return ZYDIS_STATUS_UNSUPPORTED_ARCHITECTURE; - } - - switch (nt_headers->OptionalHeader.Magic) - { - case IMAGE_NT_OPTIONAL_HDR32_MAGIC: - case IMAGE_NT_OPTIONAL_HDR64_MAGIC: - break; - default: - fputs("Unsupported architecture\n", stderr); - return ZYDIS_STATUS_UNSUPPORTED_ARCHITECTURE; - } - - ZyanStatus status; - ZydisPEContext context; - if (!ZYAN_SUCCESS((status = ZydisPEContextInit(&context, buffer, size)))) - { - goto Exit; - } - if (!ZYAN_SUCCESS((status = DisassembleMappedPEFile(&context)))) - { - ZydisPEContextFinalize(&context); - goto Exit; - } - status = ZydisPEContextFinalize(&context); - -Exit: - ZYAN_FREE(buffer); - fclose(file); - - return status; -} - -/* ============================================================================================== */