Copyright © Microsoft Corporation. This document is an archived reproduction of a version originally published by Microsoft. It may have slight formatting modifications for consistency and to improve readability. |
//========================================== // Matt Pietrek // Microsoft Systems Journal, August 1997 // FILE: EZPE.CPP //========================================== #include <windows.h> #include <stdio.h> #include <imagehlp.h> //============================================================================= // Macros to make formatted output of numerous data structure fields easier //============================================================================= #define DISPLAY_COLUMNS "35" #define DisplayPtrField( ptr, field, fmt ) \ printf( "%-" DISPLAY_COLUMNS fmt, #field, ptr->field ); #define DisplayPtrFieldD( ptr, field ) DisplayPtrField( ptr, field, "s%08X\n" ) #define DisplayPtrFieldW( ptr, field ) DisplayPtrField( ptr, field, "s%04X\n" ) #define DisplayPtrFieldStr(ptr,field ) DisplayPtrField( ptr, field, "s%s\n" ) #define DisplayPtrVersionFields( name, ptr, field1, field2 ) \ printf("%-" DISPLAY_COLUMNS "s%u.%02u\n", name,ptr->field1,ptr->field2); #define DisplayField( struct, field, fmt ) \ printf( "%-" DISPLAY_COLUMNS fmt, #field, struct.field ); #define DisplayFieldD( struct, field ) DisplayField( struct, field, "s%08X\n") #define DisplayFieldW( struct, field ) DisplayField( struct, field, "s%04X\n") #define DisplayFieldStr(struct,field ) DisplayField( struct, field, "s%s\n") #define DisplayDataDir( ptr, x ) \ printf( "%-" DISPLAY_COLUMNS "sAddress: %08X Size: %08X\n", \ #x, ptr->DataDirectory[x].VirtualAddress, ptr->DataDirectory[x].Size ); //============================================================================= // Prototypes for helper functions //============================================================================= BOOL CALLBACK EnumSymbolsCallback( LPSTR, ULONG, ULONG, PVOID ); void ShowImageFileHeaders( PIMAGE_NT_HEADERS pNTHdrs ); void ShowSectionHeaders( PIMAGE_SECTION_HEADER pSectionHdr, DWORD cSections ); void StartNewDisplaySection( PSTR pszSectionName ); BOOL ParseCommandLine(int argc, char * argv[], PSTR pszFilename, PSTR pszPath); void ShowSymbols( PSTR pszFilename, PSTR pszPath ); //============================================================================= // Global variables //============================================================================= BOOL g_fDecoratedNames = FALSE; BOOL g_fShowSymbols = TRUE; char g_szHelp[] = "EZPE - August 1997 Microsoft Systems Journal, by Matt Pietrek\n" "Syntax: EZPE [options] <filename>\n" " -d Decorated C++ names\n" " -n No symbol display\n"; //============================================================================= // Main program - First display info about the executable, then shows symbols //============================================================================= int main( int argc, char * argv[] ) { char szFilename[MAX_PATH]; char szPath[MAX_PATH]; if ( !ParseCommandLine(argc, argv, szFilename, szPath) ) { printf( g_szHelp ); return 1; } printf( "Display of file %s\n", szFilename ); LOADED_IMAGE li; if ( MapAndLoad( szFilename, 0, &li, FALSE, TRUE) ) { StartNewDisplaySection( " LOADED_IMAGE" ); DisplayFieldStr( li, ModuleName ) DisplayFieldD( li, hFile ) DisplayFieldD( li, MappedAddress ) DisplayFieldD( li, FileHeader ) DisplayFieldD( li, LastRvaSection ) DisplayFieldD( li, NumberOfSections ) DisplayFieldD( li, Sections ) DisplayFieldD( li, Characteristics ) DisplayFieldD( li, fSystemImage ) DisplayFieldD( li, fDOSImage ) DisplayFieldD( li, Links ) DisplayFieldD( li, SizeOfImage ) StartNewDisplaySection( "PE File Headers (LOADED_IMAGE.FileHeader)" ); ShowImageFileHeaders( li.FileHeader ); StartNewDisplaySection( "Section Headers (LOADED_IMAGE.Sections)" ); ShowSectionHeaders( li.Sections, li.NumberOfSections ); } else { printf( "MapAndLoad failed - exiting\n" ); return 1; } UnMapAndLoad( &li ); if ( g_fShowSymbols ) ShowSymbols( szFilename, szPath ); return 0; } #define MY_PROCESS_HANDLE 0 //============================================================================= // Loads a symbol table and enumerates the symbols in it //============================================================================= void ShowSymbols( PSTR pszFilename, PSTR pszPath ) { PIMAGE_DEBUG_INFORMATION pidi; // // First, see if there's any debug information worth displaying. If // this functions succeeds, the PE file, and its debug information // is memory mapped in. // pidi = MapDebugInformation( 0, pszFilename, "", 0 ); if ( pidi ) { StartNewDisplaySection( "IMAGE_DEBUG_INFORMATION" ); DisplayPtrFieldD( pidi, Size ) DisplayPtrFieldD( pidi, MappedBase ) DisplayPtrFieldW( pidi, Machine ) DisplayPtrFieldW( pidi, Characteristics ) DisplayPtrFieldD( pidi, CheckSum ) DisplayPtrFieldD( pidi, ImageBase ) DisplayPtrFieldD( pidi, SizeOfImage ) DisplayPtrFieldD( pidi, NumberOfSections ) DisplayPtrFieldD( pidi, Sections ) DisplayPtrFieldD( pidi, ExportedNamesSize ) DisplayPtrFieldD( pidi, ExportedNames ) DisplayPtrFieldD( pidi, NumberOfFunctionTableEntries ) DisplayPtrFieldD( pidi, FunctionTableEntries ) DisplayPtrFieldD( pidi, LowestFunctionStartingAddress ) DisplayPtrFieldD( pidi, HighestFunctionEndingAddress ) DisplayPtrFieldD( pidi, NumberOfFpoTableEntries ) DisplayPtrFieldD( pidi, FpoTableEntries ) DisplayPtrFieldD( pidi, SizeOfCoffSymbols ) DisplayPtrFieldD( pidi, CoffSymbols ) DisplayPtrFieldD( pidi, SizeOfCodeViewSymbols ) DisplayPtrFieldD( pidi, CodeViewSymbols ) DisplayPtrFieldStr( pidi, ImageFilePath ) DisplayPtrFieldStr( pidi, ImageFileName ) DisplayPtrFieldStr( pidi, DebugFilePath ) DisplayPtrFieldD( pidi, TimeDateStamp ) DisplayPtrFieldD( pidi, RomImage ) DisplayPtrFieldD( pidi, DebugDirectory ) DisplayPtrFieldD( pidi, NumberOfDebugDirectories ) } else { printf( "MapDebugInformation failed - exiting\n" ); return; } // // Init symbol handler for "process" (in our case, "0" is the process) // if ( !SymInitialize( MY_PROCESS_HANDLE, pszPath, FALSE ) ) { printf( "MapDebugInformation failed - exiting\n" ); return; } // // If the command line specified "decorated" names (-d), set the symbol // options before loading the symbol table below. // if ( g_fDecoratedNames ) { DWORD symOptions = SymGetOptions(); symOptions &= ~SYMOPT_UNDNAME; // Turn off the SYMOPT_UNDNAME flag SymSetOptions( symOptions ); } // // Load the symbol table for the specified file. A "MappedAddress" from // MapAndLoad, or a "MappedBase" from MapDebugInformation is needed as // parameter 5. // if ( !SymLoadModule( MY_PROCESS_HANDLE, 0, pszFilename, 0, (DWORD)pidi->MappedBase, 0 ) ) { printf( "SymLoadModuleFailed\n" ); return; } IMAGEHLP_MODULE im = { sizeof(im) }; if ( SymGetModuleInfo( MY_PROCESS_HANDLE, (DWORD)pidi->MappedBase, &im ) ) { StartNewDisplaySection( "IMAGEHLP_MODULE" ); // DisplayFieldD( im, SizeOfStruct ); // Not worth showing // DisplayFieldD( im, BaseOfImage ); // DisplayFieldD( im, ImageSize ); // DisplayFieldD( im, TimeDateStamp ); DisplayFieldD( im, CheckSum ); DisplayFieldD( im, NumSyms ); DisplayFieldD( im, SymType ); PSTR pszSymType; switch( im.SymType ) { case SymNone: pszSymType = "SymNone"; break; case SymCoff: pszSymType = "SymCoff"; break; case SymCv: pszSymType = "SymCv"; break; case SymPdb: pszSymType = "SymPdb"; break; case SymExport: pszSymType = "SymExport"; break; case SymDeferred: pszSymType = "SymDeferred"; break; default: pszSymType = "?"; } printf( "%-" DISPLAY_COLUMNS "s%s\n", " ", pszSymType ); DisplayFieldStr( im, ModuleName ); DisplayFieldStr( im, ImageName ); DisplayFieldStr( im, LoadedImageName ); } StartNewDisplaySection( "Symbols" ); printf( " RVA Name\n" ); printf( "-------- ----\n" ); // // Kick off the symbol enumeration. The EnumSymbolsCallback function // is called once for each symbol. // SymEnumerateSymbols( 0, (DWORD)pidi->MappedBase, EnumSymbolsCallback, pidi->MappedBase ); SymUnloadModule( 0, (DWORD)pidi->MappedBase ); // Undo SymLoadModule SymCleanup( 0 ); // Undo the SymInitialize UnmapDebugInformation( pidi ); // Close the PE file and debug info mapping } //============================================================================= // Callback function for use by the SymEnumerateSymbols API //============================================================================= BOOL CALLBACK EnumSymbolsCallback( LPSTR SymbolName, ULONG SymbolAddress, ULONG SymbolSize, PVOID UserContext ) { // User Context is whatever was passed to SymEnumerateSymbols. Here, // we passed the mapped address of the executable. This allows us // to convert the symbol addresses we get into RVAs, below. DWORD mappedBase = (DWORD)UserContext; // print out the RVA, and the symbol name passed to us. printf( "%08X %s\n", SymbolAddress - mappedBase, SymbolName ); // // If "decorated" names were specified, and if the name is "decorated," // undecorate it so that a human readable version can be displayed. // if ( g_fDecoratedNames && ('?' == *SymbolName) ) { char szUndecoratedName[0x400]; // Make symbol name buffers for the char szDecoratedName[0x400]; // decorated & undecorated versions // Make a copy of the original SymbolName, so that we can modify it lstrcpy( szDecoratedName, SymbolName ); PSTR pEnd = szDecoratedName + lstrlen( szDecoratedName ); // Strip everything off the end until we reach a 'Z' while ( (pEnd > szDecoratedName) && (*pEnd != 'Z') ) *pEnd-- = 0; // Call the IMAGEHLP function to undecorate the name if ( 0 != UnDecorateSymbolName( szDecoratedName, szUndecoratedName, sizeof(szUndecoratedName), UNDNAME_COMPLETE | UNDNAME_32_BIT_DECODE ) ) { // End the output line with the undecorated name printf( " %s\n", szUndecoratedName ); } } return TRUE; } //============================================================================= // Shows the contents of a PE header. Called by main() //============================================================================= void ShowImageFileHeaders( PIMAGE_NT_HEADERS pNTHdrs ) { PIMAGE_FILE_HEADER pImageFileHeader = &pNTHdrs->FileHeader; DisplayPtrFieldW( pImageFileHeader, Machine ) DisplayPtrFieldW( pImageFileHeader, NumberOfSections ) DisplayPtrFieldD( pImageFileHeader, TimeDateStamp ) DisplayPtrFieldD( pImageFileHeader, PointerToSymbolTable ) DisplayPtrFieldD( pImageFileHeader, NumberOfSymbols ) DisplayPtrFieldW( pImageFileHeader, SizeOfOptionalHeader ) DisplayPtrFieldW( pImageFileHeader, Characteristics ) PIMAGE_OPTIONAL_HEADER pImageOptHeader = &pNTHdrs->OptionalHeader; DisplayPtrFieldW( pImageOptHeader, Magic ) DisplayPtrVersionFields("LinkerVersion", pImageOptHeader, MajorLinkerVersion, MinorLinkerVersion ); DisplayPtrFieldD( pImageOptHeader, SizeOfCode ) DisplayPtrFieldD( pImageOptHeader, SizeOfInitializedData ) DisplayPtrFieldD( pImageOptHeader, SizeOfUninitializedData ) DisplayPtrFieldD( pImageOptHeader, AddressOfEntryPoint ) DisplayPtrFieldD( pImageOptHeader, BaseOfCode ) DisplayPtrFieldD( pImageOptHeader, BaseOfData ) DisplayPtrFieldD( pImageOptHeader, ImageBase ) DisplayPtrFieldD( pImageOptHeader, SectionAlignment ) DisplayPtrFieldD( pImageOptHeader, FileAlignment ) DisplayPtrVersionFields("OperatingSystemVersion", pImageOptHeader, MajorOperatingSystemVersion, MinorOperatingSystemVersion ) DisplayPtrVersionFields("ImageVersion", pImageOptHeader, MajorImageVersion, MinorImageVersion ) DisplayPtrVersionFields("SubsystemVersion", pImageOptHeader, MajorSubsystemVersion, MinorSubsystemVersion ) DisplayPtrFieldD( pImageOptHeader, Win32VersionValue ) DisplayPtrFieldD( pImageOptHeader, SizeOfImage ) DisplayPtrFieldD( pImageOptHeader, SizeOfHeaders ) DisplayPtrFieldD( pImageOptHeader, CheckSum ) DisplayPtrFieldW( pImageOptHeader, Subsystem ) DisplayPtrFieldW( pImageOptHeader, DllCharacteristics ) DisplayPtrFieldD( pImageOptHeader, SizeOfStackReserve ) DisplayPtrFieldD( pImageOptHeader, SizeOfStackCommit ) DisplayPtrFieldD( pImageOptHeader, SizeOfHeapReserve ) DisplayPtrFieldD( pImageOptHeader, SizeOfHeapCommit ) DisplayPtrFieldD( pImageOptHeader, LoaderFlags ) DisplayPtrFieldD( pImageOptHeader, NumberOfRvaAndSizes ) DisplayDataDir( pImageOptHeader, IMAGE_DIRECTORY_ENTRY_EXPORT ) DisplayDataDir( pImageOptHeader, IMAGE_DIRECTORY_ENTRY_IMPORT ) DisplayDataDir( pImageOptHeader, IMAGE_DIRECTORY_ENTRY_RESOURCE ) DisplayDataDir( pImageOptHeader, IMAGE_DIRECTORY_ENTRY_EXCEPTION ) DisplayDataDir( pImageOptHeader, IMAGE_DIRECTORY_ENTRY_SECURITY ) DisplayDataDir( pImageOptHeader, IMAGE_DIRECTORY_ENTRY_BASERELOC ) DisplayDataDir( pImageOptHeader, IMAGE_DIRECTORY_ENTRY_DEBUG ) DisplayDataDir( pImageOptHeader, IMAGE_DIRECTORY_ENTRY_COPYRIGHT ) DisplayDataDir( pImageOptHeader, IMAGE_DIRECTORY_ENTRY_GLOBALPTR ) DisplayDataDir( pImageOptHeader, IMAGE_DIRECTORY_ENTRY_TLS ) DisplayDataDir( pImageOptHeader, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG ) DisplayDataDir( pImageOptHeader, IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT ) DisplayDataDir( pImageOptHeader, IMAGE_DIRECTORY_ENTRY_IAT ) } //============================================================================= // Enumerates through a section table and displays basic info for each section //============================================================================= void ShowSectionHeaders( PIMAGE_SECTION_HEADER pSectionHdr, DWORD cSections ) { printf( " # Name Address VirtSize RawSize\n" ); printf( "-- -------- -------- -------- --------\n" ); for ( unsigned i=1; i <= cSections; i++, pSectionHdr++ ) { printf( "%2u %8.8s %08X %08X %08x\n", i, pSectionHdr->Name, pSectionHdr->VirtualAddress, pSectionHdr->Misc.VirtualSize, pSectionHdr->SizeOfRawData ); } } //============================================================================= // Helper function used to provide consistent delimitation of output sections //============================================================================= void StartNewDisplaySection( PSTR pszSectionName ) { printf( "\n==== %s ====\n", pszSectionName ); } //============================================================================= // Called by main() to extract any command-line arguments, as well as the // filename to be used. Also attempts to come up with a complete path to // the directory containing the named executable. Returns this value in the // pszPath param buffer. //============================================================================= BOOL ParseCommandLine( int argc, char * argv[], PSTR pszFilename, PSTR pszPath ) { if ( argc < 2 ) return FALSE; BOOL fSawFilename = FALSE; for ( int i = 1; i < argc; i++ ) { PSTR pszArg = argv[i]; if ( *pszArg == '-' ) // Is the first character a '-' ? { *pszArg++; if ( (*pszArg=='d') || (*pszArg=='D') ) // allow "d" or "D" g_fDecoratedNames = TRUE; // set global flag else if ( (*pszArg=='n') || (*pszArg=='N') ) // allow "n" or "N" g_fShowSymbols = FALSE; // set global flag } else { if ( fSawFilename ) // We should only get here once return FALSE; PSTR pszFilenamePart; if (GetFullPathName( argv[i], MAX_PATH, pszPath, &pszFilenamePart)) { // Truncate the filename portion off the path if ( pszFilenamePart ) *pszFilenamePart = 0; // Copy the input argument to the passed in filename buffer lstrcpy( pszFilename, argv[i] ); fSawFilename = TRUE; } else // GetFullPathName failed. Ooops! return FALSE; } } return fSawFilename; }
Figure 2 EZPE Output
ezpe.mak Ezpe.out Display of file EZPE.EXE ==== LOADED_IMAGE ==== ModuleName EZPE.EXE hFile FFFFFFFF MappedAddress 008B0000 FileHeader 008B0080 LastRvaSection 008B0178 NumberOfSections 00000004 Sections 008B0178 Characteristics 0000010B fSystemImage 00000000 fDOSImage 00000000 Links 0012FFA4 SizeOfImage 00003E00 ==== PE File Headers (LOADED_IMAGE.FileHeader) ==== Machine 014C NumberOfSections 0004 TimeDateStamp 336CF53B PointerToSymbolTable 00000000 NumberOfSymbols 00000000 SizeOfOptionalHeader 00E0 Characteristics 010B Magic 010B LinkerVersion 5.00 SizeOfCode 00002200 SizeOfInitializedData 00001800 SizeOfUninitializedData 00000000 AddressOfEntryPoint 00001E59 BaseOfCode 00001000 BaseOfData 00004000 ImageBase 00400000 SectionAlignment 00001000 FileAlignment 00000200 OperatingSystemVersion 4.00 ImageVersion 0.00 SubsystemVersion 4.00 Win32VersionValue 00000000 SizeOfImage 00007000 SizeOfHeaders 00000400 CheckSum 00000000 Subsystem 0003 DllCharacteristics 0000 SizeOfStackReserve 00100000 SizeOfStackCommit 00001000 SizeOfHeapReserve 00100000 SizeOfHeapCommit 00001000 LoaderFlags 00000000 NumberOfRvaAndSizes 00000010 IMAGE_DIRECTORY_ENTRY_EXPORT Address: 00000000 Size: 00000000 IMAGE_DIRECTORY_ENTRY_IMPORT Address: 00006000 Size: 00000050 IMAGE_DIRECTORY_ENTRY_RESOURCE Address: 00000000 Size: 00000000 IMAGE_DIRECTORY_ENTRY_EXCEPTION Address: 00000000 Size: 00000000 IMAGE_DIRECTORY_ENTRY_SECURITY Address: 00000000 Size: 00000000 IMAGE_DIRECTORY_ENTRY_BASERELOC Address: 00000000 Size: 00000000 IMAGE_DIRECTORY_ENTRY_DEBUG Address: 00004000 Size: 00000054 IMAGE_DIRECTORY_ENTRY_COPYRIGHT Address: 00000000 Size: 00000000 IMAGE_DIRECTORY_ENTRY_GLOBALPTR Address: 00000000 Size: 00000000 IMAGE_DIRECTORY_ENTRY_TLS Address: 00000000 Size: 00000000 IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG Address: 00000000 Size: 00000000 IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT Address: 00000000 Size: 00000000 IMAGE_DIRECTORY_ENTRY_IAT Address: 0000613C Size: 000000EC ==== Section Headers (LOADED_IMAGE.Sections) ==== # Name Address VirtSize RawSize -- -------- -------- -------- -------- 1 .text 00001000 0000206F 00002200 2 .rdata 00004000 00000164 00000200 3 .data 00005000 00000E53 00000c00 4 .idata 00006000 000004FC 00000600 ==== IMAGE_DEBUG_INFORMATION ==== Size 0000012C MappedBase 008B0000 Machine 014C Characteristics 010B CheckSum 00000000 ImageBase 00400000 SizeOfImage 00007000 NumberOfSections 00000004 Sections 008B0178 ExportedNamesSize 00000000 ExportedNames 00000000 NumberOfFunctionTableEntries 00000000 FunctionTableEntries 00000000 LowestFunctionStartingAddress 00000000 HighestFunctionEndingAddress 00000000 NumberOfFpoTableEntries 00000005 FpoTableEntries 008AE72C SizeOfCoffSymbols 00000000 CoffSymbols 00000000 SizeOfCodeViewSymbols 00000029 CodeViewSymbols 008AE780 ImageFilePath E:\column\col47\EZPE.EXE ImageFileName EZPE.EXE DebugFilePath E:\column\col47\EZPE.EXE TimeDateStamp 336CF53B RomImage 00000000 DebugDirectory 008B2600 NumberOfDebugDirectories 00000003 ==== IMAGEHLP_MODULE ==== CheckSum 00000000 NumSyms 00000000 SymType 00000003 SymPdb ModuleName EZPE ImageName EZPE.EXE LoadedImageName E:\column\col47\EZPE.EXE ==== Symbols ==== RVA Name -------- ---- 000061BC _imp__ExitProcess 00001834 ShowImageFileHeaders 0000616C _imp__UnDecorateSymbolName 00001F92 UnmapDebugInformation 00001FEC GetStdHandle 00001F8C MapAndLoad 00006014 _IMPORT_DESCRIPTOR_IMAGEHLP 00001E14 printf 000061A8 _imp__lstrlenA 00001F80 wvsprintfA 00001D5F ParseCommandLine 00001E76 _ConvertCommandLineToArgcArgv 00005A78 g_szHelp 000061F8 _imp__wvsprintfA 0000614C _imp__UnmapDebugInformation 00001FE0 GetFullPathNameA 00006154 _imp__SymUnloadModule 000061C8 KERNEL32_NULL_THUNK_DATA 00006148 _imp__MapAndLoad 00001FDA lstrcpyA 000061B0 _imp__GetCommandLineA 00001F86 UnMapAndLoad 00001FA4 SymEnumerateSymbols 00005BE0 _ppszArgv 00005A70 g_fShowSymbols 00001FAA SymGetModuleInfo 00001292 ShowSymbols 0000613C _imp__MapDebugInformation 000061FC USER32_NULL_THUNK_DATA 00001D4A StartNewDisplaySection 00001046 main 00001FBC SymGetOptions 0000615C _imp__SymGetModuleInfo 00006140 _imp__SymInitialize 00006144 _imp__UnMapAndLoad 00002004 GetCommandLineA 000061C4 _imp__GetProcessHeap 000061A4 _imp__GetFullPathNameA 00006170 IMAGEHLP_NULL_THUNK_DATA 00006164 _imp__SymSetOptions 00005BC0 g_fDecoratedNames 00006160 _imp__SymLoadModule 00001FC8 MapDebugInformation 00001F9E SymUnloadModule 00006000 _IMPORT_DESCRIPTOR_USER32 00001FF8 HeapAlloc 00006158 _imp__SymEnumerateSymbols 00001F98 SymCleanup 00001FE6 WriteFile 000061AC _imp__lstrcpyA 00001767 EnumSymbolsCallback 00006150 _imp__SymCleanup 000061B8 _imp__GetStdHandle 00001FD4 lstrlenA 00001FB6 SymSetOptions 00006168 _imp__SymGetOptions 00001FFE GetProcessHeap 000061B4 _imp__WriteFile 00006028 _IMPORT_DESCRIPTOR_KERNEL32 00001E59 mainCRTStartup 00001FF2 ExitProcess 000061C0 _imp__HeapAlloc 00001FCE UnDecorateSymbolName 00001CEE ShowSectionHeaders 00001FC2 SymInitialize 00001FB0 SymLoadModule 0000603C _NULL_IMPORT_DESCRIPTOR