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.

1018 lines
22 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. dc.cpp
  5. Abstract:
  6. Command line utility for dumping importing information from DLL's
  7. and executables.
  8. Command Line Options:
  9. /v Verbose mode
  10. /s:Func Only display functions matching search string "Func"
  11. /o:File Send output to File
  12. /f Sort by function, not by module.
  13. History:
  14. 05/10/2000 t-michkr Created
  15. --*/
  16. #include <windows.h>
  17. #include <assert.h>
  18. #include <string.h>
  19. #include <stdlib.h>
  20. #include <stdio.h>
  21. #include <ctype.h>
  22. // For some reason, this is needed to compile in
  23. // the sdktools tree.
  24. #define strdup _strdup
  25. #define stricmp _stricmp
  26. #define strnicmp _strnicmp
  27. // Structure containing all info relevent to an imported function.
  28. struct SFunction
  29. {
  30. // The name this function is imported by.
  31. char* m_szName;
  32. // Ordinal number, Win3.1 compatibility.
  33. int m_iOrdinal;
  34. // Lookup index into a DLL's export table
  35. // for quick patching.
  36. int m_iHint;
  37. // Starting address of the function.
  38. DWORD m_dwAddress;
  39. // Whether or not it is a delayed import.
  40. bool m_fDelayedImport;
  41. // Forwarded function name
  42. char* m_szForward;
  43. // Link to next function
  44. SFunction* m_pNext;
  45. SFunction()
  46. {
  47. m_szName = m_szForward = 0;
  48. m_iOrdinal = m_iHint = -1;
  49. m_dwAddress = static_cast<DWORD>(-1);
  50. m_fDelayedImport = false;
  51. m_pNext = 0;
  52. }
  53. };
  54. // A module used by the executable (ie, DLL's)
  55. struct SModule
  56. {
  57. // The name of this module
  58. char* m_szName;
  59. // All functions imported from this module
  60. SFunction* m_pFunctions;
  61. // Link to next module
  62. SModule* m_pNext;
  63. SModule()
  64. {
  65. m_szName = 0;
  66. m_pFunctions = 0;
  67. m_pNext = 0;
  68. }
  69. };
  70. // All modules imported by the executable.
  71. SModule* g_pModules = 0;
  72. void InsertFunctionSorted(SModule* pMod, SFunction* pFunc)
  73. {
  74. // Special case, insert at front
  75. if(pMod->m_pFunctions == 0
  76. || stricmp(pMod->m_pFunctions->m_szName, pFunc->m_szName) > 0)
  77. {
  78. pFunc->m_pNext = pMod->m_pFunctions;
  79. pMod->m_pFunctions = pFunc;
  80. return;
  81. }
  82. SFunction* pfPrev = pMod->m_pFunctions;
  83. SFunction* pfTemp = pMod->m_pFunctions->m_pNext;
  84. while(pfTemp)
  85. {
  86. if(stricmp(pfTemp->m_szName, pFunc->m_szName) > 0)
  87. {
  88. pFunc->m_pNext = pfTemp;
  89. pfPrev->m_pNext = pFunc;
  90. return;
  91. }
  92. pfPrev = pfTemp;
  93. pfTemp = pfTemp->m_pNext;
  94. }
  95. // Insert at end.
  96. pFunc->m_pNext = 0;
  97. pfPrev->m_pNext = pFunc;
  98. }
  99. void InsertModuleSorted(SModule* pMod)
  100. {
  101. // Special case, insert at front
  102. if(g_pModules == 0
  103. || stricmp(g_pModules->m_szName, pMod->m_szName) > 0)
  104. {
  105. pMod->m_pNext = g_pModules;
  106. g_pModules = pMod;
  107. return;
  108. }
  109. SModule* pmPrev = g_pModules;
  110. SModule* pmTemp = g_pModules->m_pNext;
  111. while(pmTemp)
  112. {
  113. if(stricmp(pmTemp->m_szName, pMod->m_szName) > 0)
  114. {
  115. pMod->m_pNext = pmTemp;
  116. pmPrev->m_pNext = pMod;
  117. return;
  118. }
  119. pmPrev = pmTemp;
  120. pmTemp = pmTemp->m_pNext;
  121. }
  122. // Insert at end.
  123. pMod->m_pNext = 0;
  124. pmPrev->m_pNext = pMod;
  125. }
  126. // Print a message about the last error that occurred.
  127. void PrintLastError()
  128. {
  129. // Get the message string
  130. void* pvMsgBuf;
  131. FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
  132. FORMAT_MESSAGE_FROM_SYSTEM |
  133. FORMAT_MESSAGE_IGNORE_INSERTS, 0, GetLastError(),
  134. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  135. reinterpret_cast<PTSTR>(&pvMsgBuf),0, 0);
  136. // Print it.
  137. fprintf(stderr, "%s\n", pvMsgBuf);
  138. // Free the buffer.
  139. LocalFree(pvMsgBuf);
  140. }
  141. /*************************************************************************
  142. * LinkName2Name
  143. *
  144. *************************************************************************/
  145. void
  146. LinkName2Name(
  147. char* szLinkName,
  148. char* szName)
  149. {
  150. /*
  151. * the link name is expected like ?Function@Class@@Params
  152. * to be converted to Class::Function
  153. */
  154. static CHAR arrOperators[][8] =
  155. {
  156. "",
  157. "",
  158. "new",
  159. "delete",
  160. "=",
  161. ">>",
  162. "<<",
  163. "!",
  164. "==",
  165. "!="
  166. };
  167. DWORD dwCrr = 0;
  168. DWORD dwCrrFunction = 0;
  169. DWORD dwCrrClass = 0;
  170. DWORD dwSize;
  171. BOOL fIsCpp = FALSE;
  172. BOOL fHasClass = FALSE;
  173. BOOL fIsContructor = FALSE;
  174. BOOL fIsDestructor = FALSE;
  175. BOOL fIsOperator = FALSE;
  176. DWORD dwOperatorIndex = 0;
  177. char szFunction[1024];
  178. char szClass[1024];
  179. if (*szLinkName == '@')
  180. szLinkName++;
  181. dwSize = lstrlen(szLinkName);
  182. /*
  183. * skip '?'
  184. */
  185. while (dwCrr < dwSize) {
  186. if (szLinkName[dwCrr] == '?') {
  187. dwCrr++;
  188. fIsCpp = TRUE;
  189. }
  190. break;
  191. }
  192. /*
  193. * check to see if this is a special function (like ??0)
  194. */
  195. if (fIsCpp) {
  196. if (szLinkName[dwCrr] == '?') {
  197. dwCrr++;
  198. /*
  199. * the next digit should tell as the function type
  200. */
  201. if (isdigit(szLinkName[dwCrr])) {
  202. switch (szLinkName[dwCrr]) {
  203. case '0':
  204. fIsContructor = TRUE;
  205. break;
  206. case '1':
  207. fIsDestructor = TRUE;
  208. break;
  209. default:
  210. fIsOperator = TRUE;
  211. dwOperatorIndex = szLinkName[dwCrr] - '0';
  212. break;
  213. }
  214. dwCrr++;
  215. }
  216. }
  217. }
  218. /*
  219. * get the function name
  220. */
  221. while (dwCrr < dwSize) {
  222. if (szLinkName[dwCrr] != '@') {
  223. szFunction[dwCrrFunction] = szLinkName[dwCrr];
  224. dwCrrFunction++;
  225. dwCrr++;
  226. } else {
  227. break;
  228. }
  229. }
  230. szFunction[dwCrrFunction] = '\0';
  231. if (fIsCpp) {
  232. /*
  233. * skip '@'
  234. */
  235. if (dwCrr < dwSize) {
  236. if (szLinkName[dwCrr] == '@') {
  237. dwCrr++;
  238. }
  239. }
  240. /*
  241. * get the class name (if any)
  242. */
  243. while (dwCrr < dwSize) {
  244. if (szLinkName[dwCrr] != '@') {
  245. fHasClass = TRUE;
  246. szClass[dwCrrClass] = szLinkName[dwCrr];
  247. dwCrrClass++;
  248. dwCrr++;
  249. } else {
  250. break;
  251. }
  252. }
  253. szClass[dwCrrClass] = '\0';
  254. }
  255. /*
  256. * print the new name
  257. */
  258. if (fIsContructor) {
  259. sprintf(szName, "%s::%s", szFunction, szFunction);
  260. } else if (fIsDestructor) {
  261. sprintf(szName, "%s::~%s", szFunction, szFunction);
  262. } else if (fIsOperator) {
  263. sprintf(szName, "%s::operator %s", szFunction, arrOperators[dwOperatorIndex]);
  264. } else if (fHasClass) {
  265. sprintf(szName, "%s::%s", szClass, szFunction);
  266. } else {
  267. sprintf(szName, "%s", szFunction);
  268. }
  269. }
  270. // Get function forwarding information for
  271. // imported functions.
  272. // This is done by loading the module and sniffing
  273. // its export table.
  274. bool GetForwardFunctions(SModule* pModule)
  275. {
  276. // Open the DLL module.
  277. char szFileName[1024];
  278. char* pstr;
  279. // Search the path and windows directories for it.
  280. if(SearchPath(0, pModule->m_szName, 0, 1024, szFileName, &pstr)==0)
  281. return false;
  282. HANDLE hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ,
  283. 0, OPEN_EXISTING, 0, 0);
  284. if(hFile == INVALID_HANDLE_VALUE)
  285. {
  286. PrintLastError();
  287. return false;
  288. }
  289. HANDLE hMap = CreateFileMapping(hFile, 0, PAGE_READONLY, 0, 0, 0);
  290. if(hMap == 0)
  291. {
  292. PrintLastError();
  293. return false;
  294. }
  295. void* pvFileBase = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
  296. if(!pvFileBase)
  297. {
  298. PrintLastError();
  299. return false;
  300. }
  301. // Get the MS-DOS compatible header
  302. PIMAGE_DOS_HEADER pidh = reinterpret_cast<PIMAGE_DOS_HEADER>(pvFileBase);
  303. if(pidh->e_magic != IMAGE_DOS_SIGNATURE)
  304. {
  305. fprintf(stderr, "File is not a valid executable\n");
  306. return false;
  307. }
  308. // Get the NT header
  309. PIMAGE_NT_HEADERS pinth = reinterpret_cast<PIMAGE_NT_HEADERS>(
  310. reinterpret_cast<DWORD>(pvFileBase) + pidh->e_lfanew);
  311. if(pinth->Signature != IMAGE_NT_SIGNATURE)
  312. {
  313. // Not a valid Win32 executable, may be a Win16 or OS/2 exe
  314. fprintf(stderr, "File is not a valid executable\n");
  315. return false;
  316. }
  317. // Get the other headers
  318. PIMAGE_FILE_HEADER pifh = &pinth->FileHeader;
  319. PIMAGE_OPTIONAL_HEADER pioh = &pinth->OptionalHeader;
  320. PIMAGE_SECTION_HEADER pish = IMAGE_FIRST_SECTION(pinth);
  321. // If no exports, we're done.
  322. if(pioh->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size == 0)
  323. return true;
  324. DWORD dwVAImageDir =
  325. pioh->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
  326. // Locate the section with this image directory.
  327. for(int i = 0; i < pifh->NumberOfSections; i++)
  328. {
  329. if( (dwVAImageDir >= pish[i].VirtualAddress) &&
  330. (dwVAImageDir < (pish[i].VirtualAddress + pish[i].SizeOfRawData)))
  331. {
  332. pish = &pish[i];
  333. break;
  334. }
  335. }
  336. if(i == pifh->NumberOfSections)
  337. {
  338. fprintf(stderr, "Could not locate export directory section\n");
  339. return false;
  340. }
  341. DWORD dwBase = reinterpret_cast<DWORD>(pvFileBase) +
  342. pish->PointerToRawData - pish->VirtualAddress;
  343. PIMAGE_EXPORT_DIRECTORY pied =
  344. reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(dwBase + dwVAImageDir);
  345. DWORD* pdwNames = reinterpret_cast<DWORD*>(dwBase +
  346. pied->AddressOfNames);
  347. WORD* pwOrdinals = reinterpret_cast<WORD*>(dwBase +
  348. pied->AddressOfNameOrdinals);
  349. DWORD* pdwAddresses = reinterpret_cast<DWORD*>(dwBase +
  350. pied->AddressOfFunctions);
  351. for(unsigned hint = 0; hint < pied->NumberOfNames; hint++)
  352. {
  353. char* szFunction = reinterpret_cast<PSTR>(dwBase + pdwNames[hint]);
  354. // Duck out early if this function isn't used in the executable.
  355. SFunction* pFunc = pModule->m_pFunctions;
  356. while(pFunc)
  357. {
  358. if(strcmp(pFunc->m_szName, szFunction)==0)
  359. break;
  360. pFunc = pFunc->m_pNext;
  361. }
  362. if(pFunc == 0)
  363. continue;
  364. int ordinal = pied->Base + static_cast<DWORD>(pwOrdinals[hint]);
  365. DWORD dwAddress = pdwAddresses[ordinal-pied->Base];
  366. // Check if this function has been forwarded to another DLL
  367. if( ((dwAddress) >= dwVAImageDir) &&
  368. ((dwAddress) <
  369. (dwVAImageDir + pioh->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size)))
  370. {
  371. char* szForward = reinterpret_cast<char*>(dwBase + dwAddress);
  372. pFunc->m_szForward = strdup(szForward);
  373. }
  374. }
  375. UnmapViewOfFile(pvFileBase);
  376. CloseHandle(hMap);
  377. CloseHandle(hFile);
  378. return true;
  379. }
  380. // Look through the import directory, and build a list of all imported functions
  381. bool ParseImportDirectory(PIMAGE_FILE_HEADER pifh, PIMAGE_OPTIONAL_HEADER pioh,
  382. PIMAGE_SECTION_HEADER pish, void* pvFileBase,
  383. bool fDelayed)
  384. {
  385. // Get which directory we want (normal imports or delayed imports)
  386. DWORD dwDir = (fDelayed) ?
  387. IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT : IMAGE_DIRECTORY_ENTRY_IMPORT;
  388. // Bail if no imports
  389. if(pioh->DataDirectory[dwDir].Size == 0)
  390. return true;
  391. // Locate the import directory in the image.
  392. PIMAGE_SECTION_HEADER pishImportDirectory = 0;
  393. DWORD dwVAImageDir = pioh->DataDirectory[dwDir].VirtualAddress;
  394. for(int i = 0; i < pifh->NumberOfSections; i++)
  395. {
  396. if((dwVAImageDir >= pish[i].VirtualAddress) &&
  397. dwVAImageDir < (pish[i].VirtualAddress + pish[i].SizeOfRawData))
  398. {
  399. // This is it.
  400. pishImportDirectory = &pish[i];
  401. break;
  402. }
  403. }
  404. if(pishImportDirectory == 0)
  405. {
  406. fprintf(stderr, "Cannot locate %s%s\n",((fDelayed) ? "delayed " : ""),
  407. "import directory section");
  408. return false;
  409. }
  410. // Get the base address for the image.
  411. DWORD dwBase = reinterpret_cast<DWORD>(pvFileBase)
  412. + pishImportDirectory->PointerToRawData
  413. - pishImportDirectory->VirtualAddress;
  414. // Get the import descriptor array
  415. PIMAGE_IMPORT_DESCRIPTOR piid =
  416. reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(dwBase + dwVAImageDir);
  417. // Loop through all imported modules
  418. while(piid->FirstThunk || piid->OriginalFirstThunk)
  419. {
  420. SModule* psm = new SModule;
  421. psm->m_szName = strdup(reinterpret_cast<char*>(dwBase + piid->Name));
  422. // Check if it is already in the list
  423. SModule* pTemp = g_pModules;
  424. while(pTemp)
  425. {
  426. if(strcmp(pTemp->m_szName, psm->m_szName) == 0)
  427. break;
  428. pTemp = pTemp->m_pNext;
  429. }
  430. // If not, insert it
  431. if(pTemp == 0)
  432. {
  433. InsertModuleSorted(psm);
  434. }
  435. else
  436. {
  437. // Otherwise, get rid of it.
  438. pTemp = g_pModules;
  439. while(pTemp)
  440. {
  441. if(strcmp(pTemp->m_szName, psm->m_szName)==0)
  442. break;
  443. pTemp = pTemp->m_pNext;
  444. }
  445. assert(pTemp);
  446. free(psm->m_szName);
  447. delete psm;
  448. psm = pTemp;
  449. }
  450. // Get the function imports from this module.
  451. PIMAGE_THUNK_DATA pitdf = 0;
  452. PIMAGE_THUNK_DATA pitda = 0;
  453. // Check for MS or Borland format
  454. if(piid->OriginalFirstThunk)
  455. {
  456. // MS format, function array is in original first thunk.
  457. pitdf = reinterpret_cast<PIMAGE_THUNK_DATA>(dwBase +
  458. piid->OriginalFirstThunk);
  459. // If the time stamp is set, the module has been bound,
  460. // and first thunk is the bound address array.
  461. if(piid->TimeDateStamp)
  462. {
  463. pitda = reinterpret_cast<PIMAGE_THUNK_DATA>(dwBase +
  464. piid->FirstThunk);
  465. }
  466. }
  467. else
  468. {
  469. // Borland format uses first thunk for function array
  470. pitdf = reinterpret_cast<PIMAGE_THUNK_DATA>(dwBase +
  471. piid->FirstThunk);
  472. }
  473. while(pitdf->u1.Ordinal)
  474. {
  475. SFunction* psf = new SFunction;
  476. if(IMAGE_SNAP_BY_ORDINAL(pitdf->u1.Ordinal))
  477. {
  478. psf->m_iOrdinal = static_cast<int>(IMAGE_ORDINAL(pitdf->u1.Ordinal));
  479. psf->m_iHint = -1;
  480. char szTemp[1024];
  481. sprintf(szTemp, "Unnamed%6d", psf->m_iOrdinal);
  482. psf->m_szName = strdup(szTemp);
  483. }
  484. else
  485. {
  486. PIMAGE_IMPORT_BY_NAME piibn =
  487. reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(dwBase +
  488. (DWORD)(pitdf->u1.AddressOfData));
  489. char* szName = reinterpret_cast<char*>(piibn->Name);
  490. char szBuffer[512];
  491. LinkName2Name(szName, szBuffer);
  492. psf->m_szName = strdup(szBuffer);
  493. psf->m_iOrdinal = -1;
  494. psf->m_iHint = piibn->Hint;
  495. }
  496. psf->m_fDelayedImport = fDelayed;
  497. psf->m_dwAddress = pitda ? (DWORD) pitda->u1.Function :
  498. reinterpret_cast<DWORD>(INVALID_HANDLE_VALUE);
  499. // Do a sorted insert of the function
  500. InsertFunctionSorted(psm, psf);
  501. // Go to next function
  502. pitdf++;
  503. if(pitda)
  504. pitda++;
  505. }
  506. // Go to next entry
  507. piid++;
  508. }
  509. return true;
  510. }
  511. bool GetImports(char* szExecutable)
  512. {
  513. // Open the file
  514. HANDLE hFile = CreateFile(szExecutable, GENERIC_READ, FILE_SHARE_READ, 0,
  515. OPEN_EXISTING, 0, 0);
  516. if(hFile == INVALID_HANDLE_VALUE)
  517. {
  518. PrintLastError();
  519. return false;
  520. }
  521. // Map this file into memory
  522. HANDLE hMap = CreateFileMapping(hFile, 0, PAGE_READONLY, 0, 0, 0);
  523. if(hMap == 0)
  524. {
  525. PrintLastError();
  526. return false;
  527. }
  528. void* pvFileBase;
  529. pvFileBase = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
  530. if(pvFileBase == 0)
  531. {
  532. PrintLastError();
  533. return false;
  534. }
  535. // Get the MS-DOS compatible header
  536. PIMAGE_DOS_HEADER pidh = reinterpret_cast<PIMAGE_DOS_HEADER>(pvFileBase);
  537. if(pidh->e_magic != IMAGE_DOS_SIGNATURE)
  538. {
  539. fprintf(stderr,"File is not a valid executable\n");
  540. return false;
  541. }
  542. // Get the NT header
  543. PIMAGE_NT_HEADERS pinth = reinterpret_cast<PIMAGE_NT_HEADERS>(
  544. reinterpret_cast<DWORD>(pvFileBase) + pidh->e_lfanew);
  545. if(pinth->Signature != IMAGE_NT_SIGNATURE)
  546. {
  547. // Not a valid Win32 executable, may be a Win16 or OS/2 exe
  548. fprintf(stderr, "File is not a valid executable\n");
  549. return false;
  550. }
  551. // Get the other headers
  552. PIMAGE_FILE_HEADER pifh = &pinth->FileHeader;
  553. PIMAGE_OPTIONAL_HEADER pioh = &pinth->OptionalHeader;
  554. PIMAGE_SECTION_HEADER pish = IMAGE_FIRST_SECTION(pinth);
  555. // Get normal imports
  556. if(!ParseImportDirectory(pifh, pioh, pish, pvFileBase, false))
  557. {
  558. return false;
  559. }
  560. // Get delayed imports
  561. if(!ParseImportDirectory(pifh, pioh, pish, pvFileBase, true))
  562. {
  563. return false;
  564. }
  565. // Resolve forwarded functions
  566. SModule* pModule = g_pModules;
  567. while(pModule)
  568. {
  569. GetForwardFunctions(pModule);
  570. pModule = pModule->m_pNext;
  571. }
  572. // We're done with the file.
  573. if(!UnmapViewOfFile(pvFileBase))
  574. {
  575. PrintLastError();
  576. return false;
  577. }
  578. CloseHandle(hMap);
  579. CloseHandle(hFile);
  580. return true;
  581. }
  582. // Return true if function name matches search string, false otherwise.
  583. bool MatchFunction(const char* szFunc, const char* szSearch)
  584. {
  585. if(strcmp(szSearch, "*") == 0)
  586. return true;
  587. while(*szSearch != '\0' && *szFunc != '\0')
  588. {
  589. // If we get a ?, we don't care and move on to the next
  590. // character.
  591. if(*szSearch == '?')
  592. {
  593. szSearch++;
  594. szFunc++;
  595. continue;
  596. }
  597. // If we have a wildcard, move to next search string and search for substring
  598. if(*szSearch == '*')
  599. {
  600. const char* szCurrSearch;
  601. szSearch++;
  602. if(*szSearch == '\0')
  603. return true;
  604. // Don't change starting point.
  605. szCurrSearch = szSearch;
  606. for(;;)
  607. {
  608. // We're done if we hit another wildcard
  609. if(*szCurrSearch == '*' ||
  610. *szCurrSearch == '?')
  611. {
  612. // Update the permanent search position.
  613. szSearch = szCurrSearch;
  614. break;
  615. }
  616. // At end of both strings, return true.
  617. if((*szCurrSearch == '\0') && (*szFunc == '\0'))
  618. return true;
  619. // We never found it
  620. if(*szFunc == '\0')
  621. return false;
  622. // If it doesn't match, start over
  623. if(toupper(*szFunc) != toupper(*szCurrSearch))
  624. {
  625. // If mismatch on first character
  626. // of search string, move to next
  627. // character in function string.
  628. if(szCurrSearch == szSearch)
  629. szFunc++;
  630. else
  631. szCurrSearch = szSearch;
  632. }
  633. else
  634. {
  635. szFunc++;
  636. szCurrSearch++;
  637. }
  638. }
  639. }
  640. else
  641. {
  642. if(toupper(*szFunc) != toupper(*szSearch))
  643. {
  644. return false;
  645. }
  646. szFunc++;
  647. szSearch++;
  648. }
  649. }
  650. if((*szFunc == 0) && ((*szSearch == '\0') || (strcmp(szSearch,"*")==0)))
  651. return true;
  652. else
  653. return false;
  654. }
  655. void PrintModule(SModule* pMod, char* szSearch, bool fVerbose, FILE* pfOut)
  656. {
  657. bool fModNamePrinted = false;
  658. SFunction* pFunc = pMod->m_pFunctions;
  659. while(pFunc)
  660. {
  661. if(!MatchFunction(pFunc->m_szName, szSearch))
  662. {
  663. pFunc = pFunc->m_pNext;
  664. continue;
  665. }
  666. if(!fModNamePrinted)
  667. {
  668. fModNamePrinted = true;
  669. fprintf(pfOut, "\n%s:\n", pMod->m_szName);
  670. if(fVerbose)
  671. fprintf(pfOut, "%-42s%-8s%-5s%-12s%s\n", "Function", "Ordinal",
  672. "Hint", "Address", "Delayed");
  673. }
  674. if(fVerbose)
  675. {
  676. fprintf(pfOut,"%-45s", (pFunc->m_szForward == 0)
  677. ? pFunc->m_szName : pFunc->m_szForward);
  678. if(pFunc->m_iOrdinal==-1)
  679. fprintf(pfOut, "%-5s", "N/A");
  680. else
  681. fprintf(pfOut, "%-5d", pFunc->m_iOrdinal);
  682. if(pFunc->m_iHint == -1)
  683. fprintf(pfOut, "%-5s", "N/A");
  684. else
  685. fprintf(pfOut, "%-5d", pFunc->m_iHint);
  686. if(pFunc->m_dwAddress == static_cast<DWORD>(-1))
  687. fprintf(pfOut, "%-12s", "Not Bound");
  688. else
  689. fprintf(pfOut, "%-#12x", pFunc->m_dwAddress);
  690. fprintf(pfOut,"%s\n", pFunc->m_fDelayedImport ? "Yes" : "No");
  691. }
  692. else
  693. fprintf(pfOut, "%s\n", pFunc->m_szName);
  694. pFunc = pFunc->m_pNext;
  695. }
  696. }
  697. int _cdecl main(int argc, char** argv)
  698. {
  699. if(argc < 2)
  700. {
  701. fprintf(stderr,"Usage: dc executable [/v /s: func /f]\n");
  702. return 0;
  703. }
  704. // Parse command line
  705. char* szFileName = argv[1];
  706. if( (strnicmp(szFileName, "/?", 2) == 0) ||
  707. (strncmp(szFileName, "/h", 2) == 0))
  708. {
  709. printf("Usage: dc executable [/v /s: func /f]\n");
  710. printf("executable:\t\tName of executable file to check\n");
  711. printf("/v:\t\t\tVerbose\n");
  712. printf("/s: func\t\tDisplay all functions matching func search string");
  713. printf(", * and ? allowed.\n");
  714. printf("/f:\t\t\tDisplay alphabetically by function, not module.\n");
  715. printf("/o: File\t\tRedirect all output to File.\n");
  716. return 0;
  717. }
  718. FILE* pfOutput = 0;
  719. // If no extension, just add .exe
  720. if(strchr(szFileName, '.') == 0)
  721. {
  722. szFileName = new char[strlen(argv[1]) + 5];
  723. strcpy(szFileName, argv[1]);
  724. strcat(szFileName, ".exe");
  725. }
  726. bool fVerbose = false;
  727. bool fUseStdout = true;
  728. char* szSearch = "*";
  729. bool fSortByFunction = false;
  730. // Get flags.
  731. for(int i = 2; i < argc; i++)
  732. {
  733. char* szFlag = argv[i];
  734. if(stricmp(szFlag, "/v") == 0)
  735. {
  736. fVerbose = true;
  737. }
  738. else if(strnicmp(szFlag, "/s:", 3) == 0)
  739. {
  740. if((i == argc-1) && (strlen(szFlag) <= 3))
  741. {
  742. fprintf(stderr,"Missing search string\n");
  743. return 0;
  744. }
  745. if(strlen(szFlag) > 3)
  746. {
  747. szSearch = strdup(&szFlag[3]);
  748. }
  749. else
  750. {
  751. szSearch = argv[i+1];
  752. i++;
  753. }
  754. }
  755. else if(stricmp(szFlag, "/f") == 0)
  756. {
  757. fSortByFunction = true;
  758. }
  759. else if( (strnicmp(szFlag, "/h",2) == 0) ||
  760. (strnicmp(szFlag, "/?",2) == 0))
  761. {
  762. printf("Usage: dc executable [/v /s: func /f]\n");
  763. printf("executable:\t\tName of executable file to check\n");
  764. printf("/v:\t\t\tVerbose\n");
  765. printf("/s: func\t\tDisplay all functions matching func search string");
  766. printf(", * and ? allowed.\n");
  767. printf("/f:\t\t\tDisplay alphabetically by function, not module.\n");
  768. printf("/o: File\t\tRedirect all output to File.\n");
  769. return 0;
  770. }
  771. else if(strnicmp(szFlag, "/o:", 3) == 0)
  772. {
  773. fUseStdout = false;
  774. if( (i == argc-1) && (strlen(szFlag) <= 3))
  775. {
  776. fprintf(stderr, "Missing output file name\n");
  777. return 0;
  778. }
  779. if(strlen(szFlag) > 3)
  780. {
  781. pfOutput = fopen(&szFlag[3], "wt");
  782. }
  783. else
  784. {
  785. pfOutput = fopen(argv[i+1], "wt");
  786. i++;
  787. }
  788. }
  789. else
  790. {
  791. fprintf(stderr,"Unknown command line option, %s\n", szFlag);
  792. return 0;
  793. }
  794. }
  795. if(fUseStdout)
  796. pfOutput = stdout;
  797. if(!pfOutput)
  798. {
  799. fprintf(stderr,"Unable to open output file\n");
  800. return 0;
  801. }
  802. // We wrap this code in a try block, because
  803. // we map the file into memory. If the file
  804. // is invalid and if the pointers in it are garbage,
  805. // we should get an access violation, which is caught.
  806. try
  807. {
  808. if(!GetImports(szFileName))
  809. {
  810. return 0;
  811. }
  812. }
  813. catch(...)
  814. {
  815. fprintf(stderr, "Invalid executable file\n");
  816. return 0;
  817. }
  818. if(fSortByFunction)
  819. {
  820. // Create a global list of functions
  821. SModule* pGlobal = new SModule;
  822. pGlobal->m_szName = "All Imported Functions";
  823. SModule* pMod = g_pModules;
  824. while(pMod)
  825. {
  826. SFunction* pFunc = pMod->m_pFunctions;
  827. while(pFunc)
  828. {
  829. // Create a copy of this function
  830. // This is a shallow copy, but
  831. // it should be ok, since we don't
  832. // delete the original
  833. SFunction* pNew = new SFunction;
  834. memcpy(pNew, pFunc, sizeof(*pFunc));
  835. InsertFunctionSorted(pGlobal, pNew);
  836. pFunc = pFunc->m_pNext;
  837. }
  838. pMod = pMod->m_pNext;
  839. }
  840. PrintModule(pGlobal, szSearch, fVerbose, pfOutput);
  841. }
  842. else
  843. {
  844. SModule* pMod = g_pModules;
  845. while(pMod)
  846. {
  847. PrintModule(pMod, szSearch, fVerbose, pfOutput);
  848. pMod = pMod->m_pNext;
  849. }
  850. }
  851. return 0;
  852. }