You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
532 lines
15 KiB
532 lines
15 KiB
//-------------------- |
|
// PROGRAM: PEDUMP |
|
// FILE: EXEDUMP.C |
|
// AUTHOR: Matt Pietrek - 1993 |
|
//-------------------- |
|
#include <windows.h> |
|
#include <stdio.h> |
|
#include "common.h" |
|
#include "extrnvar.h" |
|
|
|
typedef PIMAGE_COFF_SYMBOLS_HEADER PIMAGE_DEBUG_INFO; |
|
|
|
PIMAGE_DEBUG_INFO PCOFFDebugInfo = 0; |
|
|
|
char *SzDebugFormats[] = { |
|
"UNKNOWN/BORLAND","COFF","CODEVIEW","FPO","MISC","EXCEPTION","FIXUP" }; |
|
|
|
// |
|
// Dump the debug directory in a PE file. |
|
// |
|
void DumpDebugDirectory(DWORD base, PIMAGE_NT_HEADERS pNTHeader) |
|
{ |
|
PIMAGE_DEBUG_DIRECTORY debugDir; |
|
PIMAGE_SECTION_HEADER header; |
|
unsigned cDebugFormats, i; |
|
DWORD offsetInto_rdata; |
|
DWORD va_debug_dir; |
|
PSTR szDebugFormat; |
|
|
|
// This line was so long that we had to break it up |
|
va_debug_dir = pNTHeader->OptionalHeader. |
|
DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]. |
|
VirtualAddress; |
|
if ( va_debug_dir == 0 ) |
|
return; |
|
|
|
// If we found a .debug section, and the debug directory is at the |
|
// beginning of this section, it looks like a Borland file |
|
header = GetSectionHeader(".debug", pNTHeader); |
|
if ( header && (header->VirtualAddress == va_debug_dir) ) |
|
{ |
|
debugDir = (PIMAGE_DEBUG_DIRECTORY)(header->PointerToRawData+base); |
|
cDebugFormats = pNTHeader->OptionalHeader. |
|
DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size; |
|
} |
|
else // Look for microsoft debug directory in the .rdata section |
|
{ |
|
header = GetSectionHeader(".rdata", pNTHeader); |
|
if ( !header ) |
|
return; |
|
|
|
// See if there's even any debug directories to speak of... |
|
cDebugFormats = pNTHeader->OptionalHeader. |
|
DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size |
|
/ sizeof(IMAGE_DEBUG_DIRECTORY); |
|
if ( cDebugFormats == 0 ) |
|
return; |
|
|
|
offsetInto_rdata = va_debug_dir - header->VirtualAddress; |
|
debugDir = MakePtr(PIMAGE_DEBUG_DIRECTORY, base, |
|
header->PointerToRawData + offsetInto_rdata); |
|
} |
|
|
|
printf( |
|
"Debug Formats in File\n" |
|
" Type Size Address FilePtr Charactr TimeData Version\n" |
|
" --------------- -------- -------- -------- -------- -------- --------\n" |
|
); |
|
|
|
for ( i=0; i < cDebugFormats; i++ ) |
|
{ |
|
szDebugFormat = (debugDir->Type <= 6) |
|
? SzDebugFormats[debugDir->Type] : "???"; |
|
|
|
printf(" %-15s %08X %08X %08X %08X %08X %u.%02u\n", |
|
szDebugFormat, debugDir->SizeOfData, debugDir->AddressOfRawData, |
|
debugDir->PointerToRawData, debugDir->Characteristics, |
|
debugDir->TimeDateStamp, debugDir->MajorVersion, |
|
debugDir->MinorVersion); |
|
|
|
// If COFF debug info, save its address away for later. We |
|
// do the check for "PointerToSymbolTable" because some files |
|
// have bogus values for the COFF header offset. |
|
if ( (debugDir->Type == IMAGE_DEBUG_TYPE_COFF) && |
|
pNTHeader->FileHeader.PointerToSymbolTable ) |
|
{ |
|
PCOFFDebugInfo = |
|
(PIMAGE_DEBUG_INFO)(base+ debugDir->PointerToRawData); |
|
} |
|
|
|
debugDir++; |
|
} |
|
} |
|
|
|
// Function prototype (necessary because two functions recurse) |
|
void DumpResourceDirectory |
|
( |
|
PIMAGE_RESOURCE_DIRECTORY resDir, DWORD resourceBase, |
|
DWORD level, DWORD resourceType |
|
); |
|
|
|
// The predefined resource types |
|
char *SzResourceTypes[] = { |
|
"???_0", "CURSOR", "BITMAP", "ICON", "MENU", "DIALOG", "STRING", "FONTDIR", |
|
"FONT", "ACCELERATORS", "RCDATA", "MESSAGETABLE", "GROUP_CURSOR", |
|
"???_13", "GROUP_ICON", "???_15", "VERSION" |
|
}; |
|
|
|
// Get an ASCII string representing a resource type |
|
void GetResourceTypeName(DWORD type, PSTR buffer, UINT cBytes) |
|
{ |
|
if ( type <= 16 ) |
|
strncpy(buffer, SzResourceTypes[type], cBytes); |
|
else |
|
wsprintf(buffer, "%X", type); |
|
} |
|
|
|
// |
|
// If a resource entry has a string name (rather than an ID), go find |
|
// the string and convert it from unicode to ascii. |
|
// |
|
void GetResourceNameFromId |
|
( |
|
DWORD id, DWORD resourceBase, PSTR buffer, UINT cBytes |
|
) |
|
{ |
|
PIMAGE_RESOURCE_DIR_STRING_U prdsu; |
|
|
|
// If it's a regular ID, just format it. |
|
if ( !(id & IMAGE_RESOURCE_NAME_IS_STRING) ) |
|
{ |
|
wsprintf(buffer, "%X", id); |
|
return; |
|
} |
|
|
|
id &= 0x7FFFFFFF; |
|
prdsu = (PIMAGE_RESOURCE_DIR_STRING_U)(resourceBase + id); |
|
|
|
// prdsu->Length is the number of unicode characters |
|
WideCharToMultiByte(CP_ACP, 0, prdsu->NameString, prdsu->Length, |
|
buffer, cBytes, 0, 0); |
|
buffer[ min(cBytes-1,prdsu->Length) ] = 0; // Null terminate it!!! |
|
} |
|
|
|
// |
|
// Dump the information about one resource directory entry. If the |
|
// entry is for a subdirectory, call the directory dumping routine |
|
// instead of printing information in this routine. |
|
// |
|
void DumpResourceEntry |
|
( |
|
PIMAGE_RESOURCE_DIRECTORY_ENTRY resDirEntry, |
|
DWORD resourceBase, |
|
DWORD level |
|
) |
|
{ |
|
UINT i; |
|
char nameBuffer[128]; |
|
|
|
if ( resDirEntry->OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY ) |
|
{ |
|
DumpResourceDirectory( (PIMAGE_RESOURCE_DIRECTORY) |
|
((resDirEntry->OffsetToData & 0x7FFFFFFF) + resourceBase), |
|
resourceBase, level, resDirEntry->Name); |
|
return; |
|
} |
|
|
|
// Spit out the spacing for the level indentation |
|
for ( i=0; i < level; i++ ) |
|
printf(" "); |
|
|
|
if ( resDirEntry->Name & IMAGE_RESOURCE_NAME_IS_STRING ) |
|
{ |
|
GetResourceNameFromId(resDirEntry->Name, resourceBase, nameBuffer, |
|
sizeof(nameBuffer)); |
|
printf("Name: %s Offset: %08X\n", |
|
nameBuffer, resDirEntry->OffsetToData); |
|
} |
|
else |
|
{ |
|
printf("ID: %08X Offset: %08X\n", |
|
resDirEntry->Name, resDirEntry->OffsetToData); |
|
} |
|
} |
|
|
|
// |
|
// Dump the information about one resource directory. |
|
// |
|
void DumpResourceDirectory |
|
( |
|
PIMAGE_RESOURCE_DIRECTORY resDir, |
|
DWORD resourceBase, |
|
DWORD level, |
|
DWORD resourceType |
|
) |
|
{ |
|
PIMAGE_RESOURCE_DIRECTORY_ENTRY resDirEntry; |
|
char szType[64]; |
|
UINT i; |
|
|
|
// Spit out the spacing for the level indentation |
|
for ( i=0; i < level; i++ ) |
|
printf(" "); |
|
|
|
// Level 1 resources are the resource types |
|
if ( level == 1 && !(resourceType & IMAGE_RESOURCE_NAME_IS_STRING) ) |
|
{ |
|
GetResourceTypeName( resourceType, szType, sizeof(szType) ); |
|
} |
|
else // Just print out the regular id or name |
|
{ |
|
GetResourceNameFromId( resourceType, resourceBase, szType, |
|
sizeof(szType) ); |
|
} |
|
|
|
printf( |
|
"ResDir (%s) Named:%02X ID:%02X TimeDate:%08X Vers:%u.%02u Char:%X\n", |
|
szType, resDir->NumberOfNamedEntries, resDir->NumberOfIdEntries, |
|
resDir->TimeDateStamp, resDir->MajorVersion, |
|
resDir->MinorVersion,resDir->Characteristics); |
|
|
|
resDirEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resDir+1); |
|
|
|
for ( i=0; i < resDir->NumberOfNamedEntries; i++, resDirEntry++ ) |
|
DumpResourceEntry(resDirEntry, resourceBase, level+1); |
|
|
|
for ( i=0; i < resDir->NumberOfIdEntries; i++, resDirEntry++ ) |
|
DumpResourceEntry(resDirEntry, resourceBase, level+1); |
|
} |
|
|
|
// |
|
// Top level routine called to dump out the entire resource hierarchy |
|
// |
|
void DumpResourceSection(DWORD base, PIMAGE_NT_HEADERS pNTHeader) |
|
{ |
|
PIMAGE_RESOURCE_DIRECTORY resDir; |
|
|
|
resDir = GetSectionPtr(".rsrc", pNTHeader, (DWORD)base); |
|
if ( !resDir ) |
|
return; |
|
|
|
printf("Resources\n"); |
|
DumpResourceDirectory(resDir, (DWORD)resDir, 0, 0); |
|
} |
|
|
|
// |
|
// Dump the imports table (the .idata section) of a PE file |
|
// |
|
void DumpImportsSection(DWORD base, PIMAGE_NT_HEADERS pNTHeader) |
|
{ |
|
PIMAGE_IMPORT_DESCRIPTOR importDesc; |
|
PIMAGE_SECTION_HEADER header; |
|
PIMAGE_THUNK_DATA thunk; |
|
PIMAGE_IMPORT_BY_NAME pOrdinalName; |
|
DWORD exportsStartRVA, exportsEndRVA; |
|
|
|
INT delta; |
|
|
|
header = GetSectionHeader(".idata", pNTHeader); |
|
if ( !header ) |
|
return; |
|
importDesc = MakePtr(PIMAGE_IMPORT_DESCRIPTOR, base, |
|
header->PointerToRawData); |
|
delta = (INT)(header->VirtualAddress - header->PointerToRawData); |
|
|
|
printf("Imports Table:\n"); |
|
|
|
while ( 1 ) |
|
{ |
|
// See if we've reached an empty IMAGE_IMPORT_DESCRIPTOR |
|
if ( (importDesc->TimeDateStamp==0 ) && (importDesc->Name==0) ) |
|
break; |
|
|
|
printf(" %s\n", (PBYTE)(importDesc->Name) - delta + base); |
|
|
|
printf(" Hint/Name Table: %08X\n", importDesc->Characteristics); |
|
printf(" TimeDateStamp: %08X\n", importDesc->TimeDateStamp); |
|
printf(" ForwarderChain: %08X\n", importDesc->ForwarderChain); |
|
printf(" First thunk RVA: %08X\n", importDesc->FirstThunk); |
|
|
|
thunk = (PIMAGE_THUNK_DATA)importDesc->FirstThunk; |
|
thunk = (PIMAGE_THUNK_DATA)( (PBYTE)thunk - delta + base); |
|
|
|
// If the pointer that thunk points to is outside of the .idata |
|
// section, it looks like this file is "pre-fixed up" with regards |
|
// to the thunk table. In this situation, we'll need to fall back |
|
// to the hint-name (aka, the "Characteristics") table. |
|
exportsStartRVA = header->VirtualAddress; |
|
exportsEndRVA= exportsStartRVA + header->SizeOfRawData; |
|
if ( (*(PDWORD)thunk <= exportsStartRVA) || |
|
(*(PDWORD)thunk >= exportsEndRVA) ) |
|
{ |
|
if ( importDesc->Characteristics == 0 ) // Borland doesn't have |
|
return; // this table!!! |
|
|
|
thunk = (PIMAGE_THUNK_DATA)importDesc->Characteristics; |
|
if ( ((DWORD)thunk <= exportsStartRVA) || |
|
((DWORD)thunk >= exportsEndRVA) ) |
|
return; |
|
|
|
thunk = (PIMAGE_THUNK_DATA)( (PBYTE)thunk - delta + base); |
|
} |
|
|
|
printf(" Ordn Name\n"); |
|
|
|
while ( 1 ) // Loop forever (or until we break out) |
|
{ |
|
if ( thunk->u1.AddressOfData == 0 ) |
|
break; |
|
|
|
if ( thunk->u1.Ordinal & IMAGE_ORDINAL_FLAG ) |
|
{ |
|
printf(" %4u\n", thunk->u1.Ordinal & 0xFFFF); |
|
break; |
|
} |
|
else |
|
{ |
|
pOrdinalName = thunk->u1.AddressOfData; |
|
pOrdinalName = (PIMAGE_IMPORT_BY_NAME) |
|
((PBYTE)pOrdinalName - delta + base); |
|
|
|
printf(" %4u %s\n", pOrdinalName->Hint, pOrdinalName->Name); |
|
} |
|
|
|
thunk++; // Advance to next thunk |
|
} |
|
|
|
importDesc++; // advance to next IMAGE_IMPORT_DESCRIPTOR |
|
printf("\n"); |
|
} |
|
} |
|
|
|
// |
|
// Dump the exports table (the .edata section) of a PE file |
|
// |
|
void DumpExportsSection(DWORD base, PIMAGE_NT_HEADERS pNTHeader) |
|
{ |
|
PIMAGE_EXPORT_DIRECTORY exportDir; |
|
PIMAGE_SECTION_HEADER header; |
|
INT delta; |
|
PSTR filename; |
|
DWORD i; |
|
PDWORD functions; |
|
PWORD ordinals; |
|
PSTR *name; |
|
|
|
header = GetSectionHeader(".edata", pNTHeader); |
|
if ( !header ) |
|
return; |
|
exportDir = MakePtr(PIMAGE_EXPORT_DIRECTORY, base, |
|
header->PointerToRawData); |
|
delta = (INT)(header->VirtualAddress - header->PointerToRawData); |
|
|
|
filename = (PSTR)(exportDir->Name - delta + base); |
|
|
|
printf("exports table:\n\n"); |
|
printf(" Name: %s\n", filename); |
|
printf(" Characteristics: %08X\n", exportDir->Characteristics); |
|
printf(" TimeDateStamp: %08X\n", exportDir->TimeDateStamp); |
|
printf(" Version: %u.%02u\n", exportDir->MajorVersion, |
|
exportDir->MinorVersion); |
|
printf(" Ordinal base: %08X\n", exportDir->Base); |
|
printf(" # of functions: %08X\n", exportDir->NumberOfFunctions); |
|
printf(" # of Names: %08X\n", exportDir->NumberOfNames); |
|
|
|
functions = (PDWORD)((DWORD)exportDir->AddressOfFunctions - delta + base); |
|
ordinals = (PWORD)((DWORD)exportDir->AddressOfNameOrdinals - delta + base); |
|
name = (PSTR *)((DWORD)exportDir->AddressOfNames - delta + base); |
|
|
|
printf("\n Entry Pt Ordn Name\n"); |
|
for ( i=0; i < exportDir->NumberOfNames; i++ ) |
|
{ |
|
printf(" %08X %4u %s\n", *functions, |
|
*ordinals + exportDir->Base, |
|
(*name - delta + base)); |
|
name++; // Bump each pointer to the next array element |
|
ordinals++; |
|
functions++; |
|
} |
|
} |
|
|
|
// The names of the available base relocations |
|
char *SzRelocTypes[] = { |
|
"ABSOLUTE","HIGH","LOW","HIGHLOW","HIGHADJ","MIPS_JMPADDR", |
|
"I860_BRADDR","I860_SPLIT" }; |
|
|
|
// |
|
// Dump the base relocation table of a PE file |
|
// |
|
void DumpBaseRelocationsSection(DWORD base, PIMAGE_NT_HEADERS pNTHeader) |
|
{ |
|
PIMAGE_BASE_RELOCATION baseReloc; |
|
|
|
baseReloc = GetSectionPtr(".reloc", pNTHeader, base); |
|
if ( !baseReloc ) |
|
return; |
|
|
|
printf("base relocations:\n\n"); |
|
|
|
while ( baseReloc->SizeOfBlock != 0 ) |
|
{ |
|
unsigned i,cEntries; |
|
PWORD pEntry; |
|
char *szRelocType; |
|
WORD relocType; |
|
|
|
cEntries = (baseReloc->SizeOfBlock-sizeof(*baseReloc))/sizeof(WORD); |
|
pEntry = MakePtr( PWORD, baseReloc, sizeof(*baseReloc) ); |
|
|
|
printf("Virtual Address: %08X size: %08X\n", |
|
baseReloc->VirtualAddress, baseReloc->SizeOfBlock); |
|
|
|
for ( i=0; i < cEntries; i++ ) |
|
{ |
|
// Extract the top 4 bits of the relocation entry. Turn those 4 |
|
// bits into an appropriate descriptive string (szRelocType) |
|
relocType = (*pEntry & 0xF000) >> 12; |
|
szRelocType = relocType < 8 ? SzRelocTypes[relocType] : "unknown"; |
|
|
|
printf(" %08X %s\n", |
|
(*pEntry & 0x0FFF) + baseReloc->VirtualAddress, |
|
szRelocType); |
|
pEntry++; // Advance to next relocation entry |
|
} |
|
|
|
baseReloc = MakePtr( PIMAGE_BASE_RELOCATION, baseReloc, |
|
baseReloc->SizeOfBlock); |
|
} |
|
} |
|
|
|
// |
|
// Dump the COFF debug information header |
|
// |
|
void DumpCOFFHeader(PIMAGE_DEBUG_INFO pDbgInfo) |
|
{ |
|
printf("COFF Debug Info Header\n"); |
|
printf(" NumberOfSymbols: %08X\n", pDbgInfo->NumberOfSymbols); |
|
printf(" LvaToFirstSymbol: %08X\n", pDbgInfo->LvaToFirstSymbol); |
|
printf(" NumberOfLinenumbers: %08X\n", pDbgInfo->NumberOfLinenumbers); |
|
printf(" LvaToFirstLinenumber: %08X\n", pDbgInfo->LvaToFirstLinenumber); |
|
printf(" RvaToFirstByteOfCode: %08X\n", pDbgInfo->RvaToFirstByteOfCode); |
|
printf(" RvaToLastByteOfCode: %08X\n", pDbgInfo->RvaToLastByteOfCode); |
|
printf(" RvaToFirstByteOfData: %08X\n", pDbgInfo->RvaToFirstByteOfData); |
|
printf(" RvaToLastByteOfData: %08X\n", pDbgInfo->RvaToLastByteOfData); |
|
} |
|
|
|
// |
|
// top level routine called from PEDUMP.C to dump the components of a PE file |
|
// |
|
void DumpExeFile( PIMAGE_DOS_HEADER dosHeader ) |
|
{ |
|
PIMAGE_NT_HEADERS pNTHeader; |
|
DWORD base = (DWORD)dosHeader; |
|
|
|
pNTHeader = MakePtr( PIMAGE_NT_HEADERS, dosHeader, |
|
dosHeader->e_lfanew ); |
|
|
|
// First, verify that the e_lfanew field gave us a reasonable |
|
// pointer, then verify the PE signature. |
|
if ( IsBadReadPtr(pNTHeader, sizeof(IMAGE_NT_HEADERS)) || |
|
pNTHeader->Signature != IMAGE_NT_SIGNATURE ) |
|
{ |
|
printf("Unhandled EXE type, or invalid .EXE\n"); |
|
return; |
|
} |
|
|
|
DumpHeader((PIMAGE_FILE_HEADER)&pNTHeader->FileHeader); |
|
printf("\n"); |
|
|
|
DumpOptionalHeader((PIMAGE_OPTIONAL_HEADER)&pNTHeader->OptionalHeader); |
|
printf("\n"); |
|
|
|
DumpSectionTable( (PIMAGE_SECTION_HEADER)(pNTHeader+1), |
|
pNTHeader->FileHeader.NumberOfSections, TRUE); |
|
printf("\n"); |
|
|
|
DumpDebugDirectory(base, pNTHeader); |
|
printf("\n"); |
|
|
|
DumpResourceSection(base, pNTHeader); |
|
printf("\n"); |
|
|
|
DumpImportsSection(base, pNTHeader); |
|
printf("\n"); |
|
|
|
DumpExportsSection(base, pNTHeader); |
|
printf("\n"); |
|
|
|
if ( fShowRelocations ) |
|
{ |
|
DumpBaseRelocationsSection(base, pNTHeader); |
|
printf("\n"); |
|
} |
|
|
|
// |
|
// Initialize these vars here since we'll need them in DumpLineNumbers |
|
// |
|
PCOFFSymbolTable = MakePtr(PIMAGE_SYMBOL, base, |
|
pNTHeader->FileHeader.PointerToSymbolTable); |
|
COFFSymbolCount = pNTHeader->FileHeader.NumberOfSymbols; |
|
|
|
if ( fShowSymbolTable && PCOFFDebugInfo ) |
|
{ |
|
DumpCOFFHeader( PCOFFDebugInfo ); |
|
printf("\n"); |
|
} |
|
|
|
if ( fShowLineNumbers && PCOFFDebugInfo ) |
|
{ |
|
DumpLineNumbers( MakePtr(PIMAGE_LINENUMBER, PCOFFDebugInfo, |
|
PCOFFDebugInfo->LvaToFirstLinenumber), |
|
PCOFFDebugInfo->NumberOfLinenumbers); |
|
printf("\n"); |
|
} |
|
|
|
if ( fShowSymbolTable ) |
|
{ |
|
if ( pNTHeader->FileHeader.NumberOfSymbols |
|
&& pNTHeader->FileHeader.PointerToSymbolTable) |
|
{ |
|
DumpSymbolTable(PCOFFSymbolTable, COFFSymbolCount); |
|
printf("\n"); |
|
} |
|
} |
|
|
|
if ( fShowRawSectionData ) |
|
{ |
|
DumpRawSectionData( (PIMAGE_SECTION_HEADER)(pNTHeader+1), |
|
dosHeader, |
|
pNTHeader->FileHeader.NumberOfSections); |
|
} |
|
}
|
|
|