|
|
#include "stdafx.h"
#include <objbase.h>
#include <delayimp.h>
#include "depends.h"
#include "other.h"
//******************************************************************************
// CDepends :: Constructor/Destructor
//******************************************************************************
CDepends::CDepends() : m_hFile(NULL), m_lpvFile(NULL), m_pIFH(NULL), m_pIOH(NULL), m_pISH(NULL), m_fOutOfMemory(FALSE), m_fCircularError(FALSE), m_fMixedMachineError(FALSE), m_dwMachineType((DWORD)-1), m_pModuleRoot(NULL), m_cxOrdinal(0), m_cxHint(0), m_cImports(0), m_cExports(0) { m_cstrlstListOfBrokenLinks.RemoveAll(); m_iNumberOfBrokenLinks = 0; } //******************************************************************************
CDepends::~CDepends() { }
//******************************************************************************
BOOL CDepends::SetInitialFilename(LPCSTR szPath) {
// Set our current directory to the directory that the file is in. We need to
// do this so our file search can find dependents that happen to be in the
// same directory that our target file is in.
CString strDir(szPath); strDir = strDir.Left(strDir.ReverseFind('\\') + 1); SetCurrentDirectory(strDir);
// Create our root module node.
if (m_pModuleRoot = CreateModule(szPath, 0)) { // Start the recursion on the head module to process all modules.
ProcessModule(m_pModuleRoot); } else { m_fOutOfMemory = TRUE; }
// If we ran out of memory while processing the module, then free our
// document data, display an error, and fail the document from loading.
// Out of memory is a fairly major error. If this should occur, MFC will
// most likely notice and report the problem before we do.
if (m_fOutOfMemory) { DeleteContents(); //CString strError("Not enough memory to process \"");
//strError += m_pModuleRoot->m_pData->m_szPath;
//strError += "\"!";
//MessageBox(strError, "Dependency Walker Error", MB_ICONERROR | MB_OK);
return FALSE; }
// Display a message if the module contains a circular dependency error.
if (m_fCircularError) { //CString strError("\"");
//strError += m_pModuleRoot->m_pData->m_szPath;
//strError += "\" will fail to load due to circular dependencies.";
//g_pMainFrame->MessageBox(strError, "Dependency Walker Module Error",MB_ICONERROR | MB_OK);
}
// Display a message if the module contains a mixed machine error.
if (m_fMixedMachineError) { //CString strError("\"");
//strError += m_pModuleRoot->m_pData->m_szPath;
//strError += "\" will fail to load due to a mismatched machine type with "
// "one or more of the dependent modules.";
//g_pMainFrame->MessageBox(strError, "Dependency Walker Module Error", MB_ICONERROR | MB_OK);
}
return TRUE; }
CModule* CDepends::LoopThruAndPrintLosers(CModule *pModuleCur) { TCHAR szBigString[_MAX_PATH + _MAX_PATH]; LPWSTR pwszModuleName = NULL;
//
// loop thru the linked list and look for
// items marked with m_fExportError
//
if (!pModuleCur) { return NULL; }
// check to see if our current module is marked with m_fExportError
// Check to see if our current module matches our search module.
if (pModuleCur->m_fExportError == TRUE) { // Convert the filename to unicode.
pwszModuleName = MakeWideStrFromAnsi( (LPSTR)(pModuleCur->m_pData->m_szPath) );
_stprintf(szBigString, _T("Import\\Export Dependency MisMatch with:%s"), pwszModuleName); iisDebugOut((LOG_TYPE_TRACE_WIN32_API, szBigString)); m_cstrlstListOfBrokenLinks.AddTail(szBigString); m_iNumberOfBrokenLinks++;
if (pwszModuleName){CoTaskMemFree(pwszModuleName);} }
if (pModuleCur->m_pData->m_fFileNotFound == TRUE) { // Convert the filename to unicode.
pwszModuleName = MakeWideStrFromAnsi( (LPSTR)(pModuleCur->m_pData->m_szPath) ); _stprintf(szBigString, _T("Link Dependency MissingFile:%s"), pwszModuleName); iisDebugOut((LOG_TYPE_TRACE_WIN32_API, szBigString)); m_cstrlstListOfBrokenLinks.AddTail(szBigString); m_iNumberOfBrokenLinks++;
if (pwszModuleName){CoTaskMemFree(pwszModuleName);}
}
// Recurse into LoopThruAndPrintLosers() for each dependent module.
pModuleCur = pModuleCur->m_pDependents; while (pModuleCur) { CModule *pModuleFound = LoopThruAndPrintLosers(pModuleCur); if (pModuleFound) { return pModuleFound; } pModuleCur = pModuleCur->m_pNext; }
return NULL; }
void CDepends::DeleteContents() {
// Delete all modules by recursing into DeleteModule() with our root module.
if (m_pModuleRoot) { DeleteModule(m_pModuleRoot); m_pModuleRoot = NULL; }
// Clear our memory error flag.
m_fOutOfMemory = FALSE;
// Clear our circular dependency error flag.
m_fCircularError = FALSE;
// Clear our mixed machine error flag.
m_fMixedMachineError = FALSE; m_dwMachineType = (DWORD)-1; }
//******************************************************************************
// CDepends :: Internal functions
//******************************************************************************
CModule* CDepends::CreateModule(LPCSTR szFile, int depth) {
CHAR szPath[16384] = "", *pszFile = NULL;
// Attempt to find the file in our search path. This will mimic what the OS
// loader does when looking for a module. Our OnOpenDocument() function sets
// the current directory to the module directory, so SearchPath() will first
// look in the module directory.
//SearchPath(NULL, szFile, NULL, sizeof(szPath), szPath, &pszFile);
SearchPathA(NULL, szFile, NULL, sizeof(szPath), szPath, &pszFile);
// If we don't have a path, then just copy the file name into our path string
// and set the file pointer to the character following the last wack "\".
if (!*szPath) { strcpy(szPath, szFile); LPSTR pszWack = strrchr(szPath, '\\'); pszFile = (pszWack && *(pszWack + 1)) ? (pszWack + 1) : szPath; }
// If our file name pointer is invalid, then just point it to our path.
if (pszFile < szPath) { pszFile = szPath; }
// Create a new CModule object
CModule *pModule = new CModule(); if (!pModule) { return NULL; } ZeroMemory(pModule, sizeof(CModule));
// Store our module's depth for later recursion overflow checks.
pModule->m_depth = depth;
// Recurse our module tree to see if this module is a duplicate of another.
pModule->m_pModuleOriginal = FindModule(m_pModuleRoot, szPath);
// Check to see if a duplicate was found.
if (pModule->m_pModuleOriginal) {
// If the module is a duplicate, then just point our data field to the
// original module's data field.
pModule->m_pData = pModule->m_pModuleOriginal->m_pData;
} else {
// If this module is not a duplicate, then create a new CModuleData object.
pModule->m_pData = (CModuleData*)new BYTE[sizeof(CModuleData) + strlen(szPath)]; if (!pModule->m_pData) { delete pModule; return NULL; }
// Clear the object, copy the path string to it, and set the file pointer.
ZeroMemory(pModule->m_pData, sizeof(CModuleData)); strcpy(pModule->m_pData->m_szPath, szPath); pModule->m_pData->m_szFile = pModule->m_pData->m_szPath + (pszFile - szPath);
// For readability, make path lowercase and file uppercase.
_strlwr(pModule->m_pData->m_szPath); _strupr(pModule->m_pData->m_szFile); }
// Return our new module object.
return pModule; }
//******************************************************************************
CFunction* CDepends::CreateFunction(int ordinal, int hint, LPCSTR szName, DWORD_PTR dwAddress, LPCSTR szForward) { // Create a CFunction object.
CFunction *pFunction = (CFunction*)new BYTE[sizeof(CFunction) + strlen(szName)]; if (!pFunction) { return NULL; }
// Clear the function object and fill in its members.
ZeroMemory(pFunction, sizeof(CFunction)); strcpy(pFunction->m_szName, szName); pFunction->m_ordinal = ordinal; pFunction->m_hint = hint; pFunction->m_dwAddress = dwAddress;
// If a forward string exists, then allocate a buffer and store a pointer to
// it in our CFunction's m_dwExtra member. See the CFunction class for more
// info on m_dwExtra.
if (szForward) { if (pFunction->m_dwExtra = (DWORD_PTR)new CHAR[strlen(szForward) + 1]) { strcpy((LPSTR)pFunction->m_dwExtra, szForward); } else { delete[] (BYTE*)pFunction; return NULL; } }
// Return our new function object.
return pFunction; }
//******************************************************************************
void CDepends::DeleteModule(CModule *pModule) {
// Recurse into DeleteModule() to delete all our dependent modules first.
CModule *pModuleCur = pModule->m_pDependents; while (pModuleCur) { CModule *pModuleNext = pModuleCur->m_pNext; DeleteModule(pModuleCur); pModuleCur = pModuleNext; }
// Delete all of our current module's parent import functions.
CFunction *pFunctionCur = pModule->m_pParentImports; while (pFunctionCur) { CFunction *pFunctionNext = pFunctionCur->m_pNext; delete[] (BYTE*)pFunctionCur; pFunctionCur = pFunctionNext; }
// If we are not marked as a duplicate, then free our CModuleData.
if (!pModule->m_pModuleOriginal) {
// Delete all of our current module's export functions.
CFunction *pFunctionCur = pModule->m_pData->m_pExports; while (pFunctionCur) {
// Delete our forward string if we allocated one.
if (pFunctionCur->GetForwardString()) { delete[] (CHAR*)pFunctionCur->GetForwardString(); }
// Delete the export node itself.
CFunction *pFunctionNext = pFunctionCur->m_pNext; delete[] (BYTE*)pFunctionCur; pFunctionCur = pFunctionNext; }
// Delete any error string that may have been allocated.
if (pModule->m_pData->m_pszError) { delete[] (CHAR*)pModule->m_pData->m_pszError; }
// Delete our current module's CModuleData object.
delete[] (BYTE*)pModule->m_pData; }
// Delete our current module object itself.
delete pModule; }
//******************************************************************************
CModule* CDepends::FindModule(CModule *pModuleCur, LPCSTR szPath) {
if (!pModuleCur) { return NULL; }
// Check to see if our current module matches our search module.
if (!_stricmp(pModuleCur->m_pData->m_szPath, szPath)) { return (pModuleCur->m_pModuleOriginal ? pModuleCur->m_pModuleOriginal : pModuleCur); }
// Recurse into FindModule() for each dependent module.
pModuleCur = pModuleCur->m_pDependents; while (pModuleCur) { CModule *pModuleFound = FindModule(pModuleCur, szPath); if (pModuleFound) { return pModuleFound; } pModuleCur = pModuleCur->m_pNext; }
return NULL; }
//******************************************************************************
BOOL CDepends::VerifyModule(CModule *pModule) {
// Map an IMAGE_DOS_HEADER structure onto our module file mapping.
PIMAGE_DOS_HEADER pIDH = (PIMAGE_DOS_HEADER)m_lpvFile;
// Check for the DOS signature ("MZ").
if (pIDH->e_magic != IMAGE_DOS_SIGNATURE) { //SetModuleError(pModule, "No DOS signature found. This file is not a valid Win32 module.");
return FALSE; }
// Map an IMAGE_NT_HEADERS structure onto our module file mapping.
PIMAGE_NT_HEADERS pINTH = (PIMAGE_NT_HEADERS)((DWORD_PTR)m_lpvFile + pIDH->e_lfanew);
// Check for NT/PE signature ("PE\0\0").
if (pINTH->Signature != IMAGE_NT_SIGNATURE) { //SetModuleError(pModule, "No PE signature found. This file is not a valid Win32 module.");
return FALSE; }
// Map our IMAGE_FILE_HEADER structure onto our module file mapping.
m_pIFH = &pINTH->FileHeader;
// Map our IMAGE_OPTIONAL_HEADER structure onto our module file mapping.
m_pIOH = &pINTH->OptionalHeader;
// Map our IMAGE_SECTION_HEADER structure array onto our module file mapping
m_pISH = IMAGE_FIRST_SECTION(pINTH);
return TRUE; }
//******************************************************************************
BOOL CDepends::GetModuleInfo(CModule *pModule) {
// Store the machine type.
pModule->m_pData->m_dwMachine = m_pIFH->Machine;
// Check for a mismatched machine error.
if (m_dwMachineType == (DWORD)-1) { m_dwMachineType = pModule->m_pData->m_dwMachine; } else if (m_dwMachineType != pModule->m_pData->m_dwMachine) { m_fMixedMachineError = TRUE;
// Convert the filename to unicode.
LPWSTR pwszModuleName = NULL; TCHAR szBigString[_MAX_PATH + _MAX_PATH]; pwszModuleName = MakeWideStrFromAnsi( (LPSTR)(pModule->m_pData->m_szPath) ); _stprintf(szBigString, _T("Wrong Machine Type:(%s) %s"), MachineToString(pModule->m_pData->m_dwMachine), pwszModuleName); iisDebugOut((LOG_TYPE_TRACE_WIN32_API, szBigString)); m_cstrlstListOfBrokenLinks.AddTail(szBigString); m_iNumberOfBrokenLinks++; if (pwszModuleName){CoTaskMemFree(pwszModuleName);}
}
// Store the subsystem type
pModule->m_pData->m_dwSubsystem = m_pIOH->Subsystem;
// Store the preferred base address
pModule->m_pData->m_dwBaseAddress = m_pIOH->ImageBase;
// Store the image version
pModule->m_pData->m_dwImageVersion = MAKELONG(m_pIOH->MinorImageVersion, m_pIOH->MajorImageVersion);
// Store the linker version
pModule->m_pData->m_dwLinkerVersion = MAKELONG(m_pIOH->MinorLinkerVersion, m_pIOH->MajorLinkerVersion);
// Store the OS version
pModule->m_pData->m_dwOSVersion = MAKELONG(m_pIOH->MinorOperatingSystemVersion, m_pIOH->MajorOperatingSystemVersion);
// Store the subsystem version
pModule->m_pData->m_dwSubsystemVersion = MAKELONG(m_pIOH->MinorSubsystemVersion, m_pIOH->MajorSubsystemVersion);
return TRUE; }
BOOL CDepends::WalkIAT( PIMAGE_THUNK_DATA pITDF, PIMAGE_THUNK_DATA pITDA, CModule *pModule, DWORD_PTR dwBase ) { CFunction *pFunctionLast = NULL, *pFunctionNew;
// Loop through all the Image Thunk Data structures in the function array.
while (pITDF->u1.Ordinal) {
LPCSTR szFunction = ""; int ordinal = -1, hint = -1;
// Check to see if this function is by ordinal or by name. If the
// function is by ordinal, the ordinal's high bit will be set. If the
// the high bit is not set, then the ordinal value is really a virtual
// address of an IMAGE_IMPORT_BY_NAME structure.
if (IMAGE_SNAP_BY_ORDINAL(pITDF->u1.Ordinal)) { ordinal = (int)IMAGE_ORDINAL(pITDF->u1.Ordinal); } else { PIMAGE_IMPORT_BY_NAME pIIBN = (PIMAGE_IMPORT_BY_NAME)(dwBase + (DWORD_PTR)pITDF->u1.AddressOfData); szFunction = (LPCSTR)pIIBN->Name; hint = (int)pIIBN->Hint; }
// If this import module has been pre-bound, then get this function's
// entrypoint memory address.
DWORD_PTR dwAddress = (DWORD_PTR)(pITDA ? pITDA->u1.Function : (DWORD_PTR)INVALID_HANDLE_VALUE);
// Create a new CFunction object for this function.
if (!(pFunctionNew = CreateFunction(ordinal, hint, szFunction, dwAddress))) { m_fOutOfMemory = TRUE; return FALSE; }
// Add the function to the end of our module's function linked list
if (pFunctionLast) { pFunctionLast->m_pNext = pFunctionNew; } else { pModule->m_pParentImports = pFunctionNew; } pFunctionLast = pFunctionNew;
// Increment to the next function and address.
pITDF++; if (pITDA) { pITDA++; } } return TRUE; }
//******************************************************************************
BOOL CDepends::BuildImports(CModule *pModule) {
// If this module has no imports (like NTDLL.DLL), then just return success.
if (m_pIOH->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size == 0) { return TRUE; }
// Locate our Import Image Directory's relative virtual address
DWORD VAImageDir = m_pIOH->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
PIMAGE_SECTION_HEADER pISH = NULL;
// Locate the section that contains this Image Directory. We do this by
// walking through all of our sections until we find the one that specifies
// an address range that our Image Directory fits in.
for (int i = 0; i < m_pIFH->NumberOfSections; i++) { if ((VAImageDir >= m_pISH[i].VirtualAddress) && (VAImageDir < (m_pISH[i].VirtualAddress + m_pISH[i].SizeOfRawData))) { pISH = &m_pISH[i]; break; } }
// Bail out if we could not find a section that owns our Image Directory.
if (!pISH) { //SetModuleError(pModule, "Could not find the section that owns the Import Directory.");
return FALSE; }
// Compute our base that everything else is an offset from. We do this by
// taking our base file pointer and adding our section's PointerToRawData,
// which is an absolute offset value into our file. We then subtract off our
// Virtual Address since the offsets we are going to be adding later will be
// relative to the this Virtual Address
DWORD_PTR dwBase = (DWORD_PTR)m_lpvFile + pISH->PointerToRawData - pISH->VirtualAddress;
// To locate the beginning of our Image Import Descriptor array, we add our
// Image Directory offset to our base.
PIMAGE_IMPORT_DESCRIPTOR pIID = (PIMAGE_IMPORT_DESCRIPTOR)(dwBase + VAImageDir);
CModule *pModuleLast = NULL, *pModuleNew; CFunction *pFunctionLast = NULL, *pFunctionNew;
// Loop through all the Image Import Descriptors in the array.
while (pIID->OriginalFirstThunk || pIID->FirstThunk) {
// Locate our module name string and create the module object.
if (!(pModuleNew = CreateModule((LPCSTR)(dwBase + pIID->Name), pModule->m_depth + 1))) { m_fOutOfMemory = TRUE; return FALSE; }
// Add the module to the end of our module linked list.
if (pModuleLast) { pModuleLast->m_pNext = pModuleNew; } else { pModule->m_pDependents = pModuleNew; } pModuleLast = pModuleNew;
// Locate the beginning of our function array and address array. The
// function array (pITDF) is an array of IMAGE_THUNK_DATA structures that
// contains all the exported functions, both by name and by ordinal. The
// address array (pITDA) is an parallel array of IMAGE_THUNK_DATA
// structures that is used to store the all the function's entrypoint
// addresses. Usually the address array contains the exact same values
// the function array contains until the OS loader actually loads all the
// modules. At that time, the loader will set (bind) these addresses to
// the actual addresses that the given functions reside at in memory. Some
// modules have their exports pre-bound which can provide a speed increase
// when loading the module. If a module is pre-bound (often seen with
// system modules), the TimeDateStamp field of our IMAGE_IMPORT_DESCRIPTOR
// structure will be set and the address array will contain the actual
// memory addresses that the functions will reside at, assuming that the
// imported module loads at its preferred base address.
PIMAGE_THUNK_DATA pITDF = NULL, pITDA = NULL;
// Check to see if module is Microsoft format or Borland format.
if (pIID->OriginalFirstThunk) {
// Microsoft uses the OriginalFirstThunk field for the function array.
pITDF = (PIMAGE_THUNK_DATA)(dwBase + (DWORD)pIID->OriginalFirstThunk);
// Microsoft optionally uses the FirstThunk as a bound address array.
// If the TimeDateStamp field is set, then the module has been bound.
if (pIID->TimeDateStamp) { pITDA = (PIMAGE_THUNK_DATA)(dwBase + (DWORD)pIID->FirstThunk); }
} else {
// Borland uses the FirstThunk field for the function array.
pITDF = (PIMAGE_THUNK_DATA)(dwBase + (DWORD)pIID->FirstThunk);; }
// Find imports
if (!WalkIAT(pITDF, pITDA, pModuleLast, dwBase)) { return FALSE; }
// Increment to the next import module
pIID++; }
return TRUE; }
BOOL CDepends::BuildDelayImports(CModule *pModule) {
// If this module has no delay imports just return success.
if (m_pIOH->DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].Size == 0) { return TRUE; }
// Locate our Import Image Directory's relative virtual address
DWORD VAImageDir = m_pIOH->DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].VirtualAddress;
PIMAGE_SECTION_HEADER pISH = NULL;
// Locate the section that contains this Image Directory. We do this by
// walking through all of our sections until we find the one that specifies
// an address range that our Image Directory fits in.
for (int i = 0; i < m_pIFH->NumberOfSections; i++) { if ((VAImageDir >= m_pISH[i].VirtualAddress) && (VAImageDir < (m_pISH[i].VirtualAddress + m_pISH[i].SizeOfRawData))) { pISH = &m_pISH[i]; break; } }
// Bail out if we could not find a section that owns our Image Directory.
if (!pISH) { //SetModuleError(pModule, "Could not find the section that owns the Import Directory.");
return FALSE; }
// Compute our base that everything else is an offset from. We do this by
// taking our base file pointer and adding our section's PointerToRawData,
// which is an absolute offset value into our file. We then subtract off our
// Virtual Address since the offsets we are going to be adding later will be
// relative to the this Virtual Address
DWORD_PTR dwBase = (DWORD_PTR)m_lpvFile + pISH->PointerToRawData - pISH->VirtualAddress;
// To locate the beginning of our Image Import Descriptor array, we add our
// Image Directory offset to our base.
PImgDelayDescr pIDD = (PImgDelayDescr)(dwBase + VAImageDir);
CModule *pModuleLast = NULL, *pModuleNew; CFunction *pFunctionLast = NULL, *pFunctionNew;
if (pIDD->grAttrs & dlattrRva) { PImgDelayDescrV2 pIDDv2 = (PImgDelayDescrV2)pIDD; // Loop through all the Image Import Descriptors in the array.
while (pIDDv2->rvaINT && pIDDv2->rvaIAT && pIDDv2->rvaHmod) {
DWORD_PTR dwNameBase = 0, dwINTBase = 0;
// Locate the section that contains this Image Directory. We do this by
// walking through all of our sections until we find the one that specifies
// an address range that our Image Directory fits in.
for (int i = 0; i < m_pIFH->NumberOfSections; i++) { if (((DWORD_PTR)pIDDv2->rvaDLLName >= m_pISH[i].VirtualAddress) && ((DWORD_PTR)pIDDv2->rvaDLLName < (m_pISH[i].VirtualAddress + m_pISH[i].SizeOfRawData))) { dwNameBase = ((DWORD_PTR)m_lpvFile + m_pISH[i].PointerToRawData - m_pISH[i].VirtualAddress); }
if (((DWORD_PTR)pIDDv2->rvaINT >= (m_pISH[i].VirtualAddress)) && ((DWORD_PTR)pIDDv2->rvaINT < (m_pISH[i].VirtualAddress + m_pISH[i].SizeOfRawData))) { dwINTBase = ((DWORD_PTR)m_lpvFile + m_pISH[i].PointerToRawData - m_pISH[i].VirtualAddress); } }
if (!dwINTBase) { //SetModuleError(pModule, "Could not find the section that owns the Delay Import INT.");
return FALSE; }
if (!dwNameBase) { //SetModuleError(pModule, "Could not find the section that owns the Delay Import DllName.");
return FALSE; }
// Locate our module name string and create the module object.
if (!(pModuleNew = CreateModule((LPCSTR)(dwNameBase + pIDDv2->rvaDLLName), pModule->m_depth + 1))) { m_fOutOfMemory = TRUE; return FALSE; }
// Add the module to the end of our module linked list.
if (pModuleLast) { pModuleLast->m_pNext = pModuleNew; } else { if (pModule->m_pDependents) { pModuleLast = pModule->m_pDependents; while (pModuleLast->m_pNext) { pModuleLast = pModuleLast->m_pNext; } pModuleLast->m_pNext = pModuleNew; } else { pModule->m_pDependents = pModuleNew; } } pModuleLast = pModuleNew;
pModuleLast->m_fDelayLoad = TRUE;
// For now, don't worry about bound imports.
PIMAGE_THUNK_DATA pITDF = NULL;
pITDF = (PIMAGE_THUNK_DATA)(dwINTBase + (DWORD_PTR)pIDDv2->rvaINT);
// Find imports
if (!WalkIAT(pITDF, NULL, pModuleLast, dwNameBase)) { return FALSE; }
// Increment to the next import module
pIDDv2++; } } else { PImgDelayDescrV1 pIDDv1 = (PImgDelayDescrV1)pIDD;
// Loop through all the Image Import Descriptors in the array.
while (pIDDv1->pINT && pIDDv1->pIAT && pIDDv1->phmod) {
DWORD_PTR dwNameBase = 0, dwINTBase = 0;
// Locate the section that contains this Image Directory. We do this by
// walking through all of our sections until we find the one that specifies
// an address range that our Image Directory fits in.
for (int i = 0; i < m_pIFH->NumberOfSections; i++) { if (((DWORD_PTR)pIDDv1->szName >= (m_pIOH->ImageBase + m_pISH[i].VirtualAddress)) && ((DWORD_PTR)pIDDv1->szName < (m_pIOH->ImageBase + m_pISH[i].VirtualAddress + m_pISH[i].SizeOfRawData))) { dwNameBase = ((DWORD_PTR)m_lpvFile + m_pISH[i].PointerToRawData - m_pISH[i].VirtualAddress - m_pIOH->ImageBase); }
if (((DWORD_PTR)pIDDv1->pINT >= (m_pIOH->ImageBase + m_pISH[i].VirtualAddress)) && ((DWORD_PTR)pIDDv1->pINT < (m_pIOH->ImageBase + m_pISH[i].VirtualAddress + m_pISH[i].SizeOfRawData))) { dwINTBase = ((DWORD_PTR)m_lpvFile + m_pISH[i].PointerToRawData - m_pISH[i].VirtualAddress - m_pIOH->ImageBase); } }
if (!dwINTBase) { //SetModuleError(pModule, "Could not find the section that owns the Delay Import INT.");
return FALSE; }
if (!dwNameBase) { //SetModuleError(pModule, "Could not find the section that owns the Delay Import DllName.");
return FALSE; }
// Locate our module name string and create the module object.
if (!(pModuleNew = CreateModule((LPCSTR)(dwNameBase + pIDDv1->szName), pModule->m_depth + 1))) { m_fOutOfMemory = TRUE; return FALSE; }
// Add the module to the end of our module linked list.
if (pModuleLast) { pModuleLast->m_pNext = pModuleNew; } else { if (pModule->m_pDependents) { pModuleLast = pModule->m_pDependents; while (pModuleLast->m_pNext) { pModuleLast = pModuleLast->m_pNext; } pModuleLast->m_pNext = pModuleNew; } else { pModule->m_pDependents = pModuleNew; } } pModuleLast = pModuleNew;
pModuleLast->m_fDelayLoad = TRUE;
// For now, don't worry about bound imports.
PIMAGE_THUNK_DATA pITDF = NULL;
pITDF = (PIMAGE_THUNK_DATA)(dwINTBase + (DWORD_PTR)pIDDv1->pINT);
// Find imports
if (!WalkIAT(pITDF, NULL, pModuleLast, dwNameBase)) { return FALSE; }
// Increment to the next import module
pIDDv1++; } }
return TRUE; }
//******************************************************************************
BOOL CDepends::BuildExports(CModule *pModule) {
// If this module has no exports, then just return success.
if (m_pIOH->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size == 0) { return TRUE; }
// Locate our Export Image Directory's relative virtual address
DWORD VAImageDir = m_pIOH->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
PIMAGE_SECTION_HEADER pISH = NULL;
// Locate the section that contains this Image Directory. We do this by
// walking through all of our sections until we find the one that specifies
// an address range that our Image Directory fits in.
for (int i = 0; i < m_pIFH->NumberOfSections; i++) { if ((VAImageDir >= m_pISH[i].VirtualAddress) && (VAImageDir < (m_pISH[i].VirtualAddress + m_pISH[i].SizeOfRawData))) { pISH = &m_pISH[i]; break; } }
// Bail out if we could not find a section that owns our Image Directory.
if (!pISH) { //SetModuleError(pModule, "Could not find the section that owns the Export Directory.");
return FALSE; }
// Compute our base that everything else is an offset from. We do this by
// taking our base file pointer and adding our section's PointerToRawData,
// which is an absolute offset value into our file. We then subtract off our
// Virtual Address since the offsets we are going to be adding later will be
// relative to the this Virtual Address
DWORD_PTR dwBase = (DWORD_PTR)m_lpvFile + pISH->PointerToRawData - pISH->VirtualAddress;
// To locate the beginning of our Image Export Directory, we add our
// Image Directory offset to our base.
PIMAGE_EXPORT_DIRECTORY pIED = (PIMAGE_EXPORT_DIRECTORY)(dwBase + VAImageDir);
// pdwNames is a DWORD array of size pIED->NumberOfNames, which contains VA
// pointers to all the function name strings. pwOrdinals is a WORD array of
// size pIED->NumberOfNames, which contains all the ordinal values for each
// function exported by name. pdwNames and pwOrdinals are parallel arrays,
// meaning that the ordinal in pwOrdinals[x] goes with the function name
// pointed to by pdwNames[x]. The value used to index these arrays is
// referred to as the "hint".
// pdwAddresses is a DWORD array of size pIED->NumberOfFunctions, which
// contains the entrypoint addresses for all functions exported by the
// module. Contrary to several PE format documents, this array is *not*
// parallel with pdwNames and pwOrdinals. The index used for this array is
// the ordinal value of the function you are interested in, minus the base
// ordinal specified in pIED->Base. Another common mistake is to assume that
// pIED->NumberOfFunctions is always equal to pIED->AddressOfNames. If the
// module exports function by ordinal only, then pIED->NumberOfFunctions
// will be greater than pIED->NumberOfNames.
DWORD *pdwNames = (DWORD*)(dwBase + (DWORD)pIED->AddressOfNames); WORD *pwOrdinals = (WORD* )(dwBase + (DWORD)pIED->AddressOfNameOrdinals); DWORD *pdwAddresses = (DWORD*)(dwBase + (DWORD)pIED->AddressOfFunctions);
CFunction *pFunctionLast = NULL, *pFunctionNew;
// Loop through all the "exported by name" functions.
for (int hint = 0; hint < (int)pIED->NumberOfNames; hint++) {
// Get our ordinal value, function name, and entrypoint address
int ordinal = pIED->Base + (DWORD)pwOrdinals[hint]; LPCSTR szFunction = (LPCSTR)(dwBase + pdwNames[hint]); DWORD dwAddress = pdwAddresses[ordinal - pIED->Base]; LPCSTR szForward = NULL;
// Certain modules, such as NTDLL.DLL and MSVCRT40.DLL, have what are
// known as forwarded functions. Forwarded functions are functions that
// are exported from one module, but the code actually lives in another
// module. We can check to see if a function is forwarded by looking at
// its address pointer. If the address pointer points to the character
// immediately following the NULL character in its function name string,
// then this address pointer is really a pointer to a forward string in
// the string table. Some documents state that if the address points to
// a RVA in our current section, then the address must point to a forward
// string. This is not true since the function code can (and sometimes
// does) live in the same section that we are currently in.
if (((DWORD_PTR)szFunction + strlen(szFunction) + 1) == (dwBase + dwAddress)) { szForward = (LPCSTR)(dwBase + dwAddress); }
// Create a new CFunction object for this function.
if (!(pFunctionNew = CreateFunction(ordinal, hint, szFunction, dwAddress, szForward))) { m_fOutOfMemory = TRUE; return FALSE; }
// Add the function to the end of our module's export function linked list
if (pFunctionLast) { pFunctionLast->m_pNext = pFunctionNew; } else { pModule->m_pData->m_pExports = pFunctionNew; } pFunctionLast = pFunctionNew; }
// Loop through all the "exported by ordinal" functions. This module has
// pIED->NumberOfFunctions functions with consecutive ordinals starting
// with the ordinal specified by pIED->Base. We need to loop through all
// these ordinal values and add any to our list that have not already been
// added by name.
for (int ordinal = pIED->Base; ordinal < (int)(pIED->NumberOfFunctions + pIED->Base); ordinal++) {
// Loop through our current list to make sure we haven't already added
// this function during our "exported by name" search above.
CFunction *pFunctionCur = pModule->m_pData->m_pExports; while (pFunctionCur) { if (pFunctionCur->m_ordinal == ordinal) { break; } pFunctionCur = pFunctionCur->m_pNext; }
// If this ordinal is not currently in our list, then add it to our list.
if (!pFunctionCur) {
// Get this function's entrypoint address.
DWORD dwAddress = pdwAddresses[ordinal - pIED->Base];
// Create a new CFunction object for this function.
if (!(pFunctionNew = CreateFunction(ordinal, -1, "", dwAddress))) { m_fOutOfMemory = TRUE; return FALSE; }
// Add the function to the end of our module's export function linked list
if (pFunctionLast) { pFunctionLast->m_pNext = pFunctionNew; } else { pModule->m_pData->m_pExports = pFunctionNew; } pFunctionLast = pFunctionNew; } }
return TRUE; }
//******************************************************************************
BOOL CDepends::VerifyParentImports(CModule *pModule) {
CModule *pModuleHead = NULL, *pModuleLast, *pModuleCur;
// Loop through each of our parent import functions.
CFunction *pImport = pModule->m_pParentImports; while (pImport) {
// Mark this parent import function as not resolved before starting search.
pImport->m_dwExtra = 0;
// Loop through all our exports, looking for a match with our current import.
CFunction *pExport = pModule->m_pData->m_pExports; while (pExport) {
// If we have a name, then check for the match by name.
if (*pImport->m_szName) { if (!strcmp(pImport->m_szName, pExport->m_szName)) {
// We found a match. Link this parent import to its associated
// export, break out of loop, and move on to handling our next
// parent import.
pImport->m_dwExtra = (DWORD_PTR)pExport; break; }
// If we don't have a name, then check for the match by name.
} else if (pImport->m_ordinal == pExport->m_ordinal) {
// We found a match. Link this parent import to its associated
// export, break out of loop, and move on to handling our next
// parent import.
pImport->m_dwExtra = (DWORD_PTR)pExport; break; }
// Move to the next export
pExport = pExport->m_pNext; }
// Check to see if an export match was found.
if (pImport->GetAssociatedExport()) {
CHAR szFile[1024]; LPCSTR szFunction;
// If an export was found, check to see if it is a forwarded function.
// If it is forwarded, then we need to make sure we consider the
// forwarded module as a new dependent of the current module.
LPCSTR szForward = pImport->GetAssociatedExport()->GetForwardString(); if (szForward) {
// Extract and build the DLL name from the forward string.
LPCSTR pszDot = strchr(szForward, '.'); if (pszDot) { strncpy(szFile, szForward, (size_t)(pszDot - szForward)); strcpy(szFile + (pszDot - szForward), ".DLL"); szFunction = pszDot + 1; } else { strcpy(szFile, "Invalid"); szFunction = szForward; }
// Search our local forward module list to see if we have already
// created a forward CModoule for this DLL file.
for (pModuleLast = NULL, pModuleCur = pModuleHead; pModuleCur; pModuleLast = pModuleCur, pModuleCur = pModuleCur->m_pNext) { if (!_stricmp(pModuleCur->m_pData->m_szFile, szFile)) { break; } }
// If we have not created a forward module for this file yet, then
// create it now and add it to the end of our list.
if (!pModuleCur) {
if (!(pModuleCur = CreateModule(szFile, pModule->m_depth + 1))) { m_fOutOfMemory = TRUE; return FALSE; } pModuleCur->m_fForward = TRUE;
// Add the new module to our local forward module list.
if (pModuleLast) { pModuleLast->m_pNext = pModuleCur; } else { pModuleHead = pModuleCur; } }
// Create a new CFunction object for this function.
CFunction *pFunction = CreateFunction(-1, -1, szFunction, (DWORD)-1); if (!pFunction) { m_fOutOfMemory = TRUE; return FALSE; }
// Insert this function object into our forward module's import list.
pFunction->m_pNext = pModuleCur->m_pParentImports; pModuleCur->m_pParentImports = pFunction; }
} else {
// If we could not find an import/export match, then flag the module
// as having an export error.
pModule->m_fExportError = TRUE; }
// Move to the next parent import function.
pImport = pImport->m_pNext; }
// If we created any forward modules during our entire import verify, then
// add them to the end of our module's dependent module list.
if (pModuleHead) {
// Walk to end of our module's dependent module list.
for (pModuleLast = pModule->m_pDependents; pModuleLast && pModuleLast->m_pNext; pModuleLast = pModuleLast->m_pNext) {}
// Add our local list to the end of our module's dependent module list.
if (pModuleLast) { pModuleLast->m_pNext = pModuleHead; } else { pModule->m_pDependents = pModuleHead; } } return TRUE; }
//******************************************************************************
BOOL CDepends::ProcessModule(CModule *pModule) {
BOOL fResult = FALSE;
// First check to see if this module is a duplicate. If it is, make sure the
// original instance of this module has been processed and then just perform
// the Parent Import Verify. If the module being passed in is an original,
// then just ensure that we haven't already processed this module.
if (pModule->m_pModuleOriginal) {
// Process the original module and its subtree.
fResult = ProcessModule(pModule->m_pModuleOriginal); if (!fResult && m_fOutOfMemory) { return FALSE; }
// Exit now if we have already processed this original module in the past.
} else if (pModule->m_pData->m_fProcessed) { return TRUE;
} else {
// Mark this module as processed.
pModule->m_pData->m_fProcessed = TRUE;
// Open the file for read.
//m_hFile = CreateFile(pModule->m_pData->m_szPath, GENERIC_READ,
m_hFile = CreateFileA(pModule->m_pData->m_szPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
// Exit now if the file failed to open.
if (m_hFile == INVALID_HANDLE_VALUE) { DWORD dwGLE = GetLastError(); if (dwGLE == ERROR_FILE_NOT_FOUND) { //SetModuleError(pModule, "File not found in local directory or search path.");
pModule->m_pData->m_fFileNotFound = TRUE; } else if (dwGLE == ERROR_PATH_NOT_FOUND) { //SetModuleError(pModule, "Invalid path or file name.");
pModule->m_pData->m_fFileNotFound = TRUE; } else { //SetModuleError(pModule, "CreateFile() failed (%u).", dwGLE);
} return FALSE; }
// Create a file mapping object for the open module.
HANDLE hMap = CreateFileMapping(m_hFile, NULL, PAGE_READONLY, 0, 0, NULL);
// Exit now if the file failed to map.
if (hMap == NULL) { //SetModuleError(pModule, "CreateFileMapping() failed (%u).", GetLastError());
CloseHandle(m_hFile); m_hFile = NULL; return FALSE; }
// Create a file mapping view for the open module.
m_lpvFile = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
// Exit now if the mapped view failed to create.
if (m_lpvFile == NULL) { //SetModuleError(pModule, "MapViewOfFile() failed (%u).", GetLastError());
CloseHandle(hMap); CloseHandle(m_hFile); m_hFile = NULL; return FALSE; }
__try {
// Everything from here on is pretty much relying on the file being a
// valid binary with valid pointers and offsets. It is fairly safe to
// just wrap everything in exception handling and then blindly access
// the file. Anything that causes us to move outside our file mapping
// will generate an exception and bring us back here to fail the file.
fResult = (VerifyModule(pModule) && GetModuleInfo(pModule) && BuildImports(pModule) && BuildDelayImports(pModule) && BuildExports(pModule));
} __except(EXCEPTION_EXECUTE_HANDLER) { //SetModuleError(pModule, "Module does not appear to be a valid Win32 module.");
}
// Close our map view pointer, our map handle, and our file handle.
UnmapViewOfFile(m_lpvFile); CloseHandle(hMap); CloseHandle(m_hFile);
// Clear our file handles and pointers.
m_hFile = NULL; m_lpvFile = NULL; m_pIFH = NULL; m_pIOH = NULL; m_pISH = NULL; }
// Compare our parent imports with our exports to make sure they all match up.
if (!VerifyParentImports(pModule)) { return FALSE; }
// Safeguard to ensure that we don't get stuck in some recursize loop. This
// can occur if there is a circular dependency with forwarded functions. This
// is extremely rare and would require someone to design it, but we need
// to handle this case to prevent us from crashing on it. When NT encounters
// a module like this, it fails the load with exception 0xC00000FD which is
// defined as STATUS_STACK_OVERFLOW in WINNT.H. We use 255 as our max depth
// because the several versions of the tree control crash if more than 256
// depths are displayed.
if (pModule->m_depth >= 255) {
// If this module has dependents, then delete them.
if (pModule->m_pDependents) { DeleteModule(pModule->m_pDependents); pModule->m_pDependents = NULL; }
// Flag this document as having a circular dependency error.
m_fCircularError = TRUE; return FALSE; }
// Recurse into ProcessModule() to handle all our dependent modules.
pModule = pModule->m_pDependents; while (pModule) { if (!ProcessModule(pModule) && m_fOutOfMemory) { return FALSE; } pModule = pModule->m_pNext; }
return fResult; }
|