| /* |
| * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| #include "config.h" |
| #include "wine/port.h" |
| |
| #include <stdlib.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> |
| |
| #include "windef.h" |
| #include "winedump.h" |
| #include "pe.h" |
| |
| #ifndef O_BINARY |
| # define O_BINARY 0 |
| #endif |
| |
| void* PE_base; |
| unsigned long PE_total_len; |
| IMAGE_NT_HEADERS* PE_nt_headers; |
| |
| enum FileSig {SIG_UNKNOWN, SIG_DOS, SIG_PE, SIG_DBG, SIG_NE}; |
| |
| char* get_time_str(DWORD _t) |
| { |
| time_t t = (time_t)_t; |
| static char buf[128]; |
| |
| /* FIXME: I don't get the same values from MS' pedump running under Wine... |
| * I wonder if Wine isn't broken wrt to GMT settings... |
| */ |
| strncpy(buf, ctime(&t), sizeof(buf)); |
| buf[sizeof(buf) - 1] = '\0'; |
| if (buf[strlen(buf)-1] == '\n') |
| buf[strlen(buf)-1] = '\0'; |
| return buf; |
| } |
| |
| static const char* get_machine_str(DWORD 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"; |
| } |
| return "???"; |
| } |
| |
| void* PRD(unsigned long prd, unsigned long len) |
| { |
| return (prd + len > PE_total_len) ? NULL : (char*)PE_base + prd; |
| } |
| |
| unsigned long Offset(void* ptr) |
| { |
| if (ptr < PE_base) {printf("<<<<<ptr below\n");return 0;} |
| if ((char *)ptr >= (char*)PE_base + PE_total_len) {printf("<<<<<ptr above\n");return 0;} |
| return (char*)ptr - (char*)PE_base; |
| } |
| |
| void* RVA(unsigned long rva, unsigned long len) |
| { |
| IMAGE_SECTION_HEADER* sectHead; |
| int i; |
| |
| sectHead = (IMAGE_SECTION_HEADER*)((char*)PE_nt_headers + sizeof(DWORD) + |
| sizeof(IMAGE_FILE_HEADER) + |
| PE_nt_headers->FileHeader.SizeOfOptionalHeader); |
| |
| if (rva == 0) return NULL; |
| |
| 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) |
| break; |
| } |
| |
| if (i < 0) |
| { |
| printf("rva not found in any section (%lu)\n", rva); |
| return NULL; |
| } |
| |
| /* return image import directory offset */ |
| return PRD(sectHead[i].PointerToRawData + rva - sectHead[i].VirtualAddress, len); |
| } |
| |
| static void* get_dir(unsigned idx) |
| { |
| if (idx >= PE_nt_headers->OptionalHeader.NumberOfRvaAndSizes) |
| return NULL; |
| return RVA(PE_nt_headers->OptionalHeader.DataDirectory[idx].VirtualAddress, |
| PE_nt_headers->OptionalHeader.DataDirectory[idx].Size); |
| } |
| |
| static const char* DirectoryNames[16] = { |
| "EXPORT", "IMPORT", "RESOURCE", "EXCEPTION", |
| "SECURITY", "BASERELOC", "DEBUG", "ARCHITECTURE", |
| "GLOBALPTR", "TLS", "LOAD_CONFIG", "Bound IAT", |
| "IAT", "Delay IAT", "COM Descript", "" |
| }; |
| |
| static void dump_pe_header(void) |
| { |
| char *str; |
| IMAGE_FILE_HEADER *fileHeader; |
| IMAGE_OPTIONAL_HEADER *optionalHeader; |
| unsigned i; |
| |
| printf("File Header\n"); |
| fileHeader = &PE_nt_headers->FileHeader; |
| |
| printf(" Machine: %04X (%s)\n", |
| fileHeader->Machine, get_machine_str(fileHeader->Machine)); |
| printf(" Number of Sections: %d\n", fileHeader->NumberOfSections); |
| printf(" TimeDateStamp: %08lX (%s) offset %ld\n", |
| fileHeader->TimeDateStamp, get_time_str(fileHeader->TimeDateStamp), |
| Offset(&(fileHeader->TimeDateStamp))); |
| printf(" PointerToSymbolTable: %08lX\n", fileHeader->PointerToSymbolTable); |
| printf(" NumberOfSymbols: %08lX\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_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_SYSTEM, "SYSTEM"); |
| X(IMAGE_FILE_DLL, "DLL"); |
| X(IMAGE_FILE_BYTES_REVERSED_HI, "BYTES_REVERSED_HI"); |
| #undef X |
| printf("\n"); |
| |
| /* hope we have the right size */ |
| printf("Optional Header\n"); |
| optionalHeader = &PE_nt_headers->OptionalHeader; |
| printf(" Magic 0x%-4X %u\n", |
| optionalHeader->Magic, optionalHeader->Magic); |
| printf(" linker version %u.%02u\n", |
| optionalHeader->MajorLinkerVersion, optionalHeader->MinorLinkerVersion); |
| printf(" size of code 0x%-8lx %lu\n", |
| optionalHeader->SizeOfCode, optionalHeader->SizeOfCode); |
| printf(" size of initialized data 0x%-8lx %lu\n", |
| optionalHeader->SizeOfInitializedData, optionalHeader->SizeOfInitializedData); |
| printf(" size of uninitialized data 0x%-8lx %lu\n", |
| optionalHeader->SizeOfUninitializedData, optionalHeader->SizeOfUninitializedData); |
| printf(" entrypoint RVA 0x%-8lx %lu\n", |
| optionalHeader->AddressOfEntryPoint, optionalHeader->AddressOfEntryPoint); |
| printf(" base of code 0x%-8lx %lu\n", |
| optionalHeader->BaseOfCode, optionalHeader->BaseOfCode); |
| printf(" base of data 0x%-8lX %lu\n", |
| optionalHeader->BaseOfData, optionalHeader->BaseOfData); |
| printf(" image base 0x%-8lX %lu\n", |
| optionalHeader->ImageBase, optionalHeader->ImageBase); |
| printf(" section align 0x%-8lx %lu\n", |
| optionalHeader->SectionAlignment, optionalHeader->SectionAlignment); |
| printf(" file align 0x%-8lx %lu\n", |
| optionalHeader->FileAlignment, optionalHeader->FileAlignment); |
| printf(" required OS version %u.%02u\n", |
| optionalHeader->MajorOperatingSystemVersion, optionalHeader->MinorOperatingSystemVersion); |
| printf(" image version %u.%02u\n", |
| optionalHeader->MajorImageVersion, optionalHeader->MinorImageVersion); |
| printf(" subsystem version %u.%02u\n", |
| optionalHeader->MajorSubsystemVersion, optionalHeader->MinorSubsystemVersion); |
| printf(" Win32 Version 0x%lX\n", optionalHeader->Win32VersionValue); |
| printf(" size of image 0x%-8lx %lu\n", |
| optionalHeader->SizeOfImage, optionalHeader->SizeOfImage); |
| printf(" size of headers 0x%-8lx %lu\n", |
| optionalHeader->SizeOfHeaders, optionalHeader->SizeOfHeaders); |
| printf(" checksum 0x%lX\n", optionalHeader->CheckSum); |
| switch (optionalHeader->Subsystem) |
| { |
| 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; |
| } |
| printf(" Subsystem 0x%X (%s)\n", optionalHeader->Subsystem, str); |
| printf(" DLL flags 0x%X\n", optionalHeader->DllCharacteristics); |
| printf(" stack reserve size 0x%-8lx %lu\n", |
| optionalHeader->SizeOfStackReserve, optionalHeader->SizeOfStackReserve); |
| printf(" stack commit size 0x%-8lx %lu\n", |
| optionalHeader->SizeOfStackCommit, optionalHeader->SizeOfStackCommit); |
| printf(" heap reserve size 0x%-8lx %lu\n", |
| optionalHeader->SizeOfHeapReserve, optionalHeader->SizeOfHeapReserve); |
| printf(" heap commit size 0x%-8lx %lu\n", |
| optionalHeader->SizeOfHeapCommit, optionalHeader->SizeOfHeapCommit); |
| printf(" loader flags 0x%lX\n", optionalHeader->LoaderFlags); |
| printf(" RVAs & sizes 0x%lX\n", optionalHeader->NumberOfRvaAndSizes); |
| printf("\n"); |
| |
| printf("Data Directory\n"); |
| printf("%ld\n", optionalHeader->NumberOfRvaAndSizes * sizeof(IMAGE_DATA_DIRECTORY)); |
| |
| for (i = 0; i < optionalHeader->NumberOfRvaAndSizes && i < 16; i++) |
| { |
| printf(" %-12s rva: 0x%-8lX size: %8lu\n", |
| DirectoryNames[i], optionalHeader->DataDirectory[i].VirtualAddress, |
| optionalHeader->DataDirectory[i].Size); |
| } |
| printf("\n"); |
| } |
| |
| static void dump_sections(void* addr, unsigned num_sect) |
| { |
| IMAGE_SECTION_HEADER* sectHead = addr; |
| unsigned i; |
| |
| printf("Section Table\n"); |
| for (i = 0; i < num_sect; i++, sectHead++) |
| { |
| printf(" %02d %-8s VirtSize: %-8lu VirtAddr: %-8lu 0x%08lx\n", |
| i + 1, sectHead->Name, sectHead->Misc.VirtualSize, sectHead->VirtualAddress, |
| sectHead->VirtualAddress); |
| printf(" raw data offs: %-8lu raw data size: %-8lu\n", |
| sectHead->PointerToRawData, sectHead->SizeOfRawData); |
| printf(" relocation offs: %-8lu relocations: %-8u\n", |
| sectHead->PointerToRelocations, sectHead->NumberOfRelocations); |
| printf(" line # offs: %-8lu line #'s: %-8u\n", |
| sectHead->PointerToLinenumbers, sectHead->NumberOfLinenumbers); |
| printf(" characteristics: 0x$%08lx\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"); |
| |
| X(IMAGE_SCN_ALIGN_1BYTES, "ALIGN_1BYTES"); |
| X(IMAGE_SCN_ALIGN_2BYTES, "ALIGN_2BYTES"); |
| X(IMAGE_SCN_ALIGN_4BYTES, "ALIGN_4BYTES"); |
| X(IMAGE_SCN_ALIGN_8BYTES, "ALIGN_8BYTES"); |
| X(IMAGE_SCN_ALIGN_16BYTES, "ALIGN_16BYTES"); |
| X(IMAGE_SCN_ALIGN_32BYTES, "ALIGN_32BYTES"); |
| X(IMAGE_SCN_ALIGN_64BYTES, "ALIGN_64BYTES"); |
| /* 0x00800000 - Unused */ |
| |
| 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"); |
| } |
| printf("\n"); |
| } |
| |
| static void dump_dir_exported_functions(void) |
| { |
| IMAGE_EXPORT_DIRECTORY *exportDir = get_dir(IMAGE_FILE_EXPORT_DIRECTORY); |
| unsigned int i; |
| DWORD* pFunc; |
| DWORD* pName; |
| WORD* pOrdl; |
| DWORD* map; |
| parsed_symbol symbol; |
| |
| if (!exportDir) return; |
| |
| printf("Exports table:\n"); |
| printf("\n"); |
| printf(" Name: %s\n", (char*)RVA(exportDir->Name, sizeof(DWORD))); |
| printf(" Characteristics: %08lx\n", exportDir->Characteristics); |
| printf(" TimeDateStamp: %08lX %s\n", |
| exportDir->TimeDateStamp, get_time_str(exportDir->TimeDateStamp)); |
| printf(" Version: %u.%02u\n", exportDir->MajorVersion, exportDir->MinorVersion); |
| printf(" Ordinal base: %lu\n", exportDir->Base); |
| printf(" # of functions: %lu\n", exportDir->NumberOfFunctions); |
| printf(" # of Names: %lu\n", exportDir->NumberOfNames); |
| printf("Adresses of functions: %08lX\n", exportDir->AddressOfFunctions); |
| printf("Adresses of name ordinals: %08lX\n", exportDir->AddressOfNameOrdinals); |
| printf("Adresses of names: %08lX\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)); |
| 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;} |
| |
| /* bit map of used funcs */ |
| map = calloc(((exportDir->NumberOfFunctions + 31) & ~31) / 32, sizeof(DWORD)); |
| if (!map) fatal("no memory"); |
| |
| for (i = 0; i < exportDir->NumberOfNames; i++, pName++, pOrdl++) |
| { |
| char* name; |
| |
| map[*pOrdl / 32] |= 1 << (*pOrdl % 32); |
| |
| name = (char*)RVA(*pName, sizeof(DWORD)); |
| if (name && globals.do_demangle) |
| { |
| printf(" %08lX %4lu ", pFunc[*pOrdl], exportDir->Base + *pOrdl); |
| |
| symbol_init(&symbol, name); |
| if (symbol_demangle(&symbol) == -1) |
| printf(name); |
| else if (symbol.flags & SYM_DATA) |
| printf(symbol.arg_text[0]); |
| else |
| output_prototype(stdout, &symbol); |
| printf("\n"); |
| symbol_clear(&symbol); |
| } |
| else |
| { |
| printf(" %08lX %4lu %s\n", pFunc[*pOrdl], exportDir->Base + *pOrdl, name); |
| } |
| } |
| pFunc = RVA(exportDir->AddressOfFunctions, exportDir->NumberOfFunctions * sizeof(DWORD)); |
| if (!pFunc) {printf("Can't grab functions' address table\n"); return;} |
| for (i = 0; i < exportDir->NumberOfFunctions; i++) |
| { |
| if (pFunc[i] && !(map[i / 32] & (1 << (i % 32)))) |
| { |
| printf(" %08lX %4lu <by ordinal>\n", pFunc[i], exportDir->Base + i); |
| } |
| } |
| free(map); |
| printf("\n"); |
| } |
| |
| static void dump_dir_imported_functions(void) |
| { |
| IMAGE_IMPORT_DESCRIPTOR *importDesc = get_dir(IMAGE_FILE_IMPORT_DIRECTORY); |
| unsigned nb_imp, i; |
| |
| if (!importDesc) return; |
| nb_imp = PE_nt_headers->OptionalHeader.DataDirectory[IMAGE_FILE_IMPORT_DIRECTORY].Size / |
| sizeof(*importDesc); |
| if (!nb_imp) return; |
| |
| printf("Import Table size: %lu\n", |
| PE_nt_headers->OptionalHeader.DataDirectory[IMAGE_FILE_IMPORT_DIRECTORY].Size);/* FIXME */ |
| |
| for (i = 0; i < nb_imp - 1; i++) /* the last descr is set as 0 as a sentinel */ |
| { |
| IMAGE_THUNK_DATA* il; |
| IMAGE_IMPORT_BY_NAME* iibn; |
| |
| if (!importDesc->Name || |
| (importDesc->u.OriginalFirstThunk == NULL && importDesc->FirstThunk == NULL)) |
| { |
| /* FIXME */ |
| printf("<<<<<<<null entry\n"); |
| break; |
| } |
| printf(" offset %lu %s\n", Offset(importDesc), (char*)RVA(importDesc->Name, sizeof(DWORD))); |
| printf(" Hint/Name Table: %08lX\n", (DWORD)importDesc->u.OriginalFirstThunk); |
| printf(" TimeDataStamp: %08lX (%s)\n", |
| importDesc->TimeDateStamp, get_time_str(importDesc->TimeDateStamp)); |
| printf(" ForwarderChain: %08lX\n", importDesc->ForwarderChain); |
| printf(" First thunk RVA: %08lX (delta: %u 0x%x)\n", |
| (DWORD)importDesc->FirstThunk, -1, -1); /* FIXME */ |
| |
| 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"); continue;} |
| |
| for (; il->u1.Ordinal; il++) |
| { |
| if (IMAGE_SNAP_BY_ORDINAL(il->u1.Ordinal)) |
| { |
| printf(" %4lu <by ordinal>\n", IMAGE_ORDINAL(il->u1.Ordinal)); |
| } |
| else |
| { |
| iibn = RVA((DWORD)il->u1.AddressOfData, sizeof(DWORD)); |
| if (!il) |
| { |
| printf("Can't grab import by name info, skipping to next ordinal\n"); |
| } |
| else |
| { |
| printf(" %4u %s %lx\n", iibn->Hint, iibn->Name, (DWORD)il->u1.AddressOfData); |
| } |
| } |
| } |
| printf("\n"); |
| importDesc++; |
| } |
| printf("\n"); |
| } |
| |
| static void dump_dir_debug_dir(IMAGE_DEBUG_DIRECTORY* idd, int idx) |
| { |
| const char* str; |
| |
| printf("Directory %02u\n", idx + 1); |
| printf(" Characteristics: %08lX\n", idd->Characteristics); |
| printf(" TimeDateStamp: %08lX %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; |
| } |
| printf(" Type: %lu (%s)\n", idd->Type, str); |
| printf(" SizeOfData: %lu\n", idd->SizeOfData); |
| printf(" AddressOfRawData: %08lX\n", idd->AddressOfRawData); |
| printf(" PointerToRawData: %08lX\n", idd->PointerToRawData); |
| |
| switch (idd->Type) |
| { |
| case IMAGE_DEBUG_TYPE_UNKNOWN: |
| break; |
| case IMAGE_DEBUG_TYPE_COFF: |
| dump_coff(idd->PointerToRawData, idd->SizeOfData); |
| 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: |
| { |
| IMAGE_DEBUG_MISC* misc = PRD(idd->PointerToRawData, idd->SizeOfData); |
| if (!misc) {printf("Can't get misc debug information\n"); break;} |
| printf(" DataType: %lu (%s)\n", |
| misc->DataType, |
| (misc->DataType == IMAGE_DEBUG_MISC_EXENAME) ? "Exe name" : "Unknown"); |
| printf(" Length: %lu\n", misc->Length); |
| printf(" Unicode: %s\n", misc->Unicode ? "Yes" : "No"); |
| printf(" Data: %s\n", misc->Data); |
| } |
| break; |
| case IMAGE_DEBUG_TYPE_EXCEPTION: |
| break; |
| case IMAGE_DEBUG_TYPE_FIXUP: |
| break; |
| case IMAGE_DEBUG_TYPE_OMAP_TO_SRC: |
| break; |
| case IMAGE_DEBUG_TYPE_OMAP_FROM_SRC: |
| break; |
| case IMAGE_DEBUG_TYPE_BORLAND: |
| break; |
| case IMAGE_DEBUG_TYPE_RESERVED10: |
| break; |
| } |
| printf("\n"); |
| } |
| |
| static void dump_dir_debug(void) |
| { |
| IMAGE_DEBUG_DIRECTORY* debugDir = get_dir(IMAGE_FILE_DEBUG_DIRECTORY); |
| unsigned nb_dbg, i; |
| |
| if (!debugDir) return; |
| nb_dbg = PE_nt_headers->OptionalHeader.DataDirectory[IMAGE_FILE_DEBUG_DIRECTORY].Size / |
| sizeof(*debugDir); |
| if (!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 void dump_separate_dbg(void) |
| { |
| IMAGE_SEPARATE_DEBUG_HEADER*separateDebugHead = PRD(0, sizeof(separateDebugHead)); |
| unsigned nb_dbg; |
| unsigned i; |
| IMAGE_DEBUG_DIRECTORY* debugDir; |
| |
| if (!separateDebugHead) {printf("Can't grab the separate header, aborting\n"); return;} |
| |
| printf ("Signature: %.2s (0x%4X)\n", |
| (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%08lX (%s)\n", |
| separateDebugHead->TimeDateStamp, get_time_str(separateDebugHead->TimeDateStamp)); |
| printf ("CheckSum: 0x%08lX\n", separateDebugHead->CheckSum); |
| printf ("ImageBase: 0x%08lX\n", separateDebugHead->ImageBase); |
| printf ("SizeOfImage: 0x%08lX\n", separateDebugHead->SizeOfImage); |
| printf ("NumberOfSections: 0x%08lX\n", separateDebugHead->NumberOfSections); |
| printf ("ExportedNamesSize: 0x%08lX\n", separateDebugHead->ExportedNamesSize); |
| printf ("DebugDirectorySize: 0x%08lX\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 + 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 void dump_unicode_str( const WCHAR *str, int len ) |
| { |
| if (len == -1) for (len = 0; str[len]; len++) ; |
| printf( "L\""); |
| while (len-- > 0 && *str) |
| { |
| WCHAR c = *str++; |
| switch (c) |
| { |
| case '\n': printf( "\\n" ); break; |
| case '\r': printf( "\\r" ); break; |
| case '\t': printf( "\\t" ); break; |
| case '"': printf( "\\\"" ); break; |
| case '\\': printf( "\\\\" ); break; |
| default: |
| if (c >= ' ' && c <= 126) putchar(c); |
| else printf( "\\u%04x",c); |
| } |
| } |
| printf( "\"" ); |
| } |
| |
| static const char *get_resource_type( int id ) |
| { |
| static const char *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" |
| }; |
| |
| if (id < sizeof(types)/sizeof(types[0])) return types[id]; |
| return NULL; |
| } |
| |
| void dump_data( const unsigned char *ptr, unsigned int size, const char *prefix ) |
| { |
| unsigned int i, j; |
| |
| printf( "%s", prefix ); |
| for (i = 0; i < size; i++) |
| { |
| printf( "%02x%c", ptr[i], (i % 16 == 7) ? '-' : ' ' ); |
| if ((i % 16) == 15) |
| { |
| printf( " " ); |
| for (j = 0; j < 16; j++) |
| printf( "%c", isprint(ptr[i-15+j]) ? ptr[i-15+j] : '.' ); |
| if (i < size-1) printf( "\n%s", prefix ); |
| } |
| } |
| if (i % 16) |
| { |
| printf( "%*s ", 3 * (16-(i%16)), "" ); |
| for (j = 0; j < i % 16; j++) |
| printf( "%c", isprint(ptr[i-(i%16)+j]) ? ptr[i-(i%16)+j] : '.' ); |
| } |
| printf( "\n" ); |
| } |
| |
| 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 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(root + 1) + i; |
| namedir = (IMAGE_RESOURCE_DIRECTORY *)((char *)root + e1->u2.s3.OffsetToDirectory); |
| for (j = 0; j < namedir->NumberOfNamedEntries + namedir->NumberOfIdEntries; j++) |
| { |
| e2 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(namedir + 1) + j; |
| langdir = (IMAGE_RESOURCE_DIRECTORY *)((char *)root + e2->u2.s3.OffsetToDirectory); |
| for (k = 0; k < langdir->NumberOfNamedEntries + langdir->NumberOfIdEntries; k++) |
| { |
| e3 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(langdir + 1) + k; |
| |
| printf( "\n " ); |
| if (e1->u1.s1.NameIsString) |
| { |
| string = (PIMAGE_RESOURCE_DIR_STRING_U)((char *)root + e1->u1.s1.NameOffset); |
| dump_unicode_str( string->NameString, string->Length ); |
| } |
| else |
| { |
| const char *type = get_resource_type( e1->u1.s2.Id ); |
| if (type) printf( "%s", type ); |
| else printf( "%04x", e1->u1.s2.Id ); |
| } |
| |
| printf( " Name=" ); |
| if (e2->u1.s1.NameIsString) |
| { |
| string = (PIMAGE_RESOURCE_DIR_STRING_U) ((char *)root + e2->u1.s1.NameOffset); |
| dump_unicode_str( string->NameString, string->Length ); |
| } |
| else |
| printf( "%04x", e2->u1.s2.Id ); |
| |
| printf( " Language=%04x:\n", e3->u1.s2.Id ); |
| data = (IMAGE_RESOURCE_DATA_ENTRY *)((char *)root + e3->u2.OffsetToData); |
| dump_data( RVA( data->OffsetToData, data->Size ), data->Size, " " ); |
| } |
| } |
| } |
| printf( "\n\n" ); |
| } |
| |
| static void do_dump( enum FileSig sig ) |
| { |
| int all = (globals.dumpsect != NULL) && strcmp(globals.dumpsect, "ALL") == 0; |
| |
| if (sig == SIG_NE) |
| { |
| ne_dump( PE_base, PE_total_len ); |
| return; |
| } |
| |
| if (globals.do_dumpheader) |
| { |
| dump_pe_header(); |
| /* FIXME: should check ptr */ |
| dump_sections((char*)PE_nt_headers + sizeof(DWORD) + |
| sizeof(IMAGE_FILE_HEADER) + PE_nt_headers->FileHeader.SizeOfOptionalHeader, |
| 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(); |
| 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 0 |
| /* FIXME: not implemented yet */ |
| if (all || !strcmp(globals.dumpsect, "reloc")) |
| dump_dir_reloc(); |
| #endif |
| } |
| } |
| |
| static enum FileSig check_headers(void) |
| { |
| WORD* pw; |
| DWORD* pdw; |
| IMAGE_DOS_HEADER* dh; |
| enum FileSig sig; |
| |
| pw = PRD(0, sizeof(WORD)); |
| if (!pw) {printf("Can't get main signature, aborting\n"); return 0;} |
| |
| switch (*pw) |
| { |
| case IMAGE_DOS_SIGNATURE: |
| sig = SIG_DOS; |
| dh = PRD(0, sizeof(IMAGE_DOS_HEADER)); |
| if (dh && dh->e_lfanew >= sizeof(*dh)) /* reasonable DOS header ? */ |
| { |
| /* the signature is the first DWORD */ |
| pdw = PRD(dh->e_lfanew, sizeof(DWORD)); |
| if (pdw) |
| { |
| if (*pdw == IMAGE_NT_SIGNATURE) |
| { |
| PE_nt_headers = PRD(dh->e_lfanew, sizeof(DWORD)); |
| sig = SIG_PE; |
| } |
| else if (*(WORD *)pdw == IMAGE_OS2_SIGNATURE) |
| { |
| sig = SIG_NE; |
| } |
| else |
| { |
| printf("No PE Signature found\n"); |
| } |
| } |
| else |
| { |
| printf("Can't get the extented signature, aborting\n"); |
| } |
| } |
| break; |
| case 0x4944: /* "DI" */ |
| sig = SIG_DBG; |
| break; |
| default: |
| printf("No known main signature (%.2s/%x), aborting\n", (char*)pw, *pw); |
| sig = SIG_UNKNOWN; |
| } |
| |
| return sig; |
| } |
| |
| int pe_analysis(const char* name, void (*fn)(enum FileSig), enum FileSig wanted_sig) |
| { |
| int fd; |
| enum FileSig effective_sig; |
| int ret = 1; |
| struct stat s; |
| |
| setbuf(stdout, NULL); |
| |
| fd = open(name, O_RDONLY | O_BINARY); |
| if (fd == -1) fatal("Can't open file"); |
| |
| if (fstat(fd, &s) < 0) fatal("Can't get size"); |
| PE_total_len = s.st_size; |
| |
| #ifdef HAVE_MMAP |
| if ((PE_base = mmap(NULL, PE_total_len, PROT_READ, MAP_PRIVATE, fd, 0)) == (void *)-1) |
| #endif |
| { |
| if (!(PE_base = malloc( PE_total_len ))) fatal( "Out of memory" ); |
| if (read( fd, PE_base, PE_total_len ) != PE_total_len) fatal( "Cannot read file" ); |
| } |
| |
| effective_sig = check_headers(); |
| |
| if (effective_sig == SIG_UNKNOWN) |
| { |
| printf("Can't get a recognized file signature, aborting\n"); |
| ret = 0; |
| } |
| else if (wanted_sig == SIG_UNKNOWN || wanted_sig == effective_sig) |
| { |
| switch (effective_sig) |
| { |
| case SIG_UNKNOWN: /* shouldn't happen... */ |
| ret = 0; break; |
| case SIG_PE: |
| case SIG_NE: |
| printf("Contents of \"%s\": %ld bytes\n\n", name, PE_total_len); |
| (*fn)(effective_sig); |
| break; |
| case SIG_DBG: |
| dump_separate_dbg(); |
| break; |
| case SIG_DOS: |
| ret = 0; break; |
| } |
| } |
| else |
| { |
| printf("Can't get a suitable file signature, aborting\n"); |
| ret = 0; |
| } |
| |
| if (ret) printf("Done dumping %s\n", name); |
| #ifdef HAVE_MMAP |
| if (munmap(PE_base, PE_total_len) == -1) |
| #endif |
| { |
| free( PE_base ); |
| } |
| close(fd); |
| |
| return ret; |
| } |
| |
| void dump_file(const char* name) |
| { |
| pe_analysis(name, do_dump, SIG_UNKNOWN); |
| } |
| |
| #if 0 |
| int main(int argc, char* argv[]) |
| { |
| if (argc != 2) fatal("usage"); |
| pe_analysis(argv[1], do_dump); |
| } |
| #endif |
| |
| 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 ((dll_symbol *)left)->ordinal > ((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( enum FileSig sig ) |
| { |
| IMAGE_EXPORT_DIRECTORY *exportDir = get_dir(IMAGE_FILE_EXPORT_DIRECTORY); |
| unsigned i, j; |
| DWORD* pName; |
| DWORD* pFunc; |
| WORD* pOrdl; |
| char* ptr; |
| DWORD* map; |
| |
| if (!exportDir) 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;} |
| |
| /* dll_close(); */ |
| |
| if (!(dll_symbols = (dll_symbol *) malloc((exportDir->NumberOfFunctions + 1) * |
| sizeof (dll_symbol)))) |
| fatal ("Out of memory"); |
| if (exportDir->AddressOfFunctions != exportDir->NumberOfNames || exportDir->Base > 1) |
| globals.do_ordinals = 1; |
| |
| /* 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 = -1; /* indicate non-ordinal symbol */ |
| assert(dll_symbols[j].symbol); |
| } |
| pFunc = RVA(exportDir->AddressOfFunctions, exportDir->NumberOfFunctions * sizeof(DWORD)); |
| if (!pFunc) {printf("Can't grab functions' address table\n"); return;} |
| |
| 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_%lu", |
| 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("%lu named symbols in DLL, %lu total\n", |
| exportDir->NumberOfNames, exportDir->NumberOfFunctions); |
| |
| qsort( dll_symbols, exportDir->NumberOfFunctions, sizeof(dll_symbol), symbol_cmp ); |
| |
| dll_symbols[exportDir->NumberOfFunctions].symbol = NULL; |
| |
| dll_current_symbol = dll_symbols; |
| } |
| |
| /******************************************************************* |
| * dll_open |
| * |
| * Open a DLL and read in exported symbols |
| */ |
| void dll_open (const char *dll_name) |
| { |
| pe_analysis(dll_name, do_grab_sym, SIG_PE); |
| } |
| |
| /******************************************************************* |
| * dll_next_symbol |
| * |
| * Get next exported symbol from dll |
| */ |
| int dll_next_symbol (parsed_symbol * sym) |
| { |
| if (!dll_current_symbol->symbol) |
| return 1; |
| |
| assert (dll_symbols); |
| |
| sym->symbol = strdup (dll_current_symbol->symbol); |
| sym->ordinal = dll_current_symbol->ordinal; |
| dll_current_symbol++; |
| return 0; |
| } |