Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1225 lines
44 KiB

  1. #include "stdafx.h"
  2. #include <objbase.h>
  3. #include <delayimp.h>
  4. #include "depends.h"
  5. #include "other.h"
  6. //******************************************************************************
  7. // CDepends :: Constructor/Destructor
  8. //******************************************************************************
  9. CDepends::CDepends() :
  10. m_hFile(NULL),
  11. m_lpvFile(NULL),
  12. m_pIFH(NULL),
  13. m_pIOH(NULL),
  14. m_pISH(NULL),
  15. m_fOutOfMemory(FALSE),
  16. m_fCircularError(FALSE),
  17. m_fMixedMachineError(FALSE),
  18. m_dwMachineType((DWORD)-1),
  19. m_pModuleRoot(NULL),
  20. m_cxOrdinal(0),
  21. m_cxHint(0),
  22. m_cImports(0),
  23. m_cExports(0)
  24. {
  25. m_cstrlstListOfBrokenLinks.RemoveAll();
  26. m_iNumberOfBrokenLinks = 0;
  27. }
  28. //******************************************************************************
  29. CDepends::~CDepends() {
  30. }
  31. //******************************************************************************
  32. BOOL CDepends::SetInitialFilename(LPCSTR szPath) {
  33. // Set our current directory to the directory that the file is in. We need to
  34. // do this so our file search can find dependents that happen to be in the
  35. // same directory that our target file is in.
  36. CString strDir(szPath);
  37. strDir = strDir.Left(strDir.ReverseFind('\\') + 1);
  38. SetCurrentDirectory(strDir);
  39. // Create our root module node.
  40. if (m_pModuleRoot = CreateModule(szPath, 0))
  41. {
  42. // Start the recursion on the head module to process all modules.
  43. ProcessModule(m_pModuleRoot);
  44. }
  45. else
  46. {
  47. m_fOutOfMemory = TRUE;
  48. }
  49. // If we ran out of memory while processing the module, then free our
  50. // document data, display an error, and fail the document from loading.
  51. // Out of memory is a fairly major error. If this should occur, MFC will
  52. // most likely notice and report the problem before we do.
  53. if (m_fOutOfMemory)
  54. {
  55. DeleteContents();
  56. //CString strError("Not enough memory to process \"");
  57. //strError += m_pModuleRoot->m_pData->m_szPath;
  58. //strError += "\"!";
  59. //MessageBox(strError, "Dependency Walker Error", MB_ICONERROR | MB_OK);
  60. return FALSE;
  61. }
  62. // Display a message if the module contains a circular dependency error.
  63. if (m_fCircularError)
  64. {
  65. //CString strError("\"");
  66. //strError += m_pModuleRoot->m_pData->m_szPath;
  67. //strError += "\" will fail to load due to circular dependencies.";
  68. //g_pMainFrame->MessageBox(strError, "Dependency Walker Module Error",MB_ICONERROR | MB_OK);
  69. }
  70. // Display a message if the module contains a mixed machine error.
  71. if (m_fMixedMachineError) {
  72. //CString strError("\"");
  73. //strError += m_pModuleRoot->m_pData->m_szPath;
  74. //strError += "\" will fail to load due to a mismatched machine type with "
  75. // "one or more of the dependent modules.";
  76. //g_pMainFrame->MessageBox(strError, "Dependency Walker Module Error", MB_ICONERROR | MB_OK);
  77. }
  78. return TRUE;
  79. }
  80. CModule* CDepends::LoopThruAndPrintLosers(CModule *pModuleCur)
  81. {
  82. TCHAR szBigString[_MAX_PATH + _MAX_PATH];
  83. LPWSTR pwszModuleName = NULL;
  84. //
  85. // loop thru the linked list and look for
  86. // items marked with m_fExportError
  87. //
  88. if (!pModuleCur) {
  89. return NULL;
  90. }
  91. // check to see if our current module is marked with m_fExportError
  92. // Check to see if our current module matches our search module.
  93. if (pModuleCur->m_fExportError == TRUE)
  94. {
  95. // Convert the filename to unicode.
  96. pwszModuleName = MakeWideStrFromAnsi( (LPSTR)(pModuleCur->m_pData->m_szPath) );
  97. _stprintf(szBigString, _T("Import\\Export Dependency MisMatch with:%s"), pwszModuleName);
  98. iisDebugOut((LOG_TYPE_TRACE_WIN32_API, szBigString));
  99. m_cstrlstListOfBrokenLinks.AddTail(szBigString);
  100. m_iNumberOfBrokenLinks++;
  101. if (pwszModuleName){CoTaskMemFree(pwszModuleName);}
  102. }
  103. if (pModuleCur->m_pData->m_fFileNotFound == TRUE)
  104. {
  105. // Convert the filename to unicode.
  106. pwszModuleName = MakeWideStrFromAnsi( (LPSTR)(pModuleCur->m_pData->m_szPath) );
  107. _stprintf(szBigString, _T("Link Dependency MissingFile:%s"), pwszModuleName);
  108. iisDebugOut((LOG_TYPE_TRACE_WIN32_API, szBigString));
  109. m_cstrlstListOfBrokenLinks.AddTail(szBigString);
  110. m_iNumberOfBrokenLinks++;
  111. if (pwszModuleName){CoTaskMemFree(pwszModuleName);}
  112. }
  113. // Recurse into LoopThruAndPrintLosers() for each dependent module.
  114. pModuleCur = pModuleCur->m_pDependents;
  115. while (pModuleCur)
  116. {
  117. CModule *pModuleFound = LoopThruAndPrintLosers(pModuleCur);
  118. if (pModuleFound) {
  119. return pModuleFound;
  120. }
  121. pModuleCur = pModuleCur->m_pNext;
  122. }
  123. return NULL;
  124. }
  125. void CDepends::DeleteContents() {
  126. // Delete all modules by recursing into DeleteModule() with our root module.
  127. if (m_pModuleRoot) {
  128. DeleteModule(m_pModuleRoot);
  129. m_pModuleRoot = NULL;
  130. }
  131. // Clear our memory error flag.
  132. m_fOutOfMemory = FALSE;
  133. // Clear our circular dependency error flag.
  134. m_fCircularError = FALSE;
  135. // Clear our mixed machine error flag.
  136. m_fMixedMachineError = FALSE;
  137. m_dwMachineType = (DWORD)-1;
  138. }
  139. //******************************************************************************
  140. // CDepends :: Internal functions
  141. //******************************************************************************
  142. CModule* CDepends::CreateModule(LPCSTR szFile, int depth) {
  143. CHAR szPath[16384] = "", *pszFile = NULL;
  144. // Attempt to find the file in our search path. This will mimic what the OS
  145. // loader does when looking for a module. Our OnOpenDocument() function sets
  146. // the current directory to the module directory, so SearchPath() will first
  147. // look in the module directory.
  148. //SearchPath(NULL, szFile, NULL, sizeof(szPath), szPath, &pszFile);
  149. SearchPathA(NULL, szFile, NULL, sizeof(szPath), szPath, &pszFile);
  150. // If we don't have a path, then just copy the file name into our path string
  151. // and set the file pointer to the character following the last wack "\".
  152. if (!*szPath) {
  153. strcpy(szPath, szFile);
  154. LPSTR pszWack = strrchr(szPath, '\\');
  155. pszFile = (pszWack && *(pszWack + 1)) ? (pszWack + 1) : szPath;
  156. }
  157. // If our file name pointer is invalid, then just point it to our path.
  158. if (pszFile < szPath) {
  159. pszFile = szPath;
  160. }
  161. // Create a new CModule object
  162. CModule *pModule = new CModule();
  163. if (!pModule) {
  164. return NULL;
  165. }
  166. ZeroMemory(pModule, sizeof(CModule));
  167. // Store our module's depth for later recursion overflow checks.
  168. pModule->m_depth = depth;
  169. // Recurse our module tree to see if this module is a duplicate of another.
  170. pModule->m_pModuleOriginal = FindModule(m_pModuleRoot, szPath);
  171. // Check to see if a duplicate was found.
  172. if (pModule->m_pModuleOriginal) {
  173. // If the module is a duplicate, then just point our data field to the
  174. // original module's data field.
  175. pModule->m_pData = pModule->m_pModuleOriginal->m_pData;
  176. } else {
  177. // If this module is not a duplicate, then create a new CModuleData object.
  178. pModule->m_pData = (CModuleData*)new BYTE[sizeof(CModuleData) + strlen(szPath)];
  179. if (!pModule->m_pData) {
  180. delete pModule;
  181. return NULL;
  182. }
  183. // Clear the object, copy the path string to it, and set the file pointer.
  184. ZeroMemory(pModule->m_pData, sizeof(CModuleData));
  185. strcpy(pModule->m_pData->m_szPath, szPath);
  186. pModule->m_pData->m_szFile = pModule->m_pData->m_szPath + (pszFile - szPath);
  187. // For readability, make path lowercase and file uppercase.
  188. _strlwr(pModule->m_pData->m_szPath);
  189. _strupr(pModule->m_pData->m_szFile);
  190. }
  191. // Return our new module object.
  192. return pModule;
  193. }
  194. //******************************************************************************
  195. CFunction* CDepends::CreateFunction(int ordinal, int hint, LPCSTR szName,
  196. DWORD_PTR dwAddress, LPCSTR szForward)
  197. {
  198. // Create a CFunction object.
  199. CFunction *pFunction = (CFunction*)new BYTE[sizeof(CFunction) + strlen(szName)];
  200. if (!pFunction) {
  201. return NULL;
  202. }
  203. // Clear the function object and fill in its members.
  204. ZeroMemory(pFunction, sizeof(CFunction));
  205. strcpy(pFunction->m_szName, szName);
  206. pFunction->m_ordinal = ordinal;
  207. pFunction->m_hint = hint;
  208. pFunction->m_dwAddress = dwAddress;
  209. // If a forward string exists, then allocate a buffer and store a pointer to
  210. // it in our CFunction's m_dwExtra member. See the CFunction class for more
  211. // info on m_dwExtra.
  212. if (szForward) {
  213. if (pFunction->m_dwExtra = (DWORD_PTR)new CHAR[strlen(szForward) + 1]) {
  214. strcpy((LPSTR)pFunction->m_dwExtra, szForward);
  215. } else {
  216. delete[] (BYTE*)pFunction;
  217. return NULL;
  218. }
  219. }
  220. // Return our new function object.
  221. return pFunction;
  222. }
  223. //******************************************************************************
  224. void CDepends::DeleteModule(CModule *pModule) {
  225. // Recurse into DeleteModule() to delete all our dependent modules first.
  226. CModule *pModuleCur = pModule->m_pDependents;
  227. while (pModuleCur) {
  228. CModule *pModuleNext = pModuleCur->m_pNext;
  229. DeleteModule(pModuleCur);
  230. pModuleCur = pModuleNext;
  231. }
  232. // Delete all of our current module's parent import functions.
  233. CFunction *pFunctionCur = pModule->m_pParentImports;
  234. while (pFunctionCur) {
  235. CFunction *pFunctionNext = pFunctionCur->m_pNext;
  236. delete[] (BYTE*)pFunctionCur;
  237. pFunctionCur = pFunctionNext;
  238. }
  239. // If we are not marked as a duplicate, then free our CModuleData.
  240. if (!pModule->m_pModuleOriginal) {
  241. // Delete all of our current module's export functions.
  242. CFunction *pFunctionCur = pModule->m_pData->m_pExports;
  243. while (pFunctionCur) {
  244. // Delete our forward string if we allocated one.
  245. if (pFunctionCur->GetForwardString()) {
  246. delete[] (CHAR*)pFunctionCur->GetForwardString();
  247. }
  248. // Delete the export node itself.
  249. CFunction *pFunctionNext = pFunctionCur->m_pNext;
  250. delete[] (BYTE*)pFunctionCur;
  251. pFunctionCur = pFunctionNext;
  252. }
  253. // Delete any error string that may have been allocated.
  254. if (pModule->m_pData->m_pszError) {
  255. delete[] (CHAR*)pModule->m_pData->m_pszError;
  256. }
  257. // Delete our current module's CModuleData object.
  258. delete[] (BYTE*)pModule->m_pData;
  259. }
  260. // Delete our current module object itself.
  261. delete pModule;
  262. }
  263. //******************************************************************************
  264. CModule* CDepends::FindModule(CModule *pModuleCur, LPCSTR szPath) {
  265. if (!pModuleCur) {
  266. return NULL;
  267. }
  268. // Check to see if our current module matches our search module.
  269. if (!_stricmp(pModuleCur->m_pData->m_szPath, szPath)) {
  270. return (pModuleCur->m_pModuleOriginal ? pModuleCur->m_pModuleOriginal : pModuleCur);
  271. }
  272. // Recurse into FindModule() for each dependent module.
  273. pModuleCur = pModuleCur->m_pDependents;
  274. while (pModuleCur) {
  275. CModule *pModuleFound = FindModule(pModuleCur, szPath);
  276. if (pModuleFound) {
  277. return pModuleFound;
  278. }
  279. pModuleCur = pModuleCur->m_pNext;
  280. }
  281. return NULL;
  282. }
  283. //******************************************************************************
  284. BOOL CDepends::VerifyModule(CModule *pModule) {
  285. // Map an IMAGE_DOS_HEADER structure onto our module file mapping.
  286. PIMAGE_DOS_HEADER pIDH = (PIMAGE_DOS_HEADER)m_lpvFile;
  287. // Check for the DOS signature ("MZ").
  288. if (pIDH->e_magic != IMAGE_DOS_SIGNATURE) {
  289. //SetModuleError(pModule, "No DOS signature found. This file is not a valid Win32 module.");
  290. return FALSE;
  291. }
  292. // Map an IMAGE_NT_HEADERS structure onto our module file mapping.
  293. PIMAGE_NT_HEADERS pINTH = (PIMAGE_NT_HEADERS)((DWORD_PTR)m_lpvFile + pIDH->e_lfanew);
  294. // Check for NT/PE signature ("PE\0\0").
  295. if (pINTH->Signature != IMAGE_NT_SIGNATURE) {
  296. //SetModuleError(pModule, "No PE signature found. This file is not a valid Win32 module.");
  297. return FALSE;
  298. }
  299. // Map our IMAGE_FILE_HEADER structure onto our module file mapping.
  300. m_pIFH = &pINTH->FileHeader;
  301. // Map our IMAGE_OPTIONAL_HEADER structure onto our module file mapping.
  302. m_pIOH = &pINTH->OptionalHeader;
  303. // Map our IMAGE_SECTION_HEADER structure array onto our module file mapping
  304. m_pISH = IMAGE_FIRST_SECTION(pINTH);
  305. return TRUE;
  306. }
  307. //******************************************************************************
  308. BOOL CDepends::GetModuleInfo(CModule *pModule) {
  309. // Store the machine type.
  310. pModule->m_pData->m_dwMachine = m_pIFH->Machine;
  311. // Check for a mismatched machine error.
  312. if (m_dwMachineType == (DWORD)-1) {
  313. m_dwMachineType = pModule->m_pData->m_dwMachine;
  314. } else if (m_dwMachineType != pModule->m_pData->m_dwMachine)
  315. {
  316. m_fMixedMachineError = TRUE;
  317. // Convert the filename to unicode.
  318. LPWSTR pwszModuleName = NULL;
  319. TCHAR szBigString[_MAX_PATH + _MAX_PATH];
  320. pwszModuleName = MakeWideStrFromAnsi( (LPSTR)(pModule->m_pData->m_szPath) );
  321. _stprintf(szBigString, _T("Wrong Machine Type:(%s) %s"), MachineToString(pModule->m_pData->m_dwMachine), pwszModuleName);
  322. iisDebugOut((LOG_TYPE_TRACE_WIN32_API, szBigString));
  323. m_cstrlstListOfBrokenLinks.AddTail(szBigString);
  324. m_iNumberOfBrokenLinks++;
  325. if (pwszModuleName){CoTaskMemFree(pwszModuleName);}
  326. }
  327. // Store the subsystem type
  328. pModule->m_pData->m_dwSubsystem = m_pIOH->Subsystem;
  329. // Store the preferred base address
  330. pModule->m_pData->m_dwBaseAddress = m_pIOH->ImageBase;
  331. // Store the image version
  332. pModule->m_pData->m_dwImageVersion =
  333. MAKELONG(m_pIOH->MinorImageVersion, m_pIOH->MajorImageVersion);
  334. // Store the linker version
  335. pModule->m_pData->m_dwLinkerVersion =
  336. MAKELONG(m_pIOH->MinorLinkerVersion, m_pIOH->MajorLinkerVersion);
  337. // Store the OS version
  338. pModule->m_pData->m_dwOSVersion =
  339. MAKELONG(m_pIOH->MinorOperatingSystemVersion, m_pIOH->MajorOperatingSystemVersion);
  340. // Store the subsystem version
  341. pModule->m_pData->m_dwSubsystemVersion = MAKELONG(m_pIOH->MinorSubsystemVersion, m_pIOH->MajorSubsystemVersion);
  342. return TRUE;
  343. }
  344. BOOL
  345. CDepends::WalkIAT(
  346. PIMAGE_THUNK_DATA pITDF,
  347. PIMAGE_THUNK_DATA pITDA,
  348. CModule *pModule,
  349. DWORD_PTR dwBase
  350. )
  351. {
  352. CFunction *pFunctionLast = NULL, *pFunctionNew;
  353. // Loop through all the Image Thunk Data structures in the function array.
  354. while (pITDF->u1.Ordinal) {
  355. LPCSTR szFunction = "";
  356. int ordinal = -1, hint = -1;
  357. // Check to see if this function is by ordinal or by name. If the
  358. // function is by ordinal, the ordinal's high bit will be set. If the
  359. // the high bit is not set, then the ordinal value is really a virtual
  360. // address of an IMAGE_IMPORT_BY_NAME structure.
  361. if (IMAGE_SNAP_BY_ORDINAL(pITDF->u1.Ordinal)) {
  362. ordinal = (int)IMAGE_ORDINAL(pITDF->u1.Ordinal);
  363. } else {
  364. PIMAGE_IMPORT_BY_NAME pIIBN =
  365. (PIMAGE_IMPORT_BY_NAME)(dwBase + (DWORD_PTR)pITDF->u1.AddressOfData);
  366. szFunction = (LPCSTR)pIIBN->Name;
  367. hint = (int)pIIBN->Hint;
  368. }
  369. // If this import module has been pre-bound, then get this function's
  370. // entrypoint memory address.
  371. DWORD_PTR dwAddress = (DWORD_PTR)(pITDA ? pITDA->u1.Function : (DWORD_PTR)INVALID_HANDLE_VALUE);
  372. // Create a new CFunction object for this function.
  373. if (!(pFunctionNew = CreateFunction(ordinal, hint, szFunction, dwAddress))) {
  374. m_fOutOfMemory = TRUE;
  375. return FALSE;
  376. }
  377. // Add the function to the end of our module's function linked list
  378. if (pFunctionLast) {
  379. pFunctionLast->m_pNext = pFunctionNew;
  380. } else {
  381. pModule->m_pParentImports = pFunctionNew;
  382. }
  383. pFunctionLast = pFunctionNew;
  384. // Increment to the next function and address.
  385. pITDF++;
  386. if (pITDA) {
  387. pITDA++;
  388. }
  389. }
  390. return TRUE;
  391. }
  392. //******************************************************************************
  393. BOOL CDepends::BuildImports(CModule *pModule) {
  394. // If this module has no imports (like NTDLL.DLL), then just return success.
  395. if (m_pIOH->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size == 0) {
  396. return TRUE;
  397. }
  398. // Locate our Import Image Directory's relative virtual address
  399. DWORD VAImageDir = m_pIOH->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
  400. PIMAGE_SECTION_HEADER pISH = NULL;
  401. // Locate the section that contains this Image Directory. We do this by
  402. // walking through all of our sections until we find the one that specifies
  403. // an address range that our Image Directory fits in.
  404. for (int i = 0; i < m_pIFH->NumberOfSections; i++) {
  405. if ((VAImageDir >= m_pISH[i].VirtualAddress) &&
  406. (VAImageDir < (m_pISH[i].VirtualAddress + m_pISH[i].SizeOfRawData)))
  407. {
  408. pISH = &m_pISH[i];
  409. break;
  410. }
  411. }
  412. // Bail out if we could not find a section that owns our Image Directory.
  413. if (!pISH) {
  414. //SetModuleError(pModule, "Could not find the section that owns the Import Directory.");
  415. return FALSE;
  416. }
  417. // Compute our base that everything else is an offset from. We do this by
  418. // taking our base file pointer and adding our section's PointerToRawData,
  419. // which is an absolute offset value into our file. We then subtract off our
  420. // Virtual Address since the offsets we are going to be adding later will be
  421. // relative to the this Virtual Address
  422. DWORD_PTR dwBase = (DWORD_PTR)m_lpvFile + pISH->PointerToRawData - pISH->VirtualAddress;
  423. // To locate the beginning of our Image Import Descriptor array, we add our
  424. // Image Directory offset to our base.
  425. PIMAGE_IMPORT_DESCRIPTOR pIID = (PIMAGE_IMPORT_DESCRIPTOR)(dwBase + VAImageDir);
  426. CModule *pModuleLast = NULL, *pModuleNew;
  427. CFunction *pFunctionLast = NULL, *pFunctionNew;
  428. // Loop through all the Image Import Descriptors in the array.
  429. while (pIID->OriginalFirstThunk || pIID->FirstThunk) {
  430. // Locate our module name string and create the module object.
  431. if (!(pModuleNew = CreateModule((LPCSTR)(dwBase + pIID->Name),
  432. pModule->m_depth + 1)))
  433. {
  434. m_fOutOfMemory = TRUE;
  435. return FALSE;
  436. }
  437. // Add the module to the end of our module linked list.
  438. if (pModuleLast) {
  439. pModuleLast->m_pNext = pModuleNew;
  440. } else {
  441. pModule->m_pDependents = pModuleNew;
  442. }
  443. pModuleLast = pModuleNew;
  444. // Locate the beginning of our function array and address array. The
  445. // function array (pITDF) is an array of IMAGE_THUNK_DATA structures that
  446. // contains all the exported functions, both by name and by ordinal. The
  447. // address array (pITDA) is an parallel array of IMAGE_THUNK_DATA
  448. // structures that is used to store the all the function's entrypoint
  449. // addresses. Usually the address array contains the exact same values
  450. // the function array contains until the OS loader actually loads all the
  451. // modules. At that time, the loader will set (bind) these addresses to
  452. // the actual addresses that the given functions reside at in memory. Some
  453. // modules have their exports pre-bound which can provide a speed increase
  454. // when loading the module. If a module is pre-bound (often seen with
  455. // system modules), the TimeDateStamp field of our IMAGE_IMPORT_DESCRIPTOR
  456. // structure will be set and the address array will contain the actual
  457. // memory addresses that the functions will reside at, assuming that the
  458. // imported module loads at its preferred base address.
  459. PIMAGE_THUNK_DATA pITDF = NULL, pITDA = NULL;
  460. // Check to see if module is Microsoft format or Borland format.
  461. if (pIID->OriginalFirstThunk) {
  462. // Microsoft uses the OriginalFirstThunk field for the function array.
  463. pITDF = (PIMAGE_THUNK_DATA)(dwBase + (DWORD)pIID->OriginalFirstThunk);
  464. // Microsoft optionally uses the FirstThunk as a bound address array.
  465. // If the TimeDateStamp field is set, then the module has been bound.
  466. if (pIID->TimeDateStamp) {
  467. pITDA = (PIMAGE_THUNK_DATA)(dwBase + (DWORD)pIID->FirstThunk);
  468. }
  469. } else {
  470. // Borland uses the FirstThunk field for the function array.
  471. pITDF = (PIMAGE_THUNK_DATA)(dwBase + (DWORD)pIID->FirstThunk);;
  472. }
  473. // Find imports
  474. if (!WalkIAT(pITDF, pITDA, pModuleLast, dwBase)) {
  475. return FALSE;
  476. }
  477. // Increment to the next import module
  478. pIID++;
  479. }
  480. return TRUE;
  481. }
  482. BOOL CDepends::BuildDelayImports(CModule *pModule) {
  483. // If this module has no delay imports just return success.
  484. if (m_pIOH->DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].Size == 0) {
  485. return TRUE;
  486. }
  487. // Locate our Import Image Directory's relative virtual address
  488. DWORD VAImageDir = m_pIOH->DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].VirtualAddress;
  489. PIMAGE_SECTION_HEADER pISH = NULL;
  490. // Locate the section that contains this Image Directory. We do this by
  491. // walking through all of our sections until we find the one that specifies
  492. // an address range that our Image Directory fits in.
  493. for (int i = 0; i < m_pIFH->NumberOfSections; i++) {
  494. if ((VAImageDir >= m_pISH[i].VirtualAddress) &&
  495. (VAImageDir < (m_pISH[i].VirtualAddress + m_pISH[i].SizeOfRawData)))
  496. {
  497. pISH = &m_pISH[i];
  498. break;
  499. }
  500. }
  501. // Bail out if we could not find a section that owns our Image Directory.
  502. if (!pISH) {
  503. //SetModuleError(pModule, "Could not find the section that owns the Import Directory.");
  504. return FALSE;
  505. }
  506. // Compute our base that everything else is an offset from. We do this by
  507. // taking our base file pointer and adding our section's PointerToRawData,
  508. // which is an absolute offset value into our file. We then subtract off our
  509. // Virtual Address since the offsets we are going to be adding later will be
  510. // relative to the this Virtual Address
  511. DWORD_PTR dwBase = (DWORD_PTR)m_lpvFile + pISH->PointerToRawData - pISH->VirtualAddress;
  512. // To locate the beginning of our Image Import Descriptor array, we add our
  513. // Image Directory offset to our base.
  514. PImgDelayDescr pIDD = (PImgDelayDescr)(dwBase + VAImageDir);
  515. CModule *pModuleLast = NULL, *pModuleNew;
  516. CFunction *pFunctionLast = NULL, *pFunctionNew;
  517. if (pIDD->grAttrs & dlattrRva) {
  518. PImgDelayDescrV2 pIDDv2 = (PImgDelayDescrV2)pIDD;
  519. // Loop through all the Image Import Descriptors in the array.
  520. while (pIDDv2->rvaINT && pIDDv2->rvaIAT && pIDDv2->rvaHmod) {
  521. DWORD_PTR dwNameBase = 0, dwINTBase = 0;
  522. // Locate the section that contains this Image Directory. We do this by
  523. // walking through all of our sections until we find the one that specifies
  524. // an address range that our Image Directory fits in.
  525. for (int i = 0; i < m_pIFH->NumberOfSections; i++) {
  526. if (((DWORD_PTR)pIDDv2->rvaDLLName >= m_pISH[i].VirtualAddress) &&
  527. ((DWORD_PTR)pIDDv2->rvaDLLName < (m_pISH[i].VirtualAddress + m_pISH[i].SizeOfRawData)))
  528. {
  529. dwNameBase = ((DWORD_PTR)m_lpvFile + m_pISH[i].PointerToRawData - m_pISH[i].VirtualAddress);
  530. }
  531. if (((DWORD_PTR)pIDDv2->rvaINT >= (m_pISH[i].VirtualAddress)) &&
  532. ((DWORD_PTR)pIDDv2->rvaINT < (m_pISH[i].VirtualAddress + m_pISH[i].SizeOfRawData)))
  533. {
  534. dwINTBase = ((DWORD_PTR)m_lpvFile + m_pISH[i].PointerToRawData - m_pISH[i].VirtualAddress);
  535. }
  536. }
  537. if (!dwINTBase) {
  538. //SetModuleError(pModule, "Could not find the section that owns the Delay Import INT.");
  539. return FALSE;
  540. }
  541. if (!dwNameBase) {
  542. //SetModuleError(pModule, "Could not find the section that owns the Delay Import DllName.");
  543. return FALSE;
  544. }
  545. // Locate our module name string and create the module object.
  546. if (!(pModuleNew = CreateModule((LPCSTR)(dwNameBase + pIDDv2->rvaDLLName),
  547. pModule->m_depth + 1)))
  548. {
  549. m_fOutOfMemory = TRUE;
  550. return FALSE;
  551. }
  552. // Add the module to the end of our module linked list.
  553. if (pModuleLast) {
  554. pModuleLast->m_pNext = pModuleNew;
  555. } else {
  556. if (pModule->m_pDependents) {
  557. pModuleLast = pModule->m_pDependents;
  558. while (pModuleLast->m_pNext) {
  559. pModuleLast = pModuleLast->m_pNext;
  560. }
  561. pModuleLast->m_pNext = pModuleNew;
  562. } else {
  563. pModule->m_pDependents = pModuleNew;
  564. }
  565. }
  566. pModuleLast = pModuleNew;
  567. pModuleLast->m_fDelayLoad = TRUE;
  568. // For now, don't worry about bound imports.
  569. PIMAGE_THUNK_DATA pITDF = NULL;
  570. pITDF = (PIMAGE_THUNK_DATA)(dwINTBase + (DWORD_PTR)pIDDv2->rvaINT);
  571. // Find imports
  572. if (!WalkIAT(pITDF, NULL, pModuleLast, dwNameBase)) {
  573. return FALSE;
  574. }
  575. // Increment to the next import module
  576. pIDDv2++;
  577. }
  578. } else {
  579. PImgDelayDescrV1 pIDDv1 = (PImgDelayDescrV1)pIDD;
  580. // Loop through all the Image Import Descriptors in the array.
  581. while (pIDDv1->pINT && pIDDv1->pIAT && pIDDv1->phmod) {
  582. DWORD_PTR dwNameBase = 0, dwINTBase = 0;
  583. // Locate the section that contains this Image Directory. We do this by
  584. // walking through all of our sections until we find the one that specifies
  585. // an address range that our Image Directory fits in.
  586. for (int i = 0; i < m_pIFH->NumberOfSections; i++) {
  587. if (((DWORD_PTR)pIDDv1->szName >= (m_pIOH->ImageBase + m_pISH[i].VirtualAddress)) &&
  588. ((DWORD_PTR)pIDDv1->szName < (m_pIOH->ImageBase + m_pISH[i].VirtualAddress + m_pISH[i].SizeOfRawData)))
  589. {
  590. dwNameBase = ((DWORD_PTR)m_lpvFile + m_pISH[i].PointerToRawData - m_pISH[i].VirtualAddress - m_pIOH->ImageBase);
  591. }
  592. if (((DWORD_PTR)pIDDv1->pINT >= (m_pIOH->ImageBase + m_pISH[i].VirtualAddress)) &&
  593. ((DWORD_PTR)pIDDv1->pINT < (m_pIOH->ImageBase + m_pISH[i].VirtualAddress + m_pISH[i].SizeOfRawData)))
  594. {
  595. dwINTBase = ((DWORD_PTR)m_lpvFile + m_pISH[i].PointerToRawData - m_pISH[i].VirtualAddress - m_pIOH->ImageBase);
  596. }
  597. }
  598. if (!dwINTBase) {
  599. //SetModuleError(pModule, "Could not find the section that owns the Delay Import INT.");
  600. return FALSE;
  601. }
  602. if (!dwNameBase) {
  603. //SetModuleError(pModule, "Could not find the section that owns the Delay Import DllName.");
  604. return FALSE;
  605. }
  606. // Locate our module name string and create the module object.
  607. if (!(pModuleNew = CreateModule((LPCSTR)(dwNameBase + pIDDv1->szName),
  608. pModule->m_depth + 1)))
  609. {
  610. m_fOutOfMemory = TRUE;
  611. return FALSE;
  612. }
  613. // Add the module to the end of our module linked list.
  614. if (pModuleLast) {
  615. pModuleLast->m_pNext = pModuleNew;
  616. } else {
  617. if (pModule->m_pDependents) {
  618. pModuleLast = pModule->m_pDependents;
  619. while (pModuleLast->m_pNext) {
  620. pModuleLast = pModuleLast->m_pNext;
  621. }
  622. pModuleLast->m_pNext = pModuleNew;
  623. } else {
  624. pModule->m_pDependents = pModuleNew;
  625. }
  626. }
  627. pModuleLast = pModuleNew;
  628. pModuleLast->m_fDelayLoad = TRUE;
  629. // For now, don't worry about bound imports.
  630. PIMAGE_THUNK_DATA pITDF = NULL;
  631. pITDF = (PIMAGE_THUNK_DATA)(dwINTBase + (DWORD_PTR)pIDDv1->pINT);
  632. // Find imports
  633. if (!WalkIAT(pITDF, NULL, pModuleLast, dwNameBase)) {
  634. return FALSE;
  635. }
  636. // Increment to the next import module
  637. pIDDv1++;
  638. }
  639. }
  640. return TRUE;
  641. }
  642. //******************************************************************************
  643. BOOL CDepends::BuildExports(CModule *pModule) {
  644. // If this module has no exports, then just return success.
  645. if (m_pIOH->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size == 0) {
  646. return TRUE;
  647. }
  648. // Locate our Export Image Directory's relative virtual address
  649. DWORD VAImageDir = m_pIOH->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
  650. PIMAGE_SECTION_HEADER pISH = NULL;
  651. // Locate the section that contains this Image Directory. We do this by
  652. // walking through all of our sections until we find the one that specifies
  653. // an address range that our Image Directory fits in.
  654. for (int i = 0; i < m_pIFH->NumberOfSections; i++) {
  655. if ((VAImageDir >= m_pISH[i].VirtualAddress) &&
  656. (VAImageDir < (m_pISH[i].VirtualAddress + m_pISH[i].SizeOfRawData)))
  657. {
  658. pISH = &m_pISH[i];
  659. break;
  660. }
  661. }
  662. // Bail out if we could not find a section that owns our Image Directory.
  663. if (!pISH) {
  664. //SetModuleError(pModule, "Could not find the section that owns the Export Directory.");
  665. return FALSE;
  666. }
  667. // Compute our base that everything else is an offset from. We do this by
  668. // taking our base file pointer and adding our section's PointerToRawData,
  669. // which is an absolute offset value into our file. We then subtract off our
  670. // Virtual Address since the offsets we are going to be adding later will be
  671. // relative to the this Virtual Address
  672. DWORD_PTR dwBase = (DWORD_PTR)m_lpvFile + pISH->PointerToRawData - pISH->VirtualAddress;
  673. // To locate the beginning of our Image Export Directory, we add our
  674. // Image Directory offset to our base.
  675. PIMAGE_EXPORT_DIRECTORY pIED = (PIMAGE_EXPORT_DIRECTORY)(dwBase + VAImageDir);
  676. // pdwNames is a DWORD array of size pIED->NumberOfNames, which contains VA
  677. // pointers to all the function name strings. pwOrdinals is a WORD array of
  678. // size pIED->NumberOfNames, which contains all the ordinal values for each
  679. // function exported by name. pdwNames and pwOrdinals are parallel arrays,
  680. // meaning that the ordinal in pwOrdinals[x] goes with the function name
  681. // pointed to by pdwNames[x]. The value used to index these arrays is
  682. // referred to as the "hint".
  683. // pdwAddresses is a DWORD array of size pIED->NumberOfFunctions, which
  684. // contains the entrypoint addresses for all functions exported by the
  685. // module. Contrary to several PE format documents, this array is *not*
  686. // parallel with pdwNames and pwOrdinals. The index used for this array is
  687. // the ordinal value of the function you are interested in, minus the base
  688. // ordinal specified in pIED->Base. Another common mistake is to assume that
  689. // pIED->NumberOfFunctions is always equal to pIED->AddressOfNames. If the
  690. // module exports function by ordinal only, then pIED->NumberOfFunctions
  691. // will be greater than pIED->NumberOfNames.
  692. DWORD *pdwNames = (DWORD*)(dwBase + (DWORD)pIED->AddressOfNames);
  693. WORD *pwOrdinals = (WORD* )(dwBase + (DWORD)pIED->AddressOfNameOrdinals);
  694. DWORD *pdwAddresses = (DWORD*)(dwBase + (DWORD)pIED->AddressOfFunctions);
  695. CFunction *pFunctionLast = NULL, *pFunctionNew;
  696. // Loop through all the "exported by name" functions.
  697. for (int hint = 0; hint < (int)pIED->NumberOfNames; hint++) {
  698. // Get our ordinal value, function name, and entrypoint address
  699. int ordinal = pIED->Base + (DWORD)pwOrdinals[hint];
  700. LPCSTR szFunction = (LPCSTR)(dwBase + pdwNames[hint]);
  701. DWORD dwAddress = pdwAddresses[ordinal - pIED->Base];
  702. LPCSTR szForward = NULL;
  703. // Certain modules, such as NTDLL.DLL and MSVCRT40.DLL, have what are
  704. // known as forwarded functions. Forwarded functions are functions that
  705. // are exported from one module, but the code actually lives in another
  706. // module. We can check to see if a function is forwarded by looking at
  707. // its address pointer. If the address pointer points to the character
  708. // immediately following the NULL character in its function name string,
  709. // then this address pointer is really a pointer to a forward string in
  710. // the string table. Some documents state that if the address points to
  711. // a RVA in our current section, then the address must point to a forward
  712. // string. This is not true since the function code can (and sometimes
  713. // does) live in the same section that we are currently in.
  714. if (((DWORD_PTR)szFunction + strlen(szFunction) + 1) == (dwBase + dwAddress)) {
  715. szForward = (LPCSTR)(dwBase + dwAddress);
  716. }
  717. // Create a new CFunction object for this function.
  718. if (!(pFunctionNew = CreateFunction(ordinal, hint, szFunction, dwAddress, szForward))) {
  719. m_fOutOfMemory = TRUE;
  720. return FALSE;
  721. }
  722. // Add the function to the end of our module's export function linked list
  723. if (pFunctionLast) {
  724. pFunctionLast->m_pNext = pFunctionNew;
  725. } else {
  726. pModule->m_pData->m_pExports = pFunctionNew;
  727. }
  728. pFunctionLast = pFunctionNew;
  729. }
  730. // Loop through all the "exported by ordinal" functions. This module has
  731. // pIED->NumberOfFunctions functions with consecutive ordinals starting
  732. // with the ordinal specified by pIED->Base. We need to loop through all
  733. // these ordinal values and add any to our list that have not already been
  734. // added by name.
  735. for (int ordinal = pIED->Base;
  736. ordinal < (int)(pIED->NumberOfFunctions + pIED->Base); ordinal++) {
  737. // Loop through our current list to make sure we haven't already added
  738. // this function during our "exported by name" search above.
  739. CFunction *pFunctionCur = pModule->m_pData->m_pExports;
  740. while (pFunctionCur) {
  741. if (pFunctionCur->m_ordinal == ordinal) {
  742. break;
  743. }
  744. pFunctionCur = pFunctionCur->m_pNext;
  745. }
  746. // If this ordinal is not currently in our list, then add it to our list.
  747. if (!pFunctionCur) {
  748. // Get this function's entrypoint address.
  749. DWORD dwAddress = pdwAddresses[ordinal - pIED->Base];
  750. // Create a new CFunction object for this function.
  751. if (!(pFunctionNew = CreateFunction(ordinal, -1, "", dwAddress))) {
  752. m_fOutOfMemory = TRUE;
  753. return FALSE;
  754. }
  755. // Add the function to the end of our module's export function linked list
  756. if (pFunctionLast) {
  757. pFunctionLast->m_pNext = pFunctionNew;
  758. } else {
  759. pModule->m_pData->m_pExports = pFunctionNew;
  760. }
  761. pFunctionLast = pFunctionNew;
  762. }
  763. }
  764. return TRUE;
  765. }
  766. //******************************************************************************
  767. BOOL CDepends::VerifyParentImports(CModule *pModule) {
  768. CModule *pModuleHead = NULL, *pModuleLast, *pModuleCur;
  769. // Loop through each of our parent import functions.
  770. CFunction *pImport = pModule->m_pParentImports;
  771. while (pImport) {
  772. // Mark this parent import function as not resolved before starting search.
  773. pImport->m_dwExtra = 0;
  774. // Loop through all our exports, looking for a match with our current import.
  775. CFunction *pExport = pModule->m_pData->m_pExports;
  776. while (pExport) {
  777. // If we have a name, then check for the match by name.
  778. if (*pImport->m_szName) {
  779. if (!strcmp(pImport->m_szName, pExport->m_szName)) {
  780. // We found a match. Link this parent import to its associated
  781. // export, break out of loop, and move on to handling our next
  782. // parent import.
  783. pImport->m_dwExtra = (DWORD_PTR)pExport;
  784. break;
  785. }
  786. // If we don't have a name, then check for the match by name.
  787. } else if (pImport->m_ordinal == pExport->m_ordinal) {
  788. // We found a match. Link this parent import to its associated
  789. // export, break out of loop, and move on to handling our next
  790. // parent import.
  791. pImport->m_dwExtra = (DWORD_PTR)pExport;
  792. break;
  793. }
  794. // Move to the next export
  795. pExport = pExport->m_pNext;
  796. }
  797. // Check to see if an export match was found.
  798. if (pImport->GetAssociatedExport()) {
  799. CHAR szFile[1024];
  800. LPCSTR szFunction;
  801. // If an export was found, check to see if it is a forwarded function.
  802. // If it is forwarded, then we need to make sure we consider the
  803. // forwarded module as a new dependent of the current module.
  804. LPCSTR szForward = pImport->GetAssociatedExport()->GetForwardString();
  805. if (szForward) {
  806. // Extract and build the DLL name from the forward string.
  807. LPCSTR pszDot = strchr(szForward, '.');
  808. if (pszDot) {
  809. strncpy(szFile, szForward, (size_t)(pszDot - szForward));
  810. strcpy(szFile + (pszDot - szForward), ".DLL");
  811. szFunction = pszDot + 1;
  812. } else {
  813. strcpy(szFile, "Invalid");
  814. szFunction = szForward;
  815. }
  816. // Search our local forward module list to see if we have already
  817. // created a forward CModoule for this DLL file.
  818. for (pModuleLast = NULL, pModuleCur = pModuleHead; pModuleCur;
  819. pModuleLast = pModuleCur, pModuleCur = pModuleCur->m_pNext)
  820. {
  821. if (!_stricmp(pModuleCur->m_pData->m_szFile, szFile)) {
  822. break;
  823. }
  824. }
  825. // If we have not created a forward module for this file yet, then
  826. // create it now and add it to the end of our list.
  827. if (!pModuleCur) {
  828. if (!(pModuleCur = CreateModule(szFile, pModule->m_depth + 1))) {
  829. m_fOutOfMemory = TRUE;
  830. return FALSE;
  831. }
  832. pModuleCur->m_fForward = TRUE;
  833. // Add the new module to our local forward module list.
  834. if (pModuleLast) {
  835. pModuleLast->m_pNext = pModuleCur;
  836. } else {
  837. pModuleHead = pModuleCur;
  838. }
  839. }
  840. // Create a new CFunction object for this function.
  841. CFunction *pFunction = CreateFunction(-1, -1, szFunction, (DWORD)-1);
  842. if (!pFunction) {
  843. m_fOutOfMemory = TRUE;
  844. return FALSE;
  845. }
  846. // Insert this function object into our forward module's import list.
  847. pFunction->m_pNext = pModuleCur->m_pParentImports;
  848. pModuleCur->m_pParentImports = pFunction;
  849. }
  850. } else {
  851. // If we could not find an import/export match, then flag the module
  852. // as having an export error.
  853. pModule->m_fExportError = TRUE;
  854. }
  855. // Move to the next parent import function.
  856. pImport = pImport->m_pNext;
  857. }
  858. // If we created any forward modules during our entire import verify, then
  859. // add them to the end of our module's dependent module list.
  860. if (pModuleHead) {
  861. // Walk to end of our module's dependent module list.
  862. for (pModuleLast = pModule->m_pDependents;
  863. pModuleLast && pModuleLast->m_pNext;
  864. pModuleLast = pModuleLast->m_pNext)
  865. {}
  866. // Add our local list to the end of our module's dependent module list.
  867. if (pModuleLast) {
  868. pModuleLast->m_pNext = pModuleHead;
  869. } else {
  870. pModule->m_pDependents = pModuleHead;
  871. }
  872. }
  873. return TRUE;
  874. }
  875. //******************************************************************************
  876. BOOL CDepends::ProcessModule(CModule *pModule) {
  877. BOOL fResult = FALSE;
  878. // First check to see if this module is a duplicate. If it is, make sure the
  879. // original instance of this module has been processed and then just perform
  880. // the Parent Import Verify. If the module being passed in is an original,
  881. // then just ensure that we haven't already processed this module.
  882. if (pModule->m_pModuleOriginal) {
  883. // Process the original module and its subtree.
  884. fResult = ProcessModule(pModule->m_pModuleOriginal);
  885. if (!fResult && m_fOutOfMemory) {
  886. return FALSE;
  887. }
  888. // Exit now if we have already processed this original module in the past.
  889. } else if (pModule->m_pData->m_fProcessed) {
  890. return TRUE;
  891. } else {
  892. // Mark this module as processed.
  893. pModule->m_pData->m_fProcessed = TRUE;
  894. // Open the file for read.
  895. //m_hFile = CreateFile(pModule->m_pData->m_szPath, GENERIC_READ,
  896. m_hFile = CreateFileA(pModule->m_pData->m_szPath, GENERIC_READ,
  897. FILE_SHARE_READ, NULL, OPEN_EXISTING,
  898. FILE_ATTRIBUTE_NORMAL, NULL);
  899. // Exit now if the file failed to open.
  900. if (m_hFile == INVALID_HANDLE_VALUE) {
  901. DWORD dwGLE = GetLastError();
  902. if (dwGLE == ERROR_FILE_NOT_FOUND) {
  903. //SetModuleError(pModule, "File not found in local directory or search path.");
  904. pModule->m_pData->m_fFileNotFound = TRUE;
  905. } else if (dwGLE == ERROR_PATH_NOT_FOUND) {
  906. //SetModuleError(pModule, "Invalid path or file name.");
  907. pModule->m_pData->m_fFileNotFound = TRUE;
  908. } else {
  909. //SetModuleError(pModule, "CreateFile() failed (%u).", dwGLE);
  910. }
  911. return FALSE;
  912. }
  913. // Create a file mapping object for the open module.
  914. HANDLE hMap = CreateFileMapping(m_hFile, NULL, PAGE_READONLY, 0, 0, NULL);
  915. // Exit now if the file failed to map.
  916. if (hMap == NULL) {
  917. //SetModuleError(pModule, "CreateFileMapping() failed (%u).", GetLastError());
  918. CloseHandle(m_hFile);
  919. m_hFile = NULL;
  920. return FALSE;
  921. }
  922. // Create a file mapping view for the open module.
  923. m_lpvFile = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
  924. // Exit now if the mapped view failed to create.
  925. if (m_lpvFile == NULL) {
  926. //SetModuleError(pModule, "MapViewOfFile() failed (%u).", GetLastError());
  927. CloseHandle(hMap);
  928. CloseHandle(m_hFile);
  929. m_hFile = NULL;
  930. return FALSE;
  931. }
  932. __try {
  933. // Everything from here on is pretty much relying on the file being a
  934. // valid binary with valid pointers and offsets. It is fairly safe to
  935. // just wrap everything in exception handling and then blindly access
  936. // the file. Anything that causes us to move outside our file mapping
  937. // will generate an exception and bring us back here to fail the file.
  938. fResult = (VerifyModule(pModule) &&
  939. GetModuleInfo(pModule) &&
  940. BuildImports(pModule) &&
  941. BuildDelayImports(pModule) &&
  942. BuildExports(pModule));
  943. } __except(EXCEPTION_EXECUTE_HANDLER) {
  944. //SetModuleError(pModule, "Module does not appear to be a valid Win32 module.");
  945. }
  946. // Close our map view pointer, our map handle, and our file handle.
  947. UnmapViewOfFile(m_lpvFile);
  948. CloseHandle(hMap);
  949. CloseHandle(m_hFile);
  950. // Clear our file handles and pointers.
  951. m_hFile = NULL;
  952. m_lpvFile = NULL;
  953. m_pIFH = NULL;
  954. m_pIOH = NULL;
  955. m_pISH = NULL;
  956. }
  957. // Compare our parent imports with our exports to make sure they all match up.
  958. if (!VerifyParentImports(pModule)) {
  959. return FALSE;
  960. }
  961. // Safeguard to ensure that we don't get stuck in some recursize loop. This
  962. // can occur if there is a circular dependency with forwarded functions. This
  963. // is extremely rare and would require someone to design it, but we need
  964. // to handle this case to prevent us from crashing on it. When NT encounters
  965. // a module like this, it fails the load with exception 0xC00000FD which is
  966. // defined as STATUS_STACK_OVERFLOW in WINNT.H. We use 255 as our max depth
  967. // because the several versions of the tree control crash if more than 256
  968. // depths are displayed.
  969. if (pModule->m_depth >= 255) {
  970. // If this module has dependents, then delete them.
  971. if (pModule->m_pDependents) {
  972. DeleteModule(pModule->m_pDependents);
  973. pModule->m_pDependents = NULL;
  974. }
  975. // Flag this document as having a circular dependency error.
  976. m_fCircularError = TRUE;
  977. return FALSE;
  978. }
  979. // Recurse into ProcessModule() to handle all our dependent modules.
  980. pModule = pModule->m_pDependents;
  981. while (pModule) {
  982. if (!ProcessModule(pModule) && m_fOutOfMemory) {
  983. return FALSE;
  984. }
  985. pModule = pModule->m_pNext;
  986. }
  987. return fResult;
  988. }