| /* |
| * PE dumping utility |
| * |
| * Copyright 2001 Eric Pouech |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include "config.h" |
| #include "wine/port.h" |
| |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #ifdef HAVE_UNISTD_H |
| # include <unistd.h> |
| #endif |
| #include <time.h> |
| #ifdef HAVE_SYS_TYPES_H |
| # include <sys/types.h> |
| #endif |
| #ifdef HAVE_SYS_STAT_H |
| # include <sys/stat.h> |
| #endif |
| #ifdef HAVE_SYS_MMAN_H |
| #include <sys/mman.h> |
| #endif |
| #include <fcntl.h> |
| |
| #define NONAMELESSUNION |
| #define NONAMELESSSTRUCT |
| #include "windef.h" |
| #include "winbase.h" |
| #include "winedump.h" |
| |
| static const IMAGE_NT_HEADERS32* PE_nt_headers; |
| |
| const char *get_machine_str(int mach) |
| { |
| switch (mach) |
| { |
| case IMAGE_FILE_MACHINE_UNKNOWN: return "Unknown"; |
| case IMAGE_FILE_MACHINE_I860: return "i860"; |
| case IMAGE_FILE_MACHINE_I386: return "i386"; |
| case IMAGE_FILE_MACHINE_R3000: return "R3000"; |
| case IMAGE_FILE_MACHINE_R4000: return "R4000"; |
| case IMAGE_FILE_MACHINE_R10000: return "R10000"; |
| case IMAGE_FILE_MACHINE_ALPHA: return "Alpha"; |
| case IMAGE_FILE_MACHINE_POWERPC: return "PowerPC"; |
| case IMAGE_FILE_MACHINE_AMD64: return "AMD64"; |
| case IMAGE_FILE_MACHINE_IA64: return "IA64"; |
| case IMAGE_FILE_MACHINE_ARM64: return "ARM64"; |
| case IMAGE_FILE_MACHINE_ARM: return "ARM"; |
| case IMAGE_FILE_MACHINE_ARMNT: return "ARMNT"; |
| case IMAGE_FILE_MACHINE_THUMB: return "ARM Thumb"; |
| } |
| return "???"; |
| } |
| |
| static const void* RVA(unsigned long rva, unsigned long len) |
| { |
| IMAGE_SECTION_HEADER* sectHead; |
| int i; |
| |
| if (rva == 0) return NULL; |
| |
| sectHead = IMAGE_FIRST_SECTION(PE_nt_headers); |
| for (i = PE_nt_headers->FileHeader.NumberOfSections - 1; i >= 0; i--) |
| { |
| if (sectHead[i].VirtualAddress <= rva && |
| rva + len <= (DWORD)sectHead[i].VirtualAddress + sectHead[i].SizeOfRawData) |
| { |
| /* return image import directory offset */ |
| return PRD(sectHead[i].PointerToRawData + rva - sectHead[i].VirtualAddress, len); |
| } |
| } |
| |
| return NULL; |
| } |
| |
| static const IMAGE_NT_HEADERS32 *get_nt_header( void ) |
| { |
| const IMAGE_DOS_HEADER *dos; |
| dos = PRD(0, sizeof(*dos)); |
| if (!dos) return NULL; |
| return PRD(dos->e_lfanew, sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER)); |
| } |
| |
| static BOOL is_fake_dll( void ) |
| { |
| static const char fakedll_signature[] = "Wine placeholder DLL"; |
| const IMAGE_DOS_HEADER *dos; |
| |
| dos = PRD(0, sizeof(*dos) + sizeof(fakedll_signature)); |
| |
| if (dos && dos->e_lfanew >= sizeof(*dos) + sizeof(fakedll_signature) && |
| !memcmp( dos + 1, fakedll_signature, sizeof(fakedll_signature) )) return TRUE; |
| return FALSE; |
| } |
| |
| static const void *get_dir_and_size(unsigned int idx, unsigned int *size) |
| { |
| if(PE_nt_headers->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) |
| { |
| const IMAGE_OPTIONAL_HEADER64 *opt = (const IMAGE_OPTIONAL_HEADER64*)&PE_nt_headers->OptionalHeader; |
| if (idx >= opt->NumberOfRvaAndSizes) |
| return NULL; |
| if(size) |
| *size = opt->DataDirectory[idx].Size; |
| return RVA(opt->DataDirectory[idx].VirtualAddress, |
| opt->DataDirectory[idx].Size); |
| } |
| else |
| { |
| const IMAGE_OPTIONAL_HEADER32 *opt = (const IMAGE_OPTIONAL_HEADER32*)&PE_nt_headers->OptionalHeader; |
| if (idx >= opt->NumberOfRvaAndSizes) |
| return NULL; |
| if(size) |
| *size = opt->DataDirectory[idx].Size; |
| return RVA(opt->DataDirectory[idx].VirtualAddress, |
| opt->DataDirectory[idx].Size); |
| } |
| } |
| |
| static const void* get_dir(unsigned idx) |
| { |
| return get_dir_and_size(idx, 0); |
| } |
| |
| static const char * const DirectoryNames[16] = { |
| "EXPORT", "IMPORT", "RESOURCE", "EXCEPTION", |
| "SECURITY", "BASERELOC", "DEBUG", "ARCHITECTURE", |
| "GLOBALPTR", "TLS", "LOAD_CONFIG", "Bound IAT", |
| "IAT", "Delay IAT", "CLR Header", "" |
| }; |
| |
| static const char *get_magic_type(WORD magic) |
| { |
| switch(magic) { |
| case IMAGE_NT_OPTIONAL_HDR32_MAGIC: |
| return "32bit"; |
| case IMAGE_NT_OPTIONAL_HDR64_MAGIC: |
| return "64bit"; |
| case IMAGE_ROM_OPTIONAL_HDR_MAGIC: |
| return "ROM"; |
| } |
| return "???"; |
| } |
| |
| static inline void print_word(const char *title, WORD value) |
| { |
| printf(" %-34s 0x%-4X %u\n", title, value, value); |
| } |
| |
| static inline void print_dword(const char *title, DWORD value) |
| { |
| printf(" %-34s 0x%-8x %u\n", title, value, value); |
| } |
| |
| static inline void print_longlong(const char *title, ULONGLONG value) |
| { |
| printf(" %-34s 0x", title); |
| if(value >> 32) |
| printf("%lx%08lx\n", (unsigned long)(value >> 32), (unsigned long)value); |
| else |
| printf("%lx\n", (unsigned long)value); |
| } |
| |
| static inline void print_ver(const char *title, BYTE major, BYTE minor) |
| { |
| printf(" %-34s %u.%02u\n", title, major, minor); |
| } |
| |
| static inline void print_subsys(const char *title, WORD value) |
| { |
| const char *str; |
| switch (value) |
| { |
| default: |
| case IMAGE_SUBSYSTEM_UNKNOWN: str = "Unknown"; break; |
| case IMAGE_SUBSYSTEM_NATIVE: str = "Native"; break; |
| case IMAGE_SUBSYSTEM_WINDOWS_GUI: str = "Windows GUI"; break; |
| case IMAGE_SUBSYSTEM_WINDOWS_CUI: str = "Windows CUI"; break; |
| case IMAGE_SUBSYSTEM_OS2_CUI: str = "OS/2 CUI"; break; |
| case IMAGE_SUBSYSTEM_POSIX_CUI: str = "Posix CUI"; break; |
| case IMAGE_SUBSYSTEM_NATIVE_WINDOWS: str = "native Win9x driver"; break; |
| case IMAGE_SUBSYSTEM_WINDOWS_CE_GUI: str = "Windows CE GUI"; break; |
| case IMAGE_SUBSYSTEM_EFI_APPLICATION: str = "EFI application"; break; |
| case IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER: str = "EFI driver (boot)"; break; |
| case IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER: str = "EFI driver (runtime)"; break; |
| case IMAGE_SUBSYSTEM_EFI_ROM: str = "EFI ROM"; break; |
| case IMAGE_SUBSYSTEM_XBOX: str = "Xbox application"; break; |
| case IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION: str = "Boot application"; break; |
| } |
| printf(" %-34s 0x%X (%s)\n", title, value, str); |
| } |
| |
| static inline void print_dllflags(const char *title, WORD value) |
| { |
| printf(" %-34s 0x%X\n", title, value); |
| #define X(f,s) if (value & f) printf(" %s\n", s) |
| X(IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE, "DYNAMIC_BASE"); |
| X(IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY, "FORCE_INTEGRITY"); |
| X(IMAGE_DLLCHARACTERISTICS_NX_COMPAT, "NX_COMPAT"); |
| X(IMAGE_DLLCHARACTERISTICS_NO_ISOLATION, "NO_ISOLATION"); |
| X(IMAGE_DLLCHARACTERISTICS_NO_SEH, "NO_SEH"); |
| X(IMAGE_DLLCHARACTERISTICS_NO_BIND, "NO_BIND"); |
| X(IMAGE_DLLCHARACTERISTICS_WDM_DRIVER, "WDM_DRIVER"); |
| X(IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE, "TERMINAL_SERVER_AWARE"); |
| #undef X |
| } |
| |
| static inline void print_datadirectory(DWORD n, const IMAGE_DATA_DIRECTORY *directory) |
| { |
| unsigned i; |
| printf("Data Directory\n"); |
| |
| for (i = 0; i < n && i < 16; i++) |
| { |
| printf(" %-12s rva: 0x%-8x size: 0x%-8x\n", |
| DirectoryNames[i], directory[i].VirtualAddress, |
| directory[i].Size); |
| } |
| } |
| |
| static void dump_optional_header32(const IMAGE_OPTIONAL_HEADER32 *image_oh, UINT header_size) |
| { |
| IMAGE_OPTIONAL_HEADER32 oh; |
| const IMAGE_OPTIONAL_HEADER32 *optionalHeader; |
| |
| /* in case optional header is missing or partial */ |
| memset(&oh, 0, sizeof(oh)); |
| memcpy(&oh, image_oh, min(header_size, sizeof(oh))); |
| optionalHeader = &oh; |
| |
| print_word("Magic", optionalHeader->Magic); |
| print_ver("linker version", |
| optionalHeader->MajorLinkerVersion, optionalHeader->MinorLinkerVersion); |
| print_dword("size of code", optionalHeader->SizeOfCode); |
| print_dword("size of initialized data", optionalHeader->SizeOfInitializedData); |
| print_dword("size of uninitialized data", optionalHeader->SizeOfUninitializedData); |
| print_dword("entrypoint RVA", optionalHeader->AddressOfEntryPoint); |
| print_dword("base of code", optionalHeader->BaseOfCode); |
| print_dword("base of data", optionalHeader->BaseOfData); |
| print_dword("image base", optionalHeader->ImageBase); |
| print_dword("section align", optionalHeader->SectionAlignment); |
| print_dword("file align", optionalHeader->FileAlignment); |
| print_ver("required OS version", |
| optionalHeader->MajorOperatingSystemVersion, optionalHeader->MinorOperatingSystemVersion); |
| print_ver("image version", |
| optionalHeader->MajorImageVersion, optionalHeader->MinorImageVersion); |
| print_ver("subsystem version", |
| optionalHeader->MajorSubsystemVersion, optionalHeader->MinorSubsystemVersion); |
| print_dword("Win32 Version", optionalHeader->Win32VersionValue); |
| print_dword("size of image", optionalHeader->SizeOfImage); |
| print_dword("size of headers", optionalHeader->SizeOfHeaders); |
| print_dword("checksum", optionalHeader->CheckSum); |
| print_subsys("Subsystem", optionalHeader->Subsystem); |
| print_dllflags("DLL characteristics:", optionalHeader->DllCharacteristics); |
| print_dword("stack reserve size", optionalHeader->SizeOfStackReserve); |
| print_dword("stack commit size", optionalHeader->SizeOfStackCommit); |
| print_dword("heap reserve size", optionalHeader->SizeOfHeapReserve); |
| print_dword("heap commit size", optionalHeader->SizeOfHeapCommit); |
| print_dword("loader flags", optionalHeader->LoaderFlags); |
| print_dword("RVAs & sizes", optionalHeader->NumberOfRvaAndSizes); |
| printf("\n"); |
| print_datadirectory(optionalHeader->NumberOfRvaAndSizes, optionalHeader->DataDirectory); |
| printf("\n"); |
| } |
| |
| static void dump_optional_header64(const IMAGE_OPTIONAL_HEADER64 *image_oh, UINT header_size) |
| { |
| IMAGE_OPTIONAL_HEADER64 oh; |
| const IMAGE_OPTIONAL_HEADER64 *optionalHeader; |
| |
| /* in case optional header is missing or partial */ |
| memset(&oh, 0, sizeof(oh)); |
| memcpy(&oh, image_oh, min(header_size, sizeof(oh))); |
| optionalHeader = &oh; |
| |
| print_word("Magic", optionalHeader->Magic); |
| print_ver("linker version", |
| optionalHeader->MajorLinkerVersion, optionalHeader->MinorLinkerVersion); |
| print_dword("size of code", optionalHeader->SizeOfCode); |
| print_dword("size of initialized data", optionalHeader->SizeOfInitializedData); |
| print_dword("size of uninitialized data", optionalHeader->SizeOfUninitializedData); |
| print_dword("entrypoint RVA", optionalHeader->AddressOfEntryPoint); |
| print_dword("base of code", optionalHeader->BaseOfCode); |
| print_longlong("image base", optionalHeader->ImageBase); |
| print_dword("section align", optionalHeader->SectionAlignment); |
| print_dword("file align", optionalHeader->FileAlignment); |
| print_ver("required OS version", |
| optionalHeader->MajorOperatingSystemVersion, optionalHeader->MinorOperatingSystemVersion); |
| print_ver("image version", |
| optionalHeader->MajorImageVersion, optionalHeader->MinorImageVersion); |
| print_ver("subsystem version", |
| optionalHeader->MajorSubsystemVersion, optionalHeader->MinorSubsystemVersion); |
| print_dword("Win32 Version", optionalHeader->Win32VersionValue); |
| print_dword("size of image", optionalHeader->SizeOfImage); |
| print_dword("size of headers", optionalHeader->SizeOfHeaders); |
| print_dword("checksum", optionalHeader->CheckSum); |
| print_subsys("Subsystem", optionalHeader->Subsystem); |
| print_dllflags("DLL characteristics:", optionalHeader->DllCharacteristics); |
| print_longlong("stack reserve size", optionalHeader->SizeOfStackReserve); |
| print_longlong("stack commit size", optionalHeader->SizeOfStackCommit); |
| print_longlong("heap reserve size", optionalHeader->SizeOfHeapReserve); |
| print_longlong("heap commit size", optionalHeader->SizeOfHeapCommit); |
| print_dword("loader flags", optionalHeader->LoaderFlags); |
| print_dword("RVAs & sizes", optionalHeader->NumberOfRvaAndSizes); |
| printf("\n"); |
| print_datadirectory(optionalHeader->NumberOfRvaAndSizes, optionalHeader->DataDirectory); |
| printf("\n"); |
| } |
| |
| void dump_optional_header(const IMAGE_OPTIONAL_HEADER32 *optionalHeader, UINT header_size) |
| { |
| printf("Optional Header (%s)\n", get_magic_type(optionalHeader->Magic)); |
| |
| switch(optionalHeader->Magic) { |
| case IMAGE_NT_OPTIONAL_HDR32_MAGIC: |
| dump_optional_header32(optionalHeader, header_size); |
| break; |
| case IMAGE_NT_OPTIONAL_HDR64_MAGIC: |
| dump_optional_header64((const IMAGE_OPTIONAL_HEADER64 *)optionalHeader, header_size); |
| break; |
| default: |
| printf(" Unknown optional header magic: 0x%-4X\n", optionalHeader->Magic); |
| break; |
| } |
| } |
| |
| void dump_file_header(const IMAGE_FILE_HEADER *fileHeader) |
| { |
| printf("File Header\n"); |
| |
| printf(" Machine: %04X (%s)\n", |
| fileHeader->Machine, get_machine_str(fileHeader->Machine)); |
| printf(" Number of Sections: %d\n", fileHeader->NumberOfSections); |
| printf(" TimeDateStamp: %08X (%s) offset %lu\n", |
| fileHeader->TimeDateStamp, get_time_str(fileHeader->TimeDateStamp), |
| Offset(&(fileHeader->TimeDateStamp))); |
| printf(" PointerToSymbolTable: %08X\n", fileHeader->PointerToSymbolTable); |
| printf(" NumberOfSymbols: %08X\n", fileHeader->NumberOfSymbols); |
| printf(" SizeOfOptionalHeader: %04X\n", fileHeader->SizeOfOptionalHeader); |
| printf(" Characteristics: %04X\n", fileHeader->Characteristics); |
| #define X(f,s) if (fileHeader->Characteristics & f) printf(" %s\n", s) |
| X(IMAGE_FILE_RELOCS_STRIPPED, "RELOCS_STRIPPED"); |
| X(IMAGE_FILE_EXECUTABLE_IMAGE, "EXECUTABLE_IMAGE"); |
| X(IMAGE_FILE_LINE_NUMS_STRIPPED, "LINE_NUMS_STRIPPED"); |
| X(IMAGE_FILE_LOCAL_SYMS_STRIPPED, "LOCAL_SYMS_STRIPPED"); |
| X(IMAGE_FILE_AGGRESIVE_WS_TRIM, "AGGRESIVE_WS_TRIM"); |
| X(IMAGE_FILE_LARGE_ADDRESS_AWARE, "LARGE_ADDRESS_AWARE"); |
| X(IMAGE_FILE_16BIT_MACHINE, "16BIT_MACHINE"); |
| X(IMAGE_FILE_BYTES_REVERSED_LO, "BYTES_REVERSED_LO"); |
| X(IMAGE_FILE_32BIT_MACHINE, "32BIT_MACHINE"); |
| X(IMAGE_FILE_DEBUG_STRIPPED, "DEBUG_STRIPPED"); |
| X(IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP, "REMOVABLE_RUN_FROM_SWAP"); |
| X(IMAGE_FILE_NET_RUN_FROM_SWAP, "NET_RUN_FROM_SWAP"); |
| X(IMAGE_FILE_SYSTEM, "SYSTEM"); |
| X(IMAGE_FILE_DLL, "DLL"); |
| X(IMAGE_FILE_UP_SYSTEM_ONLY, "UP_SYSTEM_ONLY"); |
| X(IMAGE_FILE_BYTES_REVERSED_HI, "BYTES_REVERSED_HI"); |
| #undef X |
| printf("\n"); |
| } |
| |
| static void dump_pe_header(void) |
| { |
| dump_file_header(&PE_nt_headers->FileHeader); |
| dump_optional_header((const IMAGE_OPTIONAL_HEADER32*)&PE_nt_headers->OptionalHeader, PE_nt_headers->FileHeader.SizeOfOptionalHeader); |
| } |
| |
| void dump_section(const IMAGE_SECTION_HEADER *sectHead, const char* strtable) |
| { |
| unsigned offset; |
| |
| /* long section name ? */ |
| if (strtable && sectHead->Name[0] == '/' && |
| ((offset = atoi((const char*)sectHead->Name + 1)) < *(const DWORD*)strtable)) |
| printf(" %.8s (%s)", sectHead->Name, strtable + offset); |
| else |
| printf(" %-8.8s", sectHead->Name); |
| printf(" VirtSize: 0x%08x VirtAddr: 0x%08x\n", |
| sectHead->Misc.VirtualSize, sectHead->VirtualAddress); |
| printf(" raw data offs: 0x%08x raw data size: 0x%08x\n", |
| sectHead->PointerToRawData, sectHead->SizeOfRawData); |
| printf(" relocation offs: 0x%08x relocations: 0x%08x\n", |
| sectHead->PointerToRelocations, sectHead->NumberOfRelocations); |
| printf(" line # offs: %-8u line #'s: %-8u\n", |
| sectHead->PointerToLinenumbers, sectHead->NumberOfLinenumbers); |
| printf(" characteristics: 0x%08x\n", sectHead->Characteristics); |
| printf(" "); |
| #define X(b,s) if (sectHead->Characteristics & b) printf(" " s) |
| /* #define IMAGE_SCN_TYPE_REG 0x00000000 - Reserved */ |
| /* #define IMAGE_SCN_TYPE_DSECT 0x00000001 - Reserved */ |
| /* #define IMAGE_SCN_TYPE_NOLOAD 0x00000002 - Reserved */ |
| /* #define IMAGE_SCN_TYPE_GROUP 0x00000004 - Reserved */ |
| /* #define IMAGE_SCN_TYPE_NO_PAD 0x00000008 - Reserved */ |
| /* #define IMAGE_SCN_TYPE_COPY 0x00000010 - Reserved */ |
| |
| X(IMAGE_SCN_CNT_CODE, "CODE"); |
| X(IMAGE_SCN_CNT_INITIALIZED_DATA, "INITIALIZED_DATA"); |
| X(IMAGE_SCN_CNT_UNINITIALIZED_DATA, "UNINITIALIZED_DATA"); |
| |
| X(IMAGE_SCN_LNK_OTHER, "LNK_OTHER"); |
| X(IMAGE_SCN_LNK_INFO, "LNK_INFO"); |
| /* #define IMAGE_SCN_TYPE_OVER 0x00000400 - Reserved */ |
| X(IMAGE_SCN_LNK_REMOVE, "LNK_REMOVE"); |
| X(IMAGE_SCN_LNK_COMDAT, "LNK_COMDAT"); |
| |
| /* 0x00002000 - Reserved */ |
| /* #define IMAGE_SCN_MEM_PROTECTED 0x00004000 - Obsolete */ |
| X(IMAGE_SCN_MEM_FARDATA, "MEM_FARDATA"); |
| |
| /* #define IMAGE_SCN_MEM_SYSHEAP 0x00010000 - Obsolete */ |
| X(IMAGE_SCN_MEM_PURGEABLE, "MEM_PURGEABLE"); |
| X(IMAGE_SCN_MEM_16BIT, "MEM_16BIT"); |
| X(IMAGE_SCN_MEM_LOCKED, "MEM_LOCKED"); |
| X(IMAGE_SCN_MEM_PRELOAD, "MEM_PRELOAD"); |
| |
| switch (sectHead->Characteristics & IMAGE_SCN_ALIGN_MASK) |
| { |
| #define X2(b,s) case b: printf(" " s); break |
| X2(IMAGE_SCN_ALIGN_1BYTES, "ALIGN_1BYTES"); |
| X2(IMAGE_SCN_ALIGN_2BYTES, "ALIGN_2BYTES"); |
| X2(IMAGE_SCN_ALIGN_4BYTES, "ALIGN_4BYTES"); |
| X2(IMAGE_SCN_ALIGN_8BYTES, "ALIGN_8BYTES"); |
| X2(IMAGE_SCN_ALIGN_16BYTES, "ALIGN_16BYTES"); |
| X2(IMAGE_SCN_ALIGN_32BYTES, "ALIGN_32BYTES"); |
| X2(IMAGE_SCN_ALIGN_64BYTES, "ALIGN_64BYTES"); |
| X2(IMAGE_SCN_ALIGN_128BYTES, "ALIGN_128BYTES"); |
| X2(IMAGE_SCN_ALIGN_256BYTES, "ALIGN_256BYTES"); |
| X2(IMAGE_SCN_ALIGN_512BYTES, "ALIGN_512BYTES"); |
| X2(IMAGE_SCN_ALIGN_1024BYTES, "ALIGN_1024BYTES"); |
| X2(IMAGE_SCN_ALIGN_2048BYTES, "ALIGN_2048BYTES"); |
| X2(IMAGE_SCN_ALIGN_4096BYTES, "ALIGN_4096BYTES"); |
| X2(IMAGE_SCN_ALIGN_8192BYTES, "ALIGN_8192BYTES"); |
| #undef X2 |
| } |
| |
| X(IMAGE_SCN_LNK_NRELOC_OVFL, "LNK_NRELOC_OVFL"); |
| |
| X(IMAGE_SCN_MEM_DISCARDABLE, "MEM_DISCARDABLE"); |
| X(IMAGE_SCN_MEM_NOT_CACHED, "MEM_NOT_CACHED"); |
| X(IMAGE_SCN_MEM_NOT_PAGED, "MEM_NOT_PAGED"); |
| X(IMAGE_SCN_MEM_SHARED, "MEM_SHARED"); |
| X(IMAGE_SCN_MEM_EXECUTE, "MEM_EXECUTE"); |
| X(IMAGE_SCN_MEM_READ, "MEM_READ"); |
| X(IMAGE_SCN_MEM_WRITE, "MEM_WRITE"); |
| #undef X |
| printf("\n\n"); |
| } |
| |
| static void dump_sections(const void *base, const void* addr, unsigned num_sect) |
| { |
| const IMAGE_SECTION_HEADER* sectHead = addr; |
| unsigned i; |
| const char* strtable; |
| |
| if (PE_nt_headers->FileHeader.PointerToSymbolTable && PE_nt_headers->FileHeader.NumberOfSymbols) |
| { |
| strtable = (const char*)base + |
| PE_nt_headers->FileHeader.PointerToSymbolTable + |
| PE_nt_headers->FileHeader.NumberOfSymbols * sizeof(IMAGE_SYMBOL); |
| } |
| else strtable = NULL; |
| |
| printf("Section Table\n"); |
| for (i = 0; i < num_sect; i++, sectHead++) |
| { |
| dump_section(sectHead, strtable); |
| |
| if (globals.do_dump_rawdata) |
| { |
| dump_data((const unsigned char *)base + sectHead->PointerToRawData, sectHead->SizeOfRawData, " " ); |
| printf("\n"); |
| } |
| } |
| } |
| |
| static void dump_dir_exported_functions(void) |
| { |
| unsigned int size = 0; |
| const IMAGE_EXPORT_DIRECTORY*exportDir = get_dir_and_size(IMAGE_FILE_EXPORT_DIRECTORY, &size); |
| unsigned int i; |
| const DWORD* pFunc; |
| const DWORD* pName; |
| const WORD* pOrdl; |
| DWORD* funcs; |
| |
| if (!exportDir) return; |
| |
| printf("Exports table:\n"); |
| printf("\n"); |
| printf(" Name: %s\n", (const char*)RVA(exportDir->Name, sizeof(DWORD))); |
| printf(" Characteristics: %08x\n", exportDir->Characteristics); |
| printf(" TimeDateStamp: %08X %s\n", |
| exportDir->TimeDateStamp, get_time_str(exportDir->TimeDateStamp)); |
| printf(" Version: %u.%02u\n", exportDir->MajorVersion, exportDir->MinorVersion); |
| printf(" Ordinal base: %u\n", exportDir->Base); |
| printf(" # of functions: %u\n", exportDir->NumberOfFunctions); |
| printf(" # of Names: %u\n", exportDir->NumberOfNames); |
| printf("Addresses of functions: %08X\n", exportDir->AddressOfFunctions); |
| printf("Addresses of name ordinals: %08X\n", exportDir->AddressOfNameOrdinals); |
| printf("Addresses of names: %08X\n", exportDir->AddressOfNames); |
| printf("\n"); |
| printf(" Entry Pt Ordn Name\n"); |
| |
| pFunc = RVA(exportDir->AddressOfFunctions, exportDir->NumberOfFunctions * sizeof(DWORD)); |
| if (!pFunc) {printf("Can't grab functions' address table\n"); return;} |
| pName = RVA(exportDir->AddressOfNames, exportDir->NumberOfNames * sizeof(DWORD)); |
| pOrdl = RVA(exportDir->AddressOfNameOrdinals, exportDir->NumberOfNames * sizeof(WORD)); |
| |
| funcs = calloc( exportDir->NumberOfFunctions, sizeof(*funcs) ); |
| if (!funcs) fatal("no memory"); |
| |
| for (i = 0; i < exportDir->NumberOfNames; i++) funcs[pOrdl[i]] = pName[i]; |
| |
| for (i = 0; i < exportDir->NumberOfFunctions; i++) |
| { |
| if (!pFunc[i]) continue; |
| printf(" %08X %5u ", pFunc[i], exportDir->Base + i); |
| if (funcs[i]) |
| printf("%s", get_symbol_str((const char*)RVA(funcs[i], sizeof(DWORD)))); |
| else |
| printf("<by ordinal>"); |
| |
| /* check for forwarded function */ |
| if ((const char *)RVA(pFunc[i],1) >= (const char *)exportDir && |
| (const char *)RVA(pFunc[i],1) < (const char *)exportDir + size) |
| printf(" (-> %s)", (const char *)RVA(pFunc[i],1)); |
| printf("\n"); |
| } |
| free(funcs); |
| printf("\n"); |
| } |
| |
| |
| struct runtime_function_x86_64 |
| { |
| DWORD BeginAddress; |
| DWORD EndAddress; |
| DWORD UnwindData; |
| }; |
| |
| struct runtime_function_armnt |
| { |
| DWORD BeginAddress; |
| union { |
| DWORD UnwindData; |
| struct { |
| DWORD Flag : 2; |
| DWORD FunctionLength : 11; |
| DWORD Ret : 2; |
| DWORD H : 1; |
| DWORD Reg : 3; |
| DWORD R : 1; |
| DWORD L : 1; |
| DWORD C : 1; |
| DWORD StackAdjust : 10; |
| } DUMMYSTRUCTNAME; |
| } DUMMYUNIONNAME; |
| }; |
| |
| union handler_data |
| { |
| struct runtime_function_x86_64 chain; |
| DWORD handler; |
| }; |
| |
| struct opcode |
| { |
| BYTE offset; |
| BYTE code : 4; |
| BYTE info : 4; |
| }; |
| |
| struct unwind_info_x86_64 |
| { |
| BYTE version : 3; |
| BYTE flags : 5; |
| BYTE prolog; |
| BYTE count; |
| BYTE frame_reg : 4; |
| BYTE frame_offset : 4; |
| struct opcode opcodes[1]; /* count entries */ |
| /* followed by union handler_data */ |
| }; |
| |
| struct unwind_info_armnt |
| { |
| DWORD function_length : 18; |
| DWORD version : 2; |
| DWORD x : 1; |
| DWORD e : 1; |
| DWORD f : 1; |
| DWORD count : 5; |
| DWORD words : 4; |
| }; |
| |
| struct unwind_info_ext_armnt |
| { |
| WORD excount; |
| BYTE exwords; |
| BYTE reserved; |
| }; |
| |
| struct unwind_info_epilogue_armnt |
| { |
| DWORD offset : 18; |
| DWORD res : 2; |
| DWORD cond : 4; |
| DWORD index : 8; |
| }; |
| |
| #define UWOP_PUSH_NONVOL 0 |
| #define UWOP_ALLOC_LARGE 1 |
| #define UWOP_ALLOC_SMALL 2 |
| #define UWOP_SET_FPREG 3 |
| #define UWOP_SAVE_NONVOL 4 |
| #define UWOP_SAVE_NONVOL_FAR 5 |
| #define UWOP_SAVE_XMM128 8 |
| #define UWOP_SAVE_XMM128_FAR 9 |
| #define UWOP_PUSH_MACHFRAME 10 |
| |
| #define UNW_FLAG_EHANDLER 1 |
| #define UNW_FLAG_UHANDLER 2 |
| #define UNW_FLAG_CHAININFO 4 |
| |
| static void dump_x86_64_unwind_info( const struct runtime_function_x86_64 *function ) |
| { |
| static const char * const reg_names[16] = |
| { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", |
| "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" }; |
| |
| const union handler_data *handler_data; |
| const struct unwind_info_x86_64 *info; |
| unsigned int i, count; |
| |
| printf( "\nFunction %08x-%08x:\n", function->BeginAddress, function->EndAddress ); |
| if (function->UnwindData & 1) |
| { |
| const struct runtime_function_x86_64 *next = RVA( function->UnwindData & ~1, sizeof(*next) ); |
| printf( " -> function %08x-%08x\n", next->BeginAddress, next->EndAddress ); |
| return; |
| } |
| info = RVA( function->UnwindData, sizeof(*info) ); |
| |
| printf( " unwind info at %08x\n", function->UnwindData ); |
| if (info->version != 1) |
| { |
| printf( " *** unknown version %u\n", info->version ); |
| return; |
| } |
| printf( " flags %x", info->flags ); |
| if (info->flags & UNW_FLAG_EHANDLER) printf( " EHANDLER" ); |
| if (info->flags & UNW_FLAG_UHANDLER) printf( " UHANDLER" ); |
| if (info->flags & UNW_FLAG_CHAININFO) printf( " CHAININFO" ); |
| printf( "\n prolog 0x%x bytes\n", info->prolog ); |
| |
| if (info->frame_reg) |
| printf( " frame register %s offset 0x%x(%%rsp)\n", |
| reg_names[info->frame_reg], info->frame_offset * 16 ); |
| |
| for (i = 0; i < info->count; i++) |
| { |
| printf( " 0x%02x: ", info->opcodes[i].offset ); |
| switch (info->opcodes[i].code) |
| { |
| case UWOP_PUSH_NONVOL: |
| printf( "push %%%s\n", reg_names[info->opcodes[i].info] ); |
| break; |
| case UWOP_ALLOC_LARGE: |
| if (info->opcodes[i].info) |
| { |
| count = *(const DWORD *)&info->opcodes[i+1]; |
| i += 2; |
| } |
| else |
| { |
| count = *(const USHORT *)&info->opcodes[i+1] * 8; |
| i++; |
| } |
| printf( "sub $0x%x,%%rsp\n", count ); |
| break; |
| case UWOP_ALLOC_SMALL: |
| count = (info->opcodes[i].info + 1) * 8; |
| printf( "sub $0x%x,%%rsp\n", count ); |
| break; |
| case UWOP_SET_FPREG: |
| printf( "lea 0x%x(%%rsp),%s\n", |
| info->frame_offset * 16, reg_names[info->frame_reg] ); |
| break; |
| case UWOP_SAVE_NONVOL: |
| count = *(const USHORT *)&info->opcodes[i+1] * 8; |
| printf( "mov %%%s,0x%x(%%rsp)\n", reg_names[info->opcodes[i].info], count ); |
| i++; |
| break; |
| case UWOP_SAVE_NONVOL_FAR: |
| count = *(const DWORD *)&info->opcodes[i+1]; |
| printf( "mov %%%s,0x%x(%%rsp)\n", reg_names[info->opcodes[i].info], count ); |
| i += 2; |
| break; |
| case UWOP_SAVE_XMM128: |
| count = *(const USHORT *)&info->opcodes[i+1] * 16; |
| printf( "movaps %%xmm%u,0x%x(%%rsp)\n", info->opcodes[i].info, count ); |
| i++; |
| break; |
| case UWOP_SAVE_XMM128_FAR: |
| count = *(const DWORD *)&info->opcodes[i+1]; |
| printf( "movaps %%xmm%u,0x%x(%%rsp)\n", info->opcodes[i].info, count ); |
| i += 2; |
| break; |
| case UWOP_PUSH_MACHFRAME: |
| printf( "PUSH_MACHFRAME %u\n", info->opcodes[i].info ); |
| break; |
| default: |
| printf( "*** unknown code %u\n", info->opcodes[i].code ); |
| break; |
| } |
| } |
| |
| handler_data = (const union handler_data *)&info->opcodes[(info->count + 1) & ~1]; |
| if (info->flags & UNW_FLAG_CHAININFO) |
| { |
| printf( " -> function %08x-%08x\n", |
| handler_data->chain.BeginAddress, handler_data->chain.EndAddress ); |
| return; |
| } |
| if (info->flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER)) |
| printf( " handler %08x data at %08x\n", handler_data->handler, |
| (ULONG)(function->UnwindData + (const char *)(&handler_data->handler + 1) - (const char *)info )); |
| } |
| |
| static void dump_armnt_unwind_info( const struct runtime_function_armnt *fnc ) |
| { |
| const struct unwind_info_armnt *info; |
| const struct unwind_info_ext_armnt *infoex; |
| const struct unwind_info_epilogue_armnt *infoepi; |
| unsigned int rva; |
| WORD i, count = 0, words = 0; |
| |
| if (fnc->u.s.Flag) |
| { |
| char intregs[32] = {0}, intregspop[32] = {0}, vfpregs[32] = {0}; |
| WORD pf = 0, ef = 0, sc = 0; |
| |
| printf( "\nFunction %08x-%08x:\n", fnc->BeginAddress & ~1, |
| (fnc->BeginAddress & ~1) + fnc->u.s.FunctionLength * 2 ); |
| printf( " Flag %x\n", fnc->u.s.Flag ); |
| printf( " FunctionLength %x\n", fnc->u.s.FunctionLength ); |
| printf( " Ret %x\n", fnc->u.s.Ret ); |
| printf( " H %x\n", fnc->u.s.H ); |
| printf( " Reg %x\n", fnc->u.s.Reg ); |
| printf( " R %x\n", fnc->u.s.R ); |
| printf( " L %x\n", fnc->u.s.L ); |
| printf( " C %x\n", fnc->u.s.C ); |
| printf( " StackAdjust %x\n", fnc->u.s.StackAdjust ); |
| |
| if (fnc->u.s.StackAdjust >= 0x03f4) |
| { |
| pf = fnc->u.s.StackAdjust & 0x04; |
| ef = fnc->u.s.StackAdjust & 0x08; |
| } |
| |
| if (!fnc->u.s.R && !pf) |
| { |
| if (fnc->u.s.Reg) |
| { |
| sprintf(intregs, "r4-r%u", fnc->u.s.Reg + 4); |
| sprintf(intregspop, "r4-r%u", fnc->u.s.Reg + 4); |
| } |
| else |
| { |
| strcpy(intregs, "r4"); |
| strcpy(intregspop, "r4"); |
| } |
| sc = fnc->u.s.Reg + 1; |
| if (fnc->u.s.C || fnc->u.s.L) |
| { |
| strcat(intregs, ", "); |
| if (fnc->u.s.C || (fnc->u.s.L && !fnc->u.s.H)) |
| strcat(intregspop, ", "); |
| } |
| } |
| else if (fnc->u.s.R && pf) |
| { |
| if (((~fnc->u.s.StackAdjust) & 3) != 3) |
| { |
| sprintf(intregs, "r%u-r3", (~fnc->u.s.StackAdjust) & 3); |
| sprintf(intregspop, "r%u-r3", (~fnc->u.s.StackAdjust) & 3); |
| } |
| else |
| { |
| sprintf(intregs, "r3"); |
| sprintf(intregspop, "r3"); |
| } |
| sc = 4 - ((~fnc->u.s.StackAdjust) & 3); |
| if (fnc->u.s.C || fnc->u.s.L) |
| { |
| strcat(intregs, ", "); |
| if (fnc->u.s.C || (fnc->u.s.L && !fnc->u.s.H)) |
| strcat(intregspop, ", "); |
| } |
| } |
| else if (!fnc->u.s.R && pf) |
| { |
| sprintf(intregs, "r%u-r%u", (~fnc->u.s.StackAdjust) & 3, fnc->u.s.Reg + 4); |
| sprintf(intregspop, "r%u-r%u", (~fnc->u.s.StackAdjust) & 3, fnc->u.s.Reg + 4); |
| sc = fnc->u.s.Reg + 5 - ((~fnc->u.s.StackAdjust) & 3); |
| if (fnc->u.s.C || fnc->u.s.L) |
| { |
| strcat(intregs, ", "); |
| if (fnc->u.s.C || (fnc->u.s.L && !fnc->u.s.H)) |
| strcat(intregspop, ", "); |
| } |
| } |
| else if (fnc->u.s.R && !pf) |
| { |
| if (!fnc->u.s.C && !fnc->u.s.L) |
| { |
| strcpy(intregs, "none"); |
| strcpy(intregspop, "none"); |
| } |
| } |
| |
| if (fnc->u.s.C && !fnc->u.s.L) |
| { |
| strcat(intregs, "r11"); |
| strcat(intregspop, "r11"); |
| } |
| else if (fnc->u.s.C && fnc->u.s.L) |
| { |
| strcat(intregs, "r11, lr"); |
| if (fnc->u.s.H) |
| strcat(intregspop, "r11"); |
| else |
| strcat(intregspop, "r11, pc"); |
| } |
| else if (!fnc->u.s.C && fnc->u.s.L) |
| { |
| strcat(intregs, "lr"); |
| if (!fnc->u.s.H) |
| strcat(intregspop, "pc"); |
| } |
| |
| if (fnc->u.s.R) |
| { |
| if (fnc->u.s.Reg) |
| sprintf(vfpregs, "d8-d%u", fnc->u.s.Reg + 8); |
| else |
| strcpy(vfpregs, "d8"); |
| } |
| else |
| strcpy(vfpregs, "none"); |
| |
| if (fnc->u.s.H) |
| printf( " Unwind Code\tpush {r0-r3}\n" ); |
| |
| if (fnc->u.s.R || fnc->u.s.L || fnc->u.s.C || pf) |
| printf( " Unwind Code\tpush {%s}\n", intregs ); |
| |
| if (fnc->u.s.C && fnc->u.s.R && !fnc->u.s.L && !pf) |
| printf( " Unwind Code\tmov r11, sp\n" ); |
| else if (fnc->u.s.C && (!fnc->u.s.R || fnc->u.s.L || pf)) |
| { |
| if (fnc->u.s.StackAdjust >= 0x03f4 && !sc) |
| printf( " Unwind Code\tadd r11, sp, #<unknown>\n"); |
| else if (fnc->u.s.StackAdjust >= 0x03f4) |
| printf( " Unwind Code\tadd r11, sp, #%d\n", sc * 4 ); |
| else |
| printf( " Unwind Code\tadd r11, sp, #%d\n", fnc->u.s.StackAdjust * 4 ); |
| } |
| |
| if (fnc->u.s.R && fnc->u.s.Reg != 0x07) |
| printf( " Unwind Code\tvpush {%s}\n", vfpregs ); |
| |
| if (fnc->u.s.StackAdjust < 0x03f4 && !pf) |
| printf( " Unwind Code\tsub sp, sp, #%d\n", fnc->u.s.StackAdjust * 4 ); |
| |
| |
| if (fnc->u.s.StackAdjust < 0x03f4 && !ef) |
| printf( " Unwind Code\tadd sp, sp, #%d\n", fnc->u.s.StackAdjust * 4 ); |
| |
| if (fnc->u.s.R && fnc->u.s.Reg != 0x07) |
| printf( " Unwind Code\tvpop {%s}\n", vfpregs ); |
| |
| if (fnc->u.s.C || !fnc->u.s.R || ef || (fnc->u.s.L && !fnc->u.s.H)) |
| printf( " Unwind Code\tpop {%s}\n", intregspop ); |
| |
| if (fnc->u.s.H && !fnc->u.s.L) |
| printf( " Unwind Code\tadd sp, sp, #16\n" ); |
| else if (fnc->u.s.H && fnc->u.s.L) |
| printf( " Unwind Code\tldr pc, [sp], #20\n" ); |
| |
| if (fnc->u.s.Ret == 1) |
| printf( " Unwind Code\tbx <reg>\n" ); |
| else if (fnc->u.s.Ret == 2) |
| printf( " Unwind Code\tb <address>\n" ); |
| |
| return; |
| } |
| |
| info = RVA( fnc->u.UnwindData, sizeof(*info) ); |
| rva = fnc->u.UnwindData + sizeof(*info); |
| count = info->count; |
| words = info->words; |
| |
| printf( "\nFunction %08x-%08x:\n", fnc->BeginAddress & ~1, |
| (fnc->BeginAddress & ~1) + info->function_length * 2 ); |
| printf( " unwind info at %08x\n", fnc->u.UnwindData ); |
| printf( " Flag %x\n", fnc->u.s.Flag ); |
| printf( " FunctionLength %x\n", info->function_length ); |
| printf( " Version %x\n", info->version ); |
| printf( " X %x\n", info->x ); |
| printf( " E %x\n", info->e ); |
| printf( " F %x\n", info->f ); |
| printf( " Count %x\n", count ); |
| printf( " Words %x\n", words ); |
| |
| if (!info->count && !info->words) |
| { |
| infoex = RVA( rva, sizeof(*infoex) ); |
| rva = rva + sizeof(*infoex); |
| count = infoex->excount; |
| words = infoex->exwords; |
| printf( " ExtCount %x\n", count ); |
| printf( " ExtWords %x\n", words ); |
| } |
| |
| if (!info->e) |
| { |
| infoepi = RVA( rva, count * sizeof(*infoepi) ); |
| rva = rva + count * sizeof(*infoepi); |
| |
| for (i = 0; i < count; i++) |
| { |
| printf( " Epilogue Scope %x\n", i ); |
| printf( " Offset %x\n", infoepi[i].offset ); |
| printf( " Reserved %x\n", infoepi[i].res ); |
| printf( " Condition %x\n", infoepi[i].cond ); |
| printf( " Index %x\n", infoepi[i].index ); |
| } |
| } |
| else |
| infoepi = NULL; |
| |
| if (words) |
| { |
| const unsigned int *codes; |
| BYTE b, *bytes; |
| BOOL inepilogue = FALSE; |
| |
| codes = RVA( rva, words * sizeof(*codes) ); |
| rva = rva + words * sizeof(*codes); |
| bytes = (BYTE*)codes; |
| |
| for (b = 0; b < words * sizeof(*codes); b++) |
| { |
| BYTE code = bytes[b]; |
| |
| if (info->e && b == count) |
| { |
| printf( "Epilogue:\n" ); |
| inepilogue = TRUE; |
| } |
| else if (!info->e && infoepi) |
| { |
| for (i = 0; i < count; i++) |
| if (b == infoepi[i].index) |
| { |
| printf( "Epilogue from Scope %x at %08x:\n", i, |
| (fnc->BeginAddress & ~1) + infoepi[i].offset * 2 ); |
| inepilogue = TRUE; |
| } |
| } |
| |
| printf( " Unwind Code %x\t", code ); |
| |
| if (code == 0x00) |
| printf( "\n" ); |
| else if (code <= 0x7f) |
| printf( "%s sp, sp, #%u\n", inepilogue ? "add" : "sub", code * 4 ); |
| else if (code <= 0xbf) |
| { |
| WORD excode, f; |
| BOOL first = TRUE; |
| BYTE excodes = bytes[++b]; |
| |
| excode = (code << 8) | excodes; |
| printf( "%s {", inepilogue ? "pop" : "push" ); |
| |
| for (f = 0; f <= 12; f++) |
| { |
| if ((excode >> f) & 1) |
| { |
| printf( "%sr%u", first ? "" : ", ", f ); |
| first = FALSE; |
| } |
| } |
| |
| if (excode & 0x2000) |
| printf( "%s%s", first ? "" : ", ", inepilogue ? "pc" : "lr" ); |
| |
| printf( "}\n" ); |
| } |
| else if (code <= 0xcf) |
| if (inepilogue) |
| printf( "mov sp, r%u\n", code & 0x0f ); |
| else |
| printf( "mov r%u, sp\n", code & 0x0f ); |
| else if (code <= 0xd7) |
| if (inepilogue) |
| printf( "pop {r4-r%u%s}\n", (code & 0x03) + 4, (code & 0x04) ? ", pc" : "" ); |
| else |
| printf( "push {r4-r%u%s}\n", (code & 0x03) + 4, (code & 0x04) ? ", lr" : "" ); |
| else if (code <= 0xdf) |
| if (inepilogue) |
| printf( "pop {r4-r%u%s}\n", (code & 0x03) + 8, (code & 0x04) ? ", pc" : "" ); |
| else |
| printf( "push {r4-r%u%s}\n", (code & 0x03) + 8, (code & 0x04) ? ", lr" : "" ); |
| else if (code <= 0xe7) |
| printf( "%s {d8-d%u}\n", inepilogue ? "vpop" : "vpush", (code & 0x07) + 8 ); |
| else if (code <= 0xeb) |
| { |
| WORD excode; |
| BYTE excodes = bytes[++b]; |
| |
| excode = (code << 8) | excodes; |
| printf( "%s sp, sp, #%u\n", inepilogue ? "addw" : "subw", (excode & 0x03ff) *4 ); |
| } |
| else if (code <= 0xed) |
| { |
| WORD excode, f; |
| BOOL first = TRUE; |
| BYTE excodes = bytes[++b]; |
| |
| excode = (code << 8) | excodes; |
| printf( "%s {", inepilogue ? "pop" : "push" ); |
| |
| for (f = 0; f < 8; f++) |
| { |
| if ((excode >> f) & 1) |
| { |
| printf( "%sr%u", first ? "" : ", ", f ); |
| first = FALSE; |
| } |
| } |
| |
| if (excode & 0x0100) |
| printf( "%s%s", first ? "" : ", ", inepilogue ? "pc" : "lr" ); |
| |
| printf( "}\n" ); |
| } |
| else if (code == 0xee) |
| printf( "unknown 16\n" ); |
| else if (code == 0xef) |
| { |
| WORD excode; |
| BYTE excodes = bytes[++b]; |
| |
| if (excodes <= 0x0f) |
| { |
| excode = (code << 8) | excodes; |
| if (inepilogue) |
| printf( "ldr lr, [sp], #%u\n", (excode & 0x0f) * 4 ); |
| else |
| printf( "unknown 32\n" ); |
| } |
| else |
| printf( "unknown 32\n" ); |
| } |
| else if (code <= 0xf4) |
| printf( "unknown\n" ); |
| else if (code <= 0xf6) |
| { |
| WORD excode, offset = (code == 0xf6) ? 16 : 0; |
| BYTE excodes = bytes[++b]; |
| |
| excode = (code << 8) | excodes; |
| printf( "%s {d%u-d%u}\n", inepilogue ? "vpop" : "vpush", |
| ((excode & 0x00f0) >> 4) + offset, (excode & 0x0f) + offset ); |
| } |
| else if (code <= 0xf7) |
| { |
| unsigned int excode; |
| BYTE excodes[2]; |
| |
| excodes[0] = bytes[++b]; |
| excodes[1] = bytes[++b]; |
| excode = (code << 16) | (excodes[0] << 8) | excodes[1]; |
| printf( "%s sp, sp, #%u\n", inepilogue ? "add" : "sub", (excode & 0xffff) *4 ); |
| } |
| else if (code <= 0xf8) |
| { |
| unsigned int excode; |
| BYTE excodes[3]; |
| |
| excodes[0] = bytes[++b]; |
| excodes[1] = bytes[++b]; |
| excodes[2] = bytes[++b]; |
| excode = (code << 24) | (excodes[0] << 16) | (excodes[1] << 8) | excodes[2]; |
| printf( "%s sp, sp, #%u\n", inepilogue ? "add" : "sub", (excode & 0xffffff) * 4 ); |
| } |
| else if (code <= 0xf9) |
| { |
| unsigned int excode; |
| BYTE excodes[2]; |
| |
| excodes[0] = bytes[++b]; |
| excodes[1] = bytes[++b]; |
| excode = (code << 16) | (excodes[0] << 8) | excodes[1]; |
| printf( "%s sp, sp, #%u\n", inepilogue ? "add" : "sub", (excode & 0xffff) *4 ); |
| } |
| else if (code <= 0xfa) |
| { |
| unsigned int excode; |
| BYTE excodes[3]; |
| |
| excodes[0] = bytes[++b]; |
| excodes[1] = bytes[++b]; |
| excodes[2] = bytes[++b]; |
| excode = (code << 24) | (excodes[0] << 16) | (excodes[1] << 8) | excodes[2]; |
| printf( "%s sp, sp, #%u\n", inepilogue ? "add" : "sub", (excode & 0xffffff) * 4 ); |
| } |
| else if (code <= 0xfc) |
| printf( "nop\n" ); |
| else if (code <= 0xfe) |
| { |
| printf( "(end) nop\n" ); |
| inepilogue = TRUE; |
| } |
| else |
| { |
| printf( "end\n" ); |
| inepilogue = TRUE; |
| } |
| } |
| } |
| |
| if (info->x) |
| { |
| const unsigned int *handler; |
| |
| handler = RVA( rva, sizeof(*handler) ); |
| rva = rva + sizeof(*handler); |
| |
| printf( " handler %08x data at %08x\n", *handler, rva); |
| } |
| } |
| |
| static void dump_dir_exceptions(void) |
| { |
| unsigned int i, size = 0; |
| const void *funcs = get_dir_and_size(IMAGE_FILE_EXCEPTION_DIRECTORY, &size); |
| const IMAGE_FILE_HEADER *file_header = &PE_nt_headers->FileHeader; |
| |
| if (!funcs) return; |
| |
| if (file_header->Machine == IMAGE_FILE_MACHINE_AMD64) |
| { |
| size /= sizeof(struct runtime_function_x86_64); |
| printf( "Exception info (%u functions):\n", size ); |
| for (i = 0; i < size; i++) dump_x86_64_unwind_info( (struct runtime_function_x86_64*)funcs + i ); |
| } |
| else if (file_header->Machine == IMAGE_FILE_MACHINE_ARMNT) |
| { |
| size /= sizeof(struct runtime_function_armnt); |
| printf( "Exception info (%u functions):\n", size ); |
| for (i = 0; i < size; i++) dump_armnt_unwind_info( (struct runtime_function_armnt*)funcs + i ); |
| } |
| else printf( "Exception information not supported for %s binaries\n", |
| get_machine_str(file_header->Machine)); |
| } |
| |
| |
| static void dump_image_thunk_data64(const IMAGE_THUNK_DATA64 *il) |
| { |
| /* FIXME: This does not properly handle large images */ |
| const IMAGE_IMPORT_BY_NAME* iibn; |
| for (; il->u1.Ordinal; il++) |
| { |
| if (IMAGE_SNAP_BY_ORDINAL64(il->u1.Ordinal)) |
| printf(" %4u <by ordinal>\n", (DWORD)IMAGE_ORDINAL64(il->u1.Ordinal)); |
| else |
| { |
| iibn = RVA((DWORD)il->u1.AddressOfData, sizeof(DWORD)); |
| if (!iibn) |
| printf("Can't grab import by name info, skipping to next ordinal\n"); |
| else |
| printf(" %4u %s %x\n", iibn->Hint, iibn->Name, (DWORD)il->u1.AddressOfData); |
| } |
| } |
| } |
| |
| static void dump_image_thunk_data32(const IMAGE_THUNK_DATA32 *il, int offset) |
| { |
| const IMAGE_IMPORT_BY_NAME* iibn; |
| for (; il->u1.Ordinal; il++) |
| { |
| if (IMAGE_SNAP_BY_ORDINAL32(il->u1.Ordinal)) |
| printf(" %4u <by ordinal>\n", IMAGE_ORDINAL32(il->u1.Ordinal)); |
| else |
| { |
| iibn = RVA((DWORD)il->u1.AddressOfData - offset, sizeof(DWORD)); |
| if (!iibn) |
| printf("Can't grab import by name info, skipping to next ordinal\n"); |
| else |
| printf(" %4u %s %x\n", iibn->Hint, iibn->Name, (DWORD)il->u1.AddressOfData); |
| } |
| } |
| } |
| |
| static void dump_dir_imported_functions(void) |
| { |
| unsigned directorySize; |
| const IMAGE_IMPORT_DESCRIPTOR* importDesc = get_dir_and_size(IMAGE_FILE_IMPORT_DIRECTORY, &directorySize); |
| |
| if (!importDesc) return; |
| |
| printf("Import Table size: %08x\n", directorySize);/* FIXME */ |
| |
| for (;;) |
| { |
| const IMAGE_THUNK_DATA32* il; |
| |
| if (!importDesc->Name || !importDesc->FirstThunk) break; |
| |
| printf(" offset %08lx %s\n", Offset(importDesc), (const char*)RVA(importDesc->Name, sizeof(DWORD))); |
| printf(" Hint/Name Table: %08X\n", (DWORD)importDesc->u.OriginalFirstThunk); |
| printf(" TimeDateStamp: %08X (%s)\n", |
| importDesc->TimeDateStamp, get_time_str(importDesc->TimeDateStamp)); |
| printf(" ForwarderChain: %08X\n", importDesc->ForwarderChain); |
| printf(" First thunk RVA: %08X\n", (DWORD)importDesc->FirstThunk); |
| |
| printf(" Ordn Name\n"); |
| |
| il = (importDesc->u.OriginalFirstThunk != 0) ? |
| RVA((DWORD)importDesc->u.OriginalFirstThunk, sizeof(DWORD)) : |
| RVA((DWORD)importDesc->FirstThunk, sizeof(DWORD)); |
| |
| if (!il) |
| printf("Can't grab thunk data, going to next imported DLL\n"); |
| else |
| { |
| if(PE_nt_headers->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) |
| dump_image_thunk_data64((const IMAGE_THUNK_DATA64*)il); |
| else |
| dump_image_thunk_data32(il, 0); |
| printf("\n"); |
| } |
| importDesc++; |
| } |
| printf("\n"); |
| } |
| |
| static void dump_dir_loadconfig(void) |
| { |
| const IMAGE_LOAD_CONFIG_DIRECTORY32 *loadcfg32 = get_dir(IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG); |
| const IMAGE_LOAD_CONFIG_DIRECTORY64 *loadcfg64 = (void*)loadcfg32; |
| |
| if (!loadcfg32) return; |
| |
| printf( "Loadconfig\n" ); |
| print_dword( "Size", loadcfg32->Size ); |
| print_dword( "TimeDateStamp", loadcfg32->TimeDateStamp ); |
| print_word( "MajorVersion", loadcfg32->MajorVersion ); |
| print_word( "MinorVersion", loadcfg32->MinorVersion ); |
| print_dword( "GlobalFlagsClear", loadcfg32->GlobalFlagsClear ); |
| print_dword( "GlobalFlagsSet", loadcfg32->GlobalFlagsSet ); |
| print_dword( "CriticalSectionDefaultTimeout", loadcfg32->CriticalSectionDefaultTimeout ); |
| |
| if(PE_nt_headers->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) |
| { |
| print_longlong( "DeCommitFreeBlockThreshold", loadcfg64->DeCommitFreeBlockThreshold ); |
| print_longlong( "DeCommitTotalFreeThreshold", loadcfg64->DeCommitTotalFreeThreshold ); |
| print_longlong( "MaximumAllocationSize", loadcfg64->MaximumAllocationSize ); |
| print_longlong( "VirtualMemoryThreshold", loadcfg64->VirtualMemoryThreshold ); |
| print_dword( "ProcessHeapFlags", loadcfg64->ProcessHeapFlags ); |
| print_longlong( "ProcessAffinityMask", loadcfg64->ProcessAffinityMask ); |
| print_word( "CSDVersion", loadcfg64->CSDVersion ); |
| print_word( "Reserved", loadcfg64->Reserved1 ); |
| print_longlong( "SecurityCookie", loadcfg64->SecurityCookie ); |
| print_longlong( "SEHandlerTable", loadcfg64->SEHandlerTable ); |
| print_longlong( "SEHandlerCount", loadcfg64->SEHandlerCount ); |
| } |
| else |
| { |
| print_dword( "DeCommitFreeBlockThreshold", loadcfg32->DeCommitFreeBlockThreshold ); |
| print_dword( "DeCommitTotalFreeThreshold", loadcfg32->DeCommitTotalFreeThreshold ); |
| print_dword( "MaximumAllocationSize", loadcfg32->MaximumAllocationSize ); |
| print_dword( "VirtualMemoryThreshold", loadcfg32->VirtualMemoryThreshold ); |
| print_dword( "ProcessHeapFlags", loadcfg32->ProcessHeapFlags ); |
| print_dword( "ProcessAffinityMask", loadcfg32->ProcessAffinityMask ); |
| print_word( "CSDVersion", loadcfg32->CSDVersion ); |
| print_word( "Reserved", loadcfg32->Reserved1 ); |
| print_dword( "SecurityCookie", loadcfg32->SecurityCookie ); |
| print_dword( "SEHandlerTable", loadcfg32->SEHandlerTable ); |
| print_dword( "SEHandlerCount", loadcfg32->SEHandlerCount ); |
| } |
| } |
| |
| static void dump_dir_delay_imported_functions(void) |
| { |
| unsigned directorySize; |
| const IMAGE_DELAYLOAD_DESCRIPTOR *importDesc = get_dir_and_size(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT, &directorySize); |
| |
| if (!importDesc) return; |
| |
| printf("Delay Import Table size: %08x\n", directorySize); /* FIXME */ |
| |
| for (;;) |
| { |
| const IMAGE_THUNK_DATA32* il; |
| int offset = (importDesc->Attributes.AllAttributes & 1) ? 0 : PE_nt_headers->OptionalHeader.ImageBase; |
| |
| if (!importDesc->DllNameRVA || !importDesc->ImportAddressTableRVA || !importDesc->ImportNameTableRVA) break; |
| |
| printf(" grAttrs %08x offset %08lx %s\n", importDesc->Attributes.AllAttributes, Offset(importDesc), |
| (const char *)RVA(importDesc->DllNameRVA - offset, sizeof(DWORD))); |
| printf(" Hint/Name Table: %08x\n", importDesc->ImportNameTableRVA); |
| printf(" TimeDateStamp: %08X (%s)\n", |
| importDesc->TimeDateStamp, get_time_str(importDesc->TimeDateStamp)); |
| |
| printf(" Ordn Name\n"); |
| |
| il = RVA(importDesc->ImportNameTableRVA - offset, sizeof(DWORD)); |
| |
| if (!il) |
| printf("Can't grab thunk data, going to next imported DLL\n"); |
| else |
| { |
| if (PE_nt_headers->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) |
| dump_image_thunk_data64((const IMAGE_THUNK_DATA64 *)il); |
| else |
| dump_image_thunk_data32(il, offset); |
| printf("\n"); |
| } |
| importDesc++; |
| } |
| printf("\n"); |
| } |
| |
| static void dump_dir_debug_dir(const IMAGE_DEBUG_DIRECTORY* idd, int idx) |
| { |
| const char* str; |
| |
| printf("Directory %02u\n", idx + 1); |
| printf(" Characteristics: %08X\n", idd->Characteristics); |
| printf(" TimeDateStamp: %08X %s\n", |
| idd->TimeDateStamp, get_time_str(idd->TimeDateStamp)); |
| printf(" Version %u.%02u\n", idd->MajorVersion, idd->MinorVersion); |
| switch (idd->Type) |
| { |
| default: |
| case IMAGE_DEBUG_TYPE_UNKNOWN: str = "UNKNOWN"; break; |
| case IMAGE_DEBUG_TYPE_COFF: str = "COFF"; break; |
| case IMAGE_DEBUG_TYPE_CODEVIEW: str = "CODEVIEW"; break; |
| case IMAGE_DEBUG_TYPE_FPO: str = "FPO"; break; |
| case IMAGE_DEBUG_TYPE_MISC: str = "MISC"; break; |
| case IMAGE_DEBUG_TYPE_EXCEPTION: str = "EXCEPTION"; break; |
| case IMAGE_DEBUG_TYPE_FIXUP: str = "FIXUP"; break; |
| case IMAGE_DEBUG_TYPE_OMAP_TO_SRC: str = "OMAP_TO_SRC"; break; |
| case IMAGE_DEBUG_TYPE_OMAP_FROM_SRC:str = "OMAP_FROM_SRC"; break; |
| case IMAGE_DEBUG_TYPE_BORLAND: str = "BORLAND"; break; |
| case IMAGE_DEBUG_TYPE_RESERVED10: str = "RESERVED10"; break; |
| case IMAGE_DEBUG_TYPE_CLSID: str = "CLSID"; break; |
| case IMAGE_DEBUG_TYPE_VC_FEATURE: str = "VC_FEATURE"; break; |
| case IMAGE_DEBUG_TYPE_POGO: str = "POGO"; break; |
| case IMAGE_DEBUG_TYPE_ILTCG: str = "ILTCG"; break; |
| case IMAGE_DEBUG_TYPE_MPX: str = "MPX"; break; |
| } |
| printf(" Type: %u (%s)\n", idd->Type, str); |
| printf(" SizeOfData: %u\n", idd->SizeOfData); |
| printf(" AddressOfRawData: %08X\n", idd->AddressOfRawData); |
| printf(" PointerToRawData: %08X\n", idd->PointerToRawData); |
| |
| switch (idd->Type) |
| { |
| case IMAGE_DEBUG_TYPE_UNKNOWN: |
| break; |
| case IMAGE_DEBUG_TYPE_COFF: |
| dump_coff(idd->PointerToRawData, idd->SizeOfData, |
| IMAGE_FIRST_SECTION(PE_nt_headers)); |
| break; |
| case IMAGE_DEBUG_TYPE_CODEVIEW: |
| dump_codeview(idd->PointerToRawData, idd->SizeOfData); |
| break; |
| case IMAGE_DEBUG_TYPE_FPO: |
| dump_frame_pointer_omission(idd->PointerToRawData, idd->SizeOfData); |
| break; |
| case IMAGE_DEBUG_TYPE_MISC: |
| { |
| const IMAGE_DEBUG_MISC* misc = PRD(idd->PointerToRawData, idd->SizeOfData); |
| if (!misc) {printf("Can't get misc debug information\n"); break;} |
| printf(" DataType: %u (%s)\n", |
| misc->DataType, |
| (misc->DataType == IMAGE_DEBUG_MISC_EXENAME) ? "Exe name" : "Unknown"); |
| printf(" Length: %u\n", misc->Length); |
| printf(" Unicode: %s\n", misc->Unicode ? "Yes" : "No"); |
| printf(" Data: %s\n", misc->Data); |
| } |
| break; |
| default: break; |
| } |
| printf("\n"); |
| } |
| |
| static void dump_dir_debug(void) |
| { |
| unsigned nb_dbg, i; |
| const IMAGE_DEBUG_DIRECTORY*debugDir = get_dir_and_size(IMAGE_FILE_DEBUG_DIRECTORY, &nb_dbg); |
| |
| nb_dbg /= sizeof(*debugDir); |
| if (!debugDir || !nb_dbg) return; |
| |
| printf("Debug Table (%u directories)\n", nb_dbg); |
| |
| for (i = 0; i < nb_dbg; i++) |
| { |
| dump_dir_debug_dir(debugDir, i); |
| debugDir++; |
| } |
| printf("\n"); |
| } |
| |
| static inline void print_clrflags(const char *title, DWORD value) |
| { |
| printf(" %-34s 0x%X\n", title, value); |
| #define X(f,s) if (value & f) printf(" %s\n", s) |
| X(COMIMAGE_FLAGS_ILONLY, "ILONLY"); |
| X(COMIMAGE_FLAGS_32BITREQUIRED, "32BITREQUIRED"); |
| X(COMIMAGE_FLAGS_IL_LIBRARY, "IL_LIBRARY"); |
| X(COMIMAGE_FLAGS_STRONGNAMESIGNED, "STRONGNAMESIGNED"); |
| X(COMIMAGE_FLAGS_TRACKDEBUGDATA, "TRACKDEBUGDATA"); |
| #undef X |
| } |
| |
| static inline void print_clrdirectory(const char *title, const IMAGE_DATA_DIRECTORY *dir) |
| { |
| printf(" %-23s rva: 0x%-8x size: 0x%-8x\n", title, dir->VirtualAddress, dir->Size); |
| } |
| |
| static void dump_dir_clr_header(void) |
| { |
| unsigned int size = 0; |
| const IMAGE_COR20_HEADER *dir = get_dir_and_size(IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR, &size); |
| |
| if (!dir) return; |
| |
| printf( "CLR Header\n" ); |
| print_dword( "Header Size", dir->cb ); |
| print_ver( "Required runtime version", dir->MajorRuntimeVersion, dir->MinorRuntimeVersion ); |
| print_clrflags( "Flags", dir->Flags ); |
| print_dword( "EntryPointToken", dir->u.EntryPointToken ); |
| printf("\n"); |
| printf( "CLR Data Directory\n" ); |
| print_clrdirectory( "MetaData", &dir->MetaData ); |
| print_clrdirectory( "Resources", &dir->Resources ); |
| print_clrdirectory( "StrongNameSignature", &dir->StrongNameSignature ); |
| print_clrdirectory( "CodeManagerTable", &dir->CodeManagerTable ); |
| print_clrdirectory( "VTableFixups", &dir->VTableFixups ); |
| print_clrdirectory( "ExportAddressTableJumps", &dir->ExportAddressTableJumps ); |
| print_clrdirectory( "ManagedNativeHeader", &dir->ManagedNativeHeader ); |
| printf("\n"); |
| } |
| |
| static void dump_dir_reloc(void) |
| { |
| unsigned int i, size = 0; |
| const USHORT *relocs; |
| const IMAGE_BASE_RELOCATION *rel = get_dir_and_size(IMAGE_DIRECTORY_ENTRY_BASERELOC, &size); |
| const IMAGE_BASE_RELOCATION *end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + size); |
| static const char * const names[] = |
| { |
| "BASED_ABSOLUTE", |
| "BASED_HIGH", |
| "BASED_LOW", |
| "BASED_HIGHLOW", |
| "BASED_HIGHADJ", |
| "BASED_MIPS_JMPADDR", |
| "BASED_SECTION", |
| "BASED_REL", |
| "unknown 8", |
| "BASED_IA64_IMM64", |
| "BASED_DIR64", |
| "BASED_HIGH3ADJ", |
| "unknown 12", |
| "unknown 13", |
| "unknown 14", |
| "unknown 15" |
| }; |
| |
| if (!rel) return; |
| |
| printf( "Relocations\n" ); |
| while (rel < end - 1 && rel->SizeOfBlock) |
| { |
| printf( " Page %x\n", rel->VirtualAddress ); |
| relocs = (const USHORT *)(rel + 1); |
| i = (rel->SizeOfBlock - sizeof(*rel)) / sizeof(USHORT); |
| while (i--) |
| { |
| USHORT offset = *relocs & 0xfff; |
| int type = *relocs >> 12; |
| printf( " off %04x type %s\n", offset, names[type] ); |
| relocs++; |
| } |
| rel = (const IMAGE_BASE_RELOCATION *)relocs; |
| } |
| printf("\n"); |
| } |
| |
| static void dump_dir_tls(void) |
| { |
| IMAGE_TLS_DIRECTORY64 dir; |
| const DWORD *callbacks; |
| const IMAGE_TLS_DIRECTORY32 *pdir = get_dir(IMAGE_FILE_THREAD_LOCAL_STORAGE); |
| |
| if (!pdir) return; |
| |
| if(PE_nt_headers->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) |
| memcpy(&dir, pdir, sizeof(dir)); |
| else |
| { |
| dir.StartAddressOfRawData = pdir->StartAddressOfRawData; |
| dir.EndAddressOfRawData = pdir->EndAddressOfRawData; |
| dir.AddressOfIndex = pdir->AddressOfIndex; |
| dir.AddressOfCallBacks = pdir->AddressOfCallBacks; |
| dir.SizeOfZeroFill = pdir->SizeOfZeroFill; |
| dir.Characteristics = pdir->Characteristics; |
| } |
| |
| /* FIXME: This does not properly handle large images */ |
| printf( "Thread Local Storage\n" ); |
| printf( " Raw data %08x-%08x (data size %x zero fill size %x)\n", |
| (DWORD)dir.StartAddressOfRawData, (DWORD)dir.EndAddressOfRawData, |
| (DWORD)(dir.EndAddressOfRawData - dir.StartAddressOfRawData), |
| (DWORD)dir.SizeOfZeroFill ); |
| printf( " Index address %08x\n", (DWORD)dir.AddressOfIndex ); |
| printf( " Characteristics %08x\n", dir.Characteristics ); |
| printf( " Callbacks %08x -> {", (DWORD)dir.AddressOfCallBacks ); |
| if (dir.AddressOfCallBacks) |
| { |
| DWORD addr = (DWORD)dir.AddressOfCallBacks - PE_nt_headers->OptionalHeader.ImageBase; |
| while ((callbacks = RVA(addr, sizeof(DWORD))) && *callbacks) |
| { |
| printf( " %08x", *callbacks ); |
| addr += sizeof(DWORD); |
| } |
| } |
| printf(" }\n\n"); |
| } |
| |
| enum FileSig get_kind_dbg(void) |
| { |
| const WORD* pw; |
| |
| pw = PRD(0, sizeof(WORD)); |
| if (!pw) {printf("Can't get main signature, aborting\n"); return 0;} |
| |
| if (*pw == 0x4944 /* "DI" */) return SIG_DBG; |
| return SIG_UNKNOWN; |
| } |
| |
| void dbg_dump(void) |
| { |
| const IMAGE_SEPARATE_DEBUG_HEADER* separateDebugHead; |
| unsigned nb_dbg; |
| unsigned i; |
| const IMAGE_DEBUG_DIRECTORY* debugDir; |
| |
| separateDebugHead = PRD(0, sizeof(*separateDebugHead)); |
| if (!separateDebugHead) {printf("Can't grab the separate header, aborting\n"); return;} |
| |
| printf ("Signature: %.2s (0x%4X)\n", |
| (const char*)&separateDebugHead->Signature, separateDebugHead->Signature); |
| printf ("Flags: 0x%04X\n", separateDebugHead->Flags); |
| printf ("Machine: 0x%04X (%s)\n", |
| separateDebugHead->Machine, get_machine_str(separateDebugHead->Machine)); |
| printf ("Characteristics: 0x%04X\n", separateDebugHead->Characteristics); |
| printf ("TimeDateStamp: 0x%08X (%s)\n", |
| separateDebugHead->TimeDateStamp, get_time_str(separateDebugHead->TimeDateStamp)); |
| printf ("CheckSum: 0x%08X\n", separateDebugHead->CheckSum); |
| printf ("ImageBase: 0x%08X\n", separateDebugHead->ImageBase); |
| printf ("SizeOfImage: 0x%08X\n", separateDebugHead->SizeOfImage); |
| printf ("NumberOfSections: 0x%08X\n", separateDebugHead->NumberOfSections); |
| printf ("ExportedNamesSize: 0x%08X\n", separateDebugHead->ExportedNamesSize); |
| printf ("DebugDirectorySize: 0x%08X\n", separateDebugHead->DebugDirectorySize); |
| |
| if (!PRD(sizeof(IMAGE_SEPARATE_DEBUG_HEADER), |
| separateDebugHead->NumberOfSections * sizeof(IMAGE_SECTION_HEADER))) |
| {printf("Can't get the sections, aborting\n"); return;} |
| |
| dump_sections(separateDebugHead, separateDebugHead + 1, separateDebugHead->NumberOfSections); |
| |
| nb_dbg = separateDebugHead->DebugDirectorySize / sizeof(IMAGE_DEBUG_DIRECTORY); |
| debugDir = PRD(sizeof(IMAGE_SEPARATE_DEBUG_HEADER) + |
| separateDebugHead->NumberOfSections * sizeof(IMAGE_SECTION_HEADER) + |
| separateDebugHead->ExportedNamesSize, |
| nb_dbg * sizeof(IMAGE_DEBUG_DIRECTORY)); |
| if (!debugDir) {printf("Couldn't get the debug directory info, aborting\n");return;} |
| |
| printf("Debug Table (%u directories)\n", nb_dbg); |
| |
| for (i = 0; i < nb_dbg; i++) |
| { |
| dump_dir_debug_dir(debugDir, i); |
| debugDir++; |
| } |
| } |
| |
| static const char *get_resource_type( unsigned int id ) |
| { |
| static const char * const types[] = |
| { |
| NULL, |
| "CURSOR", |
| "BITMAP", |
| "ICON", |
| "MENU", |
| "DIALOG", |
| "STRING", |
| "FONTDIR", |
| "FONT", |
| "ACCELERATOR", |
| "RCDATA", |
| "MESSAGETABLE", |
| "GROUP_CURSOR", |
| NULL, |
| "GROUP_ICON", |
| NULL, |
| "VERSION", |
| "DLGINCLUDE", |
| NULL, |
| "PLUGPLAY", |
| "VXD", |
| "ANICURSOR", |
| "ANIICON", |
| "HTML", |
| "RT_MANIFEST" |
| }; |
| |
| if ((size_t)id < sizeof(types)/sizeof(types[0])) return types[id]; |
| return NULL; |
| } |
| |
| /* dump an ASCII string with proper escaping */ |
| static int dump_strA( const unsigned char *str, size_t len ) |
| { |
| static const char escapes[32] = ".......abtnvfr.............e...."; |
| char buffer[256]; |
| char *pos = buffer; |
| int count = 0; |
| |
| for (; len; str++, len--) |
| { |
| if (pos > buffer + sizeof(buffer) - 8) |
| { |
| fwrite( buffer, pos - buffer, 1, stdout ); |
| count += pos - buffer; |
| pos = buffer; |
| } |
| if (*str > 127) /* hex escape */ |
| { |
| pos += sprintf( pos, "\\x%02x", *str ); |
| continue; |
| } |
| if (*str < 32) /* octal or C escape */ |
| { |
| if (!*str && len == 1) continue; /* do not output terminating NULL */ |
| if (escapes[*str] != '.') |
| pos += sprintf( pos, "\\%c", escapes[*str] ); |
| else if (len > 1 && str[1] >= '0' && str[1] <= '7') |
| pos += sprintf( pos, "\\%03o", *str ); |
| else |
| pos += sprintf( pos, "\\%o", *str ); |
| continue; |
| } |
| if (*str == '\\') *pos++ = '\\'; |
| *pos++ = *str; |
| } |
| fwrite( buffer, pos - buffer, 1, stdout ); |
| count += pos - buffer; |
| return count; |
| } |
| |
| /* dump a Unicode string with proper escaping */ |
| static int dump_strW( const WCHAR *str, size_t len ) |
| { |
| static const char escapes[32] = ".......abtnvfr.............e...."; |
| char buffer[256]; |
| char *pos = buffer; |
| int count = 0; |
| |
| for (; len; str++, len--) |
| { |
| if (pos > buffer + sizeof(buffer) - 8) |
| { |
| fwrite( buffer, pos - buffer, 1, stdout ); |
| count += pos - buffer; |
| pos = buffer; |
| } |
| if (*str > 127) /* hex escape */ |
| { |
| if (len > 1 && str[1] < 128 && isxdigit((char)str[1])) |
| pos += sprintf( pos, "\\x%04x", *str ); |
| else |
| pos += sprintf( pos, "\\x%x", *str ); |
| continue; |
| } |
| if (*str < 32) /* octal or C escape */ |
| { |
| if (!*str && len == 1) continue; /* do not output terminating NULL */ |
| if (escapes[*str] != '.') |
| pos += sprintf( pos, "\\%c", escapes[*str] ); |
| else if (len > 1 && str[1] >= '0' && str[1] <= '7') |
| pos += sprintf( pos, "\\%03o", *str ); |
| else |
| pos += sprintf( pos, "\\%o", *str ); |
| continue; |
| } |
| if (*str == '\\') *pos++ = '\\'; |
| *pos++ = *str; |
| } |
| fwrite( buffer, pos - buffer, 1, stdout ); |
| count += pos - buffer; |
| return count; |
| } |
| |
| /* dump data for a STRING resource */ |
| static void dump_string_data( const WCHAR *ptr, unsigned int size, unsigned int id, const char *prefix ) |
| { |
| int i; |
| |
| for (i = 0; i < 16 && size; i++) |
| { |
| unsigned len = *ptr++; |
| |
| if (len >= size) |
| { |
| len = size; |
| size = 0; |
| } |
| else size -= len + 1; |
| |
| if (len) |
| { |
| printf( "%s%04x \"", prefix, (id - 1) * 16 + i ); |
| dump_strW( ptr, len ); |
| printf( "\"\n" ); |
| ptr += len; |
| } |
| } |
| } |
| |
| /* dump data for a MESSAGETABLE resource */ |
| static void dump_msgtable_data( const void *ptr, unsigned int size, unsigned int id, const char *prefix ) |
| { |
| const MESSAGE_RESOURCE_DATA *data = ptr; |
| const MESSAGE_RESOURCE_BLOCK *block = data->Blocks; |
| unsigned i, j; |
| |
| for (i = 0; i < data->NumberOfBlocks; i++, block++) |
| { |
| const MESSAGE_RESOURCE_ENTRY *entry; |
| |
| entry = (const MESSAGE_RESOURCE_ENTRY *)((const char *)data + block->OffsetToEntries); |
| for (j = block->LowId; j <= block->HighId; j++) |
| { |
| if (entry->Flags & MESSAGE_RESOURCE_UNICODE) |
| { |
| const WCHAR *str = (const WCHAR *)entry->Text; |
| printf( "%s%08x L\"", prefix, j ); |
| dump_strW( str, strlenW(str) ); |
| printf( "\"\n" ); |
| } |
| else |
| { |
| const char *str = (const char *) entry->Text; |
| printf( "%s%08x \"", prefix, j ); |
| dump_strA( entry->Text, strlen(str) ); |
| printf( "\"\n" ); |
| } |
| entry = (const MESSAGE_RESOURCE_ENTRY *)((const char *)entry + entry->Length); |
| } |
| } |
| } |
| |
| static void dump_dir_resource(void) |
| { |
| const IMAGE_RESOURCE_DIRECTORY *root = get_dir(IMAGE_FILE_RESOURCE_DIRECTORY); |
| const IMAGE_RESOURCE_DIRECTORY *namedir; |
| const IMAGE_RESOURCE_DIRECTORY *langdir; |
| const IMAGE_RESOURCE_DIRECTORY_ENTRY *e1, *e2, *e3; |
| const IMAGE_RESOURCE_DIR_STRING_U *string; |
| const IMAGE_RESOURCE_DATA_ENTRY *data; |
| int i, j, k; |
| |
| if (!root) return; |
| |
| printf( "Resources:" ); |
| |
| for (i = 0; i< root->NumberOfNamedEntries + root->NumberOfIdEntries; i++) |
| { |
| e1 = (const IMAGE_RESOURCE_DIRECTORY_ENTRY*)(root + 1) + i; |
| namedir = (const IMAGE_RESOURCE_DIRECTORY *)((const char *)root + e1->u2.s2.OffsetToDirectory); |
| for (j = 0; j < namedir->NumberOfNamedEntries + namedir->NumberOfIdEntries; j++) |
| { |
| e2 = (const IMAGE_RESOURCE_DIRECTORY_ENTRY*)(namedir + 1) + j; |
| langdir = (const IMAGE_RESOURCE_DIRECTORY *)((const char *)root + e2->u2.s2.OffsetToDirectory); |
| for (k = 0; k < langdir->NumberOfNamedEntries + langdir->NumberOfIdEntries; k++) |
| { |
| e3 = (const IMAGE_RESOURCE_DIRECTORY_ENTRY*)(langdir + 1) + k; |
| |
| printf( "\n " ); |
| if (e1->u.s.NameIsString) |
| { |
| string = (const IMAGE_RESOURCE_DIR_STRING_U*)((const char *)root + e1->u.s.NameOffset); |
| dump_unicode_str( string->NameString, string->Length ); |
| } |
| else |
| { |
| const char *type = get_resource_type( e1->u.Id ); |
| if (type) printf( "%s", type ); |
| else printf( "%04x", e1->u.Id ); |
| } |
| |
| printf( " Name=" ); |
| if (e2->u.s.NameIsString) |
| { |
| string = (const IMAGE_RESOURCE_DIR_STRING_U*) ((const char *)root + e2->u.s.NameOffset); |
| dump_unicode_str( string->NameString, string->Length ); |
| } |
| else |
| printf( "%04x", e2->u.Id ); |
| |
| printf( " Language=%04x:\n", e3->u.Id ); |
| data = (const IMAGE_RESOURCE_DATA_ENTRY *)((const char *)root + e3->u2.OffsetToData); |
| if (e1->u.s.NameIsString) |
| { |
| dump_data( RVA( data->OffsetToData, data->Size ), data->Size, " " ); |
| } |
| else switch(e1->u.Id) |
| { |
| case 6: |
| dump_string_data( RVA( data->OffsetToData, data->Size ), data->Size, e2->u.Id, " " ); |
| break; |
| case 11: |
| dump_msgtable_data( RVA( data->OffsetToData, data->Size ), data->Size, |
| e2->u.Id, " " ); |
| break; |
| default: |
| dump_data( RVA( data->OffsetToData, data->Size ), data->Size, " " ); |
| break; |
| } |
| } |
| } |
| } |
| printf( "\n\n" ); |
| } |
| |
| static void dump_debug(void) |
| { |
| const char* stabs = NULL; |
| unsigned szstabs = 0; |
| const char* stabstr = NULL; |
| unsigned szstr = 0; |
| unsigned i; |
| const IMAGE_SECTION_HEADER* sectHead; |
| |
| sectHead = IMAGE_FIRST_SECTION(PE_nt_headers); |
| |
| for (i = 0; i < PE_nt_headers->FileHeader.NumberOfSections; i++, sectHead++) |
| { |
| if (!strcmp((const char *)sectHead->Name, ".stab")) |
| { |
| stabs = RVA(sectHead->VirtualAddress, sectHead->Misc.VirtualSize); |
| szstabs = sectHead->Misc.VirtualSize; |
| } |
| if (!strncmp((const char *)sectHead->Name, ".stabstr", 8)) |
| { |
| stabstr = RVA(sectHead->VirtualAddress, sectHead->Misc.VirtualSize); |
| szstr = sectHead->Misc.VirtualSize; |
| } |
| } |
| if (stabs && stabstr) |
| dump_stabs(stabs, szstabs, stabstr, szstr); |
| } |
| |
| static void dump_symbol_table(void) |
| { |
| const IMAGE_SYMBOL* sym; |
| int numsym; |
| |
| numsym = PE_nt_headers->FileHeader.NumberOfSymbols; |
| if (!PE_nt_headers->FileHeader.PointerToSymbolTable || !numsym) |
| return; |
| sym = PRD(PE_nt_headers->FileHeader.PointerToSymbolTable, |
| sizeof(*sym) * numsym); |
| if (!sym) return; |
| |
| dump_coff_symbol_table(sym, numsym, IMAGE_FIRST_SECTION(PE_nt_headers)); |
| } |
| |
| enum FileSig get_kind_exec(void) |
| { |
| const WORD* pw; |
| const DWORD* pdw; |
| const IMAGE_DOS_HEADER* dh; |
| |
| pw = PRD(0, sizeof(WORD)); |
| if (!pw) {printf("Can't get main signature, aborting\n"); return 0;} |
| |
| if (*pw != IMAGE_DOS_SIGNATURE) return SIG_UNKNOWN; |
| |
| if ((dh = PRD(0, sizeof(IMAGE_DOS_HEADER)))) |
| { |
| /* the signature is the first DWORD */ |
| pdw = PRD(dh->e_lfanew, sizeof(DWORD)); |
| if (pdw) |
| { |
| if (*pdw == IMAGE_NT_SIGNATURE) return SIG_PE; |
| if (*(const WORD *)pdw == IMAGE_OS2_SIGNATURE) return SIG_NE; |
| if (*(const WORD *)pdw == IMAGE_VXD_SIGNATURE) return SIG_LE; |
| } |
| return SIG_DOS; |
| } |
| return SIG_UNKNOWN; |
| } |
| |
| void pe_dump(void) |
| { |
| int all = (globals.dumpsect != NULL) && strcmp(globals.dumpsect, "ALL") == 0; |
| |
| PE_nt_headers = get_nt_header(); |
| if (is_fake_dll()) printf( "*** This is a Wine fake DLL ***\n\n" ); |
| |
| if (globals.do_dumpheader) |
| { |
| dump_pe_header(); |
| /* FIXME: should check ptr */ |
| dump_sections(PRD(0, 1), IMAGE_FIRST_SECTION(PE_nt_headers), |
| PE_nt_headers->FileHeader.NumberOfSections); |
| } |
| else if (!globals.dumpsect) |
| { |
| /* show at least something here */ |
| dump_pe_header(); |
| } |
| |
| if (globals.dumpsect) |
| { |
| if (all || !strcmp(globals.dumpsect, "import")) |
| { |
| dump_dir_imported_functions(); |
| dump_dir_delay_imported_functions(); |
| } |
| if (all || !strcmp(globals.dumpsect, "export")) |
| dump_dir_exported_functions(); |
| if (all || !strcmp(globals.dumpsect, "debug")) |
| dump_dir_debug(); |
| if (all || !strcmp(globals.dumpsect, "resource")) |
| dump_dir_resource(); |
| if (all || !strcmp(globals.dumpsect, "tls")) |
| dump_dir_tls(); |
| if (all || !strcmp(globals.dumpsect, "loadcfg")) |
| dump_dir_loadconfig(); |
| if (all || !strcmp(globals.dumpsect, "clr")) |
| dump_dir_clr_header(); |
| if (all || !strcmp(globals.dumpsect, "reloc")) |
| dump_dir_reloc(); |
| if (all || !strcmp(globals.dumpsect, "except")) |
| dump_dir_exceptions(); |
| } |
| if (globals.do_symbol_table) |
| dump_symbol_table(); |
| if (globals.do_debug) |
| dump_debug(); |
| } |
| |
| typedef struct _dll_symbol { |
| size_t ordinal; |
| char *symbol; |
| } dll_symbol; |
| |
| static dll_symbol *dll_symbols = NULL; |
| static dll_symbol *dll_current_symbol = NULL; |
| |
| /* Compare symbols by ordinal for qsort */ |
| static int symbol_cmp(const void *left, const void *right) |
| { |
| return ((const dll_symbol *)left)->ordinal > ((const dll_symbol *)right)->ordinal; |
| } |
| |
| /******************************************************************* |
| * dll_close |
| * |
| * Free resources used by DLL |
| */ |
| /* FIXME: Not used yet |
| static void dll_close (void) |
| { |
| dll_symbol* ds; |
| |
| if (!dll_symbols) { |
| fatal("No symbols"); |
| } |
| for (ds = dll_symbols; ds->symbol; ds++) |
| free(ds->symbol); |
| free (dll_symbols); |
| dll_symbols = NULL; |
| } |
| */ |
| |
| static void do_grab_sym( void ) |
| { |
| const IMAGE_EXPORT_DIRECTORY*exportDir; |
| unsigned i, j; |
| const DWORD* pName; |
| const DWORD* pFunc; |
| const WORD* pOrdl; |
| const char* ptr; |
| DWORD* map; |
| |
| PE_nt_headers = get_nt_header(); |
| if (!(exportDir = get_dir(IMAGE_FILE_EXPORT_DIRECTORY))) return; |
| |
| pName = RVA(exportDir->AddressOfNames, exportDir->NumberOfNames * sizeof(DWORD)); |
| if (!pName) {printf("Can't grab functions' name table\n"); return;} |
| pOrdl = RVA(exportDir->AddressOfNameOrdinals, exportDir->NumberOfNames * sizeof(WORD)); |
| if (!pOrdl) {printf("Can't grab functions' ordinal table\n"); return;} |
| pFunc = RVA(exportDir->AddressOfFunctions, exportDir->NumberOfFunctions * sizeof(DWORD)); |
| if (!pFunc) {printf("Can't grab functions' address table\n"); return;} |
| |
| /* dll_close(); */ |
| |
| if (!(dll_symbols = malloc((exportDir->NumberOfFunctions + 1) * sizeof(dll_symbol)))) |
| fatal ("Out of memory"); |
| |
| /* bit map of used funcs */ |
| map = calloc(((exportDir->NumberOfFunctions + 31) & ~31) / 32, sizeof(DWORD)); |
| if (!map) fatal("no memory"); |
| |
| for (j = 0; j < exportDir->NumberOfNames; j++, pOrdl++) |
| { |
| map[*pOrdl / 32] |= 1 << (*pOrdl % 32); |
| ptr = RVA(*pName++, sizeof(DWORD)); |
| if (!ptr) ptr = "cant_get_function"; |
| dll_symbols[j].symbol = strdup(ptr); |
| dll_symbols[j].ordinal = exportDir->Base + *pOrdl; |
| assert(dll_symbols[j].symbol); |
| } |
| |
| for (i = 0; i < exportDir->NumberOfFunctions; i++) |
| { |
| if (pFunc[i] && !(map[i / 32] & (1 << (i % 32)))) |
| { |
| char ordinal_text[256]; |
| /* Ordinal only entry */ |
| snprintf (ordinal_text, sizeof(ordinal_text), "%s_%u", |
| globals.forward_dll ? globals.forward_dll : OUTPUT_UC_DLL_NAME, |
| exportDir->Base + i); |
| str_toupper(ordinal_text); |
| dll_symbols[j].symbol = strdup(ordinal_text); |
| assert(dll_symbols[j].symbol); |
| dll_symbols[j].ordinal = exportDir->Base + i; |
| j++; |
| assert(j <= exportDir->NumberOfFunctions); |
| } |
| } |
| free(map); |
| |
| if (NORMAL) |
| printf("%u named symbols in DLL, %u total, %d unique (ordinal base = %d)\n", |
| exportDir->NumberOfNames, exportDir->NumberOfFunctions, j, exportDir->Base); |
| |
| qsort( dll_symbols, j, sizeof(dll_symbol), symbol_cmp ); |
| |
| dll_symbols[j].symbol = NULL; |
| |
| dll_current_symbol = dll_symbols; |
| } |
| |
| /******************************************************************* |
| * dll_open |
| * |
| * Open a DLL and read in exported symbols |
| */ |
| BOOL dll_open (const char *dll_name) |
| { |
| return dump_analysis(dll_name, do_grab_sym, SIG_PE); |
| } |
| |
| /******************************************************************* |
| * dll_next_symbol |
| * |
| * Get next exported symbol from dll |
| */ |
| BOOL dll_next_symbol (parsed_symbol * sym) |
| { |
| if (!dll_current_symbol || !dll_current_symbol->symbol) |
| return FALSE; |
| assert (dll_symbols); |
| sym->symbol = strdup (dll_current_symbol->symbol); |
| sym->ordinal = dll_current_symbol->ordinal; |
| dll_current_symbol++; |
| return TRUE; |
| } |