Introduction
This article accompanies a number of command line sample applications that wrap some common code of mine. This common code can be used to extract various information from PE format files (PE format stands for Portable Executable Format). The four samples are named bitness, pefileuses, dotnetsearch and pdbget.
bitness expects a file name as the command line parameter and will tell you if the file passed as an argument is a 32 bit or a 64 bit PE file. It wraps the following common code functions:
BOOL IsFile64BitPEFileW(LPCWSTR szFile, PBOOL pbIs64Bits);
BOOL IsFile64BitPEFileA(LPCSTR szFile, PBOOL pbIs64Bits);
The parameters should be pretty self-explanatory. If the function succeeds, it returns a non-zero value. If it fails, the return value is FALSE
and extended error information is available via GetLastError
. In case of success, the out
-Parameter pbIs64Bits
will contain a non-zero value if the PE file passed as parameter szFile
is 64 bits.
pefileuses is meant to determine if a given PE file links against a certain DLL or uses a function from a given DLL. It expects 3 command line parameters and optionally a fourth parameter. The first parameter is a number between zero and 2. This number determines whether the import table or the table for delayloaded functions should be scanned or both. Passing "0" means, both tables are scanned. Passing "1" means, only the import table, passing "2" means, only the table for delayloads are scanned. The second parameter is the PE file to be scanned. The third parameter denotes the DLL name that the tables should be scanned for. Finally the fourth parameter is an optional function name. The application will print on stdout
whether or not the specified binary links against the given DLL or even uses the optional function name. This tool wraps the following common code functions:
BOOL __stdcall PeFileUsesImportA(LPCSTR szPeFile, LPCSTR szDllName,
LPCSTR szFunction,
PBOOL pbUse, DWORD dwFlags);
BOOL __stdcall PeFileUsesImportW(LPCWSTR szPeFile, LPCWSTR szDllName,
LPCWSTR szFunction, PBOOL pbUse,
DWORD dwFlags);
The flags to be passed for this function are those that are passed as the first parameter to pefiluses.exe and are defined as such:
#define PUI_USE_IMPORT_ONLY 0x1
#define PUI_USE_DELAYLOAD_ONLY 0x2
Passing 0L as the dwFlags
parameter scans both tables as described above. The other parameters should be pretty self-explanatory. If the function succeeds, it returns a non-zero value. If it fails, the return value is FALSE
and extended error information is available via GetLastError
.
dotnetsearch is a tool to scan an entire directory tree and evaluate each DLL and EXE file found, whether it is a .NET binary. I wrote this tool in order to look at each new build of Windows Vista and to find out how many files on the entire Vista harddisk use the .NET-Framework. A binary that uses the .NET-Framework can be easily identified as it links against mscoree.dll. The dotnetsearch tool wraps the following common code functions:
BOOL __stdcall BinaryUsesDotNetA(LPCSTR szFileName, PBOOL pbUse);
BOOL __stdcall BinaryUsesDotNetW(LPCWSTR szFileName, PBOOL pbUse);
Again, the parameters should be pretty self-explanatory. If the function succeeds, it returns a non-zero value. If it fails, the return value is FALSE
and extended error information is available via GetLastError;
.
pdbget examines a given PE file for the location of its debugging information. Nowadays, if you use Microsoft compilers, CodeView debugging information is located in PDB files. In order for the debugger to find the PDB file for a given PE file, its file path is embedded within the PE file as an ANSI string. The pdbget sample application expects the path to the PE file to be examined as a command line parameter and will print the location of the associated pdb file on stdout. The location of debugging information in the two formats PDB 2.0 (VC 6) and PDB 7.0 (Visual Studio .NET and later) are properly detected and this tool wraps the following common code functions:
BOOL __stdcall GetPDBFileA(LPCSTR szPeFile, LPSTR szPdbFile, PDWORD pdwSize);
BOOL __stdcall GetPDBFileW(LPCWSTR szPeFile, LPWSTR szPdbFile, PDWORD pdwSize);
The first parameter to these functions is the path to the PE file to be examined. The second parameter is an optional parameter to the memory buffer, where the function will write the path of the PDB file as a null terminated string. The third parameter is the size of the memory buffer in characters and is acting as an in/out parameter. The memory buffer has to be allocated by the caller but NULL
can be specified as the szPdbFile
parameter, in which case only the required size is written to pdwSize
. This way you can first call the functions with NULL
as the second parameter and allocate the required memory with the size that has been retrieved via the third parameter. Using this allocated memory, you can then call the functions a second time to retrieve the path to the PDB file.
If the function succeeds, it returns a non-zero value. If it fails, the return value is FALSE
and extended error information is available via GetLastError
.
Using the Code
The code compiles cleanly in both UNICODE and ANSI builds and for x86 and x64 targets on Visual Studio 2005. You can also use Visual Studio 2008 and convert the project and solution files into its newer format.
Kudos
The functions that I am using in this common code of mine are mostly stolen from Matt Pietrek and a sample that came with an MSDN magazine article. All I did was to separate the functionality a bit better into reusable chunks, remove the globals that were used throughout the original code and reorganize things a bit better for proper usage in x86 and x64 builds and with the latest header files. If anything works well for you using this code, it is the sole merit of Matt Pietrek, if the code fails or breaks, it is my fault. The information about the CodeView 2.0 debugging information in PE files comes from an article from the fine folks of debuginfo.com.
History
- 1st version: 07/30/2006: Initial version
- 2nd version: 04/13/2010: Removed the VC6 support, added
pdbget
- 3rd version: 05/05/2010: The
GetPDBFile
function now determines the PDB location from x64 binaries as well, PeFileUsesImportW
doesn't convert the PE file parameter to MBCS anymore