dynarmic/tools/ZydisPE.c

1189 lines
42 KiB
C
Raw Normal View History

/***************************************************************************************************
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 <errno.h>
#include <inttypes.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <Zycore/LibC.h>
#include <Zycore/Vector.h>
#include <Zydis/Zydis.h>
// 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;
ZydisAddressWidth address_width;
switch (nt_headers->FileHeader.Machine)
{
case IMAGE_FILE_MACHINE_I386:
machine_mode = ZYDIS_MACHINE_MODE_LONG_COMPAT_32;
address_width = ZYDIS_ADDRESS_WIDTH_32;
break;
case IMAGE_FILE_MACHINE_IA64:
case IMAGE_FILE_MACHINE_AMD64:
machine_mode = ZYDIS_MACHINE_MODE_LONG_64;
address_width = ZYDIS_ADDRESS_WIDTH_64;
break;
default:
ZYAN_UNREACHABLE;
}
ZydisDecoder decoder;
if (!ZYAN_SUCCESS((status = ZydisDecoderInit(&decoder, machine_mode, address_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 <input file>\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;
}
/* ============================================================================================== */