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.

2443 lines
65 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. imagechk.c
  5. Abstract:
  6. this module implements a sanity check of certain image characteristics
  7. Author:
  8. NT Base
  9. Revision History:
  10. Notes:
  11. --*/
  12. #ifdef __cplusplus
  13. extern "C" {
  14. #endif
  15. #include <nt.h>
  16. #include <ntrtl.h>
  17. #include <nturtl.h>
  18. #ifdef __cplusplus
  19. }
  20. #endif
  21. #include <errno.h>
  22. #include <direct.h>
  23. #include <cvinfo.h>
  24. #include <private.h>
  25. typedef struct _SYMMODLIST{
  26. char *ModName;
  27. void *ModBase;
  28. struct _SYMMODLIST *Next;
  29. } SYMMODLIST, *PSYMMODLIST;
  30. typedef struct List {
  31. char Name[40];
  32. unsigned long Attributes;
  33. } List, *pList;
  34. typedef struct _LogListItem {
  35. char *LogLine;
  36. struct _LogListItem *Next;
  37. } LogListItem, *pLogListItem;
  38. //
  39. // decarations
  40. //
  41. VOID
  42. FindFiles();
  43. VOID
  44. Imagechk(
  45. List *rgpList,
  46. TCHAR *szDirectory
  47. );
  48. VOID
  49. ParseArgs(
  50. int *pargc,
  51. char **argv
  52. );
  53. int
  54. __cdecl
  55. CompFileAndDir(
  56. const void *elem1,
  57. const void *elem2
  58. );
  59. int
  60. __cdecl
  61. CompName(
  62. const void *elem1,
  63. const void *elem2
  64. );
  65. VOID
  66. Usage(
  67. VOID
  68. );
  69. int
  70. _cdecl
  71. _cwild(
  72. VOID
  73. );
  74. PSYMMODLIST
  75. MakeModList(
  76. HANDLE
  77. );
  78. void
  79. FreeModList(
  80. PSYMMODLIST
  81. );
  82. BOOL
  83. CALLBACK
  84. SymEnumerateModulesCallback(
  85. LPSTR,
  86. ULONG64,
  87. PVOID
  88. );
  89. void *
  90. GetModAddrFromName(
  91. PSYMMODLIST,
  92. char *
  93. );
  94. BOOL
  95. VerifyVersionResource(
  96. PCHAR FileName,
  97. BOOL fSelfRegister
  98. );
  99. BOOL
  100. ValidatePdata(
  101. PIMAGE_DOS_HEADER DosHeader
  102. );
  103. BOOL
  104. ImageNeedsOleSelfRegister(
  105. PIMAGE_DOS_HEADER DosHeader
  106. );
  107. NTSTATUS
  108. MiVerifyImageHeader (
  109. IN PIMAGE_NT_HEADERS NtHeader,
  110. IN PIMAGE_DOS_HEADER DosHeader,
  111. IN DWORD NtHeaderSize
  112. );
  113. pLogListItem
  114. LogAppend(
  115. char *,
  116. pLogListItem
  117. );
  118. void
  119. LogOutAndClean(
  120. BOOL
  121. );
  122. void
  123. __cdecl
  124. LogPrintf(
  125. const char *format,
  126. ...
  127. );
  128. #define X64K (64*1024)
  129. #define MM_SIZE_OF_LARGEST_IMAGE ((ULONG)0x10000000)
  130. #define MM_MAXIMUM_IMAGE_HEADER (2 * PageSize)
  131. #define MM_MAXIMUM_IMAGE_SECTIONS \
  132. ((MM_MAXIMUM_IMAGE_HEADER - (4096 + sizeof(IMAGE_NT_HEADERS))) / \
  133. sizeof(IMAGE_SECTION_HEADER))
  134. #define MMSECTOR_SHIFT 9 //MUST BE LESS THAN OR EQUAL TO PageShift
  135. #define MMSECTOR_MASK 0x1ff
  136. #define MI_ROUND_TO_SIZE(LENGTH,ALIGNMENT) \
  137. (((ULONG)LENGTH + ALIGNMENT - 1) & ~(ALIGNMENT - 1))
  138. #define BYTES_TO_PAGES(Size) (((ULONG)(Size) >> PageShift) + \
  139. (((ULONG)(Size) & (PageSize - 1)) != 0))
  140. #define ArgFlag_OK 1
  141. #define ArgFlag_CKMZ 2
  142. #define ArgFlag_SymCK 4
  143. #define ArgFlag_OLESelf 8
  144. #define ArgFlag_CKBase 16
  145. //
  146. // file global data
  147. //
  148. BOOL fRecurse;
  149. BOOL fFileOut;
  150. BOOL fNotCurrent;
  151. BOOL fPattern;
  152. BOOL fSingleFile;
  153. BOOL fPathOverride;
  154. BOOL fSingleSlash;
  155. BOOL fDebugMapped;
  156. FILE* fout;
  157. CHAR *szFileName = {"*.*"};
  158. CHAR *pszRootDir;
  159. CHAR *pszFileOut;
  160. CHAR szDirectory[MAX_PATH] = {"."};
  161. CHAR szSympath[MAX_PATH] = {0};
  162. CHAR *szPattern;
  163. int endpath, DirNum=1, ProcessedFiles;
  164. ULONG PageSize;
  165. ULONG PageShift;
  166. PVOID HighestUserAddress;
  167. USHORT ValidMachineIDMin;
  168. USHORT ValidMachineIDMax;
  169. DWORD ArgFlag;
  170. //
  171. // logging support
  172. //
  173. pLogListItem pLogList = NULL;
  174. pLogListItem pLogListTmp = NULL;
  175. typedef
  176. NTSTATUS
  177. (NTAPI *LPLDRVERIFYIMAGECHKSUM)(
  178. IN HANDLE ImageFileHandle
  179. );
  180. LPLDRVERIFYIMAGECHKSUM lpOldLdrVerifyImageMatchesChecksum;
  181. typedef
  182. NTSTATUS
  183. (NTAPI *LPLDRVERIFYIMAGEMATCHESCHECKSUM) (
  184. IN HANDLE ImageFileHandle,
  185. IN PLDR_IMPORT_MODULE_CALLBACK ImportCallbackRoutine OPTIONAL,
  186. IN PVOID ImportCallbackParameter,
  187. OUT PUSHORT ImageCharacteristics OPTIONAL
  188. );
  189. LPLDRVERIFYIMAGEMATCHESCHECKSUM lpNewLdrVerifyImageMatchesChecksum;
  190. typedef
  191. NTSTATUS
  192. (NTAPI *LPNTQUERYSYSTEMINFORMATION) (
  193. IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
  194. OUT PVOID SystemInformation,
  195. IN ULONG SystemInformationLength,
  196. OUT PULONG ReturnLength OPTIONAL
  197. );
  198. LPNTQUERYSYSTEMINFORMATION lpNtQuerySystemInformation;
  199. OSVERSIONINFO VersionInformation;
  200. //
  201. // function definitions
  202. //
  203. VOID __cdecl
  204. main(
  205. int argc,
  206. char *argv[],
  207. char *envp[]
  208. )
  209. /*++
  210. Routine Description:
  211. program entry
  212. Arguments:
  213. int argc,
  214. char *argv[]
  215. char *envp[]
  216. Return Value:
  217. none
  218. Notes:
  219. --*/
  220. {
  221. TCHAR CWD[MAX_PATH];
  222. int dirlen=0;
  223. if (argc < 2) {
  224. Usage();
  225. }
  226. ParseArgs(&argc, argv);
  227. GetCurrentDirectory(MAX_PATH, CWD);
  228. VersionInformation.dwOSVersionInfoSize = sizeof(VersionInformation);
  229. if (!GetVersionEx( &VersionInformation )) {
  230. fprintf(stderr, "Unable to detect OS version. Terminating.\n" );
  231. exit(1);
  232. }
  233. if ((VersionInformation.dwPlatformId != VER_PLATFORM_WIN32_NT) ||
  234. (VersionInformation.dwBuildNumber < 1230))
  235. {
  236. lpOldLdrVerifyImageMatchesChecksum = (LPLDRVERIFYIMAGECHKSUM)
  237. GetProcAddress(GetModuleHandle(TEXT("NTDLL.DLL")), TEXT("LdrVerifyImageMatchesChecksum"));
  238. if (lpOldLdrVerifyImageMatchesChecksum == NULL) {
  239. fprintf(stderr, "Incorrect operating system version.\n" );
  240. exit(1);
  241. }
  242. } else {
  243. lpOldLdrVerifyImageMatchesChecksum = NULL;
  244. if ((VersionInformation.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
  245. (VersionInformation.dwBuildNumber >= 1230))
  246. {
  247. lpNewLdrVerifyImageMatchesChecksum = (LPLDRVERIFYIMAGEMATCHESCHECKSUM)
  248. GetProcAddress(GetModuleHandle(TEXT("NTDLL.DLL")), TEXT("LdrVerifyImageMatchesChecksum"));
  249. if (lpNewLdrVerifyImageMatchesChecksum == NULL) {
  250. fprintf(stderr, "OS is screwed up. NTDLL doesn't export LdrVerifyImageMatchesChecksum.\n" );
  251. exit(1);
  252. }
  253. }
  254. }
  255. if (VersionInformation.dwPlatformId == VER_PLATFORM_WIN32_NT) {
  256. SYSTEM_BASIC_INFORMATION SystemInformation;
  257. if (VersionInformation.dwBuildNumber <= 1465) {
  258. goto UseWin9x;
  259. }
  260. ValidMachineIDMin = USER_SHARED_DATA->ImageNumberLow;
  261. ValidMachineIDMax = USER_SHARED_DATA->ImageNumberHigh;
  262. lpNtQuerySystemInformation = (LPNTQUERYSYSTEMINFORMATION)
  263. GetProcAddress(GetModuleHandle(TEXT("NTDLL.DLL")), TEXT("NtQuerySystemInformation"));
  264. if (!lpNtQuerySystemInformation) {
  265. fprintf(stderr, "Incorrect operation system version.\n");
  266. exit(1);
  267. }
  268. if (!NT_SUCCESS((*lpNtQuerySystemInformation)(SystemBasicInformation,
  269. &SystemInformation,
  270. sizeof(SystemInformation),
  271. NULL))) {
  272. fprintf(stderr, "OS is screwed up. NtQuerySystemInformation failed.\n");
  273. exit(1);
  274. }
  275. HighestUserAddress = (PVOID)SystemInformation.MaximumUserModeAddress;
  276. } else {
  277. UseWin9x:
  278. HighestUserAddress = (PVOID) 0x7FFE0000;
  279. #ifdef _M_IX86
  280. ValidMachineIDMin = IMAGE_FILE_MACHINE_I386;
  281. ValidMachineIDMax = IMAGE_FILE_MACHINE_I386;
  282. #elif defined(_M_AMD64)
  283. ValidMachineIDMin = IMAGE_FILE_MACHINE_AMD64;
  284. ValidMachineIDMax = IMAGE_FILE_MACHINE_AMD64;
  285. #elif defined(_M_IA64)
  286. ValidMachineIDMin = IMAGE_FILE_MACHINE_IA64;
  287. ValidMachineIDMax = IMAGE_FILE_MACHINE_IA64;
  288. #else
  289. #error("Unknown machine type")
  290. #endif
  291. }
  292. if (fPathOverride) {
  293. if (_chdir(szDirectory) == -1){ // cd to dir
  294. fprintf(stderr, "Path not found: %s\n", szDirectory);
  295. Usage();
  296. }
  297. }
  298. // remove trailing '\' needed only for above chdir, not for output formatting
  299. if (fSingleSlash) {
  300. dirlen = strlen(szDirectory);
  301. szDirectory[dirlen-1] = '\0';
  302. }
  303. FindFiles();
  304. fprintf(stdout, "%d files processed in %d directories\n", ProcessedFiles, DirNum);
  305. }
  306. VOID
  307. FindFiles()
  308. /*++
  309. Routine Description:
  310. make list of files to check, then check them
  311. Arguments:
  312. none
  313. Return Value:
  314. none
  315. Notes:
  316. --*/
  317. {
  318. HANDLE fh;
  319. TCHAR CWD[MAX_PATH];
  320. char *q;
  321. WIN32_FIND_DATA *pfdata;
  322. BOOL fFilesInDir=FALSE;
  323. BOOL fDirsFound=FALSE;
  324. int dnCounter=0, cNumDir=0, i=0, Length=0, NameSize=0, total=0, cNumFiles=0;
  325. pList rgpList[5000];
  326. pfdata = (WIN32_FIND_DATA*)malloc(sizeof(WIN32_FIND_DATA));
  327. if (!pfdata) {
  328. fprintf(stderr, "Not enough memory.\n");
  329. return;
  330. }
  331. if (!fRecurse) {
  332. fh = FindFirstFile(szFileName, pfdata); // find only filename (pattern) if not recursive
  333. } else {
  334. fh = FindFirstFile("*.*", pfdata); // find all if recursive in order to determine subdirectory names
  335. }
  336. if (fh == INVALID_HANDLE_VALUE) {
  337. fprintf(fout==NULL? stderr : fout , "File not found: %s\n", szFileName);
  338. return;
  339. }
  340. // loop to find all files and directories in current directory
  341. // and copy pertinent data to individual List structures.
  342. do {
  343. if (strcmp(pfdata->cFileName, ".") && strcmp(pfdata->cFileName, "..")) { // skip . and ..
  344. rgpList[dnCounter] = (pList)malloc(sizeof(List)); // allocate the memory
  345. if (!rgpList[dnCounter]) {
  346. fprintf(stderr, "Not enough memory.\n");
  347. return;
  348. }
  349. if (!(pfdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { // if file
  350. fFilesInDir=TRUE;
  351. // see if given pattern wildcard extension matches pfdata->cFileName extension
  352. if (fPattern) {
  353. q = strchr(pfdata->cFileName, '.'); // find first instance of "." in filename
  354. if (q == NULL) goto blah; // "." not found
  355. _strlwr(q); // lowercase before compare
  356. if (strcmp(q, szPattern)) goto blah; // if pattern and name doesn't match goto
  357. } // OK, I used a goto, get over it.
  358. if (fSingleFile) {
  359. _strlwr(pfdata->cFileName);
  360. _strlwr(szFileName);
  361. if (strcmp(pfdata->cFileName, szFileName)) goto blah;
  362. }
  363. // if pattern && match || no pattern
  364. strcpy(rgpList[dnCounter]->Name, pfdata->cFileName);
  365. _strlwr(rgpList[dnCounter]->Name); // all lowercase for strcmp in CompName
  366. memcpy(&(rgpList[dnCounter]->Attributes), &pfdata->dwFileAttributes, 4);
  367. dnCounter++;
  368. cNumFiles++;
  369. } else {
  370. if (pfdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { // if dir
  371. fDirsFound=TRUE;
  372. //cNumDir++;
  373. if (fRecurse) {
  374. strcpy(rgpList[dnCounter]->Name, pfdata->cFileName);
  375. _strlwr(rgpList[dnCounter]->Name); // all lowercase for strcmp in CompName
  376. memcpy(&(rgpList[dnCounter]->Attributes), &pfdata->dwFileAttributes, 4);
  377. cNumDir++;
  378. dnCounter++;
  379. }
  380. }
  381. }
  382. }
  383. blah: ;
  384. } while (FindNextFile(fh, pfdata));
  385. FindClose(fh); // close the file handle
  386. // Sort Array arranging FILE entries at top
  387. qsort( (void *)rgpList, dnCounter, sizeof(List *), CompFileAndDir);
  388. // Sort Array alphabetizing only FILE names
  389. qsort( (void *)rgpList, dnCounter-cNumDir, sizeof(List *), CompName);
  390. // Sort Array alphabetizing only DIRectory names
  391. if (fRecurse) {
  392. qsort( (void *)&rgpList[dnCounter-cNumDir], cNumDir, sizeof(List *), CompName);
  393. }
  394. // Process newly sorted structures.
  395. for (i=0; i < dnCounter; ++i) {
  396. if (rgpList[i]->Attributes & FILE_ATTRIBUTE_DIRECTORY) { // if Dir
  397. if (fRecurse) {
  398. if (_chdir(rgpList[i]->Name) == -1){ // cd into subdir and check for error
  399. fprintf(stderr, "Unable to change directory: %s\n", rgpList[i]->Name);
  400. } else {
  401. NameSize = strlen(rgpList[i]->Name);
  402. strcat(szDirectory, "\\");
  403. strcat(szDirectory, rgpList[i]->Name); //append name to directory path
  404. total = strlen(szDirectory);
  405. DirNum++; // directory counter
  406. // start another iteration of FindFiles
  407. FindFiles();
  408. // get back to previous directory when above iteration returns
  409. _chdir("..");
  410. // cut off previously appended directory name - for output only
  411. szDirectory[total-(NameSize+1)]='\0';
  412. }
  413. }
  414. } else {
  415. if (!(rgpList[i]->Attributes & FILE_ATTRIBUTE_DIRECTORY)) // check image if not dir
  416. Imagechk(rgpList[i], szDirectory);
  417. }
  418. }
  419. } // end FindFiles
  420. VOID
  421. Imagechk(
  422. List *rgpList,
  423. TCHAR *szDirectory
  424. )
  425. /*++
  426. Routine Description:
  427. check various things, including:
  428. image type, header alignment, image size, machine type
  429. alignment, some properties of various sections, checksum integrity
  430. symbol / image file checksum agreement, existence of symbols, etc
  431. Arguments:
  432. List * rgpList,
  433. TCHAR * szDirectory
  434. Return Value:
  435. none
  436. Notes:
  437. --*/
  438. {
  439. HANDLE File;
  440. HANDLE MemMap;
  441. PIMAGE_DOS_HEADER DosHeader;
  442. PIMAGE_NT_HEADERS NtHeader;
  443. NTSTATUS Status;
  444. BY_HANDLE_FILE_INFORMATION FileInfo;
  445. ULONG NumberOfPtes;
  446. ULONG SectionVirtualSize = 0;
  447. ULONG i;
  448. PIMAGE_SECTION_HEADER SectionTableEntry;
  449. ULONG NumberOfSubsections;
  450. PCHAR ExtendedHeader = NULL;
  451. ULONG_PTR PreferredImageBase;
  452. ULONG_PTR NextVa;
  453. ULONG ImageFileSize;
  454. ULONG OffsetToSectionTable;
  455. ULONG ImageAlignment;
  456. ULONG PtesInSubsection;
  457. ULONG StartingSector;
  458. ULONG EndingSector;
  459. LPSTR ImageName;
  460. LPSTR MachineType = "Unknown";
  461. BOOL MachineTypeMismatch;
  462. BOOL ImageOk;
  463. BOOL fHasPdata;
  464. OSVERSIONINFO OSVerInfo;
  465. ImageName = rgpList->Name;
  466. OSVerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  467. GetVersionEx(&OSVerInfo);
  468. LogPrintf("ImageChk: %s\\%s \n", szDirectory, ImageName);
  469. ProcessedFiles++;
  470. DosHeader = NULL;
  471. ImageOk = TRUE;
  472. File = CreateFile (ImageName,
  473. GENERIC_READ | FILE_EXECUTE,
  474. OSVerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ? (FILE_SHARE_READ | FILE_SHARE_DELETE) : FILE_SHARE_READ,
  475. NULL,
  476. OPEN_EXISTING,
  477. FILE_ATTRIBUTE_NORMAL,
  478. NULL);
  479. if (File == INVALID_HANDLE_VALUE) {
  480. LogPrintf("Error, CreateFile() %d\n", GetLastError());
  481. ImageOk = FALSE;
  482. goto NextImage;
  483. }
  484. MemMap = CreateFileMapping (File,
  485. NULL, // default security.
  486. PAGE_READONLY, // file protection.
  487. 0, // high-order file size.
  488. 0,
  489. NULL);
  490. if (!GetFileInformationByHandle(File, &FileInfo)) {
  491. fprintf(stderr,"Error, GetFileInfo() %d\n", GetLastError());
  492. CloseHandle(File);
  493. ImageOk = FALSE; goto NextImage;
  494. }
  495. DosHeader = (PIMAGE_DOS_HEADER) MapViewOfFile(MemMap,
  496. FILE_MAP_READ,
  497. 0, // high
  498. 0, // low
  499. 0 // whole file
  500. );
  501. CloseHandle(MemMap);
  502. if (!DosHeader) {
  503. fprintf(stderr,"Error, MapViewOfFile() %d\n", GetLastError());
  504. ImageOk = FALSE; goto NextImage;
  505. }
  506. //
  507. // Check to determine if this is an NT image (PE format) or
  508. // a DOS image, Win-16 image, or OS/2 image. If the image is
  509. // not NT format, return an error indicating which image it
  510. // appears to be.
  511. //
  512. if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
  513. if (ArgFlag & ArgFlag_CKMZ) {
  514. LogPrintf("MZ header not found\n");
  515. ImageOk = FALSE;
  516. }
  517. goto NeImage;
  518. }
  519. if (((ULONG)DosHeader->e_lfanew & 3) != 0) {
  520. //
  521. // The image header is not aligned on a long boundary.
  522. // Report this as an invalid protect mode image.
  523. //
  524. LogPrintf("Image header not on Long boundary\n");
  525. ImageOk = FALSE;
  526. goto NeImage;
  527. }
  528. if ((ULONG)DosHeader->e_lfanew > FileInfo.nFileSizeLow) {
  529. LogPrintf("Image size bigger than size of file\n");
  530. ImageOk = FALSE;
  531. goto NeImage;
  532. }
  533. NtHeader = (PIMAGE_NT_HEADERS)((PCHAR)DosHeader + (ULONG)DosHeader->e_lfanew);
  534. if (NtHeader->Signature != IMAGE_NT_SIGNATURE) { //if not PE image
  535. LogPrintf("Non 32-bit image");
  536. ImageOk = TRUE;
  537. goto NeImage;
  538. }
  539. //
  540. // Check to see if this is an NT image or a DOS or OS/2 image.
  541. //
  542. Status = MiVerifyImageHeader (NtHeader, DosHeader, 50000);
  543. if (Status != STATUS_SUCCESS) {
  544. ImageOk = FALSE; //continue checking the image but don't print "OK"
  545. }
  546. //
  547. // Verify machine type.
  548. //
  549. fHasPdata = TRUE; // Most do
  550. switch (NtHeader->FileHeader.Machine) {
  551. case IMAGE_FILE_MACHINE_I386:
  552. MachineType = "x86";
  553. PageSize = 4096;
  554. PageShift = 12;
  555. fHasPdata = FALSE;
  556. break;
  557. case IMAGE_FILE_MACHINE_ALPHA:
  558. MachineType = "Alpha";
  559. PageSize = 8192;
  560. PageShift = 13;
  561. break;
  562. case IMAGE_FILE_MACHINE_IA64:
  563. MachineType = "Intel64";
  564. PageSize = 8192;
  565. PageShift = 13;
  566. break;
  567. case IMAGE_FILE_MACHINE_ALPHA64:
  568. MachineType = "Alpha64";
  569. PageSize = 8192;
  570. PageShift = 13;
  571. break;
  572. default:
  573. LogPrintf("Unrecognized machine type x%lx\n",
  574. NtHeader->FileHeader.Machine);
  575. ImageOk = FALSE;
  576. break;
  577. }
  578. if ((NtHeader->FileHeader.Machine < ValidMachineIDMin) ||
  579. (NtHeader->FileHeader.Machine > ValidMachineIDMax)) {
  580. MachineTypeMismatch = TRUE;
  581. } else {
  582. MachineTypeMismatch = FALSE;
  583. }
  584. ImageAlignment = NtHeader->OptionalHeader.SectionAlignment;
  585. NumberOfPtes = BYTES_TO_PAGES (NtHeader->OptionalHeader.SizeOfImage);
  586. NextVa = NtHeader->OptionalHeader.ImageBase;
  587. if ((NextVa & (X64K - 1)) != 0) {
  588. //
  589. // Image header is not aligned on a 64k boundary.
  590. //
  591. LogPrintf("image base not on 64k boundary %lx\n",NextVa);
  592. ImageOk = FALSE;
  593. goto BadPeImageSegment;
  594. }
  595. //BasedAddress = (PVOID)NextVa;
  596. PtesInSubsection = MI_ROUND_TO_SIZE (
  597. NtHeader->OptionalHeader.SizeOfHeaders,
  598. ImageAlignment
  599. ) >> PageShift;
  600. if (ImageAlignment >= PageSize) {
  601. //
  602. // Aligmment is PageSize of greater.
  603. //
  604. if (PtesInSubsection > NumberOfPtes) {
  605. //
  606. // Inconsistent image, size does not agree with header.
  607. //
  608. LogPrintf("Image size in header (%ld.) not consistent with sections (%ld.)\n",
  609. NumberOfPtes, PtesInSubsection);
  610. ImageOk = FALSE;
  611. goto BadPeImageSegment;
  612. }
  613. NumberOfPtes -= PtesInSubsection;
  614. EndingSector = NtHeader->OptionalHeader.SizeOfHeaders >> MMSECTOR_SHIFT;
  615. for (i = 0; i < PtesInSubsection; i++) {
  616. NextVa += PageSize;
  617. }
  618. }
  619. //
  620. // Build the next subsections.
  621. //
  622. NumberOfSubsections = NtHeader->FileHeader.NumberOfSections;
  623. PreferredImageBase = NtHeader->OptionalHeader.ImageBase;
  624. //
  625. // At this point the object table is read in (if it was not
  626. // already read in) and may displace the image header.
  627. //
  628. OffsetToSectionTable = sizeof(ULONG) +
  629. sizeof(IMAGE_FILE_HEADER) +
  630. NtHeader->FileHeader.SizeOfOptionalHeader;
  631. SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeader + OffsetToSectionTable);
  632. if (ImageAlignment < PageSize) {
  633. // The image header is no longer valid, TempPte is
  634. // used to indicate that this image alignment is
  635. // less than a PageSize.
  636. //
  637. // Loop through all sections and make sure there is no
  638. // unitialized data.
  639. //
  640. while (NumberOfSubsections > 0) {
  641. if (SectionTableEntry->Misc.VirtualSize == 0) {
  642. SectionVirtualSize = SectionTableEntry->SizeOfRawData;
  643. } else {
  644. SectionVirtualSize = SectionTableEntry->Misc.VirtualSize;
  645. }
  646. //
  647. // If the pointer to raw data is zero and the virtual size
  648. // is zero, OR, the section goes past the end of file, OR
  649. // the virtual size does not match the size of raw data, then
  650. // return an error.
  651. //
  652. if (((SectionTableEntry->PointerToRawData !=
  653. SectionTableEntry->VirtualAddress))
  654. ||
  655. ((SectionTableEntry->SizeOfRawData +
  656. SectionTableEntry->PointerToRawData) >
  657. FileInfo.nFileSizeLow)
  658. ||
  659. (SectionVirtualSize > SectionTableEntry->SizeOfRawData)) {
  660. LogPrintf("invalid BSS/Trailingzero section/file size\n");
  661. ImageOk = FALSE;
  662. goto NeImage;
  663. }
  664. SectionTableEntry += 1;
  665. NumberOfSubsections -= 1;
  666. }
  667. goto PeReturnSuccess;
  668. }
  669. while (NumberOfSubsections > 0) {
  670. //
  671. // Handle case where virtual size is 0.
  672. //
  673. if (SectionTableEntry->Misc.VirtualSize == 0) {
  674. SectionVirtualSize = SectionTableEntry->SizeOfRawData;
  675. } else {
  676. SectionVirtualSize = SectionTableEntry->Misc.VirtualSize;
  677. }
  678. if (!strcmp(SectionTableEntry->Name, ".debug")) {
  679. fDebugMapped = TRUE;
  680. }
  681. if (SectionVirtualSize == 0) {
  682. //
  683. // The specified virtual address does not align
  684. // with the next prototype PTE.
  685. //
  686. LogPrintf("Section virtual size is 0, NextVa for section %lx %lx\n",
  687. SectionTableEntry->VirtualAddress, NextVa);
  688. ImageOk = FALSE;
  689. goto BadPeImageSegment;
  690. }
  691. if (NextVa !=
  692. (PreferredImageBase + SectionTableEntry->VirtualAddress)) {
  693. //
  694. // The specified virtual address does not align
  695. // with the next prototype PTE.
  696. //
  697. LogPrintf("Section Va not set to alignment, NextVa for section %lx %lx\n",
  698. SectionTableEntry->VirtualAddress, NextVa);
  699. ImageOk = FALSE;
  700. goto BadPeImageSegment;
  701. }
  702. PtesInSubsection =
  703. MI_ROUND_TO_SIZE (SectionVirtualSize, ImageAlignment) >> PageShift;
  704. if (PtesInSubsection > NumberOfPtes) {
  705. //
  706. // Inconsistent image, size does not agree with object tables.
  707. //
  708. LogPrintf("Image size in header not consistent with sections, needs %ld. pages\n",
  709. PtesInSubsection - NumberOfPtes);
  710. LogPrintf("va of bad section %lx\n",SectionTableEntry->VirtualAddress);
  711. ImageOk = FALSE;
  712. goto BadPeImageSegment;
  713. }
  714. NumberOfPtes -= PtesInSubsection;
  715. StartingSector = SectionTableEntry->PointerToRawData >> MMSECTOR_SHIFT;
  716. EndingSector =
  717. (SectionTableEntry->PointerToRawData +
  718. SectionVirtualSize);
  719. EndingSector = EndingSector >> MMSECTOR_SHIFT;
  720. ImageFileSize = SectionTableEntry->PointerToRawData +
  721. SectionTableEntry->SizeOfRawData;
  722. for (i = 0; i < PtesInSubsection; i++) {
  723. //
  724. // Set all the prototype PTEs to refer to the control section.
  725. //
  726. NextVa += PageSize;
  727. }
  728. SectionTableEntry += 1;
  729. NumberOfSubsections -= 1;
  730. }
  731. //
  732. // If the file size is not as big as the image claimed to be,
  733. // return an error.
  734. //
  735. if (ImageFileSize > FileInfo.nFileSizeLow) {
  736. //
  737. // Invalid image size.
  738. //
  739. LogPrintf("invalid image size - file size %lx - image size %lx\n",
  740. FileInfo.nFileSizeLow, ImageFileSize);
  741. ImageOk = FALSE;
  742. goto BadPeImageSegment;
  743. }
  744. {
  745. // Validate the debug information (as much as we can).
  746. PVOID ImageBase;
  747. ULONG DebugDirectorySize, NumberOfDebugDirectories, i;
  748. PIMAGE_DEBUG_DIRECTORY DebugDirectory;
  749. ImageBase = (PVOID) DosHeader;
  750. DebugDirectory = (PIMAGE_DEBUG_DIRECTORY)
  751. ImageDirectoryEntryToData(
  752. ImageBase,
  753. FALSE,
  754. IMAGE_DIRECTORY_ENTRY_DEBUG,
  755. &DebugDirectorySize );
  756. if (!DebugDirectoryIsUseful(DebugDirectory, DebugDirectorySize)) {
  757. // Not useful. Are they valid? (both s/b zero)
  758. if (DebugDirectory || DebugDirectorySize) {
  759. LogPrintf("Debug directory values [%x, %x] are invalid\n",
  760. DebugDirectory,
  761. DebugDirectorySize);
  762. ImageOk = FALSE;
  763. }
  764. goto DebugDirsDone;
  765. }
  766. NumberOfDebugDirectories = DebugDirectorySize / sizeof( IMAGE_DEBUG_DIRECTORY );
  767. for (i=0; i < NumberOfDebugDirectories; i++) {
  768. if (DebugDirectory->PointerToRawData > FileInfo.nFileSizeLow) {
  769. LogPrintf("Invalid debug directory entry[%d] - File Offset %x is beyond the end of the file\n",
  770. i,
  771. DebugDirectory->PointerToRawData
  772. );
  773. ImageOk = FALSE;
  774. goto BadPeImageSegment;
  775. }
  776. if ((DebugDirectory->PointerToRawData + DebugDirectory->SizeOfData) > FileInfo.nFileSizeLow) {
  777. LogPrintf("Invalid debug directory entry[%d] - File Offset (%X) + Size (%X) is beyond the end of the file (filesize: %X)\n",
  778. i,
  779. DebugDirectory->PointerToRawData,
  780. DebugDirectory->SizeOfData,
  781. FileInfo.nFileSizeLow
  782. );
  783. ImageOk = FALSE;
  784. goto BadPeImageSegment;
  785. }
  786. #if 0
  787. if (DebugDirectory->AddressOfRawData != 0) {
  788. if (DebugDirectory->AddressOfRawData > ImageFileSize){
  789. LogPrintf("Invalid debug directory entry[%d] - VA (%X) is beyond the end of the image VA (%X)\n",
  790. i,
  791. DebugDirectory->AddressOfRawData,
  792. ImageFileSize);
  793. ImageOk = FALSE;
  794. goto BadPeImageSegment;
  795. }
  796. if ((DebugDirectory->AddressOfRawData + DebugDirectory->SizeOfData )> ImageFileSize){
  797. LogPrintf("Invalid debug directory entry[%d] - VA (%X) + size (%X) is beyond the end of the image VA (%X)\n",
  798. i,
  799. DebugDirectory->AddressOfRawData,
  800. DebugDirectory->SizeOfData,
  801. ImageFileSize);
  802. ImageOk = FALSE;
  803. goto BadPeImageSegment;
  804. }
  805. }
  806. #endif
  807. if (DebugDirectory->Type <= 0x7fffffff) {
  808. switch (DebugDirectory->Type) {
  809. case IMAGE_DEBUG_TYPE_MISC:
  810. {
  811. PIMAGE_DEBUG_MISC pDebugMisc;
  812. // MISC should point to an IMAGE_DEBUG_MISC structure
  813. pDebugMisc = (PIMAGE_DEBUG_MISC)((PCHAR)ImageBase + DebugDirectory->PointerToRawData);
  814. if (pDebugMisc->DataType != IMAGE_DEBUG_MISC_EXENAME) {
  815. LogPrintf("MISC Debug has an invalid DataType\n");
  816. ImageOk = FALSE;
  817. goto BadPeImageSegment;
  818. }
  819. if (pDebugMisc->Length != DebugDirectory->SizeOfData) {
  820. LogPrintf("MISC Debug has an invalid size.\n");
  821. ImageOk = FALSE;
  822. goto BadPeImageSegment;
  823. }
  824. if (!pDebugMisc->Unicode) {
  825. i= 0;
  826. while (i < pDebugMisc->Length - sizeof(IMAGE_DEBUG_MISC)) {
  827. if (!isprint(pDebugMisc->Data[i]) &&
  828. (pDebugMisc->Data[i] != '\0') )
  829. {
  830. LogPrintf("MISC Debug has unprintable characters... Possibly corrupt\n");
  831. ImageOk = FALSE;
  832. goto BadPeImageSegment;
  833. }
  834. i++;
  835. }
  836. // The data must be a null terminated string.
  837. if (strlen(pDebugMisc->Data) > (pDebugMisc->Length - sizeof(IMAGE_DEBUG_MISC))) {
  838. LogPrintf("MISC Debug has invalid data... Possibly corrupt\n");
  839. ImageOk = FALSE;
  840. goto BadPeImageSegment;
  841. }
  842. }
  843. }
  844. break;
  845. case IMAGE_DEBUG_TYPE_CODEVIEW:
  846. // CV will point to either a NB09 or an NB10 signature. Make sure it does.
  847. {
  848. OMFSignature * CVDebug;
  849. CVDebug = (OMFSignature *)((PCHAR)ImageBase + DebugDirectory->PointerToRawData);
  850. if (((*(PULONG)(CVDebug->Signature)) != '90BN') &&
  851. ((*(PULONG)(CVDebug->Signature)) != '01BN') &&
  852. ((*(PULONG)(CVDebug->Signature)) != 'SDSR'))
  853. {
  854. LogPrintf("CV Debug has an invalid signature\n");
  855. ImageOk = FALSE;
  856. goto BadPeImageSegment;
  857. }
  858. }
  859. break;
  860. case IMAGE_DEBUG_TYPE_COFF:
  861. case IMAGE_DEBUG_TYPE_FPO:
  862. case IMAGE_DEBUG_TYPE_EXCEPTION:
  863. case IMAGE_DEBUG_TYPE_FIXUP:
  864. case IMAGE_DEBUG_TYPE_OMAP_TO_SRC:
  865. case IMAGE_DEBUG_TYPE_OMAP_FROM_SRC:
  866. // Not much we can do about these now.
  867. break;
  868. default:
  869. LogPrintf("Invalid debug directory type: %d\n", DebugDirectory->Type);
  870. ImageOk = FALSE;
  871. goto BadPeImageSegment;
  872. break;
  873. }
  874. }
  875. }
  876. }
  877. DebugDirsDone:
  878. //
  879. // The total number of PTEs was decremented as sections were built,
  880. // make sure that there are less than 64ks worth at this point.
  881. //
  882. if (NumberOfPtes >= (ImageAlignment >> PageShift)) {
  883. //
  884. // Inconsistent image, size does not agree with object tables.
  885. //
  886. LogPrintf("invalid image - PTEs left %lx\n",
  887. NumberOfPtes);
  888. ImageOk = FALSE;
  889. goto BadPeImageSegment;
  890. }
  891. //
  892. // check checksum.
  893. //
  894. PeReturnSuccess:
  895. if (NtHeader->OptionalHeader.CheckSum == 0) {
  896. LogPrintf("(checksum is zero)\n");
  897. } else {
  898. __try {
  899. if (lpOldLdrVerifyImageMatchesChecksum == NULL) {
  900. if (lpNewLdrVerifyImageMatchesChecksum == NULL) {
  901. Status = STATUS_SUCCESS;
  902. LogPrintf("Unable to validate checksum\n");
  903. } else {
  904. Status = (*lpNewLdrVerifyImageMatchesChecksum)(File, NULL, NULL, NULL);
  905. }
  906. } else {
  907. Status = (*lpOldLdrVerifyImageMatchesChecksum)(File);
  908. }
  909. if (NT_ERROR(Status)) {
  910. LogPrintf("checksum mismatch\n");
  911. ImageOk = FALSE;
  912. }
  913. } __except (EXCEPTION_EXECUTE_HANDLER) {
  914. ImageOk = FALSE;
  915. LogPrintf("checksum mismatch\n");
  916. }
  917. }
  918. if (fHasPdata && ImageOk) {
  919. ImageOk = ValidatePdata(DosHeader);
  920. }
  921. if (ImageOk) {
  922. ImageOk = VerifyVersionResource(ImageName, ImageNeedsOleSelfRegister(DosHeader));
  923. }
  924. //
  925. // sanity test for symbols
  926. // basically : if this does not work, debugging probably will not either
  927. // these high-level debugging api's will also call a pdb validation routine
  928. //
  929. if(ArgFlag & ArgFlag_SymCK)
  930. {
  931. HANDLE hProcess = 0;
  932. char Target[MAX_PATH] = {0};
  933. char drive[_MAX_DRIVE];
  934. char dir[_MAX_DIR];
  935. char fname[_MAX_FNAME];
  936. char ext[_MAX_EXT];
  937. IMAGEHLP_MODULE64 ModuleInfo = {0};
  938. PSYMMODLIST ModList = 0;
  939. void *vpAddr;
  940. PLOADED_IMAGE pLImage = NULL;
  941. DWORD64 symLMflag;
  942. strcpy(Target, szDirectory);
  943. strcat(Target, "\\");
  944. strcat(Target, ImageName);
  945. //
  946. // set up for debugging
  947. //
  948. hProcess = GetCurrentProcess();
  949. if(!SymInitialize(hProcess, szSympath, FALSE))
  950. {
  951. LogPrintf("ERROR:SymInitialize failed!\n");
  952. hProcess = 0;
  953. goto symckend;
  954. }
  955. //
  956. // attempt to use symbols
  957. //
  958. _splitpath(Target, drive, dir, fname, ext );
  959. symLMflag = SymLoadModule64(hProcess, NULL, Target, fname, 0, 0);
  960. if(!symLMflag)
  961. {
  962. LogPrintf("ERROR:SymLoadModule failed! last error:0x%x\n", GetLastError());
  963. goto symckend;
  964. }
  965. //
  966. // identify module type
  967. // find module, symgetmoduleinfo, check dbg type
  968. //
  969. ModuleInfo.SizeOfStruct = sizeof(ModuleInfo);
  970. ModList = MakeModList(hProcess);
  971. vpAddr = GetModAddrFromName(ModList, fname);
  972. if(!SymGetModuleInfo64(hProcess, (DWORD64)vpAddr, &ModuleInfo))
  973. {
  974. LogPrintf("ERROR:SymGetModuleInfo failed! last error:0x%x\n", GetLastError());
  975. goto symckend;
  976. }
  977. if(ModuleInfo.SymType != SymPdb)
  978. {
  979. LogPrintf("WARNING: No pdb info for file!\n");
  980. switch(ModuleInfo.SymType){
  981. case SymNone:
  982. LogPrintf("symtype: SymNone\n");
  983. break;
  984. case SymCoff:
  985. LogPrintf("symtype: SymCoff\n");
  986. break;
  987. case SymCv:
  988. LogPrintf("symtype: SymCv\n");
  989. break;
  990. case SymPdb:
  991. LogPrintf("symtype: SymPdb\n");
  992. break;
  993. case SymExport:
  994. LogPrintf("symtype: SymExport\n");
  995. break;
  996. case SymDeferred:
  997. LogPrintf("symtype: SymDeferred\n");
  998. break;
  999. case SymSym:
  1000. LogPrintf("symtype: SymSym\n");
  1001. break;
  1002. }
  1003. }
  1004. //
  1005. // get image, symbol checksum, compare
  1006. //
  1007. pLImage = ImageLoad(Target, NULL);
  1008. {
  1009. CHAR szDbgPath[_MAX_PATH];
  1010. HANDLE DbgFileHandle;
  1011. DbgFileHandle = FindDebugInfoFile(Target, szSympath, szDbgPath);
  1012. if (DbgFileHandle != INVALID_HANDLE_VALUE) {
  1013. IMAGE_SEPARATE_DEBUG_HEADER DbgHeader;
  1014. DWORD BytesRead;
  1015. BOOL ReadSuccess;
  1016. SetFilePointer(DbgFileHandle, 0, 0, FILE_BEGIN);
  1017. ReadSuccess = ReadFile(DbgFileHandle, &DbgHeader, sizeof(DbgHeader), &BytesRead, NULL);
  1018. if (ReadSuccess && (BytesRead == sizeof(DbgHeader))) {
  1019. // Got enough to check if it's a valid dbg file.
  1020. if(((PIMAGE_NT_HEADERS)pLImage->FileHeader)->OptionalHeader.CheckSum != DbgHeader.CheckSum) {
  1021. LogPrintf("ERROR! image / debug file checksum not equal\n");
  1022. ImageOk = FALSE;
  1023. }
  1024. }
  1025. CloseHandle(DbgFileHandle);
  1026. }
  1027. }
  1028. //
  1029. // cleanup
  1030. //
  1031. symckend:
  1032. if(ModList)
  1033. {
  1034. FreeModList(ModList);
  1035. }
  1036. if(pLImage)
  1037. {
  1038. ImageUnload(pLImage);
  1039. }
  1040. if(symLMflag)
  1041. {
  1042. SymUnloadModule64(hProcess, (DWORD)symLMflag);
  1043. }
  1044. if(hProcess)
  1045. {
  1046. SymCleanup(hProcess);
  1047. }
  1048. }
  1049. NextImage:
  1050. BadPeImageSegment:
  1051. NeImage:
  1052. if ( ImageOk && (ArgFlag & ArgFlag_OK)) {
  1053. if (MachineTypeMismatch) {
  1054. LogPrintf(" OK [%s]\n", MachineType);
  1055. } else {
  1056. LogPrintf(" OK\n");
  1057. }
  1058. }
  1059. //
  1060. // print out results
  1061. //
  1062. if (ImageOk)
  1063. {
  1064. LogOutAndClean((ArgFlag & ArgFlag_OK) ? TRUE : FALSE);
  1065. } else {
  1066. LogOutAndClean(TRUE);
  1067. }
  1068. if ( File != INVALID_HANDLE_VALUE ) {
  1069. CloseHandle(File);
  1070. }
  1071. if ( DosHeader ) {
  1072. UnmapViewOfFile(DosHeader);
  1073. }
  1074. }
  1075. NTSTATUS
  1076. MiVerifyImageHeader (
  1077. IN PIMAGE_NT_HEADERS NtHeader,
  1078. IN PIMAGE_DOS_HEADER DosHeader,
  1079. IN ULONG NtHeaderSize
  1080. )
  1081. /*++
  1082. Routine Description:
  1083. Checks image header for consistency.
  1084. Arguments:
  1085. IN PIMAGE_NT_HEADERS NtHeader
  1086. IN PIMAGE_DOS_HEADER DosHeader
  1087. IN ULONG NtHeaderSize
  1088. Return Value:
  1089. Returns the status value.
  1090. TBS
  1091. --*/
  1092. {
  1093. if ((NtHeader->FileHeader.Machine == 0) &&
  1094. (NtHeader->FileHeader.SizeOfOptionalHeader == 0)) {
  1095. //
  1096. // This is a bogus DOS app which has a 32-bit portion
  1097. // mascarading as a PE image.
  1098. //
  1099. LogPrintf("Image machine type and size of optional header bad\n");
  1100. return STATUS_INVALID_IMAGE_PROTECT;
  1101. }
  1102. if (!(NtHeader->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE)) {
  1103. LogPrintf("Characteristics not image file executable\n");
  1104. return STATUS_INVALID_IMAGE_FORMAT;
  1105. }
  1106. #ifdef i386
  1107. //
  1108. // Make sure the image header is aligned on a Long word boundary.
  1109. //
  1110. if (((ULONG)NtHeader & 3) != 0) {
  1111. LogPrintf("NtHeader is not aligned on longword boundary\n");
  1112. return STATUS_INVALID_IMAGE_FORMAT;
  1113. }
  1114. #endif
  1115. // Non-driver code must have file alignment set to a multiple of 512
  1116. if (((NtHeader->OptionalHeader.FileAlignment & 511) != 0) &&
  1117. (NtHeader->OptionalHeader.FileAlignment !=
  1118. NtHeader->OptionalHeader.SectionAlignment)) {
  1119. LogPrintf("file alignment is not multiple of 512 and power of 2\n");
  1120. return STATUS_INVALID_IMAGE_FORMAT;
  1121. }
  1122. //
  1123. // File aligment must be power of 2.
  1124. //
  1125. if ((((NtHeader->OptionalHeader.FileAlignment << 1) - 1) &
  1126. NtHeader->OptionalHeader.FileAlignment) !=
  1127. NtHeader->OptionalHeader.FileAlignment) {
  1128. LogPrintf("file alignment not power of 2\n");
  1129. return STATUS_INVALID_IMAGE_FORMAT;
  1130. }
  1131. if (NtHeader->OptionalHeader.SectionAlignment < NtHeader->OptionalHeader.FileAlignment) {
  1132. LogPrintf("SectionAlignment < FileAlignment\n");
  1133. return STATUS_INVALID_IMAGE_FORMAT;
  1134. }
  1135. if (NtHeader->OptionalHeader.SizeOfImage > MM_SIZE_OF_LARGEST_IMAGE) {
  1136. LogPrintf("Image too big %lx\n",NtHeader->OptionalHeader.SizeOfImage);
  1137. return STATUS_INVALID_IMAGE_FORMAT;
  1138. }
  1139. if (NtHeader->FileHeader.NumberOfSections > MM_MAXIMUM_IMAGE_SECTIONS) {
  1140. LogPrintf("Too many image sections %ld.\n",
  1141. NtHeader->FileHeader.NumberOfSections);
  1142. return STATUS_INVALID_IMAGE_FORMAT;
  1143. }
  1144. if (ArgFlag & ArgFlag_CKBase) {
  1145. if ((PVOID)NtHeader->OptionalHeader.ImageBase >= HighestUserAddress) {
  1146. LogPrintf("Image base (%lx) is invalid on this machine\n",
  1147. NtHeader->OptionalHeader.ImageBase);
  1148. return STATUS_SUCCESS;
  1149. }
  1150. }
  1151. return STATUS_SUCCESS;
  1152. }
  1153. VOID
  1154. ParseArgs(
  1155. int *pargc,
  1156. char **argv
  1157. )
  1158. /*++
  1159. Routine Description:
  1160. parse arguments to this program
  1161. Arguments:
  1162. int *pargc
  1163. char **argv
  1164. Return Value:
  1165. none
  1166. Notes:
  1167. command line args:
  1168. (original)
  1169. case '?': call usage and exit
  1170. case 'b': check whether base address of image is in user space for this machine
  1171. case 's': /s <sympath> check symbols
  1172. case 'p': PE Errors only
  1173. case 'r': recurse subdirectories
  1174. (new)
  1175. case 'v': verbose - output "OK"
  1176. case 'o': output "OleSelfRegister not set"
  1177. --*/
  1178. {
  1179. CHAR cswitch, c, *p;
  1180. CHAR sztmp[MAX_PATH];
  1181. int argnum = 1, i=0, len=0, count=0;
  1182. BOOL fslashfound = FALSE;
  1183. //
  1184. // set default flags here
  1185. //
  1186. ArgFlag |= ArgFlag_CKBase;
  1187. while ( argnum < *pargc ) {
  1188. _strlwr(argv[argnum]);
  1189. cswitch = *(argv[argnum]);
  1190. if (cswitch == '/' || cswitch == '-') {
  1191. c = *(argv[argnum]+1);
  1192. switch (c) {
  1193. case 'o':
  1194. ArgFlag |= ArgFlag_OLESelf;
  1195. break;
  1196. case 'v':
  1197. ArgFlag |= ArgFlag_OK | ArgFlag_CKMZ | ArgFlag_OLESelf;
  1198. break;
  1199. case '?':
  1200. Usage();
  1201. break;
  1202. case 'b':
  1203. ArgFlag ^= ArgFlag_CKBase;
  1204. break;
  1205. case 's':
  1206. if (argv[argnum+1]) {
  1207. strcpy(szSympath, (argv[argnum+1]));
  1208. ArgFlag |= ArgFlag_SymCK;
  1209. argnum++;
  1210. }
  1211. break;
  1212. case 'p':
  1213. ArgFlag |= ArgFlag_CKMZ;
  1214. break;
  1215. case 'r':
  1216. fRecurse = TRUE;
  1217. if (argv[argnum+1]) {
  1218. fPathOverride=TRUE;
  1219. strcpy(szDirectory, (argv[argnum+1]));
  1220. if (!(strcmp(szDirectory, "\\"))) { // if just '\'
  1221. fSingleSlash=TRUE;
  1222. }
  1223. //LogPrintf("dir %s\n", szDirectory);
  1224. argnum++;
  1225. }
  1226. break;
  1227. default:
  1228. fprintf(stderr, "Invalid argument.\n");
  1229. Usage();
  1230. }
  1231. } else {
  1232. // Check for path\filename or wildcards
  1233. // Search for '\' in string
  1234. strcpy(sztmp, (argv[argnum]));
  1235. len = strlen(sztmp);
  1236. for (i=0; i < len; i++) {
  1237. if (sztmp[i]=='\\') {
  1238. count++;
  1239. endpath=i; // mark last '\' char found
  1240. fslashfound=TRUE; // found backslash, so must be a path\filename combination
  1241. }
  1242. }
  1243. if (fslashfound && !fRecurse) { // if backslash found and not a recursive operation
  1244. // seperate the directory and filename into two strings
  1245. fPathOverride=TRUE;
  1246. strcpy(szDirectory, sztmp);
  1247. if (!(strcmp(szDirectory, "\\"))) {
  1248. Usage();
  1249. }
  1250. szFileName = _strdup(&(sztmp[endpath+1]));
  1251. if (count == 1) { //&& szDirectory[1] == ':') { // if only one '\' char and drive letter indicated
  1252. fSingleSlash=TRUE;
  1253. szDirectory[endpath+1]='\0'; // keep trailing '\' in order to chdir properly
  1254. } else {
  1255. szDirectory[endpath]='\0';
  1256. }
  1257. if (szFileName[0] == '*' && szFileName[1] == '.' && szFileName[2] != '*') {
  1258. _strlwr(szFileName);
  1259. szPattern = strchr(szFileName, '.'); //search for '.'
  1260. fPattern = TRUE;
  1261. }
  1262. } else { // no backslash found, assume filename without preceeding path
  1263. szFileName = _strdup(argv[argnum]);
  1264. if (!szFileName) {
  1265. // drastic error, just bail
  1266. szFileName = "";
  1267. return;
  1268. }
  1269. //
  1270. // filename or wildcard
  1271. //
  1272. if ( (*(argv[argnum]) == '*') && (*(argv[argnum]+1) == '.') && (*(argv[argnum]+2) != '*') ){
  1273. // *.xxx
  1274. _strlwr(szFileName);
  1275. szPattern = strchr(szFileName, '.'); //search for '.'
  1276. fPattern = TRUE;
  1277. } else if ( (*(argv[argnum]) == '*') && (*(argv[argnum]+1) == '.') && (*(argv[argnum]+2) == '*') ) {
  1278. // *.*
  1279. } else {
  1280. // probably a single filename
  1281. _strlwr(szFileName);
  1282. fSingleFile = TRUE;
  1283. }
  1284. if (fRecurse && strchr(szFileName, '\\') ) { // don't want path\filename when recursing
  1285. Usage();
  1286. }
  1287. }
  1288. //fprintf(stdout, "dir %s\nfile %s\n", szDirectory, szFileName);
  1289. }
  1290. ++argnum;
  1291. }
  1292. if (szFileName[0] == '\0') {
  1293. Usage();
  1294. }
  1295. } // parseargs
  1296. int
  1297. __cdecl
  1298. CompFileAndDir(
  1299. const void *elem1,
  1300. const void *elem2
  1301. )
  1302. /*++
  1303. Routine Description:
  1304. Purpose: a comparision routine passed to QSort. It compares elem1 and elem2
  1305. based upon their attribute, i.e., is it a file or directory.
  1306. Arguments:
  1307. const void *elem1,
  1308. const void *elem2
  1309. Return Value:
  1310. result of comparison function
  1311. Notes:
  1312. --*/
  1313. {
  1314. pList p1, p2;
  1315. // qsort passes a void universal pointer. Use a typecast (List**)
  1316. // so the compiler recognizes the data as a List structure.
  1317. // Typecast pointer-to-pointer-to-List and dereference ONCE
  1318. // leaving a pList. I don't dereference the remaining pointer
  1319. // in the p1 and p2 definitions to avoid copying the structure.
  1320. p1 = (*(List**)elem1);
  1321. p2 = (*(List**)elem2);
  1322. if ( (p1->Attributes & FILE_ATTRIBUTE_DIRECTORY) && (p2->Attributes & FILE_ATTRIBUTE_DIRECTORY))
  1323. return 0;
  1324. //both dirs
  1325. if (!(p1->Attributes & FILE_ATTRIBUTE_DIRECTORY) && !(p2->Attributes & FILE_ATTRIBUTE_DIRECTORY))
  1326. return 0;
  1327. //both files
  1328. if ( (p1->Attributes & FILE_ATTRIBUTE_DIRECTORY) && !(p2->Attributes & FILE_ATTRIBUTE_DIRECTORY))
  1329. return 1;
  1330. // elem1 is dir and elem2 is file
  1331. if (!(p1->Attributes & FILE_ATTRIBUTE_DIRECTORY) && (p2->Attributes & FILE_ATTRIBUTE_DIRECTORY))
  1332. return -1;
  1333. // elem1 is file and elem2 is dir
  1334. return 0; // if none of the above
  1335. }
  1336. int
  1337. __cdecl
  1338. CompName(
  1339. const void *elem1,
  1340. const void *elem2
  1341. )
  1342. /*++
  1343. Routine Description:
  1344. another compare routine passed to QSort that compares the two Name strings
  1345. Arguments:
  1346. const void *elem1,
  1347. const void *elem2
  1348. Return Value:
  1349. result of comparison function
  1350. Notes:
  1351. this uses a noignore-case strcmp
  1352. --*/
  1353. {
  1354. return strcmp( (*(List**)elem1)->Name, (*(List**)elem2)->Name );
  1355. }
  1356. VOID
  1357. Usage(
  1358. VOID
  1359. )
  1360. /*++
  1361. Routine Description:
  1362. Arguments:
  1363. Return Value:
  1364. Notes:
  1365. --*/
  1366. {
  1367. fputs("Usage: imagechk [/?] displays this message\n"
  1368. " [/r dir] recurse from directory dir\n"
  1369. " [/b] don't check image base address\n"
  1370. " [/v] verbose - output everything\n"
  1371. " [/o] output \"OleSelfRegister not set\" warning\n"
  1372. " [/p] output \"MZ header not found\"\n"
  1373. " [/s <sympath>] check pdb symbols\n"
  1374. " [filename] file to check\n"
  1375. " Accepts wildcard extensions such as *.exe\n"
  1376. " imagechk /r . \"*.exe\" check all *.exe recursing on current directory\n"
  1377. " imagechk /r \\ \"*.exe\" check all *.exe recursing from root of current drive\n"
  1378. " imagechk \"*.exe\" check all *.exe in current directory\n"
  1379. " imagechk c:\\bar.exe check c:\\bar.exe only\n",
  1380. stderr);
  1381. exit(1);
  1382. }
  1383. int
  1384. __cdecl
  1385. _cwild()
  1386. /*++
  1387. Routine Description:
  1388. Arguments:
  1389. Return Value:
  1390. Notes:
  1391. --*/
  1392. {
  1393. return(0);
  1394. }
  1395. typedef DWORD (WINAPI *PFNGVS)(LPSTR, LPDWORD);
  1396. typedef BOOL (WINAPI *PFNGVI)(LPTSTR, DWORD, DWORD, LPVOID);
  1397. typedef BOOL (WINAPI *PFNVQV)(const LPVOID, LPTSTR, LPVOID *, PUINT);
  1398. BOOL
  1399. VerifyVersionResource(
  1400. PCHAR FileName,
  1401. BOOL fSelfRegister
  1402. )
  1403. /*++
  1404. Routine Description:
  1405. validate the version resource in a file
  1406. Arguments:
  1407. PCHAR FileName
  1408. BOOL fSelfRegister
  1409. Return Value:
  1410. TRUE if: no version.dll found
  1411. FALSE if: version resource missing
  1412. Notes:
  1413. --*/
  1414. {
  1415. static HINSTANCE hVersion = NULL;
  1416. static PFNGVS pfnGetFileVersionInfoSize = NULL;
  1417. static PFNGVI pfnGetFileVersionInfo = NULL;
  1418. static PFNVQV pfnVerQueryValue = NULL;
  1419. DWORD dwSize;
  1420. DWORD lpInfoSize;
  1421. LPVOID lpData = NULL, lpInfo;
  1422. BOOL rc = FALSE;
  1423. DWORD dwDefLang = 0x00000409;
  1424. DWORD *pdwTranslation, uLen;
  1425. CHAR buf[60];
  1426. CHAR szVersionDll[_MAX_PATH];
  1427. if (GetSystemDirectory(szVersionDll, sizeof(szVersionDll))) {
  1428. strcat(szVersionDll, "\\version.dll");
  1429. } else {
  1430. strcpy(szVersionDll, "version.dll");
  1431. }
  1432. if (!hVersion) {
  1433. hVersion = LoadLibraryA(szVersionDll);
  1434. if (hVersion == NULL) {
  1435. return TRUE;
  1436. }
  1437. pfnGetFileVersionInfoSize = (PFNGVS) GetProcAddress(hVersion, "GetFileVersionInfoSizeA");
  1438. pfnGetFileVersionInfo = (PFNGVI) GetProcAddress(hVersion, "GetFileVersionInfoA");
  1439. pfnVerQueryValue = (PFNVQV) GetProcAddress(hVersion, "VerQueryValueA");
  1440. }
  1441. if (!pfnGetFileVersionInfoSize || !pfnGetFileVersionInfo || !pfnVerQueryValue) {
  1442. rc = TRUE;
  1443. goto cleanup;
  1444. }
  1445. if ((dwSize = (*pfnGetFileVersionInfoSize)(FileName, &dwSize)) == 0){
  1446. LogPrintf("No version resource detected\n");
  1447. goto cleanup;
  1448. }
  1449. if (!fSelfRegister) {
  1450. // All we need to do is see if the version resource exists. Ole Self Register not necessary.
  1451. rc = TRUE;
  1452. goto cleanup;
  1453. }
  1454. if ((lpData = malloc(dwSize)) == NULL) {
  1455. LogPrintf("Out of memory\n");
  1456. goto cleanup;
  1457. }
  1458. if (!(*pfnGetFileVersionInfo)(FileName, 0, dwSize, lpData)) {
  1459. LogPrintf("Unable to read version info\n - %d", GetLastError());
  1460. goto cleanup;
  1461. }
  1462. if(!pfnVerQueryValue(lpData, "\\VarFileInfo\\Translation", &pdwTranslation, &uLen)) {
  1463. pdwTranslation = &dwDefLang;
  1464. uLen = sizeof(DWORD);
  1465. }
  1466. sprintf(buf, "\\StringFileInfo\\%04x%04x\\OleSelfRegister", LOWORD(*pdwTranslation), HIWORD(*pdwTranslation));
  1467. if (!pfnVerQueryValue(lpData, buf, &lpInfo, &lpInfoSize) && (ArgFlag & ArgFlag_OLESelf )) {
  1468. LogPrintf("OleSelfRegister not set\n");
  1469. } else {
  1470. rc = TRUE;
  1471. }
  1472. cleanup:
  1473. if (lpData) {
  1474. free(lpData);
  1475. }
  1476. // No need to free the hVersion
  1477. return(rc);
  1478. }
  1479. BOOL
  1480. ValidatePdata(
  1481. PIMAGE_DOS_HEADER DosHeader
  1482. )
  1483. /*++
  1484. Routine Description:
  1485. validates the PIMAGE_RUNTIME_FUNCTION_ENTRY in the executable
  1486. Arguments:
  1487. PIMAGE_DOS_HEADER DosHeader
  1488. Return Value:
  1489. TRUE if:
  1490. FALSE if: no exception data
  1491. exception table size incorrect
  1492. exception table corrupt
  1493. Notes:
  1494. --*/
  1495. {
  1496. // The machine type indicates this image should have pdata (an exception table).
  1497. // Ensure it looks reasonable.
  1498. // Todo: Add a range check for exception handler and data
  1499. PIMAGE_RUNTIME_FUNCTION_ENTRY ExceptionTable;
  1500. DWORD ExceptionTableSize, i;
  1501. DWORD_PTR LastEnd;
  1502. BOOL fRc;
  1503. PIMAGE_NT_HEADERS NtHeader = (PIMAGE_NT_HEADERS)((PCHAR)DosHeader + (ULONG)DosHeader->e_lfanew);
  1504. ULONG_PTR ImageBase = NtHeader->OptionalHeader.ImageBase;
  1505. DWORD PDataStart = NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress;
  1506. DWORD PDataSize = NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size;
  1507. ExceptionTable = (PIMAGE_RUNTIME_FUNCTION_ENTRY)
  1508. ImageDirectoryEntryToData(
  1509. DosHeader,
  1510. FALSE,
  1511. IMAGE_DIRECTORY_ENTRY_EXCEPTION,
  1512. &ExceptionTableSize );
  1513. if (!ExceptionTable ||
  1514. (ExceptionTable && (ExceptionTableSize == 0)))
  1515. {
  1516. // No Exception table.
  1517. return(TRUE);
  1518. }
  1519. if (ExceptionTableSize % sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY)) {
  1520. // The size isn't an even multiple.
  1521. LogPrintf("exception table size is not correct\n");
  1522. return(FALSE);
  1523. }
  1524. LastEnd = 0;
  1525. fRc = TRUE;
  1526. for (i=0; i < (ExceptionTableSize / sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY)); i++) {
  1527. if (!ExceptionTable[i].BeginAddress) {
  1528. if (fRc != FALSE) {
  1529. LogPrintf("exception table is corrupt.\n");
  1530. }
  1531. LogPrintf("PDATA Entry[%d]: zero value for BeginAddress\n",
  1532. i);
  1533. fRc = FALSE;
  1534. }
  1535. if (!ExceptionTable[i].EndAddress) {
  1536. if (fRc != FALSE) {
  1537. LogPrintf("exception table is corrupt.\n");
  1538. }
  1539. LogPrintf("PDATA Entry[%d]: zero value for EndAddress\n",
  1540. i);
  1541. fRc = FALSE;
  1542. }
  1543. #if defined(_IA64_)
  1544. if (!ExceptionTable[i].UnwindInfoAddress) {
  1545. if (fRc != FALSE) {
  1546. LogPrintf("exception table is corrupt.\n");
  1547. }
  1548. LogPrintf("PDATA Entry[%d]: zero value for UnwindInfoAddress\n",
  1549. i);
  1550. fRc = FALSE;
  1551. }
  1552. #elif defined(_ALPHA_) || defined(_AXP64_)
  1553. if (!ExceptionTable[i].PrologEndAddress) {
  1554. if (fRc != FALSE) {
  1555. LogPrintf("exception table is corrupt.\n");
  1556. }
  1557. LogPrintf("PDATA Entry[%d]: zero value for PrologEndAddress\n",
  1558. i);
  1559. fRc = FALSE;
  1560. }
  1561. #endif // defined(_IA64_)
  1562. if (ExceptionTable[i].BeginAddress < LastEnd) {
  1563. if (fRc != FALSE) {
  1564. LogPrintf("exception table is corrupt.\n");
  1565. }
  1566. LogPrintf("PDATA Entry[%d]: the begin address [%8.8x] is out of sequence. Prior end was [%8.8x]\n",
  1567. i,
  1568. ExceptionTable[i].BeginAddress,
  1569. LastEnd);
  1570. fRc = FALSE;
  1571. }
  1572. if (ExceptionTable[i].EndAddress < ExceptionTable[i].BeginAddress) {
  1573. if (fRc != FALSE) {
  1574. LogPrintf("exception table is corrupt.\n");
  1575. }
  1576. LogPrintf("PDATA Entry[%d]: the end address [%8.8x] is before the begin address[%8.8X]\n",
  1577. i,
  1578. ExceptionTable[i].EndAddress,
  1579. ExceptionTable[i].BeginAddress);
  1580. fRc = FALSE;
  1581. }
  1582. #if defined(_ALPHA_) || defined(_AXP64_)
  1583. if (!((ExceptionTable[i].PrologEndAddress >= ExceptionTable[i].BeginAddress) &&
  1584. (ExceptionTable[i].PrologEndAddress <= ExceptionTable[i].EndAddress)))
  1585. {
  1586. if (NtHeader->FileHeader.Machine == IMAGE_FILE_MACHINE_ALPHA) {
  1587. // Change this test. On Alpha, the PrologEndAddress is allowed to be
  1588. // outside the Function Start/End range. If this is true, the PrologEnd
  1589. // - ImageBase - pdata section VA s/b divisible by sizeof IMAGE_RUNTIME_FUNCTION_ENTRY
  1590. // AND within the bounds of the PdataSize. It's supposed to be an index into the
  1591. // pdata data that descibes the real scoping function.
  1592. LONG PrologAddress;
  1593. PrologAddress = (LONG) (ExceptionTable[i].PrologEndAddress - ImageBase - PDataStart);
  1594. if (PrologAddress % sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY)) {
  1595. if (fRc != FALSE) {
  1596. LogPrintf("exception table is corrupt.\n");
  1597. }
  1598. LogPrintf("PDATA Entry[%d]: the secondary prolog end address[%8.8x] does not evenly index into the exception table.\n",
  1599. i,
  1600. ExceptionTable[i].PrologEndAddress,
  1601. ExceptionTable[i].BeginAddress,
  1602. ExceptionTable[i].EndAddress
  1603. );
  1604. fRc = FALSE;
  1605. } else {
  1606. if ((PrologAddress < 0) || (PrologAddress > (LONG)(PDataStart - sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY)))) {
  1607. if (fRc != FALSE) {
  1608. LogPrintf("exception table is corrupt.\n");
  1609. }
  1610. LogPrintf("PDATA Entry[%d]: the secondary prolog end address[%8.8x] does not index into the exception table.\n",
  1611. i,
  1612. ExceptionTable[i].PrologEndAddress,
  1613. ExceptionTable[i].BeginAddress,
  1614. ExceptionTable[i].EndAddress
  1615. );
  1616. fRc = FALSE;
  1617. }
  1618. }
  1619. } else {
  1620. if (fRc != FALSE) {
  1621. LogPrintf("exception table is corrupt.\n");
  1622. }
  1623. LogPrintf("PDATA Entry[%d]: the prolog end address[%8.8x] is not within the bounds of the frame [%8.8X] - [%8.8X]\n",
  1624. i,
  1625. ExceptionTable[i].PrologEndAddress,
  1626. ExceptionTable[i].BeginAddress,
  1627. ExceptionTable[i].EndAddress
  1628. );
  1629. fRc = FALSE;
  1630. }
  1631. }
  1632. #endif // !defined(_IA64_)
  1633. LastEnd = ExceptionTable[i].EndAddress;
  1634. }
  1635. return(fRc);
  1636. }
  1637. BOOL
  1638. ImageNeedsOleSelfRegister(
  1639. PIMAGE_DOS_HEADER DosHeader
  1640. )
  1641. /*++
  1642. Routine Description:
  1643. Arguments:
  1644. PIMAGE_DOS_HEADER DosHeader
  1645. Return Value:
  1646. TRUE if DllRegisterServer or DllUnRegisterServer is exported
  1647. --*/
  1648. {
  1649. PIMAGE_EXPORT_DIRECTORY ExportDirectory;
  1650. PIMAGE_NT_HEADERS NtHeader;
  1651. PIMAGE_SECTION_HEADER SectionHeader;
  1652. DWORD ExportDirectorySize, i;
  1653. USHORT x;
  1654. PCHAR rvaDelta;
  1655. PULONG NameTable;
  1656. ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)
  1657. ImageDirectoryEntryToData(
  1658. DosHeader,
  1659. FALSE,
  1660. IMAGE_DIRECTORY_ENTRY_EXPORT,
  1661. &ExportDirectorySize );
  1662. if (!ExportDirectory ||
  1663. !ExportDirectorySize ||
  1664. !ExportDirectory->NumberOfNames)
  1665. {
  1666. // No exports (no directory, no size, or no names).
  1667. return(FALSE);
  1668. }
  1669. // Walk the section headers and find the va/raw offsets.
  1670. NtHeader = (PIMAGE_NT_HEADERS)((PCHAR)DosHeader + (ULONG)DosHeader->e_lfanew);
  1671. SectionHeader = IMAGE_FIRST_SECTION(NtHeader);
  1672. for (x = 0; x < NtHeader->FileHeader.NumberOfSections; x++) {
  1673. if (((ULONG)((PCHAR)ExportDirectory - (PCHAR)DosHeader) >= SectionHeader->PointerToRawData) &&
  1674. ((ULONG)((PCHAR)ExportDirectory - (PCHAR)DosHeader) <
  1675. (SectionHeader->PointerToRawData + SectionHeader->SizeOfRawData))) {
  1676. break;
  1677. } else {
  1678. SectionHeader++;
  1679. }
  1680. }
  1681. if (x == NtHeader->FileHeader.NumberOfSections) {
  1682. // We didn't find the section that contained the export table. Assume it's not there.
  1683. return(FALSE);
  1684. }
  1685. rvaDelta = (PCHAR)DosHeader + SectionHeader->PointerToRawData - SectionHeader->VirtualAddress;
  1686. NameTable = (PULONG)(rvaDelta + ExportDirectory->AddressOfNames);
  1687. for (i = 0; i < ExportDirectory->NumberOfNames; i++) {
  1688. if (!strcmp("DllRegisterServer", rvaDelta + NameTable[i]) ||
  1689. !strcmp("DllUnRegisterServer", rvaDelta + NameTable[i]))
  1690. {
  1691. return(TRUE);
  1692. }
  1693. }
  1694. return(FALSE);
  1695. }
  1696. //
  1697. // support routines for symbol checker - could all
  1698. // be done without this using lower-level internal api's
  1699. //
  1700. PSYMMODLIST
  1701. MakeModList(
  1702. HANDLE hProcess
  1703. )
  1704. /*++
  1705. Routine Description:
  1706. build a list of loaded symbol modules and addresses
  1707. Arguments:
  1708. HANDLE hProcess
  1709. Return Value:
  1710. PSYMMODLIST
  1711. Notes:
  1712. --*/
  1713. {
  1714. PSYMMODLIST ModList;
  1715. ModList = (PSYMMODLIST)calloc(1, sizeof(SYMMODLIST));
  1716. SymEnumerateModules64(hProcess, SymEnumerateModulesCallback, ModList);
  1717. return(ModList);
  1718. }
  1719. BOOL
  1720. CALLBACK
  1721. SymEnumerateModulesCallback(
  1722. LPSTR ModuleName,
  1723. ULONG64 BaseOfDll,
  1724. PVOID UserContext
  1725. )
  1726. /*++
  1727. Routine Description:
  1728. callback routine for SymEnumerateModules
  1729. in this case, UserContext is a pointer to a head of a _SYMMODLIST struct
  1730. that will have a new item appended
  1731. We are avoiding global state for these lists so we can use several at once,
  1732. they will be short, so we will find the end each time we want to add
  1733. runs slower, simpler to maintain
  1734. Arguments:
  1735. LPSTR ModuleName
  1736. ULONG64 BaseOfDll
  1737. PVOID UserContext
  1738. Return Value:
  1739. TRUE
  1740. Notes:
  1741. --*/
  1742. {
  1743. PSYMMODLIST pSymModList;
  1744. //
  1745. // find end of list, key on pSymModList->ModBase
  1746. //
  1747. pSymModList = (PSYMMODLIST)UserContext;
  1748. while (pSymModList->ModBase)
  1749. {
  1750. pSymModList = pSymModList->Next;
  1751. }
  1752. //
  1753. // append entry
  1754. //
  1755. pSymModList->ModName = malloc(strlen(ModuleName) + 1);
  1756. if (!pSymModList->ModName)
  1757. return FALSE;
  1758. strcpy(pSymModList->ModName, ModuleName);
  1759. pSymModList->ModBase = (void *)BaseOfDll;
  1760. pSymModList->Next = (PSYMMODLIST)calloc(1, sizeof(SYMMODLIST));
  1761. return(TRUE);
  1762. }
  1763. void *
  1764. GetModAddrFromName(
  1765. PSYMMODLIST ModList,
  1766. char *ModName
  1767. )
  1768. /*++
  1769. Routine Description:
  1770. gets module address from a SYMMODLIST given module base name
  1771. Arguments:
  1772. PSYMMODLIST ModList
  1773. char * ModName
  1774. Return Value:
  1775. module address
  1776. --*/
  1777. {
  1778. while (ModList->Next != 0)
  1779. {
  1780. if (strcmp(ModList->ModName, ModName) == 0)
  1781. {
  1782. break;
  1783. }
  1784. ModList = ModList->Next;
  1785. }
  1786. return(ModList->ModBase);
  1787. }
  1788. void
  1789. FreeModList(
  1790. PSYMMODLIST ModList
  1791. )
  1792. /*++
  1793. Routine Description:
  1794. free a list of loaded symbol modules and addresses
  1795. Arguments:
  1796. PSYMMODLIST ModList
  1797. Return Value:
  1798. none
  1799. --*/
  1800. {
  1801. PSYMMODLIST ModListNext;
  1802. while (ModList)
  1803. {
  1804. if(ModList->ModName)
  1805. {
  1806. free(ModList->ModName);
  1807. }
  1808. ModListNext = ModList->Next;
  1809. free(ModList);
  1810. ModList = ModListNext;
  1811. }
  1812. }
  1813. pLogListItem LogAppend(
  1814. char *logitem,
  1815. pLogListItem plog
  1816. )
  1817. /*++
  1818. Routine Description:
  1819. add a log line to the linked list of log lines
  1820. Arguments:
  1821. char * logitem - a formatted line of text to be logged
  1822. pLogListItem plog - pointer to LogListItem
  1823. Return Value:
  1824. a pointer to the LogListItem allocated
  1825. the first call to this function should save this pointer and use
  1826. it for the head of the list, and it should be used when calling
  1827. LogOutAndClean() to print the list and free all the memory
  1828. you can call this with plog == head of list, or == to last item
  1829. if plog == 0, this means that the item being allocated is the head
  1830. of the list.
  1831. If plog == head of list, search for end of list
  1832. if plog == last item allocated, then the search is much faster
  1833. --*/
  1834. {
  1835. pLogListItem ptemp;
  1836. ptemp = plog;
  1837. if(plog)
  1838. {
  1839. while(ptemp->Next)
  1840. {
  1841. ptemp = ptemp->Next;
  1842. }
  1843. }
  1844. if(!ptemp)
  1845. {
  1846. ptemp = (pLogListItem)calloc(sizeof(LogListItem), 1);
  1847. if (!ptemp)
  1848. return NULL;
  1849. } else {
  1850. ptemp->Next = (pLogListItem)calloc(sizeof(LogListItem), 1);
  1851. if (!ptemp->Next)
  1852. return NULL;
  1853. ptemp = ptemp->Next;
  1854. }
  1855. ptemp->LogLine = (char *)malloc(strlen(logitem) + 1);
  1856. if (!ptemp->LogLine)
  1857. return NULL;
  1858. strcpy(ptemp->LogLine, logitem);
  1859. return (ptemp);
  1860. }
  1861. void LogOutAndClean(
  1862. BOOL print
  1863. )
  1864. /*++
  1865. Routine Description:
  1866. output the log output, and free all the items in the list
  1867. Arguments:
  1868. none
  1869. Return Value:
  1870. none
  1871. --*/
  1872. {
  1873. pLogListItem ptemp;
  1874. pLogListItem plog = pLogList;
  1875. while(plog)
  1876. {
  1877. ptemp = plog;
  1878. if(print)
  1879. {
  1880. fprintf(stderr, plog->LogLine);
  1881. }
  1882. plog = plog->Next;
  1883. free(ptemp->LogLine);
  1884. free(ptemp);
  1885. }
  1886. if(print)
  1887. {
  1888. fprintf(stderr, "\n");
  1889. }
  1890. pLogListTmp = pLogList = NULL;
  1891. }
  1892. void
  1893. __cdecl
  1894. LogPrintf(
  1895. const char *format,
  1896. ...
  1897. )
  1898. /*++
  1899. Routine Description:
  1900. logging wrapper for fprintf
  1901. Arguments:
  1902. none
  1903. Return Value:
  1904. none
  1905. --*/
  1906. {
  1907. va_list arglist;
  1908. char LogStr[1024];
  1909. va_start(arglist, format);
  1910. vsprintf(LogStr, format, arglist);
  1911. if(pLogList == NULL)
  1912. {
  1913. //
  1914. // initialize log
  1915. //
  1916. pLogListTmp = pLogList = LogAppend(LogStr, NULL);
  1917. } else {
  1918. //
  1919. // append to log
  1920. //
  1921. pLogListTmp = LogAppend(LogStr, pLogListTmp);
  1922. }
  1923. }