533 lines
15 KiB
C
533 lines
15 KiB
C
//--------------------
|
|
// 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);
|
|
}
|
|
}
|