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.

4170 lines
140 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. ldrrsrc.c
  5. Abstract:
  6. Loader API calls for accessing resource sections.
  7. Author:
  8. Steve Wood (stevewo) 16-Sep-1991
  9. Revision History:
  10. --*/
  11. #include "ntrtlp.h"
  12. #if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
  13. #pragma alloc_text(PAGE,LdrAccessResource)
  14. #pragma alloc_text(PAGE,LdrpAccessResourceData)
  15. #pragma alloc_text(PAGE,LdrpAccessResourceDataNoMultipleLanguage)
  16. #pragma alloc_text(PAGE,LdrFindEntryForAddress)
  17. #pragma alloc_text(PAGE,LdrFindResource_U)
  18. #pragma alloc_text(PAGE,LdrFindResourceEx_U)
  19. #pragma alloc_text(PAGE,LdrFindResourceDirectory_U)
  20. #pragma alloc_text(PAGE,LdrpCompareResourceNames_U)
  21. #pragma alloc_text(PAGE,LdrpSearchResourceSection_U)
  22. #pragma alloc_text(PAGE,LdrEnumResources)
  23. #endif
  24. #define USE_RC_CHECKSUM
  25. // winuser.h
  26. #define IS_INTRESOURCE(_r) (((ULONG_PTR)(_r) >> 16) == 0)
  27. #define RT_VERSION 16
  28. #define RT_MANIFEST 24
  29. #define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
  30. #define ISOLATIONAWARE_MANIFEST_RESOURCE_ID 2
  31. #define MINIMUM_RESERVED_MANIFEST_RESOURCE_ID 1
  32. #define MAXIMUM_RESERVED_MANIFEST_RESOURCE_ID 16
  33. #define LDRP_MIN(x,y) (((x)<(y)) ? (x) : (y))
  34. #define DPFLTR_LEVEL_STATUS(x) ((NT_SUCCESS(x) \
  35. || (x) == STATUS_OBJECT_NAME_NOT_FOUND \
  36. || (x) == STATUS_RESOURCE_DATA_NOT_FOUND \
  37. || (x) == STATUS_RESOURCE_TYPE_NOT_FOUND \
  38. || (x) == STATUS_RESOURCE_NAME_NOT_FOUND \
  39. ) \
  40. ? DPFLTR_TRACE_LEVEL : DPFLTR_ERROR_LEVEL)
  41. #ifndef NTOS_KERNEL_RUNTIME
  42. #include <md5.h>
  43. //
  44. // The size in byte of the resource MD5 checksum. 16 bytes = 128 bits.
  45. //
  46. #define RESOURCE_CHECKSUM_SIZE 16
  47. //
  48. // The registry key path which stores the file version information for MUI files.
  49. //
  50. #define REG_MUI_PATH L"Software\\Microsoft\\Windows\\CurrentVersion"
  51. #define REG_MUI_RC_PATH L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Nls\\MUILanguages\\RC"
  52. #define MUI_MUILANGUAGES_KEY_NAME L"MUILanguages"
  53. #define MUI_FILE_VERSION_KEY_NAME L"FileVersions"
  54. #define MUI_ALTERNATE_VERSION_KEY L"MUIVer"
  55. #define MUI_RC_CHECKSUM_DISABLE_KEY L"ChecksumDisable"
  56. PALT_RESOURCE_MODULE AlternateResourceModules;
  57. ULONG AlternateResourceModuleCount;
  58. ULONG AltResMemBlockCount;
  59. LANGID UILangId, InstallLangId;
  60. #define DWORD_ALIGNMENT(x) (((x)+3) & ~3)
  61. #define MEMBLOCKSIZE 16
  62. #define RESMODSIZE sizeof(ALT_RESOURCE_MODULE)
  63. #define uint32 unsigned int
  64. #define ENG_US_LANGID MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US )
  65. #endif
  66. #if defined(_X86_) && !defined(BLDR_KERNEL_RUNTIME) && !defined(NTOS_KERNEL_RUNTIME)
  67. // appcompat: There's some code that depends on the Win2k instruction stream - duplicate it here.
  68. __declspec(naked)
  69. #endif
  70. NTSTATUS
  71. LdrAccessResource(
  72. IN PVOID DllHandle,
  73. IN const IMAGE_RESOURCE_DATA_ENTRY* ResourceDataEntry,
  74. OUT PVOID *Address OPTIONAL,
  75. OUT PULONG Size OPTIONAL
  76. )
  77. /*++
  78. Routine Description:
  79. This function locates the address of the specified resource in the
  80. specified DLL and returns its address.
  81. Arguments:
  82. DllHandle - Supplies a handle to the image file that the resource is
  83. contained in.
  84. ResourceDataEntry - Supplies a pointer to the resource data entry in
  85. the resource data section of the image file specified by the
  86. DllHandle parameter. This pointer should have been one returned
  87. by the LdrFindResource function.
  88. Address - Optional pointer to a variable that will receive the
  89. address of the resource specified by the first two parameters.
  90. Size - Optional pointer to a variable that will receive the size of
  91. the resource specified by the first two parameters.
  92. Return Value:
  93. TBD
  94. --*/
  95. {
  96. #if defined(_X86_) && !defined(BLDR_KERNEL_RUNTIME) && !defined(NTOS_KERNEL_RUNTIME)
  97. __asm {
  98. push [esp+0x10] // Size
  99. push [esp+0x10] // Address
  100. push [esp+0x10] // ResourceDataEntry
  101. push [esp+0x10] // DllHandle
  102. call LdrpAccessResourceData
  103. ret 16
  104. }
  105. #else
  106. NTSTATUS Status;
  107. RTL_PAGED_CODE();
  108. Status =
  109. LdrpAccessResourceData(
  110. DllHandle,
  111. ResourceDataEntry,
  112. Address,
  113. Size
  114. );
  115. if (!NT_SUCCESS(Status)) {
  116. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_LEVEL_STATUS(Status), "LDR: %s() exiting 0x%08lx\n", __FUNCTION__, Status));
  117. }
  118. return Status;
  119. #endif
  120. }
  121. NTSTATUS
  122. LdrpAccessResourceDataNoMultipleLanguage(
  123. IN PVOID DllHandle,
  124. IN const IMAGE_RESOURCE_DATA_ENTRY* ResourceDataEntry,
  125. OUT PVOID *Address OPTIONAL,
  126. OUT PULONG Size OPTIONAL
  127. )
  128. /*++
  129. Routine Description:
  130. This function returns the data necessary to actually examine the
  131. contents of a particular resource, without allowing for the .mui
  132. feature. It used to be the tail of LdrpAccessResourceData, from
  133. which it is now called.
  134. Arguments:
  135. DllHandle - Supplies a handle to the image file that the resource is
  136. contained in.
  137. ResourceDataEntry - Supplies a pointer to the resource data entry in
  138. the resource data directory of the image file specified by the
  139. DllHandle parameter. This pointer should have been one returned
  140. by the LdrFindResource function.
  141. Address - Optional pointer to a variable that will receive the
  142. address of the resource specified by the first two parameters.
  143. Size - Optional pointer to a variable that will receive the size of
  144. the resource specified by the first two parameters.
  145. Return Value:
  146. TBD
  147. --*/
  148. {
  149. PIMAGE_RESOURCE_DIRECTORY ResourceDirectory;
  150. ULONG ResourceSize;
  151. PIMAGE_NT_HEADERS NtHeaders;
  152. ULONG_PTR VirtualAddressOffset;
  153. PIMAGE_SECTION_HEADER NtSection;
  154. NTSTATUS Status = STATUS_SUCCESS;
  155. RTL_PAGED_CODE();
  156. try {
  157. ResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
  158. RtlImageDirectoryEntryToData(DllHandle,
  159. TRUE,
  160. IMAGE_DIRECTORY_ENTRY_RESOURCE,
  161. &ResourceSize
  162. );
  163. if (!ResourceDirectory) {
  164. return STATUS_RESOURCE_DATA_NOT_FOUND;
  165. }
  166. if (LDR_IS_DATAFILE(DllHandle)) {
  167. ULONG ResourceRVA;
  168. DllHandle = LDR_DATAFILE_TO_VIEW(DllHandle);
  169. NtHeaders = RtlImageNtHeader( DllHandle );
  170. if (NtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
  171. ResourceRVA=((PIMAGE_NT_HEADERS32)NtHeaders)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_RESOURCE ].VirtualAddress;
  172. } else if (NtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
  173. ResourceRVA=((PIMAGE_NT_HEADERS64)NtHeaders)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_RESOURCE ].VirtualAddress;
  174. } else {
  175. ResourceRVA = 0;
  176. }
  177. if (!ResourceRVA) {
  178. return STATUS_RESOURCE_DATA_NOT_FOUND;
  179. }
  180. VirtualAddressOffset = (ULONG_PTR)DllHandle + ResourceRVA - (ULONG_PTR)ResourceDirectory;
  181. //
  182. // Now, we must check to see if the resource is not in the
  183. // same section as the resource table. If it's in .rsrc1,
  184. // we've got to adjust the RVA in the ResourceDataEntry
  185. // to point to the correct place in the non-VA data file.
  186. //
  187. NtSection = RtlSectionTableFromVirtualAddress( NtHeaders, DllHandle, ResourceRVA);
  188. if (!NtSection) {
  189. return STATUS_RESOURCE_DATA_NOT_FOUND;
  190. }
  191. if ( ResourceDataEntry->OffsetToData > NtSection->Misc.VirtualSize ) {
  192. ULONG rva;
  193. rva = NtSection->VirtualAddress;
  194. NtSection = RtlSectionTableFromVirtualAddress(NtHeaders,
  195. DllHandle,
  196. ResourceDataEntry->OffsetToData
  197. );
  198. if (!NtSection) {
  199. return STATUS_RESOURCE_DATA_NOT_FOUND;
  200. }
  201. VirtualAddressOffset +=
  202. ((ULONG_PTR)NtSection->VirtualAddress - rva) -
  203. ((ULONG_PTR)RtlAddressInSectionTable ( NtHeaders, DllHandle, NtSection->VirtualAddress ) - (ULONG_PTR)ResourceDirectory);
  204. }
  205. } else {
  206. VirtualAddressOffset = 0;
  207. }
  208. if (ARGUMENT_PRESENT( Address )) {
  209. *Address = (PVOID)( (PCHAR)DllHandle +
  210. (ResourceDataEntry->OffsetToData - VirtualAddressOffset)
  211. );
  212. }
  213. if (ARGUMENT_PRESENT( Size )) {
  214. *Size = ResourceDataEntry->Size;
  215. }
  216. } except (EXCEPTION_EXECUTE_HANDLER) {
  217. Status = GetExceptionCode();
  218. }
  219. if (!NT_SUCCESS(Status)) {
  220. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_LEVEL_STATUS(Status), "LDR: %s() exiting 0x%08lx\n", __FUNCTION__, Status));
  221. }
  222. return Status;
  223. }
  224. NTSTATUS
  225. LdrpAccessResourceData(
  226. IN PVOID DllHandle,
  227. IN const IMAGE_RESOURCE_DATA_ENTRY* ResourceDataEntry,
  228. OUT PVOID *Address OPTIONAL,
  229. OUT PULONG Size OPTIONAL
  230. )
  231. /*++
  232. Routine Description:
  233. This function returns the data necessary to actually examine the
  234. contents of a particular resource.
  235. Arguments:
  236. DllHandle - Supplies a handle to the image file that the resource is
  237. contained in.
  238. ResourceDataEntry - Supplies a pointer to the resource data entry in
  239. the resource data directory of the image file specified by the
  240. DllHandle parameter. This pointer should have been one returned
  241. by the LdrFindResource function.
  242. Address - Optional pointer to a variable that will receive the
  243. address of the resource specified by the first two parameters.
  244. Size - Optional pointer to a variable that will receive the size of
  245. the resource specified by the first two parameters.
  246. Return Value:
  247. TBD
  248. --*/
  249. {
  250. PIMAGE_RESOURCE_DIRECTORY ResourceDirectory;
  251. ULONG ResourceSize;
  252. PIMAGE_NT_HEADERS NtHeaders;
  253. NTSTATUS Status = STATUS_SUCCESS;
  254. RTL_PAGED_CODE();
  255. #ifndef NTOS_KERNEL_RUNTIME
  256. ResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
  257. RtlImageDirectoryEntryToData(DllHandle,
  258. TRUE,
  259. IMAGE_DIRECTORY_ENTRY_RESOURCE,
  260. &ResourceSize
  261. );
  262. if (!ResourceDirectory) {
  263. Status = STATUS_RESOURCE_DATA_NOT_FOUND;
  264. goto Exit;
  265. }
  266. if ((ULONG_PTR)ResourceDataEntry < (ULONG_PTR) ResourceDirectory ){
  267. DllHandle = LdrLoadAlternateResourceModule (DllHandle, NULL);
  268. } else{
  269. NtHeaders = RtlImageNtHeader(LDR_DATAFILE_TO_VIEW(DllHandle));
  270. if (NtHeaders) {
  271. // Find the bounds of the image so we can see if this resource entry is in an alternate
  272. // resource dll.
  273. ULONG_PTR ImageStart = (ULONG_PTR)LDR_DATAFILE_TO_VIEW(DllHandle);
  274. SIZE_T ImageSize = 0;
  275. if (LDR_IS_DATAFILE(DllHandle)) {
  276. // mapped as datafile. Ask mm for the size
  277. NTSTATUS Status;
  278. MEMORY_BASIC_INFORMATION MemInfo;
  279. Status = NtQueryVirtualMemory(
  280. NtCurrentProcess(),
  281. (PVOID) ImageStart,
  282. MemoryBasicInformation,
  283. &MemInfo,
  284. sizeof(MemInfo),
  285. NULL
  286. );
  287. if ( !NT_SUCCESS(Status) ) {
  288. ImageSize = 0;
  289. } else {
  290. ImageSize = MemInfo.RegionSize;
  291. }
  292. } else {
  293. ImageSize = ((PIMAGE_NT_HEADERS32)NtHeaders)->OptionalHeader.SizeOfImage;
  294. }
  295. if (!(((ULONG_PTR)ResourceDataEntry >= ImageStart) && ((ULONG_PTR)ResourceDataEntry < (ImageStart + ImageSize)))) {
  296. // Doesn't fall within the specified image. Must be an alternate dll.
  297. DllHandle = LdrLoadAlternateResourceModule (DllHandle, NULL);
  298. }
  299. }
  300. }
  301. if (!DllHandle){
  302. Status = STATUS_RESOURCE_DATA_NOT_FOUND;
  303. goto Exit;
  304. }
  305. #endif
  306. Status =
  307. LdrpAccessResourceDataNoMultipleLanguage(
  308. DllHandle,
  309. ResourceDataEntry,
  310. Address,
  311. Size
  312. );
  313. #ifndef NTOS_KERNEL_RUNTIME
  314. Exit:
  315. #endif
  316. if (!NT_SUCCESS(Status)) {
  317. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_LEVEL_STATUS(Status), "LDR: %s() exiting 0x%08lx\n", __FUNCTION__, Status));
  318. }
  319. return Status;
  320. }
  321. NTSTATUS
  322. LdrFindEntryForAddress(
  323. IN PVOID Address,
  324. OUT PLDR_DATA_TABLE_ENTRY *TableEntry
  325. )
  326. /*++
  327. Routine Description:
  328. This function returns the load data table entry that describes the virtual
  329. address range that contains the passed virtual address.
  330. Arguments:
  331. Address - Supplies a 32-bit virtual address.
  332. TableEntry - Supplies a pointer to the variable that will receive the
  333. address of the loader data table entry.
  334. Return Value:
  335. Status
  336. --*/
  337. {
  338. PPEB_LDR_DATA Ldr;
  339. PLIST_ENTRY Head, Next;
  340. PLDR_DATA_TABLE_ENTRY Entry;
  341. PIMAGE_NT_HEADERS NtHeaders;
  342. PVOID ImageBase;
  343. PVOID EndOfImage;
  344. NTSTATUS Status;
  345. Ldr = NtCurrentPeb()->Ldr;
  346. if (Ldr == NULL) {
  347. Status = STATUS_NO_MORE_ENTRIES;
  348. goto Exit;
  349. }
  350. Entry = (PLDR_DATA_TABLE_ENTRY) Ldr->EntryInProgress;
  351. if (Entry != NULL) {
  352. NtHeaders = RtlImageNtHeader( Entry->DllBase );
  353. if (NtHeaders != NULL) {
  354. ImageBase = (PVOID)Entry->DllBase;
  355. EndOfImage = (PVOID)
  356. ((ULONG_PTR)ImageBase + NtHeaders->OptionalHeader.SizeOfImage);
  357. if ((ULONG_PTR)Address >= (ULONG_PTR)ImageBase && (ULONG_PTR)Address < (ULONG_PTR)EndOfImage) {
  358. *TableEntry = Entry;
  359. Status = STATUS_SUCCESS;
  360. goto Exit;
  361. }
  362. }
  363. }
  364. Head = &Ldr->InMemoryOrderModuleList;
  365. Next = Head->Flink;
  366. while ( Next != Head ) {
  367. Entry = CONTAINING_RECORD( Next, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks );
  368. NtHeaders = RtlImageNtHeader( Entry->DllBase );
  369. if (NtHeaders != NULL) {
  370. ImageBase = (PVOID)Entry->DllBase;
  371. EndOfImage = (PVOID)
  372. ((ULONG_PTR)ImageBase + NtHeaders->OptionalHeader.SizeOfImage);
  373. if ((ULONG_PTR)Address >= (ULONG_PTR)ImageBase && (ULONG_PTR)Address < (ULONG_PTR)EndOfImage) {
  374. *TableEntry = Entry;
  375. Status = STATUS_SUCCESS;
  376. goto Exit;
  377. }
  378. }
  379. Next = Next->Flink;
  380. }
  381. Status = STATUS_NO_MORE_ENTRIES;
  382. Exit:
  383. if (!NT_SUCCESS(Status)) {
  384. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_LEVEL_STATUS(Status), "LDR: %s() exiting 0x%08lx\n", __FUNCTION__, Status));
  385. }
  386. return( Status );
  387. }
  388. NTSTATUS
  389. LdrFindResource_U(
  390. IN PVOID DllHandle,
  391. IN const ULONG_PTR* ResourceIdPath,
  392. IN ULONG ResourceIdPathLength,
  393. OUT PIMAGE_RESOURCE_DATA_ENTRY *ResourceDataEntry
  394. )
  395. /*++
  396. Routine Description:
  397. This function locates the address of the specified resource in the
  398. specified DLL and returns its address.
  399. Arguments:
  400. DllHandle - Supplies a handle to the image file that the resource is
  401. contained in.
  402. ResourceIdPath - Supplies a pointer to an array of 32-bit resource
  403. identifiers. Each identifier is either an integer or a pointer
  404. to a STRING structure that specifies a resource name. The array
  405. is used to traverse the directory structure contained in the
  406. resource section in the image file specified by the DllHandle
  407. parameter.
  408. ResourceIdPathLength - Supplies the number of elements in the
  409. ResourceIdPath array.
  410. ResourceDataEntry - Supplies a pointer to a variable that will
  411. receive the address of the resource data entry in the resource
  412. data section of the image file specified by the DllHandle
  413. parameter.
  414. Return Value:
  415. TBD
  416. --*/
  417. {
  418. RTL_PAGED_CODE();
  419. return LdrpSearchResourceSection_U(
  420. DllHandle,
  421. ResourceIdPath,
  422. ResourceIdPathLength,
  423. 0, // Look for a leaf node, ineaxt lang match
  424. (PVOID *)ResourceDataEntry
  425. );
  426. }
  427. NTSTATUS
  428. LdrFindResourceEx_U(
  429. IN ULONG Flags,
  430. IN PVOID DllHandle,
  431. IN const ULONG_PTR* ResourceIdPath,
  432. IN ULONG ResourceIdPathLength,
  433. OUT PIMAGE_RESOURCE_DATA_ENTRY *ResourceDataEntry
  434. )
  435. /*++
  436. Routine Description:
  437. This function locates the address of the specified resource in the
  438. specified DLL and returns its address.
  439. Arguments:
  440. Flags -
  441. LDRP_FIND_RESOURCE_DIRECTORY
  442. searching for a resource directory, otherwise the caller is
  443. searching for a resource data entry.
  444. LDR_FIND_RESOURCE_LANGUAGE_EXACT
  445. searching for a resource with, and only with, the language id
  446. specified in ResourceIdPath, otherwise the caller wants the routine
  447. to come up with default when specified langid is not found.
  448. LDR_FIND_RESOURCE_LANGUAGE_REDIRECT_VERSION
  449. searching for a resource version in both main and alternative
  450. module paths
  451. DllHandle - Supplies a handle to the image file that the resource is
  452. contained in.
  453. ResourceIdPath - Supplies a pointer to an array of 32-bit resource
  454. identifiers. Each identifier is either an integer or a pointer
  455. to a STRING structure that specifies a resource name. The array
  456. is used to traverse the directory structure contained in the
  457. resource section in the image file specified by the DllHandle
  458. parameter.
  459. ResourceIdPathLength - Supplies the number of elements in the
  460. ResourceIdPath array.
  461. ResourceDataEntry - Supplies a pointer to a variable that will
  462. receive the address of the resource data entry in the resource
  463. data section of the image file specified by the DllHandle
  464. parameter.
  465. Return Value:
  466. TBD
  467. --*/
  468. {
  469. RTL_PAGED_CODE();
  470. return LdrpSearchResourceSection_U(
  471. DllHandle,
  472. ResourceIdPath,
  473. ResourceIdPathLength,
  474. Flags,
  475. (PVOID *)ResourceDataEntry
  476. );
  477. }
  478. NTSTATUS
  479. LdrFindResourceDirectory_U(
  480. IN PVOID DllHandle,
  481. IN const ULONG_PTR* ResourceIdPath,
  482. IN ULONG ResourceIdPathLength,
  483. OUT PIMAGE_RESOURCE_DIRECTORY *ResourceDirectory
  484. )
  485. /*++
  486. Routine Description:
  487. This function locates the address of the specified resource directory in
  488. specified DLL and returns its address.
  489. Arguments:
  490. DllHandle - Supplies a handle to the image file that the resource
  491. directory is contained in.
  492. ResourceIdPath - Supplies a pointer to an array of 32-bit resource
  493. identifiers. Each identifier is either an integer or a pointer
  494. to a STRING structure that specifies a resource name. The array
  495. is used to traverse the directory structure contained in the
  496. resource section in the image file specified by the DllHandle
  497. parameter.
  498. ResourceIdPathLength - Supplies the number of elements in the
  499. ResourceIdPath array.
  500. ResourceDirectory - Supplies a pointer to a variable that will
  501. receive the address of the resource directory specified by
  502. ResourceIdPath in the resource data section of the image file
  503. the DllHandle parameter.
  504. Return Value:
  505. TBD
  506. --*/
  507. {
  508. RTL_PAGED_CODE();
  509. return LdrpSearchResourceSection_U(
  510. DllHandle,
  511. ResourceIdPath,
  512. ResourceIdPathLength,
  513. LDRP_FIND_RESOURCE_DIRECTORY, // Look for a directory node
  514. (PVOID *)ResourceDirectory
  515. );
  516. }
  517. LONG
  518. LdrpCompareResourceNames_U(
  519. IN ULONG_PTR ResourceName,
  520. IN const IMAGE_RESOURCE_DIRECTORY* ResourceDirectory,
  521. IN const IMAGE_RESOURCE_DIRECTORY_ENTRY* ResourceDirectoryEntry
  522. )
  523. {
  524. LONG li;
  525. PIMAGE_RESOURCE_DIR_STRING_U ResourceNameString;
  526. if (ResourceName & LDR_RESOURCE_ID_NAME_MASK) {
  527. if (!ResourceDirectoryEntry->NameIsString) {
  528. return( -1 );
  529. }
  530. ResourceNameString = (PIMAGE_RESOURCE_DIR_STRING_U)
  531. ((PCHAR)ResourceDirectory + ResourceDirectoryEntry->NameOffset);
  532. li = wcsncmp( (LPWSTR)ResourceName,
  533. ResourceNameString->NameString,
  534. ResourceNameString->Length
  535. );
  536. if (!li && wcslen((PWSTR)ResourceName) != ResourceNameString->Length) {
  537. return( 1 );
  538. }
  539. return(li);
  540. }
  541. else {
  542. if (ResourceDirectoryEntry->NameIsString) {
  543. return( 1 );
  544. }
  545. return( (ULONG)(ResourceName - ResourceDirectoryEntry->Name) );
  546. }
  547. }
  548. // Language ids are 16bits so any value with any bits
  549. // set above 16 should be ok, and this value only has
  550. // to fit in a ULONG_PTR. 0x10000 should be sufficient.
  551. // The value used is actually 0xFFFF regardless of 32bit or 64bit,
  552. // I guess assuming this is not an actual langid, which it isn't,
  553. // due to the relatively small number of languages, around 70.
  554. #define USE_FIRSTAVAILABLE_LANGID (0xFFFFFFFF & ~LDR_RESOURCE_ID_NAME_MASK)
  555. NTSTATUS
  556. LdrpSearchResourceSection_U(
  557. IN PVOID DllHandle,
  558. IN const ULONG_PTR* ResourceIdPath,
  559. IN ULONG ResourceIdPathLength,
  560. IN ULONG Flags,
  561. OUT PVOID *ResourceDirectoryOrData
  562. )
  563. /*++
  564. Routine Description:
  565. This function locates the address of the specified resource in the
  566. specified DLL and returns its address.
  567. Arguments:
  568. DllHandle - Supplies a handle to the image file that the resource is
  569. contained in.
  570. ResourceIdPath - Supplies a pointer to an array of 32-bit resource
  571. identifiers. Each identifier is either an integer or a pointer
  572. to a null terminated string (PSZ) that specifies a resource
  573. name. The array is used to traverse the directory structure
  574. contained in the resource section in the image file specified by
  575. the DllHandle parameter.
  576. ResourceIdPathLength - Supplies the number of elements in the
  577. ResourceIdPath array.
  578. Flags -
  579. LDRP_FIND_RESOURCE_DIRECTORY
  580. searching for a resource directory, otherwise the caller is
  581. searching for a resource data entry.
  582. LDR_FIND_RESOURCE_LANGUAGE_EXACT
  583. searching for a resource with, and only with, the language id
  584. specified in ResourceIdPath, otherwise the caller wants the routine
  585. to come up with default when specified langid is not found.
  586. LDR_FIND_RESOURCE_LANGUAGE_REDIRECT_VERSION
  587. searching for a resource version in main and alternative
  588. modules paths
  589. FindDirectoryEntry - Supplies a boolean that is TRUE if caller is
  590. searching for a resource directory, otherwise the caller is
  591. searching for a resource data entry.
  592. ExactLangMatchOnly - Supplies a boolean that is TRUE if caller is
  593. searching for a resource with, and only with, the language id
  594. specified in ResourceIdPath, otherwise the caller wants the routine
  595. to come up with default when specified langid is not found.
  596. ResourceDirectoryOrData - Supplies a pointer to a variable that will
  597. receive the address of the resource directory or data entry in
  598. the resource data section of the image file specified by the
  599. DllHandle parameter.
  600. Return Value:
  601. TBD
  602. --*/
  603. {
  604. NTSTATUS Status;
  605. PIMAGE_RESOURCE_DIRECTORY LanguageResourceDirectory, ResourceDirectory, TopResourceDirectory;
  606. PIMAGE_RESOURCE_DIRECTORY_ENTRY ResourceDirEntLow;
  607. PIMAGE_RESOURCE_DIRECTORY_ENTRY ResourceDirEntMiddle;
  608. PIMAGE_RESOURCE_DIRECTORY_ENTRY ResourceDirEntHigh;
  609. PIMAGE_RESOURCE_DATA_ENTRY ResourceEntry;
  610. USHORT n, half;
  611. LONG dir;
  612. ULONG size;
  613. ULONG_PTR ResourceIdRetry;
  614. ULONG RetryCount;
  615. LANGID NewLangId;
  616. const ULONG_PTR* IdPath = ResourceIdPath;
  617. ULONG IdPathLength = ResourceIdPathLength;
  618. BOOLEAN fIsNeutral = FALSE;
  619. LANGID GivenLanguage;
  620. #ifndef NTOS_KERNEL_RUNTIME
  621. LCID DefaultThreadLocale, DefaultSystemLocale;
  622. PVOID AltResourceDllHandle = NULL;
  623. ULONG_PTR UIResourceIdPath[3];
  624. #endif
  625. RTL_PAGED_CODE();
  626. try {
  627. TopResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
  628. RtlImageDirectoryEntryToData(DllHandle,
  629. TRUE,
  630. IMAGE_DIRECTORY_ENTRY_RESOURCE,
  631. &size
  632. );
  633. if (!TopResourceDirectory) {
  634. return( STATUS_RESOURCE_DATA_NOT_FOUND );
  635. }
  636. ResourceDirectory = TopResourceDirectory;
  637. ResourceIdRetry = USE_FIRSTAVAILABLE_LANGID;
  638. RetryCount = 0;
  639. ResourceEntry = NULL;
  640. LanguageResourceDirectory = NULL;
  641. while (ResourceDirectory != NULL && ResourceIdPathLength--) {
  642. //
  643. // If search path includes a language id, then attempt to
  644. // match the following language ids in this order:
  645. //
  646. // (0) use given language id
  647. // (1) use primary language of given language id
  648. // (2) use id 0 (neutral resource)
  649. // (3) use thread language id for console app
  650. //
  651. // If the PRIMARY language id is ZERO, then ALSO attempt to
  652. // match the following language ids in this order:
  653. //
  654. // (4) use user UI language
  655. // (5) use lang id of TEB for windows app if it is different from user locale
  656. // (6) use UI lang from exe resource
  657. // (7) use primary UI lang from exe resource
  658. // (8) use Install Language
  659. // (9) use lang id from user's locale id
  660. // (10) use primary language of user's locale id
  661. // (11) use lang id from system default locale id
  662. // (12) use lang id of system default locale id
  663. // (13) use primary language of system default locale id
  664. // (14) use US English lang id
  665. // (15) use any lang id that matches requested info
  666. //
  667. if (ResourceIdPathLength == 0 && IdPathLength == 3) {
  668. LanguageResourceDirectory = ResourceDirectory;
  669. }
  670. if (LanguageResourceDirectory != NULL) {
  671. GivenLanguage = (LANGID)IdPath[ 2 ];
  672. fIsNeutral = (PRIMARYLANGID( GivenLanguage ) == LANG_NEUTRAL);
  673. TryNextLangId:
  674. switch( RetryCount++ ) {
  675. #ifdef NTOS_KERNEL_RUNTIME
  676. case 0: // Use given language id
  677. NewLangId = GivenLanguage;
  678. break;
  679. case 1: // Use primary language of given language id
  680. NewLangId = PRIMARYLANGID( GivenLanguage );
  681. break;
  682. case 2: // Use id 0 (neutral resource)
  683. NewLangId = 0;
  684. break;
  685. case 3: // Use user's default UI language
  686. NewLangId = (LANGID)ResourceIdRetry;
  687. break;
  688. case 4: // Use native UI language
  689. if ( !fIsNeutral ) {
  690. // Stop looking - Not in the neutral case
  691. goto ReturnFailure;
  692. break;
  693. }
  694. NewLangId = PsInstallUILanguageId;
  695. break;
  696. case 5: // Use default system locale
  697. NewLangId = LANGIDFROMLCID(PsDefaultSystemLocaleId);
  698. break;
  699. case 6:
  700. // Use US English language
  701. NewLangId = MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US );
  702. break;
  703. case 7: // Take any lang id that matches
  704. NewLangId = USE_FIRSTAVAILABLE_LANGID;
  705. break;
  706. #else
  707. case 0: // Use given language id
  708. NewLangId = GivenLanguage;
  709. break;
  710. case 1: // Use primary language of given language id
  711. if ( Flags & LDR_FIND_RESOURCE_LANGUAGE_EXACT) {
  712. //
  713. // Did not find an exact language match.
  714. // Stop looking.
  715. //
  716. goto ReturnFailure;
  717. }
  718. NewLangId = PRIMARYLANGID( GivenLanguage );
  719. break;
  720. case 2: // Use id 0 (neutral resource)
  721. NewLangId = 0;
  722. break;
  723. case 3:
  724. if ( !fIsNeutral ) {
  725. // Stop looking - Not in the neutral case
  726. NewLangId = (LANGID)ResourceIdRetry;
  727. break;
  728. }
  729. // Use thread langid if caller is a console app
  730. if (NtCurrentPeb()->ProcessParameters->ConsoleHandle)
  731. {
  732. NewLangId = LANGIDFROMLCID(NtCurrentTeb()->CurrentLocale);
  733. }
  734. else
  735. {
  736. NewLangId = (LANGID)ResourceIdRetry;
  737. }
  738. break;
  739. case 4: // Use user's default UI language
  740. if (!UILangId || NtCurrentTeb()->ImpersonationLocale){
  741. Status = NtQueryDefaultUILanguage( &UILangId );
  742. if (!NT_SUCCESS( Status )) {
  743. //
  744. // Failed reading key. Skip this lookup.
  745. //
  746. NewLangId = (LANGID)ResourceIdRetry;
  747. break;
  748. }
  749. }
  750. if (NtCurrentPeb()->ProcessParameters->ConsoleHandle &&
  751. LANGIDFROMLCID(NtCurrentTeb()->CurrentLocale) != UILangId)
  752. {
  753. NewLangId = (LANGID)ResourceIdRetry;
  754. break;
  755. }
  756. NewLangId = UILangId;
  757. //
  758. // Arabic/Hebrew MUI files may contain resources with LANG ID different than 401/40d.
  759. // e.g. Comdlg32.dll has two sets of Arabic/Hebrew resources one mirrored (401/40d)
  760. // and one flipped (801/80d).
  761. //
  762. if( !fIsNeutral &&
  763. ((PRIMARYLANGID (GivenLanguage) == LANG_ARABIC) || (PRIMARYLANGID (GivenLanguage) == LANG_HEBREW)) &&
  764. (PRIMARYLANGID (GivenLanguage) == PRIMARYLANGID (NewLangId))
  765. ) {
  766. NewLangId = GivenLanguage;
  767. }
  768. //
  769. // Bug #246044 WeiWu 12/07/00
  770. // BiDi modules use version block FileDescription field to store LRM markers,
  771. // LDR_FIND_RESOURCE_LANGUAGE_REDIRECT_VERSION will allow lpk.dll to get version resource from MUI alternative modules.
  772. //
  773. if ((IdPath[0] != RT_VERSION) ||
  774. (Flags & LDR_FIND_RESOURCE_LANGUAGE_REDIRECT_VERSION)) {
  775. //
  776. // Load alternate resource dll when:
  777. // 1. language is neutral
  778. // or
  779. // Given language is not tried.
  780. // and
  781. // 2. the resource to load is not a version info.
  782. //
  783. AltResourceDllHandle=LdrLoadAlternateResourceModule(
  784. DllHandle,
  785. NULL);
  786. if (!AltResourceDllHandle){
  787. //
  788. // Alternate resource dll not available.
  789. // Skip this lookup.
  790. //
  791. NewLangId = (LANGID)ResourceIdRetry;
  792. break;
  793. }
  794. //
  795. // Map to alternate resource dll and search
  796. // it instead.
  797. //
  798. UIResourceIdPath[0]=IdPath[0];
  799. UIResourceIdPath[1]=IdPath[1];
  800. UIResourceIdPath[2]=NewLangId;
  801. Status = LdrpSearchResourceSection_U(
  802. AltResourceDllHandle,
  803. UIResourceIdPath,
  804. 3,
  805. Flags | LDR_FIND_RESOURCE_LANGUAGE_EXACT,
  806. (PVOID *)ResourceDirectoryOrData
  807. );
  808. if (NT_SUCCESS(Status)){
  809. //
  810. // We sucessfully found alternate resource,
  811. // return it.
  812. //
  813. return Status;
  814. }
  815. }
  816. //
  817. // Caller does not want alternate resource, or
  818. // alternate resource not found.
  819. //
  820. NewLangId = (LANGID)ResourceIdRetry;
  821. break;
  822. case 5: // Use langid of the thread locale if caller is a Windows app and thread locale is different from user locale
  823. if ( !fIsNeutral ) {
  824. // Stop looking - Not in the neutral case
  825. goto ReturnFailure;
  826. break;
  827. }
  828. if (!NtCurrentPeb()->ProcessParameters->ConsoleHandle && NtCurrentTeb()){
  829. Status = NtQueryDefaultLocale(
  830. TRUE,
  831. &DefaultThreadLocale
  832. );
  833. if (NT_SUCCESS( Status ) &&
  834. DefaultThreadLocale !=
  835. NtCurrentTeb()->CurrentLocale) {
  836. //
  837. // Thread locale is different from
  838. // default locale.
  839. //
  840. NewLangId = LANGIDFROMLCID(NtCurrentTeb()->CurrentLocale);
  841. break;
  842. }
  843. }
  844. NewLangId = (LANGID)ResourceIdRetry;
  845. break;
  846. case 6: // UI language from the executable resource
  847. if (!UILangId){
  848. NewLangId = (LANGID)ResourceIdRetry;
  849. } else {
  850. NewLangId = UILangId;
  851. }
  852. break;
  853. case 7: // Parimary lang of UI language from the executable resource
  854. if (!UILangId){
  855. NewLangId = (LANGID)ResourceIdRetry;
  856. } else {
  857. NewLangId = PRIMARYLANGID( (LANGID) UILangId );
  858. }
  859. break;
  860. case 8: // Use install -native- language
  861. //
  862. // Thread locale is the same as the user locale, then let's
  863. // try loading the native (install) ui language resources.
  864. //
  865. if (!InstallLangId){
  866. Status = NtQueryInstallUILanguage(&InstallLangId);
  867. if (!NT_SUCCESS( Status )) {
  868. //
  869. // Failed reading key. Skip this lookup.
  870. //
  871. NewLangId = (LANGID)ResourceIdRetry;
  872. break;
  873. }
  874. }
  875. NewLangId = InstallLangId;
  876. break;
  877. case 9: // Use lang id from locale in TEB
  878. if (SUBLANGID( GivenLanguage ) == SUBLANG_SYS_DEFAULT) {
  879. // Skip over all USER locale options
  880. DefaultThreadLocale = 0;
  881. RetryCount += 2;
  882. break;
  883. }
  884. if (NtCurrentTeb() != NULL) {
  885. NewLangId = LANGIDFROMLCID(NtCurrentTeb()->CurrentLocale);
  886. }
  887. break;
  888. case 10: // Use User's default locale
  889. Status = NtQueryDefaultLocale( TRUE, &DefaultThreadLocale );
  890. if (NT_SUCCESS( Status )) {
  891. NewLangId = LANGIDFROMLCID(DefaultThreadLocale);
  892. break;
  893. }
  894. RetryCount++;
  895. break;
  896. case 11: // Use primary language of User's default locale
  897. NewLangId = PRIMARYLANGID( (LANGID)ResourceIdRetry );
  898. break;
  899. case 12: // Use System default locale
  900. Status = NtQueryDefaultLocale( FALSE, &DefaultSystemLocale );
  901. if (!NT_SUCCESS( Status )) {
  902. RetryCount++;
  903. break;
  904. }
  905. if (DefaultSystemLocale != DefaultThreadLocale) {
  906. NewLangId = LANGIDFROMLCID(DefaultSystemLocale);
  907. break;
  908. }
  909. RetryCount += 2;
  910. // fall through
  911. case 14: // Use US English language
  912. NewLangId = MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US );
  913. break;
  914. case 13: // Use primary language of System default locale
  915. NewLangId = PRIMARYLANGID( (LANGID)ResourceIdRetry );
  916. break;
  917. case 15: // Take any lang id that matches
  918. NewLangId = USE_FIRSTAVAILABLE_LANGID;
  919. break;
  920. #endif
  921. default: // No lang ids to match
  922. goto ReturnFailure;
  923. break;
  924. }
  925. //
  926. // If looking for a specific language id and same as the
  927. // one we just looked up, then skip it.
  928. //
  929. if (NewLangId != USE_FIRSTAVAILABLE_LANGID &&
  930. NewLangId == ResourceIdRetry
  931. ) {
  932. goto TryNextLangId;
  933. }
  934. //
  935. // Try this new language Id
  936. //
  937. ResourceIdRetry = (ULONG_PTR)NewLangId;
  938. ResourceIdPath = &ResourceIdRetry;
  939. ResourceDirectory = LanguageResourceDirectory;
  940. }
  941. n = ResourceDirectory->NumberOfNamedEntries;
  942. ResourceDirEntLow = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(ResourceDirectory+1);
  943. if (!(*ResourceIdPath & LDR_RESOURCE_ID_NAME_MASK)) {
  944. ResourceDirEntLow += n;
  945. n = ResourceDirectory->NumberOfIdEntries;
  946. }
  947. if (!n) {
  948. ResourceDirectory = NULL;
  949. goto NotFound;
  950. }
  951. if (LanguageResourceDirectory != NULL &&
  952. *ResourceIdPath == USE_FIRSTAVAILABLE_LANGID
  953. ) {
  954. ResourceDirectory = NULL;
  955. ResourceIdRetry = ResourceDirEntLow->Name;
  956. ResourceEntry = (PIMAGE_RESOURCE_DATA_ENTRY)
  957. ((PCHAR)TopResourceDirectory +
  958. ResourceDirEntLow->OffsetToData
  959. );
  960. break;
  961. }
  962. ResourceDirectory = NULL;
  963. ResourceDirEntHigh = ResourceDirEntLow + n - 1;
  964. while (ResourceDirEntLow <= ResourceDirEntHigh) {
  965. if ((half = (n >> 1)) != 0) {
  966. ResourceDirEntMiddle = ResourceDirEntLow;
  967. if (*(PUCHAR)&n & 1) {
  968. ResourceDirEntMiddle += half;
  969. }
  970. else {
  971. ResourceDirEntMiddle += half - 1;
  972. }
  973. dir = LdrpCompareResourceNames_U( *ResourceIdPath,
  974. TopResourceDirectory,
  975. ResourceDirEntMiddle
  976. );
  977. if (!dir) {
  978. if (ResourceDirEntMiddle->DataIsDirectory) {
  979. ResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
  980. ((PCHAR)TopResourceDirectory +
  981. ResourceDirEntMiddle->OffsetToDirectory
  982. );
  983. }
  984. else {
  985. ResourceDirectory = NULL;
  986. ResourceEntry = (PIMAGE_RESOURCE_DATA_ENTRY)
  987. ((PCHAR)TopResourceDirectory +
  988. ResourceDirEntMiddle->OffsetToData
  989. );
  990. }
  991. break;
  992. }
  993. else {
  994. if (dir < 0) {
  995. ResourceDirEntHigh = ResourceDirEntMiddle - 1;
  996. if (*(PUCHAR)&n & 1) {
  997. n = half;
  998. }
  999. else {
  1000. n = half - 1;
  1001. }
  1002. }
  1003. else {
  1004. ResourceDirEntLow = ResourceDirEntMiddle + 1;
  1005. n = half;
  1006. }
  1007. }
  1008. }
  1009. else {
  1010. if (n != 0) {
  1011. dir = LdrpCompareResourceNames_U( *ResourceIdPath,
  1012. TopResourceDirectory,
  1013. ResourceDirEntLow
  1014. );
  1015. if (!dir) {
  1016. if (ResourceDirEntLow->DataIsDirectory) {
  1017. ResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
  1018. ((PCHAR)TopResourceDirectory +
  1019. ResourceDirEntLow->OffsetToDirectory
  1020. );
  1021. }
  1022. else {
  1023. ResourceEntry = (PIMAGE_RESOURCE_DATA_ENTRY)
  1024. ((PCHAR)TopResourceDirectory +
  1025. ResourceDirEntLow->OffsetToData
  1026. );
  1027. }
  1028. }
  1029. }
  1030. break;
  1031. }
  1032. }
  1033. ResourceIdPath++;
  1034. }
  1035. if (ResourceEntry != NULL && !(Flags & LDRP_FIND_RESOURCE_DIRECTORY)) {
  1036. *ResourceDirectoryOrData = (PVOID)ResourceEntry;
  1037. Status = STATUS_SUCCESS;
  1038. }
  1039. else
  1040. if (ResourceDirectory != NULL && (Flags & LDRP_FIND_RESOURCE_DIRECTORY)) {
  1041. *ResourceDirectoryOrData = (PVOID)ResourceDirectory;
  1042. Status = STATUS_SUCCESS;
  1043. }
  1044. else {
  1045. NotFound:
  1046. switch( IdPathLength - ResourceIdPathLength) {
  1047. case 3: Status = STATUS_RESOURCE_LANG_NOT_FOUND; break;
  1048. case 2: Status = STATUS_RESOURCE_NAME_NOT_FOUND; break;
  1049. case 1: Status = STATUS_RESOURCE_TYPE_NOT_FOUND; break;
  1050. default: Status = STATUS_INVALID_PARAMETER; break;
  1051. }
  1052. }
  1053. if (Status == STATUS_RESOURCE_LANG_NOT_FOUND &&
  1054. LanguageResourceDirectory != NULL
  1055. ) {
  1056. ResourceEntry = NULL;
  1057. goto TryNextLangId;
  1058. ReturnFailure: ;
  1059. Status = STATUS_RESOURCE_LANG_NOT_FOUND;
  1060. }
  1061. }
  1062. except (EXCEPTION_EXECUTE_HANDLER) {
  1063. Status = GetExceptionCode();
  1064. }
  1065. return Status;
  1066. }
  1067. #ifndef NTOS_KERNEL_RUNTIME // { {
  1068. VOID
  1069. NTAPI
  1070. LdrDestroyOutOfProcessImage(
  1071. IN OUT PLDR_OUT_OF_PROCESS_IMAGE Image
  1072. )
  1073. /*++
  1074. Routine Description:
  1075. Arguments:
  1076. Image -
  1077. Return Value:
  1078. None.
  1079. --*/
  1080. {
  1081. NTSTATUS Status = STATUS_SUCCESS;
  1082. RtlFreeBuffer(&Image->HeadersBuffer);
  1083. Image->Flags = 0;
  1084. Image->ProcessHandle = NULL;
  1085. Image->DllHandle = NULL;
  1086. }
  1087. NTSTATUS
  1088. NTAPI
  1089. LdrCreateOutOfProcessImage(
  1090. IN ULONG Flags,
  1091. IN HANDLE ProcessHandle,
  1092. IN PVOID DllHandle,
  1093. OUT PLDR_OUT_OF_PROCESS_IMAGE Image
  1094. )
  1095. /*++
  1096. Routine Description:
  1097. This function initialized the out parameter Image for use with
  1098. other functions, like LdrFindOutOfProcessResource.
  1099. It reads in the headers, in order to work with many existing inproc
  1100. RtlImage* functions, without reading them in for every operation.
  1101. When you are done with the Image, pass it to LdrDestroyOutOfProcessImage.
  1102. Arguments:
  1103. Flags - fiddle with the behavior of the function
  1104. LDR_DLL_MAPPED_AS_DATA - "flat" memory mapping of file
  1105. LDR_DLL_MAPPED_AS_IMAGE - SEC_IMAGE was passed to NtCreateSection, inter section padding
  1106. reflected in offsets stored on disk is reflected in the address
  1107. space this is the simpler situation
  1108. LDR_DLL_MAPPED_AS_UNFORMATED_IMAGE - LDR_DLL_MAPPED_AS_IMAGE but LdrpWx86FormatVirtualImage
  1109. hasn't run yet.
  1110. ProcessHandle - The process DllHandle is in a mapped section in
  1111. DllHandle - the base address of a mapped view in process ProcessHandle
  1112. for legacy reasons, the lowest bit of this implies LDR_DLL_MAPPED_AS_DATA
  1113. Image - an opaque object that you can pass to other "OutOfProcessImage" functions.
  1114. Return Value:
  1115. NTSTATUS
  1116. --*/
  1117. {
  1118. PUCHAR RemoteAddress = NULL;
  1119. PRTL_BUFFER Buffer = NULL;
  1120. PIMAGE_DOS_HEADER DosHeader = NULL;
  1121. SIZE_T Headers32Offset = 0;
  1122. PIMAGE_NT_HEADERS32 Headers32 = NULL;
  1123. SIZE_T BytesRead = 0;
  1124. SIZE_T BytesToRead = 0;
  1125. SIZE_T Offset = 0;
  1126. SIZE_T InitialReadSize = 4096;
  1127. C_ASSERT(PAGE_SIZE >= 4096);
  1128. NTSTATUS Status = STATUS_SUCCESS;
  1129. KdPrintEx((
  1130. DPFLTR_LDR_ID,
  1131. DPFLTR_TRACE_LEVEL,
  1132. "LDR: %s(%lx, %p, %p, %p) beginning\n",
  1133. __FUNCTION__,
  1134. Flags,
  1135. ProcessHandle,
  1136. DllHandle,
  1137. Image
  1138. ));
  1139. // if this assertion triggers, you probably passed a handle instead of a base address
  1140. ASSERT(((ULONG_PTR)DllHandle) >= 0xffff);
  1141. // Unformated images are only ever 32bit on 64bit.
  1142. // The memory manager doesn't "spread them out", ntdll.dll does it.
  1143. // There is only a short span of time between the mapping of the image and
  1144. // the code in ntdll.dll reformating it, we leave it to our caller to know
  1145. // if they are in that path.
  1146. #if !defined(_WIN64) && !defined(BUILD_WOW6432)
  1147. if ((Flags & LDR_DLL_MAPPED_AS_MASK) == LDR_DLL_MAPPED_AS_UNFORMATED_IMAGE) {
  1148. Flags = (Flags & ~LDR_DLL_MAPPED_AS_MASK) | LDR_DLL_MAPPED_AS_IMAGE;
  1149. }
  1150. #endif
  1151. if (LDR_IS_DATAFILE(DllHandle)) {
  1152. DllHandle = LDR_DATAFILE_TO_VIEW(DllHandle);
  1153. ASSERT((Flags & LDR_DLL_MAPPED_AS_MASK) == 0 || (Flags & LDR_DLL_MAPPED_AS_MASK) == LDR_DLL_MAPPED_AS_DATA);
  1154. Flags |= LDR_DLL_MAPPED_AS_DATA;
  1155. }
  1156. Image->Flags = Flags;
  1157. Image->ProcessHandle = ProcessHandle;
  1158. Image->DllHandle = DllHandle;
  1159. RemoteAddress = (PUCHAR)DllHandle;
  1160. Buffer = &Image->HeadersBuffer;
  1161. RtlInitBuffer(Buffer, Image->PreallocatedHeadersBuffer, sizeof(Image->PreallocatedHeadersBuffer));
  1162. if (ProcessHandle == NtCurrentProcess()) {
  1163. Status = STATUS_SUCCESS;
  1164. goto Exit;
  1165. }
  1166. //
  1167. // first read 4k since that is generally enough and we can avoid
  1168. // multiple calls to NtReadVirtualMemory
  1169. //
  1170. // 4k is also the smallest page NT has run on, so even if the .exe is
  1171. // smaller than 4k, we should be able to read 4k
  1172. //
  1173. BytesToRead = InitialReadSize;
  1174. if (!NT_SUCCESS(Status = RtlEnsureBufferSize(0, Buffer, Offset + BytesToRead))) {
  1175. goto Exit;
  1176. }
  1177. Status = NtReadVirtualMemory(ProcessHandle, RemoteAddress + Offset, Buffer->Buffer + Offset, BytesToRead, &BytesRead);
  1178. if (Status == STATUS_PARTIAL_COPY && BytesRead != 0) {
  1179. InitialReadSize = BytesRead;
  1180. }
  1181. else if (!NT_SUCCESS(Status)) {
  1182. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL, "LDR: %s(): NtReadVirtualMemory failed.\n", __FUNCTION__));
  1183. goto Exit;
  1184. }
  1185. BytesToRead = sizeof(*DosHeader);
  1186. if (Offset + BytesToRead > InitialReadSize) {
  1187. if (!NT_SUCCESS(Status = RtlEnsureBufferSize(0, Buffer, Offset + BytesToRead))) {
  1188. goto Exit;
  1189. }
  1190. if (!NT_SUCCESS(Status = NtReadVirtualMemory(ProcessHandle, RemoteAddress + Offset, Buffer->Buffer + Offset, BytesToRead, &BytesRead))) {
  1191. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL, "LDR: %s(): NtReadVirtualMemory failed.\n", __FUNCTION__));
  1192. goto Exit;
  1193. }
  1194. if (BytesToRead != BytesRead) {
  1195. goto ReadTruncated;
  1196. }
  1197. }
  1198. DosHeader = (PIMAGE_DOS_HEADER)Buffer->Buffer;
  1199. if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
  1200. goto InvalidImageFormat;
  1201. }
  1202. if (DosHeader->e_lfanew >= RTLP_IMAGE_MAX_DOS_HEADER) {
  1203. goto InvalidImageFormat;
  1204. }
  1205. Offset += DosHeader->e_lfanew;
  1206. BytesToRead = RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader);
  1207. {
  1208. C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader)
  1209. == RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS64, FileHeader));
  1210. }
  1211. if (Offset + BytesToRead > InitialReadSize) {
  1212. if (!NT_SUCCESS(Status = RtlEnsureBufferSize(0, Buffer, Offset + BytesToRead))) {
  1213. goto Exit;
  1214. }
  1215. if (!NT_SUCCESS(Status = NtReadVirtualMemory(ProcessHandle, RemoteAddress + Offset, Buffer->Buffer + Offset, BytesToRead, &BytesRead))) {
  1216. goto Exit;
  1217. }
  1218. if (BytesToRead != BytesRead) {
  1219. goto ReadTruncated;
  1220. }
  1221. }
  1222. Headers32Offset = Offset;
  1223. Headers32 = (PIMAGE_NT_HEADERS32)(Buffer->Buffer + Headers32Offset); // correct for 64bit too
  1224. if (Headers32->Signature != IMAGE_NT_SIGNATURE) {
  1225. goto InvalidImageFormat;
  1226. }
  1227. Offset += BytesToRead;
  1228. BytesToRead = Headers32->FileHeader.SizeOfOptionalHeader
  1229. + Headers32->FileHeader.NumberOfSections * IMAGE_SIZEOF_SECTION_HEADER;
  1230. if (Offset + BytesToRead > InitialReadSize) {
  1231. if (!NT_SUCCESS(Status = RtlEnsureBufferSize(0, Buffer, Offset + BytesToRead))) {
  1232. goto Exit;
  1233. }
  1234. if (!NT_SUCCESS(Status = NtReadVirtualMemory(ProcessHandle, RemoteAddress + Offset, Buffer->Buffer + Offset, BytesToRead, &BytesRead))) {
  1235. goto Exit;
  1236. }
  1237. if (BytesToRead != BytesRead) {
  1238. goto ReadTruncated;
  1239. }
  1240. }
  1241. Headers32 = (PIMAGE_NT_HEADERS32)(Buffer->Buffer + Headers32Offset); // correct for 64bit too
  1242. if (Headers32->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC
  1243. && Headers32->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC
  1244. ) {
  1245. goto InvalidImageFormat;
  1246. }
  1247. #if defined(_WIN64) || defined(BUILD_WOW6432)
  1248. #define NATIVE_PAGE_SIZE 0x2000
  1249. if ((Image->Flags & LDR_DLL_MAPPED_AS_MASK) == LDR_DLL_MAPPED_AS_UNFORMATED_IMAGE) {
  1250. PIMAGE_NT_HEADERS64 Headers64 = (PIMAGE_NT_HEADERS64)Headers32;
  1251. // This test is copied from ntdll.dll's conditionall call to LdrpWx86FormatVirtualImage.
  1252. if (
  1253. Headers32->FileHeader.Machine == IMAGE_FILE_MACHINE_I386
  1254. && Headers32->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC
  1255. && Headers32->OptionalHeader.SectionAlignment < NATIVE_PAGE_SIZE
  1256. ) {
  1257. Image->Flags = (Image->Flags & ~LDR_DLL_MAPPED_AS_MASK) | LDR_DLL_MAPPED_AS_DATA;
  1258. } else {
  1259. Image->Flags = (Image->Flags & ~LDR_DLL_MAPPED_AS_MASK) | LDR_DLL_MAPPED_AS_IMAGE;
  1260. }
  1261. }
  1262. #endif
  1263. Status = STATUS_SUCCESS;
  1264. Exit:
  1265. if (!NT_SUCCESS(Status)) {
  1266. LdrDestroyOutOfProcessImage(Image);
  1267. }
  1268. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_LEVEL_STATUS(Status), "LDR: %s() exiting 0x%08lx\n", __FUNCTION__, Status));
  1269. return Status;
  1270. ReadTruncated:
  1271. Status = STATUS_UNSUCCESSFUL;
  1272. goto Exit;
  1273. InvalidImageFormat:
  1274. Status = STATUS_INVALID_IMAGE_FORMAT;
  1275. goto Exit;
  1276. }
  1277. NTSTATUS
  1278. NTAPI
  1279. LdrFindCreateProcessManifest(
  1280. IN ULONG Flags,
  1281. IN OUT PLDR_OUT_OF_PROCESS_IMAGE Image,
  1282. IN const ULONG_PTR* IdPath,
  1283. IN ULONG IdPathLength,
  1284. OUT PIMAGE_RESOURCE_DATA_ENTRY OutDataEntry
  1285. )
  1286. /*++
  1287. Routine Description:
  1288. This function is like LdrFindResource_U, but it can load resources
  1289. from a file mapped into another process. It only works as much
  1290. as has been needed.
  1291. Arguments:
  1292. Flags -
  1293. LDR_FIND_RESOURCE_LANGUAGE_CAN_FALLBACK - if the specified langid is not found,
  1294. fallback on the usual or any strategy,
  1295. the current implementation always loads the
  1296. first langid
  1297. LDR_FIND_RESOURCE_LANGUAGE_EXACT - only load the resource with exactly
  1298. specified langid
  1299. ProcessHandle - The process the DllHandle is valid in. Passed to NtReadVirtualMemory.
  1300. DllHandle - Same as LdrFindResource_U
  1301. ResourceIdPath - Same as LdrFindResource_U
  1302. ResourceIdPathLength - Same as LdrFindResource_U
  1303. OutDataEntry - Similar to LdrFindResource_U, but returned by value instead of address.
  1304. Return Value:
  1305. NTSTATUS
  1306. --*/
  1307. {
  1308. NTSTATUS Status = STATUS_SUCCESS;
  1309. SIZE_T BytesRead = 0;
  1310. SIZE_T BytesToRead = 0;
  1311. #if DBG
  1312. ULONG Line = __LINE__;
  1313. #endif
  1314. // we depend on these values sorting first
  1315. C_ASSERT(CREATEPROCESS_MANIFEST_RESOURCE_ID == 1);
  1316. C_ASSERT(ISOLATIONAWARE_MANIFEST_RESOURCE_ID == 2);
  1317. #if DBG
  1318. KdPrintEx((
  1319. DPFLTR_LDR_ID,
  1320. DPFLTR_TRACE_LEVEL,
  1321. "LDR: %s(0x%lx, %p, %p[%Id, %Id, %Id], %lu, %p) beginning\n",
  1322. __FUNCTION__,
  1323. Flags,
  1324. Image,
  1325. IdPath,
  1326. // 3 is the usual number, type, id/name, language
  1327. (IdPath != NULL && IdPathLength > 0) ? IdPath[0] : 0,
  1328. (IdPath != NULL && IdPathLength > 1) ? IdPath[1] : 0,
  1329. (IdPath != NULL && IdPathLength > 2) ? IdPath[2] : 0,
  1330. IdPathLength,
  1331. OutDataEntry
  1332. ));
  1333. #endif
  1334. #define LDRP_CHECK_PARAMETER(x) if (!(x)) { ASSERT(x); return STATUS_INVALID_PARAMETER; }
  1335. LDRP_CHECK_PARAMETER(Image != NULL);
  1336. LDRP_CHECK_PARAMETER(Image->DllHandle != NULL);
  1337. LDRP_CHECK_PARAMETER(Image->ProcessHandle != NULL);
  1338. LDRP_CHECK_PARAMETER(Image->HeadersBuffer.Buffer != NULL);
  1339. LDRP_CHECK_PARAMETER(OutDataEntry != NULL);
  1340. LDRP_CHECK_PARAMETER(IdPath != NULL);
  1341. LDRP_CHECK_PARAMETER((Image->Flags & LDR_DLL_MAPPED_AS_MASK) != LDR_DLL_MAPPED_AS_UNFORMATED_IMAGE);
  1342. // not all flags are implemented (only image vs. data is)
  1343. LDRP_CHECK_PARAMETER((Flags & LDR_FIND_RESOURCE_LANGUAGE_EXACT) == 0);
  1344. LDRP_CHECK_PARAMETER((Flags & LDRP_FIND_RESOURCE_DIRECTORY ) == 0);
  1345. RtlZeroMemory(OutDataEntry, sizeof(OutDataEntry));
  1346. if (Image->ProcessHandle == NtCurrentProcess()) {
  1347. PVOID DirectoryOrData = NULL;
  1348. Status = LdrpSearchResourceSection_U(
  1349. (Image->Flags & LDR_DLL_MAPPED_AS_DATA)
  1350. ? LDR_VIEW_TO_DATAFILE(Image->DllHandle)
  1351. : Image->DllHandle,
  1352. IdPath,
  1353. IdPathLength,
  1354. Flags,
  1355. &DirectoryOrData
  1356. );
  1357. if (NT_SUCCESS(Status) && DirectoryOrData != NULL && OutDataEntry != NULL) {
  1358. *OutDataEntry = *(PIMAGE_RESOURCE_DATA_ENTRY)DirectoryOrData;
  1359. }
  1360. goto Exit;
  1361. }
  1362. //
  1363. // All we handle cross process currently is finding the first resource id,
  1364. // first langid, of a given type.
  1365. //
  1366. // And we only handle numbers, not strings/names.
  1367. //
  1368. LDRP_CHECK_PARAMETER(IdPathLength == 3); // type, id/name, langid
  1369. LDRP_CHECK_PARAMETER(IdPath[0] != 0); // type
  1370. LDRP_CHECK_PARAMETER(IdPath[1] == 0 || IdPath[1] == CREATEPROCESS_MANIFEST_RESOURCE_ID); // just find first id
  1371. LDRP_CHECK_PARAMETER(IdPath[2] == 0); // first langid
  1372. // no strings/names, just numbers
  1373. LDRP_CHECK_PARAMETER(IS_INTRESOURCE(IdPath[0]));
  1374. LDRP_CHECK_PARAMETER(IS_INTRESOURCE(IdPath[1]));
  1375. LDRP_CHECK_PARAMETER(IS_INTRESOURCE(IdPath[2]));
  1376. __try {
  1377. USHORT n = 0;
  1378. USHORT half = 0;
  1379. LONG dir = 0;
  1380. SIZE_T TopDirectorySize = 0;
  1381. ULONG Size = 0;
  1382. PIMAGE_RESOURCE_DIRECTORY Directory = NULL;
  1383. PIMAGE_RESOURCE_DIRECTORY RemoteDirectoryAddress = NULL;
  1384. UCHAR DirectoryBuffer[
  1385. sizeof(IMAGE_RESOURCE_DIRECTORY)
  1386. + sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY)
  1387. ];
  1388. PIMAGE_RESOURCE_DIRECTORY TopDirectory = NULL;
  1389. PIMAGE_RESOURCE_DIRECTORY RemoteTopDirectoryAddress = NULL;
  1390. RTL_BUFFER TopDirectoryBuffer = {0};
  1391. UCHAR TopStaticDirectoryBuffer[256];
  1392. C_ASSERT(sizeof(TopStaticDirectoryBuffer) >= sizeof(*RemoteTopDirectoryAddress));
  1393. IMAGE_RESOURCE_DATA_ENTRY DataEntry;
  1394. PIMAGE_RESOURCE_DATA_ENTRY RemoteDataEntryAddress = NULL;
  1395. PIMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntry = NULL;
  1396. PIMAGE_RESOURCE_DIRECTORY_ENTRY DirEntLow = NULL;
  1397. PIMAGE_RESOURCE_DIRECTORY_ENTRY DirEntMiddle = NULL;
  1398. PIMAGE_RESOURCE_DIRECTORY_ENTRY DirEntHigh = NULL;
  1399. __try {
  1400. RtlInitBuffer(&TopDirectoryBuffer, TopStaticDirectoryBuffer, sizeof(TopStaticDirectoryBuffer));
  1401. Status = RtlEnsureBufferSize(0, &TopDirectoryBuffer, TopDirectoryBuffer.StaticSize);
  1402. ASSERT(NT_SUCCESS(Status));
  1403. RemoteTopDirectoryAddress = (PIMAGE_RESOURCE_DIRECTORY)
  1404. RtlImageDirectoryEntryToData(Image->HeadersBuffer.Buffer,
  1405. (Image->Flags & LDR_DLL_MAPPED_AS_DATA) ? FALSE : TRUE,
  1406. IMAGE_DIRECTORY_ENTRY_RESOURCE,
  1407. &Size
  1408. );
  1409. if (RemoteTopDirectoryAddress == NULL) {
  1410. Status = STATUS_RESOURCE_DATA_NOT_FOUND;
  1411. goto Exit;
  1412. }
  1413. //
  1414. // rebase..
  1415. //
  1416. RemoteTopDirectoryAddress =
  1417. (PIMAGE_RESOURCE_DIRECTORY)
  1418. (((PUCHAR)Image->DllHandle)
  1419. + (((PUCHAR)RemoteTopDirectoryAddress)
  1420. - ((PUCHAR)Image->HeadersBuffer.Buffer)));
  1421. Status = NtReadVirtualMemory(Image->ProcessHandle, RemoteTopDirectoryAddress, TopDirectoryBuffer.Buffer, TopDirectoryBuffer.Size, &BytesRead);
  1422. if (Status == STATUS_PARTIAL_COPY && BytesRead >= sizeof(*TopDirectory)) {
  1423. // nothing
  1424. }
  1425. else if (!NT_SUCCESS(Status)) {
  1426. goto Exit;
  1427. }
  1428. TopDirectory = (PIMAGE_RESOURCE_DIRECTORY)TopDirectoryBuffer.Buffer;
  1429. //
  1430. // determine the size of the entire directory, including the named entries,
  1431. // since they occur before the numbered ones (note that we currently
  1432. // don't optimize away reading of the named ones, even though we never
  1433. // search them)
  1434. //
  1435. TopDirectorySize = sizeof(*TopDirectory)
  1436. + (TopDirectory->NumberOfIdEntries + TopDirectory->NumberOfNamedEntries)
  1437. * sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY);
  1438. //
  1439. // now check the result of NtReadVirtualMemory again, if our guess was
  1440. // big enough, but the read was not, error
  1441. //
  1442. if (TopDirectorySize <= TopDirectoryBuffer.Size
  1443. && BytesRead < TopDirectorySize) {
  1444. // REVIEW STATUS_PARTIAL_COPY is only a warning. Is it a strong enough return value?
  1445. // Should we return STATUS_INVALID_IMAGE_FORMAT or STATUS_ACCESS_DENIED instead?
  1446. // There are other places in this file where we propogate STATUS_PARTIAL_COPY, if
  1447. // zero bytes are actually read.
  1448. if (Status == STATUS_PARTIAL_COPY) {
  1449. goto Exit;
  1450. }
  1451. #if DBG
  1452. Line = __LINE__;
  1453. #endif
  1454. goto ReadTruncated;
  1455. }
  1456. //
  1457. // if our initial guessed size was too small, read the correct size
  1458. //
  1459. if (TopDirectorySize > TopDirectoryBuffer.Size) {
  1460. KdPrintEx((
  1461. DPFLTR_LDR_ID,
  1462. DPFLTR_ERROR_LEVEL, // otherwise we'll never see it
  1463. "LDR: %s(): %Id was not enough of a preread for a resource directory, %Id required.\n",
  1464. __FUNCTION__,
  1465. TopDirectoryBuffer.Size,
  1466. TopDirectorySize
  1467. ));
  1468. Status = RtlEnsureBufferSize(0, &TopDirectoryBuffer, TopDirectorySize);
  1469. if (!NT_SUCCESS(Status)) {
  1470. goto Exit;
  1471. }
  1472. Status = NtReadVirtualMemory(Image->ProcessHandle, RemoteTopDirectoryAddress, TopDirectoryBuffer.Buffer, TopDirectoryBuffer.Size, &BytesRead);
  1473. if (!NT_SUCCESS(Status)) {
  1474. goto Exit;
  1475. }
  1476. if (BytesRead != TopDirectoryBuffer.Size) {
  1477. #if DBG
  1478. Line = __LINE__;
  1479. #endif
  1480. goto ReadTruncated;
  1481. }
  1482. TopDirectory = (PIMAGE_RESOURCE_DIRECTORY) TopDirectoryBuffer.Buffer;
  1483. }
  1484. // point to start of named entries
  1485. DirEntLow = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(TopDirectory + 1);
  1486. // move past named entries to numbered entries
  1487. DirEntLow += TopDirectory->NumberOfNamedEntries;
  1488. n = TopDirectory->NumberOfIdEntries;
  1489. if (n == 0) {
  1490. Status = STATUS_RESOURCE_TYPE_NOT_FOUND;
  1491. goto Exit;
  1492. }
  1493. DirectoryEntry = NULL;
  1494. Directory = NULL;
  1495. DirEntHigh = DirEntLow + n - 1;
  1496. while (DirEntLow <= DirEntHigh) {
  1497. if ((half = (n >> 1)) != 0) {
  1498. DirEntMiddle = DirEntLow;
  1499. if (n & 1) {
  1500. DirEntMiddle += half;
  1501. }
  1502. else {
  1503. DirEntMiddle += half - 1;
  1504. }
  1505. if (DirEntMiddle->NameIsString) {
  1506. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL, "LDR: No strings expected in %s().\n", __FUNCTION__));
  1507. ASSERT(FALSE);
  1508. Status = STATUS_INVALID_PARAMETER;
  1509. goto Exit;
  1510. }
  1511. dir = LdrpCompareResourceNames_U( *IdPath,
  1512. TopDirectory,
  1513. DirEntMiddle
  1514. );
  1515. if (dir == 0) {
  1516. if (DirEntMiddle->DataIsDirectory) {
  1517. Directory = (PIMAGE_RESOURCE_DIRECTORY)
  1518. (((PCHAR)TopDirectory)
  1519. + DirEntMiddle->OffsetToDirectory);
  1520. }
  1521. else {
  1522. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL, "LDR: %s(): First id in resource path is expected to be a directory.\n", __FUNCTION__));
  1523. Status = STATUS_INVALID_PARAMETER;
  1524. goto Exit;
  1525. /* This is what you do if we allow specifying the id and language,
  1526. which we might do in the future.
  1527. Directory = NULL;
  1528. Entry = (PIMAGE_RESOURCE_DATA_ENTRY)
  1529. (((PCHAR)TopDirectory)
  1530. + DirEntMiddle->OffsetToData);
  1531. */
  1532. }
  1533. break;
  1534. }
  1535. else {
  1536. if (dir < 0) {
  1537. DirEntHigh = DirEntMiddle - 1;
  1538. if (n & 1) {
  1539. n = half;
  1540. }
  1541. else {
  1542. n = half - 1;
  1543. }
  1544. }
  1545. else {
  1546. DirEntLow = DirEntMiddle + 1;
  1547. n = half;
  1548. }
  1549. }
  1550. }
  1551. else {
  1552. if (n != 0) {
  1553. if (DirEntLow->NameIsString) {
  1554. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL, "LDR: %s() No strings expected.\n", __FUNCTION__));
  1555. Status = STATUS_INVALID_PARAMETER;
  1556. goto Exit;
  1557. }
  1558. dir = LdrpCompareResourceNames_U( *IdPath,
  1559. TopDirectory,
  1560. DirEntLow
  1561. );
  1562. if (dir == 0) {
  1563. if (DirEntLow->DataIsDirectory) {
  1564. Directory = (PIMAGE_RESOURCE_DIRECTORY)
  1565. (((PCHAR)TopDirectory)
  1566. + DirEntLow->OffsetToDirectory);
  1567. }
  1568. else {
  1569. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL, "LDR: %s() First id in resource path is expected to be a directory", __FUNCTION__));
  1570. Status = STATUS_INVALID_PARAMETER;
  1571. goto Exit;
  1572. /*
  1573. Entry = (PIMAGE_RESOURCE_DATA_ENTRY)
  1574. (((PCHAR)TopDirectory)
  1575. + DirEntLow->OffsetToData);
  1576. */
  1577. }
  1578. }
  1579. }
  1580. break;
  1581. }
  1582. }
  1583. //
  1584. // ok, now we have found address of the type's name/id directory (or not)
  1585. //
  1586. if (Directory == NULL) {
  1587. Status = STATUS_RESOURCE_TYPE_NOT_FOUND;
  1588. goto Exit;
  1589. }
  1590. //
  1591. // we copied the binary search and didn't quite compute what we want,
  1592. // it found the local address, change this to an offset and apply
  1593. // to the remote address ("rebase")
  1594. //
  1595. RemoteDirectoryAddress =
  1596. (PIMAGE_RESOURCE_DIRECTORY)(
  1597. ((ULONG_PTR)RemoteTopDirectoryAddress)
  1598. + ((ULONG_PTR)Directory)
  1599. - ((ULONG_PTR)TopDirectory));
  1600. //
  1601. // Now do the read of both the directory and the first entry.
  1602. //
  1603. Directory = (PIMAGE_RESOURCE_DIRECTORY)&DirectoryBuffer;
  1604. Status = NtReadVirtualMemory(Image->ProcessHandle, RemoteDirectoryAddress, Directory, sizeof(DirectoryBuffer), &BytesRead);
  1605. if (!NT_SUCCESS(Status)) {
  1606. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL, "LDR: %s() NtReadVirtualMemory failed.", __FUNCTION__));
  1607. goto Exit;
  1608. }
  1609. if (BytesRead != sizeof(DirectoryBuffer)) {
  1610. #if DBG
  1611. Line = __LINE__;
  1612. #endif
  1613. goto ReadTruncated;
  1614. }
  1615. if ((Directory->NumberOfNamedEntries + Directory->NumberOfIdEntries) == 0) {
  1616. Status = STATUS_RESOURCE_NAME_NOT_FOUND;
  1617. goto Exit;
  1618. }
  1619. if (IdPath[1] == CREATEPROCESS_MANIFEST_RESOURCE_ID && Directory->NumberOfNamedEntries != 0) {
  1620. KdPrintEx((
  1621. DPFLTR_LDR_ID,
  1622. DPFLTR_ERROR_LEVEL,
  1623. "LDR: %s() caller asked for id==1 but there are named entries we are not bothering to skip.\n",
  1624. __FUNCTION__
  1625. ));
  1626. Status = STATUS_RESOURCE_NAME_NOT_FOUND;
  1627. goto Exit;
  1628. }
  1629. //
  1630. // grab the entry for the first id
  1631. //
  1632. DirectoryEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(Directory + 1);
  1633. if (!DirectoryEntry->DataIsDirectory) {
  1634. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL, "LDR: Second level of resource directory is expected to be a directory\n"));
  1635. Status = STATUS_INVALID_IMAGE_FORMAT; // REVIEW too strong?
  1636. goto Exit;
  1637. }
  1638. //
  1639. // If there is more than one entry, ensure no conflicts.
  1640. //
  1641. if (Directory->NumberOfIdEntries > 1
  1642. && DirectoryEntry->Id >= MINIMUM_RESERVED_MANIFEST_RESOURCE_ID
  1643. && DirectoryEntry->Id <= MAXIMUM_RESERVED_MANIFEST_RESOURCE_ID
  1644. ) {
  1645. PIMAGE_RESOURCE_DIRECTORY_ENTRY RemoteDirectoryEntryPointer;
  1646. IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[
  1647. MAXIMUM_RESERVED_MANIFEST_RESOURCE_ID
  1648. - MINIMUM_RESERVED_MANIFEST_RESOURCE_ID
  1649. + 1];
  1650. ULONG ResourceId;
  1651. ULONG NumberOfEntriesToCheck;
  1652. ULONG CountOfReservedManifestIds;
  1653. C_ASSERT(MINIMUM_RESERVED_MANIFEST_RESOURCE_ID == 1);
  1654. RemoteDirectoryEntryPointer = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(RemoteDirectoryAddress + 1);
  1655. NumberOfEntriesToCheck = LDRP_MIN(RTL_NUMBER_OF(DirectoryEntries), Directory->NumberOfIdEntries);
  1656. BytesToRead = sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY) * NumberOfEntriesToCheck;
  1657. ASSERT(BytesToRead <= sizeof(DirectoryEntries));
  1658. Status = NtReadVirtualMemory(Image->ProcessHandle, RemoteDirectoryEntryPointer, &DirectoryEntries, BytesToRead, &BytesRead);
  1659. if (!NT_SUCCESS(Status)) {
  1660. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL, "LDR: %s() NtReadVirtualMemory failed.", __FUNCTION__));
  1661. goto Exit;
  1662. }
  1663. if (BytesRead != BytesToRead) {
  1664. #if DBG
  1665. Line = __LINE__;
  1666. #endif
  1667. goto ReadTruncated;
  1668. }
  1669. CountOfReservedManifestIds = 0;
  1670. for (ResourceId = MINIMUM_RESERVED_MANIFEST_RESOURCE_ID;
  1671. ResourceId != MINIMUM_RESERVED_MANIFEST_RESOURCE_ID + NumberOfEntriesToCheck;
  1672. ResourceId += 1
  1673. ) {
  1674. if (DirectoryEntries[ResourceId - MINIMUM_RESERVED_MANIFEST_RESOURCE_ID].Id >= MINIMUM_RESERVED_MANIFEST_RESOURCE_ID
  1675. && DirectoryEntries[ResourceId - MINIMUM_RESERVED_MANIFEST_RESOURCE_ID].Id <= MAXIMUM_RESERVED_MANIFEST_RESOURCE_ID
  1676. ) {
  1677. CountOfReservedManifestIds += 1;
  1678. if (CountOfReservedManifestIds > 1) {
  1679. #if DBG
  1680. DbgPrintEx(
  1681. DPFLTR_LDR_ID,
  1682. DPFLTR_ERROR_LEVEL,
  1683. "LDR: %s() multiple reserved manifest resource ids present\n",
  1684. __FUNCTION__
  1685. );
  1686. #endif
  1687. Status = STATUS_INVALID_PARAMETER;
  1688. goto Exit;
  1689. }
  1690. }
  1691. }
  1692. }
  1693. if (IdPath[1] == CREATEPROCESS_MANIFEST_RESOURCE_ID && DirectoryEntry->Id != CREATEPROCESS_MANIFEST_RESOURCE_ID) {
  1694. Status = STATUS_RESOURCE_NAME_NOT_FOUND;
  1695. goto Exit;
  1696. }
  1697. //
  1698. // now get address of langid directory
  1699. //
  1700. RemoteDirectoryAddress =
  1701. (PIMAGE_RESOURCE_DIRECTORY)(
  1702. ((ULONG_PTR)RemoteTopDirectoryAddress)
  1703. + DirectoryEntry->OffsetToDirectory);
  1704. //
  1705. // now read the langid directory and its first entry
  1706. //
  1707. Status = NtReadVirtualMemory(Image->ProcessHandle, RemoteDirectoryAddress, Directory, sizeof(DirectoryBuffer), &BytesRead);
  1708. if (!NT_SUCCESS(Status)) {
  1709. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL, "LDR: %s() NtReadVirtualMemory failed.", __FUNCTION__));
  1710. goto Exit;
  1711. }
  1712. if (BytesRead != sizeof(DirectoryBuffer)) {
  1713. goto ReadTruncated;
  1714. }
  1715. if ((Directory->NumberOfNamedEntries + Directory->NumberOfIdEntries) == 0) {
  1716. Status = STATUS_RESOURCE_LANG_NOT_FOUND;
  1717. goto Exit;
  1718. }
  1719. //
  1720. // look at the langid directory's first entry
  1721. //
  1722. if (DirectoryEntry->DataIsDirectory) {
  1723. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL, "LDR: Third level of resource directory is not expected to be a directory\n"));
  1724. Status = STATUS_INVALID_IMAGE_FORMAT; // REVIEW too strong?
  1725. goto Exit;
  1726. }
  1727. RemoteDataEntryAddress =
  1728. (PIMAGE_RESOURCE_DATA_ENTRY)(
  1729. ((ULONG_PTR)RemoteTopDirectoryAddress)
  1730. + DirectoryEntry->OffsetToData);
  1731. //
  1732. // read the data entry
  1733. //
  1734. Status = NtReadVirtualMemory(Image->ProcessHandle, RemoteDataEntryAddress, &DataEntry, sizeof(DataEntry), &BytesRead);
  1735. if (!NT_SUCCESS(Status)) {
  1736. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL, "LDR: %s() NtReadVirtualMemory failed.", __FUNCTION__));
  1737. goto Exit;
  1738. }
  1739. if (BytesRead != sizeof(DataEntry)) {
  1740. goto ReadTruncated;
  1741. }
  1742. *OutDataEntry = DataEntry;
  1743. Status = STATUS_SUCCESS;
  1744. }
  1745. __finally {
  1746. RtlFreeBuffer(&TopDirectoryBuffer);
  1747. }
  1748. }
  1749. __except (EXCEPTION_EXECUTE_HANDLER) {
  1750. Status = GetExceptionCode();
  1751. }
  1752. Exit:
  1753. #if DBG
  1754. //
  1755. // Fix/raid dcpromo, msiexec, etc..
  1756. // DPFLTR_LEVEL_STATUS filters all forms of resource not found.
  1757. //
  1758. if (DPFLTR_LEVEL_STATUS(Status) == DPFLTR_ERROR_LEVEL) {
  1759. KdPrintEx((
  1760. DPFLTR_LDR_ID,
  1761. DPFLTR_LEVEL_STATUS(Status),
  1762. "LDR: %s(0x%lx, %p, %p[%Id, %Id, %Id], %lu, %p) failed %08x\n",
  1763. __FUNCTION__,
  1764. Flags,
  1765. Image,
  1766. IdPath,
  1767. // 3 is the usual number, type, id/name, language
  1768. (IdPath != NULL && IdPathLength > 0) ? IdPath[0] : 0,
  1769. (IdPath != NULL && IdPathLength > 1) ? IdPath[1] : 0,
  1770. (IdPath != NULL && IdPathLength > 2) ? IdPath[2] : 0,
  1771. IdPathLength,
  1772. OutDataEntry,
  1773. Status
  1774. ));
  1775. KdPrintEx((
  1776. DPFLTR_LDR_ID,
  1777. DPFLTR_LEVEL_STATUS(Status),
  1778. "LDR: %s() returning Status:0x%lx IMAGE_RESOURCE_DATA_ENTRY:{OffsetToData=%#lx, Size=%#lx}\n",
  1779. __FUNCTION__,
  1780. Status,
  1781. (OutDataEntry != NULL) ? OutDataEntry->OffsetToData : 0,
  1782. (OutDataEntry != NULL) ? OutDataEntry->Size : 0
  1783. ));
  1784. }
  1785. #endif
  1786. return Status;
  1787. ReadTruncated:
  1788. #if DBG
  1789. DbgPrintEx(DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL,
  1790. "LDR: %s(): Line %lu NtReadVirtualMemory() succeeded, but returned too few bytes (0x%Ix out of 0x%Ix).\n",
  1791. __FUNCTION__, Line, BytesRead, BytesToRead);
  1792. #endif
  1793. Status = STATUS_UNSUCCESSFUL;
  1794. goto Exit;
  1795. }
  1796. NTSTATUS
  1797. NTAPI
  1798. LdrAccessOutOfProcessResource(
  1799. IN ULONG Flags,
  1800. IN OUT PLDR_OUT_OF_PROCESS_IMAGE Image, // currently only IN
  1801. IN const IMAGE_RESOURCE_DATA_ENTRY* DataEntry,
  1802. OUT PVOID* Address OPTIONAL,
  1803. OUT PULONG Size OPTIONAL
  1804. )
  1805. /*++
  1806. Routine Description:
  1807. This function is like LdrAccessResource, but it works on images
  1808. mapped out of process.
  1809. Arguments:
  1810. Flags -
  1811. Image - an opaque object representing an image or file mapped into another process,
  1812. created with LdrCreateOutOfProcessImage.
  1813. DataEntry - Same as LdrAccessResource, but returned by-value from LdrFindOutOfProcessResource
  1814. Address - Same as LdrAccessResource
  1815. Size - Same as LdrAccessResource
  1816. Return Value:
  1817. NTSTATUS
  1818. --*/
  1819. {
  1820. NTSTATUS Status;
  1821. ASSERT(Image != NULL);
  1822. ASSERT(Image->DllHandle != NULL);
  1823. ASSERT(Image->ProcessHandle != NULL);
  1824. ASSERT(Image->HeadersBuffer.Buffer != NULL);
  1825. if (Image->ProcessHandle != NtCurrentProcess()) {
  1826. Status = LdrpAccessResourceDataNoMultipleLanguage(
  1827. (Image->Flags & LDR_DLL_MAPPED_AS_DATA)
  1828. ? LDR_VIEW_TO_DATAFILE(Image->HeadersBuffer.Buffer)
  1829. : Image->HeadersBuffer.Buffer,
  1830. DataEntry,
  1831. Address,
  1832. Size
  1833. );
  1834. //
  1835. // rebase..
  1836. //
  1837. if (NT_SUCCESS(Status) && Address != NULL && *Address != NULL) {
  1838. *Address =
  1839. (((PUCHAR)Image->DllHandle)
  1840. + (((PUCHAR)*Address)
  1841. - ((PUCHAR)Image->HeadersBuffer.Buffer)));
  1842. }
  1843. } else {
  1844. Status = LdrpAccessResourceDataNoMultipleLanguage(
  1845. (Image->Flags & LDR_DLL_MAPPED_AS_DATA)
  1846. ? LDR_VIEW_TO_DATAFILE(Image->DllHandle)
  1847. : Image->DllHandle,
  1848. DataEntry,
  1849. Address,
  1850. Size
  1851. );
  1852. }
  1853. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_LEVEL_STATUS(Status), "LDR: %s() exiting 0x%08lx\n", __FUNCTION__, Status));
  1854. return Status;
  1855. }
  1856. #endif // } }
  1857. NTSTATUS
  1858. LdrEnumResources(
  1859. IN PVOID DllHandle,
  1860. IN const ULONG_PTR* ResourceIdPath,
  1861. IN ULONG ResourceIdPathLength,
  1862. IN OUT PULONG NumberOfResources,
  1863. OUT PLDR_ENUM_RESOURCE_ENTRY Resources OPTIONAL
  1864. )
  1865. {
  1866. NTSTATUS Status;
  1867. PIMAGE_RESOURCE_DIRECTORY TopResourceDirectory;
  1868. PIMAGE_RESOURCE_DIRECTORY TypeResourceDirectory;
  1869. PIMAGE_RESOURCE_DIRECTORY NameResourceDirectory;
  1870. PIMAGE_RESOURCE_DIRECTORY LangResourceDirectory;
  1871. PIMAGE_RESOURCE_DIRECTORY_ENTRY TypeResourceDirectoryEntry;
  1872. PIMAGE_RESOURCE_DIRECTORY_ENTRY NameResourceDirectoryEntry;
  1873. PIMAGE_RESOURCE_DIRECTORY_ENTRY LangResourceDirectoryEntry;
  1874. ULONG TypeDirectoryIndex, NumberOfTypeDirectoryEntries;
  1875. ULONG NameDirectoryIndex, NumberOfNameDirectoryEntries;
  1876. ULONG LangDirectoryIndex, NumberOfLangDirectoryEntries;
  1877. BOOLEAN ScanTypeDirectory;
  1878. BOOLEAN ScanNameDirectory;
  1879. BOOLEAN ReturnThisResource;
  1880. PIMAGE_RESOURCE_DIR_STRING_U ResourceNameString;
  1881. ULONG_PTR TypeResourceNameOrId;
  1882. ULONG_PTR NameResourceNameOrId;
  1883. ULONG_PTR LangResourceNameOrId;
  1884. PLDR_ENUM_RESOURCE_ENTRY ResourceInfo;
  1885. PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry;
  1886. ULONG ResourceIndex, MaxResourceIndex;
  1887. ULONG Size;
  1888. ResourceIndex = 0;
  1889. if (!ARGUMENT_PRESENT( Resources )) {
  1890. MaxResourceIndex = 0;
  1891. }
  1892. else {
  1893. MaxResourceIndex = *NumberOfResources;
  1894. }
  1895. *NumberOfResources = 0;
  1896. TopResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
  1897. RtlImageDirectoryEntryToData( DllHandle,
  1898. TRUE,
  1899. IMAGE_DIRECTORY_ENTRY_RESOURCE,
  1900. &Size
  1901. );
  1902. if (!TopResourceDirectory) {
  1903. return STATUS_RESOURCE_DATA_NOT_FOUND;
  1904. }
  1905. TypeResourceDirectory = TopResourceDirectory;
  1906. TypeResourceDirectoryEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(TypeResourceDirectory+1);
  1907. NumberOfTypeDirectoryEntries = TypeResourceDirectory->NumberOfNamedEntries +
  1908. TypeResourceDirectory->NumberOfIdEntries;
  1909. TypeDirectoryIndex = 0;
  1910. Status = STATUS_SUCCESS;
  1911. for (TypeDirectoryIndex=0;
  1912. TypeDirectoryIndex<NumberOfTypeDirectoryEntries;
  1913. TypeDirectoryIndex++, TypeResourceDirectoryEntry++
  1914. ) {
  1915. if (ResourceIdPathLength > 0) {
  1916. ScanTypeDirectory = LdrpCompareResourceNames_U( ResourceIdPath[ 0 ],
  1917. TopResourceDirectory,
  1918. TypeResourceDirectoryEntry
  1919. ) == 0;
  1920. }
  1921. else {
  1922. ScanTypeDirectory = TRUE;
  1923. }
  1924. if (ScanTypeDirectory) {
  1925. if (!TypeResourceDirectoryEntry->DataIsDirectory) {
  1926. return STATUS_INVALID_IMAGE_FORMAT;
  1927. }
  1928. if (TypeResourceDirectoryEntry->NameIsString) {
  1929. ResourceNameString = (PIMAGE_RESOURCE_DIR_STRING_U)
  1930. ((PCHAR)TopResourceDirectory + TypeResourceDirectoryEntry->NameOffset);
  1931. TypeResourceNameOrId = (ULONG_PTR)ResourceNameString;
  1932. }
  1933. else {
  1934. TypeResourceNameOrId = (ULONG_PTR)TypeResourceDirectoryEntry->Id;
  1935. }
  1936. NameResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
  1937. ((PCHAR)TopResourceDirectory + TypeResourceDirectoryEntry->OffsetToDirectory);
  1938. NameResourceDirectoryEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(NameResourceDirectory+1);
  1939. NumberOfNameDirectoryEntries = NameResourceDirectory->NumberOfNamedEntries +
  1940. NameResourceDirectory->NumberOfIdEntries;
  1941. for (NameDirectoryIndex=0;
  1942. NameDirectoryIndex<NumberOfNameDirectoryEntries;
  1943. NameDirectoryIndex++, NameResourceDirectoryEntry++
  1944. ) {
  1945. if (ResourceIdPathLength > 1) {
  1946. ScanNameDirectory = LdrpCompareResourceNames_U( ResourceIdPath[ 1 ],
  1947. TopResourceDirectory,
  1948. NameResourceDirectoryEntry
  1949. ) == 0;
  1950. }
  1951. else {
  1952. ScanNameDirectory = TRUE;
  1953. }
  1954. if (ScanNameDirectory) {
  1955. if (!NameResourceDirectoryEntry->DataIsDirectory) {
  1956. return STATUS_INVALID_IMAGE_FORMAT;
  1957. }
  1958. if (NameResourceDirectoryEntry->NameIsString) {
  1959. ResourceNameString = (PIMAGE_RESOURCE_DIR_STRING_U)
  1960. ((PCHAR)TopResourceDirectory + NameResourceDirectoryEntry->NameOffset);
  1961. NameResourceNameOrId = (ULONG_PTR)ResourceNameString;
  1962. }
  1963. else {
  1964. NameResourceNameOrId = (ULONG_PTR)NameResourceDirectoryEntry->Id;
  1965. }
  1966. LangResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
  1967. ((PCHAR)TopResourceDirectory + NameResourceDirectoryEntry->OffsetToDirectory);
  1968. LangResourceDirectoryEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(LangResourceDirectory+1);
  1969. NumberOfLangDirectoryEntries = LangResourceDirectory->NumberOfNamedEntries +
  1970. LangResourceDirectory->NumberOfIdEntries;
  1971. LangDirectoryIndex = 0;
  1972. for (LangDirectoryIndex=0;
  1973. LangDirectoryIndex<NumberOfLangDirectoryEntries;
  1974. LangDirectoryIndex++, LangResourceDirectoryEntry++
  1975. ) {
  1976. if (ResourceIdPathLength > 2) {
  1977. ReturnThisResource = LdrpCompareResourceNames_U( ResourceIdPath[ 2 ],
  1978. TopResourceDirectory,
  1979. LangResourceDirectoryEntry
  1980. ) == 0;
  1981. }
  1982. else {
  1983. ReturnThisResource = TRUE;
  1984. }
  1985. if (ReturnThisResource) {
  1986. if (LangResourceDirectoryEntry->DataIsDirectory) {
  1987. return STATUS_INVALID_IMAGE_FORMAT;
  1988. }
  1989. if (LangResourceDirectoryEntry->NameIsString) {
  1990. ResourceNameString = (PIMAGE_RESOURCE_DIR_STRING_U)
  1991. ((PCHAR)TopResourceDirectory + LangResourceDirectoryEntry->NameOffset);
  1992. LangResourceNameOrId = (ULONG_PTR)ResourceNameString;
  1993. }
  1994. else {
  1995. LangResourceNameOrId = (ULONG_PTR)LangResourceDirectoryEntry->Id;
  1996. }
  1997. ResourceDataEntry = (PIMAGE_RESOURCE_DATA_ENTRY)
  1998. ((PCHAR)TopResourceDirectory + LangResourceDirectoryEntry->OffsetToData);
  1999. ResourceInfo = &Resources[ ResourceIndex++ ];
  2000. if (ResourceIndex <= MaxResourceIndex) {
  2001. ResourceInfo->Path[ 0 ].NameOrId = TypeResourceNameOrId;
  2002. ResourceInfo->Path[ 1 ].NameOrId = NameResourceNameOrId;
  2003. ResourceInfo->Path[ 2 ].NameOrId = LangResourceNameOrId;
  2004. ResourceInfo->Data = (PVOID)((ULONG_PTR)DllHandle + ResourceDataEntry->OffsetToData);
  2005. ResourceInfo->Size = ResourceDataEntry->Size;
  2006. ResourceInfo->Reserved = 0;
  2007. }
  2008. else {
  2009. Status = STATUS_INFO_LENGTH_MISMATCH;
  2010. }
  2011. }
  2012. }
  2013. }
  2014. }
  2015. }
  2016. }
  2017. *NumberOfResources = ResourceIndex;
  2018. return Status;
  2019. }
  2020. #ifndef NTOS_KERNEL_RUNTIME
  2021. BOOLEAN
  2022. LdrAlternateResourcesEnabled(
  2023. VOID
  2024. )
  2025. /*++
  2026. Routine Description:
  2027. This function determines if the althernate resources are enabled.
  2028. Arguments:
  2029. None.
  2030. Return Value:
  2031. True - Alternate Resource enabled.
  2032. False - Alternate Resource not enabled.
  2033. --*/
  2034. {
  2035. NTSTATUS Status;
  2036. if (!UILangId || NtCurrentTeb()->ImpersonationLocale){
  2037. Status = NtQueryDefaultUILanguage( &UILangId );
  2038. if (!NT_SUCCESS( Status )) {
  2039. //
  2040. // Failed to get UI LangID. AltResource not enabled.
  2041. //
  2042. return FALSE;
  2043. }
  2044. }
  2045. if (!InstallLangId){
  2046. Status = NtQueryInstallUILanguage( &InstallLangId);
  2047. if (!NT_SUCCESS( Status )) {
  2048. //
  2049. // Failed to get Intall LangID. AltResource not enabled.
  2050. //
  2051. return FALSE;
  2052. }
  2053. }
  2054. if (UILangId == InstallLangId) {
  2055. //
  2056. // UI Lang matches Installed Lang. AltResource not enabled.
  2057. //
  2058. return FALSE;
  2059. }
  2060. return TRUE;
  2061. }
  2062. PVOID
  2063. LdrGetAlternateResourceModuleHandle(
  2064. IN PVOID Module
  2065. )
  2066. /*++
  2067. Routine Description:
  2068. This function gets the alternate resource module from the table
  2069. containing the handle.
  2070. Arguments:
  2071. Module - Module of which alternate resource module needs to loaded.
  2072. Return Value:
  2073. Handle of the alternate resource module.
  2074. --*/
  2075. {
  2076. ULONG ModuleIndex;
  2077. for (ModuleIndex = 0;
  2078. ModuleIndex < AlternateResourceModuleCount;
  2079. ModuleIndex++ ){
  2080. if (AlternateResourceModules[ModuleIndex].ModuleBase ==
  2081. Module){
  2082. return AlternateResourceModules[ModuleIndex].AlternateModule;
  2083. }
  2084. }
  2085. return NULL;
  2086. }
  2087. BOOLEAN
  2088. LdrpGetFileVersion(
  2089. IN PVOID ImageBase,
  2090. IN LANGID LangId,
  2091. OUT PULONGLONG Version
  2092. )
  2093. /*++
  2094. Routine Description:
  2095. Get the version stamp out of the VS_FIXEDFILEINFO resource in a PE
  2096. image.
  2097. Arguments:
  2098. ImageBase - supplies the address in memory where the file is mapped in.
  2099. Version - receives 64bit version number, or 0 if the file is not
  2100. a PE image or has no version data.
  2101. Return Value:
  2102. None.
  2103. --*/
  2104. {
  2105. PIMAGE_RESOURCE_DATA_ENTRY DataEntry;
  2106. NTSTATUS Status;
  2107. ULONG_PTR IdPath[3];
  2108. ULONG ResourceSize;
  2109. typedef struct tagVS_FIXEDFILEINFO
  2110. {
  2111. LONG dwSignature; /* e.g. 0xfeef04bd */
  2112. LONG dwStrucVersion; /* e.g. 0x00000042 = "0.42" */
  2113. LONG dwFileVersionMS; /* e.g. 0x00030075 = "3.75" */
  2114. LONG dwFileVersionLS; /* e.g. 0x00000031 = "0.31" */
  2115. LONG dwProductVersionMS; /* e.g. 0x00030010 = "3.10" */
  2116. LONG dwProductVersionLS; /* e.g. 0x00000031 = "0.31" */
  2117. LONG dwFileFlagsMask; /* = 0x3F for version "0.42" */
  2118. LONG dwFileFlags; /* e.g. VFF_DEBUG | VFF_PRERELEASE */
  2119. LONG dwFileOS; /* e.g. VOS_DOS_WINDOWS16 */
  2120. LONG dwFileType; /* e.g. VFT_DRIVER */
  2121. LONG dwFileSubtype; /* e.g. VFT2_DRV_KEYBOARD */
  2122. LONG dwFileDateMS; /* e.g. 0 */
  2123. LONG dwFileDateLS; /* e.g. 0 */
  2124. } VS_FIXEDFILEINFO;
  2125. struct {
  2126. USHORT TotalSize;
  2127. USHORT DataSize;
  2128. USHORT Type;
  2129. WCHAR Name[16]; // L"VS_VERSION_INFO" + unicode null terminator
  2130. VS_FIXEDFILEINFO FixedFileInfo;
  2131. } *Resource;
  2132. *Version = 0;
  2133. IdPath[0] = RT_VERSION;
  2134. IdPath[1] = 1;
  2135. IdPath[2] = LangId;
  2136. try {
  2137. Status = LdrpSearchResourceSection_U(
  2138. ImageBase,
  2139. IdPath,
  2140. 3,
  2141. LDR_FIND_RESOURCE_LANGUAGE_EXACT,
  2142. &DataEntry);
  2143. } except(EXCEPTION_EXECUTE_HANDLER) {
  2144. Status = STATUS_UNSUCCESSFUL;
  2145. }
  2146. if(!NT_SUCCESS(Status)) {
  2147. return FALSE;
  2148. }
  2149. try {
  2150. Status = LdrpAccessResourceData(
  2151. ImageBase,
  2152. DataEntry,
  2153. &Resource,
  2154. &ResourceSize);
  2155. } except(EXCEPTION_EXECUTE_HANDLER) {
  2156. Status = STATUS_UNSUCCESSFUL;
  2157. }
  2158. if(!NT_SUCCESS(Status)) {
  2159. return FALSE;
  2160. }
  2161. try {
  2162. if((ResourceSize >= sizeof(*Resource))
  2163. && !_wcsicmp(Resource->Name,L"VS_VERSION_INFO")) {
  2164. *Version = ((ULONGLONG)Resource->FixedFileInfo.dwFileVersionMS << 32)
  2165. | (ULONGLONG)Resource->FixedFileInfo.dwFileVersionLS;
  2166. } else {
  2167. DbgPrint(("LDR: Warning: invalid version resource\n"));
  2168. return FALSE;
  2169. }
  2170. } except(EXCEPTION_EXECUTE_HANDLER) {
  2171. DbgPrint(("LDR: Exception encountered processing bogus version resource\n"));
  2172. return FALSE;
  2173. }
  2174. return TRUE;
  2175. }
  2176. BOOLEAN
  2177. LdrpGetResourceChecksum(
  2178. IN PVOID Module,
  2179. OUT unsigned char** ppMD5Checksum
  2180. )
  2181. {
  2182. NTSTATUS Status;
  2183. ULONG_PTR IdPath[3];
  2184. ULONG ResourceSize;
  2185. PIMAGE_RESOURCE_DATA_ENTRY DataEntry;
  2186. LONG BlockLen;
  2187. LONG VarFileInfoSize;
  2188. typedef struct tagVS_FIXEDFILEINFO
  2189. {
  2190. LONG dwSignature; /* e.g. 0xfeef04bd */
  2191. LONG dwStrucVersion; /* e.g. 0x00000042 = "0.42" */
  2192. LONG dwFileVersionMS; /* e.g. 0x00030075 = "3.75" */
  2193. LONG dwFileVersionLS; /* e.g. 0x00000031 = "0.31" */
  2194. LONG dwProductVersionMS; /* e.g. 0x00030010 = "3.10" */
  2195. LONG dwProductVersionLS; /* e.g. 0x00000031 = "0.31" */
  2196. LONG dwFileFlagsMask; /* = 0x3F for version "0.42" */
  2197. LONG dwFileFlags; /* e.g. VFF_DEBUG | VFF_PRERELEASE */
  2198. LONG dwFileOS; /* e.g. VOS_DOS_WINDOWS16 */
  2199. LONG dwFileType; /* e.g. VFT_DRIVER */
  2200. LONG dwFileSubtype; /* e.g. VFT2_DRV_KEYBOARD */
  2201. LONG dwFileDateMS; /* e.g. 0 */
  2202. LONG dwFileDateLS; /* e.g. 0 */
  2203. } VS_FIXEDFILEINFO;
  2204. struct
  2205. {
  2206. USHORT TotalSize;
  2207. USHORT DataSize;
  2208. USHORT Type;
  2209. WCHAR Name[16]; // L"VS_VERSION_INFO" + unicode null terminator
  2210. // Note that the previous 4 members has 16*2 + 3*2 = 38 bytes.
  2211. // So that compiler will silently add a 2 bytes padding to make
  2212. // FixedFileInfo to align in DWORD boundary.
  2213. VS_FIXEDFILEINFO FixedFileInfo;
  2214. } *Resource;
  2215. typedef struct tagVERBLOCK
  2216. {
  2217. USHORT wTotalLen;
  2218. USHORT wValueLen;
  2219. USHORT wType;
  2220. WCHAR szKey[1];
  2221. // BYTE[] padding
  2222. // WORD value;
  2223. } VERBLOCK;
  2224. VERBLOCK *pVerBlock;
  2225. IdPath[0] = RT_VERSION;
  2226. IdPath[1] = 1;
  2227. IdPath[2] = 0;
  2228. //
  2229. // find the version resource data entry
  2230. //
  2231. try
  2232. {
  2233. Status = LdrFindResource_U(Module,IdPath,3,&DataEntry);
  2234. } except(EXCEPTION_EXECUTE_HANDLER)
  2235. {
  2236. Status = STATUS_UNSUCCESSFUL;
  2237. }
  2238. if(!NT_SUCCESS(Status))
  2239. {
  2240. return (FALSE);
  2241. }
  2242. //
  2243. // Access the version resource data.
  2244. //
  2245. try
  2246. {
  2247. Status = LdrpAccessResourceData(
  2248. Module,
  2249. DataEntry,
  2250. &Resource,
  2251. &ResourceSize);
  2252. } except(EXCEPTION_EXECUTE_HANDLER)
  2253. {
  2254. Status = STATUS_UNSUCCESSFUL;
  2255. }
  2256. if(!NT_SUCCESS(Status))
  2257. {
  2258. return FALSE;
  2259. }
  2260. try
  2261. {
  2262. if((ResourceSize < sizeof(*Resource))
  2263. || _wcsicmp(Resource->Name,L"VS_VERSION_INFO") != 0)
  2264. {
  2265. DbgPrint(("LDR: Warning: invalid version resource\n"));
  2266. return FALSE;
  2267. }
  2268. } except(EXCEPTION_EXECUTE_HANDLER) {
  2269. DbgPrint(("LDR: Exception encountered processing bogus version resource\n"));
  2270. return FALSE;
  2271. }
  2272. ResourceSize -= DWORD_ALIGNMENT(sizeof(*Resource));
  2273. //
  2274. // Get the beginning address of the children of the version information.
  2275. //
  2276. pVerBlock = (VERBLOCK*)(Resource + 1);
  2277. while (ResourceSize > 0)
  2278. {
  2279. if (wcscmp(pVerBlock->szKey, L"VarFileInfo") == 0)
  2280. {
  2281. //
  2282. // Find VarFileInfo block. Search the ResourceChecksum block.
  2283. //
  2284. VarFileInfoSize = pVerBlock->wTotalLen;
  2285. BlockLen =DWORD_ALIGNMENT(sizeof(*pVerBlock) -1 + sizeof(L"VarFileInfo"));
  2286. VarFileInfoSize -= BlockLen;
  2287. pVerBlock = (VERBLOCK*)((unsigned char*)pVerBlock + BlockLen);
  2288. while (VarFileInfoSize > 0)
  2289. {
  2290. if (wcscmp(pVerBlock->szKey, L"ResourceChecksum") == 0)
  2291. {
  2292. *ppMD5Checksum = (unsigned char*)DWORD_ALIGNMENT((UINT_PTR)(pVerBlock->szKey) + sizeof(L"ResourceChecksum"));
  2293. return (TRUE);
  2294. }
  2295. BlockLen = DWORD_ALIGNMENT(pVerBlock->wTotalLen);
  2296. pVerBlock = (VERBLOCK*)((unsigned char*)pVerBlock + BlockLen);
  2297. VarFileInfoSize -= BlockLen;
  2298. }
  2299. return (FALSE);
  2300. }
  2301. BlockLen = DWORD_ALIGNMENT(pVerBlock->wTotalLen);
  2302. pVerBlock = (VERBLOCK*)((unsigned char*)pVerBlock + BlockLen);
  2303. ResourceSize -= BlockLen;
  2304. }
  2305. return (FALSE);
  2306. }
  2307. BOOLEAN
  2308. LdrpCalcResourceChecksum(
  2309. IN PVOID Module,
  2310. OUT unsigned char* MD5Checksum
  2311. )
  2312. /*++
  2313. Rountine Description:
  2314. Enumerate resources in the specified module, and generate a MD5 checksum.
  2315. --*/
  2316. {
  2317. // The top resource directory.
  2318. PIMAGE_RESOURCE_DIRECTORY TopDirectory;
  2319. // The resource type directory.
  2320. PIMAGE_RESOURCE_DIRECTORY TypeDirectory;
  2321. // The resource name directory.
  2322. PIMAGE_RESOURCE_DIRECTORY NameDirectory;
  2323. // The resource language directory.
  2324. PIMAGE_RESOURCE_DIRECTORY LangDirectory;
  2325. PIMAGE_RESOURCE_DIRECTORY_ENTRY TypeDirectoryEntry;
  2326. PIMAGE_RESOURCE_DIRECTORY_ENTRY NameDirectoryEntry;
  2327. PIMAGE_RESOURCE_DIRECTORY_ENTRY LangDirectoryEntry;
  2328. PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry;
  2329. ULONG Size;
  2330. ULONG NumTypeDirectoryEntries;
  2331. ULONG NumNameDirectoryEntries;
  2332. ULONG NumLangDirectoryEntries;
  2333. PVOID ResourceData;
  2334. ULONG ResourceSize;
  2335. ULONG i, j, k;
  2336. LANGID ChecksumLangID;
  2337. ULONGLONG Version;
  2338. try
  2339. {
  2340. MD5_CTX ChecksumContext;
  2341. //
  2342. // we specify the langauge ID for checksum calculation.
  2343. // First, we search with InstallLangID, If it succeed, InsallID will be used, if not, English used.
  2344. // InatallLangID is already set in LdrAlternateResourcesEnabled and LdrpVerifyAlternateResourceModule.
  2345. //
  2346. ChecksumLangID = ENG_US_LANGID;
  2347. if (InstallLangId != ENG_US_LANGID)
  2348. {
  2349. if (LdrpGetFileVersion(Module, InstallLangId, &Version))
  2350. {
  2351. ChecksumLangID = InstallLangId;
  2352. }
  2353. }
  2354. MD5Init(&ChecksumContext);
  2355. //
  2356. // TopDirectory is our reference point to directory offsets.
  2357. //
  2358. TopDirectory = (PIMAGE_RESOURCE_DIRECTORY)
  2359. RtlImageDirectoryEntryToData( Module,
  2360. TRUE,
  2361. IMAGE_DIRECTORY_ENTRY_RESOURCE,
  2362. &Size
  2363. );
  2364. if (!TopDirectory)
  2365. {
  2366. return (FALSE);
  2367. }
  2368. //
  2369. // Point to the children of the TopResourceDirecotry.
  2370. // This is the beginning of the type resource directory.
  2371. //
  2372. TypeDirectoryEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(TopDirectory+1);
  2373. //
  2374. // Get the total number of the types (named resource types + ID resource types)
  2375. //
  2376. NumTypeDirectoryEntries = TopDirectory->NumberOfNamedEntries +
  2377. TopDirectory->NumberOfIdEntries;
  2378. for (i=0; i<NumTypeDirectoryEntries; i++, TypeDirectoryEntry++)
  2379. {
  2380. if (!TypeDirectoryEntry->NameIsString)
  2381. {
  2382. // If the directory type is an ID, check if this is a version info.
  2383. if (TypeDirectoryEntry->Id == RT_VERSION)
  2384. {
  2385. //
  2386. // If this is a version info, just skip it.
  2387. // When calculation checksum for resources, version info should not be
  2388. // included, since they will always be updated when a new version
  2389. // of the file is created.
  2390. //
  2391. continue;
  2392. }
  2393. }
  2394. NameDirectory = (PIMAGE_RESOURCE_DIRECTORY)
  2395. ((PCHAR)TopDirectory + TypeDirectoryEntry->OffsetToDirectory);
  2396. //
  2397. // Point to the children of this TypeResourceDirecotry.
  2398. // This will be the beginning of the name resource directory.
  2399. //
  2400. NameDirectoryEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(NameDirectory+1);
  2401. //
  2402. // Get the total number of the names for the specified type (named resource + ID resource)
  2403. //
  2404. NumNameDirectoryEntries = NameDirectory->NumberOfNamedEntries +
  2405. NameDirectory->NumberOfIdEntries;
  2406. for (j=0; j<NumNameDirectoryEntries; j++, NameDirectoryEntry++ )
  2407. {
  2408. LangDirectory = (PIMAGE_RESOURCE_DIRECTORY)
  2409. ((PCHAR)TopDirectory + NameDirectoryEntry->OffsetToDirectory);
  2410. LangDirectoryEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(LangDirectory+1);
  2411. NumLangDirectoryEntries = LangDirectory->NumberOfNamedEntries +
  2412. LangDirectory->NumberOfIdEntries;
  2413. for (k=0; k<NumLangDirectoryEntries; k++, LangDirectoryEntry++)
  2414. {
  2415. NTSTATUS Status;
  2416. // we caculate RC only based of English language.
  2417. if ( LangDirectoryEntry->Id != ChecksumLangID )
  2418. {
  2419. continue;
  2420. }
  2421. ResourceDataEntry = (PIMAGE_RESOURCE_DATA_ENTRY)
  2422. ((PCHAR)TopDirectory + LangDirectoryEntry->OffsetToData);
  2423. Status = LdrpAccessResourceDataNoMultipleLanguage(
  2424. Module,
  2425. (const PIMAGE_RESOURCE_DATA_ENTRY)ResourceDataEntry,
  2426. &ResourceData,
  2427. &ResourceSize
  2428. );
  2429. if (!NT_SUCCESS(Status))
  2430. {
  2431. return (FALSE);
  2432. }
  2433. MD5Update(&ChecksumContext, (unsigned char*)ResourceData, ResourceSize);
  2434. }
  2435. }
  2436. }
  2437. MD5Final(&ChecksumContext);
  2438. memcpy(MD5Checksum, ChecksumContext.digest, RESOURCE_CHECKSUM_SIZE);
  2439. } except (EXCEPTION_EXECUTE_HANDLER)
  2440. {
  2441. return (FALSE);
  2442. }
  2443. return (TRUE);
  2444. }
  2445. BOOLEAN
  2446. LdrpGetRegValueKey(
  2447. IN HANDLE Handle,
  2448. IN LPWSTR KeyValueName,
  2449. IN ULONG KeyValueType,
  2450. OUT PVOID Buffer,
  2451. IN ULONG BufferSize)
  2452. /*++
  2453. Routine Description:
  2454. This function returns the the registry key value for MUI versioning.
  2455. Arguments:
  2456. Handle - Supplies a handle to the registry which contains MUI versioning
  2457. information.
  2458. KeyValueName - the key name. The values are used to retreive original versiong,
  2459. working version and MUI version.
  2460. KeyValueType - the type of the key value.
  2461. Buffer - pointer to a variable that will receive the retrieved information.
  2462. BufferSize - The size of the buffer.
  2463. Return Value:
  2464. False if the query of the registry fails.
  2465. --*/
  2466. {
  2467. NTSTATUS Status;
  2468. UNICODE_STRING KeyValueString;
  2469. CHAR KeyValueBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 128 * sizeof(WCHAR)];
  2470. PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
  2471. ULONG ResultLength;
  2472. RtlInitUnicodeString(&KeyValueString, KeyValueName);
  2473. KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION)KeyValueBuffer;
  2474. Status = NtQueryValueKey( Handle,
  2475. &KeyValueString,
  2476. KeyValuePartialInformation,
  2477. KeyValueInformation,
  2478. sizeof( KeyValueBuffer ),
  2479. &ResultLength
  2480. );
  2481. if (!NT_SUCCESS(Status) || KeyValueInformation->Type != KeyValueType)
  2482. {
  2483. return (FALSE);
  2484. }
  2485. memcpy(Buffer, KeyValueInformation->Data, BufferSize);
  2486. return (TRUE);
  2487. }
  2488. NTSTATUS
  2489. LdrpCreateKey(
  2490. IN PUNICODE_STRING KeyName,
  2491. IN HANDLE ParentHandle,
  2492. OUT PHANDLE ChildHandle
  2493. )
  2494. /*++
  2495. Routine Description:
  2496. Creates a registry key for writting.
  2497. This is a thin wrapper over NtCreateKey().
  2498. Arguments:
  2499. KeyName - Name of the key to create
  2500. ParentHandle - Handle of parent key
  2501. ChildHandle - Pointer to where the handle is returned
  2502. Return Value:
  2503. Status of create/open
  2504. --*/
  2505. {
  2506. NTSTATUS status;
  2507. OBJECT_ATTRIBUTES objectAttributes;
  2508. //
  2509. // Initialize the OBJECT Attributes to a known value
  2510. //
  2511. InitializeObjectAttributes(
  2512. &objectAttributes,
  2513. KeyName,
  2514. OBJ_CASE_INSENSITIVE,
  2515. ParentHandle,
  2516. NULL
  2517. );
  2518. //
  2519. // Create the key here
  2520. //
  2521. *ChildHandle = 0;
  2522. status = NtCreateKey(
  2523. ChildHandle,
  2524. KEY_ALL_ACCESS,
  2525. &objectAttributes,
  2526. 0,
  2527. NULL,
  2528. REG_OPTION_NON_VOLATILE,
  2529. NULL
  2530. );
  2531. return (status);
  2532. }
  2533. NTSTATUS
  2534. LdrpOpenKey(
  2535. IN PUNICODE_STRING KeyName,
  2536. IN HANDLE ParentHandle,
  2537. OUT PHANDLE ChildHandle
  2538. )
  2539. /*++
  2540. Routine Description:
  2541. Open a registry key. This is a thin wrapper of NtOpenKey().
  2542. Arguments:
  2543. KeyName - Name of the key to create
  2544. ParentHandle - Handle of parent key
  2545. ChildHandle - Pointer to where the handle is returned
  2546. Return Value:
  2547. Status of open registry.
  2548. --*/
  2549. {
  2550. NTSTATUS status;
  2551. OBJECT_ATTRIBUTES objectAttributes;
  2552. //
  2553. // Initialize the OBJECT Attributes to a known value
  2554. //
  2555. InitializeObjectAttributes(
  2556. &objectAttributes,
  2557. KeyName,
  2558. OBJ_CASE_INSENSITIVE,
  2559. ParentHandle,
  2560. NULL
  2561. );
  2562. //
  2563. // Create the key here
  2564. //
  2565. *ChildHandle = 0;
  2566. status = NtOpenKey(ChildHandle, KEY_READ, &objectAttributes);
  2567. return (status);
  2568. }
  2569. BOOLEAN LdrpOpenFileVersionKey(
  2570. IN LPWSTR LangID,
  2571. IN LPWSTR BaseDllName,
  2572. IN ULONGLONG AltModuleVersion,
  2573. IN LPWSTR AltModuleVersionStr,
  2574. OUT PHANDLE pHandle)
  2575. /*++
  2576. Routine Description:
  2577. Open the registry key which contains the versioning information for the specified alternate resource module.
  2578. Arguments:
  2579. LangID - The UI langauge of the resource.
  2580. BaseDllName - The name of the base DLL.
  2581. AltModulePath - The full path of the alternate resource module.
  2582. pHandle - The registry key which stores the version information for this alternate resource module
  2583. Return Value:
  2584. Return TRUE if succeeds in opening/creating the key. Otherwise return FALSE.
  2585. --*/
  2586. {
  2587. BOOLEAN Result = FALSE;
  2588. HANDLE NlsHandle = NULL, MuiHandle = NULL, VersionHandle = NULL, LangHandle = NULL, DllKeyHandle = NULL;
  2589. UNICODE_STRING BufferString;
  2590. NTSTATUS Status;
  2591. PKEY_BASIC_INFORMATION KeyInfo;
  2592. ULONG ResultLength, Index;
  2593. CHAR ValueBuffer[sizeof(KEY_BASIC_INFORMATION) + 32];
  2594. WCHAR buffer[32]; // Temp string buffer.
  2595. ULONGLONG CachedAlternateVersion;
  2596. CHAR KeyFullInfoBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION ) + DOS_MAX_PATH_LENGTH * sizeof(WCHAR)];
  2597. PKEY_VALUE_PARTIAL_INFORMATION KeyFullInfo = (PKEY_VALUE_PARTIAL_INFORMATION )KeyFullInfoBuffer;
  2598. ULONG ChecksumDisabled;
  2599. HANDLE UserKeyHandle; // HKEY_CURRENT_USER equivalent
  2600. ULONG rc;
  2601. *pHandle = NULL;
  2602. rc = RtlOpenCurrentUser(MAXIMUM_ALLOWED, &UserKeyHandle);
  2603. if (!NT_SUCCESS(rc))
  2604. {
  2605. return (FALSE);
  2606. }
  2607. // Open registry REG_MUI_PATH ("HKCU\Software\\Microsoft\\Windows\\CurrentVersion")
  2608. //
  2609. RtlInitUnicodeString(&BufferString, REG_MUI_PATH);
  2610. if (!NT_SUCCESS(LdrpCreateKey(&BufferString, UserKeyHandle, &NlsHandle)))
  2611. {
  2612. goto Exit;
  2613. }
  2614. //
  2615. // Open/Create registry in REG_MUI_PATH\MUILanguages
  2616. //
  2617. RtlInitUnicodeString(&BufferString, MUI_MUILANGUAGES_KEY_NAME);
  2618. if (!NT_SUCCESS(LdrpCreateKey(&BufferString, NlsHandle, &MuiHandle)))
  2619. {
  2620. goto Exit;
  2621. }
  2622. //
  2623. // Open/Create REG_MUI_PATH\MUILanguages\FileVersions
  2624. //
  2625. RtlInitUnicodeString(&BufferString, MUI_FILE_VERSION_KEY_NAME);
  2626. if (!NT_SUCCESS(LdrpCreateKey(&BufferString, MuiHandle, &VersionHandle)))
  2627. {
  2628. goto Exit;
  2629. }
  2630. if (LdrpGetRegValueKey(VersionHandle, MUI_RC_CHECKSUM_DISABLE_KEY, REG_DWORD, &ChecksumDisabled, sizeof(ChecksumDisabled)) &&
  2631. ChecksumDisabled == 1)
  2632. {
  2633. goto Exit;
  2634. }
  2635. //
  2636. // Open/Create ""\\HKCU\Software\\Microsoft\\Windows\\CurrentVersion"\\MUILanguages\\FileVersions\\<LangID>"
  2637. //
  2638. RtlInitUnicodeString(&BufferString, LangID);
  2639. if (!NT_SUCCESS(LdrpCreateKey(&BufferString, VersionHandle, &LangHandle)))
  2640. {
  2641. goto Exit;
  2642. }
  2643. //
  2644. // Open/Create "\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Nls\\MUILanguages\\FileVersions\\<LandID>\\<Name of DLL>"
  2645. //
  2646. RtlInitUnicodeString(&BufferString, BaseDllName);
  2647. if (!NT_SUCCESS(LdrpCreateKey(&BufferString, LangHandle, &DllKeyHandle)))
  2648. {
  2649. goto Exit;
  2650. }
  2651. if (!LdrpGetRegValueKey(DllKeyHandle, MUI_ALTERNATE_VERSION_KEY, REG_QWORD, &CachedAlternateVersion, sizeof(CachedAlternateVersion)))
  2652. {
  2653. RtlInitUnicodeString(&BufferString, MUI_ALTERNATE_VERSION_KEY);
  2654. Result = NT_SUCCESS(NtSetValueKey(DllKeyHandle, &BufferString, 0, REG_QWORD, &AltModuleVersion, sizeof(AltModuleVersion)));
  2655. if (Result)
  2656. {
  2657. *pHandle = DllKeyHandle;
  2658. }
  2659. goto Exit;
  2660. }
  2661. if (CachedAlternateVersion == AltModuleVersion)
  2662. {
  2663. *pHandle = DllKeyHandle;
  2664. Result = TRUE;
  2665. } else
  2666. {
  2667. //
  2668. // Open/Create "\Registry\Machine\System\CurrentControlSet\Control\Nls\MUILanguages\FileVersions
  2669. // \<LandID>\<Name of DLL>\<AltVersionStr>"
  2670. //
  2671. RtlInitUnicodeString(&BufferString, AltModuleVersionStr);
  2672. Result = NT_SUCCESS(LdrpCreateKey(&BufferString, DllKeyHandle, pHandle));
  2673. }
  2674. Exit:
  2675. if (UserKeyHandle) {NtClose(UserKeyHandle);}
  2676. if (NlsHandle) {NtClose(NlsHandle);}
  2677. if (MuiHandle) {NtClose(MuiHandle);}
  2678. if (VersionHandle) {NtClose(VersionHandle);}
  2679. if (LangHandle) {NtClose(LangHandle);}
  2680. // If DllKeyHandle is the handle that we are going to return,
  2681. // we can not close it.
  2682. if (DllKeyHandle && *pHandle != DllKeyHandle)
  2683. {
  2684. NtClose(DllKeyHandle);
  2685. }
  2686. return (Result);
  2687. }
  2688. void
  2689. LdrpConvertVersionString(
  2690. IN ULONGLONG ModuleVersion,
  2691. OUT LPWSTR ModuleVersionStr
  2692. )
  2693. /*++
  2694. Routine Description:
  2695. Convert a 64-bit version information into a Unicode string.
  2696. Arguments:
  2697. ModuleVersion - The 64-b8t version information.
  2698. ModuleVersionStr - The converted string.
  2699. Return Value:
  2700. None.
  2701. --*/
  2702. {
  2703. LPWSTR StringStart = ModuleVersionStr;
  2704. WCHAR digit;
  2705. // Put the null-terminated char at the end of the converted string.
  2706. ModuleVersionStr[16] = L'\0';
  2707. ModuleVersionStr += 15;
  2708. while (ModuleVersionStr >= StringStart)
  2709. {
  2710. digit = (WCHAR)(ModuleVersion & (ULONGLONG)0xf);
  2711. *ModuleVersionStr-- = (digit < 10 ? digit + '0' : (digit - 10) + 'a');
  2712. ModuleVersion >>= 4;
  2713. }
  2714. }
  2715. BOOLEAN
  2716. LdrpCompareResourceChecksum(
  2717. IN LPWSTR LangID,
  2718. IN PVOID Module,
  2719. IN ULONGLONG ModuleVersion,
  2720. IN PVOID AlternateModule,
  2721. IN ULONGLONG AltModuleVersion,
  2722. IN LPWSTR BaseDllName
  2723. )
  2724. /*++
  2725. Routine Description:
  2726. In the case that the version for the original module is different from that
  2727. of the alternate module, check if the alternate module can still be used
  2728. for the original version.
  2729. First, the function will look at the registry to see if there is information
  2730. cached for the module.
  2731. In the case that the information is not cached for this module,
  2732. this function will retrieve the MD5 resource checksum from the alternate
  2733. resource module. And then check if the MD5 resource checksum is embeded
  2734. in the original module. If MD5 resource checksum is not in the original
  2735. moduel, it will enumerate all resources in the module to calculate the
  2736. MD5 checksum.
  2737. Arguments:
  2738. LangID - Supplies a language of the resource to be loaded.
  2739. Module - The original module.
  2740. ModuleVersion - The version for the original version.
  2741. AlternateModule - The alternate module.
  2742. AltModuleVersion - The version for the alternate module.
  2743. BaseDllName - The name of the DLL.
  2744. Return Value:
  2745. Ture if the alternate module can be used.
  2746. Otherwise, return false.
  2747. --*/
  2748. {
  2749. // Flag to indicate if the alternate resource can be used for this module.
  2750. ULONG UseAlternateResource = 0;
  2751. unsigned char* ModuleChecksum; // The 128-bit MD5 resource checksum for the module.
  2752. unsigned char CalculatedModuleChecksum[RESOURCE_CHECKSUM_SIZE]; // The calculated 128-bit MD5 resource checksum for the module.
  2753. unsigned char* AlternateModuleChecksum; // The 128-bit MD5 resource checksum embeded in the alternate module.
  2754. WCHAR ModuleVersionStr[17]; // The string for the 16 heximal digit version.
  2755. WCHAR AltModuleVersionStr[17];
  2756. HANDLE Handle = NULL; // The registry which caches the information for this module.
  2757. // Flag to indicate if we have retrieved or calucated the MD5 resource checksum for the original module successfully.
  2758. BOOLEAN FoundModuleChecksum;
  2759. UNICODE_STRING BufferString;
  2760. HANDLE RCHandle = NULL;
  2761. HANDLE RCFileHandle = NULL;
  2762. unsigned char RegisteredModuleChecksum[RESOURCE_CHECKSUM_SIZE];
  2763. BOOLEAN fIsRCInRegsitry = FALSE;
  2764. //
  2765. // Check the cached information in the registry first.
  2766. //
  2767. LdrpConvertVersionString(AltModuleVersion, AltModuleVersionStr);
  2768. //
  2769. // Open the version information key under:
  2770. // HKCU\Control Panel\International\MUI\FileVersions\<LangID>\<BaseDllName>
  2771. //
  2772. if (LdrpOpenFileVersionKey(LangID, BaseDllName, AltModuleVersion, AltModuleVersionStr, &Handle))
  2773. {
  2774. LdrpConvertVersionString(ModuleVersion, ModuleVersionStr);
  2775. //
  2776. // Try to check if this module exists in version information.
  2777. // If yes, see if the AlternateModule can be used.
  2778. //
  2779. //
  2780. // Get the cached version information in the registry to see if the original module can re-use the alternative module.
  2781. //
  2782. if (LdrpGetRegValueKey(Handle, ModuleVersionStr, REG_DWORD, &UseAlternateResource, sizeof(UseAlternateResource)))
  2783. {
  2784. // Get the cached information. Let's bail and return the cached result in UseAlternativeResource.
  2785. goto exit;
  2786. }
  2787. }
  2788. //
  2789. // When we are here, we know that we either:
  2790. // 1. Can't open the registry key which cached the information. Or
  2791. // 2. This file has never been looked before.
  2792. //
  2793. // Get the resource checksum for the alternate module.
  2794. //
  2795. //
  2796. // Some US binary in SP has different checksum value from RTM MUI, so we want to look for
  2797. // special registry created for this purpose before we get checksum from MUI.
  2798. //
  2799. RtlInitUnicodeString(&BufferString, REG_MUI_RC_PATH);
  2800. if (NT_SUCCESS(LdrpOpenKey(&BufferString, NULL, &RCHandle)))
  2801. {
  2802. //
  2803. // We open a file key and read a US_SP binary checksum from same Alterhnatve Module.
  2804. //
  2805. RtlInitUnicodeString(&BufferString, BaseDllName);
  2806. if (NT_SUCCESS(LdrpOpenKey(&BufferString, RCHandle, &RCFileHandle)))
  2807. {
  2808. if(LdrpGetRegValueKey(RCFileHandle, AltModuleVersionStr, REG_BINARY, &RegisteredModuleChecksum, RESOURCE_CHECKSUM_SIZE ))
  2809. {
  2810. AlternateModuleChecksum = RegisteredModuleChecksum;
  2811. fIsRCInRegsitry = TRUE;
  2812. }
  2813. }
  2814. }
  2815. //
  2816. // Reading checksum from registry or MUI file
  2817. //
  2818. if ( fIsRCInRegsitry || LdrpGetResourceChecksum(AlternateModule, &AlternateModuleChecksum) )
  2819. {
  2820. //
  2821. // First, check if the resource checksum is built in the module.
  2822. //
  2823. if (!(FoundModuleChecksum = LdrpGetResourceChecksum(Module, &ModuleChecksum))) {
  2824. //
  2825. // If not, calculate the resource checksum for the current module.
  2826. //
  2827. if (FoundModuleChecksum = LdrpCalcResourceChecksum(Module, CalculatedModuleChecksum))
  2828. {
  2829. ModuleChecksum = CalculatedModuleChecksum;
  2830. }
  2831. }
  2832. if (FoundModuleChecksum)
  2833. {
  2834. if (memcmp(ModuleChecksum, AlternateModuleChecksum, RESOURCE_CHECKSUM_SIZE) == 0)
  2835. {
  2836. //
  2837. // If the checksums are equal, the working version is the module version.
  2838. //
  2839. UseAlternateResource = 1;
  2840. }
  2841. }
  2842. }
  2843. if (Handle != NULL) {
  2844. // If we find the version registry key successfully, cache the result in the registry.
  2845. //
  2846. // Write the working module information into registry.
  2847. //
  2848. RtlInitUnicodeString(&BufferString, ModuleVersionStr);
  2849. NtSetValueKey(Handle, &BufferString, 0, REG_DWORD, &UseAlternateResource, sizeof(UseAlternateResource));
  2850. }
  2851. exit:
  2852. if (Handle != NULL) {NtClose(Handle);}
  2853. if (RCHandle != NULL) {NtClose(RCHandle);}
  2854. if (RCFileHandle != NULL) {NtClose(RCFileHandle);}
  2855. return ((BOOLEAN)(UseAlternateResource));
  2856. }
  2857. BOOLEAN
  2858. LdrpVerifyAlternateResourceModule(
  2859. IN PWSTR LangID,
  2860. IN PVOID Module,
  2861. IN PVOID AlternateModule,
  2862. IN LPWSTR BaseDllName
  2863. )
  2864. /*++
  2865. Routine Description:
  2866. This function verifies if the alternate resource module has the same
  2867. version of the base module.
  2868. Arguments:
  2869. Module - The handle of the base module.
  2870. AlternateModule - The handle of the alternate resource module
  2871. BaseDllName - The file name of base DLL.
  2872. Return Value:
  2873. TBD.
  2874. --*/
  2875. {
  2876. ULONGLONG ModuleVersion;
  2877. ULONGLONG AltModuleVersion;
  2878. NTSTATUS Status;
  2879. if (!UILangId || NtCurrentTeb()->ImpersonationLocale){
  2880. Status = NtQueryDefaultUILanguage( &UILangId);
  2881. if (!NT_SUCCESS( Status )) {
  2882. //
  2883. // Failed to get UI LangID. AltResource not enabled.
  2884. //
  2885. return FALSE;
  2886. }
  2887. }
  2888. if (!LdrpGetFileVersion(AlternateModule, UILangId, &AltModuleVersion)){
  2889. return FALSE;
  2890. }
  2891. if (!InstallLangId){
  2892. Status = NtQueryInstallUILanguage (&InstallLangId);
  2893. if (!NT_SUCCESS( Status )) {
  2894. //
  2895. // Failed to get Install LangID. AltResource not enabled.
  2896. //
  2897. return FALSE;
  2898. }
  2899. }
  2900. if (!LdrpGetFileVersion(Module, InstallLangId, &ModuleVersion) &&
  2901. !LdrpGetFileVersion(Module, ENG_US_LANGID, &ModuleVersion)){
  2902. return FALSE;
  2903. }
  2904. if (ModuleVersion == AltModuleVersion){
  2905. return TRUE;
  2906. }
  2907. else
  2908. {
  2909. #ifdef USE_RC_CHECKSUM
  2910. return (LdrpCompareResourceChecksum(LangID, Module, ModuleVersion, AlternateModule, AltModuleVersion, BaseDllName));
  2911. #else
  2912. return FALSE;
  2913. #endif
  2914. }
  2915. }
  2916. BOOLEAN
  2917. LdrpSetAlternateResourceModuleHandle(
  2918. IN PVOID Module,
  2919. IN PVOID AlternateModule
  2920. )
  2921. /*++
  2922. Routine Description:
  2923. This function records the handle of the base module and alternate
  2924. resource module in an array.
  2925. Arguments:
  2926. Module - The handle of the base module.
  2927. AlternateModule - The handle of the alternate resource module
  2928. Return Value:
  2929. TBD.
  2930. --*/
  2931. {
  2932. PALT_RESOURCE_MODULE NewModules;
  2933. if (AlternateResourceModules == NULL){
  2934. //
  2935. // Allocate memory of initial size MEMBLOCKSIZE.
  2936. //
  2937. NewModules = RtlAllocateHeap(
  2938. RtlProcessHeap(),
  2939. HEAP_ZERO_MEMORY,
  2940. RESMODSIZE * MEMBLOCKSIZE);
  2941. if (!NewModules){
  2942. return FALSE;
  2943. }
  2944. AlternateResourceModules = NewModules;
  2945. AltResMemBlockCount = MEMBLOCKSIZE;
  2946. }
  2947. else
  2948. if (AlternateResourceModuleCount >= AltResMemBlockCount ){
  2949. //
  2950. // ReAllocate another chunk of memory.
  2951. //
  2952. NewModules = RtlReAllocateHeap(
  2953. RtlProcessHeap(),
  2954. 0,
  2955. AlternateResourceModules,
  2956. (AltResMemBlockCount + MEMBLOCKSIZE) * RESMODSIZE
  2957. );
  2958. if (!NewModules){
  2959. return FALSE;
  2960. }
  2961. AlternateResourceModules = NewModules;
  2962. AltResMemBlockCount += MEMBLOCKSIZE;
  2963. }
  2964. AlternateResourceModules[AlternateResourceModuleCount].ModuleBase = Module;
  2965. AlternateResourceModules[AlternateResourceModuleCount].AlternateModule = AlternateModule;
  2966. AlternateResourceModuleCount++;
  2967. return TRUE;
  2968. }
  2969. PVOID
  2970. LdrLoadAlternateResourceModule(
  2971. IN PVOID Module,
  2972. IN LPCWSTR PathToAlternateModule OPTIONAL
  2973. )
  2974. /*++
  2975. Routine Description:
  2976. This function does the acutally loading into memory of the alternate
  2977. resource module, or loads from the table if it was loaded before.
  2978. Arguments:
  2979. Module - The handle of the base module.
  2980. PathToAlternateModule - Optional path from which module is being loaded.
  2981. Return Value:
  2982. Handle to the alternate resource module.
  2983. --*/
  2984. {
  2985. PVOID AlternateModule, DllBase;
  2986. PLDR_DATA_TABLE_ENTRY Entry;
  2987. HANDLE FileHandle, MappingHandle;
  2988. PIMAGE_NT_HEADERS NtHeaders;
  2989. NTSTATUS Status;
  2990. OBJECT_ATTRIBUTES ObjectAttributes;
  2991. UNICODE_STRING AltDllName;
  2992. PVOID FreeBuffer;
  2993. LPWSTR BaseDllName = NULL, p;
  2994. WCHAR DllPathName[DOS_MAX_PATH_LENGTH];
  2995. ULONG DllPathNameLength, BaseDllNameLength, CopyCount;
  2996. ULONG Digit;
  2997. int i, RetryCount;
  2998. WCHAR AltModulePath[DOS_MAX_PATH_LENGTH];
  2999. WCHAR AltModulePathMUI[DOS_MAX_PATH_LENGTH];
  3000. WCHAR AltModulePathFallback[DOS_MAX_PATH_LENGTH];
  3001. IO_STATUS_BLOCK IoStatusBlock;
  3002. RTL_RELATIVE_NAME RelativeName;
  3003. SIZE_T ViewSize;
  3004. LARGE_INTEGER SectionOffset;
  3005. WCHAR LangIdDir[6];
  3006. PVOID ReturnValue = NULL;
  3007. //
  3008. // The full path of the current MUI file that we are searching.
  3009. //
  3010. UNICODE_STRING CurrentAltModuleFile;
  3011. UNICODE_STRING NtSystemRoot;
  3012. //
  3013. // The current MUI folder that we are searching.
  3014. //
  3015. UNICODE_STRING CurrentAltModulePath;
  3016. WCHAR CurrentAltModulePathBuffer[DOS_MAX_PATH_LENGTH];
  3017. //
  3018. // The string contains the first MUI folder that we will search.
  3019. // This is the folder which lives under the folder of the base DLL.
  3020. // AltDllMUIPath = [the folder of the base DLL] + "\mui" + "\[UI Language]";
  3021. // E.g. if the base DLL is "c:\winnt\system32\ntdll.dll" and UI language is 0411,
  3022. // AltDllMUIPath will be "c:\winnt\system32\mui\0411\"
  3023. //
  3024. UNICODE_STRING AltDllMUIPath;
  3025. WCHAR AltDllMUIPathBuffer[DOS_MAX_PATH_LENGTH];
  3026. //
  3027. // MUI Redir
  3028. //
  3029. UNICODE_STRING BaseDllNameUstr;
  3030. UNICODE_STRING StaticStringAltModulePathRedirected;
  3031. UNICODE_STRING DynamicStringAltModulePathRedirected;
  3032. PUNICODE_STRING FullPathStringFoundAltModulePathRedirected = NULL;
  3033. BOOLEAN fRedirMUI = FALSE;
  3034. PVOID LockCookie = NULL;
  3035. // bail out early if this isn't a MUI-enabled system
  3036. if (!LdrAlternateResourcesEnabled()) {
  3037. return NULL;
  3038. }
  3039. LdrLockLoaderLock(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, NULL, &LockCookie);
  3040. __try {
  3041. //
  3042. // Look at the cache of the alternate module first.
  3043. //
  3044. AlternateModule = LdrGetAlternateResourceModuleHandle(Module);
  3045. if (AlternateModule == NO_ALTERNATE_RESOURCE_MODULE) {
  3046. //
  3047. // We tried to load this module before but failed. Don't try
  3048. // again in the future.
  3049. //
  3050. return NULL;
  3051. } else if (AlternateModule > 0) {
  3052. //
  3053. // We found the previously loaded match
  3054. //
  3055. return AlternateModule;
  3056. }
  3057. AlternateModule = NULL;
  3058. if (ARGUMENT_PRESENT(PathToAlternateModule)) {
  3059. //
  3060. // Caller suplied path.
  3061. //
  3062. p = wcsrchr(PathToAlternateModule, L'\\');
  3063. if (p == NULL)
  3064. goto error_exit;
  3065. p++;
  3066. DllPathNameLength = (ULONG)(p - PathToAlternateModule) * sizeof(WCHAR);
  3067. RtlCopyMemory(
  3068. DllPathName,
  3069. PathToAlternateModule,
  3070. DllPathNameLength);
  3071. BaseDllName = p;
  3072. BaseDllNameLength = wcslen(p);
  3073. } else {
  3074. //
  3075. // Try to get full dll path from Ldr data table.
  3076. //
  3077. Status = LdrFindEntryForAddress(Module, &Entry);
  3078. if (!NT_SUCCESS(Status))
  3079. goto error_exit;
  3080. DllPathNameLength = Entry->FullDllName.Length - Entry->BaseDllName.Length;
  3081. RtlCopyMemory(
  3082. DllPathName,
  3083. Entry->FullDllName.Buffer,
  3084. DllPathNameLength);
  3085. BaseDllName = Entry->BaseDllName.Buffer;
  3086. BaseDllNameLength = Entry->BaseDllName.Length;
  3087. }
  3088. DllPathName[DllPathNameLength / sizeof(WCHAR)] = UNICODE_NULL;
  3089. //
  3090. // dll redirection for the dll to be loaded xiaoyuw@10/31/2000
  3091. //
  3092. StaticStringAltModulePathRedirected.Buffer = AltModulePath; // reuse the array instead of define another array
  3093. StaticStringAltModulePathRedirected.Length = 0;
  3094. StaticStringAltModulePathRedirected.MaximumLength = sizeof(AltModulePath);
  3095. DynamicStringAltModulePathRedirected.Buffer = NULL;
  3096. DynamicStringAltModulePathRedirected.Length = 0;
  3097. DynamicStringAltModulePathRedirected.MaximumLength = 0;
  3098. BaseDllNameUstr.Buffer = AltModulePathMUI; // reuse the array instead of define another array
  3099. BaseDllNameUstr.Length = 0;
  3100. BaseDllNameUstr.MaximumLength = sizeof(AltModulePathMUI);
  3101. RtlAppendUnicodeToString(&BaseDllNameUstr, BaseDllName);
  3102. RtlAppendUnicodeToString(&BaseDllNameUstr, L".mui");
  3103. Status = RtlDosApplyFileIsolationRedirection_Ustr(
  3104. RTL_DOS_APPLY_FILE_REDIRECTION_USTR_FLAG_RESPECT_DOT_LOCAL,
  3105. &BaseDllNameUstr, NULL,
  3106. &StaticStringAltModulePathRedirected,
  3107. &DynamicStringAltModulePathRedirected,
  3108. &FullPathStringFoundAltModulePathRedirected,
  3109. NULL,NULL, NULL);
  3110. if (!NT_SUCCESS(Status)) // no redirection info found for this string
  3111. {
  3112. if (Status != STATUS_SXS_KEY_NOT_FOUND)
  3113. goto error_exit;
  3114. //
  3115. // Generate the langid directory like "0804\"
  3116. //
  3117. if (!UILangId || NtCurrentTeb()->ImpersonationLocale){
  3118. Status = NtQueryDefaultUILanguage( &UILangId );
  3119. if (!NT_SUCCESS( Status )) {
  3120. goto error_exit;
  3121. }
  3122. }
  3123. CopyCount = 0;
  3124. for (i = 12; i >= 0; i -= 4) {
  3125. Digit = ((UILangId >> i) & 0xF);
  3126. if (Digit >= 10) {
  3127. LangIdDir[CopyCount++] = (WCHAR) (Digit - 10 + L'A');
  3128. } else {
  3129. LangIdDir[CopyCount++] = (WCHAR) (Digit + L'0');
  3130. }
  3131. }
  3132. LangIdDir[CopyCount++] = L'\\';
  3133. LangIdDir[CopyCount++] = UNICODE_NULL;
  3134. //
  3135. // Create the MUI path under the directory of the base DLL.
  3136. //
  3137. AltDllMUIPath.Buffer = AltDllMUIPathBuffer;
  3138. AltDllMUIPath.Length = 0;
  3139. AltDllMUIPath.MaximumLength = sizeof(AltDllMUIPathBuffer);
  3140. RtlAppendUnicodeToString(&AltDllMUIPath, DllPathName); // e.g. "c:\winnt\system32\"
  3141. RtlAppendUnicodeToString(&AltDllMUIPath, L"mui\\"); // e.g. "c:\winnt\system32\mui\"
  3142. RtlAppendUnicodeToString(&AltDllMUIPath, LangIdDir); // e.g. "c:\winnt\system32\mui\0411\"
  3143. CurrentAltModulePath.Buffer = CurrentAltModulePathBuffer;
  3144. CurrentAltModulePath.Length = 0;
  3145. CurrentAltModulePath.MaximumLength = sizeof(CurrentAltModulePathBuffer);
  3146. } else {
  3147. fRedirMUI = TRUE;
  3148. //set CurrentAltModuleFile and CurrentAltModulePath
  3149. CurrentAltModuleFile.Buffer = AltModulePathMUI;
  3150. CurrentAltModuleFile.Length = 0;
  3151. CurrentAltModuleFile.MaximumLength = sizeof(AltModulePathMUI);
  3152. RtlCopyUnicodeString(&CurrentAltModuleFile, FullPathStringFoundAltModulePathRedirected);
  3153. }
  3154. //
  3155. // Try name with .mui extesion first.
  3156. //
  3157. RetryCount = 0;
  3158. while (RetryCount < 3){
  3159. if ( ! fRedirMUI )
  3160. {
  3161. switch (RetryCount)
  3162. {
  3163. case 0:
  3164. //
  3165. // Generate the first path under the folder of the base DLL
  3166. // (e.g. c:\winnt\system32\mui\0804\ntdll.dll)
  3167. //
  3168. CurrentAltModuleFile.Buffer = AltModulePathMUI;
  3169. CurrentAltModuleFile.Length = 0;
  3170. CurrentAltModuleFile.MaximumLength = sizeof(AltModulePathMUI);
  3171. RtlCopyUnicodeString(&CurrentAltModuleFile, &AltDllMUIPath); // e.g. "c:\winnt\system32\mui\0411\"
  3172. RtlCopyUnicodeString(&CurrentAltModulePath, &AltDllMUIPath);
  3173. RtlAppendUnicodeToString(&CurrentAltModuleFile, BaseDllName); // e.g. "c:\winnt\system32\mui\0411\ntdll.dll"
  3174. RtlAppendUnicodeToString(&CurrentAltModuleFile, L".mui"); // e.g. "c:\winnt\system32\mui\0411\ntdll.dll.mui"
  3175. break;
  3176. case 1:
  3177. //
  3178. // Generate the second path c:\winnt\system32\mui\0804\ntdll.dll.mui
  3179. //
  3180. CurrentAltModuleFile.Buffer = AltModulePath;
  3181. CurrentAltModuleFile.Length = 0;
  3182. CurrentAltModuleFile.MaximumLength = sizeof(AltModulePath);
  3183. RtlCopyUnicodeString(&CurrentAltModuleFile, &AltDllMUIPath); // e.g. "c:\winnt\system32\mui\0411\"
  3184. RtlAppendUnicodeToString(&CurrentAltModuleFile, BaseDllName); // e.g. "c:\winnt\system32\mui\0411\ntdll.dll"
  3185. break;
  3186. case 2:
  3187. //
  3188. // Generate path c:\winnt\mui\fallback\0804\foo.exe.mui
  3189. //
  3190. CurrentAltModuleFile.Buffer = AltModulePathFallback;
  3191. CurrentAltModuleFile.Length = 0;
  3192. CurrentAltModuleFile.MaximumLength = sizeof(AltModulePathFallback);
  3193. RtlInitUnicodeString(&NtSystemRoot, USER_SHARED_DATA->NtSystemRoot); // e.g. "c:\winnt\system32\"
  3194. RtlAppendUnicodeStringToString(&CurrentAltModuleFile, &NtSystemRoot); // e.g. "c:\winnt\system32\"
  3195. RtlAppendUnicodeToString(&CurrentAltModuleFile, L"\\mui\\fallback\\"); // e.g. "c:\winnt\system32\mui\fallback\"
  3196. RtlAppendUnicodeToString(&CurrentAltModuleFile, LangIdDir); // e.g. "c:\winnt\system32\mui\fallback\0411\"
  3197. RtlCopyUnicodeString(&CurrentAltModulePath, &CurrentAltModuleFile);
  3198. RtlAppendUnicodeToString(&CurrentAltModuleFile, BaseDllName); // e.g. "c:\winnt\system32\mui\fallback\0411\ntdll.dll"
  3199. RtlAppendUnicodeToString(&CurrentAltModuleFile, L".mui"); // e.g. "c:\winnt\system32\mui\fallback\0411\ntdll.dll.mui"
  3200. break;
  3201. }
  3202. }
  3203. if (!RtlDosPathNameToNtPathName_U(
  3204. CurrentAltModuleFile.Buffer,
  3205. &AltDllName,
  3206. NULL,
  3207. &RelativeName))
  3208. goto error_exit;
  3209. FreeBuffer = AltDllName.Buffer;
  3210. if (RelativeName.RelativeName.Length != 0) {
  3211. AltDllName = *(PUNICODE_STRING)&RelativeName.RelativeName;
  3212. } else {
  3213. RelativeName.ContainingDirectory = NULL;
  3214. }
  3215. InitializeObjectAttributes(
  3216. &ObjectAttributes,
  3217. &AltDllName,
  3218. OBJ_CASE_INSENSITIVE,
  3219. RelativeName.ContainingDirectory,
  3220. NULL
  3221. );
  3222. Status = NtCreateFile(
  3223. &FileHandle,
  3224. (ACCESS_MASK) GENERIC_READ | SYNCHRONIZE | FILE_READ_ATTRIBUTES,
  3225. &ObjectAttributes,
  3226. &IoStatusBlock,
  3227. NULL,
  3228. 0L,
  3229. FILE_SHARE_READ | FILE_SHARE_DELETE,
  3230. FILE_OPEN,
  3231. 0L,
  3232. NULL,
  3233. 0L
  3234. );
  3235. RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
  3236. if (NT_SUCCESS(Status)) {
  3237. goto CreateSection;
  3238. }
  3239. if (fRedirMUI) { // definitely failed
  3240. goto error_exit;
  3241. }
  3242. if (Status != STATUS_OBJECT_NAME_NOT_FOUND && RetryCount == 0) {
  3243. //
  3244. // Error other than the file name with .mui not found.
  3245. // Most likely directory is missing. Skip file name w/o .mui
  3246. // and goto fallback directory.
  3247. //
  3248. RetryCount++;
  3249. }
  3250. RetryCount++;
  3251. }
  3252. // No alternate resource was found during the iterations. Fail!
  3253. goto error_exit;
  3254. CreateSection:
  3255. Status = NtCreateSection(
  3256. &MappingHandle,
  3257. STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ,
  3258. NULL,
  3259. NULL,
  3260. PAGE_WRITECOPY,
  3261. SEC_COMMIT,
  3262. FileHandle
  3263. );
  3264. NtClose( FileHandle );
  3265. if (!NT_SUCCESS(Status)) {
  3266. goto error_exit;
  3267. }
  3268. SectionOffset.LowPart = 0;
  3269. SectionOffset.HighPart = 0;
  3270. ViewSize = 0;
  3271. DllBase = NULL;
  3272. Status = NtMapViewOfSection(
  3273. MappingHandle,
  3274. NtCurrentProcess(),
  3275. &DllBase,
  3276. 0L,
  3277. 0L,
  3278. &SectionOffset,
  3279. &ViewSize,
  3280. ViewShare,
  3281. 0L,
  3282. PAGE_WRITECOPY
  3283. );
  3284. NtClose(MappingHandle);
  3285. if (!NT_SUCCESS(Status)){
  3286. goto error_exit;
  3287. }
  3288. NtHeaders = RtlImageNtHeader(DllBase);
  3289. if (!NtHeaders) {
  3290. NtUnmapViewOfSection(NtCurrentProcess(), (PVOID) DllBase);
  3291. goto error_exit;
  3292. }
  3293. AlternateModule = LDR_VIEW_TO_DATAFILE(DllBase);
  3294. if(!LdrpVerifyAlternateResourceModule(LangIdDir, Module, AlternateModule, BaseDllName)) {
  3295. NtUnmapViewOfSection(NtCurrentProcess(), (PVOID) DllBase);
  3296. goto error_exit;
  3297. }
  3298. LdrpSetAlternateResourceModuleHandle(Module, AlternateModule);
  3299. return AlternateModule;
  3300. error_exit:
  3301. if (BaseDllName != NULL) {
  3302. //
  3303. // If we looked for a MUI file and couldn't find one keep track. If
  3304. // we couldn't get the base dll name (e.g. someone passing in a
  3305. // mapped image with the low bit set but no path name), we don't want
  3306. // to "remember" that there's no MUI.
  3307. //
  3308. LdrpSetAlternateResourceModuleHandle(Module, NO_ALTERNATE_RESOURCE_MODULE);
  3309. }
  3310. return NULL;
  3311. } __finally {
  3312. Status = LdrUnlockLoaderLock(LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, LockCookie);
  3313. }
  3314. // Compiler *should* be smart enough to recognize that we don't need a return here...
  3315. }
  3316. BOOLEAN
  3317. LdrUnloadAlternateResourceModule(
  3318. IN PVOID Module
  3319. )
  3320. /*++
  3321. Routine Description:
  3322. This function unmaps an alternate resource module from the process'
  3323. address space and updates alternate resource module table.
  3324. Arguments:
  3325. Module - handle of the base module.
  3326. Return Value:
  3327. TBD.
  3328. --*/
  3329. {
  3330. ULONG ModuleIndex;
  3331. PALT_RESOURCE_MODULE AltModule;
  3332. NTSTATUS Status;
  3333. PVOID LockCookie = NULL;
  3334. LdrLockLoaderLock(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, NULL, &LockCookie);
  3335. __try {
  3336. if (AlternateResourceModuleCount == 0)
  3337. return TRUE;
  3338. for (ModuleIndex = AlternateResourceModuleCount;
  3339. ModuleIndex > 0;
  3340. ModuleIndex--) {
  3341. if (AlternateResourceModules[ModuleIndex-1].ModuleBase == Module) {
  3342. break;
  3343. }
  3344. }
  3345. if (ModuleIndex == 0)
  3346. return FALSE;
  3347. //
  3348. // Adjust to the actual index
  3349. //
  3350. ModuleIndex --;
  3351. AltModule = &AlternateResourceModules[ModuleIndex];
  3352. if (AltModule->AlternateModule != NO_ALTERNATE_RESOURCE_MODULE) {
  3353. NtUnmapViewOfSection(
  3354. NtCurrentProcess(),
  3355. LDR_DATAFILE_TO_VIEW(AltModule->AlternateModule));
  3356. }
  3357. if (ModuleIndex != AlternateResourceModuleCount - 1) {
  3358. //
  3359. // Consolidate the array. Skip this if unloaded item
  3360. // is the last element.
  3361. //
  3362. RtlMoveMemory(
  3363. AltModule,
  3364. AltModule + 1,
  3365. (AlternateResourceModuleCount - ModuleIndex - 1) * RESMODSIZE);
  3366. }
  3367. AlternateResourceModuleCount--;
  3368. if (AlternateResourceModuleCount == 0){
  3369. RtlFreeHeap(
  3370. RtlProcessHeap(),
  3371. 0,
  3372. AlternateResourceModules
  3373. );
  3374. AlternateResourceModules = NULL;
  3375. AltResMemBlockCount = 0;
  3376. } else {
  3377. if (AlternateResourceModuleCount < AltResMemBlockCount - MEMBLOCKSIZE) {
  3378. AltModule = RtlReAllocateHeap(
  3379. RtlProcessHeap(),
  3380. 0,
  3381. AlternateResourceModules,
  3382. (AltResMemBlockCount - MEMBLOCKSIZE) * RESMODSIZE);
  3383. if (!AltModule)
  3384. return FALSE;
  3385. AlternateResourceModules = AltModule;
  3386. AltResMemBlockCount -= MEMBLOCKSIZE;
  3387. }
  3388. }
  3389. } __finally {
  3390. LdrUnlockLoaderLock(LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, LockCookie);
  3391. }
  3392. return TRUE;
  3393. }
  3394. BOOLEAN
  3395. LdrFlushAlternateResourceModules(
  3396. VOID
  3397. )
  3398. /*++
  3399. Routine Description:
  3400. This function unmaps all the alternate resouce modules for the
  3401. process address space. This function would be used mainly by
  3402. CSRSS, and any sub-systems that are permanent during logon and
  3403. logoff.
  3404. Arguments:
  3405. None
  3406. Return Value:
  3407. TRUE : Successful
  3408. FALSE : Failed
  3409. --*/
  3410. {
  3411. ULONG ModuleIndex;
  3412. PALT_RESOURCE_MODULE AltModule;
  3413. NTSTATUS Status;
  3414. PVOID LockCookie = NULL;
  3415. //
  3416. // Grab the loader lock
  3417. //
  3418. Status = LdrLockLoaderLock(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, NULL, &LockCookie);
  3419. if (!NT_SUCCESS(Status)) {
  3420. // This function erroneously doesn't have any way to communicate failure statuses up so
  3421. // we're stuck with just returning false.
  3422. return FALSE;
  3423. }
  3424. __try {
  3425. if (AlternateResourceModuleCount > 0) {
  3426. //
  3427. // Let's unmap the alternate resource modules from the process
  3428. // address space
  3429. //
  3430. for (ModuleIndex=0;
  3431. ModuleIndex<AlternateResourceModuleCount;
  3432. ModuleIndex++) {
  3433. AltModule = &AlternateResourceModules[ModuleIndex];
  3434. if (AltModule->AlternateModule != NO_ALTERNATE_RESOURCE_MODULE) {
  3435. NtUnmapViewOfSection(NtCurrentProcess(),
  3436. LDR_DATAFILE_TO_VIEW(AltModule->AlternateModule));
  3437. }
  3438. }
  3439. //
  3440. // Cleanup alternate resource modules memory
  3441. //
  3442. RtlFreeHeap(RtlProcessHeap(), 0, AlternateResourceModules);
  3443. AlternateResourceModules = NULL;
  3444. AlternateResourceModuleCount = 0;
  3445. AltResMemBlockCount = 0;
  3446. }
  3447. //
  3448. // Re-Initialize the UI language for the current process,
  3449. //
  3450. UILangId = 0;
  3451. } __finally {
  3452. LdrUnlockLoaderLock(LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, LockCookie);
  3453. }
  3454. return TRUE;
  3455. }
  3456. #endif