|
|
/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
appparse.cpp
Abstract:
Core Engine for dumping importing information from DLL's and executables into an XML file
Used by command line appparse and web-based appparse
History:
06/07/2000 t-michkr Created
--*/
//#define PJOB_SET_ARRAY int
#include "stdafx.h"
#include <windows.h>
#include <delayimp.h>
#include <shlwapi.h>
#include <sfc.h>
#include <lmcons.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <new.h>
#include "acFileAttr.h"
// These are needed for command line compiling
#define stricmp _stricmp
// Global heap for AppParse. If 0, Process Heap is used instead.
HANDLE g_hHeap = 0;
// Global search string
char* g_szSearch = "*";
// Whether we are in "verbose" mode, or not.
bool g_fVerbose = false;
// To sort output by DLLs
bool g_fAPILogging = false;
// True if no XML tags are to be printed, false otherwise
bool g_fRaw = false;
// Whether to recurse into subdirectories.
bool g_fRecurse = false;
// Current path relative to start, used by CModule
char g_szCurrentPath[MAX_PATH] = {'\0'};
// Returns true if szFileName is a system DLL (like gdi32, user32, etc.)
bool IsSystemDLL(const char* szFileName);
// Resolve a linker name to a "normal" name (unmangle C++ names, etc.)
void LinkName2Name(char* szLinkName, char* szName);
// Just do indentation, save repetitious code
void Indent(int iLevel, FILE* pFile = stdout);
// Check if function matches global search string
bool MatchFunction(const char* szFunc);
// Go through a directory and profile EXE's.
void ProfileDirectory(char* szDirectory, HANDLE hEvent);
void* __cdecl operator new(size_t size); void __cdecl operator delete(void* pVal);
// Replace XML reserved characters like > with >
void WriteXMLOKString(char* szString, FILE* pFile);
// Parsing history for modules
class CModuleParseStack { private: struct SNode { char* szName; SNode* pNext;
SNode() { szName = 0; pNext = 0; }
~SNode() { if(szName) { delete szName; szName = 0; } } }; SNode* m_pList;
public:
// Constructor, setup empty list.
CModuleParseStack() { m_pList = 0; } // Add a name to the top of the parse stack
void PushName(char* szName) { assert(!IsBadReadPtr(szName, 1)); SNode* pNode = new SNode; pNode->szName = new char[strlen(szName)+1]; strcpy(pNode->szName, szName); pNode->pNext = m_pList; m_pList = pNode; }
// Remove a name from the top of the parse stack
void Pop() { assert(m_pList); SNode* pTemp = m_pList->pNext; delete m_pList; m_pList = pTemp; }
// Return true if module has already been parsed
bool CheckModuleParsed(char* szName) { assert(!IsBadReadPtr(szName, 1)); SNode* pNode = m_pList; while(pNode) { if(stricmp(pNode->szName, szName) == 0) return true; pNode = pNode->pNext; }
return false; }
bool IsEmpty() { return (m_pList == 0); }
void ClearParseHistory() { SNode* pNode = m_pList; while(pNode) { SNode* pNext = pNode->pNext;
delete pNode; pNode = pNext; } m_pList = 0; } };
// CFunction, an imported function and associated information.
class CFunction { private: // Name of function (if imported by name)
char* m_szName;
// Name of function actually pointed to.
char* m_szForwardName;
// Ordinal, older style importing
int m_iOrdinal;
// Quick lookup info
int m_iHint;
// Address of function, if bound
DWORD m_dwAddress;
// Whether this function is a delayed import or not.
bool m_fDelayed;
// Next function in list
CFunction* m_pNext;
// No default construction or copying allowed
CFunction(); CFunction operator=(const CFunction&);
public: CFunction(char* szName, int iHint, int iOrdinal, DWORD dwAddress, bool fDelayed) { assert(!IsBadReadPtr(szName, 1)); m_szName = new char[strlen(szName)+1]; strcpy(m_szName, szName); m_iOrdinal = iOrdinal; m_iHint = iHint; m_dwAddress = dwAddress; m_pNext = 0; m_fDelayed = fDelayed; m_szForwardName = 0; }
CFunction(const CFunction& fn) { m_szName = new char[strlen(fn.m_szName)+1]; strcpy(m_szName, fn.m_szName); m_iOrdinal = fn.m_iOrdinal; m_iHint = fn.m_iHint; m_dwAddress = fn.m_dwAddress; m_pNext = 0; m_fDelayed = fn.m_fDelayed;
if(fn.m_szForwardName) { m_szForwardName = new char[strlen(fn.m_szForwardName)+1]; strcpy(m_szForwardName, fn.m_szForwardName); } else m_szForwardName = 0; } ~CFunction() { if(m_szName) { delete m_szName; m_szName = 0; }
if(m_szForwardName) { delete m_szForwardName; m_szForwardName = 0; } }
CFunction* Next() { return m_pNext; }
char* Name() { return m_szName; }
void SetForwardName(char* szForward) { assert(!IsBadReadPtr(szForward, 1)); m_szForwardName = new char[strlen(szForward)+1]; strcpy(m_szForwardName, szForward); }
void SetNext(CFunction* pFunc) { assert(pFunc == 0 || !IsBadReadPtr(pFunc, 1)); m_pNext = pFunc; }
// Display function info, either to console
// or to XML file.
static void WriteHeader(int iIndentLevel, FILE* pFile); void WriteFunction(int iIndentLevel, FILE* pFile); };
// COrdinalImport
// A function imported by ordinal, to be resolved to a CFunction
class COrdinalImport { private: int m_iOrdinal; COrdinalImport* m_pNext; bool m_fDelayed;
COrdinalImport(); COrdinalImport(const COrdinalImport&); COrdinalImport& operator = (const COrdinalImport&);
public: COrdinalImport(int iOrd, bool fDelayed = false) { m_iOrdinal = iOrd; m_fDelayed = fDelayed; }
int GetOrdinal() { return m_iOrdinal;} bool GetDelayed() { return m_fDelayed; }
COrdinalImport* Next() { return m_pNext; }
void SetNext(COrdinalImport* pNext) { m_pNext = pNext; } };
// CModule, an executable image with imports
class CModule { friend class CGlobalModuleList; private: // The name of this module (in the form path\foo.exe)
char* m_szName;
// The name of this module relative to the starting path
char* m_szFullName;
// Base pointer of the image in memory.
void* m_pvImageBase;
// DLL's imported by this module.
CModule* m_pImportedDLLs;
// Functions imported from this module by its parent.
CFunction* m_pFunctions;
// Functions imported by ordinal from this module
COrdinalImport* m_pOrdinals;
// Image headers
PIMAGE_OPTIONAL_HEADER m_pioh; PIMAGE_SECTION_HEADER m_pish; PIMAGE_FILE_HEADER m_pifh;
// Next module in a list
CModule* m_pNext;
// Text description of any errors that may have occurred
char* m_szError;
// Whether or not this module is an OS module
bool m_fSystem; // Version info
WORD m_wDosDate; WORD m_wDosTime;
int m_nAttrCount; char** m_szAttrValues; char** m_szAttrNames;
bool WalkImportTable(); bool WalkDelayImportTable();
static void InsertFunctionSorted(CFunction* pFunc, CFunction** ppList);
bool ResolveForwardedFunctionsAndOrdinals(); bool ParseImportTables();
void InsertOrdinal(int iOrdinal, bool fDelayed = false);
CModule* FindChild(char* szName);
bool Empty();
void GetAllFunctions(CFunction** ppFunctionList);
void* RVAToPtr(const void* pAddr) { return RVAToPtr(reinterpret_cast<DWORD>(pAddr)); }
void* RVAToPtr(DWORD dwRVA);
void GetFileVerInfo(HANDLE hFile, char* szFileName);
public: CModule(char* szName); ~CModule();
bool ParseModule(HANDLE hEvent); void InsertChildModuleSorted(CModule* pcm);
// Functions to write module info to either the console or an XML file
void WriteModule(bool fTopLevel, int iIndentLevel, FILE* pFile); };
// List of all top-level modules being profiled
class CGlobalModuleList { private: CModule* m_pModules; public: CGlobalModuleList() { m_pModules = 0; } ~CGlobalModuleList() { Clear(); }
void Clear() { CModule* pMod = m_pModules; while(pMod) { CModule* pNext = pMod->m_pNext; delete pMod; pMod = pNext; } m_pModules = 0; }
void InsertModuleSorted(CModule* pMod) { assert(!IsBadReadPtr(pMod, 1)); // Special case, insert at front
if(m_pModules == 0 || stricmp(m_pModules->m_szFullName, pMod->m_szFullName) > 0) { pMod->m_pNext = m_pModules; m_pModules = pMod; return; } CModule* pPrev = m_pModules; CModule* pTemp = m_pModules->m_pNext;
while(pTemp) { if(stricmp(pTemp->m_szFullName, pMod->m_szFullName) > 0) { pMod->m_pNext = pTemp; pPrev->m_pNext = pMod;; return; } pPrev = pTemp; pTemp = pTemp->m_pNext; }
// Insert at end
pMod->m_pNext = 0; pPrev->m_pNext = pMod;; }
void Write(FILE* pFile, char* szProjectName, int iPtolemyID) { if(!g_fRaw) { fprintf(pFile, "<APPPARSERESULTS>\n"); fprintf(pFile, "<PROJECT NAME=\"%s\" ID=\"%d\">\n", szProjectName, iPtolemyID); }
CModule* pMod = m_pModules; while(pMod) { pMod->WriteModule(true, 0, pFile); pMod = pMod->m_pNext; }
if(!g_fRaw) { fprintf(pFile, "</PROJECT>\n"); fprintf(pFile, "</APPPARSERESULTS>\n"); } } };
// Global parsing history
CModuleParseStack g_ParseStack;
// Empty global module, containing all modules parsed
CGlobalModuleList g_modules;
CModule::CModule(char* szName) { assert(!IsBadReadPtr(szName, 1)); m_szName = new char[strlen(szName)+1]; strcpy(m_szName, szName);
WIN32_FIND_DATA ffd; // Only give it the full relative path if it is in this directory
// If elsewhere, give it just the filename.
HANDLE hSearch = FindFirstFile(szName, &ffd); if(hSearch == INVALID_HANDLE_VALUE) { m_szFullName = new char[strlen(m_szName) + 1]; strcpy(m_szFullName, m_szName); } else { m_szFullName = new char[strlen(m_szName) + strlen(g_szCurrentPath)+1]; strcpy(m_szFullName, g_szCurrentPath); strcat(m_szFullName, m_szName); FindClose(hSearch); }
m_pvImageBase = 0; m_pImportedDLLs = 0; m_pFunctions = 0; m_pOrdinals = 0; m_pioh = 0; m_pish = 0; m_pifh = 0; m_pNext = 0; m_szError = 0; m_fSystem = false; m_nAttrCount = 0; m_szAttrValues = 0; m_szAttrNames = 0; m_wDosDate = 0; m_wDosTime = 0; }
CModule::~CModule() { if(m_szName) { delete m_szName; m_szName = 0; }
if(m_szFullName) { delete m_szFullName; m_szFullName = 0; }
CFunction* pFunc = m_pFunctions; while(pFunc) { CFunction* pNext = pFunc->Next(); delete pFunc; pFunc = pNext; } m_pFunctions = 0; COrdinalImport* pOrd = m_pOrdinals; while(pOrd) { COrdinalImport* pNext = pOrd->Next(); delete pOrd; pOrd = pNext; } m_pOrdinals = 0;
for(int i = 0; i < m_nAttrCount; i++) { if(m_szAttrNames) { if(m_szAttrNames[i]) { delete m_szAttrNames[i]; m_szAttrNames[i] = 0; } } if(m_szAttrValues) { if(m_szAttrValues[i]) { delete m_szAttrValues[i]; m_szAttrValues[i] = 0; } }
} if(m_szAttrNames) { delete m_szAttrNames; m_szAttrNames = 0; }
if(m_szAttrValues) { delete m_szAttrValues; m_szAttrValues = 0; } }
// Return true no functions are imported from this module,
// or any of its children modules.
bool CModule::Empty() { if(m_pFunctions != 0 || m_pOrdinals != 0) return false;
CModule* pMod = m_pImportedDLLs; while(pMod) { if(!pMod->Empty()) return false; pMod = pMod->m_pNext; } return true; }
// Convert a relative virtual address to an absolute address
void* CModule::RVAToPtr(DWORD dwRVA) { assert(!IsBadReadPtr(m_pifh, sizeof(*m_pifh))); assert(!IsBadReadPtr(m_pish, sizeof(*m_pish))); assert(!IsBadReadPtr(m_pvImageBase, 1));
PIMAGE_SECTION_HEADER pish = m_pish;
// Go through each section
for (int i = 0; i < m_pifh->NumberOfSections; i++) { // If it's in this section, computer address and return it.
if ((dwRVA >= pish->VirtualAddress) && (dwRVA < (pish->VirtualAddress + pish->SizeOfRawData))) { void* pAddr = reinterpret_cast<void*>(reinterpret_cast<DWORD>(m_pvImageBase) + pish->PointerToRawData + dwRVA - pish->VirtualAddress); return pAddr; } pish++; }
// This indicates an invalid RVA, meaning an invalid image, so
// throw an exception
throw; return 0; }
// Return a pointer to the first child matching szName, false otehrwise
CModule* CModule::FindChild(char* szName) { assert(!IsBadReadPtr(szName, 1)); CModule* pMod = m_pImportedDLLs; while(pMod) { if(stricmp(pMod->m_szName, szName)==0) return pMod;
pMod = pMod->m_pNext; } return 0; }
// Add an ordinal import to the module.
void CModule::InsertOrdinal(int iOrdinal, bool fDelayed) { COrdinalImport* pNew = new COrdinalImport(iOrdinal, fDelayed); pNew->SetNext(m_pOrdinals); m_pOrdinals = pNew; }
// Add an imported function to a function list.
void CModule::InsertFunctionSorted(CFunction* pFunc, CFunction** ppList) { // Special case, insert at front
if((*ppList)== 0 || stricmp((*ppList)->Name(), pFunc->Name()) > 0) { pFunc->SetNext(*ppList); (*ppList) = pFunc; return; } CFunction* pPrev = *ppList; CFunction* pTemp = (*ppList)->Next();
while(pTemp) { // Don't insert duplicates. This is mainly for API logging only.
if(strcmp(pTemp->Name(), pFunc->Name())==0) return;
if(stricmp(pTemp->Name(), pFunc->Name()) > 0) { pFunc->SetNext(pTemp); pPrev->SetNext(pFunc); return; }
pPrev = pTemp; pTemp = pTemp->Next(); }
// Insert at end
pFunc->SetNext(0); pPrev->SetNext(pFunc); }
// Add a child module to this module.
void CModule::InsertChildModuleSorted(CModule* pcm) { // Special case, insert at front
if(m_pImportedDLLs == 0 || stricmp(m_pImportedDLLs->m_szName, pcm->m_szName) > 0) { pcm->m_pNext = m_pImportedDLLs; m_pImportedDLLs = pcm; return; } CModule* pPrev = m_pImportedDLLs; CModule* pTemp = m_pImportedDLLs->m_pNext;
while(pTemp) { if(stricmp(pTemp->m_szName, pcm->m_szName) > 0) { pcm->m_pNext = pTemp; pPrev->m_pNext = pcm;; return; } pPrev = pTemp; pTemp = pTemp->m_pNext; }
// Insert at end
pcm->m_pNext = 0; pPrev->m_pNext = pcm;; }
// Add all functions imported from this module to the function list
// Used mainly for API logging.
void CModule::GetAllFunctions(CFunction** ppFunctionList) { CFunction* pFunc = m_pFunctions; while(pFunc) { // Copy pFunc
CFunction* pNew = new CFunction(*pFunc); InsertFunctionSorted(pNew, ppFunctionList);
pFunc = pFunc->Next(); }
CModule* pMod = m_pImportedDLLs; while(pMod) { pMod->GetAllFunctions(ppFunctionList); pMod = pMod->m_pNext; } }
// Go through a modules export table and get forwarding information
// and resolve ordinal imports to name.
bool CModule::ResolveForwardedFunctionsAndOrdinals() { // Get virtual address of export table
DWORD dwVAImageDir = m_pioh->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; // Get export table info
PIMAGE_EXPORT_DIRECTORY pied = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(RVAToPtr(dwVAImageDir));
DWORD* pdwNames = reinterpret_cast<DWORD*>(RVAToPtr(pied->AddressOfNames));
WORD* pwOrdinals = reinterpret_cast<WORD*>(RVAToPtr(pied->AddressOfNameOrdinals));
DWORD* pdwAddresses = reinterpret_cast<DWORD*>(RVAToPtr(pied->AddressOfFunctions));
// Go through each entry in the export table
for(unsigned uiHint = 0; uiHint < pied->NumberOfNames; uiHint++) { // Get function name, ordinal, and address info.
char* szFunction = reinterpret_cast<char*>(RVAToPtr(pdwNames[uiHint])); int ordinal = pied->Base + static_cast<DWORD>(pwOrdinals[uiHint]); DWORD dwAddress = pdwAddresses[ordinal-pied->Base]; char* szForward = 0; // Check if this function has been forwarded to another DLL
// Function has been forwarded if address is in this section.
// NOTE: The DEPENDS 1.0 source says otherwise, but is incorrect.
if( (dwAddress >= dwVAImageDir) && (dwAddress < (dwVAImageDir + m_pioh->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size))) szForward = reinterpret_cast<char*>(RVAToPtr(dwAddress));
// Check if we have an ordinal import refering to this
COrdinalImport* pOrd = m_pOrdinals; CFunction* pFunc = 0; // See if we have a matching ordinal import.
while(pOrd) { if(pOrd->GetOrdinal() == ordinal) break; pOrd = pOrd->Next(); }
if(pOrd != 0) { char szTemp[1024];
// Unmangle forwarded name.
LinkName2Name(szFunction, szTemp);
// Check against search string
if(MatchFunction(szTemp)) { // Insert into module.
pFunc = new CFunction(szTemp, -1, ordinal, dwAddress, pOrd->GetDelayed()); InsertFunctionSorted(pFunc, &m_pFunctions); } } // No matching ordinal import, check normal imports.
else { // Duck out early if this function isn't used in the executable.
pFunc = m_pFunctions; while(pFunc) { if(strcmp(pFunc->Name(), szFunction)==0) break;
pFunc = pFunc->Next(); }
if(pFunc == 0) continue; }
// Set forwarding info
if(szForward && pFunc) pFunc->SetForwardName(szForward); }
return true; }
// Get delayed import info from module.
bool CModule::WalkDelayImportTable() { // Bail early if no delayed import table.
if(m_pioh->DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].Size == 0) return true;
// Locate the directory section
DWORD dwVAImageDir = m_pioh->DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].VirtualAddress;
// Get the import descriptor array
PImgDelayDescr pidd = reinterpret_cast<PImgDelayDescr>(RVAToPtr(dwVAImageDir));
while(pidd->pINT) { char* szName; if(pidd->grAttrs & 1) szName = reinterpret_cast<char*>(RVAToPtr(pidd->szName)); else szName = reinterpret_cast<char*>(RVAToPtr(pidd->szName - m_pioh->ImageBase));
PIMAGE_THUNK_DATA pitdf; if(pidd->grAttrs & 1) pitdf = reinterpret_cast<PIMAGE_THUNK_DATA>(RVAToPtr(pidd->pINT)); else pitdf = reinterpret_cast<PIMAGE_THUNK_DATA>(RVAToPtr( reinterpret_cast<DWORD>(pidd->pINT) - static_cast<DWORD>(m_pioh->ImageBase)));
// Locate child module, or create new if it does not exist.
CModule* pcm = FindChild(szName); if(!pcm) { pcm = new CModule(szName); InsertChildModuleSorted(pcm); }
// Loop through all imported functions
while(pitdf->u1.Ordinal) { int iOrdinal; int iHint;
// Check if imported by name or ordinal
if(!IMAGE_SNAP_BY_ORDINAL(pitdf->u1.Ordinal)) { // Get name import info
PIMAGE_IMPORT_BY_NAME piibn = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>( RVAToPtr(pitdf->u1.AddressOfData - m_pioh->ImageBase));
char* szTemp = reinterpret_cast<char*>(piibn->Name); char szBuffer[1024];
// Unmangle link name
LinkName2Name(szTemp, szBuffer);
// Ordinal info is invalid
iOrdinal = -1;
iHint = piibn->Hint;
// Check against search string
if(MatchFunction(szBuffer)) { // Insert into function list
CFunction* psf = new CFunction(szBuffer, iHint, iOrdinal, static_cast<DWORD>(-1), true);
pcm->InsertFunctionSorted(psf, &pcm->m_pFunctions); } } else { // Insert a new delayed ordinal import
iOrdinal = static_cast<int>(IMAGE_ORDINAL(pitdf->u1.Ordinal));
pcm->InsertOrdinal(iOrdinal, true); }
// Move on to next function
pitdf++; }
// Move to next delay import descriptor
pidd++; }
return true; }
// Determine all functions imported by this module
bool CModule::WalkImportTable() { // Bail out early if no directory
if(m_pioh->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size == 0) return true;
// Locate the directory section
DWORD dwVAImageDir = m_pioh->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
// Get the import descriptor array
PIMAGE_IMPORT_DESCRIPTOR piid = reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(RVAToPtr(dwVAImageDir));
// Loop through all imported modules
while(piid->FirstThunk || piid->OriginalFirstThunk) {
// Get module name
char* szName = reinterpret_cast<char*>(RVAToPtr(piid->Name));
// Find child, or create new if it does not exist.
CModule* pcm = FindChild(szName); if(!pcm) { pcm = new CModule(szName); InsertChildModuleSorted(pcm); }
// Get all imports from this module
PIMAGE_THUNK_DATA pitdf = 0; PIMAGE_THUNK_DATA pitda = 0;
// Check for MS or Borland format
if(piid->OriginalFirstThunk) { // MS format, function array is original first thunk
pitdf = reinterpret_cast<PIMAGE_THUNK_DATA>(RVAToPtr(piid->OriginalFirstThunk));
// If the time stamp is set, this module has
// been bound and the first thunk is the bound address array
if(piid->TimeDateStamp) pitda = reinterpret_cast<PIMAGE_THUNK_DATA>(RVAToPtr(piid->FirstThunk)); } else { // Borland format uses first thunk for function array
pitdf = reinterpret_cast<PIMAGE_THUNK_DATA>(RVAToPtr(piid->FirstThunk)); }
// Loop through all imported functions
while(pitdf->u1.Ordinal) { int iOrdinal; int iHint;
// Determine if imported by ordinal or name
if(!IMAGE_SNAP_BY_ORDINAL(pitdf->u1.Ordinal)) { // Get name import info
PIMAGE_IMPORT_BY_NAME piibn = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>( RVAToPtr(pitdf->u1.AddressOfData));
// Get function name
char* szTemp = reinterpret_cast<char*>(piibn->Name);
// Unmangle
char szBuffer[1024]; LinkName2Name(szTemp, szBuffer);
iOrdinal = -1; iHint = piibn->Hint;
// Check against search string
if(MatchFunction(szBuffer)) { // Insert into function list
CFunction* psf = new CFunction(szBuffer, iHint, iOrdinal, pitda ? pitda->u1.Function : static_cast<DWORD>(-1), false);
pcm->InsertFunctionSorted(psf, &pcm->m_pFunctions); } } else { // Insert an ordinal import into the module.
iOrdinal = static_cast<int>(IMAGE_ORDINAL(pitdf->u1.Ordinal)); pcm->InsertOrdinal(iOrdinal); }
// Move to next function
pitdf++;
if(pitda) pitda++; }
// Move to next module
piid++; }
return true; }
// Parse all import tables
bool CModule::ParseImportTables() { return (WalkImportTable() && WalkDelayImportTable()); }
// Load a module into memory, and parse it.
bool CModule::ParseModule(HANDLE hEvent) { // Cancel parsing if user canceled
if(hEvent && WaitForSingleObject(hEvent, 0)==WAIT_OBJECT_0) return false;
bool fSucceeded = false; HANDLE hFile = INVALID_HANDLE_VALUE; HANDLE hMap = 0;
bool fPushed = false;
m_pvImageBase = 0;
// Wrap in a __try block, because an invalid executable image
// may have bad pointers in our memory mapped region.
__try { // Open the file
char szFileName[1024]; char* szJunk; if(!SearchPath(0, m_szName, 0, 1024, szFileName, &szJunk)) { m_szError = "Unable to find file"; __leave; }
hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); if(hFile == INVALID_HANDLE_VALUE) { m_szError = "Unable to open file"; __leave; }
GetFileVerInfo(hFile, szFileName);
// Map the file into memory
hMap = CreateFileMapping(hFile, 0, PAGE_READONLY, 0, 0, 0); if(hMap == 0) { m_szError = "Unable to map file"; __leave; }
m_pvImageBase = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0); if(m_pvImageBase == 0) { m_szError = "Unable to map file"; __leave; }
// Get header information and verify this is a valid executable
// Get the MS-DOS compatible header
PIMAGE_DOS_HEADER pidh = reinterpret_cast<PIMAGE_DOS_HEADER>(m_pvImageBase); if(pidh->e_magic != IMAGE_DOS_SIGNATURE) { m_szError = "Invalid image, no MS-DOS header"; __leave; }
// Get the NT header and verify
PIMAGE_NT_HEADERS pinth = reinterpret_cast<PIMAGE_NT_HEADERS>( reinterpret_cast<DWORD>(m_pvImageBase) + pidh->e_lfanew);
if(pinth->Signature != IMAGE_NT_SIGNATURE) { // Not a valid Win32 executable, may be a Win16 or OS/2 exe
m_szError = "Invalid image, no PE signature"; __leave; }
// Get the other headers
m_pifh = &pinth->FileHeader; m_pioh = &pinth->OptionalHeader; m_pish = IMAGE_FIRST_SECTION(pinth);
// Check if anyone is importing
// functions from us, and if so resolve
// function forwarding and ordinals
if(m_pFunctions || m_pOrdinals) { if(!ResolveForwardedFunctionsAndOrdinals()) __leave; }
// Parse import tables (only if not a system DLL or if parsing
// this module may result in a dependency loop)
m_fSystem = IsSystemDLL(m_szName); if(!m_fSystem && !g_ParseStack.CheckModuleParsed(m_szName)) { // Add to parse stack
g_ParseStack.PushName(m_szName); fPushed = true;
// Parse
if(!ParseImportTables()) __leave; } // Loop through each DLL imported
CModule* pModule = m_pImportedDLLs; while(pModule) { // Parse each child module
pModule->ParseModule(hEvent); pModule = pModule->m_pNext; }
fSucceeded = true; } __except(EXCEPTION_EXECUTE_HANDLER) { m_szError = "Unable to parse module"; fSucceeded = false; }
// Cleanup . . .
if(m_pvImageBase) UnmapViewOfFile(m_pvImageBase);
if(hMap != 0) CloseHandle(hMap);
if(hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile);
if(fPushed) g_ParseStack.Pop();
return fSucceeded; }
void CModule::GetFileVerInfo(HANDLE hFile, char* szFileName) { if(g_fRaw || !g_fVerbose) return;
// Get file version info
HANDLE hVersionInfo = ReadFileAttributes(szFileName, &m_nAttrCount);
// Get date info
BY_HANDLE_FILE_INFORMATION fileInfo; GetFileInformationByHandle(hFile, &fileInfo); FILETIME ftDate; memcpy(&ftDate, &fileInfo.ftLastWriteTime, sizeof(FILETIME)); CoFileTimeToDosDateTime(&ftDate, &m_wDosDate, &m_wDosTime);
if(m_nAttrCount) { m_szAttrValues = new char*[m_nAttrCount]; m_szAttrNames = new char*[m_nAttrCount]; ZeroMemory(m_szAttrValues, sizeof(char*)*m_nAttrCount); ZeroMemory(m_szAttrNames, sizeof(char*)*m_nAttrCount); if(hVersionInfo) for(int i = 0; i < m_nAttrCount; i++) { char* szVal = GetAttrValue(hVersionInfo, i); if(szVal) { m_szAttrValues[i] = new char[strlen(szVal)+1]; strcpy(m_szAttrValues[i], szVal);
char* szAttrName = GetAttrNameXML(i); if(szAttrName) { m_szAttrNames[i] = new char[strlen(szAttrName)+1]; strcpy(m_szAttrNames[i], szAttrName); } } } }
if(hVersionInfo) CleanupFileManager(hVersionInfo); }
// Return true if module is a system DLL, false otherwise
// We use the system file protection system, and assume all system
// files are protected.
bool IsSystemDLL(const char* szFileName) { char szBuffer[1024], *szJunk;
if(!SearchPath(0, szFileName, 0, 1024, szBuffer, &szJunk)) return false;
// Only check DLL's
if(!StrStrI(szFileName, ".dll")) return false;
wchar_t* wszFileName = new wchar_t[strlen(szBuffer) + 1]; MultiByteToWideChar(CP_ACP, 0, szBuffer, strlen(szBuffer)+1, wszFileName, strlen(szBuffer)+1);
bool fRet = (SfcIsFileProtected(0, wszFileName) != FALSE); delete wszFileName;
return fRet; }
// LinkName2Name()
// Resolve name mangling
void LinkName2Name(char* szLinkName, char* szName) { /*
* the link name is expected like ?Function@Class@@Params * to be converted to Class::Function */
static CHAR arrOperators[][8] = { "", "", "new", "delete", "=", ">>", "<<", "!", "==", "!=" };
DWORD dwCrr = 0; DWORD dwCrrFunction = 0; DWORD dwCrrClass = 0; DWORD dwSize; BOOL fIsCpp = FALSE; BOOL fHasClass = FALSE; BOOL fIsContructor = FALSE; BOOL fIsDestructor = FALSE; BOOL fIsOperator = FALSE; DWORD dwOperatorIndex = 0; bool fIsStdcall = false, fIsFastcall = false; char szFunction[1024]; char szClass[1024];
// Unmangle stdcall and fastcall names
char* szAtSymbol = strrchr(szLinkName, '@'); fIsFastcall = (szLinkName[0] == '@') && szAtSymbol && isdigit(szAtSymbol[1]); fIsStdcall = (szLinkName[0] == '_') && szAtSymbol && isdigit(szAtSymbol[1]); if(fIsFastcall || fIsStdcall) { szLinkName++;
// Modifying the link name, so make a copy.
// The file is mapped as read-only, and if it
// were read/write, changes would be made to the
// executable.
char* szTemp = new char[strlen(szLinkName)+1]; strcpy(szTemp, szLinkName); szLinkName = szTemp;
*(strchr(szLinkName, '@'))= '\0';
// ?????
// I think we need to keep going, because it is possible
// to have C++ name mangling on a stdcall name.
}
if (*szLinkName == '@') szLinkName++;
dwSize = lstrlen(szLinkName);
/*
* skip '?' */ while (dwCrr < dwSize) { if (szLinkName[dwCrr] == '?') {
dwCrr++; fIsCpp = TRUE; } break; }
/*
* check to see if this is a special function (like ??0) */ if (fIsCpp) {
if (szLinkName[dwCrr] == '?') {
dwCrr++;
/*
* the next digit should tell as the function type */ if (isdigit(szLinkName[dwCrr])) {
switch (szLinkName[dwCrr]) {
case '0': fIsContructor = TRUE; break; case '1': fIsDestructor = TRUE; break; default: fIsOperator = TRUE; dwOperatorIndex = szLinkName[dwCrr] - '0'; break; } dwCrr++; } } }
/*
* get the function name */ while (dwCrr < dwSize) {
if (szLinkName[dwCrr] != '@') {
szFunction[dwCrrFunction] = szLinkName[dwCrr]; dwCrrFunction++; dwCrr++; } else { break; } } szFunction[dwCrrFunction] = '\0';
if (fIsCpp) { /*
* skip '@' */ if (dwCrr < dwSize) {
if (szLinkName[dwCrr] == '@') { dwCrr++; } }
/*
* get the class name (if any) */ while (dwCrr < dwSize) {
if (szLinkName[dwCrr] != '@') {
fHasClass = TRUE; szClass[dwCrrClass] = szLinkName[dwCrr]; dwCrrClass++; dwCrr++; } else { break; } } szClass[dwCrrClass] = '\0'; }
/*
* print the new name */ if (fIsContructor) { sprintf(szName, "%s::%s", szFunction, szFunction); } else if (fIsDestructor) { sprintf(szName, "%s::~%s", szFunction, szFunction); } else if (fIsOperator) { sprintf(szName, "%s::operator %s", szFunction, arrOperators[dwOperatorIndex]); } else if (fHasClass) { sprintf(szName, "%s::%s", szClass, szFunction); } else { sprintf(szName, "%s", szFunction); }
// stdcall and fastcall unmangling do a slight modification to
// the link name, we need to free it here.
if(fIsStdcall || fIsFastcall) delete szLinkName; }
// Parse a top level module
void ParseHighLevelModule(char* szName, HANDLE hEvent) { // Create a new module
CModule* pModule = new CModule(szName);
assert(g_ParseStack.IsEmpty()); g_ParseStack.ClearParseHistory();
pModule->ParseModule(hEvent);
// Add to global module list
g_modules.InsertModuleSorted(pModule); }
// Functions to print to console or XML file
// Just do indentation, save repetitious code
void Indent(int iLevel, FILE* pFile) { for(int i = 0; i < iLevel; i++) fprintf(pFile, "\t"); }
// Write function header info for raw output
void CFunction::WriteHeader(int iIndentLevel, FILE* pFile) { if(g_fVerbose && g_fRaw) { Indent(iIndentLevel, pFile); fprintf(pFile, "%-40s%-10s%-6s%-8s%-40s%-6s\n", "Name", "Address", "Hint", "Ordinal", "Forwarded to", "Delayed"); } }
// Write a function, raw or XML
void CFunction::WriteFunction(int iIndentLevel, FILE* pFile) { Indent(iIndentLevel, pFile);
if(!g_fRaw) { if(g_fVerbose) { fprintf(pFile, "<FUNCTION NAME=\""); WriteXMLOKString(m_szName, pFile); fprintf(pFile, "\" ", m_szName);
if(m_dwAddress != static_cast<DWORD>(-1)) fprintf(pFile, "ADDRESS=\"0x%x\" ", m_dwAddress);
if(m_iHint != -1) fprintf(pFile, "HINT=\"%d\" ", m_iHint); if(m_iOrdinal != -1) fprintf(pFile, "ORDINAL=\"%d\" ", m_iOrdinal); if(m_szForwardName != 0) { fprintf(pFile, "FORWARD_TO=\""); WriteXMLOKString(m_szForwardName, pFile); fprintf(pFile, "\" "); }
fprintf(pFile, "DELAYED=\"%s\"/>\n", m_fDelayed ? "true" : "false"); } else { fprintf(pFile, "<FUNCTION NAME=\""); WriteXMLOKString(m_szName, pFile); fprintf(pFile, "\"/>\n"); } } else { if(g_fVerbose) { char szAddress[16] = "N/A"; if(m_dwAddress != static_cast<DWORD>(-1)) sprintf(szAddress, "0x%x", m_dwAddress);
char szOrdinal[16] = "N/A"; if(m_iOrdinal != -1) sprintf(szOrdinal, "0x%x", m_iOrdinal);
char szHint[16] = "N/A"; if(m_iHint != -1) sprintf(szHint, "%d", m_iHint);
fprintf(pFile, "%-40s%-10s%-6s%-8s%-40s%-6s\n", m_szName, szAddress, szHint, szOrdinal, m_szForwardName ? m_szForwardName : "N/A", m_fDelayed ? "true" : "false"); } else { fprintf(pFile, "%s\n", m_szName); } } }
// Write an XML-compliant string (no <'s and >'s, replace with >, <, etc.)
void WriteXMLOKString(char* szString, FILE* pFile) { const int c_nChars = 5; char acIllegal[] = {'<','>', '&', '\'', '\"'}; char* szEntities[] = {"<", ">", "&", "'", """};
while(*szString) { int i; for(i = 0; i < c_nChars; i++) { if(*szString == acIllegal[i]) { fprintf(pFile, szEntities[i]); break; } } if(i == c_nChars) fputc(*szString, pFile); szString++; } }
// Write an entire module as output, either raw or XML.
void CModule::WriteModule(bool fTopLevel, int iIndentLevel, FILE* pFile) { if(Empty() && m_szError == 0) return;
Indent(iIndentLevel, pFile);
if(!g_fRaw) { if(fTopLevel) fprintf(pFile, "<EXE NAME=\""); else fprintf(pFile, "<DLL NAME=\""); WriteXMLOKString(m_szFullName, pFile); fprintf(pFile,"\">\n"); } else { fprintf(pFile, "%s:\n", m_szFullName); }
if(!g_fRaw && g_fVerbose && (m_nAttrCount || m_wDosDate)) { Indent(iIndentLevel + 1, pFile); fprintf(pFile, "<INFO>\n"); // Print out date information
Indent(iIndentLevel + 1, pFile); fprintf(pFile, "<DATE>%d/%d/%d</DATE>\n", (m_wDosDate & 0x1E0) >> 5, m_wDosDate & 0x1F, ((m_wDosDate & 0xFE00) >> 9) + 1980);
for(int i = 0; i < m_nAttrCount; i++) { if(m_szAttrValues[i]) { if(m_szAttrNames[i]) { if(strlen(m_szAttrNames[i]) != 0) { Indent(iIndentLevel+1, pFile); fprintf(pFile, "<"); WriteXMLOKString(m_szAttrNames[i], pFile); fprintf(pFile,">"); WriteXMLOKString(m_szAttrValues[i], pFile); fprintf(pFile,"</"); WriteXMLOKString(m_szAttrNames[i], pFile); fprintf(pFile, ">\n"); } } } } Indent(iIndentLevel + 1, pFile); fprintf(pFile, "</INFO>\n"); }
// If an error occured in parsing
if(m_szError) { Indent(iIndentLevel+1, pFile); if(!g_fRaw) { fprintf(pFile, "<ERROR TYPE=\""); WriteXMLOKString(m_szError, pFile); fprintf(pFile,"\"/>\n"); } else fprintf(pFile, "Parse Error: %s\n", m_szError); }
if(g_fVerbose) { Indent(iIndentLevel+1, pFile);
if(m_fSystem) { if(!g_fRaw) fprintf(pFile, "<SYSTEMMODULE VALUE=\"1\"/>\n"); else fprintf(pFile, "(System Module)\n"); } else { if(!g_fRaw) fprintf(pFile, "<SYSTEMMODULE VALUE =\"0\"/>\n"); else fprintf(pFile, "(Private Module)\n"); } }
// Print all functions imported from this module
if(g_fAPILogging && fTopLevel) { CFunction* pAllFunctions = 0; GetAllFunctions(&pAllFunctions);
if(pAllFunctions) pAllFunctions->WriteHeader(iIndentLevel+1, pFile); while(pAllFunctions) { CFunction* pOld; pAllFunctions->WriteFunction(iIndentLevel+1, pFile); pOld = pAllFunctions; pAllFunctions = pAllFunctions->Next(); delete pOld; } } else {
CFunction* pFunc = m_pFunctions; if(pFunc) pFunc->WriteHeader(iIndentLevel, pFile);
while(pFunc) { pFunc->WriteFunction(iIndentLevel, pFile); pFunc = pFunc->Next(); }
CModule* pMod = m_pImportedDLLs; while(pMod) { pMod->WriteModule(false, iIndentLevel + 1, pFile); pMod = pMod->m_pNext; } }
Indent(iIndentLevel, pFile); if(!g_fRaw) { if(fTopLevel) fprintf(pFile, "</EXE>\n"); else fprintf(pFile, "</DLL>\n"); }
fprintf(pFile, "\n");
// Child modules no longer needed, delete
CModule* pMod = m_pImportedDLLs; while(pMod) { CModule* pNext = pMod->m_pNext; delete pMod; pMod = pNext; } m_pImportedDLLs = 0; }
// Write out the XML header
void WriteXMLHeader(FILE* pFile) { if(g_fRaw) return;
static char* szMonths[] = {"", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
static char* szDays[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
SYSTEMTIME st; GetLocalTime(&st);
fprintf(pFile, "<?xml version = \"1.0\"?>\n"); fprintf(pFile, "<!--\n"); fprintf(pFile, "\tAppParse Datafile\n"); fprintf(pFile, "\tGenerated: %s, %s %d, %d %2d:%2d:%2d\n", szDays[st.wDayOfWeek], szMonths[st.wMonth], st.wDay, st.wYear, st.wHour, st.wMinute, st.wSecond);
fprintf(pFile, "-->\n\n"); }
// Return true if function name matches search string, false otherwise.
bool MatchFunction(const char* szFunc) { if(strcmp(g_szSearch, "*") == 0) return true;
char* szSearch = g_szSearch; while(*szSearch != '\0' && *szFunc != '\0') { // If we get a ?, we don't care and move on to the next
// character.
if(*szSearch == '?') { szSearch++; szFunc++; continue; }
// If we have a wildcard, move to next search string and search for substring
if(*szSearch == '*') { char* szCurrSearch; szSearch++;
if(*szSearch == '\0') return true;
// Don't change starting point.
szCurrSearch = szSearch; for(;;) { // We're done if we hit another wildcard
if(*szCurrSearch == '*' || *szCurrSearch == '?') { // Update the permanent search position.
szSearch = szCurrSearch; break; } // At end of both strings, return true.
if((*szCurrSearch == '\0') && (*szFunc == '\0')) return true;
// We never found it
if(*szFunc == '\0') return false;
// If it doesn't match, start over
if(toupper(*szFunc) != toupper(*szCurrSearch)) { // If mismatch on first character
// of search string, move to next
// character in function string.
if(szCurrSearch == szSearch) szFunc++; else szCurrSearch = szSearch; } else { szFunc++; szCurrSearch++; } } } else { if(toupper(*szFunc) != toupper(*szSearch)) { return false; }
szFunc++; szSearch++; } }
if((*szFunc == 0) && ((*szSearch == '\0') || (strcmp(szSearch,"*")==0))) return true; else return false; }
// Profile an entire directory
void ProfileDirectory(char* szDirectory, HANDLE hEvent) { if(!SetCurrentDirectory(szDirectory)) return; WIN32_FIND_DATA ffd;
// Find and parse all EXE's.
HANDLE hSearch = FindFirstFile("*.exe", &ffd); if(hSearch != INVALID_HANDLE_VALUE) { do { ParseHighLevelModule(ffd.cFileName, hEvent); // Terminate parsing if user canceled
if(hEvent && WaitForSingleObject(hEvent, 0)==WAIT_OBJECT_0) { FindClose(hSearch); SetCurrentDirectory(".."); return; } } while(FindNextFile(hSearch, &ffd));
FindClose(hSearch); }
// See if we should go deeper into directories.
if(g_fRecurse) { hSearch = FindFirstFile("*", &ffd); if(hSearch == INVALID_HANDLE_VALUE) { SetCurrentDirectory(".."); return; }
do { if(GetFileAttributes(ffd.cFileName) & FILE_ATTRIBUTE_DIRECTORY) { // Don't do an infinite recursion.
if(ffd.cFileName[0] != '.') { int nCurrLength = strlen(g_szCurrentPath); strcat(g_szCurrentPath, ffd.cFileName); strcat(g_szCurrentPath, "\\"); ProfileDirectory(ffd.cFileName, hEvent);
g_szCurrentPath[nCurrLength] = '\0'; }
// Terminate search if user signaled
if(hEvent && WaitForSingleObject(hEvent, 0)==WAIT_OBJECT_0) { FindClose(hSearch); SetCurrentDirectory(".."); return; } } } while(FindNextFile(hSearch, &ffd)); }
FindClose(hSearch);
SetCurrentDirectory(".."); }
void* __cdecl operator new(size_t size) { void* pv = 0; if(!g_hHeap) pv = HeapAlloc(GetProcessHeap(), 0, size); else pv = HeapAlloc(g_hHeap, 0, size);
if(!pv) { MessageBox(0, TEXT("Out of memory, terminating."), TEXT("ERROR"), MB_OK | MB_ICONERROR); exit(-1); }
return pv; }
void __cdecl operator delete(void* pVal) { if(g_hHeap) HeapFree(g_hHeap, 0, pVal); else HeapFree(GetProcessHeap(), 0, pVal); }
DWORD __stdcall AppParse(char* szAppName, FILE* pFile, bool fRaw, bool fAPILogging, bool fRecurse, bool fVerbose, char* szSearchKey, int iPtolemyID, HANDLE hEvent) { g_fRaw = fRaw; g_fAPILogging = fAPILogging; g_fVerbose = fVerbose; g_szSearch = szSearchKey; g_fRecurse = fRecurse;
bool fProfileDirectory = false; // Check if it is a directory, or a regular file.
DWORD dwAttributes = GetFileAttributes(szAppName); if(dwAttributes != static_cast<DWORD>(-1) && (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)) fProfileDirectory = true; // Check for directory profiling
if(fProfileDirectory) { // Search for all EXE's in this Directory
// Remove trailing \, if present
if(szAppName[strlen(szAppName)-1]== '\\') szAppName[strlen(szAppName)-1] = '\0';
char szBuff[MAX_PATH]; strcpy(szBuff, szAppName);
// If we're profiling a drive, don't include
// the drive letter in the path
if(szBuff[strlen(szBuff)-1]==':') { *g_szCurrentPath='\0'; } else { if(strrchr(szBuff, '\\')) strcpy(g_szCurrentPath, strrchr(szBuff, '\\')+1); else strcpy(g_szCurrentPath, szBuff); strcat(g_szCurrentPath, "\\"); } ProfileDirectory(szAppName, hEvent); } else { // Maybe they left off the .exe
if(GetFileAttributes(szAppName) == static_cast<DWORD>(-1)) { char szBuffer[MAX_PATH+1]; strcpy(szBuffer, szAppName); strcat(szBuffer, ".exe"); dwAttributes = GetFileAttributes(szBuffer); if(dwAttributes == static_cast<DWORD>(-1)) { return ERROR_FILE_NOT_FOUND; } szAppName = szBuffer; }
// Get the directory name
char szBuffer[MAX_PATH+1]; strcpy(szBuffer, szAppName);
char* p; for(p = &szBuffer[strlen(szBuffer)]; p != szBuffer; p--) { if(*p == '\\') { *p = '\0'; break; } }
if(p != szBuffer) { SetCurrentDirectory(szBuffer); szAppName = p+1; } ParseHighLevelModule(szAppName, hEvent); }
char* szProjectName = ""; if(fProfileDirectory) { // If a directory, get the volume name
if(strrchr(szAppName, '\\')) szAppName = strrchr(szAppName, '\\') + 1;
// If we're profiling a drive, get volume name
if(szAppName[strlen(szAppName)-1]==':') { char szBuffer[MAX_PATH]; if(GetVolumeInformation(szAppName, szBuffer, MAX_PATH, 0, 0, 0, 0, 0)) szProjectName = szBuffer; else szProjectName = szAppName; } else szProjectName = szAppName; } else { szProjectName = szAppName; char* szExtension = strstr(szAppName, ".exe");
if(szExtension) *szExtension = '\0'; }
// Only write if there wasn't an event object, or user canceled.
if(!hEvent || WaitForSingleObject(hEvent, 0) != WAIT_OBJECT_0) { // Write all output
WriteXMLHeader(pFile); g_modules.Write(pFile, szProjectName, iPtolemyID); }
g_modules.Clear();
return ERROR_SUCCESS; }
|