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.

534 lines
17 KiB

  1. /* dlcheck - verify that a DLL using delay-load calls APIs that have
  2. * stubs in kernel32.dll (aka dload.lib)
  3. *
  4. * HISTORY:
  5. * 25-Nov-98 barrybo Wrote it.
  6. */
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <windows.h>
  10. #include <imagehlp.h>
  11. #include <delayimp.h>
  12. #include <dloaddef.h>
  13. // Function Forward Parameters...
  14. void Usage( void );
  15. int __cdecl main( int, char ** );
  16. int DloadBreakOnFail = FALSE;
  17. extern int DloadDbgPrint = FALSE;
  18. // implemented in kernel32p.lib
  19. FARPROC
  20. WINAPI
  21. DelayLoadFailureHook (
  22. LPCSTR pszDllName,
  23. LPCSTR pszProcName
  24. );
  25. typedef FARPROC (WINAPI *PfnKernel32HookProc)(
  26. LPCSTR pszDllName,
  27. LPCSTR pszProcName
  28. );
  29. PfnKernel32HookProc __pfnFailureProc = DelayLoadFailureHook;
  30. const char rgstrUsage[] = {
  31. "Verify that delayloaded imports all have failure handlers in kernel32.\n"
  32. "usage: dlcheck [switches] image-name\n"
  33. "where: [-?] display this message\n"
  34. " [-l] use the live version of kernel32.dll on the machine\n"
  35. " [-s] use the static dload.lib linked into dlcheck\n"
  36. " [-t] test the static dload.lib linked into dlcheck and exit\n"
  37. " [-i <inifile>] use the information in inifile to check a binary\n"
  38. " [-f] force check the binary (assumes -s)\n"
  39. "\n"
  40. };
  41. HANDLE BaseDllHandle;
  42. PLOADED_IMAGE g_pli;
  43. PIMAGE_SECTION_HEADER g_DelaySection;
  44. char g_szImageName[MAX_PATH];
  45. char g_szDelayLoadHandler[MAX_PATH];
  46. BOOL fForceCheckImage = FALSE;
  47. //
  48. // Convert an absolute pointer that points into the image if the image
  49. // was loaded as a DLL at its preferred base, into a pointer into the
  50. // DLL as it was mapped by imagehlp.
  51. //
  52. void *
  53. ConvertImagePointer(void * p)
  54. {
  55. if (!p) {
  56. return NULL;
  57. } else {
  58. return (void *)((ULONG_PTR)(p) -
  59. (ULONG_PTR)g_pli->FileHeader->OptionalHeader.ImageBase +
  60. (ULONG_PTR)g_pli->MappedAddress -
  61. (ULONG_PTR)g_DelaySection->VirtualAddress +
  62. (ULONG_PTR)g_DelaySection->PointerToRawData);
  63. }
  64. }
  65. void *
  66. RvaToPtr(DWORD_PTR rva)
  67. {
  68. DWORD i;
  69. PIMAGE_SECTION_HEADER pSect;
  70. if (!rva)
  71. return NULL;
  72. for (i = 0; i < g_pli->NumberOfSections; i++) {
  73. pSect = g_pli->Sections+i;
  74. if (rva >= g_pli->Sections[i].VirtualAddress &&
  75. rva <= (g_pli->Sections[i].VirtualAddress + g_pli->Sections[i].Misc.VirtualSize))
  76. {
  77. return (PVOID)
  78. (g_pli->MappedAddress +
  79. g_pli->Sections[i].PointerToRawData +
  80. (rva - g_pli->Sections[i].VirtualAddress));
  81. }
  82. }
  83. return NULL;
  84. }
  85. void Usage( void )
  86. {
  87. puts(rgstrUsage);
  88. exit (1);
  89. }
  90. BOOLEAN ImageLinksToKernel32Handler( void )
  91. {
  92. PIMAGE_IMPORT_DESCRIPTOR Imports;
  93. ULONG ImportSize;
  94. PULONG_PTR pIAT;
  95. PIMAGE_IMPORT_BY_NAME pImport;
  96. Imports = (PIMAGE_IMPORT_DESCRIPTOR)
  97. ImageDirectoryEntryToData(g_pli->MappedAddress,
  98. FALSE,
  99. IMAGE_DIRECTORY_ENTRY_IMPORT,
  100. &ImportSize
  101. );
  102. if (!Imports) {
  103. // Image has delayload imports, but no true imports.
  104. return FALSE;
  105. }
  106. while (Imports->Name) {
  107. char *szName;
  108. szName = ImageRvaToVa(g_pli->FileHeader, (PVOID)g_pli->MappedAddress, Imports->Name, NULL);
  109. if (szName && _stricmp(szName, "KERNEL32.DLL") == 0) {
  110. pIAT = ImageRvaToVa(g_pli->FileHeader,
  111. (PVOID)g_pli->MappedAddress,
  112. Imports->OriginalFirstThunk,
  113. NULL);
  114. while (pIAT && *pIAT) {
  115. pImport = ImageRvaToVa(g_pli->FileHeader,
  116. (PVOID)g_pli->MappedAddress,
  117. (ULONG) *pIAT,
  118. NULL);
  119. if (pImport && _stricmp(pImport->Name, "DelayLoadFailureHook") == 0) {
  120. return TRUE;
  121. }
  122. pIAT++;
  123. }
  124. }
  125. Imports++;
  126. }
  127. return FALSE;
  128. }
  129. //
  130. // Validate that the statically-linked delayload stub table is not
  131. // blatantly broken. The most common error is not listing the functions
  132. // in the correct order so the binary search fails.
  133. //
  134. int ValidateStaticDelayloadStubs()
  135. {
  136. extern const DLOAD_DLL_MAP g_DllMap;
  137. UINT i, j;
  138. int Errors = 0;
  139. //
  140. // Ensure that the DLL map is in alphabetical order.
  141. //
  142. for (i = 1; i < g_DllMap.NumberOfEntries; i++)
  143. {
  144. if (strcmp(g_DllMap.pDllEntry[i-1].pszDll,
  145. g_DllMap.pDllEntry[i].pszDll) >= 0)
  146. {
  147. fprintf(stderr, "DLCHECK : error DL000001 : Static delayload table is corrupted\n"
  148. " %s and %s not in alphabetical order\n",
  149. g_DllMap.pDllEntry[i-1].pszDll,
  150. g_DllMap.pDllEntry[i].pszDll);
  151. Errors = 1;
  152. }
  153. }
  154. // For each DLL...
  155. for (i = 0; i < g_DllMap.NumberOfEntries; i++)
  156. {
  157. const DLOAD_DLL_ENTRY *pEntry = &g_DllMap.pDllEntry[i];
  158. //
  159. // Name must be lowercase.
  160. //
  161. char szLower[MAX_PATH];
  162. strcpy(szLower, pEntry->pszDll);
  163. _strlwr(szLower);
  164. if (strcmp(szLower, pEntry->pszDll) != 0)
  165. {
  166. fprintf(stderr, "DLCHECK : error DL000002 : Static delayload table is corrupted\n"
  167. " %s must be all-lowercase\n",
  168. pEntry->pszDll);
  169. Errors = 1;
  170. }
  171. //
  172. // Ensure that the exports are in alphabetical order
  173. //
  174. {
  175. const DLOAD_PROCNAME_MAP *pProcNameMap = pEntry->pProcNameMap;
  176. if (pProcNameMap)
  177. {
  178. const DLOAD_PROCNAME_ENTRY *pProcNameEntry = pProcNameMap->pProcNameEntry;
  179. for (j = 1; j < pProcNameMap->NumberOfEntries; j++)
  180. {
  181. if (strcmp(pProcNameEntry[j-1].pszProcName,
  182. pProcNameEntry[j].pszProcName) >= 0)
  183. {
  184. fprintf(stderr, "DLCHECK : error DL000003 : Static delayload table is corrupted\n"
  185. " %s.%s and %s.%s not in alphabetical order\n",
  186. g_DllMap.pDllEntry[i].pszDll,
  187. pProcNameEntry[j-1].pszProcName,
  188. g_DllMap.pDllEntry[i].pszDll,
  189. pProcNameEntry[j].pszProcName);
  190. Errors = 1;
  191. }
  192. }
  193. }
  194. }
  195. //
  196. // Ensure that the ordinals are in alphabetical order
  197. //
  198. {
  199. const DLOAD_ORDINAL_MAP *pOrdinalMap = pEntry->pOrdinalMap;
  200. if (pOrdinalMap)
  201. {
  202. const DLOAD_ORDINAL_ENTRY *pOrdinalEntry = pOrdinalMap->pOrdinalEntry;
  203. for (j = 1; j < pOrdinalMap->NumberOfEntries; j++)
  204. {
  205. if (pOrdinalEntry[j-1].dwOrdinal >= pOrdinalEntry[j].dwOrdinal)
  206. {
  207. fprintf(stderr, "DLCHECK : error DL000001 : Static delayload table is corrupted\n"
  208. " %s.%d and %s.%d not in numeric order\n",
  209. g_DllMap.pDllEntry[i].pszDll,
  210. pOrdinalEntry[j-1].dwOrdinal,
  211. g_DllMap.pDllEntry[i].pszDll,
  212. pOrdinalEntry[j-1].dwOrdinal);
  213. Errors = 1;
  214. }
  215. }
  216. }
  217. }
  218. }
  219. return Errors;
  220. }
  221. int
  222. __cdecl
  223. main (
  224. int c,
  225. char *v[]
  226. )
  227. {
  228. PImgDelayDescr Imports;
  229. ULONG ImportSize;
  230. char *szName;
  231. PIMAGE_THUNK_DATA pINT;
  232. DelayLoadInfo dlinfo;
  233. FARPROC fp;
  234. int ReturnValue;
  235. BOOL fCallHandler;
  236. BOOL fPE32;
  237. if (*v[1] == '-' || *v[1] == '/') {
  238. switch ( *(v[1]+1) ) {
  239. case 's':
  240. case 'S':
  241. if (c != 3) {
  242. Usage();
  243. }
  244. strcpy(g_szImageName, v[2]);
  245. break; // nothing needs to be done.
  246. case 'l':
  247. case 'L':
  248. __pfnFailureProc = (PfnKernel32HookProc)GetProcAddress(GetModuleHandleA("kernel32.dll"), "DelayLoadFailureHook");
  249. if (!__pfnFailureProc) {
  250. fprintf(stderr, "DLCHECK : fatal error %d: looking up kernel32 delayload hook\n", GetLastError());
  251. return 1;
  252. }
  253. if (c != 3) {
  254. Usage();
  255. }
  256. strcpy(g_szImageName, v[2]);
  257. break;
  258. case 'i':
  259. case 'I':
  260. if (c != 3) {
  261. Usage();
  262. }
  263. {
  264. char szIniFile[MAX_PATH];
  265. char szTemp[MAX_PATH];
  266. char szTemp2[MAX_PATH];
  267. char* p = v[2];
  268. if (p[1] != ':')
  269. {
  270. // not a full path...
  271. GetCurrentDirectory(sizeof(szTemp), szTemp);
  272. sprintf(szIniFile, "%s\\%s", szTemp, v[2]);
  273. }
  274. GetPrivateProfileString("Default", "DelayLoadHandler", "", g_szDelayLoadHandler, sizeof(g_szDelayLoadHandler), szIniFile);
  275. // foomodule.dll.ini -> foomodule.dll
  276. strcpy(g_szImageName, v[2]);
  277. p = strstr(g_szImageName, ".ini");
  278. if (p)
  279. {
  280. *p = '\0';
  281. }
  282. if (_stricmp(g_szDelayLoadHandler, "FORCE") == 0)
  283. {
  284. // if the delayload handler is set to FORCE, we check the binary as if it were
  285. // using kernel32
  286. fForceCheckImage = TRUE;
  287. }
  288. if ((_stricmp(g_szDelayLoadHandler, "kernel32") != 0) &&
  289. (_stricmp(g_szDelayLoadHandler, "FORCE") != 0))
  290. {
  291. // currently only able to check dll's who use kernel32.dll for their delayload handler
  292. fprintf(stdout, "DLCHECK : warning DL000000 : Unable to check delayload failure behavior\n"
  293. " %s uses %s as a handler, not kernel32\n", g_szImageName, g_szDelayLoadHandler);
  294. return 0;
  295. }
  296. // foomodule.dll -> d:\binaries.x86chk\foomodule.dll
  297. if (ExpandEnvironmentStrings("%_NTPostBld%", szTemp, sizeof(szTemp)) == 0)
  298. {
  299. fprintf(stderr, "DLCHECK : fatal error : _NTPostBld environment variable not set\n");
  300. return 1;
  301. }
  302. GetPrivateProfileString("Default", "DestinationDir", "", szTemp2, sizeof(szTemp2), szIniFile);
  303. strcat(szTemp, "\\");
  304. strcat(szTemp, szTemp2);
  305. strcat(szTemp, g_szImageName);
  306. strcpy(g_szImageName, szTemp);
  307. }
  308. break;
  309. case 't':
  310. case 'T':
  311. if (c != 2) {
  312. Usage();
  313. }
  314. return ValidateStaticDelayloadStubs();
  315. case 'f':
  316. case 'F':
  317. if (c != 3)
  318. {
  319. Usage();
  320. }
  321. fForceCheckImage = TRUE;
  322. strcpy(g_szImageName, v[2]);
  323. break; // nothing needs to be done.
  324. default:
  325. Usage();
  326. }
  327. } else {
  328. Usage();
  329. }
  330. g_pli = ImageLoad(g_szImageName, NULL);
  331. if (!g_pli) {
  332. fprintf(stderr, "DLCHECK : fatal error %d: loading '%s'\n", GetLastError(), g_szImageName);
  333. return 1;
  334. }
  335. Imports = (PImgDelayDescr)
  336. ImageDirectoryEntryToDataEx(g_pli->MappedAddress,
  337. FALSE,
  338. IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT,
  339. &ImportSize,
  340. &g_DelaySection
  341. );
  342. if (!Imports) {
  343. fprintf(stdout, "DLCHECK : warning DL000000: image '%s' has no delayload imports\n", g_szImageName);
  344. return 0;
  345. }
  346. fPE32 = g_pli->FileHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC ? TRUE : FALSE;
  347. if (fForceCheckImage)
  348. {
  349. fCallHandler = TRUE;
  350. }
  351. else
  352. {
  353. fCallHandler = ImageLinksToKernel32Handler();
  354. }
  355. if (!fCallHandler) {
  356. fprintf(stderr, "DLCHECK : fatal errror : DLL doesn't import kernel32!DelayLoadFailureHook.\n"
  357. "(use -f option to override)\n"
  358. "\n");
  359. return 1;
  360. }
  361. //
  362. // Walk each delayloaded DLL
  363. //
  364. ReturnValue = 0; // assume success
  365. if (Imports->grAttrs & dlattrRva) {
  366. PImgDelayDescrV2 pImportsV2 = (PImgDelayDescrV2)Imports;
  367. szName = (char *)RvaToPtr(pImportsV2->rvaDLLName);
  368. pINT = (PIMAGE_THUNK_DATA)RvaToPtr(pImportsV2->rvaINT);
  369. } else {
  370. PImgDelayDescrV1 pImportsV1 = (PImgDelayDescrV1)Imports;
  371. szName = (char *)ConvertImagePointer((void *)pImportsV1->szName);
  372. pINT = (PIMAGE_THUNK_DATA)ConvertImagePointer((void *)pImportsV1->pINT);
  373. }
  374. while (szName) {
  375. // printf("DelayLoad DLL %s\n", szName);
  376. char szModuleName[MAX_PATH];
  377. char szImportName[MAX_PATH];
  378. {
  379. char* p;
  380. // change "module.dll" to just "module"
  381. strcpy(szModuleName, szName);
  382. p = szModuleName;
  383. while (*p != '\0')
  384. {
  385. if (*p == '.')
  386. {
  387. *p = '\0';
  388. break;
  389. }
  390. p++;
  391. }
  392. }
  393. //
  394. // Walk each function called from the delayloaded DLL
  395. //
  396. while (pINT->u1.AddressOfData) {
  397. dlinfo.cb = sizeof(dlinfo);
  398. dlinfo.pidd = NULL;
  399. dlinfo.ppfn = NULL;
  400. dlinfo.szDll = szName;
  401. dlinfo.pfnCur = NULL;
  402. dlinfo.dwLastError = ERROR_NOT_ENOUGH_MEMORY;
  403. dlinfo.dlp.szProcName = NULL; // Make sure the upper 32 bits are zeroed out on win64.
  404. if (
  405. ( fPE32 && IMAGE_SNAP_BY_ORDINAL32(((PIMAGE_THUNK_DATA32)pINT)->u1.AddressOfData)) ||
  406. (!fPE32 && IMAGE_SNAP_BY_ORDINAL64(((PIMAGE_THUNK_DATA64)pINT)->u1.AddressOfData))
  407. )
  408. {
  409. sprintf(szImportName, "Ordinal%d", IMAGE_ORDINAL(pINT->u1.AddressOfData));
  410. dlinfo.dlp.fImportByName = FALSE;
  411. dlinfo.dlp.dwOrdinal = IMAGE_ORDINAL((ULONG)pINT->u1.AddressOfData);
  412. } else {
  413. PIMAGE_IMPORT_BY_NAME pImport;
  414. if (Imports->grAttrs & dlattrRva) {
  415. pImport = (PIMAGE_IMPORT_BY_NAME)RvaToPtr(pINT->u1.AddressOfData);
  416. } else {
  417. pImport = (PIMAGE_IMPORT_BY_NAME)ConvertImagePointer((void *)pINT->u1.AddressOfData);
  418. }
  419. sprintf(szImportName, "%s", pImport->Name);
  420. dlinfo.dlp.fImportByName = TRUE;
  421. dlinfo.dlp.szProcName = pImport->Name;
  422. }
  423. if (fCallHandler) {
  424. //
  425. // Call the delayload handler and see what it does.
  426. //
  427. try {
  428. fp = (*__pfnFailureProc)(dlinfo.szDll, dlinfo.dlp.szProcName);
  429. if (!fp) {
  430. fprintf(stderr, "DLCHECK : error DL000000: %s imports %s!%s which is not handled.\n", g_szImageName, szModuleName, szImportName);
  431. ReturnValue = 1;
  432. } else {
  433. printf("DLCHECK : %s imports %s!%s - OK.\n", g_szImageName, szModuleName, szImportName);
  434. }
  435. } except (EXCEPTION_EXECUTE_HANDLER) {
  436. fprintf(stderr, "DLCHECK : error %x: %s imports %s!%s - handler threw an exception.\n", GetExceptionCode(), g_szImageName, szModuleName, szImportName);
  437. ReturnValue = 1;
  438. }
  439. }
  440. else
  441. {
  442. printf("DLCHECK : %s imports %s!%s - not checked.\n", g_szImageName, szModuleName, szImportName);
  443. }
  444. if (fPE32) {
  445. pINT = (PIMAGE_THUNK_DATA)(((PIMAGE_THUNK_DATA32)pINT)++);
  446. } else {
  447. pINT = (PIMAGE_THUNK_DATA)(((PIMAGE_THUNK_DATA64)pINT)++);
  448. }
  449. }
  450. if (Imports->grAttrs & dlattrRva) {
  451. PImgDelayDescrV2 pImportsV2 = (PImgDelayDescrV2)Imports;
  452. pImportsV2++;
  453. Imports = (PImgDelayDescr)pImportsV2;
  454. szName = (char *)RvaToPtr(pImportsV2->rvaDLLName);
  455. pINT = (PIMAGE_THUNK_DATA)RvaToPtr(pImportsV2->rvaINT);
  456. } else {
  457. PImgDelayDescrV1 pImportsV1 = (PImgDelayDescrV1)Imports;
  458. pImportsV1++;
  459. Imports = (PImgDelayDescr)pImportsV1;
  460. szName = (char *)ConvertImagePointer((void *)pImportsV1->szName);
  461. pINT = (PIMAGE_THUNK_DATA)ConvertImagePointer((void *)pImportsV1->pINT);
  462. }
  463. }
  464. if (ReturnValue == 0)
  465. {
  466. printf("DLCHECK : succeeded on %s \n", g_szImageName);
  467. }
  468. else
  469. {
  470. fprintf(stderr, "DLCHECK : failed on %s \n", g_szImageName);
  471. }
  472. return ReturnValue;
  473. }