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.

2050 lines
52 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. appparse.cpp
  5. Abstract:
  6. Core Engine for dumping importing information from DLL's
  7. and executables into an XML file
  8. Used by command line appparse and web-based appparse
  9. History:
  10. 06/07/2000 t-michkr Created
  11. --*/
  12. //#define PJOB_SET_ARRAY int
  13. #include "stdafx.h"
  14. #include <windows.h>
  15. #include <delayimp.h>
  16. #include <shlwapi.h>
  17. #include <sfc.h>
  18. #include <lmcons.h>
  19. #include <assert.h>
  20. #include <string.h>
  21. #include <stdlib.h>
  22. #include <stdio.h>
  23. #include <ctype.h>
  24. #include <new.h>
  25. #include "acFileAttr.h"
  26. // These are needed for command line compiling
  27. #define stricmp _stricmp
  28. // Global heap for AppParse. If 0, Process Heap is used instead.
  29. HANDLE g_hHeap = 0;
  30. // Global search string
  31. char* g_szSearch = "*";
  32. // Whether we are in "verbose" mode, or not.
  33. bool g_fVerbose = false;
  34. // To sort output by DLLs
  35. bool g_fAPILogging = false;
  36. // True if no XML tags are to be printed, false otherwise
  37. bool g_fRaw = false;
  38. // Whether to recurse into subdirectories.
  39. bool g_fRecurse = false;
  40. // Current path relative to start, used by CModule
  41. char g_szCurrentPath[MAX_PATH] = {'\0'};
  42. // Returns true if szFileName is a system DLL (like gdi32, user32, etc.)
  43. bool IsSystemDLL(const char* szFileName);
  44. // Resolve a linker name to a "normal" name (unmangle C++ names, etc.)
  45. void LinkName2Name(char* szLinkName, char* szName);
  46. // Just do indentation, save repetitious code
  47. void Indent(int iLevel, FILE* pFile = stdout);
  48. // Check if function matches global search string
  49. bool MatchFunction(const char* szFunc);
  50. // Go through a directory and profile EXE's.
  51. void ProfileDirectory(char* szDirectory, HANDLE hEvent);
  52. void* __cdecl operator new(size_t size);
  53. void __cdecl operator delete(void* pVal);
  54. // Replace XML reserved characters like > with &gt
  55. void WriteXMLOKString(char* szString, FILE* pFile);
  56. // Parsing history for modules
  57. class CModuleParseStack
  58. {
  59. private:
  60. struct SNode
  61. {
  62. char* szName;
  63. SNode* pNext;
  64. SNode()
  65. {
  66. szName = 0;
  67. pNext = 0;
  68. }
  69. ~SNode()
  70. {
  71. if(szName)
  72. {
  73. delete szName;
  74. szName = 0;
  75. }
  76. }
  77. };
  78. SNode* m_pList;
  79. public:
  80. // Constructor, setup empty list.
  81. CModuleParseStack()
  82. {
  83. m_pList = 0;
  84. }
  85. // Add a name to the top of the parse stack
  86. void PushName(char* szName)
  87. {
  88. assert(!IsBadReadPtr(szName, 1));
  89. SNode* pNode = new SNode;
  90. pNode->szName = new char[strlen(szName)+1];
  91. strcpy(pNode->szName, szName);
  92. pNode->pNext = m_pList;
  93. m_pList = pNode;
  94. }
  95. // Remove a name from the top of the parse stack
  96. void Pop()
  97. {
  98. assert(m_pList);
  99. SNode* pTemp = m_pList->pNext;
  100. delete m_pList;
  101. m_pList = pTemp;
  102. }
  103. // Return true if module has already been parsed
  104. bool CheckModuleParsed(char* szName)
  105. {
  106. assert(!IsBadReadPtr(szName, 1));
  107. SNode* pNode = m_pList;
  108. while(pNode)
  109. {
  110. if(stricmp(pNode->szName, szName) == 0)
  111. return true;
  112. pNode = pNode->pNext;
  113. }
  114. return false;
  115. }
  116. bool IsEmpty()
  117. {
  118. return (m_pList == 0);
  119. }
  120. void ClearParseHistory()
  121. {
  122. SNode* pNode = m_pList;
  123. while(pNode)
  124. {
  125. SNode* pNext = pNode->pNext;
  126. delete pNode;
  127. pNode = pNext;
  128. }
  129. m_pList = 0;
  130. }
  131. };
  132. // CFunction, an imported function and associated information.
  133. class CFunction
  134. {
  135. private:
  136. // Name of function (if imported by name)
  137. char* m_szName;
  138. // Name of function actually pointed to.
  139. char* m_szForwardName;
  140. // Ordinal, older style importing
  141. int m_iOrdinal;
  142. // Quick lookup info
  143. int m_iHint;
  144. // Address of function, if bound
  145. DWORD m_dwAddress;
  146. // Whether this function is a delayed import or not.
  147. bool m_fDelayed;
  148. // Next function in list
  149. CFunction* m_pNext;
  150. // No default construction or copying allowed
  151. CFunction();
  152. CFunction operator=(const CFunction&);
  153. public:
  154. CFunction(char* szName, int iHint, int iOrdinal, DWORD dwAddress,
  155. bool fDelayed)
  156. {
  157. assert(!IsBadReadPtr(szName, 1));
  158. m_szName = new char[strlen(szName)+1];
  159. strcpy(m_szName, szName);
  160. m_iOrdinal = iOrdinal;
  161. m_iHint = iHint;
  162. m_dwAddress = dwAddress;
  163. m_pNext = 0;
  164. m_fDelayed = fDelayed;
  165. m_szForwardName = 0;
  166. }
  167. CFunction(const CFunction& fn)
  168. {
  169. m_szName = new char[strlen(fn.m_szName)+1];
  170. strcpy(m_szName, fn.m_szName);
  171. m_iOrdinal = fn.m_iOrdinal;
  172. m_iHint = fn.m_iHint;
  173. m_dwAddress = fn.m_dwAddress;
  174. m_pNext = 0;
  175. m_fDelayed = fn.m_fDelayed;
  176. if(fn.m_szForwardName)
  177. {
  178. m_szForwardName = new char[strlen(fn.m_szForwardName)+1];
  179. strcpy(m_szForwardName, fn.m_szForwardName);
  180. }
  181. else
  182. m_szForwardName = 0;
  183. }
  184. ~CFunction()
  185. {
  186. if(m_szName)
  187. {
  188. delete m_szName;
  189. m_szName = 0;
  190. }
  191. if(m_szForwardName)
  192. {
  193. delete m_szForwardName;
  194. m_szForwardName = 0;
  195. }
  196. }
  197. CFunction* Next()
  198. { return m_pNext; }
  199. char* Name()
  200. { return m_szName; }
  201. void SetForwardName(char* szForward)
  202. {
  203. assert(!IsBadReadPtr(szForward, 1));
  204. m_szForwardName = new char[strlen(szForward)+1];
  205. strcpy(m_szForwardName, szForward);
  206. }
  207. void SetNext(CFunction* pFunc)
  208. {
  209. assert(pFunc == 0 || !IsBadReadPtr(pFunc, 1));
  210. m_pNext = pFunc;
  211. }
  212. // Display function info, either to console
  213. // or to XML file.
  214. static void WriteHeader(int iIndentLevel, FILE* pFile);
  215. void WriteFunction(int iIndentLevel, FILE* pFile);
  216. };
  217. // COrdinalImport
  218. // A function imported by ordinal, to be resolved to a CFunction
  219. class COrdinalImport
  220. {
  221. private:
  222. int m_iOrdinal;
  223. COrdinalImport* m_pNext;
  224. bool m_fDelayed;
  225. COrdinalImport();
  226. COrdinalImport(const COrdinalImport&);
  227. COrdinalImport& operator = (const COrdinalImport&);
  228. public:
  229. COrdinalImport(int iOrd, bool fDelayed = false)
  230. {
  231. m_iOrdinal = iOrd;
  232. m_fDelayed = fDelayed;
  233. }
  234. int GetOrdinal()
  235. { return m_iOrdinal;}
  236. bool GetDelayed()
  237. { return m_fDelayed; }
  238. COrdinalImport* Next()
  239. { return m_pNext; }
  240. void SetNext(COrdinalImport* pNext)
  241. { m_pNext = pNext; }
  242. };
  243. // CModule, an executable image with imports
  244. class CModule
  245. {
  246. friend class CGlobalModuleList;
  247. private:
  248. // The name of this module (in the form path\foo.exe)
  249. char* m_szName;
  250. // The name of this module relative to the starting path
  251. char* m_szFullName;
  252. // Base pointer of the image in memory.
  253. void* m_pvImageBase;
  254. // DLL's imported by this module.
  255. CModule* m_pImportedDLLs;
  256. // Functions imported from this module by its parent.
  257. CFunction* m_pFunctions;
  258. // Functions imported by ordinal from this module
  259. COrdinalImport* m_pOrdinals;
  260. // Image headers
  261. PIMAGE_OPTIONAL_HEADER m_pioh;
  262. PIMAGE_SECTION_HEADER m_pish;
  263. PIMAGE_FILE_HEADER m_pifh;
  264. // Next module in a list
  265. CModule* m_pNext;
  266. // Text description of any errors that may have occurred
  267. char* m_szError;
  268. // Whether or not this module is an OS module
  269. bool m_fSystem;
  270. // Version info
  271. WORD m_wDosDate;
  272. WORD m_wDosTime;
  273. int m_nAttrCount;
  274. char** m_szAttrValues;
  275. char** m_szAttrNames;
  276. bool WalkImportTable();
  277. bool WalkDelayImportTable();
  278. static void InsertFunctionSorted(CFunction* pFunc, CFunction** ppList);
  279. bool ResolveForwardedFunctionsAndOrdinals();
  280. bool ParseImportTables();
  281. void InsertOrdinal(int iOrdinal, bool fDelayed = false);
  282. CModule* FindChild(char* szName);
  283. bool Empty();
  284. void GetAllFunctions(CFunction** ppFunctionList);
  285. void* RVAToPtr(const void* pAddr)
  286. { return RVAToPtr(reinterpret_cast<DWORD>(pAddr)); }
  287. void* RVAToPtr(DWORD dwRVA);
  288. void GetFileVerInfo(HANDLE hFile, char* szFileName);
  289. public:
  290. CModule(char* szName);
  291. ~CModule();
  292. bool ParseModule(HANDLE hEvent);
  293. void InsertChildModuleSorted(CModule* pcm);
  294. // Functions to write module info to either the console or an XML file
  295. void WriteModule(bool fTopLevel, int iIndentLevel, FILE* pFile);
  296. };
  297. // List of all top-level modules being profiled
  298. class CGlobalModuleList
  299. {
  300. private:
  301. CModule* m_pModules;
  302. public:
  303. CGlobalModuleList()
  304. {
  305. m_pModules = 0;
  306. }
  307. ~CGlobalModuleList()
  308. {
  309. Clear();
  310. }
  311. void Clear()
  312. {
  313. CModule* pMod = m_pModules;
  314. while(pMod)
  315. {
  316. CModule* pNext = pMod->m_pNext;
  317. delete pMod;
  318. pMod = pNext;
  319. }
  320. m_pModules = 0;
  321. }
  322. void InsertModuleSorted(CModule* pMod)
  323. {
  324. assert(!IsBadReadPtr(pMod, 1));
  325. // Special case, insert at front
  326. if(m_pModules == 0
  327. || stricmp(m_pModules->m_szFullName, pMod->m_szFullName) > 0)
  328. {
  329. pMod->m_pNext = m_pModules;
  330. m_pModules = pMod;
  331. return;
  332. }
  333. CModule* pPrev = m_pModules;
  334. CModule* pTemp = m_pModules->m_pNext;
  335. while(pTemp)
  336. {
  337. if(stricmp(pTemp->m_szFullName, pMod->m_szFullName) > 0)
  338. {
  339. pMod->m_pNext = pTemp;
  340. pPrev->m_pNext = pMod;;
  341. return;
  342. }
  343. pPrev = pTemp;
  344. pTemp = pTemp->m_pNext;
  345. }
  346. // Insert at end
  347. pMod->m_pNext = 0;
  348. pPrev->m_pNext = pMod;;
  349. }
  350. void Write(FILE* pFile, char* szProjectName, int iPtolemyID)
  351. {
  352. if(!g_fRaw)
  353. {
  354. fprintf(pFile, "<APPPARSERESULTS>\n");
  355. fprintf(pFile, "<PROJECT NAME=\"%s\" ID=\"%d\">\n",
  356. szProjectName, iPtolemyID);
  357. }
  358. CModule* pMod = m_pModules;
  359. while(pMod)
  360. {
  361. pMod->WriteModule(true, 0, pFile);
  362. pMod = pMod->m_pNext;
  363. }
  364. if(!g_fRaw)
  365. {
  366. fprintf(pFile, "</PROJECT>\n");
  367. fprintf(pFile, "</APPPARSERESULTS>\n");
  368. }
  369. }
  370. };
  371. // Global parsing history
  372. CModuleParseStack g_ParseStack;
  373. // Empty global module, containing all modules parsed
  374. CGlobalModuleList g_modules;
  375. CModule::CModule(char* szName)
  376. {
  377. assert(!IsBadReadPtr(szName, 1));
  378. m_szName = new char[strlen(szName)+1];
  379. strcpy(m_szName, szName);
  380. WIN32_FIND_DATA ffd;
  381. // Only give it the full relative path if it is in this directory
  382. // If elsewhere, give it just the filename.
  383. HANDLE hSearch = FindFirstFile(szName, &ffd);
  384. if(hSearch == INVALID_HANDLE_VALUE)
  385. {
  386. m_szFullName = new char[strlen(m_szName) + 1];
  387. strcpy(m_szFullName, m_szName);
  388. }
  389. else
  390. {
  391. m_szFullName = new char[strlen(m_szName) + strlen(g_szCurrentPath)+1];
  392. strcpy(m_szFullName, g_szCurrentPath);
  393. strcat(m_szFullName, m_szName);
  394. FindClose(hSearch);
  395. }
  396. m_pvImageBase = 0;
  397. m_pImportedDLLs = 0;
  398. m_pFunctions = 0;
  399. m_pOrdinals = 0;
  400. m_pioh = 0;
  401. m_pish = 0;
  402. m_pifh = 0;
  403. m_pNext = 0;
  404. m_szError = 0;
  405. m_fSystem = false;
  406. m_nAttrCount = 0;
  407. m_szAttrValues = 0;
  408. m_szAttrNames = 0;
  409. m_wDosDate = 0;
  410. m_wDosTime = 0;
  411. }
  412. CModule::~CModule()
  413. {
  414. if(m_szName)
  415. {
  416. delete m_szName;
  417. m_szName = 0;
  418. }
  419. if(m_szFullName)
  420. {
  421. delete m_szFullName;
  422. m_szFullName = 0;
  423. }
  424. CFunction* pFunc = m_pFunctions;
  425. while(pFunc)
  426. {
  427. CFunction* pNext = pFunc->Next();
  428. delete pFunc;
  429. pFunc = pNext;
  430. }
  431. m_pFunctions = 0;
  432. COrdinalImport* pOrd = m_pOrdinals;
  433. while(pOrd)
  434. {
  435. COrdinalImport* pNext = pOrd->Next();
  436. delete pOrd;
  437. pOrd = pNext;
  438. }
  439. m_pOrdinals = 0;
  440. for(int i = 0; i < m_nAttrCount; i++)
  441. {
  442. if(m_szAttrNames)
  443. {
  444. if(m_szAttrNames[i])
  445. {
  446. delete m_szAttrNames[i];
  447. m_szAttrNames[i] = 0;
  448. }
  449. }
  450. if(m_szAttrValues)
  451. {
  452. if(m_szAttrValues[i])
  453. {
  454. delete m_szAttrValues[i];
  455. m_szAttrValues[i] = 0;
  456. }
  457. }
  458. }
  459. if(m_szAttrNames)
  460. {
  461. delete m_szAttrNames;
  462. m_szAttrNames = 0;
  463. }
  464. if(m_szAttrValues)
  465. {
  466. delete m_szAttrValues;
  467. m_szAttrValues = 0;
  468. }
  469. }
  470. // Return true no functions are imported from this module,
  471. // or any of its children modules.
  472. bool CModule::Empty()
  473. {
  474. if(m_pFunctions != 0 || m_pOrdinals != 0)
  475. return false;
  476. CModule* pMod = m_pImportedDLLs;
  477. while(pMod)
  478. {
  479. if(!pMod->Empty())
  480. return false;
  481. pMod = pMod->m_pNext;
  482. }
  483. return true;
  484. }
  485. // Convert a relative virtual address to an absolute address
  486. void* CModule::RVAToPtr(DWORD dwRVA)
  487. {
  488. assert(!IsBadReadPtr(m_pifh, sizeof(*m_pifh)));
  489. assert(!IsBadReadPtr(m_pish, sizeof(*m_pish)));
  490. assert(!IsBadReadPtr(m_pvImageBase, 1));
  491. PIMAGE_SECTION_HEADER pish = m_pish;
  492. // Go through each section
  493. for (int i = 0; i < m_pifh->NumberOfSections; i++)
  494. {
  495. // If it's in this section, computer address and return it.
  496. if ((dwRVA >= pish->VirtualAddress) &&
  497. (dwRVA < (pish->VirtualAddress + pish->SizeOfRawData)))
  498. {
  499. void* pAddr =
  500. reinterpret_cast<void*>(reinterpret_cast<DWORD>(m_pvImageBase) +
  501. pish->PointerToRawData + dwRVA - pish->VirtualAddress);
  502. return pAddr;
  503. }
  504. pish++;
  505. }
  506. // This indicates an invalid RVA, meaning an invalid image, so
  507. // throw an exception
  508. throw;
  509. return 0;
  510. }
  511. // Return a pointer to the first child matching szName, false otehrwise
  512. CModule* CModule::FindChild(char* szName)
  513. {
  514. assert(!IsBadReadPtr(szName, 1));
  515. CModule* pMod = m_pImportedDLLs;
  516. while(pMod)
  517. {
  518. if(stricmp(pMod->m_szName, szName)==0)
  519. return pMod;
  520. pMod = pMod->m_pNext;
  521. }
  522. return 0;
  523. }
  524. // Add an ordinal import to the module.
  525. void CModule::InsertOrdinal(int iOrdinal, bool fDelayed)
  526. {
  527. COrdinalImport* pNew = new COrdinalImport(iOrdinal, fDelayed);
  528. pNew->SetNext(m_pOrdinals);
  529. m_pOrdinals = pNew;
  530. }
  531. // Add an imported function to a function list.
  532. void CModule::InsertFunctionSorted(CFunction* pFunc, CFunction** ppList)
  533. {
  534. // Special case, insert at front
  535. if((*ppList)== 0
  536. || stricmp((*ppList)->Name(), pFunc->Name()) > 0)
  537. {
  538. pFunc->SetNext(*ppList);
  539. (*ppList) = pFunc;
  540. return;
  541. }
  542. CFunction* pPrev = *ppList;
  543. CFunction* pTemp = (*ppList)->Next();
  544. while(pTemp)
  545. {
  546. // Don't insert duplicates. This is mainly for API logging only.
  547. if(strcmp(pTemp->Name(), pFunc->Name())==0)
  548. return;
  549. if(stricmp(pTemp->Name(), pFunc->Name()) > 0)
  550. {
  551. pFunc->SetNext(pTemp);
  552. pPrev->SetNext(pFunc);
  553. return;
  554. }
  555. pPrev = pTemp;
  556. pTemp = pTemp->Next();
  557. }
  558. // Insert at end
  559. pFunc->SetNext(0);
  560. pPrev->SetNext(pFunc);
  561. }
  562. // Add a child module to this module.
  563. void CModule::InsertChildModuleSorted(CModule* pcm)
  564. {
  565. // Special case, insert at front
  566. if(m_pImportedDLLs == 0
  567. || stricmp(m_pImportedDLLs->m_szName, pcm->m_szName) > 0)
  568. {
  569. pcm->m_pNext = m_pImportedDLLs;
  570. m_pImportedDLLs = pcm;
  571. return;
  572. }
  573. CModule* pPrev = m_pImportedDLLs;
  574. CModule* pTemp = m_pImportedDLLs->m_pNext;
  575. while(pTemp)
  576. {
  577. if(stricmp(pTemp->m_szName, pcm->m_szName) > 0)
  578. {
  579. pcm->m_pNext = pTemp;
  580. pPrev->m_pNext = pcm;;
  581. return;
  582. }
  583. pPrev = pTemp;
  584. pTemp = pTemp->m_pNext;
  585. }
  586. // Insert at end
  587. pcm->m_pNext = 0;
  588. pPrev->m_pNext = pcm;;
  589. }
  590. // Add all functions imported from this module to the function list
  591. // Used mainly for API logging.
  592. void CModule::GetAllFunctions(CFunction** ppFunctionList)
  593. {
  594. CFunction* pFunc = m_pFunctions;
  595. while(pFunc)
  596. {
  597. // Copy pFunc
  598. CFunction* pNew = new CFunction(*pFunc);
  599. InsertFunctionSorted(pNew, ppFunctionList);
  600. pFunc = pFunc->Next();
  601. }
  602. CModule* pMod = m_pImportedDLLs;
  603. while(pMod)
  604. {
  605. pMod->GetAllFunctions(ppFunctionList);
  606. pMod = pMod->m_pNext;
  607. }
  608. }
  609. // Go through a modules export table and get forwarding information
  610. // and resolve ordinal imports to name.
  611. bool CModule::ResolveForwardedFunctionsAndOrdinals()
  612. {
  613. // Get virtual address of export table
  614. DWORD dwVAImageDir =
  615. m_pioh->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
  616. // Get export table info
  617. PIMAGE_EXPORT_DIRECTORY pied =
  618. reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(RVAToPtr(dwVAImageDir));
  619. DWORD* pdwNames = reinterpret_cast<DWORD*>(RVAToPtr(pied->AddressOfNames));
  620. WORD* pwOrdinals = reinterpret_cast<WORD*>(RVAToPtr(pied->AddressOfNameOrdinals));
  621. DWORD* pdwAddresses = reinterpret_cast<DWORD*>(RVAToPtr(pied->AddressOfFunctions));
  622. // Go through each entry in the export table
  623. for(unsigned uiHint = 0; uiHint < pied->NumberOfNames; uiHint++)
  624. {
  625. // Get function name, ordinal, and address info.
  626. char* szFunction = reinterpret_cast<char*>(RVAToPtr(pdwNames[uiHint]));
  627. int ordinal = pied->Base + static_cast<DWORD>(pwOrdinals[uiHint]);
  628. DWORD dwAddress = pdwAddresses[ordinal-pied->Base];
  629. char* szForward = 0;
  630. // Check if this function has been forwarded to another DLL
  631. // Function has been forwarded if address is in this section.
  632. // NOTE: The DEPENDS 1.0 source says otherwise, but is incorrect.
  633. if( (dwAddress >= dwVAImageDir) &&
  634. (dwAddress < (dwVAImageDir +
  635. m_pioh->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size)))
  636. szForward = reinterpret_cast<char*>(RVAToPtr(dwAddress));
  637. // Check if we have an ordinal import refering to this
  638. COrdinalImport* pOrd = m_pOrdinals;
  639. CFunction* pFunc = 0;
  640. // See if we have a matching ordinal import.
  641. while(pOrd)
  642. {
  643. if(pOrd->GetOrdinal() == ordinal)
  644. break;
  645. pOrd = pOrd->Next();
  646. }
  647. if(pOrd != 0)
  648. {
  649. char szTemp[1024];
  650. // Unmangle forwarded name.
  651. LinkName2Name(szFunction, szTemp);
  652. // Check against search string
  653. if(MatchFunction(szTemp))
  654. {
  655. // Insert into module.
  656. pFunc = new CFunction(szTemp, -1, ordinal,
  657. dwAddress, pOrd->GetDelayed());
  658. InsertFunctionSorted(pFunc, &m_pFunctions);
  659. }
  660. }
  661. // No matching ordinal import, check normal imports.
  662. else
  663. {
  664. // Duck out early if this function isn't used in the executable.
  665. pFunc = m_pFunctions;
  666. while(pFunc)
  667. {
  668. if(strcmp(pFunc->Name(), szFunction)==0)
  669. break;
  670. pFunc = pFunc->Next();
  671. }
  672. if(pFunc == 0)
  673. continue;
  674. }
  675. // Set forwarding info
  676. if(szForward && pFunc)
  677. pFunc->SetForwardName(szForward);
  678. }
  679. return true;
  680. }
  681. // Get delayed import info from module.
  682. bool CModule::WalkDelayImportTable()
  683. {
  684. // Bail early if no delayed import table.
  685. if(m_pioh->DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].Size == 0)
  686. return true;
  687. // Locate the directory section
  688. DWORD dwVAImageDir =
  689. m_pioh->DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].VirtualAddress;
  690. // Get the import descriptor array
  691. PImgDelayDescr pidd = reinterpret_cast<PImgDelayDescr>(RVAToPtr(dwVAImageDir));
  692. while(pidd->pINT)
  693. {
  694. char* szName;
  695. if(pidd->grAttrs & 1)
  696. szName = reinterpret_cast<char*>(RVAToPtr(pidd->szName));
  697. else
  698. szName = reinterpret_cast<char*>(RVAToPtr(pidd->szName - m_pioh->ImageBase));
  699. PIMAGE_THUNK_DATA pitdf;
  700. if(pidd->grAttrs & 1)
  701. pitdf = reinterpret_cast<PIMAGE_THUNK_DATA>(RVAToPtr(pidd->pINT));
  702. else
  703. pitdf = reinterpret_cast<PIMAGE_THUNK_DATA>(RVAToPtr(
  704. reinterpret_cast<DWORD>(pidd->pINT) -
  705. static_cast<DWORD>(m_pioh->ImageBase)));
  706. // Locate child module, or create new if it does not exist.
  707. CModule* pcm = FindChild(szName);
  708. if(!pcm)
  709. {
  710. pcm = new CModule(szName);
  711. InsertChildModuleSorted(pcm);
  712. }
  713. // Loop through all imported functions
  714. while(pitdf->u1.Ordinal)
  715. {
  716. int iOrdinal;
  717. int iHint;
  718. // Check if imported by name or ordinal
  719. if(!IMAGE_SNAP_BY_ORDINAL(pitdf->u1.Ordinal))
  720. {
  721. // Get name import info
  722. PIMAGE_IMPORT_BY_NAME piibn =
  723. reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
  724. RVAToPtr(pitdf->u1.AddressOfData - m_pioh->ImageBase));
  725. char* szTemp = reinterpret_cast<char*>(piibn->Name);
  726. char szBuffer[1024];
  727. // Unmangle link name
  728. LinkName2Name(szTemp, szBuffer);
  729. // Ordinal info is invalid
  730. iOrdinal = -1;
  731. iHint = piibn->Hint;
  732. // Check against search string
  733. if(MatchFunction(szBuffer))
  734. {
  735. // Insert into function list
  736. CFunction* psf = new CFunction(szBuffer, iHint, iOrdinal,
  737. static_cast<DWORD>(-1), true);
  738. pcm->InsertFunctionSorted(psf, &pcm->m_pFunctions);
  739. }
  740. }
  741. else
  742. {
  743. // Insert a new delayed ordinal import
  744. iOrdinal = static_cast<int>(IMAGE_ORDINAL(pitdf->u1.Ordinal));
  745. pcm->InsertOrdinal(iOrdinal, true);
  746. }
  747. // Move on to next function
  748. pitdf++;
  749. }
  750. // Move to next delay import descriptor
  751. pidd++;
  752. }
  753. return true;
  754. }
  755. // Determine all functions imported by this module
  756. bool CModule::WalkImportTable()
  757. {
  758. // Bail out early if no directory
  759. if(m_pioh->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size == 0)
  760. return true;
  761. // Locate the directory section
  762. DWORD dwVAImageDir =
  763. m_pioh->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
  764. // Get the import descriptor array
  765. PIMAGE_IMPORT_DESCRIPTOR piid =
  766. reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(RVAToPtr(dwVAImageDir));
  767. // Loop through all imported modules
  768. while(piid->FirstThunk || piid->OriginalFirstThunk)
  769. {
  770. // Get module name
  771. char* szName = reinterpret_cast<char*>(RVAToPtr(piid->Name));
  772. // Find child, or create new if it does not exist.
  773. CModule* pcm = FindChild(szName);
  774. if(!pcm)
  775. {
  776. pcm = new CModule(szName);
  777. InsertChildModuleSorted(pcm);
  778. }
  779. // Get all imports from this module
  780. PIMAGE_THUNK_DATA pitdf = 0;
  781. PIMAGE_THUNK_DATA pitda = 0;
  782. // Check for MS or Borland format
  783. if(piid->OriginalFirstThunk)
  784. {
  785. // MS format, function array is original first thunk
  786. pitdf = reinterpret_cast<PIMAGE_THUNK_DATA>(RVAToPtr(piid->OriginalFirstThunk));
  787. // If the time stamp is set, this module has
  788. // been bound and the first thunk is the bound address array
  789. if(piid->TimeDateStamp)
  790. pitda = reinterpret_cast<PIMAGE_THUNK_DATA>(RVAToPtr(piid->FirstThunk));
  791. }
  792. else
  793. {
  794. // Borland format uses first thunk for function array
  795. pitdf = reinterpret_cast<PIMAGE_THUNK_DATA>(RVAToPtr(piid->FirstThunk));
  796. }
  797. // Loop through all imported functions
  798. while(pitdf->u1.Ordinal)
  799. {
  800. int iOrdinal;
  801. int iHint;
  802. // Determine if imported by ordinal or name
  803. if(!IMAGE_SNAP_BY_ORDINAL(pitdf->u1.Ordinal))
  804. {
  805. // Get name import info
  806. PIMAGE_IMPORT_BY_NAME piibn =
  807. reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
  808. RVAToPtr(pitdf->u1.AddressOfData));
  809. // Get function name
  810. char* szTemp = reinterpret_cast<char*>(piibn->Name);
  811. // Unmangle
  812. char szBuffer[1024];
  813. LinkName2Name(szTemp, szBuffer);
  814. iOrdinal = -1;
  815. iHint = piibn->Hint;
  816. // Check against search string
  817. if(MatchFunction(szBuffer))
  818. {
  819. // Insert into function list
  820. CFunction* psf = new CFunction(szBuffer, iHint, iOrdinal,
  821. pitda ? pitda->u1.Function : static_cast<DWORD>(-1),
  822. false);
  823. pcm->InsertFunctionSorted(psf, &pcm->m_pFunctions);
  824. }
  825. }
  826. else
  827. {
  828. // Insert an ordinal import into the module.
  829. iOrdinal = static_cast<int>(IMAGE_ORDINAL(pitdf->u1.Ordinal));
  830. pcm->InsertOrdinal(iOrdinal);
  831. }
  832. // Move to next function
  833. pitdf++;
  834. if(pitda)
  835. pitda++;
  836. }
  837. // Move to next module
  838. piid++;
  839. }
  840. return true;
  841. }
  842. // Parse all import tables
  843. bool CModule::ParseImportTables()
  844. {
  845. return (WalkImportTable()
  846. && WalkDelayImportTable());
  847. }
  848. // Load a module into memory, and parse it.
  849. bool CModule::ParseModule(HANDLE hEvent)
  850. {
  851. // Cancel parsing if user canceled
  852. if(hEvent && WaitForSingleObject(hEvent, 0)==WAIT_OBJECT_0)
  853. return false;
  854. bool fSucceeded = false;
  855. HANDLE hFile = INVALID_HANDLE_VALUE;
  856. HANDLE hMap = 0;
  857. bool fPushed = false;
  858. m_pvImageBase = 0;
  859. // Wrap in a __try block, because an invalid executable image
  860. // may have bad pointers in our memory mapped region.
  861. __try
  862. {
  863. // Open the file
  864. char szFileName[1024];
  865. char* szJunk;
  866. if(!SearchPath(0, m_szName, 0, 1024, szFileName, &szJunk))
  867. {
  868. m_szError = "Unable to find file";
  869. __leave;
  870. }
  871. hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ,
  872. 0, OPEN_EXISTING, 0, 0);
  873. if(hFile == INVALID_HANDLE_VALUE)
  874. {
  875. m_szError = "Unable to open file";
  876. __leave;
  877. }
  878. GetFileVerInfo(hFile, szFileName);
  879. // Map the file into memory
  880. hMap = CreateFileMapping(hFile, 0, PAGE_READONLY, 0, 0, 0);
  881. if(hMap == 0)
  882. {
  883. m_szError = "Unable to map file";
  884. __leave;
  885. }
  886. m_pvImageBase = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
  887. if(m_pvImageBase == 0)
  888. {
  889. m_szError = "Unable to map file";
  890. __leave;
  891. }
  892. // Get header information and verify this is a valid executable
  893. // Get the MS-DOS compatible header
  894. PIMAGE_DOS_HEADER pidh = reinterpret_cast<PIMAGE_DOS_HEADER>(m_pvImageBase);
  895. if(pidh->e_magic != IMAGE_DOS_SIGNATURE)
  896. {
  897. m_szError = "Invalid image, no MS-DOS header";
  898. __leave;
  899. }
  900. // Get the NT header and verify
  901. PIMAGE_NT_HEADERS pinth = reinterpret_cast<PIMAGE_NT_HEADERS>(
  902. reinterpret_cast<DWORD>(m_pvImageBase) + pidh->e_lfanew);
  903. if(pinth->Signature != IMAGE_NT_SIGNATURE)
  904. {
  905. // Not a valid Win32 executable, may be a Win16 or OS/2 exe
  906. m_szError = "Invalid image, no PE signature";
  907. __leave;
  908. }
  909. // Get the other headers
  910. m_pifh = &pinth->FileHeader;
  911. m_pioh = &pinth->OptionalHeader;
  912. m_pish = IMAGE_FIRST_SECTION(pinth);
  913. // Check if anyone is importing
  914. // functions from us, and if so resolve
  915. // function forwarding and ordinals
  916. if(m_pFunctions || m_pOrdinals)
  917. {
  918. if(!ResolveForwardedFunctionsAndOrdinals())
  919. __leave;
  920. }
  921. // Parse import tables (only if not a system DLL or if parsing
  922. // this module may result in a dependency loop)
  923. m_fSystem = IsSystemDLL(m_szName);
  924. if(!m_fSystem && !g_ParseStack.CheckModuleParsed(m_szName))
  925. {
  926. // Add to parse stack
  927. g_ParseStack.PushName(m_szName);
  928. fPushed = true;
  929. // Parse
  930. if(!ParseImportTables())
  931. __leave;
  932. }
  933. // Loop through each DLL imported
  934. CModule* pModule = m_pImportedDLLs;
  935. while(pModule)
  936. {
  937. // Parse each child module
  938. pModule->ParseModule(hEvent);
  939. pModule = pModule->m_pNext;
  940. }
  941. fSucceeded = true;
  942. }
  943. __except(EXCEPTION_EXECUTE_HANDLER)
  944. {
  945. m_szError = "Unable to parse module";
  946. fSucceeded = false;
  947. }
  948. // Cleanup . . .
  949. if(m_pvImageBase)
  950. UnmapViewOfFile(m_pvImageBase);
  951. if(hMap != 0)
  952. CloseHandle(hMap);
  953. if(hFile != INVALID_HANDLE_VALUE)
  954. CloseHandle(hFile);
  955. if(fPushed)
  956. g_ParseStack.Pop();
  957. return fSucceeded;
  958. }
  959. void CModule::GetFileVerInfo(HANDLE hFile, char* szFileName)
  960. {
  961. if(g_fRaw || !g_fVerbose)
  962. return;
  963. // Get file version info
  964. HANDLE hVersionInfo = ReadFileAttributes(szFileName, &m_nAttrCount);
  965. // Get date info
  966. BY_HANDLE_FILE_INFORMATION fileInfo;
  967. GetFileInformationByHandle(hFile, &fileInfo);
  968. FILETIME ftDate;
  969. memcpy(&ftDate, &fileInfo.ftLastWriteTime, sizeof(FILETIME));
  970. CoFileTimeToDosDateTime(&ftDate, &m_wDosDate, &m_wDosTime);
  971. if(m_nAttrCount)
  972. {
  973. m_szAttrValues = new char*[m_nAttrCount];
  974. m_szAttrNames = new char*[m_nAttrCount];
  975. ZeroMemory(m_szAttrValues, sizeof(char*)*m_nAttrCount);
  976. ZeroMemory(m_szAttrNames, sizeof(char*)*m_nAttrCount);
  977. if(hVersionInfo)
  978. for(int i = 0; i < m_nAttrCount; i++)
  979. {
  980. char* szVal = GetAttrValue(hVersionInfo, i);
  981. if(szVal)
  982. {
  983. m_szAttrValues[i] = new char[strlen(szVal)+1];
  984. strcpy(m_szAttrValues[i], szVal);
  985. char* szAttrName = GetAttrNameXML(i);
  986. if(szAttrName)
  987. {
  988. m_szAttrNames[i] = new char[strlen(szAttrName)+1];
  989. strcpy(m_szAttrNames[i], szAttrName);
  990. }
  991. }
  992. }
  993. }
  994. if(hVersionInfo)
  995. CleanupFileManager(hVersionInfo);
  996. }
  997. // Return true if module is a system DLL, false otherwise
  998. // We use the system file protection system, and assume all system
  999. // files are protected.
  1000. bool IsSystemDLL(const char* szFileName)
  1001. {
  1002. char szBuffer[1024], *szJunk;
  1003. if(!SearchPath(0, szFileName, 0, 1024, szBuffer, &szJunk))
  1004. return false;
  1005. // Only check DLL's
  1006. if(!StrStrI(szFileName, ".dll"))
  1007. return false;
  1008. wchar_t* wszFileName = new wchar_t[strlen(szBuffer) + 1];
  1009. MultiByteToWideChar(CP_ACP, 0, szBuffer, strlen(szBuffer)+1,
  1010. wszFileName, strlen(szBuffer)+1);
  1011. bool fRet = (SfcIsFileProtected(0, wszFileName) != FALSE);
  1012. delete wszFileName;
  1013. return fRet;
  1014. }
  1015. // LinkName2Name()
  1016. // Resolve name mangling
  1017. void LinkName2Name(char* szLinkName, char* szName)
  1018. {
  1019. /*
  1020. * the link name is expected like ?Function@Class@@Params
  1021. * to be converted to Class::Function
  1022. */
  1023. static CHAR arrOperators[][8] =
  1024. {
  1025. "",
  1026. "",
  1027. "new",
  1028. "delete",
  1029. "=",
  1030. ">>",
  1031. "<<",
  1032. "!",
  1033. "==",
  1034. "!="
  1035. };
  1036. DWORD dwCrr = 0;
  1037. DWORD dwCrrFunction = 0;
  1038. DWORD dwCrrClass = 0;
  1039. DWORD dwSize;
  1040. BOOL fIsCpp = FALSE;
  1041. BOOL fHasClass = FALSE;
  1042. BOOL fIsContructor = FALSE;
  1043. BOOL fIsDestructor = FALSE;
  1044. BOOL fIsOperator = FALSE;
  1045. DWORD dwOperatorIndex = 0;
  1046. bool fIsStdcall = false, fIsFastcall = false;
  1047. char szFunction[1024];
  1048. char szClass[1024];
  1049. // Unmangle stdcall and fastcall names
  1050. char* szAtSymbol = strrchr(szLinkName, '@');
  1051. fIsFastcall = (szLinkName[0] == '@') && szAtSymbol && isdigit(szAtSymbol[1]);
  1052. fIsStdcall = (szLinkName[0] == '_') && szAtSymbol && isdigit(szAtSymbol[1]);
  1053. if(fIsFastcall || fIsStdcall)
  1054. {
  1055. szLinkName++;
  1056. // Modifying the link name, so make a copy.
  1057. // The file is mapped as read-only, and if it
  1058. // were read/write, changes would be made to the
  1059. // executable.
  1060. char* szTemp = new char[strlen(szLinkName)+1];
  1061. strcpy(szTemp, szLinkName);
  1062. szLinkName = szTemp;
  1063. *(strchr(szLinkName, '@'))= '\0';
  1064. // ?????
  1065. // I think we need to keep going, because it is possible
  1066. // to have C++ name mangling on a stdcall name.
  1067. }
  1068. if (*szLinkName == '@')
  1069. szLinkName++;
  1070. dwSize = lstrlen(szLinkName);
  1071. /*
  1072. * skip '?'
  1073. */
  1074. while (dwCrr < dwSize) {
  1075. if (szLinkName[dwCrr] == '?') {
  1076. dwCrr++;
  1077. fIsCpp = TRUE;
  1078. }
  1079. break;
  1080. }
  1081. /*
  1082. * check to see if this is a special function (like ??0)
  1083. */
  1084. if (fIsCpp) {
  1085. if (szLinkName[dwCrr] == '?') {
  1086. dwCrr++;
  1087. /*
  1088. * the next digit should tell as the function type
  1089. */
  1090. if (isdigit(szLinkName[dwCrr])) {
  1091. switch (szLinkName[dwCrr]) {
  1092. case '0':
  1093. fIsContructor = TRUE;
  1094. break;
  1095. case '1':
  1096. fIsDestructor = TRUE;
  1097. break;
  1098. default:
  1099. fIsOperator = TRUE;
  1100. dwOperatorIndex = szLinkName[dwCrr] - '0';
  1101. break;
  1102. }
  1103. dwCrr++;
  1104. }
  1105. }
  1106. }
  1107. /*
  1108. * get the function name
  1109. */
  1110. while (dwCrr < dwSize) {
  1111. if (szLinkName[dwCrr] != '@') {
  1112. szFunction[dwCrrFunction] = szLinkName[dwCrr];
  1113. dwCrrFunction++;
  1114. dwCrr++;
  1115. } else {
  1116. break;
  1117. }
  1118. }
  1119. szFunction[dwCrrFunction] = '\0';
  1120. if (fIsCpp) {
  1121. /*
  1122. * skip '@'
  1123. */
  1124. if (dwCrr < dwSize) {
  1125. if (szLinkName[dwCrr] == '@') {
  1126. dwCrr++;
  1127. }
  1128. }
  1129. /*
  1130. * get the class name (if any)
  1131. */
  1132. while (dwCrr < dwSize) {
  1133. if (szLinkName[dwCrr] != '@') {
  1134. fHasClass = TRUE;
  1135. szClass[dwCrrClass] = szLinkName[dwCrr];
  1136. dwCrrClass++;
  1137. dwCrr++;
  1138. } else {
  1139. break;
  1140. }
  1141. }
  1142. szClass[dwCrrClass] = '\0';
  1143. }
  1144. /*
  1145. * print the new name
  1146. */
  1147. if (fIsContructor) {
  1148. sprintf(szName, "%s::%s", szFunction, szFunction);
  1149. } else if (fIsDestructor) {
  1150. sprintf(szName, "%s::~%s", szFunction, szFunction);
  1151. } else if (fIsOperator) {
  1152. sprintf(szName, "%s::operator %s", szFunction, arrOperators[dwOperatorIndex]);
  1153. } else if (fHasClass) {
  1154. sprintf(szName, "%s::%s", szClass, szFunction);
  1155. } else {
  1156. sprintf(szName, "%s", szFunction);
  1157. }
  1158. // stdcall and fastcall unmangling do a slight modification to
  1159. // the link name, we need to free it here.
  1160. if(fIsStdcall || fIsFastcall)
  1161. delete szLinkName;
  1162. }
  1163. // Parse a top level module
  1164. void ParseHighLevelModule(char* szName, HANDLE hEvent)
  1165. {
  1166. // Create a new module
  1167. CModule* pModule = new CModule(szName);
  1168. assert(g_ParseStack.IsEmpty());
  1169. g_ParseStack.ClearParseHistory();
  1170. pModule->ParseModule(hEvent);
  1171. // Add to global module list
  1172. g_modules.InsertModuleSorted(pModule);
  1173. }
  1174. // Functions to print to console or XML file
  1175. // Just do indentation, save repetitious code
  1176. void Indent(int iLevel, FILE* pFile)
  1177. {
  1178. for(int i = 0; i < iLevel; i++)
  1179. fprintf(pFile, "\t");
  1180. }
  1181. // Write function header info for raw output
  1182. void CFunction::WriteHeader(int iIndentLevel, FILE* pFile)
  1183. {
  1184. if(g_fVerbose && g_fRaw)
  1185. {
  1186. Indent(iIndentLevel, pFile);
  1187. fprintf(pFile, "%-40s%-10s%-6s%-8s%-40s%-6s\n", "Name", "Address", "Hint",
  1188. "Ordinal", "Forwarded to", "Delayed");
  1189. }
  1190. }
  1191. // Write a function, raw or XML
  1192. void CFunction::WriteFunction(int iIndentLevel, FILE* pFile)
  1193. {
  1194. Indent(iIndentLevel, pFile);
  1195. if(!g_fRaw)
  1196. {
  1197. if(g_fVerbose)
  1198. {
  1199. fprintf(pFile, "<FUNCTION NAME=\"");
  1200. WriteXMLOKString(m_szName, pFile);
  1201. fprintf(pFile, "\" ", m_szName);
  1202. if(m_dwAddress != static_cast<DWORD>(-1))
  1203. fprintf(pFile, "ADDRESS=\"0x%x\" ", m_dwAddress);
  1204. if(m_iHint != -1)
  1205. fprintf(pFile, "HINT=\"%d\" ", m_iHint);
  1206. if(m_iOrdinal != -1)
  1207. fprintf(pFile, "ORDINAL=\"%d\" ", m_iOrdinal);
  1208. if(m_szForwardName != 0)
  1209. {
  1210. fprintf(pFile, "FORWARD_TO=\"");
  1211. WriteXMLOKString(m_szForwardName, pFile);
  1212. fprintf(pFile, "\" ");
  1213. }
  1214. fprintf(pFile, "DELAYED=\"%s\"/>\n", m_fDelayed ? "true" : "false");
  1215. }
  1216. else
  1217. {
  1218. fprintf(pFile, "<FUNCTION NAME=\"");
  1219. WriteXMLOKString(m_szName, pFile);
  1220. fprintf(pFile, "\"/>\n");
  1221. }
  1222. }
  1223. else
  1224. {
  1225. if(g_fVerbose)
  1226. {
  1227. char szAddress[16] = "N/A";
  1228. if(m_dwAddress != static_cast<DWORD>(-1))
  1229. sprintf(szAddress, "0x%x", m_dwAddress);
  1230. char szOrdinal[16] = "N/A";
  1231. if(m_iOrdinal != -1)
  1232. sprintf(szOrdinal, "0x%x", m_iOrdinal);
  1233. char szHint[16] = "N/A";
  1234. if(m_iHint != -1)
  1235. sprintf(szHint, "%d", m_iHint);
  1236. fprintf(pFile, "%-40s%-10s%-6s%-8s%-40s%-6s\n", m_szName, szAddress,
  1237. szHint, szOrdinal, m_szForwardName ? m_szForwardName : "N/A",
  1238. m_fDelayed ? "true" : "false");
  1239. }
  1240. else
  1241. {
  1242. fprintf(pFile, "%s\n", m_szName);
  1243. }
  1244. }
  1245. }
  1246. // Write an XML-compliant string (no <'s and >'s, replace with &gt, &lt, etc.)
  1247. void WriteXMLOKString(char* szString, FILE* pFile)
  1248. {
  1249. const int c_nChars = 5;
  1250. char acIllegal[] = {'<','>', '&', '\'', '\"'};
  1251. char* szEntities[] = {"&lt;", "&gt;", "&amp;", "&apos;", "&quot;"};
  1252. while(*szString)
  1253. {
  1254. int i;
  1255. for(i = 0; i < c_nChars; i++)
  1256. {
  1257. if(*szString == acIllegal[i])
  1258. {
  1259. fprintf(pFile, szEntities[i]);
  1260. break;
  1261. }
  1262. }
  1263. if(i == c_nChars)
  1264. fputc(*szString, pFile);
  1265. szString++;
  1266. }
  1267. }
  1268. // Write an entire module as output, either raw or XML.
  1269. void CModule::WriteModule(bool fTopLevel, int iIndentLevel, FILE* pFile)
  1270. {
  1271. if(Empty() && m_szError == 0)
  1272. return;
  1273. Indent(iIndentLevel, pFile);
  1274. if(!g_fRaw)
  1275. {
  1276. if(fTopLevel)
  1277. fprintf(pFile, "<EXE NAME=\"");
  1278. else
  1279. fprintf(pFile, "<DLL NAME=\"");
  1280. WriteXMLOKString(m_szFullName, pFile);
  1281. fprintf(pFile,"\">\n");
  1282. }
  1283. else
  1284. {
  1285. fprintf(pFile, "%s:\n", m_szFullName);
  1286. }
  1287. if(!g_fRaw && g_fVerbose && (m_nAttrCount || m_wDosDate))
  1288. {
  1289. Indent(iIndentLevel + 1, pFile);
  1290. fprintf(pFile, "<INFO>\n");
  1291. // Print out date information
  1292. Indent(iIndentLevel + 1, pFile);
  1293. fprintf(pFile, "<DATE>%d/%d/%d</DATE>\n", (m_wDosDate & 0x1E0) >> 5,
  1294. m_wDosDate & 0x1F, ((m_wDosDate & 0xFE00) >> 9) + 1980);
  1295. for(int i = 0; i < m_nAttrCount; i++)
  1296. {
  1297. if(m_szAttrValues[i])
  1298. {
  1299. if(m_szAttrNames[i])
  1300. {
  1301. if(strlen(m_szAttrNames[i]) != 0)
  1302. {
  1303. Indent(iIndentLevel+1, pFile);
  1304. fprintf(pFile, "<");
  1305. WriteXMLOKString(m_szAttrNames[i], pFile);
  1306. fprintf(pFile,">");
  1307. WriteXMLOKString(m_szAttrValues[i], pFile);
  1308. fprintf(pFile,"</");
  1309. WriteXMLOKString(m_szAttrNames[i], pFile);
  1310. fprintf(pFile, ">\n");
  1311. }
  1312. }
  1313. }
  1314. }
  1315. Indent(iIndentLevel + 1, pFile);
  1316. fprintf(pFile, "</INFO>\n");
  1317. }
  1318. // If an error occured in parsing
  1319. if(m_szError)
  1320. {
  1321. Indent(iIndentLevel+1, pFile);
  1322. if(!g_fRaw)
  1323. {
  1324. fprintf(pFile, "<ERROR TYPE=\"");
  1325. WriteXMLOKString(m_szError, pFile);
  1326. fprintf(pFile,"\"/>\n");
  1327. }
  1328. else
  1329. fprintf(pFile, "Parse Error: %s\n", m_szError);
  1330. }
  1331. if(g_fVerbose)
  1332. {
  1333. Indent(iIndentLevel+1, pFile);
  1334. if(m_fSystem)
  1335. {
  1336. if(!g_fRaw)
  1337. fprintf(pFile, "<SYSTEMMODULE VALUE=\"1\"/>\n");
  1338. else
  1339. fprintf(pFile, "(System Module)\n");
  1340. }
  1341. else
  1342. {
  1343. if(!g_fRaw)
  1344. fprintf(pFile, "<SYSTEMMODULE VALUE =\"0\"/>\n");
  1345. else
  1346. fprintf(pFile, "(Private Module)\n");
  1347. }
  1348. }
  1349. // Print all functions imported from this module
  1350. if(g_fAPILogging && fTopLevel)
  1351. {
  1352. CFunction* pAllFunctions = 0;
  1353. GetAllFunctions(&pAllFunctions);
  1354. if(pAllFunctions)
  1355. pAllFunctions->WriteHeader(iIndentLevel+1, pFile);
  1356. while(pAllFunctions)
  1357. {
  1358. CFunction* pOld;
  1359. pAllFunctions->WriteFunction(iIndentLevel+1, pFile);
  1360. pOld = pAllFunctions;
  1361. pAllFunctions = pAllFunctions->Next();
  1362. delete pOld;
  1363. }
  1364. }
  1365. else
  1366. {
  1367. CFunction* pFunc = m_pFunctions;
  1368. if(pFunc)
  1369. pFunc->WriteHeader(iIndentLevel, pFile);
  1370. while(pFunc)
  1371. {
  1372. pFunc->WriteFunction(iIndentLevel, pFile);
  1373. pFunc = pFunc->Next();
  1374. }
  1375. CModule* pMod = m_pImportedDLLs;
  1376. while(pMod)
  1377. {
  1378. pMod->WriteModule(false, iIndentLevel + 1, pFile);
  1379. pMod = pMod->m_pNext;
  1380. }
  1381. }
  1382. Indent(iIndentLevel, pFile);
  1383. if(!g_fRaw)
  1384. {
  1385. if(fTopLevel)
  1386. fprintf(pFile, "</EXE>\n");
  1387. else
  1388. fprintf(pFile, "</DLL>\n");
  1389. }
  1390. fprintf(pFile, "\n");
  1391. // Child modules no longer needed, delete
  1392. CModule* pMod = m_pImportedDLLs;
  1393. while(pMod)
  1394. {
  1395. CModule* pNext = pMod->m_pNext;
  1396. delete pMod;
  1397. pMod = pNext;
  1398. }
  1399. m_pImportedDLLs = 0;
  1400. }
  1401. // Write out the XML header
  1402. void WriteXMLHeader(FILE* pFile)
  1403. {
  1404. if(g_fRaw)
  1405. return;
  1406. static char* szMonths[] =
  1407. {"",
  1408. "January",
  1409. "February",
  1410. "March",
  1411. "April",
  1412. "May",
  1413. "June",
  1414. "July",
  1415. "August",
  1416. "September",
  1417. "October",
  1418. "November",
  1419. "December"};
  1420. static char* szDays[] =
  1421. {"Sunday",
  1422. "Monday",
  1423. "Tuesday",
  1424. "Wednesday",
  1425. "Thursday",
  1426. "Friday",
  1427. "Saturday"};
  1428. SYSTEMTIME st;
  1429. GetLocalTime(&st);
  1430. fprintf(pFile, "<?xml version = \"1.0\"?>\n");
  1431. fprintf(pFile, "<!--\n");
  1432. fprintf(pFile, "\tAppParse Datafile\n");
  1433. fprintf(pFile, "\tGenerated: %s, %s %d, %d %2d:%2d:%2d\n",
  1434. szDays[st.wDayOfWeek], szMonths[st.wMonth], st.wDay, st.wYear,
  1435. st.wHour, st.wMinute, st.wSecond);
  1436. fprintf(pFile, "-->\n\n");
  1437. }
  1438. // Return true if function name matches search string, false otherwise.
  1439. bool MatchFunction(const char* szFunc)
  1440. {
  1441. if(strcmp(g_szSearch, "*") == 0)
  1442. return true;
  1443. char* szSearch = g_szSearch;
  1444. while(*szSearch != '\0' && *szFunc != '\0')
  1445. {
  1446. // If we get a ?, we don't care and move on to the next
  1447. // character.
  1448. if(*szSearch == '?')
  1449. {
  1450. szSearch++;
  1451. szFunc++;
  1452. continue;
  1453. }
  1454. // If we have a wildcard, move to next search string and search for substring
  1455. if(*szSearch == '*')
  1456. {
  1457. char* szCurrSearch;
  1458. szSearch++;
  1459. if(*szSearch == '\0')
  1460. return true;
  1461. // Don't change starting point.
  1462. szCurrSearch = szSearch;
  1463. for(;;)
  1464. {
  1465. // We're done if we hit another wildcard
  1466. if(*szCurrSearch == '*' ||
  1467. *szCurrSearch == '?')
  1468. {
  1469. // Update the permanent search position.
  1470. szSearch = szCurrSearch;
  1471. break;
  1472. }
  1473. // At end of both strings, return true.
  1474. if((*szCurrSearch == '\0') && (*szFunc == '\0'))
  1475. return true;
  1476. // We never found it
  1477. if(*szFunc == '\0')
  1478. return false;
  1479. // If it doesn't match, start over
  1480. if(toupper(*szFunc) != toupper(*szCurrSearch))
  1481. {
  1482. // If mismatch on first character
  1483. // of search string, move to next
  1484. // character in function string.
  1485. if(szCurrSearch == szSearch)
  1486. szFunc++;
  1487. else
  1488. szCurrSearch = szSearch;
  1489. }
  1490. else
  1491. {
  1492. szFunc++;
  1493. szCurrSearch++;
  1494. }
  1495. }
  1496. }
  1497. else
  1498. {
  1499. if(toupper(*szFunc) != toupper(*szSearch))
  1500. {
  1501. return false;
  1502. }
  1503. szFunc++;
  1504. szSearch++;
  1505. }
  1506. }
  1507. if((*szFunc == 0) && ((*szSearch == '\0') || (strcmp(szSearch,"*")==0)))
  1508. return true;
  1509. else
  1510. return false;
  1511. }
  1512. // Profile an entire directory
  1513. void ProfileDirectory(char* szDirectory, HANDLE hEvent)
  1514. {
  1515. if(!SetCurrentDirectory(szDirectory))
  1516. return;
  1517. WIN32_FIND_DATA ffd;
  1518. // Find and parse all EXE's.
  1519. HANDLE hSearch = FindFirstFile("*.exe", &ffd);
  1520. if(hSearch != INVALID_HANDLE_VALUE)
  1521. {
  1522. do
  1523. {
  1524. ParseHighLevelModule(ffd.cFileName, hEvent);
  1525. // Terminate parsing if user canceled
  1526. if(hEvent && WaitForSingleObject(hEvent, 0)==WAIT_OBJECT_0)
  1527. {
  1528. FindClose(hSearch);
  1529. SetCurrentDirectory("..");
  1530. return;
  1531. }
  1532. }
  1533. while(FindNextFile(hSearch, &ffd));
  1534. FindClose(hSearch);
  1535. }
  1536. // See if we should go deeper into directories.
  1537. if(g_fRecurse)
  1538. {
  1539. hSearch = FindFirstFile("*", &ffd);
  1540. if(hSearch == INVALID_HANDLE_VALUE)
  1541. {
  1542. SetCurrentDirectory("..");
  1543. return;
  1544. }
  1545. do
  1546. {
  1547. if(GetFileAttributes(ffd.cFileName) & FILE_ATTRIBUTE_DIRECTORY)
  1548. {
  1549. // Don't do an infinite recursion.
  1550. if(ffd.cFileName[0] != '.')
  1551. {
  1552. int nCurrLength = strlen(g_szCurrentPath);
  1553. strcat(g_szCurrentPath, ffd.cFileName);
  1554. strcat(g_szCurrentPath, "\\");
  1555. ProfileDirectory(ffd.cFileName, hEvent);
  1556. g_szCurrentPath[nCurrLength] = '\0';
  1557. }
  1558. // Terminate search if user signaled
  1559. if(hEvent && WaitForSingleObject(hEvent, 0)==WAIT_OBJECT_0)
  1560. {
  1561. FindClose(hSearch);
  1562. SetCurrentDirectory("..");
  1563. return;
  1564. }
  1565. }
  1566. } while(FindNextFile(hSearch, &ffd));
  1567. }
  1568. FindClose(hSearch);
  1569. SetCurrentDirectory("..");
  1570. }
  1571. void* __cdecl operator new(size_t size)
  1572. {
  1573. void* pv = 0;
  1574. if(!g_hHeap)
  1575. pv = HeapAlloc(GetProcessHeap(), 0, size);
  1576. else
  1577. pv = HeapAlloc(g_hHeap, 0, size);
  1578. if(!pv)
  1579. {
  1580. MessageBox(0, TEXT("Out of memory, terminating."), TEXT("ERROR"),
  1581. MB_OK | MB_ICONERROR);
  1582. exit(-1);
  1583. }
  1584. return pv;
  1585. }
  1586. void __cdecl operator delete(void* pVal)
  1587. {
  1588. if(g_hHeap)
  1589. HeapFree(g_hHeap, 0, pVal);
  1590. else
  1591. HeapFree(GetProcessHeap(), 0, pVal);
  1592. }
  1593. DWORD __stdcall AppParse(char* szAppName, FILE* pFile, bool fRaw,
  1594. bool fAPILogging, bool fRecurse, bool fVerbose, char* szSearchKey,
  1595. int iPtolemyID, HANDLE hEvent)
  1596. {
  1597. g_fRaw = fRaw;
  1598. g_fAPILogging = fAPILogging;
  1599. g_fVerbose = fVerbose;
  1600. g_szSearch = szSearchKey;
  1601. g_fRecurse = fRecurse;
  1602. bool fProfileDirectory = false;
  1603. // Check if it is a directory, or a regular file.
  1604. DWORD dwAttributes = GetFileAttributes(szAppName);
  1605. if(dwAttributes != static_cast<DWORD>(-1) &&
  1606. (dwAttributes & FILE_ATTRIBUTE_DIRECTORY))
  1607. fProfileDirectory = true;
  1608. // Check for directory profiling
  1609. if(fProfileDirectory)
  1610. {
  1611. // Search for all EXE's in this Directory
  1612. // Remove trailing \, if present
  1613. if(szAppName[strlen(szAppName)-1]== '\\')
  1614. szAppName[strlen(szAppName)-1] = '\0';
  1615. char szBuff[MAX_PATH];
  1616. strcpy(szBuff, szAppName);
  1617. // If we're profiling a drive, don't include
  1618. // the drive letter in the path
  1619. if(szBuff[strlen(szBuff)-1]==':')
  1620. {
  1621. *g_szCurrentPath='\0';
  1622. }
  1623. else
  1624. {
  1625. if(strrchr(szBuff, '\\'))
  1626. strcpy(g_szCurrentPath, strrchr(szBuff, '\\')+1);
  1627. else
  1628. strcpy(g_szCurrentPath, szBuff);
  1629. strcat(g_szCurrentPath, "\\");
  1630. }
  1631. ProfileDirectory(szAppName, hEvent);
  1632. }
  1633. else
  1634. {
  1635. // Maybe they left off the .exe
  1636. if(GetFileAttributes(szAppName) == static_cast<DWORD>(-1))
  1637. {
  1638. char szBuffer[MAX_PATH+1];
  1639. strcpy(szBuffer, szAppName);
  1640. strcat(szBuffer, ".exe");
  1641. dwAttributes = GetFileAttributes(szBuffer);
  1642. if(dwAttributes == static_cast<DWORD>(-1))
  1643. {
  1644. return ERROR_FILE_NOT_FOUND;
  1645. }
  1646. szAppName = szBuffer;
  1647. }
  1648. // Get the directory name
  1649. char szBuffer[MAX_PATH+1];
  1650. strcpy(szBuffer, szAppName);
  1651. char* p;
  1652. for(p = &szBuffer[strlen(szBuffer)]; p != szBuffer; p--)
  1653. {
  1654. if(*p == '\\')
  1655. {
  1656. *p = '\0';
  1657. break;
  1658. }
  1659. }
  1660. if(p != szBuffer)
  1661. {
  1662. SetCurrentDirectory(szBuffer);
  1663. szAppName = p+1;
  1664. }
  1665. ParseHighLevelModule(szAppName, hEvent);
  1666. }
  1667. char* szProjectName = "";
  1668. if(fProfileDirectory)
  1669. {
  1670. // If a directory, get the volume name
  1671. if(strrchr(szAppName, '\\'))
  1672. szAppName = strrchr(szAppName, '\\') + 1;
  1673. // If we're profiling a drive, get volume name
  1674. if(szAppName[strlen(szAppName)-1]==':')
  1675. {
  1676. char szBuffer[MAX_PATH];
  1677. if(GetVolumeInformation(szAppName, szBuffer, MAX_PATH, 0, 0,
  1678. 0, 0, 0))
  1679. szProjectName = szBuffer;
  1680. else
  1681. szProjectName = szAppName;
  1682. }
  1683. else
  1684. szProjectName = szAppName;
  1685. }
  1686. else
  1687. {
  1688. szProjectName = szAppName;
  1689. char* szExtension = strstr(szAppName, ".exe");
  1690. if(szExtension)
  1691. *szExtension = '\0';
  1692. }
  1693. // Only write if there wasn't an event object, or user canceled.
  1694. if(!hEvent || WaitForSingleObject(hEvent, 0) != WAIT_OBJECT_0)
  1695. {
  1696. // Write all output
  1697. WriteXMLHeader(pFile);
  1698. g_modules.Write(pFile, szProjectName, iPtolemyID);
  1699. }
  1700. g_modules.Clear();
  1701. return ERROR_SUCCESS;
  1702. }