#include "stdafx.h" #include #include #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; }