From 869dfb42992236ba61376ba983a2c323200a2b0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joel=20Ho=CC=88ner?= Date: Fri, 22 Oct 2021 16:27:26 +0200 Subject: [PATCH] Remove ZydisPE The PE parsing code is wildly unsafe and doesn't perform any input validation. It was originally written as an example on how to use our formatter API for using custom symbols in disassembly output. However, living in the `tools` directory incorrectly suggested to users that it's a safe tool intended for actual public use. Following a security report, we decided to just delete it for the time being, possibly bringing it back later. We intentionally never defined install rules for it (it's not installed on `make install`) and also don't ship it in the packages of any package manager repository, so the practical security impact should be low. --- CMakeLists.txt | 8 - tools/ZydisPE.c | 1188 ----------------------------------------------- 2 files changed, 1196 deletions(-) delete mode 100644 tools/ZydisPE.c 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; -} - -/* ============================================================================================== */