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.

780 lines
15 KiB

  1. /*++
  2. Copyright (c) 1995-1997 Microsoft Corporation
  3. Module Name:
  4. ver.cxx
  5. Abstract:
  6. This module contains an NTSD debugger extension for dumping module
  7. version resources.
  8. Author:
  9. Keith Moore (keithmo) 16-Sep-1997
  10. Revision History:
  11. --*/
  12. #include "inetdbgp.h"
  13. PSTR VersionLabels[] =
  14. {
  15. "CompanyName",
  16. "FileDescription",
  17. "FileVersion",
  18. "InternalName",
  19. "LegalCopyright",
  20. "OriginalFilename",
  21. "ProductName",
  22. "ProductVersion"
  23. };
  24. #define NUM_LABELS ( sizeof(VersionLabels) / sizeof(VersionLabels[0]) )
  25. typedef struct _ENUM_CONTEXT {
  26. PSTR ModuleName;
  27. INT NameLength;
  28. } ENUM_CONTEXT, *PENUM_CONTEXT;
  29. /************************************************************
  30. * Dump File Version Info
  31. ************************************************************/
  32. PIMAGE_RESOURCE_DIRECTORY
  33. FindResourceDir(
  34. IN PIMAGE_RESOURCE_DIRECTORY BaseResourceDir,
  35. IN PIMAGE_RESOURCE_DIRECTORY TargetResourceDir,
  36. IN USHORT ResourceId
  37. )
  38. /*++
  39. Routine Description:
  40. Finds the specified resource directory.
  41. Arguments:
  42. BaseResourceDir - The (remote) address of the *start* of the resource
  43. section.
  44. TargetResourceDir - The (remote) address of the resource directory
  45. to search.
  46. ResourceId - The resource ID we're looking for.
  47. Return Value:
  48. PIMAGE_RESOURCE_DIRECTORY - Pointer to the resource directory
  49. corresponding to ResourceId if successful, NULL otherwise.
  50. --*/
  51. {
  52. IMAGE_RESOURCE_DIRECTORY localDir;
  53. IMAGE_RESOURCE_DIRECTORY_ENTRY localEntry;
  54. PIMAGE_RESOURCE_DIRECTORY_ENTRY remoteEntry;
  55. USHORT i;
  56. //
  57. // Read the target resource directory.
  58. //
  59. if( !ReadMemory(
  60. (ULONG_PTR)TargetResourceDir,
  61. &localDir,
  62. sizeof(localDir),
  63. NULL
  64. ) ) {
  65. return NULL;
  66. }
  67. //
  68. // Scan it.
  69. //
  70. remoteEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)( TargetResourceDir + 1 );
  71. for( i = localDir.NumberOfNamedEntries + localDir.NumberOfIdEntries ;
  72. i > 0 ;
  73. i--, remoteEntry++ ) {
  74. //
  75. // Read the directory entry.
  76. //
  77. if( !ReadMemory(
  78. (ULONG_PTR)remoteEntry,
  79. &localEntry,
  80. sizeof(localEntry),
  81. NULL
  82. ) ) {
  83. return NULL;
  84. }
  85. //
  86. // If the entry is a directory and the IDs match, then return it.
  87. //
  88. if( localEntry.DataIsDirectory == 0 ) {
  89. continue;
  90. }
  91. if( localEntry.NameIsString == 0 &&
  92. localEntry.Id == ResourceId ) {
  93. return (PIMAGE_RESOURCE_DIRECTORY)
  94. ( (ULONG_PTR)BaseResourceDir + localEntry.OffsetToDirectory );
  95. }
  96. }
  97. return NULL;
  98. } // FindResourceDir
  99. PIMAGE_RESOURCE_DATA_ENTRY
  100. FindResourceData(
  101. IN PIMAGE_RESOURCE_DIRECTORY BaseResourceDir,
  102. IN PIMAGE_RESOURCE_DIRECTORY TargetResourceDir,
  103. IN USHORT ResourceId
  104. )
  105. /*++
  106. Routine Description:
  107. Finds the specified resource data item.
  108. Arguments:
  109. BaseResourceDir - The (remote) address of the *start* of the resource
  110. section.
  111. TargetResourceDir - The (remote) address of the resource directory
  112. to search.
  113. ResourceId - The resource ID we're looking for. This may be zero
  114. to return any resource.
  115. Return Value:
  116. PIMAGE_RESOURCE_DATA_ENTRY - Pointer to the resource data entry
  117. corresponding to ResourceId if successful, NULL otherwise.
  118. --*/
  119. {
  120. IMAGE_RESOURCE_DIRECTORY localDir;
  121. IMAGE_RESOURCE_DIRECTORY_ENTRY localEntry;
  122. PIMAGE_RESOURCE_DIRECTORY_ENTRY remoteEntry;
  123. USHORT i;
  124. //
  125. // Read the target resource directory.
  126. //
  127. if( !ReadMemory(
  128. (ULONG_PTR)TargetResourceDir,
  129. &localDir,
  130. sizeof(localDir),
  131. NULL
  132. ) ) {
  133. return NULL;
  134. }
  135. //
  136. // Scan it.
  137. //
  138. remoteEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)( TargetResourceDir + 1 );
  139. for( i = localDir.NumberOfNamedEntries + localDir.NumberOfIdEntries ;
  140. i > 0 ;
  141. i--, remoteEntry++ ) {
  142. //
  143. // Read the directory entry.
  144. //
  145. if( !ReadMemory(
  146. (ULONG_PTR)remoteEntry,
  147. &localEntry,
  148. sizeof(localEntry),
  149. NULL
  150. ) ) {
  151. return NULL;
  152. }
  153. //
  154. // If the entry is not a directory and the IDs match (or the
  155. // requested ID is zero, meaning any ID) then return it.
  156. //
  157. if( localEntry.DataIsDirectory != 0 ) {
  158. continue;
  159. }
  160. if( localEntry.NameIsString == 0 &&
  161. ( localEntry.Id == ResourceId ||
  162. ResourceId == 0 ) ) {
  163. return (PIMAGE_RESOURCE_DATA_ENTRY)
  164. ( (ULONG_PTR)BaseResourceDir + localEntry.OffsetToDirectory );
  165. }
  166. }
  167. return NULL;
  168. } // FindResourceData
  169. BOOL
  170. DumpVersionResource(
  171. IN PVOID VersionResource
  172. )
  173. /*++
  174. Routine Description:
  175. Dumps a version resource block.
  176. Arguments:
  177. VersionResource - The version resource to dump.
  178. Return Value:
  179. BOOL - TRUE if successful, FALSE if the version resource block
  180. was corrupt or unreadable.
  181. --*/
  182. {
  183. ULONG charSet;
  184. LPVOID version;
  185. UINT versionLength;
  186. INT i;
  187. VS_FIXEDFILEINFO * fixedFileInfo;
  188. CHAR label[MAX_PATH];
  189. //
  190. // Get the language/character-set pair.
  191. //
  192. if( !VerQueryValueA(
  193. VersionResource,
  194. "\\VarFileInfo\\Translation",
  195. &version,
  196. &versionLength
  197. ) ) {
  198. return FALSE;
  199. }
  200. charSet = *(LPDWORD)version;
  201. charSet = (DWORD)MAKELONG( HIWORD(charSet), LOWORD(charSet) );
  202. //
  203. // Get the root block so we can determine if this is a free or
  204. // checked build.
  205. //
  206. if( !VerQueryValue(
  207. VersionResource,
  208. "\\",
  209. &version,
  210. &versionLength
  211. ) ) {
  212. return FALSE;
  213. }
  214. fixedFileInfo = (VS_FIXEDFILEINFO *)version;
  215. dprintf(
  216. "%-19s = 0x%08lx (%s)\n",
  217. "dwFileFlags",
  218. fixedFileInfo->dwFileFlags,
  219. ( ( fixedFileInfo->dwFileFlags & VS_FF_DEBUG ) != 0 )
  220. ? "CHECKED"
  221. : "FREE"
  222. );
  223. //
  224. // Dump the various version strings.
  225. //
  226. for( i = 0 ; i < NUM_LABELS ; i++ ) {
  227. wsprintfA(
  228. label,
  229. "\\StringFileInfo\\%08lX\\%s",
  230. charSet,
  231. VersionLabels[i]
  232. );
  233. if( VerQueryValue(
  234. VersionResource,
  235. label,
  236. &version,
  237. &versionLength
  238. ) ) {
  239. dprintf(
  240. "%-19s = %s\n",
  241. VersionLabels[i],
  242. version
  243. );
  244. }
  245. }
  246. dprintf( "\n" );
  247. return TRUE;
  248. } // DumpVersionResource
  249. VOID
  250. FindAndDumpVersionResourceByAddress(
  251. IN ULONG_PTR ModuleAddress,
  252. IN PSTR ModuleName
  253. )
  254. /*++
  255. Routine Description:
  256. Locates and dumps the version resource for the module based at
  257. the specified address.
  258. Arguments:
  259. ModuleAddress - The base address of the module to dump.
  260. ModuleName - The module name, for display purposes.
  261. Return Value:
  262. None.
  263. --*/
  264. {
  265. IMAGE_DOS_HEADER dosHeader;
  266. IMAGE_NT_HEADERS ntHeaders;
  267. PIMAGE_OPTIONAL_HEADER optionalHeader;
  268. PIMAGE_DATA_DIRECTORY dataDir;
  269. PIMAGE_RESOURCE_DIRECTORY baseResourceDir;
  270. PIMAGE_RESOURCE_DIRECTORY tmpResourceDir;
  271. PIMAGE_RESOURCE_DATA_ENTRY dataEntry;
  272. IMAGE_RESOURCE_DATA_ENTRY localDataEntry;
  273. PVOID versionResource;
  274. //
  275. // Setup locals so we know how to cleanup on exit.
  276. //
  277. versionResource = NULL;
  278. //
  279. // Read & validate the image headers.
  280. //
  281. if( !ReadMemory(
  282. ModuleAddress,
  283. &dosHeader,
  284. sizeof(dosHeader),
  285. NULL
  286. ) ) {
  287. dprintf(
  288. "inetdbg.ver: cannot read DOS header @ 0x%p\n",
  289. ModuleAddress
  290. );
  291. goto cleanup;
  292. }
  293. if( dosHeader.e_magic != IMAGE_DOS_SIGNATURE ) {
  294. dprintf(
  295. "inetdbg.ver: module @ 0x%p has invalid DOS header\n",
  296. ModuleAddress
  297. );
  298. goto cleanup;
  299. }
  300. if( !ReadMemory(
  301. ModuleAddress + dosHeader.e_lfanew,
  302. &ntHeaders,
  303. sizeof(ntHeaders),
  304. NULL
  305. ) ) {
  306. dprintf(
  307. "inetdbg.ver: cannot read NT headers @ 0x%p\n",
  308. ModuleAddress
  309. );
  310. goto cleanup;
  311. }
  312. if( ntHeaders.Signature != IMAGE_NT_SIGNATURE ) {
  313. dprintf(
  314. "inetdbg.ver: module @ 0x%p has invalid NT headers\n",
  315. ModuleAddress
  316. );
  317. goto cleanup;
  318. }
  319. optionalHeader = &ntHeaders.OptionalHeader;
  320. if( optionalHeader->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC ) {
  321. dprintf(
  322. "inetdbg.ver: module @ 0x%p has invalid optional header\n",
  323. ModuleAddress
  324. );
  325. goto cleanup;
  326. }
  327. //
  328. // Locate the resource.
  329. //
  330. dataDir = &optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE];
  331. if( dataDir->VirtualAddress == 0 ||
  332. dataDir->Size == 0 ) {
  333. dprintf(
  334. "inetdbg.ver: module @ 0x%p has no resource information\n",
  335. ModuleAddress
  336. );
  337. goto cleanup;
  338. }
  339. baseResourceDir = (PIMAGE_RESOURCE_DIRECTORY)
  340. ( ModuleAddress + dataDir->VirtualAddress );
  341. //
  342. // Now go and find the resource in the image. Since resources are
  343. // stored heirarchally, we're basically for the resource path:
  344. //
  345. // VS_FILE_INFO\VS_VERSION_INFO\LanguageId
  346. //
  347. // For the language ID, we'll first try 0x409 (English) and if
  348. // that fails, we'll take any language.
  349. //
  350. dataEntry = NULL;
  351. tmpResourceDir = FindResourceDir(
  352. baseResourceDir,
  353. baseResourceDir,
  354. (USHORT)VS_FILE_INFO
  355. );
  356. if( tmpResourceDir != NULL ) {
  357. tmpResourceDir = FindResourceDir(
  358. baseResourceDir,
  359. tmpResourceDir,
  360. (USHORT)VS_VERSION_INFO
  361. );
  362. if( tmpResourceDir != NULL ) {
  363. dataEntry = FindResourceData(
  364. baseResourceDir,
  365. tmpResourceDir,
  366. 0x409
  367. );
  368. if( dataEntry == NULL ) {
  369. dataEntry = FindResourceData(
  370. baseResourceDir,
  371. tmpResourceDir,
  372. 0
  373. );
  374. }
  375. }
  376. }
  377. if( dataEntry == NULL ) {
  378. dprintf(
  379. "inetdbg.ver: cannot find version resource\n"
  380. );
  381. goto cleanup;
  382. }
  383. //
  384. // Actually read the dir entry.
  385. //
  386. if( !ReadMemory(
  387. (ULONG_PTR)dataEntry,
  388. &localDataEntry,
  389. sizeof(localDataEntry),
  390. NULL
  391. ) ) {
  392. dprintf(
  393. "inetdbg.ver: error reading resource\n"
  394. );
  395. goto cleanup;
  396. }
  397. //
  398. // Now we can allocate & read the resource.
  399. //
  400. versionResource = malloc( localDataEntry.Size );
  401. if( versionResource == NULL ) {
  402. dprintf(
  403. "inetdbg.ver: not enough memory\n"
  404. );
  405. goto cleanup;
  406. }
  407. if( !ReadMemory(
  408. ModuleAddress + localDataEntry.OffsetToData,
  409. versionResource,
  410. localDataEntry.Size,
  411. NULL
  412. ) ) {
  413. dprintf(
  414. "inetdbg.ver: error reading resource\n"
  415. );
  416. goto cleanup;
  417. }
  418. //
  419. // Dump it.
  420. //
  421. dprintf(
  422. "Module @ 0x%p = %s\n",
  423. ModuleAddress,
  424. ModuleName
  425. );
  426. if( !DumpVersionResource( versionResource ) ) {
  427. dprintf(
  428. "Cannot interpret version resource\n"
  429. );
  430. goto cleanup;
  431. }
  432. cleanup:
  433. if( versionResource != NULL ) {
  434. free( versionResource );
  435. }
  436. } // FindAndDumpVersionResourceByAddress
  437. BOOLEAN
  438. CALLBACK
  439. VerpEnumProc(
  440. IN PVOID Param,
  441. IN PMODULE_INFO ModuleInfo
  442. )
  443. {
  444. PENUM_CONTEXT context;
  445. INT baseNameLength;
  446. context = (PENUM_CONTEXT)Param;
  447. baseNameLength = strlen( ModuleInfo->BaseName );
  448. //
  449. // If the user wants all modules, or if the specified module matches
  450. // the "tail" of the module name, dump it.
  451. //
  452. if( context->ModuleName == NULL ||
  453. ( baseNameLength >= context->NameLength &&
  454. !_stricmp(
  455. context->ModuleName,
  456. ModuleInfo->BaseName + baseNameLength - context->NameLength
  457. ) ) ) {
  458. FindAndDumpVersionResourceByAddress(
  459. ModuleInfo->DllBase,
  460. ModuleInfo->BaseName
  461. );
  462. }
  463. return TRUE;
  464. } // VerpEnumProc
  465. VOID
  466. FindAndDumpVersionResourceByName(
  467. IN PSTR ModuleName
  468. )
  469. /*++
  470. Routine Description:
  471. Locates and dumps the version resource for the specified module.
  472. Arguments:
  473. ModuleName - The name of the module to dump. If this is NULL then
  474. all modules are dumped.
  475. Return Value:
  476. None.
  477. --*/
  478. {
  479. ENUM_CONTEXT context;
  480. context.ModuleName = ModuleName;
  481. if( ModuleName == NULL ) {
  482. context.NameLength = 0;
  483. } else {
  484. context.NameLength = strlen( ModuleName );
  485. }
  486. if( !EnumModules(
  487. VerpEnumProc,
  488. (PVOID)&context
  489. ) ) {
  490. dprintf( "error retrieving module list\n" );
  491. }
  492. } // FindAndDumpVersionResourceByName
  493. DECLARE_API( ver )
  494. /*++
  495. Routine Description:
  496. This function is called as an NTSD extension to format and dump
  497. module version info.
  498. Arguments:
  499. hCurrentProcess - Supplies a handle to the current process (at the
  500. time the extension was called).
  501. hCurrentThread - Supplies a handle to the current thread (at the
  502. time the extension was called).
  503. CurrentPc - Supplies the current pc at the time the extension is
  504. called.
  505. lpExtensionApis - Supplies the address of the functions callable
  506. by this extension.
  507. lpArgumentString - Supplies the asciiz string that describes the
  508. ansi string to be dumped.
  509. Return Value:
  510. None.
  511. --*/
  512. {
  513. ULONG module;
  514. PSTR endPointer;
  515. INIT_API();
  516. //
  517. // Skip leading blanks.
  518. //
  519. while( *lpArgumentString == ' ' ||
  520. *lpArgumentString == '\t' ) {
  521. lpArgumentString++;
  522. }
  523. if( *lpArgumentString == '\0' ) {
  524. //
  525. // No argument passed, dump all modules.
  526. //
  527. FindAndDumpVersionResourceByName( NULL );
  528. } else {
  529. module = strtoul( lpArgumentString, &endPointer, 16 );
  530. if( *endPointer != ' ' && *endPointer != '\t' && *endPointer != '\0' ) {
  531. //
  532. // Assume the argument is actually a module name, not
  533. // a base address.
  534. //
  535. FindAndDumpVersionResourceByName( lpArgumentString );
  536. } else {
  537. FindAndDumpVersionResourceByAddress( module, NULL );
  538. }
  539. }
  540. } // DECLARE_API( ver )