Leaked source code of windows server 2003
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.

5867 lines
202 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(BLDR_KERNEL_RUNTIME)
  13. #error "This file is not used in the boot loader runtime."
  14. #endif
  15. #if !defined(NTOS_KERNEL_RUNTIME) && !defined(BLDR_KERNEL_RUNTIME)
  16. #define NTOS_USERMODE_RUNTIME
  17. #endif
  18. #if defined(NTOS_USERMODE_RUNTIME)
  19. #include "wow64t.h"
  20. #include "ntwow64.h"
  21. #endif
  22. #if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
  23. #pragma alloc_text(PAGE,LdrAccessResource)
  24. #pragma alloc_text(PAGE,LdrpAccessResourceData)
  25. #pragma alloc_text(PAGE,LdrpAccessResourceDataNoMultipleLanguage)
  26. #pragma alloc_text(PAGE,LdrFindEntryForAddress)
  27. #pragma alloc_text(PAGE,LdrFindResource_U)
  28. #pragma alloc_text(PAGE,LdrFindResourceEx_U)
  29. #pragma alloc_text(PAGE,LdrFindResourceDirectory_U)
  30. #pragma alloc_text(PAGE,LdrpCompareResourceNames_U)
  31. #pragma alloc_text(PAGE,LdrpSearchResourceSection_U)
  32. #pragma alloc_text(PAGE,LdrEnumResources)
  33. #endif
  34. #define USE_RC_CHECKSUM
  35. // winuser.h
  36. #define IS_INTRESOURCE(_r) (((ULONG_PTR)(_r) >> 16) == 0)
  37. #define RT_VERSION 16
  38. #define RT_MANIFEST 24
  39. #define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
  40. #define ISOLATIONAWARE_MANIFEST_RESOURCE_ID 2
  41. #define MINIMUM_RESERVED_MANIFEST_RESOURCE_ID 1
  42. #define MAXIMUM_RESERVED_MANIFEST_RESOURCE_ID 16
  43. #define LDRP_MIN(x,y) (((x)<(y)) ? (x) : (y))
  44. #define DPFLTR_LEVEL_STATUS(x) ((NT_SUCCESS(x) \
  45. || (x) == STATUS_OBJECT_NAME_NOT_FOUND \
  46. || (x) == STATUS_RESOURCE_DATA_NOT_FOUND \
  47. || (x) == STATUS_RESOURCE_TYPE_NOT_FOUND \
  48. || (x) == STATUS_RESOURCE_NAME_NOT_FOUND \
  49. ) \
  50. ? DPFLTR_TRACE_LEVEL : DPFLTR_WARNING_LEVEL)
  51. #ifdef NTOS_USERMODE_RUNTIME
  52. #include <md5.h>
  53. //
  54. // The size in byte of the resource MD5 checksum. 16 bytes = 128 bits.
  55. //
  56. #define RESOURCE_CHECKSUM_SIZE 16
  57. //
  58. // The registry key path which stores the file version information for MUI files.
  59. //
  60. #define REG_MUI_PATH L"Software\\Microsoft\\Windows\\CurrentVersion"
  61. #define MUI_MUILANGUAGES_KEY_NAME L"MUILanguages"
  62. #define MUI_FILE_VERSION_KEY_NAME L"FileVersions"
  63. #define MUI_ALTERNATE_VERSION_KEY L"MUIVer"
  64. #define MUI_RC_CHECKSUM_DISABLE_KEY L"ChecksumDisable"
  65. #define MUI_NEUTRAL_LANGID MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US )
  66. #ifdef MUI_MAGIC
  67. #define MUI_COMPACT L"CMF"
  68. #define CMF_64K_OFFSET (ULONG)65536
  69. //
  70. // Locate target mui file in CMF file with CMFModule, Index data.
  71. // This will be replaced with function with sanity check
  72. //
  73. #define LDRP_GET_MODULE_OFFSET_FROM_CMF(CMFModule, wIndex) ((ULONG)(((PCOMPACT_MUI)( (unsigned char*)(CMFModule) + \
  74. sizeof (COMPACT_MUI_RESOURCE) + sizeof (COMPACT_MUI) * (wIndex)))->ulpOffset) );
  75. #define LDRP_GET_MODULE_FILESIZE_FROM_CMF(CMFModule, wIndex) ((ULONG)(((PCOMPACT_MUI)( (unsigned char*)(CMFModule) + \
  76. sizeof (COMPACT_MUI_RESOURCE) + sizeof (COMPACT_MUI) * (wIndex)))->dwFileSize) );
  77. //
  78. // Number of CMF files
  79. //
  80. #define CMF_BLOCK_NUM 32
  81. #endif
  82. PALT_RESOURCE_MODULE AlternateResourceModules;
  83. ULONG AlternateResourceModuleCount;
  84. ULONG AltResMemBlockCount;
  85. //
  86. // ImpersonateLangId UI langid stores pervious impersonated language id.
  87. //
  88. LANGID UILangId, InstallLangId, ImpersonateLangId;
  89. #define DWORD_ALIGNMENT(x) (((x)+3) & ~3)
  90. #define MEMBLOCKSIZE 32
  91. #define RESMODSIZE sizeof(ALT_RESOURCE_MODULE)
  92. #define uint32 unsigned int
  93. #if defined(_WIN64) || defined(BUILD_WOW6432)
  94. extern ULONG NativePageSize;
  95. #endif
  96. //
  97. // This macro ensures that correct UI language will be retrieved.
  98. //
  99. #define GET_UI_LANGID() \
  100. { \
  101. if (!UILangId || \
  102. ImpersonateLangId || \
  103. NtCurrentTeb()->IsImpersonating) \
  104. { \
  105. if (NT_SUCCESS( NtQueryDefaultUILanguage( &UILangId ) )) \
  106. { \
  107. ImpersonateLangId = NtCurrentTeb()->IsImpersonating? UILangId : 0; \
  108. } \
  109. } \
  110. } \
  111. #ifdef MUI_MAGIC
  112. VOID
  113. LdrpConvertVersionString(
  114. IN ULONGLONG ModuleVersion,
  115. OUT LPWSTR ModuleVersionStr
  116. );
  117. BOOLEAN
  118. LdrpOpenFileVersionKey(
  119. IN LPWSTR LangID,
  120. IN LPWSTR BaseDllName,
  121. IN ULONGLONG AltModuleVersion,
  122. IN LPWSTR AltModuleVersionStr,
  123. OUT PHANDLE pHandle);
  124. BOOLEAN
  125. LdrpGetRegValueKey(
  126. IN HANDLE Handle,
  127. IN LPWSTR KeyValueName,
  128. IN ULONG KeyValueType,
  129. OUT PVOID Buffer,
  130. IN ULONG BufferSize);
  131. BOOLEAN
  132. LdrpGetResourceChecksum(
  133. IN PVOID Module,
  134. OUT unsigned char** ppMD5Checksum
  135. );
  136. BOOLEAN
  137. LdrpCalcResourceChecksum(
  138. IN PVOID Module,
  139. IN PVOID AlternateModule,
  140. OUT unsigned char* MD5Checksum
  141. );
  142. BOOLEAN
  143. LdrpGetFileVersion(
  144. IN PVOID ImageBase,
  145. IN LANGID LangId,
  146. OUT PULONGLONG Version,
  147. OUT PVOID *VersionResource,
  148. OUT ULONG *VersionResSize
  149. );
  150. BOOLEAN
  151. LdrpGetCMFNameFromModule(
  152. IN PVOID Module,
  153. OUT LPWSTR *pCMFFileName,
  154. OUT PUSHORT wIndex
  155. )
  156. /* ++
  157. Routine description:
  158. If the file support CMF file for its resource storage, it should have "CompactMui" under
  159. VarInfo section uder Version resource. we search these block whether the module has this string.
  160. Args.
  161. Module - Base dll module, in this case Code module.
  162. CMFFileName - If the routine find CMF file, it will be stored here.
  163. wIndex - Module location inside CMF file.
  164. */
  165. {
  166. NTSTATUS Status;
  167. ULONG_PTR IdPath[3];
  168. ULONG ResourceSize;
  169. PIMAGE_RESOURCE_DATA_ENTRY DataEntry;
  170. LONG BlockLen;
  171. LONG VarFileInfoSize;
  172. typedef struct tagVS_FIXEDFILEINFO
  173. {
  174. LONG dwSignature; /* e.g. 0xfeef04bd */
  175. LONG dwStrucVersion; /* e.g. 0x00000042 = "0.42" */
  176. LONG dwFileVersionMS; /* e.g. 0x00030075 = "3.75" */
  177. LONG dwFileVersionLS; /* e.g. 0x00000031 = "0.31" */
  178. LONG dwProductVersionMS; /* e.g. 0x00030010 = "3.10" */
  179. LONG dwProductVersionLS; /* e.g. 0x00000031 = "0.31" */
  180. LONG dwFileFlagsMask; /* = 0x3F for version "0.42" */
  181. LONG dwFileFlags; /* e.g. VFF_DEBUG | VFF_PRERELEASE */
  182. LONG dwFileOS; /* e.g. VOS_DOS_WINDOWS16 */
  183. LONG dwFileType; /* e.g. VFT_DRIVER */
  184. LONG dwFileSubtype; /* e.g. VFT2_DRV_KEYBOARD */
  185. LONG dwFileDateMS; /* e.g. 0 */
  186. LONG dwFileDateLS; /* e.g. 0 */
  187. } VS_FIXEDFILEINFO;
  188. struct
  189. {
  190. USHORT TotalSize;
  191. USHORT DataSize;
  192. USHORT Type;
  193. WCHAR Name[16]; // L"VS_VERSION_INFO" + unicode null terminator
  194. // Note that the previous 4 members has 16*2 + 3*2 = 38 bytes.
  195. // So that compiler will silently add a 2 bytes padding to make
  196. // FixedFileInfo to align in DWORD boundary.
  197. VS_FIXEDFILEINFO FixedFileInfo;
  198. } *Resource;
  199. typedef struct tagVERBLOCK
  200. {
  201. USHORT wTotalLen;
  202. USHORT wValueLen;
  203. USHORT wType;
  204. WCHAR szKey[1];
  205. // BYTE[] padding
  206. // WORD value;
  207. } VERBLOCK;
  208. VERBLOCK *pVerBlock;
  209. IdPath[0] = RT_VERSION;
  210. IdPath[1] = 1;
  211. IdPath[2] = MUI_NEUTRAL_LANGID; // suppose, all localized OS use english resource code, is it corrret ???
  212. //
  213. // find the version resource data entry
  214. //
  215. try
  216. {
  217. Status = LdrFindResource_U(Module,IdPath,3,&DataEntry);
  218. if( !NT_SUCCESS(Status))
  219. {
  220. if (!InstallLangId){
  221. Status = NtQueryInstallUILanguage( &InstallLangId);
  222. if (!NT_SUCCESS( Status )) {
  223. //
  224. // Failed to get Intall LangID. AltResource not enabled.
  225. //
  226. return FALSE;
  227. }
  228. }
  229. //
  230. // InstallLangId vs 0 -> when user develop the application for their language resource,
  231. // and then use language neutral case. if it is different from installlangeId. it fails.
  232. // 0 is neutral lang id so it will search UI language, Installed Language ID.
  233. // 01/14/02; usiing InstalllangID instead of 0, we need to provide solution to localized application
  234. // mui developer;their code is same with UI language REVIST.
  235. //
  236. if (InstallLangId != MUI_NEUTRAL_LANGID)
  237. {
  238. IdPath[2] = InstallLangId;
  239. Status = LdrFindResource_U(Module,IdPath,3,&DataEntry);
  240. }
  241. }
  242. } except(EXCEPTION_EXECUTE_HANDLER)
  243. {
  244. Status = STATUS_UNSUCCESSFUL;
  245. }
  246. if(!NT_SUCCESS(Status))
  247. {
  248. return (FALSE);
  249. }
  250. //
  251. // Access the version resource data.
  252. //
  253. try
  254. {
  255. Status = LdrpAccessResourceDataNoMultipleLanguage(
  256. Module,
  257. DataEntry,
  258. &Resource,
  259. &ResourceSize);
  260. } except(EXCEPTION_EXECUTE_HANDLER)
  261. {
  262. Status = STATUS_UNSUCCESSFUL;
  263. }
  264. if(!NT_SUCCESS(Status))
  265. {
  266. return FALSE;
  267. }
  268. try
  269. {
  270. if((ResourceSize < sizeof(*Resource))
  271. || _wcsicmp(Resource->Name,L"VS_VERSION_INFO") != 0)
  272. {
  273. DbgPrint(("LDR: Warning: invalid version resource\n"));
  274. return FALSE;
  275. }
  276. } except(EXCEPTION_EXECUTE_HANDLER) {
  277. DbgPrint(("LDR: Exception encountered processing bogus version resource\n"));
  278. return FALSE;
  279. }
  280. ResourceSize -= DWORD_ALIGNMENT(sizeof(*Resource));
  281. //
  282. // Get the beginning address of the children of the version information.
  283. //
  284. pVerBlock = (VERBLOCK*)(Resource + 1);
  285. while ((LONG)ResourceSize > 0)
  286. {
  287. if (wcscmp(pVerBlock->szKey, L"VarFileInfo") == 0)
  288. {
  289. //
  290. // Find VarFileInfo block. Search the ResourceChecksum block.
  291. //
  292. VarFileInfoSize = pVerBlock->wTotalLen;
  293. BlockLen =DWORD_ALIGNMENT(sizeof(*pVerBlock) -1 + sizeof(L"VarFileInfo"));
  294. VarFileInfoSize -= BlockLen;
  295. pVerBlock = (VERBLOCK*)((unsigned char*)pVerBlock + BlockLen);
  296. while (VarFileInfoSize > 0)
  297. {
  298. if (wcscmp(pVerBlock->szKey, MUI_COMPACT) == 0)
  299. {
  300. *wIndex = *(PUSHORT)DWORD_ALIGNMENT((UINT_PTR)(pVerBlock->szKey) + sizeof(MUI_COMPACT));
  301. *pCMFFileName = (PVOID) DWORD_ALIGNMENT((UINT_PTR)(pVerBlock->szKey) + sizeof(MUI_COMPACT) + sizeof (ULONG) );
  302. return (TRUE);
  303. }
  304. BlockLen = DWORD_ALIGNMENT(pVerBlock->wTotalLen);
  305. pVerBlock = (VERBLOCK*)((unsigned char*)pVerBlock + BlockLen);
  306. VarFileInfoSize -= BlockLen;
  307. }
  308. return (FALSE);
  309. }
  310. BlockLen = DWORD_ALIGNMENT(pVerBlock->wTotalLen);
  311. pVerBlock = (VERBLOCK*)((unsigned char*)pVerBlock + BlockLen);
  312. ResourceSize -= BlockLen;
  313. }
  314. return (FALSE);
  315. }
  316. BOOLEAN
  317. LdrpCompareResourceChecksumInCMF(
  318. IN LPWSTR szLangIdDir,
  319. IN PVOID Module,
  320. IN ULONGLONG ModuleVersion,
  321. IN PVOID AlternateModule,
  322. IN PVOID CMFModule,
  323. IN USHORT wIndex,
  324. IN ULONGLONG AltModuleVersion,
  325. IN LPWSTR BaseDllName
  326. )
  327. /*++
  328. Routine Description:
  329. In the case that the version for the original module is different from that
  330. of the alternate module, check if the alternate module can still be used
  331. for the original version.
  332. First, the function will look at the registry to see if there is information
  333. cached for the module.
  334. In the case that the information is not cached for this module,
  335. this function will retrieve the MD5 resource checksum for the alternate
  336. resource module from CMF module instead of Alterate, which is overall efficient
  337. rather than retrieve the version from Module. And then check if the MD5 resource
  338. checksum is embeded in the original module. If MD5 resource checksum is not in the original
  339. moduel, it will enumerate all resources in the module to calculate the
  340. MD5 checksum.
  341. Arguments:
  342. szLangIdDir - Supplies a language of the resource to be loaded.
  343. Module - The original module.
  344. ModuleVersion - The version for the original version.
  345. CMFModule - The CMF file module.
  346. wIndex - The target Module location insde CMFModule.
  347. AltModuleVersion - The version for the alternate module.
  348. BaseDllName - The name of the DLL.
  349. Return Value:
  350. Ture if the alternate module can be used.
  351. Otherwise, return false.
  352. --*/
  353. {
  354. // Flag to indicate if the alternate resource can be used for this module.
  355. ULONG UseAlternateResource = 0;
  356. unsigned char* ModuleChecksum; // The 128-bit MD5 resource checksum for the module.
  357. unsigned char CalculatedModuleChecksum[16]; // The calculated 128-bit MD5 resource checksum for the module.
  358. unsigned char AlternateModuleChecksum[16]; // The 128-bit MD5 resource checksum embeded in the alternate module.
  359. WCHAR ModuleVersionStr[17]; // The string for the 16 heximal digit version.
  360. WCHAR AltModuleVersionStr[17];
  361. HANDLE Handle = NULL; // The registry which caches the information for this module.
  362. // Flag to indicate if we have retrieved or calucated the MD5 resource checksum for the original module successfully.
  363. BOOLEAN FoundModuleChecksum;
  364. UNICODE_STRING BufferString;
  365. PCOMPACT_MUI pcmui;
  366. //
  367. // Check the cached information in the registry first.
  368. //
  369. LdrpConvertVersionString(AltModuleVersion, AltModuleVersionStr);
  370. //
  371. // Open the version information key under:
  372. // HKCU\Control Panel\International\MUI\FileVersions\<LangID>\<BaseDllName>
  373. //
  374. if (LdrpOpenFileVersionKey(szLangIdDir, BaseDllName, AltModuleVersion, AltModuleVersionStr, &Handle))
  375. {
  376. LdrpConvertVersionString(ModuleVersion, ModuleVersionStr);
  377. //
  378. // Try to check if this module exists in version information.
  379. // If yes, see if the AlternateModule can be used.
  380. //
  381. //
  382. // Get the cached version information in the registry to see if the original module can re-use the alternative module.
  383. //
  384. if (LdrpGetRegValueKey(Handle, ModuleVersionStr, REG_DWORD, &UseAlternateResource, sizeof(UseAlternateResource)))
  385. {
  386. // Get the cached information. Let's bail and return the cached result in UseAlternativeResource.
  387. goto exit;
  388. }
  389. }
  390. //
  391. // When we are here, we know that we either:
  392. // 1. Can't open the registry key which cached the information. Or
  393. // 2. This file has never been looked before.
  394. //
  395. // Get the resource checksum for the alternate module.
  396. //
  397. try
  398. {
  399. pcmui = (PCOMPACT_MUI)((unsigned char*)CMFModule + sizeof (COMPACT_MUI_RESOURCE) +
  400. sizeof(COMPACT_MUI) * wIndex);
  401. }
  402. except(EXCEPTION_EXECUTE_HANDLER) {
  403. DbgPrint(("LDR: Exception encountered processing bogus CMF module\n"));
  404. goto exit;
  405. }
  406. if ( pcmui->Checksum )
  407. {
  408. memcpy(AlternateModuleChecksum, pcmui->Checksum, RESOURCE_CHECKSUM_SIZE);
  409. //
  410. // First, check if the resource checksum is built in the module.
  411. //
  412. if (!(FoundModuleChecksum = LdrpGetResourceChecksum(Module, &ModuleChecksum))) {
  413. //
  414. // If not, calculate the resource checksum for the current module.
  415. //
  416. if (FoundModuleChecksum = LdrpCalcResourceChecksum(Module, AlternateModule, CalculatedModuleChecksum))
  417. {
  418. ModuleChecksum = CalculatedModuleChecksum;
  419. }
  420. }
  421. if (FoundModuleChecksum)
  422. {
  423. if (memcmp(ModuleChecksum, AlternateModuleChecksum, RESOURCE_CHECKSUM_SIZE) == 0)
  424. {
  425. //
  426. // If the checksums are equal, the working version is the module version.
  427. //
  428. UseAlternateResource = 1;
  429. }
  430. }
  431. }
  432. if (Handle != NULL) {
  433. // If we find the version registry key successfully, cache the result in the registry.
  434. //
  435. // Write the working module information into registry.
  436. //
  437. RtlInitUnicodeString(&BufferString, ModuleVersionStr);
  438. NtSetValueKey(Handle, &BufferString, 0, REG_DWORD, &UseAlternateResource, sizeof(UseAlternateResource));
  439. }
  440. exit:
  441. if (Handle != NULL)
  442. {
  443. NtClose(Handle);
  444. }
  445. return ((BOOLEAN)(UseAlternateResource));
  446. }
  447. BOOLEAN
  448. LdrpVerifyAlternateResourceModuleInCMF(
  449. IN PWSTR szLangIdDir, // Language DIR
  450. IN PVOID Module, // Code file module
  451. IN PVOID AlternateModule,
  452. IN PVOID CMFModule, // CMF file module
  453. IN USHORT wIndex, // Compared MUI file index in CMF file.
  454. IN PWSTR BaseDllName
  455. )
  456. /*++
  457. Routine Description:
  458. This function verifies if the alternate resource module has the same
  459. version of the base module. For the Alternate, it refer the header in CMFModule
  460. rather than VERSION resource block.
  461. Arguments:
  462. szLangIdDir - The language ID path.
  463. Module - The handle of the base module.
  464. CMFModule - The handle of the CMF Module.
  465. wIndex - The index of target MUI in CMF module.
  466. BaseDllName - The file name of base DLL.
  467. Return Value:
  468. TBD.
  469. --*/
  470. {
  471. ULONGLONG ModuleVersion;
  472. ULONGLONG AltModuleVersion;
  473. NTSTATUS Status;
  474. PCOMPACT_MUI pcmui;
  475. int RetryCount =0;
  476. LANGID newLangID;
  477. LANGID preLangID = 0;
  478. try
  479. {
  480. pcmui = (PCOMPACT_MUI)((unsigned char*)CMFModule + sizeof (COMPACT_MUI_RESOURCE) +
  481. sizeof(COMPACT_MUI) * wIndex);
  482. // There is no sanity check for AltModuleVersion value.
  483. AltModuleVersion = ((ULONGLONG)pcmui->dwFileVersionMS << 32) |
  484. (ULONGLONG)pcmui->dwFileVersionLS;
  485. }
  486. except(EXCEPTION_EXECUTE_HANDLER) {
  487. DbgPrint(("LDR: Exception encountered processing bogus CMF Module\n"));
  488. return FALSE;
  489. }
  490. //
  491. // Some component is
  492. //
  493. while (RetryCount < 3 ) {
  494. switch(RetryCount) {
  495. case 0:
  496. newLangID = MUI_NEUTRAL_LANGID;
  497. break;
  498. case 1:
  499. if (!InstallLangId){
  500. Status = NtQueryInstallUILanguage( &InstallLangId);
  501. if (!NT_SUCCESS( Status )) {
  502. //
  503. // Failed to get Install LangID. AltResource not enabled.
  504. //
  505. return FALSE;
  506. }
  507. }
  508. newLangID = InstallLangId;
  509. break;
  510. case 2:
  511. if (MUI_NEUTRAL_LANGID != 0x409 ) {
  512. newLangID = 0x409;
  513. }
  514. break;
  515. }
  516. if ( newLangID != preLangID) {
  517. if (LdrpGetFileVersion(Module, newLangID, &ModuleVersion, NULL, NULL)){
  518. break;
  519. }
  520. }
  521. preLangID = newLangID;
  522. RetryCount++;
  523. }
  524. if (RetryCount >= 3) {
  525. return FALSE;
  526. }
  527. if (ModuleVersion == AltModuleVersion){
  528. return TRUE;
  529. }
  530. else
  531. {
  532. #ifdef USE_RC_CHECKSUM
  533. return LdrpCompareResourceChecksumInCMF(szLangIdDir, Module, ModuleVersion, AlternateModule, CMFModule, wIndex, AltModuleVersion, BaseDllName);
  534. #else
  535. return FALSE;
  536. #endif
  537. }
  538. }
  539. BOOLEAN
  540. LdrpSetAlternateResourceModuleHandleInCMF(
  541. IN PVOID Module,
  542. IN PVOID AlternateModule,
  543. IN PVOID CMFModule,
  544. IN PWSTR pwszCMFFileName,
  545. IN LANGID LangId)
  546. {
  547. /*++
  548. Routine Description:
  549. This function records the handle of the base module and alternate
  550. resource module in an array. In addition to this works of AlternateModule, this
  551. monitor CMF cache, which hold the data of CMF Module, CMF Name, Reference count.
  552. Arguments:
  553. Module - The handle of the base module.
  554. AlternateModule - The handle of the Alternate Module.
  555. CMFModule - The handle of the CMF module.
  556. pwszCMFFileName - The CMF file name.
  557. Return Value:
  558. True/ False
  559. --*/
  560. PALT_RESOURCE_MODULE NewModules;
  561. if (!LangId) {
  562. GET_UI_LANGID();
  563. LangId = UILangId;
  564. }
  565. if (!LangId) {
  566. return FALSE;
  567. }
  568. if (AlternateResourceModules == NULL){
  569. //
  570. // Allocate memory of initial size MEMBLOCKSIZE.
  571. //
  572. NewModules = (PALT_RESOURCE_MODULE)RtlAllocateHeap(
  573. RtlProcessHeap(),
  574. HEAP_ZERO_MEMORY,
  575. RESMODSIZE * MEMBLOCKSIZE);
  576. if (!NewModules){
  577. return FALSE;
  578. }
  579. AlternateResourceModules = NewModules;
  580. AltResMemBlockCount = MEMBLOCKSIZE;
  581. }
  582. else
  583. if (AlternateResourceModuleCount >= AltResMemBlockCount ){
  584. //
  585. // ReAllocate another chunk of memory.
  586. //
  587. NewModules = (PALT_RESOURCE_MODULE)RtlReAllocateHeap(
  588. RtlProcessHeap(),
  589. 0,
  590. AlternateResourceModules,
  591. (AltResMemBlockCount + MEMBLOCKSIZE) * RESMODSIZE
  592. );
  593. if (!NewModules){
  594. return FALSE;
  595. }
  596. AlternateResourceModules = NewModules;
  597. AltResMemBlockCount += MEMBLOCKSIZE;
  598. }
  599. AlternateResourceModules[AlternateResourceModuleCount].ModuleBase = Module;
  600. AlternateResourceModules[AlternateResourceModuleCount].AlternateModule = AlternateModule;
  601. AlternateResourceModules[AlternateResourceModuleCount].LangId = LangId ? LangId : UILangId;
  602. AlternateResourceModules[AlternateResourceModuleCount].CMFModule = CMFModule;
  603. AlternateResourceModuleCount++;
  604. return TRUE;
  605. }
  606. PVOID
  607. LdrpLoadAlternateResourceModule(
  608. IN LANGID LangId,
  609. IN PVOID Module,
  610. IN LPCWSTR PathToAlternateModule OPTIONAL
  611. )
  612. /*++
  613. Routine Description:
  614. Extend LdrLoadAlternateResourceModule with LangId to load
  615. language specific MUI alternative modules
  616. Once MUI_MAGIC is enabled, this API should become a public API
  617. and replace LdrLoadAlternateResourceModule
  618. This function does the acutally loading into memory of the alternate
  619. resource module, or loads from the table if it was loaded before.
  620. Arguments:
  621. LangId - Overwrite default UI language if it isn't zero
  622. Module - The handle of the base module.
  623. PathToAlternateModule - Optional path from which module is being loaded.
  624. Return Value:
  625. Handle to the alternate resource module.
  626. --*/
  627. {
  628. PVOID AlternateModule, DllBase;
  629. PLDR_DATA_TABLE_ENTRY Entry;
  630. HANDLE FileHandle, MappingHandle;
  631. PIMAGE_NT_HEADERS NtHeaders;
  632. NTSTATUS Status;
  633. OBJECT_ATTRIBUTES ObjectAttributes;
  634. UNICODE_STRING AltDllName;
  635. PVOID FreeBuffer;
  636. LPWSTR BaseDllName = NULL, p;
  637. WCHAR DllPathName[DOS_MAX_PATH_LENGTH];
  638. ULONG DllPathNameLength, BaseDllNameLength, CopyCount;
  639. ULONG Digit;
  640. int i, RetryCount;
  641. WCHAR AltModulePath[DOS_MAX_PATH_LENGTH];
  642. WCHAR AltModulePathMUI[DOS_MAX_PATH_LENGTH];
  643. WCHAR AltModulePathFallback[DOS_MAX_PATH_LENGTH];
  644. IO_STATUS_BLOCK IoStatusBlock;
  645. RTL_RELATIVE_NAME_U RelativeName;
  646. SIZE_T ViewSize;
  647. LARGE_INTEGER SectionOffset;
  648. WCHAR LangIdDir[6];
  649. PVOID ReturnValue = NULL;
  650. char szBaseName[20];
  651. //
  652. // The full path of the current MUI file that we are searching.
  653. //
  654. UNICODE_STRING CurrentAltModuleFile;
  655. UNICODE_STRING SystemRoot;
  656. //
  657. // The current MUI folder that we are searching.
  658. //
  659. UNICODE_STRING CurrentAltModulePath;
  660. WCHAR CurrentAltModulePathBuffer[DOS_MAX_PATH_LENGTH];
  661. //
  662. // The string contains the first MUI folder that we will search.
  663. // This is the folder which lives under the folder of the base DLL.
  664. // AltDllMUIPath = [the folder of the base DLL] + "\mui" + "\[UI Language]";
  665. // E.g. if the base DLL is "c:\winnt\system32\ntdll.dll" and UI language is 0411,
  666. // AltDllMUIPath will be "c:\winnt\system32\mui\0411\"
  667. //
  668. UNICODE_STRING AltDllMUIPath;
  669. WCHAR AltDllMUIPathBuffer[DOS_MAX_PATH_LENGTH];
  670. //
  671. // MUI Redir
  672. //
  673. UNICODE_STRING BaseDllNameUstr;
  674. UNICODE_STRING StaticStringAltModulePathRedirected;
  675. UNICODE_STRING DynamicStringAltModulePathRedirected;
  676. PUNICODE_STRING FullPathStringFoundAltModulePathRedirected = NULL;
  677. BOOLEAN fRedirMUI = FALSE;
  678. PVOID LockCookie = NULL;
  679. BOOLEAN fIsCMFFile = FALSE;
  680. LPWSTR CMFFileName = NULL;
  681. USHORT wIndex;
  682. PVOID CMFModule = NULL;
  683. PVOID pX64KBase = NULL;
  684. ULONG ulMuiFileSize;
  685. ULONG ulOffset;
  686. // bail out early if this isn't a MUI-enabled system
  687. if (!LdrAlternateResourcesEnabled()) {
  688. return NULL;
  689. }
  690. if (!LangId) {
  691. GET_UI_LANGID();
  692. LangId = UILangId;
  693. }
  694. LdrLockLoaderLock(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, NULL, &LockCookie);
  695. __try {
  696. //
  697. // Look at the cache of the alternate module first.
  698. //
  699. AlternateModule = LdrpGetAlternateResourceModuleHandle(Module, LangId);
  700. if (AlternateModule == NO_ALTERNATE_RESOURCE_MODULE) {
  701. //
  702. // We tried to load this module before but failed. Don't try
  703. // again in the future.
  704. //
  705. ReturnValue = NULL;
  706. __leave;
  707. } else if (AlternateModule > 0) {
  708. //
  709. // We found the previously loaded match
  710. //
  711. ReturnValue = AlternateModule;
  712. __leave;
  713. }
  714. AlternateModule = NULL;
  715. if (ARGUMENT_PRESENT(PathToAlternateModule)) {
  716. //
  717. // Caller suplied path.
  718. //
  719. p = wcsrchr(PathToAlternateModule, L'\\');
  720. if (p == NULL){ // ReturnValue == NULL;
  721. __leave;
  722. }
  723. p++;
  724. DllPathNameLength = (ULONG)(p - PathToAlternateModule) * sizeof(WCHAR);
  725. RtlCopyMemory(
  726. DllPathName,
  727. PathToAlternateModule,
  728. DllPathNameLength);
  729. BaseDllName = p;
  730. BaseDllNameLength = wcslen(p);
  731. } else {
  732. //
  733. // Try to get full dll path from Ldr data table.
  734. //
  735. Status = LdrFindEntryForAddress(Module, &Entry);
  736. if (!NT_SUCCESS(Status)) { // ReturnValue = NULL;
  737. __leave;
  738. }
  739. DllPathNameLength = Entry->FullDllName.Length - Entry->BaseDllName.Length;
  740. RtlCopyMemory(
  741. DllPathName,
  742. Entry->FullDllName.Buffer,
  743. DllPathNameLength);
  744. BaseDllName = Entry->BaseDllName.Buffer;
  745. BaseDllNameLength = Entry->BaseDllName.Length;
  746. }
  747. // if module support CMF for MUI file, we replace BaseDllName(resource dll) with CMF name.
  748. if (LdrpGetCMFNameFromModule(Module, &CMFFileName, &wIndex))
  749. {
  750. fIsCMFFile = TRUE;
  751. BaseDllName = CMFFileName;
  752. BaseDllNameLength = wcslen(CMFFileName);
  753. }
  754. DllPathName[DllPathNameLength / sizeof(WCHAR)] = UNICODE_NULL;
  755. //
  756. // dll redirection for the dll to be loaded xiaoyuw@10/31/2000
  757. //
  758. StaticStringAltModulePathRedirected.Buffer = AltModulePath; // reuse the array instead of define another array
  759. StaticStringAltModulePathRedirected.Length = 0;
  760. StaticStringAltModulePathRedirected.MaximumLength = sizeof(AltModulePath);
  761. DynamicStringAltModulePathRedirected.Buffer = NULL;
  762. DynamicStringAltModulePathRedirected.Length = 0;
  763. DynamicStringAltModulePathRedirected.MaximumLength = 0;
  764. BaseDllNameUstr.Buffer = AltModulePathMUI; // reuse the array instead of define another array
  765. BaseDllNameUstr.Length = 0;
  766. BaseDllNameUstr.MaximumLength = sizeof(AltModulePathMUI);
  767. RtlAppendUnicodeToString(&BaseDllNameUstr, BaseDllName);
  768. if (!fIsCMFFile){
  769. RtlAppendUnicodeToString(&BaseDllNameUstr, L".mui");
  770. }
  771. Status = RtlDosApplyFileIsolationRedirection_Ustr(
  772. RTL_DOS_APPLY_FILE_REDIRECTION_USTR_FLAG_RESPECT_DOT_LOCAL,
  773. &BaseDllNameUstr, NULL,
  774. &StaticStringAltModulePathRedirected,
  775. &DynamicStringAltModulePathRedirected,
  776. &FullPathStringFoundAltModulePathRedirected,
  777. NULL,NULL, NULL);
  778. if (!NT_SUCCESS(Status)) // no redirection info found for this string
  779. {
  780. if (Status != STATUS_SXS_KEY_NOT_FOUND)
  781. goto error_exit;
  782. //
  783. // Generate the langid directory like "0804\"
  784. //
  785. if (!LangId) {
  786. GET_UI_LANGID();
  787. if (!UILangId){
  788. goto error_exit;
  789. }
  790. else {
  791. LangId = UILangId;
  792. }
  793. }
  794. CopyCount = 0;
  795. for (i = 12; i >= 0; i -= 4) {
  796. Digit = ((LangId >> i) & 0xF);
  797. if (Digit >= 10) {
  798. LangIdDir[CopyCount++] = (WCHAR) (Digit - 10 + L'A');
  799. } else {
  800. LangIdDir[CopyCount++] = (WCHAR) (Digit + L'0');
  801. }
  802. }
  803. LangIdDir[CopyCount++] = L'\\';
  804. LangIdDir[CopyCount++] = UNICODE_NULL;
  805. //
  806. // Create the MUI path under the directory of the base DLL.
  807. //
  808. AltDllMUIPath.Buffer = AltDllMUIPathBuffer;
  809. AltDllMUIPath.Length = 0;
  810. AltDllMUIPath.MaximumLength = sizeof(AltDllMUIPathBuffer);
  811. RtlAppendUnicodeToString(&AltDllMUIPath, DllPathName); // e.g. "c:\winnt\system32\"
  812. RtlAppendUnicodeToString(&AltDllMUIPath, L"mui\\"); // e.g. "c:\winnt\system32\mui\"
  813. RtlAppendUnicodeToString(&AltDllMUIPath, LangIdDir); // e.g. "c:\winnt\system32\mui\0411\"
  814. CurrentAltModulePath.Buffer = CurrentAltModulePathBuffer;
  815. CurrentAltModulePath.Length = 0;
  816. CurrentAltModulePath.MaximumLength = sizeof(CurrentAltModulePathBuffer);
  817. } else {
  818. fRedirMUI = TRUE;
  819. //set CurrentAltModuleFile and CurrentAltModulePath
  820. CurrentAltModuleFile.Buffer = AltModulePathMUI;
  821. CurrentAltModuleFile.Length = 0;
  822. CurrentAltModuleFile.MaximumLength = sizeof(AltModulePathMUI);
  823. RtlCopyUnicodeString(&CurrentAltModuleFile, FullPathStringFoundAltModulePathRedirected);
  824. }
  825. //
  826. // Try name with .mui extesion first.
  827. //
  828. RetryCount = 0;
  829. while (RetryCount < 3){
  830. if ( ! fRedirMUI )
  831. {
  832. switch (RetryCount)
  833. {
  834. case 0:
  835. //
  836. // Generate the first path under the folder of the base DLL
  837. // (e.g. c:\winnt\system32\mui\0804\ntdll.dll.mui)
  838. //
  839. CurrentAltModuleFile.Buffer = AltModulePathMUI;
  840. CurrentAltModuleFile.Length = 0;
  841. CurrentAltModuleFile.MaximumLength = sizeof(AltModulePathMUI);
  842. RtlCopyUnicodeString(&CurrentAltModuleFile, &AltDllMUIPath); // e.g. "c:\winnt\system32\mui\0411\"
  843. RtlCopyUnicodeString(&CurrentAltModulePath, &AltDllMUIPath);
  844. RtlAppendUnicodeToString(&CurrentAltModuleFile, BaseDllName); // e.g. "c:\winnt\system32\mui\0411\ntdll.dll"
  845. if (!fIsCMFFile)
  846. {
  847. RtlAppendUnicodeToString(&CurrentAltModuleFile, L".mui"); // e.g. "c:\winnt\system32\mui\0411\ntdll.dll.mui"
  848. }
  849. break;
  850. case 1:
  851. //
  852. // Generate the second path c:\winnt\system32\mui\0804\ntdll.dll.mui
  853. //
  854. CurrentAltModuleFile.Buffer = AltModulePath;
  855. CurrentAltModuleFile.Length = 0;
  856. CurrentAltModuleFile.MaximumLength = sizeof(AltModulePath);
  857. RtlCopyUnicodeString(&CurrentAltModuleFile, &AltDllMUIPath); // e.g. "c:\winnt\system32\mui\0411\"
  858. RtlAppendUnicodeToString(&CurrentAltModuleFile, BaseDllName); // e.g. "c:\winnt\system32\mui\0411\ntdll.dll"
  859. break;
  860. case 2:
  861. //
  862. // Generate path c:\winnt\mui\fallback\0804\foo.exe.mui
  863. //
  864. CurrentAltModuleFile.Buffer = AltModulePathFallback;
  865. CurrentAltModuleFile.Length = 0;
  866. CurrentAltModuleFile.MaximumLength = sizeof(AltModulePathFallback);
  867. RtlInitUnicodeString(&SystemRoot, USER_SHARED_DATA->NtSystemRoot); // e.g. "c:\winnt\system32\"
  868. RtlAppendUnicodeStringToString(&CurrentAltModuleFile, &SystemRoot); // e.g. "c:\winnt\system32\"
  869. RtlAppendUnicodeToString(&CurrentAltModuleFile, L"\\mui\\fallback\\"); // e.g. "c:\winnt\system32\mui\fallback\"
  870. RtlAppendUnicodeToString(&CurrentAltModuleFile, LangIdDir); // e.g. "c:\winnt\system32\mui\fallback\0411\"
  871. RtlCopyUnicodeString(&CurrentAltModulePath, &CurrentAltModuleFile);
  872. RtlAppendUnicodeToString(&CurrentAltModuleFile, BaseDllName); // e.g. "c:\winnt\system32\mui\fallback\0411\ntdll.dll"
  873. if(!fIsCMFFile)
  874. {
  875. RtlAppendUnicodeToString(&CurrentAltModuleFile, L".mui"); // e.g. "c:\winnt\system32\mui\fallback\0411\ntdll.dll.mui"
  876. }
  877. break;
  878. }
  879. }
  880. if (!RtlDosPathNameToRelativeNtPathName_U(
  881. CurrentAltModuleFile.Buffer,
  882. &AltDllName,
  883. NULL,
  884. &RelativeName)) {
  885. goto error_exit;
  886. }
  887. FreeBuffer = AltDllName.Buffer;
  888. if (RelativeName.RelativeName.Length != 0) {
  889. AltDllName = RelativeName.RelativeName;
  890. } else {
  891. RelativeName.ContainingDirectory = NULL;
  892. }
  893. InitializeObjectAttributes(
  894. &ObjectAttributes,
  895. &AltDllName,
  896. OBJ_CASE_INSENSITIVE,
  897. RelativeName.ContainingDirectory,
  898. NULL
  899. );
  900. Status = NtCreateFile(
  901. &FileHandle,
  902. (ACCESS_MASK) GENERIC_READ | SYNCHRONIZE | FILE_READ_ATTRIBUTES,
  903. &ObjectAttributes,
  904. &IoStatusBlock,
  905. NULL,
  906. 0L,
  907. FILE_SHARE_READ | FILE_SHARE_DELETE,
  908. FILE_OPEN,
  909. 0L,
  910. NULL,
  911. 0L
  912. );
  913. RtlReleaseRelativeName(&RelativeName);
  914. RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
  915. if (NT_SUCCESS(Status)) {
  916. goto CreateSection;
  917. }
  918. if (fRedirMUI) { // definitely failed
  919. goto error_exit;
  920. }
  921. if (Status != STATUS_OBJECT_NAME_NOT_FOUND && RetryCount == 0) {
  922. //
  923. // Error other than the file name with .mui not found.
  924. // Most likely directory is missing. Skip file name w/o .mui
  925. // and goto fallback directory.
  926. //
  927. RetryCount++;
  928. }
  929. RetryCount++;
  930. }
  931. // No alternate resource was found during the iterations. Fail!
  932. goto error_exit;
  933. CreateSection:
  934. Status = NtCreateSection(
  935. &MappingHandle,
  936. STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ,
  937. NULL,
  938. NULL,
  939. PAGE_WRITECOPY,
  940. SEC_COMMIT,
  941. FileHandle
  942. );
  943. NtClose( FileHandle );
  944. if (!NT_SUCCESS(Status)) {
  945. goto error_exit;
  946. }
  947. SectionOffset.LowPart = 0;
  948. SectionOffset.HighPart = 0;
  949. ViewSize = 0;
  950. DllBase = NULL;
  951. Status = NtMapViewOfSection(
  952. MappingHandle,
  953. NtCurrentProcess(),
  954. &DllBase,
  955. 0L,
  956. 0L,
  957. &SectionOffset,
  958. &ViewSize,
  959. ViewShare,
  960. 0L,
  961. PAGE_WRITECOPY
  962. );
  963. if (!(fIsCMFFile && NT_SUCCESS(Status)) ) {
  964. NtClose(MappingHandle);
  965. }
  966. if (!NT_SUCCESS(Status)){
  967. goto error_exit;
  968. }
  969. if (fIsCMFFile)
  970. {
  971. CMFModule = DllBase;
  972. try
  973. {
  974. ulOffset = LDRP_GET_MODULE_OFFSET_FROM_CMF(CMFModule, wIndex);
  975. ulMuiFileSize = LDRP_GET_MODULE_FILESIZE_FROM_CMF(CMFModule, wIndex);
  976. }
  977. except(EXCEPTION_EXECUTE_HANDLER) {
  978. NtClose(MappingHandle);
  979. DbgPrint(("LDR: Exception encountered processing bogus CMF Module\n"));
  980. goto error_exit;
  981. }
  982. //
  983. // MapView section offset should start on the based of system granularity(64K), and when we
  984. // set this with specified view size, it map with round to 4K page base.
  985. //
  986. SectionOffset.LowPart = (ulOffset >> 16)*CMF_64K_OFFSET ; // get 64K base
  987. SectionOffset.HighPart = 0;
  988. ViewSize = (ulOffset & (CMF_64K_OFFSET-1) ) + ulMuiFileSize; // size from 64K offset, we can have 4K based mem.
  989. DllBase = NULL;
  990. Status = NtMapViewOfSection(
  991. MappingHandle,
  992. NtCurrentProcess(),
  993. &DllBase,
  994. 0L,
  995. 0L,
  996. &SectionOffset,
  997. &ViewSize,
  998. ViewShare,
  999. 0L,
  1000. PAGE_WRITECOPY
  1001. );
  1002. NtClose(MappingHandle);
  1003. if (!NT_SUCCESS(Status)){
  1004. goto error_exit;
  1005. }
  1006. pX64KBase = DllBase;
  1007. DllBase = (unsigned char*)pX64KBase + (ulOffset & (CMF_64K_OFFSET-1) ) ;
  1008. }
  1009. NtHeaders = RtlImageNtHeader(DllBase);
  1010. if (!NtHeaders) {
  1011. if (fIsCMFFile) {
  1012. DllBase = CMFModule;
  1013. }
  1014. NtUnmapViewOfSection(NtCurrentProcess(), (PVOID) DllBase);
  1015. goto error_exit;
  1016. }
  1017. AlternateModule = LDR_VIEW_TO_DATAFILE(DllBase);
  1018. //
  1019. // Verify althenative module and module version, checksum.
  1020. // In case of using CMF,Version, Checksum is saved in CMF header. we can use it.
  1021. //
  1022. if (fIsCMFFile)
  1023. {
  1024. if (!LdrpVerifyAlternateResourceModuleInCMF(LangIdDir, Module, AlternateModule, CMFModule, wIndex, BaseDllName) )
  1025. {
  1026. NtUnmapViewOfSection(NtCurrentProcess(), (PVOID) CMFModule);
  1027. goto error_exit;
  1028. }
  1029. // Finally, we don't need CMFModule anymore.
  1030. NtUnmapViewOfSection(NtCurrentProcess(), CMFModule);
  1031. }
  1032. else
  1033. {
  1034. if(!LdrpVerifyAlternateResourceModule(LangIdDir, Module, AlternateModule, BaseDllName, LangId))
  1035. {
  1036. NtUnmapViewOfSection(NtCurrentProcess(), (PVOID) DllBase);
  1037. goto error_exit;
  1038. }
  1039. }
  1040. LdrpSetAlternateResourceModuleHandleInCMF(Module, AlternateModule, pX64KBase, NULL, LangId);
  1041. #if DBG
  1042. for ( i=0; i< 16; i++) {
  1043. if (!BaseDllName[i])
  1044. break;
  1045. szBaseName[i] = (char) BaseDllName[i];
  1046. }
  1047. szBaseName[i] = 0;
  1048. DbgPrintEx(
  1049. DPFLTR_LDR_ID,
  1050. DPFLTR_TRACE_LEVEL,
  1051. "LDR: MUI module for %s %x is %x, Lang: %x, Impersonation:%d\n",
  1052. szBaseName, Module, AlternateModule, LangId, NtCurrentTeb()->IsImpersonating
  1053. );
  1054. #endif
  1055. ReturnValue = AlternateModule;
  1056. __leave;
  1057. error_exit:
  1058. if (BaseDllName != NULL) {
  1059. //
  1060. // If we looked for a MUI file and couldn't find one keep track. If
  1061. // we couldn't get the base dll name (e.g. someone passing in a
  1062. // mapped image with the low bit set but no path name), we don't want
  1063. // to "remember" that there's no MUI.
  1064. //
  1065. LdrpSetAlternateResourceModuleHandleInCMF(Module, NO_ALTERNATE_RESOURCE_MODULE, NULL, NULL, LangId);
  1066. #if DBG
  1067. for ( i=0; i< 16; i++){
  1068. if (!BaseDllName[i])
  1069. break;
  1070. szBaseName[i] = (char) BaseDllName[i];
  1071. }
  1072. szBaseName[i] = 0;
  1073. DbgPrintEx(
  1074. DPFLTR_LDR_ID,
  1075. DPFLTR_TRACE_LEVEL,
  1076. "LDR: No MUI module for %s %x, Lang: %x, Impersonation:%d\n",
  1077. szBaseName, Module, LangId, NtCurrentTeb()->IsImpersonating);
  1078. #endif
  1079. }
  1080. ReturnValue = NULL;
  1081. } __finally {
  1082. Status = LdrUnlockLoaderLock(LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, LockCookie);
  1083. }
  1084. return ReturnValue;
  1085. // Compiler *should* be smart enough to recognize that we don't need a return here...
  1086. }
  1087. NTSTATUS LdrpLoadResourceFromAlternativeModule(
  1088. IN PVOID DllHandle,
  1089. IN ULONG_PTR* ResourceIdPath,
  1090. IN ULONG ResourceIdPathLength,
  1091. IN ULONG Flags,
  1092. OUT PVOID *ResourceDirectoryOrData )
  1093. /*++
  1094. Routine Description:
  1095. This function locates the address of the specified resource in the
  1096. specified module's MUI resource DLL and returns its address.
  1097. Arguments:
  1098. DllHandle - Supplies a handle to the image file that the resource is
  1099. contained in.
  1100. ResourceIdPath - Supplies a pointer to an array of 32-bit resource
  1101. identifiers. Each identifier is either an integer or a pointer
  1102. to a null terminated string (PSZ) that specifies a resource
  1103. name. The array is used to traverse the directory structure
  1104. contained in the resource section in the image file specified by
  1105. the DllHandle parameter.
  1106. ResourceIdPathLength - Supplies the number of elements in the
  1107. ResourceIdPath array.
  1108. Flags -
  1109. LDRP_FIND_RESOURCE_DIRECTORY
  1110. searching for a resource directory, otherwise the caller is
  1111. searching for a resource data entry.
  1112. LDR_FIND_RESOURCE_LANGUAGE_EXACT
  1113. searching for a resource with, and only with, the language id
  1114. specified in ResourceIdPath, otherwise the caller wants the routine
  1115. to come up with default when specified langid is not found.
  1116. LDR_FIND_RESOURCE_LANGUAGE_REDIRECT_VERSION
  1117. searching for a resource version in main and alternative
  1118. modules paths
  1119. FindDirectoryEntry - Supplies a boolean that is TRUE if caller is
  1120. searching for a resource directory, otherwise the caller is
  1121. searching for a resource data entry.
  1122. ExactLangMatchOnly - Supplies a boolean that is TRUE if caller is
  1123. searching for a resource with, and only with, the language id
  1124. specified in ResourceIdPath, otherwise the caller wants the routine
  1125. to come up with default when specified langid is not found.
  1126. ResourceDirectoryOrData - Supplies a pointer to a variable that will
  1127. receive the address of the resource directory or data entry in
  1128. the resource data section of the image file specified by the
  1129. DllHandle parameter.
  1130. Return Value:
  1131. TBD
  1132. --*/
  1133. {
  1134. NTSTATUS Status = STATUS_RESOURCE_DATA_NOT_FOUND;
  1135. PVOID AltResourceDllHandle = NULL;
  1136. LANGID MUIDirLang;
  1137. if (3 != ResourceIdPathLength)
  1138. return Status;
  1139. GET_UI_LANGID();
  1140. if (!UILangId){
  1141. return Status;
  1142. }
  1143. if (!InstallLangId){
  1144. Status = NtQueryInstallUILanguage (&InstallLangId);
  1145. if (!NT_SUCCESS( Status )) {
  1146. //
  1147. // Failed to get Install LangID. AltResource not enabled.
  1148. //
  1149. return FALSE;
  1150. }
  1151. }
  1152. if (ResourceIdPath[2]) {
  1153. //
  1154. // Arabic/Hebrew MUI files may contain resources with LANG ID different than 401/40d.
  1155. // e.g. Comdlg32.dll has two sets of Arabic/Hebrew resources one mirrored (401/40d)
  1156. // and one flipped (801/80d).
  1157. //
  1158. if( (ResourceIdPath[2] != UILangId) &&
  1159. ((PRIMARYLANGID ( UILangId) == LANG_ARABIC) || (PRIMARYLANGID ( UILangId) == LANG_HEBREW)) &&
  1160. (PRIMARYLANGID (ResourceIdPath[2]) == PRIMARYLANGID (UILangId))
  1161. ) {
  1162. ResourceIdPath[2] = UILangId;
  1163. }
  1164. }
  1165. else {
  1166. ResourceIdPath[2] = UILangId;
  1167. }
  1168. // Don't load alternative modules for console process if UI language doesn't match system locale
  1169. // In this case, we always load English
  1170. if (NtCurrentPeb()->ProcessParameters->ConsoleHandle &&
  1171. LANGIDFROMLCID(NtCurrentTeb()->CurrentLocale) != ResourceIdPath[2])
  1172. {
  1173. ResourceIdPath[2] = MUI_NEUTRAL_LANGID;
  1174. UILangId = MUI_NEUTRAL_LANGID;
  1175. }
  1176. //
  1177. // Bug #246044 WeiWu 12/07/00
  1178. // BiDi modules use version block FileDescription field to store LRM markers,
  1179. // LDR_FIND_RESOURCE_LANGUAGE_REDIRECT_VERSION will allow lpk.dll to get version resource from MUI alternative modules.
  1180. //
  1181. if ((ResourceIdPath[0] != RT_VERSION) ||
  1182. (Flags & LDR_FIND_RESOURCE_LANGUAGE_REDIRECT_VERSION)) {
  1183. RESOURCE_TRY_AGAIN:
  1184. //
  1185. // Load alternate resource dll when:
  1186. // 1. language is neutral
  1187. // or
  1188. // Given language is not tried.
  1189. // and
  1190. // 2. the resource to load is not a version info.
  1191. //
  1192. AltResourceDllHandle=LdrpLoadAlternateResourceModule(
  1193. (LANGID) ResourceIdPath[2],
  1194. DllHandle,
  1195. NULL);
  1196. if (!AltResourceDllHandle){
  1197. //
  1198. // Alternate resource dll not available.
  1199. // Skip this lookup.
  1200. //
  1201. if ((LANGID) ResourceIdPath[2] != InstallLangId){
  1202. ResourceIdPath[2] = InstallLangId;
  1203. //UILangId = InstallLangId;
  1204. goto RESOURCE_TRY_AGAIN;
  1205. }
  1206. else {
  1207. return Status;
  1208. }
  1209. }
  1210. //
  1211. // Add fallback steps here for alternative module search
  1212. // 1) Given langid
  1213. // 2) Primary langid of given langid if 2 != 1
  1214. // 3) System installed langid if 3 != 1
  1215. //
  1216. MUIDirLang = (LANGID)ResourceIdPath[2];
  1217. SearchResourceSection:
  1218. Status = LdrpSearchResourceSection_U(
  1219. AltResourceDllHandle,
  1220. ResourceIdPath,
  1221. 3,
  1222. Flags | LDR_FIND_RESOURCE_LANGUAGE_EXACT,
  1223. (PVOID *)ResourceDirectoryOrData
  1224. );
  1225. if (!NT_SUCCESS(Status) ) {
  1226. if ((LANGID) ResourceIdPath[2] != 0x409) {
  1227. // some English components are not localized. but this unlocalized mui file
  1228. // is saved under \mui\fallback\%LocalizedLang%\ so we just repeat search.
  1229. // this is a temporary hack, we'll have a better solution
  1230. ResourceIdPath[2] = 0x409;
  1231. goto SearchResourceSection;
  1232. }
  1233. if ( (LANGID) MUIDirLang != InstallLangId) {
  1234. ResourceIdPath[2] = InstallLangId;
  1235. goto RESOURCE_TRY_AGAIN;
  1236. }
  1237. }
  1238. }
  1239. return Status;
  1240. }
  1241. #endif // #ifdef MUI_MAGIC
  1242. #endif // #ifdef NTOS_USERMODE_RUNTIME
  1243. #if defined(_X86_) && defined(NTOS_USERMODE_RUNTIME)
  1244. // appcompat: There's some code that depends on the Win2k instruction stream - duplicate it here.
  1245. __declspec(naked)
  1246. #endif
  1247. NTSTATUS
  1248. LdrAccessResource(
  1249. IN PVOID DllHandle,
  1250. IN const IMAGE_RESOURCE_DATA_ENTRY* ResourceDataEntry,
  1251. OUT PVOID *Address OPTIONAL,
  1252. OUT PULONG Size OPTIONAL
  1253. )
  1254. /*++
  1255. Routine Description:
  1256. This function locates the address of the specified resource in the
  1257. specified DLL and returns its address.
  1258. Arguments:
  1259. DllHandle - Supplies a handle to the image file that the resource is
  1260. contained in.
  1261. ResourceDataEntry - Supplies a pointer to the resource data entry in
  1262. the resource data section of the image file specified by the
  1263. DllHandle parameter. This pointer should have been one returned
  1264. by the LdrFindResource function.
  1265. Address - Optional pointer to a variable that will receive the
  1266. address of the resource specified by the first two parameters.
  1267. Size - Optional pointer to a variable that will receive the size of
  1268. the resource specified by the first two parameters.
  1269. Return Value:
  1270. TBD
  1271. --*/
  1272. {
  1273. #if defined(_X86_) && defined(NTOS_USERMODE_RUNTIME)
  1274. __asm {
  1275. push [esp+0x10] // Size
  1276. push [esp+0x10] // Address
  1277. push [esp+0x10] // ResourceDataEntry
  1278. push [esp+0x10] // DllHandle
  1279. call LdrpAccessResourceData
  1280. ret 16
  1281. }
  1282. #else
  1283. NTSTATUS Status;
  1284. RTL_PAGED_CODE();
  1285. Status =
  1286. LdrpAccessResourceData(
  1287. DllHandle,
  1288. ResourceDataEntry,
  1289. Address,
  1290. Size
  1291. );
  1292. #if DBG
  1293. if (!NT_SUCCESS(Status)) {
  1294. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_LEVEL_STATUS(Status), "LDR: %s() exiting 0x%08lx\n", __FUNCTION__, Status));
  1295. }
  1296. #endif
  1297. return Status;
  1298. #endif
  1299. }
  1300. NTSTATUS
  1301. LdrpAccessResourceDataNoMultipleLanguage(
  1302. IN PVOID DllHandle,
  1303. IN const IMAGE_RESOURCE_DATA_ENTRY* ResourceDataEntry,
  1304. OUT PVOID *Address OPTIONAL,
  1305. OUT PULONG Size OPTIONAL
  1306. )
  1307. /*++
  1308. Routine Description:
  1309. This function returns the data necessary to actually examine the
  1310. contents of a particular resource, without allowing for the .mui
  1311. feature. It used to be the tail of LdrpAccessResourceData, from
  1312. which it is now called.
  1313. Arguments:
  1314. DllHandle - Supplies a handle to the image file that the resource is
  1315. contained in.
  1316. ResourceDataEntry - Supplies a pointer to the resource data entry in
  1317. the resource data directory of the image file specified by the
  1318. DllHandle parameter. This pointer should have been one returned
  1319. by the LdrFindResource function.
  1320. Address - Optional pointer to a variable that will receive the
  1321. address of the resource specified by the first two parameters.
  1322. Size - Optional pointer to a variable that will receive the size of
  1323. the resource specified by the first two parameters.
  1324. Return Value:
  1325. TBD
  1326. --*/
  1327. {
  1328. PIMAGE_RESOURCE_DIRECTORY ResourceDirectory;
  1329. ULONG ResourceSize;
  1330. PIMAGE_NT_HEADERS NtHeaders;
  1331. ULONG_PTR VirtualAddressOffset;
  1332. PIMAGE_SECTION_HEADER NtSection;
  1333. NTSTATUS Status = STATUS_SUCCESS;
  1334. RTL_PAGED_CODE();
  1335. try {
  1336. ResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
  1337. RtlImageDirectoryEntryToData(DllHandle,
  1338. TRUE,
  1339. IMAGE_DIRECTORY_ENTRY_RESOURCE,
  1340. &ResourceSize
  1341. );
  1342. if (!ResourceDirectory) {
  1343. return STATUS_RESOURCE_DATA_NOT_FOUND;
  1344. }
  1345. if (LDR_IS_DATAFILE(DllHandle)) {
  1346. ULONG ResourceRVA;
  1347. DllHandle = LDR_DATAFILE_TO_VIEW(DllHandle);
  1348. NtHeaders = RtlImageNtHeader( DllHandle );
  1349. if (NtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
  1350. ResourceRVA=((PIMAGE_NT_HEADERS32)NtHeaders)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_RESOURCE ].VirtualAddress;
  1351. } else if (NtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
  1352. ResourceRVA=((PIMAGE_NT_HEADERS64)NtHeaders)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_RESOURCE ].VirtualAddress;
  1353. } else {
  1354. ResourceRVA = 0;
  1355. }
  1356. if (!ResourceRVA) {
  1357. return STATUS_RESOURCE_DATA_NOT_FOUND;
  1358. }
  1359. VirtualAddressOffset = (ULONG_PTR)DllHandle + ResourceRVA - (ULONG_PTR)ResourceDirectory;
  1360. //
  1361. // Now, we must check to see if the resource is not in the
  1362. // same section as the resource table. If it's in .rsrc1,
  1363. // we've got to adjust the RVA in the ResourceDataEntry
  1364. // to point to the correct place in the non-VA data file.
  1365. //
  1366. NtSection = RtlSectionTableFromVirtualAddress( NtHeaders, DllHandle, ResourceRVA);
  1367. if (!NtSection) {
  1368. return STATUS_RESOURCE_DATA_NOT_FOUND;
  1369. }
  1370. if ( ResourceDataEntry->OffsetToData > NtSection->Misc.VirtualSize ) {
  1371. ULONG rva;
  1372. rva = NtSection->VirtualAddress;
  1373. NtSection = RtlSectionTableFromVirtualAddress(NtHeaders,
  1374. DllHandle,
  1375. ResourceDataEntry->OffsetToData
  1376. );
  1377. if (!NtSection) {
  1378. return STATUS_RESOURCE_DATA_NOT_FOUND;
  1379. }
  1380. VirtualAddressOffset +=
  1381. ((ULONG_PTR)NtSection->VirtualAddress - rva) -
  1382. ((ULONG_PTR)RtlAddressInSectionTable ( NtHeaders, DllHandle, NtSection->VirtualAddress ) - (ULONG_PTR)ResourceDirectory);
  1383. }
  1384. } else {
  1385. VirtualAddressOffset = 0;
  1386. }
  1387. if (ARGUMENT_PRESENT( Address )) {
  1388. *Address = (PVOID)( (PCHAR)DllHandle +
  1389. (ResourceDataEntry->OffsetToData - VirtualAddressOffset)
  1390. );
  1391. }
  1392. if (ARGUMENT_PRESENT( Size )) {
  1393. *Size = ResourceDataEntry->Size;
  1394. }
  1395. } except (EXCEPTION_EXECUTE_HANDLER) {
  1396. Status = GetExceptionCode();
  1397. }
  1398. #if DBG
  1399. if (!NT_SUCCESS(Status)) {
  1400. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_LEVEL_STATUS(Status), "LDR: %s() exiting 0x%08lx\n", __FUNCTION__, Status));
  1401. }
  1402. #endif
  1403. return Status;
  1404. }
  1405. NTSTATUS
  1406. LdrpAccessResourceData(
  1407. IN PVOID DllHandle,
  1408. IN const IMAGE_RESOURCE_DATA_ENTRY* ResourceDataEntry,
  1409. OUT PVOID *Address OPTIONAL,
  1410. OUT PULONG Size OPTIONAL
  1411. )
  1412. /*++
  1413. Routine Description:
  1414. This function returns the data necessary to actually examine the
  1415. contents of a particular resource.
  1416. Arguments:
  1417. DllHandle - Supplies a handle to the image file that the resource is
  1418. contained in.
  1419. ResourceDataEntry - Supplies a pointer to the resource data entry in
  1420. the resource data directory of the image file specified by the
  1421. DllHandle parameter. This pointer should have been one returned
  1422. by the LdrFindResource function.
  1423. Address - Optional pointer to a variable that will receive the
  1424. address of the resource specified by the first two parameters.
  1425. Size - Optional pointer to a variable that will receive the size of
  1426. the resource specified by the first two parameters.
  1427. Return Value:
  1428. TBD
  1429. --*/
  1430. {
  1431. PIMAGE_RESOURCE_DIRECTORY ResourceDirectory;
  1432. ULONG ResourceSize;
  1433. PIMAGE_NT_HEADERS NtHeaders;
  1434. NTSTATUS Status = STATUS_SUCCESS;
  1435. RTL_PAGED_CODE();
  1436. #ifdef NTOS_USERMODE_RUNTIME
  1437. ResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
  1438. RtlImageDirectoryEntryToData(DllHandle,
  1439. TRUE,
  1440. IMAGE_DIRECTORY_ENTRY_RESOURCE,
  1441. &ResourceSize
  1442. );
  1443. if (!ResourceDirectory) {
  1444. Status = STATUS_RESOURCE_DATA_NOT_FOUND;
  1445. goto Exit;
  1446. }
  1447. if ((ULONG_PTR)ResourceDataEntry < (ULONG_PTR) ResourceDirectory ){
  1448. DllHandle = LdrLoadAlternateResourceModule (DllHandle, NULL);
  1449. #ifdef MUI_MAGIC
  1450. if (!DllHandle){
  1451. if (!InstallLangId){
  1452. Status = NtQueryInstallUILanguage (&InstallLangId);
  1453. if (!NT_SUCCESS( Status )) {
  1454. goto Exit;
  1455. }
  1456. }
  1457. if (InstallLangId != UILangId) {
  1458. DllHandle = LdrpLoadAlternateResourceModule (InstallLangId, DllHandle, NULL);
  1459. }
  1460. }
  1461. #endif
  1462. } else{
  1463. NtHeaders = RtlImageNtHeader(LDR_DATAFILE_TO_VIEW(DllHandle));
  1464. if (NtHeaders) {
  1465. // Find the bounds of the image so we can see if this resource entry is in an alternate
  1466. // resource dll.
  1467. ULONG_PTR ImageStart = (ULONG_PTR)LDR_DATAFILE_TO_VIEW(DllHandle);
  1468. SIZE_T ImageSize = 0;
  1469. if (LDR_IS_DATAFILE(DllHandle)) {
  1470. // mapped as datafile. Ask mm for the size
  1471. NTSTATUS xStatus;
  1472. MEMORY_BASIC_INFORMATION MemInfo;
  1473. xStatus = NtQueryVirtualMemory(
  1474. NtCurrentProcess(),
  1475. (PVOID) ImageStart,
  1476. MemoryBasicInformation,
  1477. &MemInfo,
  1478. sizeof(MemInfo),
  1479. NULL
  1480. );
  1481. if ( !NT_SUCCESS(xStatus) ) {
  1482. ImageSize = 0;
  1483. } else {
  1484. ImageSize = MemInfo.RegionSize;
  1485. }
  1486. } else {
  1487. ImageSize = ((PIMAGE_NT_HEADERS32)NtHeaders)->OptionalHeader.SizeOfImage;
  1488. }
  1489. if (!(((ULONG_PTR)ResourceDataEntry >= ImageStart) && ((ULONG_PTR)ResourceDataEntry < (ImageStart + ImageSize)))) {
  1490. // Doesn't fall within the specified image. Must be an alternate dll.
  1491. DllHandle = LdrLoadAlternateResourceModule (DllHandle, NULL);
  1492. #ifdef MUI_MAGIC
  1493. if (!DllHandle) {
  1494. if (!InstallLangId){
  1495. Status = NtQueryInstallUILanguage (&InstallLangId);
  1496. if (!NT_SUCCESS( Status )) {
  1497. goto Exit;
  1498. }
  1499. }
  1500. if (InstallLangId != UILangId) {
  1501. DllHandle = LdrpLoadAlternateResourceModule (InstallLangId, DllHandle, NULL);
  1502. }
  1503. }
  1504. #endif
  1505. }
  1506. }
  1507. }
  1508. if (!DllHandle){
  1509. Status = STATUS_RESOURCE_DATA_NOT_FOUND;
  1510. goto Exit;
  1511. }
  1512. #endif
  1513. Status =
  1514. LdrpAccessResourceDataNoMultipleLanguage(
  1515. DllHandle,
  1516. ResourceDataEntry,
  1517. Address,
  1518. Size
  1519. );
  1520. #ifdef NTOS_USERMODE_RUNTIME
  1521. Exit:
  1522. #endif
  1523. if (!NT_SUCCESS(Status)) {
  1524. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_LEVEL_STATUS(Status), "LDR: %s() exiting 0x%08lx\n", __FUNCTION__, Status));
  1525. }
  1526. return Status;
  1527. }
  1528. NTSTATUS
  1529. LdrFindEntryForAddress(
  1530. IN PVOID Address,
  1531. OUT PLDR_DATA_TABLE_ENTRY *TableEntry
  1532. )
  1533. /*++
  1534. Routine Description:
  1535. This function returns the load data table entry that describes the virtual
  1536. address range that contains the passed virtual address.
  1537. Arguments:
  1538. Address - Supplies a 32-bit virtual address.
  1539. TableEntry - Supplies a pointer to the variable that will receive the
  1540. address of the loader data table entry.
  1541. Return Value:
  1542. Status
  1543. --*/
  1544. {
  1545. PPEB_LDR_DATA Ldr;
  1546. PLIST_ENTRY Head, Next;
  1547. PLDR_DATA_TABLE_ENTRY Entry;
  1548. PIMAGE_NT_HEADERS NtHeaders;
  1549. PVOID ImageBase;
  1550. PVOID EndOfImage;
  1551. NTSTATUS Status;
  1552. Ldr = NtCurrentPeb()->Ldr;
  1553. if (Ldr == NULL) {
  1554. Status = STATUS_NO_MORE_ENTRIES;
  1555. goto Exit;
  1556. }
  1557. Entry = (PLDR_DATA_TABLE_ENTRY) Ldr->EntryInProgress;
  1558. if (Entry != NULL) {
  1559. NtHeaders = RtlImageNtHeader( Entry->DllBase );
  1560. if (NtHeaders != NULL) {
  1561. ImageBase = (PVOID)Entry->DllBase;
  1562. EndOfImage = (PVOID)
  1563. ((ULONG_PTR)ImageBase + NtHeaders->OptionalHeader.SizeOfImage);
  1564. if ((ULONG_PTR)Address >= (ULONG_PTR)ImageBase && (ULONG_PTR)Address < (ULONG_PTR)EndOfImage) {
  1565. *TableEntry = Entry;
  1566. Status = STATUS_SUCCESS;
  1567. goto Exit;
  1568. }
  1569. }
  1570. }
  1571. Head = &Ldr->InMemoryOrderModuleList;
  1572. Next = Head->Flink;
  1573. while ( Next != Head ) {
  1574. Entry = CONTAINING_RECORD( Next, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks );
  1575. NtHeaders = RtlImageNtHeader( Entry->DllBase );
  1576. if (NtHeaders != NULL) {
  1577. ImageBase = (PVOID)Entry->DllBase;
  1578. EndOfImage = (PVOID)
  1579. ((ULONG_PTR)ImageBase + NtHeaders->OptionalHeader.SizeOfImage);
  1580. if ((ULONG_PTR)Address >= (ULONG_PTR)ImageBase && (ULONG_PTR)Address < (ULONG_PTR)EndOfImage) {
  1581. *TableEntry = Entry;
  1582. Status = STATUS_SUCCESS;
  1583. goto Exit;
  1584. }
  1585. }
  1586. Next = Next->Flink;
  1587. }
  1588. Status = STATUS_NO_MORE_ENTRIES;
  1589. Exit:
  1590. if (!NT_SUCCESS(Status)) {
  1591. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_LEVEL_STATUS(Status), "LDR: %s() exiting 0x%08lx\n", __FUNCTION__, Status));
  1592. }
  1593. return( Status );
  1594. }
  1595. NTSTATUS
  1596. LdrFindResource_U(
  1597. IN PVOID DllHandle,
  1598. IN const ULONG_PTR* ResourceIdPath,
  1599. IN ULONG ResourceIdPathLength,
  1600. OUT PIMAGE_RESOURCE_DATA_ENTRY *ResourceDataEntry
  1601. )
  1602. /*++
  1603. Routine Description:
  1604. This function locates the address of the specified resource in the
  1605. specified DLL and returns its address.
  1606. Arguments:
  1607. DllHandle - Supplies a handle to the image file that the resource is
  1608. contained in.
  1609. ResourceIdPath - Supplies a pointer to an array of 32-bit resource
  1610. identifiers. Each identifier is either an integer or a pointer
  1611. to a STRING structure that specifies a resource name. The array
  1612. is used to traverse the directory structure contained in the
  1613. resource section in the image file specified by the DllHandle
  1614. parameter.
  1615. ResourceIdPathLength - Supplies the number of elements in the
  1616. ResourceIdPath array.
  1617. ResourceDataEntry - Supplies a pointer to a variable that will
  1618. receive the address of the resource data entry in the resource
  1619. data section of the image file specified by the DllHandle
  1620. parameter.
  1621. Return Value:
  1622. TBD
  1623. --*/
  1624. {
  1625. RTL_PAGED_CODE();
  1626. return LdrpSearchResourceSection_U(
  1627. DllHandle,
  1628. ResourceIdPath,
  1629. ResourceIdPathLength,
  1630. 0, // Look for a leaf node, ineaxt lang match
  1631. (PVOID *)ResourceDataEntry
  1632. );
  1633. }
  1634. NTSTATUS
  1635. LdrFindResourceEx_U(
  1636. IN ULONG Flags,
  1637. IN PVOID DllHandle,
  1638. IN const ULONG_PTR* ResourceIdPath,
  1639. IN ULONG ResourceIdPathLength,
  1640. OUT PIMAGE_RESOURCE_DATA_ENTRY *ResourceDataEntry
  1641. )
  1642. /*++
  1643. Routine Description:
  1644. This function locates the address of the specified resource in the
  1645. specified DLL and returns its address.
  1646. Arguments:
  1647. Flags -
  1648. LDRP_FIND_RESOURCE_DIRECTORY
  1649. searching for a resource directory, otherwise the caller is
  1650. searching for a resource data entry.
  1651. LDR_FIND_RESOURCE_LANGUAGE_EXACT
  1652. searching for a resource with, and only with, the language id
  1653. specified in ResourceIdPath, otherwise the caller wants the routine
  1654. to come up with default when specified langid is not found.
  1655. LDR_FIND_RESOURCE_LANGUAGE_REDIRECT_VERSION
  1656. searching for a resource version in both main and alternative
  1657. module paths
  1658. DllHandle - Supplies a handle to the image file that the resource is
  1659. contained in.
  1660. ResourceIdPath - Supplies a pointer to an array of 32-bit resource
  1661. identifiers. Each identifier is either an integer or a pointer
  1662. to a STRING structure that specifies a resource name. The array
  1663. is used to traverse the directory structure contained in the
  1664. resource section in the image file specified by the DllHandle
  1665. parameter.
  1666. ResourceIdPathLength - Supplies the number of elements in the
  1667. ResourceIdPath array.
  1668. ResourceDataEntry - Supplies a pointer to a variable that will
  1669. receive the address of the resource data entry in the resource
  1670. data section of the image file specified by the DllHandle
  1671. parameter.
  1672. Return Value:
  1673. TBD
  1674. --*/
  1675. {
  1676. RTL_PAGED_CODE();
  1677. return LdrpSearchResourceSection_U(
  1678. DllHandle,
  1679. ResourceIdPath,
  1680. ResourceIdPathLength,
  1681. Flags,
  1682. (PVOID *)ResourceDataEntry
  1683. );
  1684. }
  1685. NTSTATUS
  1686. LdrFindResourceDirectory_U(
  1687. IN PVOID DllHandle,
  1688. IN const ULONG_PTR* ResourceIdPath,
  1689. IN ULONG ResourceIdPathLength,
  1690. OUT PIMAGE_RESOURCE_DIRECTORY *ResourceDirectory
  1691. )
  1692. /*++
  1693. Routine Description:
  1694. This function locates the address of the specified resource directory in
  1695. specified DLL and returns its address.
  1696. Arguments:
  1697. DllHandle - Supplies a handle to the image file that the resource
  1698. directory is contained in.
  1699. ResourceIdPath - Supplies a pointer to an array of 32-bit resource
  1700. identifiers. Each identifier is either an integer or a pointer
  1701. to a STRING structure that specifies a resource name. The array
  1702. is used to traverse the directory structure contained in the
  1703. resource section in the image file specified by the DllHandle
  1704. parameter.
  1705. ResourceIdPathLength - Supplies the number of elements in the
  1706. ResourceIdPath array.
  1707. ResourceDirectory - Supplies a pointer to a variable that will
  1708. receive the address of the resource directory specified by
  1709. ResourceIdPath in the resource data section of the image file
  1710. the DllHandle parameter.
  1711. Return Value:
  1712. TBD
  1713. --*/
  1714. {
  1715. RTL_PAGED_CODE();
  1716. return LdrpSearchResourceSection_U(
  1717. DllHandle,
  1718. ResourceIdPath,
  1719. ResourceIdPathLength,
  1720. LDRP_FIND_RESOURCE_DIRECTORY, // Look for a directory node
  1721. (PVOID *)ResourceDirectory
  1722. );
  1723. }
  1724. LONG
  1725. LdrpCompareResourceNames_U(
  1726. IN ULONG_PTR ResourceName,
  1727. IN const IMAGE_RESOURCE_DIRECTORY* ResourceDirectory,
  1728. IN const IMAGE_RESOURCE_DIRECTORY_ENTRY* ResourceDirectoryEntry
  1729. )
  1730. {
  1731. LONG li;
  1732. PIMAGE_RESOURCE_DIR_STRING_U ResourceNameString;
  1733. if (ResourceName & LDR_RESOURCE_ID_NAME_MASK) {
  1734. if (!ResourceDirectoryEntry->NameIsString) {
  1735. return( -1 );
  1736. }
  1737. ResourceNameString = (PIMAGE_RESOURCE_DIR_STRING_U)
  1738. ((PCHAR)ResourceDirectory + ResourceDirectoryEntry->NameOffset);
  1739. li = wcsncmp( (LPWSTR)ResourceName,
  1740. ResourceNameString->NameString,
  1741. ResourceNameString->Length
  1742. );
  1743. if (!li && wcslen((PWSTR)ResourceName) != ResourceNameString->Length) {
  1744. return( 1 );
  1745. }
  1746. return(li);
  1747. }
  1748. else {
  1749. if (ResourceDirectoryEntry->NameIsString) {
  1750. return( 1 );
  1751. }
  1752. return( (ULONG)(ResourceName - ResourceDirectoryEntry->Name) );
  1753. }
  1754. }
  1755. // Language ids are 16bits so any value with any bits
  1756. // set above 16 should be ok, and this value only has
  1757. // to fit in a ULONG_PTR. 0x10000 should be sufficient.
  1758. // The value used is actually 0xFFFF regardless of 32bit or 64bit,
  1759. // I guess assuming this is not an actual langid, which it isn't,
  1760. // due to the relatively small number of languages, around 70.
  1761. #define USE_FIRSTAVAILABLE_LANGID (0xFFFFFFFF & ~LDR_RESOURCE_ID_NAME_MASK)
  1762. NTSTATUS
  1763. LdrpSearchResourceSection_U(
  1764. IN PVOID DllHandle,
  1765. IN const ULONG_PTR* ResourceIdPath,
  1766. IN ULONG ResourceIdPathLength,
  1767. IN ULONG Flags,
  1768. OUT PVOID *ResourceDirectoryOrData
  1769. )
  1770. /*++
  1771. Routine Description:
  1772. This function locates the address of the specified resource in the
  1773. specified DLL and returns its address.
  1774. Arguments:
  1775. DllHandle - Supplies a handle to the image file that the resource is
  1776. contained in.
  1777. ResourceIdPath - Supplies a pointer to an array of 32-bit resource
  1778. identifiers. Each identifier is either an integer or a pointer
  1779. to a null terminated string (PSZ) that specifies a resource
  1780. name. The array is used to traverse the directory structure
  1781. contained in the resource section in the image file specified by
  1782. the DllHandle parameter.
  1783. ResourceIdPathLength - Supplies the number of elements in the
  1784. ResourceIdPath array.
  1785. Flags -
  1786. LDRP_FIND_RESOURCE_DIRECTORY
  1787. searching for a resource directory, otherwise the caller is
  1788. searching for a resource data entry.
  1789. LDR_FIND_RESOURCE_LANGUAGE_EXACT
  1790. searching for a resource with, and only with, the language id
  1791. specified in ResourceIdPath, otherwise the caller wants the routine
  1792. to come up with default when specified langid is not found.
  1793. LDR_FIND_RESOURCE_LANGUAGE_REDIRECT_VERSION
  1794. searching for a resource version in main and alternative
  1795. modules paths
  1796. FindDirectoryEntry - Supplies a boolean that is TRUE if caller is
  1797. searching for a resource directory, otherwise the caller is
  1798. searching for a resource data entry.
  1799. ExactLangMatchOnly - Supplies a boolean that is TRUE if caller is
  1800. searching for a resource with, and only with, the language id
  1801. specified in ResourceIdPath, otherwise the caller wants the routine
  1802. to come up with default when specified langid is not found.
  1803. ResourceDirectoryOrData - Supplies a pointer to a variable that will
  1804. receive the address of the resource directory or data entry in
  1805. the resource data section of the image file specified by the
  1806. DllHandle parameter.
  1807. Return Value:
  1808. TBD
  1809. --*/
  1810. {
  1811. NTSTATUS Status;
  1812. PIMAGE_RESOURCE_DIRECTORY LanguageResourceDirectory, ResourceDirectory, TopResourceDirectory;
  1813. PIMAGE_RESOURCE_DIRECTORY_ENTRY ResourceDirEntLow;
  1814. PIMAGE_RESOURCE_DIRECTORY_ENTRY ResourceDirEntMiddle;
  1815. PIMAGE_RESOURCE_DIRECTORY_ENTRY ResourceDirEntHigh;
  1816. PIMAGE_RESOURCE_DATA_ENTRY ResourceEntry;
  1817. USHORT n, half;
  1818. LONG dir;
  1819. ULONG size;
  1820. ULONG_PTR ResourceIdRetry;
  1821. ULONG RetryCount;
  1822. LANGID NewLangId;
  1823. const ULONG_PTR* IdPath = ResourceIdPath;
  1824. ULONG IdPathLength = ResourceIdPathLength;
  1825. BOOLEAN fIsNeutral = FALSE;
  1826. LANGID GivenLanguage;
  1827. #ifdef NTOS_USERMODE_RUNTIME
  1828. LCID DefaultThreadLocale, DefaultSystemLocale;
  1829. PVOID AltResourceDllHandle = NULL;
  1830. ULONG_PTR UIResourceIdPath[3];
  1831. #endif
  1832. RTL_PAGED_CODE();
  1833. try {
  1834. TopResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
  1835. RtlImageDirectoryEntryToData(DllHandle,
  1836. TRUE,
  1837. IMAGE_DIRECTORY_ENTRY_RESOURCE,
  1838. &size
  1839. );
  1840. if (!TopResourceDirectory) {
  1841. return( STATUS_RESOURCE_DATA_NOT_FOUND );
  1842. }
  1843. ResourceDirectory = TopResourceDirectory;
  1844. ResourceIdRetry = USE_FIRSTAVAILABLE_LANGID;
  1845. RetryCount = 0;
  1846. ResourceEntry = NULL;
  1847. LanguageResourceDirectory = NULL;
  1848. while (ResourceDirectory != NULL && ResourceIdPathLength--) {
  1849. //
  1850. // If search path includes a language id, then attempt to
  1851. // match the following language ids in this order:
  1852. //
  1853. // (0) use given language id
  1854. // (1) use primary language of given language id
  1855. // (2) use id 0 (neutral resource)
  1856. // (4) use user UI language
  1857. //
  1858. // If the PRIMARY language id is ZERO, then ALSO attempt to
  1859. // match the following language ids in this order:
  1860. //
  1861. // (3) use thread language id for console app
  1862. // (4) use user UI language
  1863. // (5) use lang id of TEB for windows app if it is different from user locale
  1864. // (6) use UI lang from exe resource
  1865. // (7) use primary UI lang from exe resource
  1866. // (8) use Install Language
  1867. // (9) use lang id from user's locale id
  1868. // (10) use primary language of user's locale id
  1869. // (11) use lang id from system default locale id
  1870. // (12) use lang id of system default locale id
  1871. // (13) use primary language of system default locale id
  1872. // (14) use US English lang id
  1873. // (15) use any lang id that matches requested info
  1874. //
  1875. if (ResourceIdPathLength == 0 && IdPathLength == 3) {
  1876. LanguageResourceDirectory = ResourceDirectory;
  1877. }
  1878. if (LanguageResourceDirectory != NULL) {
  1879. GivenLanguage = (LANGID)IdPath[ 2 ];
  1880. fIsNeutral = (PRIMARYLANGID( GivenLanguage ) == LANG_NEUTRAL);
  1881. TryNextLangId:
  1882. switch( RetryCount++ ) {
  1883. #if defined(NTOS_KERNEL_RUNTIME)
  1884. case 0: // Use given language id
  1885. NewLangId = GivenLanguage;
  1886. break;
  1887. case 1: // Use primary language of given language id
  1888. NewLangId = PRIMARYLANGID( GivenLanguage );
  1889. break;
  1890. case 2: // Use id 0 (neutral resource)
  1891. NewLangId = 0;
  1892. break;
  1893. case 3: // Use user's default UI language
  1894. NewLangId = (LANGID)ResourceIdRetry;
  1895. break;
  1896. case 4: // Use native UI language
  1897. if ( !fIsNeutral ) {
  1898. // Stop looking - Not in the neutral case
  1899. goto ReturnFailure;
  1900. break;
  1901. }
  1902. NewLangId = PsInstallUILanguageId;
  1903. break;
  1904. case 5: // Use default system locale
  1905. NewLangId = LANGIDFROMLCID(PsDefaultSystemLocaleId);
  1906. break;
  1907. case 6:
  1908. // Use US English language
  1909. NewLangId = MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US );
  1910. break;
  1911. case 7: // Take any lang id that matches
  1912. NewLangId = USE_FIRSTAVAILABLE_LANGID;
  1913. break;
  1914. #elif defined(NTOS_USERMODE_RUNTIME)
  1915. case 0: // Use given language id
  1916. NewLangId = GivenLanguage;
  1917. break;
  1918. case 1: // Use primary language of given language id
  1919. if ( Flags & LDR_FIND_RESOURCE_LANGUAGE_EXACT) {
  1920. //
  1921. // Did not find an exact language match.
  1922. // Stop looking.
  1923. //
  1924. goto ReturnFailure;
  1925. }
  1926. NewLangId = PRIMARYLANGID( GivenLanguage );
  1927. break;
  1928. case 2: // Use id 0 (neutral resource)
  1929. NewLangId = 0;
  1930. break;
  1931. case 3: // Use thread langid if caller is a console app
  1932. if ( !fIsNeutral ) {
  1933. // Stop looking - Not in the neutral case
  1934. NewLangId = (LANGID)ResourceIdRetry;
  1935. break;
  1936. }
  1937. if (NtCurrentPeb()->ProcessParameters->ConsoleHandle)
  1938. {
  1939. NewLangId = LANGIDFROMLCID(NtCurrentTeb()->CurrentLocale);
  1940. }
  1941. else
  1942. {
  1943. NewLangId = (LANGID)ResourceIdRetry;
  1944. }
  1945. break;
  1946. case 4: // Use user's default UI language
  1947. GET_UI_LANGID();
  1948. if (!UILangId){
  1949. NewLangId = (LANGID)ResourceIdRetry;
  1950. break;
  1951. }
  1952. // Don't load alternative modules for console process if UI language doesn't match system locale
  1953. if (NtCurrentPeb()->ProcessParameters->ConsoleHandle &&
  1954. LANGIDFROMLCID(NtCurrentTeb()->CurrentLocale) != UILangId)
  1955. {
  1956. NewLangId = (LANGID)ResourceIdRetry;
  1957. break;
  1958. }
  1959. NewLangId = UILangId;
  1960. //
  1961. // Arabic/Hebrew MUI files may contain resources with LANG ID different than 401/40d.
  1962. // e.g. Comdlg32.dll has two sets of Arabic/Hebrew resources one mirrored (401/40d)
  1963. // and one flipped (801/80d).
  1964. //
  1965. if( !fIsNeutral &&
  1966. ((PRIMARYLANGID (GivenLanguage) == LANG_ARABIC) || (PRIMARYLANGID (GivenLanguage) == LANG_HEBREW)) &&
  1967. (PRIMARYLANGID (GivenLanguage) == PRIMARYLANGID (NewLangId))) {
  1968. NewLangId = GivenLanguage;
  1969. }
  1970. //
  1971. // Bug #246044 WeiWu 12/07/00
  1972. // BiDi modules use version block FileDescription field to store LRM markers,
  1973. // LDR_FIND_RESOURCE_LANGUAGE_REDIRECT_VERSION will allow lpk.dll to get version resource from MUI alternative modules.
  1974. //
  1975. if ( ( (IdPath[0] != RT_VERSION) && (IdPath[0] != RT_MANIFEST) ) ||
  1976. (Flags & LDR_FIND_RESOURCE_LANGUAGE_REDIRECT_VERSION)) {
  1977. //
  1978. // Load alternate resource dll when:
  1979. // 1. language is neutral
  1980. // or
  1981. // Given language is not tried.
  1982. // and
  1983. // 2. the resource to load is not a version info.
  1984. //
  1985. AltResourceDllHandle=LdrLoadAlternateResourceModule(
  1986. DllHandle,
  1987. NULL);
  1988. if (!AltResourceDllHandle){
  1989. //
  1990. // Alternate resource dll not available.
  1991. // Skip this lookup.
  1992. //
  1993. NewLangId = (LANGID)ResourceIdRetry;
  1994. break;
  1995. }
  1996. //
  1997. // Map to alternate resource dll and search
  1998. // it instead.
  1999. //
  2000. UIResourceIdPath[0]=IdPath[0];
  2001. UIResourceIdPath[1]=IdPath[1];
  2002. UIResourceIdPath[2]=NewLangId;
  2003. Status = LdrpSearchResourceSection_U(
  2004. AltResourceDllHandle,
  2005. UIResourceIdPath,
  2006. 3,
  2007. Flags | LDR_FIND_RESOURCE_LANGUAGE_EXACT,
  2008. (PVOID *)ResourceDirectoryOrData
  2009. );
  2010. if (NT_SUCCESS(Status)){
  2011. //
  2012. // We sucessfully found alternate resource,
  2013. // return it.
  2014. //
  2015. return Status;
  2016. }
  2017. }
  2018. //
  2019. // Caller does not want alternate resource, or
  2020. // alternate resource not found.
  2021. //
  2022. NewLangId = (LANGID)ResourceIdRetry;
  2023. break;
  2024. case 5: // Use langid of the thread locale if caller is a Windows app and thread locale is different from user locale
  2025. if ( !fIsNeutral ) {
  2026. // Stop looking - Not in the neutral case
  2027. goto ReturnFailure;
  2028. break;
  2029. }
  2030. if (!NtCurrentPeb()->ProcessParameters->ConsoleHandle && NtCurrentTeb()){
  2031. Status = NtQueryDefaultLocale(
  2032. TRUE,
  2033. &DefaultThreadLocale
  2034. );
  2035. if (NT_SUCCESS( Status ) &&
  2036. DefaultThreadLocale !=
  2037. NtCurrentTeb()->CurrentLocale) {
  2038. //
  2039. // Thread locale is different from
  2040. // default locale.
  2041. //
  2042. NewLangId = LANGIDFROMLCID(NtCurrentTeb()->CurrentLocale);
  2043. break;
  2044. }
  2045. }
  2046. NewLangId = (LANGID)ResourceIdRetry;
  2047. break;
  2048. case 6: // UI language from the executable resource
  2049. if (!UILangId){
  2050. NewLangId = (LANGID)ResourceIdRetry;
  2051. } else {
  2052. NewLangId = UILangId;
  2053. }
  2054. break;
  2055. case 7: // Parimary lang of UI language from the executable resource
  2056. if (!UILangId){
  2057. NewLangId = (LANGID)ResourceIdRetry;
  2058. } else {
  2059. NewLangId = PRIMARYLANGID( (LANGID) UILangId );
  2060. }
  2061. break;
  2062. case 8: // Use install -native- language
  2063. //
  2064. // Thread locale is the same as the user locale, then let's
  2065. // try loading the native (install) ui language resources.
  2066. //
  2067. if (!InstallLangId){
  2068. Status = NtQueryInstallUILanguage(&InstallLangId);
  2069. if (!NT_SUCCESS( Status )) {
  2070. //
  2071. // Failed reading key. Skip this lookup.
  2072. //
  2073. NewLangId = (LANGID)ResourceIdRetry;
  2074. break;
  2075. }
  2076. }
  2077. NewLangId = InstallLangId;
  2078. break;
  2079. case 9: // Use lang id from locale in TEB
  2080. if (SUBLANGID( GivenLanguage ) == SUBLANG_SYS_DEFAULT) {
  2081. // Skip over all USER locale options
  2082. DefaultThreadLocale = 0;
  2083. RetryCount += 2;
  2084. break;
  2085. }
  2086. if (NtCurrentTeb() != NULL) {
  2087. NewLangId = LANGIDFROMLCID(NtCurrentTeb()->CurrentLocale);
  2088. }
  2089. break;
  2090. case 10: // Use User's default locale
  2091. Status = NtQueryDefaultLocale( TRUE, &DefaultThreadLocale );
  2092. if (NT_SUCCESS( Status )) {
  2093. NewLangId = LANGIDFROMLCID(DefaultThreadLocale);
  2094. break;
  2095. }
  2096. RetryCount++;
  2097. break;
  2098. case 11: // Use primary language of User's default locale
  2099. NewLangId = PRIMARYLANGID( (LANGID)ResourceIdRetry );
  2100. break;
  2101. case 12: // Use System default locale
  2102. Status = NtQueryDefaultLocale( FALSE, &DefaultSystemLocale );
  2103. if (!NT_SUCCESS( Status )) {
  2104. RetryCount++;
  2105. break;
  2106. }
  2107. if (DefaultSystemLocale != DefaultThreadLocale) {
  2108. NewLangId = LANGIDFROMLCID(DefaultSystemLocale);
  2109. break;
  2110. }
  2111. RetryCount += 2;
  2112. // fall through
  2113. case 14: // Use US English language
  2114. NewLangId = MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US );
  2115. break;
  2116. case 13: // Use primary language of System default locale
  2117. NewLangId = PRIMARYLANGID( (LANGID)ResourceIdRetry );
  2118. break;
  2119. case 15: // Take any lang id that matches
  2120. NewLangId = USE_FIRSTAVAILABLE_LANGID;
  2121. break;
  2122. #else
  2123. #error "Unknown environment."
  2124. #endif
  2125. default: // No lang ids to match
  2126. goto ReturnFailure;
  2127. break;
  2128. }
  2129. //
  2130. // If looking for a specific language id and same as the
  2131. // one we just looked up, then skip it.
  2132. //
  2133. if (NewLangId != USE_FIRSTAVAILABLE_LANGID &&
  2134. NewLangId == ResourceIdRetry
  2135. ) {
  2136. goto TryNextLangId;
  2137. }
  2138. //
  2139. // Try this new language Id
  2140. //
  2141. ResourceIdRetry = (ULONG_PTR)NewLangId;
  2142. ResourceIdPath = &ResourceIdRetry;
  2143. ResourceDirectory = LanguageResourceDirectory;
  2144. }
  2145. n = ResourceDirectory->NumberOfNamedEntries;
  2146. ResourceDirEntLow = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(ResourceDirectory+1);
  2147. if (!(*ResourceIdPath & LDR_RESOURCE_ID_NAME_MASK)) { // No string(name),so we need ID
  2148. ResourceDirEntLow += n;
  2149. n = ResourceDirectory->NumberOfIdEntries;
  2150. }
  2151. if (!n) {
  2152. ResourceDirectory = NULL;
  2153. goto NotFound; // Resource directory contains zero types or names or langID.
  2154. }
  2155. if (LanguageResourceDirectory != NULL &&
  2156. *ResourceIdPath == USE_FIRSTAVAILABLE_LANGID
  2157. ) {
  2158. ResourceDirectory = NULL;
  2159. ResourceIdRetry = ResourceDirEntLow->Name;
  2160. ResourceEntry = (PIMAGE_RESOURCE_DATA_ENTRY)
  2161. ((PCHAR)TopResourceDirectory +
  2162. ResourceDirEntLow->OffsetToData
  2163. );
  2164. break;
  2165. }
  2166. ResourceDirectory = NULL;
  2167. ResourceDirEntHigh = ResourceDirEntLow + n - 1;
  2168. while (ResourceDirEntLow <= ResourceDirEntHigh) {
  2169. if ((half = (n >> 1)) != 0) {
  2170. ResourceDirEntMiddle = ResourceDirEntLow;
  2171. if (*(PUCHAR)&n & 1) {
  2172. ResourceDirEntMiddle += half;
  2173. }
  2174. else {
  2175. ResourceDirEntMiddle += half - 1;
  2176. }
  2177. dir = LdrpCompareResourceNames_U( *ResourceIdPath,
  2178. TopResourceDirectory,
  2179. ResourceDirEntMiddle
  2180. );
  2181. if (!dir) {
  2182. if (ResourceDirEntMiddle->DataIsDirectory) {
  2183. ResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
  2184. ((PCHAR)TopResourceDirectory +
  2185. ResourceDirEntMiddle->OffsetToDirectory
  2186. );
  2187. }
  2188. else {
  2189. ResourceDirectory = NULL;
  2190. ResourceEntry = (PIMAGE_RESOURCE_DATA_ENTRY)
  2191. ((PCHAR)TopResourceDirectory +
  2192. ResourceDirEntMiddle->OffsetToData
  2193. );
  2194. }
  2195. break;
  2196. }
  2197. else {
  2198. if (dir < 0) { // Order in the resource: Name, ID.
  2199. ResourceDirEntHigh = ResourceDirEntMiddle - 1;
  2200. if (*(PUCHAR)&n & 1) {
  2201. n = half;
  2202. }
  2203. else {
  2204. n = half - 1;
  2205. }
  2206. }
  2207. else {
  2208. ResourceDirEntLow = ResourceDirEntMiddle + 1;
  2209. n = half;
  2210. }
  2211. }
  2212. }
  2213. else {
  2214. if (n != 0) {
  2215. dir = LdrpCompareResourceNames_U( *ResourceIdPath,
  2216. TopResourceDirectory,
  2217. ResourceDirEntLow
  2218. );
  2219. if (!dir) { // find, or it fail to set ResourceDirectory so go to NotFound.
  2220. if (ResourceDirEntLow->DataIsDirectory) {
  2221. ResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
  2222. ((PCHAR)TopResourceDirectory +
  2223. ResourceDirEntLow->OffsetToDirectory
  2224. );
  2225. }
  2226. else {
  2227. ResourceEntry = (PIMAGE_RESOURCE_DATA_ENTRY)
  2228. ((PCHAR)TopResourceDirectory +
  2229. ResourceDirEntLow->OffsetToData
  2230. );
  2231. }
  2232. }
  2233. }
  2234. break;
  2235. }
  2236. }
  2237. ResourceIdPath++;
  2238. }
  2239. if (ResourceEntry != NULL && !(Flags & LDRP_FIND_RESOURCE_DIRECTORY)) {
  2240. *ResourceDirectoryOrData = (PVOID)ResourceEntry;
  2241. Status = STATUS_SUCCESS;
  2242. }
  2243. else
  2244. if (ResourceDirectory != NULL && (Flags & LDRP_FIND_RESOURCE_DIRECTORY)) {
  2245. *ResourceDirectoryOrData = (PVOID)ResourceDirectory;
  2246. Status = STATUS_SUCCESS;
  2247. }
  2248. else {
  2249. NotFound:
  2250. switch( IdPathLength - ResourceIdPathLength) {
  2251. case 3: Status = STATUS_RESOURCE_LANG_NOT_FOUND; break;
  2252. case 2: Status = STATUS_RESOURCE_NAME_NOT_FOUND; break;
  2253. case 1: Status = STATUS_RESOURCE_TYPE_NOT_FOUND; break;
  2254. default: Status = STATUS_INVALID_PARAMETER; break;
  2255. }
  2256. }
  2257. if (Status == STATUS_RESOURCE_LANG_NOT_FOUND &&
  2258. LanguageResourceDirectory != NULL
  2259. ) {
  2260. ResourceEntry = NULL;
  2261. goto TryNextLangId;
  2262. ReturnFailure: ;
  2263. Status = STATUS_RESOURCE_LANG_NOT_FOUND;
  2264. }
  2265. #ifdef NTOS_USERMODE_RUNTIME
  2266. #ifdef MUI_MAGIC
  2267. //
  2268. // Load resource from alternative modules if
  2269. // resource type doesn't exist in the main DLL.
  2270. //
  2271. else if (Status == STATUS_RESOURCE_TYPE_NOT_FOUND &&
  2272. 3 == IdPathLength ) {
  2273. GET_UI_LANGID();
  2274. if (UILangId) {
  2275. UIResourceIdPath[0]=IdPath[0];
  2276. UIResourceIdPath[1]=IdPath[1];
  2277. UIResourceIdPath[2]=UILangId;
  2278. // ASSERT (IdPath[0] != RT_VERSION);
  2279. if (IdPath[0] != RT_MANIFEST && IdPath[0] != RT_VERSION) {
  2280. Status = LdrpLoadResourceFromAlternativeModule(DllHandle,
  2281. UIResourceIdPath,
  2282. 3,
  2283. Flags,
  2284. ResourceDirectoryOrData );
  2285. } else {
  2286. #if DBG
  2287. //
  2288. // Althernative module loading will invoke version resource loading for resource checksum
  2289. // and CMF info, so explicitly loading version resource from alternative module could cause loader deadlock
  2290. // Besides, in most cases, version resource should always exist in the main binary, we don't expect it to go
  2291. // through alternative module.
  2292. //
  2293. DbgPrintEx(DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL,
  2294. "LDR: Module %p load version from alternative module, potential deadlocks!\n", DllHandle);
  2295. #endif
  2296. }
  2297. }
  2298. }
  2299. #endif
  2300. #endif
  2301. }
  2302. except (EXCEPTION_EXECUTE_HANDLER) {
  2303. Status = GetExceptionCode();
  2304. }
  2305. return Status;
  2306. }
  2307. #if defined(NTOS_USERMODE_RUNTIME)
  2308. NTSTATUS
  2309. LdrpNativeReadVirtualMemory(
  2310. IN HANDLE ProcessHandle,
  2311. IN NATIVE_ULONG_PTR BaseAddress,
  2312. OUT PVOID Buffer,
  2313. IN SIZE_T BufferSize,
  2314. OUT PSIZE_T NumberOfBytesRead OPTIONAL
  2315. )
  2316. //
  2317. // NtWow64ReadVirtualMemory64 takes both 64bit pointers and 64bit sizes,
  2318. // but only 64bit pointers make sense for us. You can't read 64sizes into
  2319. // 32bit address spaces.
  2320. //
  2321. // This function is like NtReadVirtualMemory/NtWow64ReadVirtualMemory64
  2322. // but the remote address is an integer and the sizes match the local address space.
  2323. //
  2324. {
  2325. NTSTATUS Status;
  2326. #if defined(BUILD_WOW6432)
  2327. NATIVE_SIZE_T NativeNumberOfBytesRead;
  2328. Status = NtWow64ReadVirtualMemory64(ProcessHandle, (NATIVE_PVOID)BaseAddress, Buffer, BufferSize, &NativeNumberOfBytesRead);
  2329. if (ARGUMENT_PRESENT(NumberOfBytesRead)) {
  2330. *NumberOfBytesRead = (SIZE_T)NativeNumberOfBytesRead;
  2331. }
  2332. #else
  2333. Status = NtReadVirtualMemory(ProcessHandle, (NATIVE_PVOID)BaseAddress, Buffer, BufferSize, NumberOfBytesRead);
  2334. #endif
  2335. return Status;
  2336. }
  2337. #if defined(BUILD_WOW6432)
  2338. #define LdrpNativeQueryVirtualMemory NtWow64QueryVirtualMemory64
  2339. #else
  2340. #define LdrpNativeQueryVirtualMemory NtQueryVirtualMemory
  2341. #endif
  2342. VOID
  2343. NTAPI
  2344. LdrDestroyOutOfProcessImage(
  2345. IN OUT PLDR_OUT_OF_PROCESS_IMAGE Image
  2346. )
  2347. /*++
  2348. Routine Description:
  2349. Arguments:
  2350. Image -
  2351. Return Value:
  2352. None.
  2353. --*/
  2354. {
  2355. NTSTATUS Status = STATUS_SUCCESS;
  2356. RtlFreeBuffer(&Image->HeadersBuffer);
  2357. Image->Flags = 0;
  2358. Image->ProcessHandle = NULL;
  2359. Image->DllHandle = 0;
  2360. }
  2361. NTSTATUS
  2362. NTAPI
  2363. LdrCreateOutOfProcessImage(
  2364. IN ULONG Flags,
  2365. IN HANDLE ProcessHandle,
  2366. IN ULONG64 DllHandle,
  2367. OUT PLDR_OUT_OF_PROCESS_IMAGE Image
  2368. )
  2369. /*++
  2370. Routine Description:
  2371. This function initialized the out parameter Image for use with
  2372. other functions, like LdrFindOutOfProcessResource.
  2373. It reads in the headers, in order to work with many existing inproc
  2374. RtlImage* functions, without reading them in for every operation.
  2375. When you are done with the Image, pass it to LdrDestroyOutOfProcessImage.
  2376. Arguments:
  2377. Flags - fiddle with the behavior of the function
  2378. LDR_DLL_MAPPED_AS_DATA - "flat" memory mapping of file
  2379. LDR_DLL_MAPPED_AS_IMAGE - SEC_IMAGE was passed to NtCreateSection, inter section padding
  2380. reflected in offsets stored on disk is reflected in the address
  2381. space this is the simpler situation
  2382. LDR_DLL_MAPPED_AS_UNFORMATED_IMAGE - LDR_DLL_MAPPED_AS_IMAGE but LdrpWx86FormatVirtualImage
  2383. hasn't run yet.
  2384. ProcessHandle - The process DllHandle is in a mapped section in
  2385. DllHandle - the base address of a mapped view in process ProcessHandle
  2386. for legacy reasons, the lowest bit of this implies LDR_DLL_MAPPED_AS_DATA
  2387. Image - an opaque object that you can pass to other "OutOfProcessImage" functions.
  2388. Return Value:
  2389. NTSTATUS
  2390. --*/
  2391. {
  2392. NATIVE_ULONG_PTR RemoteAddress = 0;
  2393. PRTL_BUFFER Buffer = NULL;
  2394. PIMAGE_DOS_HEADER DosHeader = NULL;
  2395. SIZE_T Headers32Offset = 0;
  2396. PIMAGE_NT_HEADERS32 Headers32 = NULL;
  2397. SIZE_T BytesRead = 0;
  2398. SIZE_T BytesToRead = 0;
  2399. SIZE_T Offset = 0;
  2400. SIZE_T InitialReadSize = 4096;
  2401. C_ASSERT(PAGE_SIZE >= 4096);
  2402. NTSTATUS Status = STATUS_SUCCESS;
  2403. KdPrintEx((
  2404. DPFLTR_LDR_ID,
  2405. DPFLTR_TRACE_LEVEL,
  2406. "LDR: %s(%lx, %p, %p, %p) beginning\n",
  2407. __FUNCTION__,
  2408. Flags,
  2409. ProcessHandle,
  2410. DllHandle,
  2411. Image
  2412. ));
  2413. // if this assertion triggers, you probably passed a handle instead of a base address
  2414. ASSERT(DllHandle >= 0xffff);
  2415. // Unformated images are only ever 32bit on 64bit.
  2416. // The memory manager doesn't "spread them out", ntdll.dll does it.
  2417. // There is only a short span of time between the mapping of the image and
  2418. // the code in ntdll.dll reformating it, we leave it to our caller to know
  2419. // if they are in that path.
  2420. #if !defined(_WIN64) && !defined(BUILD_WOW6432)
  2421. if ((Flags & LDR_DLL_MAPPED_AS_MASK) == LDR_DLL_MAPPED_AS_UNFORMATED_IMAGE) {
  2422. Flags = (Flags & ~LDR_DLL_MAPPED_AS_MASK) | LDR_DLL_MAPPED_AS_IMAGE;
  2423. }
  2424. #endif
  2425. if (LDR_IS_DATAFILE_INTEGER(DllHandle)) {
  2426. DllHandle = LDR_DATAFILE_TO_VIEW_INTEGER(DllHandle);
  2427. ASSERT((Flags & LDR_DLL_MAPPED_AS_MASK) == 0 || (Flags & LDR_DLL_MAPPED_AS_MASK) == LDR_DLL_MAPPED_AS_DATA);
  2428. Flags |= LDR_DLL_MAPPED_AS_DATA;
  2429. }
  2430. Image->Flags = Flags;
  2431. Image->ProcessHandle = ProcessHandle;
  2432. Image->DllHandle = DllHandle;
  2433. RemoteAddress = (NATIVE_ULONG_PTR)DllHandle;
  2434. Buffer = &Image->HeadersBuffer;
  2435. RtlInitBuffer(Buffer, NULL, 0);
  2436. if (ProcessHandle == NtCurrentProcess()) {
  2437. Status = STATUS_SUCCESS;
  2438. goto Exit;
  2439. }
  2440. //
  2441. // first read 4k since that is generally enough and we can avoid
  2442. // multiple calls to NtReadVirtualMemory
  2443. //
  2444. // 4k is also the smallest page NT has run on, so even if the .exe is
  2445. // smaller than 4k, we should be able to read 4k
  2446. //
  2447. // The memory manager only allows two pages of headers, so it may be
  2448. // faster to always read 2 * native_page_size or always 16k instead
  2449. // the initial 4k read.
  2450. //
  2451. BytesToRead = InitialReadSize;
  2452. if (!NT_SUCCESS(Status = RtlEnsureBufferSize(0, Buffer, Offset + BytesToRead))) {
  2453. goto Exit;
  2454. }
  2455. Status = LdrpNativeReadVirtualMemory(ProcessHandle, RemoteAddress + Offset, Buffer->Buffer + Offset, BytesToRead, &BytesRead);
  2456. if (Status == STATUS_PARTIAL_COPY && BytesRead != 0) {
  2457. InitialReadSize = BytesRead;
  2458. }
  2459. else if (!NT_SUCCESS(Status)) {
  2460. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL, "LDR: %s(): NtReadVirtualMemory failed.\n", __FUNCTION__));
  2461. goto Exit;
  2462. }
  2463. BytesToRead = sizeof(*DosHeader);
  2464. if (Offset + BytesToRead > InitialReadSize) {
  2465. if (!NT_SUCCESS(Status = RtlEnsureBufferSize(0, Buffer, Offset + BytesToRead))) {
  2466. goto Exit;
  2467. }
  2468. if (!NT_SUCCESS(Status = LdrpNativeReadVirtualMemory(ProcessHandle, RemoteAddress + Offset, Buffer->Buffer + Offset, BytesToRead, &BytesRead))) {
  2469. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL, "LDR: %s(): NtReadVirtualMemory failed.\n", __FUNCTION__));
  2470. goto Exit;
  2471. }
  2472. if (BytesToRead != BytesRead) {
  2473. goto ReadTruncated;
  2474. }
  2475. }
  2476. DosHeader = (PIMAGE_DOS_HEADER)Buffer->Buffer;
  2477. if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
  2478. goto InvalidImageFormat;
  2479. }
  2480. if (DosHeader->e_lfanew >= RTLP_IMAGE_MAX_DOS_HEADER) {
  2481. goto InvalidImageFormat;
  2482. }
  2483. Offset += DosHeader->e_lfanew;
  2484. BytesToRead = RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader);
  2485. {
  2486. C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader)
  2487. == RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS64, FileHeader));
  2488. }
  2489. if (Offset + BytesToRead > InitialReadSize) {
  2490. if (!NT_SUCCESS(Status = RtlEnsureBufferSize(0, Buffer, Offset + BytesToRead))) {
  2491. goto Exit;
  2492. }
  2493. if (!NT_SUCCESS(Status = LdrpNativeReadVirtualMemory(ProcessHandle, RemoteAddress + Offset, Buffer->Buffer + Offset, BytesToRead, &BytesRead))) {
  2494. goto Exit;
  2495. }
  2496. if (BytesToRead != BytesRead) {
  2497. goto ReadTruncated;
  2498. }
  2499. }
  2500. Headers32Offset = Offset;
  2501. Headers32 = (PIMAGE_NT_HEADERS32)(Buffer->Buffer + Headers32Offset); // correct for 64bit too
  2502. if (Headers32->Signature != IMAGE_NT_SIGNATURE) {
  2503. goto InvalidImageFormat;
  2504. }
  2505. Offset += BytesToRead;
  2506. BytesToRead = Headers32->FileHeader.SizeOfOptionalHeader
  2507. + Headers32->FileHeader.NumberOfSections * IMAGE_SIZEOF_SECTION_HEADER;
  2508. if (Offset + BytesToRead > InitialReadSize) {
  2509. if (!NT_SUCCESS(Status = RtlEnsureBufferSize(0, Buffer, Offset + BytesToRead))) {
  2510. goto Exit;
  2511. }
  2512. if (!NT_SUCCESS(Status = LdrpNativeReadVirtualMemory(ProcessHandle, RemoteAddress + Offset, Buffer->Buffer + Offset, BytesToRead, &BytesRead))) {
  2513. goto Exit;
  2514. }
  2515. if (BytesToRead != BytesRead) {
  2516. goto ReadTruncated;
  2517. }
  2518. }
  2519. Headers32 = (PIMAGE_NT_HEADERS32)(Buffer->Buffer + Headers32Offset); // correct for 64bit too
  2520. if (Headers32->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC
  2521. && Headers32->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC
  2522. ) {
  2523. goto InvalidImageFormat;
  2524. }
  2525. #if defined(_WIN64) || defined(BUILD_WOW6432)
  2526. if ((Image->Flags & LDR_DLL_MAPPED_AS_MASK) == LDR_DLL_MAPPED_AS_UNFORMATED_IMAGE) {
  2527. // This test is copied from ntdll.dll's conditional call to LdrpWx86FormatVirtualImage.
  2528. if (
  2529. Headers32->FileHeader.Machine == IMAGE_FILE_MACHINE_I386
  2530. && Headers32->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC
  2531. && Headers32->OptionalHeader.SectionAlignment < NativePageSize
  2532. ) {
  2533. NTSTATUS st;
  2534. NATIVE_MEMORY_BASIC_INFORMATION MemoryInformation;
  2535. st =
  2536. LdrpNativeQueryVirtualMemory(
  2537. ProcessHandle,
  2538. (NATIVE_PVOID)RemoteAddress,
  2539. MemoryBasicInformation,
  2540. &MemoryInformation,
  2541. sizeof MemoryInformation,
  2542. NULL);
  2543. if ((! NT_SUCCESS(st))
  2544. || ((MemoryInformation.Protect != PAGE_READONLY) &&
  2545. (MemoryInformation.Protect != PAGE_EXECUTE_READ))
  2546. ) {
  2547. Image->Flags = (Image->Flags & ~LDR_DLL_MAPPED_AS_MASK) | LDR_DLL_MAPPED_AS_DATA;
  2548. } else {
  2549. Image->Flags = (Image->Flags & ~LDR_DLL_MAPPED_AS_MASK) | LDR_DLL_MAPPED_AS_IMAGE;
  2550. }
  2551. } else {
  2552. Image->Flags = (Image->Flags & ~LDR_DLL_MAPPED_AS_MASK) | LDR_DLL_MAPPED_AS_IMAGE;
  2553. }
  2554. }
  2555. #endif
  2556. Status = STATUS_SUCCESS;
  2557. Exit:
  2558. if (!NT_SUCCESS(Status)) {
  2559. LdrDestroyOutOfProcessImage(Image);
  2560. }
  2561. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_LEVEL_STATUS(Status), "LDR: %s() exiting 0x%08lx\n", __FUNCTION__, Status));
  2562. return Status;
  2563. ReadTruncated:
  2564. Status = STATUS_UNSUCCESSFUL;
  2565. goto Exit;
  2566. InvalidImageFormat:
  2567. Status = STATUS_INVALID_IMAGE_FORMAT;
  2568. goto Exit;
  2569. }
  2570. NTSTATUS
  2571. NTAPI
  2572. LdrFindCreateProcessManifest(
  2573. IN ULONG Flags,
  2574. IN OUT PLDR_OUT_OF_PROCESS_IMAGE Image,
  2575. IN const ULONG_PTR* IdPath,
  2576. IN ULONG IdPathLength,
  2577. OUT PIMAGE_RESOURCE_DATA_ENTRY OutDataEntry
  2578. )
  2579. /*++
  2580. Routine Description:
  2581. This function is like LdrFindResource_U, but it can load resources
  2582. from a file mapped into another process. It only works as much
  2583. as has been needed.
  2584. Arguments:
  2585. Flags -
  2586. LDR_FIND_RESOURCE_LANGUAGE_CAN_FALLBACK - if the specified langid is not found,
  2587. fallback on the usual or any strategy,
  2588. the current implementation always loads the
  2589. first langid
  2590. LDR_FIND_RESOURCE_LANGUAGE_EXACT - only load the resource with exactly
  2591. specified langid
  2592. ProcessHandle - The process the DllHandle is valid in. Passed to NtReadVirtualMemory.
  2593. DllHandle - Same as LdrFindResource_U
  2594. ResourceIdPath - Same as LdrFindResource_U
  2595. ResourceIdPathLength - Same as LdrFindResource_U
  2596. OutDataEntry - Similar to LdrFindResource_U, but returned by value instead of address.
  2597. Return Value:
  2598. NTSTATUS
  2599. --*/
  2600. {
  2601. NTSTATUS Status = STATUS_SUCCESS;
  2602. SIZE_T BytesRead = 0;
  2603. SIZE_T BytesToRead = 0;
  2604. #if DBG
  2605. ULONG Line = __LINE__;
  2606. #endif
  2607. // we depend on these values sorting first
  2608. C_ASSERT(CREATEPROCESS_MANIFEST_RESOURCE_ID == 1);
  2609. C_ASSERT(ISOLATIONAWARE_MANIFEST_RESOURCE_ID == 2);
  2610. #if DBG
  2611. KdPrintEx((
  2612. DPFLTR_LDR_ID,
  2613. DPFLTR_TRACE_LEVEL,
  2614. "LDR: %s(0x%lx, %p, %p[%Id, %Id, %Id], %lu, %p) beginning\n",
  2615. __FUNCTION__,
  2616. Flags,
  2617. Image,
  2618. IdPath,
  2619. // 3 is the usual number, type, id/name, language
  2620. (IdPath != NULL && IdPathLength > 0) ? IdPath[0] : 0,
  2621. (IdPath != NULL && IdPathLength > 1) ? IdPath[1] : 0,
  2622. (IdPath != NULL && IdPathLength > 2) ? IdPath[2] : 0,
  2623. IdPathLength,
  2624. OutDataEntry
  2625. ));
  2626. #endif
  2627. #define LDRP_CHECK_PARAMETER(x) if (!(x)) { ASSERT(x); return STATUS_INVALID_PARAMETER; }
  2628. LDRP_CHECK_PARAMETER(Image != NULL);
  2629. LDRP_CHECK_PARAMETER(Image->DllHandle != 0);
  2630. LDRP_CHECK_PARAMETER(Image->ProcessHandle != NULL);
  2631. LDRP_CHECK_PARAMETER(OutDataEntry != NULL);
  2632. LDRP_CHECK_PARAMETER(IdPath != NULL);
  2633. LDRP_CHECK_PARAMETER((Image->Flags & LDR_DLL_MAPPED_AS_MASK) != LDR_DLL_MAPPED_AS_UNFORMATED_IMAGE);
  2634. // not all flags are implemented (only image vs. data is)
  2635. LDRP_CHECK_PARAMETER((Flags & LDR_FIND_RESOURCE_LANGUAGE_EXACT) == 0);
  2636. LDRP_CHECK_PARAMETER((Flags & LDRP_FIND_RESOURCE_DIRECTORY ) == 0);
  2637. RtlZeroMemory(OutDataEntry, sizeof(OutDataEntry));
  2638. if (Image->ProcessHandle == NtCurrentProcess()) {
  2639. PVOID DirectoryOrData = NULL;
  2640. PVOID DllHandle = (PVOID)(ULONG_PTR)Image->DllHandle;
  2641. Status = LdrpSearchResourceSection_U(
  2642. (Image->Flags & LDR_DLL_MAPPED_AS_DATA)
  2643. ? LDR_VIEW_TO_DATAFILE(DllHandle)
  2644. : DllHandle,
  2645. IdPath,
  2646. IdPathLength,
  2647. Flags,
  2648. &DirectoryOrData
  2649. );
  2650. if (NT_SUCCESS(Status) && DirectoryOrData != NULL && OutDataEntry != NULL) {
  2651. *OutDataEntry = *(PIMAGE_RESOURCE_DATA_ENTRY)DirectoryOrData;
  2652. }
  2653. goto Exit;
  2654. }
  2655. //
  2656. // All we handle cross process currently is finding the first resource id,
  2657. // first langid, of a given type.
  2658. //
  2659. // And we only handle numbers, not strings/names.
  2660. //
  2661. LDRP_CHECK_PARAMETER(Image->HeadersBuffer.Buffer != NULL);
  2662. LDRP_CHECK_PARAMETER(IdPathLength == 3); // type, id/name, langid
  2663. LDRP_CHECK_PARAMETER(IdPath[0] != 0); // type
  2664. LDRP_CHECK_PARAMETER(IdPath[1] == 0 || IdPath[1] == CREATEPROCESS_MANIFEST_RESOURCE_ID); // just find first id
  2665. LDRP_CHECK_PARAMETER(IdPath[2] == 0); // first langid
  2666. // no strings/names, just numbers
  2667. LDRP_CHECK_PARAMETER(IS_INTRESOURCE(IdPath[0]));
  2668. LDRP_CHECK_PARAMETER(IS_INTRESOURCE(IdPath[1]));
  2669. LDRP_CHECK_PARAMETER(IS_INTRESOURCE(IdPath[2]));
  2670. __try {
  2671. USHORT n = 0;
  2672. USHORT half = 0;
  2673. LONG dir = 0;
  2674. SIZE_T TopDirectorySize = 0;
  2675. ULONG Size = 0;
  2676. PIMAGE_RESOURCE_DIRECTORY Directory = NULL;
  2677. NATIVE_ULONG_PTR RemoteDirectoryAddress = 0;
  2678. UCHAR DirectoryBuffer[
  2679. sizeof(IMAGE_RESOURCE_DIRECTORY)
  2680. + sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY)
  2681. ];
  2682. PIMAGE_RESOURCE_DIRECTORY TopDirectory = NULL;
  2683. NATIVE_ULONG_PTR RemoteTopDirectoryAddress = 0;
  2684. RTL_BUFFER TopDirectoryBuffer = {0};
  2685. UCHAR TopStaticDirectoryBuffer[256];
  2686. C_ASSERT(sizeof(TopStaticDirectoryBuffer) >= sizeof(IMAGE_RESOURCE_DIRECTORY));
  2687. IMAGE_RESOURCE_DATA_ENTRY DataEntry;
  2688. NATIVE_ULONG_PTR RemoteDataEntryAddress = 0;
  2689. PIMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntry = NULL;
  2690. PIMAGE_RESOURCE_DIRECTORY_ENTRY DirEntLow = NULL;
  2691. PIMAGE_RESOURCE_DIRECTORY_ENTRY DirEntMiddle = NULL;
  2692. PIMAGE_RESOURCE_DIRECTORY_ENTRY DirEntHigh = NULL;
  2693. __try {
  2694. RtlInitBuffer(&TopDirectoryBuffer, TopStaticDirectoryBuffer, sizeof(TopStaticDirectoryBuffer));
  2695. Status = RtlEnsureBufferSize(0, &TopDirectoryBuffer, TopDirectoryBuffer.StaticSize);
  2696. ASSERT(NT_SUCCESS(Status));
  2697. RemoteTopDirectoryAddress = (ULONG_PTR)
  2698. RtlImageDirectoryEntryToData(Image->HeadersBuffer.Buffer,
  2699. (Image->Flags & LDR_DLL_MAPPED_AS_DATA) ? FALSE : TRUE,
  2700. IMAGE_DIRECTORY_ENTRY_RESOURCE,
  2701. &Size
  2702. );
  2703. if (RemoteTopDirectoryAddress == 0) {
  2704. Status = STATUS_RESOURCE_DATA_NOT_FOUND;
  2705. __leave;
  2706. }
  2707. //
  2708. // rebase..
  2709. //
  2710. RemoteTopDirectoryAddress =
  2711. LDR_DATAFILE_TO_VIEW_INTEGER((NATIVE_ULONG_PTR)Image->DllHandle)
  2712. + RemoteTopDirectoryAddress
  2713. - ((ULONG_PTR)Image->HeadersBuffer.Buffer);
  2714. Status = LdrpNativeReadVirtualMemory(Image->ProcessHandle, RemoteTopDirectoryAddress, TopDirectoryBuffer.Buffer, TopDirectoryBuffer.Size, &BytesRead);
  2715. if (Status == STATUS_PARTIAL_COPY && BytesRead >= sizeof(*TopDirectory)) {
  2716. // nothing
  2717. }
  2718. else if (!NT_SUCCESS(Status)) {
  2719. __leave;
  2720. }
  2721. TopDirectory = (PIMAGE_RESOURCE_DIRECTORY)TopDirectoryBuffer.Buffer;
  2722. //
  2723. // determine the size of the entire directory, including the named entries,
  2724. // since they occur before the numbered ones (note that we currently
  2725. // don't optimize away reading of the named ones, even though we never
  2726. // search them)
  2727. //
  2728. TopDirectorySize = sizeof(*TopDirectory)
  2729. + (TopDirectory->NumberOfIdEntries + TopDirectory->NumberOfNamedEntries)
  2730. * sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY);
  2731. //
  2732. // now check the result of NtReadVirtualMemory again, if our guess was
  2733. // big enough, but the read was not, error
  2734. //
  2735. if (TopDirectorySize <= TopDirectoryBuffer.Size
  2736. && BytesRead < TopDirectorySize) {
  2737. // REVIEW STATUS_PARTIAL_COPY is only a warning. Is it a strong enough return value?
  2738. // Should we return STATUS_INVALID_IMAGE_FORMAT or STATUS_ACCESS_DENIED instead?
  2739. // There are other places in this file where we propogate STATUS_PARTIAL_COPY, if
  2740. // zero bytes are actually read.
  2741. if (Status == STATUS_PARTIAL_COPY) {
  2742. __leave;
  2743. }
  2744. #if DBG
  2745. Line = __LINE__;
  2746. DbgPrintEx(DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL,
  2747. "LDR: %s(): Line %lu NtReadVirtualMemory() succeeded, but returned too few bytes (0x%Ix out of 0x%Ix).\n",
  2748. __FUNCTION__, Line, BytesRead, BytesToRead);
  2749. #endif
  2750. Status = STATUS_UNSUCCESSFUL;
  2751. __leave;
  2752. }
  2753. //
  2754. // if our initial guessed size was too small, read the correct size
  2755. //
  2756. if (TopDirectorySize > TopDirectoryBuffer.Size) {
  2757. KdPrintEx((
  2758. DPFLTR_LDR_ID,
  2759. DPFLTR_ERROR_LEVEL, // otherwise we'll never see it
  2760. "LDR: %s(): %Id was not enough of a preread for a resource directory, %Id required.\n",
  2761. __FUNCTION__,
  2762. TopDirectoryBuffer.Size,
  2763. TopDirectorySize
  2764. ));
  2765. Status = RtlEnsureBufferSize(0, &TopDirectoryBuffer, TopDirectorySize);
  2766. if (!NT_SUCCESS(Status)) {
  2767. __leave;
  2768. }
  2769. Status = LdrpNativeReadVirtualMemory(Image->ProcessHandle, RemoteTopDirectoryAddress, TopDirectoryBuffer.Buffer, TopDirectoryBuffer.Size, &BytesRead);
  2770. if (!NT_SUCCESS(Status)) {
  2771. __leave;
  2772. }
  2773. if (BytesRead != TopDirectoryBuffer.Size) {
  2774. #if DBG
  2775. Line = __LINE__;
  2776. DbgPrintEx(DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL,
  2777. "LDR: %s(): Line %lu NtReadVirtualMemory() succeeded, but returned too few bytes (0x%Ix out of 0x%Ix).\n",
  2778. __FUNCTION__, Line, BytesRead, BytesToRead);
  2779. #endif
  2780. Status = STATUS_UNSUCCESSFUL;
  2781. __leave;
  2782. }
  2783. TopDirectory = (PIMAGE_RESOURCE_DIRECTORY) TopDirectoryBuffer.Buffer;
  2784. }
  2785. // point to start of named entries
  2786. DirEntLow = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(TopDirectory + 1);
  2787. // move past named entries to numbered entries
  2788. DirEntLow += TopDirectory->NumberOfNamedEntries;
  2789. n = TopDirectory->NumberOfIdEntries;
  2790. if (n == 0) {
  2791. Status = STATUS_RESOURCE_TYPE_NOT_FOUND;
  2792. __leave;
  2793. }
  2794. DirectoryEntry = NULL;
  2795. Directory = NULL;
  2796. DirEntHigh = DirEntLow + n - 1;
  2797. while (DirEntLow <= DirEntHigh) {
  2798. if ((half = (n >> 1)) != 0) {
  2799. DirEntMiddle = DirEntLow;
  2800. if (n & 1) {
  2801. DirEntMiddle += half;
  2802. }
  2803. else {
  2804. DirEntMiddle += half - 1;
  2805. }
  2806. if (DirEntMiddle->NameIsString) {
  2807. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL, "LDR: No strings expected in %s().\n", __FUNCTION__));
  2808. ASSERT(FALSE);
  2809. Status = STATUS_INVALID_PARAMETER;
  2810. __leave;
  2811. }
  2812. dir = LdrpCompareResourceNames_U( *IdPath,
  2813. TopDirectory,
  2814. DirEntMiddle
  2815. );
  2816. if (dir == 0) {
  2817. if (DirEntMiddle->DataIsDirectory) {
  2818. Directory = (PIMAGE_RESOURCE_DIRECTORY)
  2819. (((PCHAR)TopDirectory)
  2820. + DirEntMiddle->OffsetToDirectory);
  2821. }
  2822. else {
  2823. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL, "LDR: %s(): First id in resource path is expected to be a directory.\n", __FUNCTION__));
  2824. Status = STATUS_INVALID_PARAMETER;
  2825. __leave;
  2826. /* This is what you do if we allow specifying the id and language,
  2827. which we might do in the future.
  2828. Directory = NULL;
  2829. Entry = (PIMAGE_RESOURCE_DATA_ENTRY)
  2830. (((PCHAR)TopDirectory)
  2831. + DirEntMiddle->OffsetToData);
  2832. */
  2833. }
  2834. break;
  2835. }
  2836. else {
  2837. if (dir < 0) {
  2838. DirEntHigh = DirEntMiddle - 1;
  2839. if (n & 1) {
  2840. n = half;
  2841. }
  2842. else {
  2843. n = half - 1;
  2844. }
  2845. }
  2846. else {
  2847. DirEntLow = DirEntMiddle + 1;
  2848. n = half;
  2849. }
  2850. }
  2851. }
  2852. else {
  2853. if (n != 0) {
  2854. if (DirEntLow->NameIsString) {
  2855. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL, "LDR: %s() No strings expected.\n", __FUNCTION__));
  2856. Status = STATUS_INVALID_PARAMETER;
  2857. __leave;
  2858. }
  2859. dir = LdrpCompareResourceNames_U( *IdPath,
  2860. TopDirectory,
  2861. DirEntLow
  2862. );
  2863. if (dir == 0) {
  2864. if (DirEntLow->DataIsDirectory) {
  2865. Directory = (PIMAGE_RESOURCE_DIRECTORY)
  2866. (((PCHAR)TopDirectory)
  2867. + DirEntLow->OffsetToDirectory);
  2868. }
  2869. else {
  2870. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL, "LDR: %s() First id in resource path is expected to be a directory", __FUNCTION__));
  2871. Status = STATUS_INVALID_PARAMETER;
  2872. __leave;
  2873. /*
  2874. Entry = (PIMAGE_RESOURCE_DATA_ENTRY)
  2875. (((PCHAR)TopDirectory)
  2876. + DirEntLow->OffsetToData);
  2877. */
  2878. }
  2879. }
  2880. }
  2881. break;
  2882. }
  2883. }
  2884. //
  2885. // ok, now we have found address of the type's name/id directory (or not)
  2886. //
  2887. if (Directory == NULL) {
  2888. Status = STATUS_RESOURCE_TYPE_NOT_FOUND;
  2889. __leave;
  2890. }
  2891. //
  2892. // we copied the binary search and didn't quite compute what we want,
  2893. // it found the local address, change this to an offset and apply
  2894. // to the remote address ("rebase")
  2895. //
  2896. RemoteDirectoryAddress =
  2897. RemoteTopDirectoryAddress
  2898. + ((ULONG_PTR)Directory)
  2899. - ((ULONG_PTR)TopDirectory);
  2900. //
  2901. // Now do the read of both the directory and the first entry.
  2902. //
  2903. Directory = (PIMAGE_RESOURCE_DIRECTORY)&DirectoryBuffer[0];
  2904. Status = LdrpNativeReadVirtualMemory(Image->ProcessHandle, RemoteDirectoryAddress, Directory, sizeof(DirectoryBuffer), &BytesRead);
  2905. if (!NT_SUCCESS(Status)) {
  2906. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL, "LDR: %s() NtReadVirtualMemory failed.", __FUNCTION__));
  2907. __leave;
  2908. }
  2909. if (BytesRead != sizeof(DirectoryBuffer)) {
  2910. #if DBG
  2911. Line = __LINE__;
  2912. DbgPrintEx(DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL,
  2913. "LDR: %s(): Line %lu NtReadVirtualMemory() succeeded, but returned too few bytes (0x%Ix out of 0x%Ix).\n",
  2914. __FUNCTION__, Line, BytesRead, BytesToRead);
  2915. #endif
  2916. Status = STATUS_UNSUCCESSFUL;
  2917. __leave;
  2918. }
  2919. if ((Directory->NumberOfNamedEntries + Directory->NumberOfIdEntries) == 0) {
  2920. Status = STATUS_RESOURCE_NAME_NOT_FOUND;
  2921. __leave;
  2922. }
  2923. if (IdPath[1] == CREATEPROCESS_MANIFEST_RESOURCE_ID && Directory->NumberOfNamedEntries != 0) {
  2924. KdPrintEx((
  2925. DPFLTR_LDR_ID,
  2926. DPFLTR_ERROR_LEVEL,
  2927. "LDR: %s() caller asked for id==1 but there are named entries we are not bothering to skip.\n",
  2928. __FUNCTION__
  2929. ));
  2930. Status = STATUS_RESOURCE_NAME_NOT_FOUND;
  2931. __leave;
  2932. }
  2933. //
  2934. // grab the entry for the first id
  2935. //
  2936. DirectoryEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(Directory + 1);
  2937. if (!DirectoryEntry->DataIsDirectory) {
  2938. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL, "LDR: Second level of resource directory is expected to be a directory\n"));
  2939. Status = STATUS_INVALID_IMAGE_FORMAT; // REVIEW too strong?
  2940. __leave;
  2941. }
  2942. //
  2943. // If there is more than one entry, ensure no conflicts.
  2944. //
  2945. if (Directory->NumberOfIdEntries > 1
  2946. && DirectoryEntry->Id >= MINIMUM_RESERVED_MANIFEST_RESOURCE_ID
  2947. && DirectoryEntry->Id <= MAXIMUM_RESERVED_MANIFEST_RESOURCE_ID
  2948. ) {
  2949. NATIVE_ULONG_PTR RemoteDirectoryEntryPointer = 0;
  2950. IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[
  2951. MAXIMUM_RESERVED_MANIFEST_RESOURCE_ID
  2952. - MINIMUM_RESERVED_MANIFEST_RESOURCE_ID
  2953. + 1];
  2954. ULONG ResourceId;
  2955. ULONG NumberOfEntriesToCheck;
  2956. ULONG CountOfReservedManifestIds;
  2957. C_ASSERT(MINIMUM_RESERVED_MANIFEST_RESOURCE_ID == 1);
  2958. RemoteDirectoryEntryPointer = RemoteDirectoryAddress + sizeof(IMAGE_RESOURCE_DIRECTORY);
  2959. NumberOfEntriesToCheck = LDRP_MIN(RTL_NUMBER_OF(DirectoryEntries), Directory->NumberOfIdEntries);
  2960. BytesToRead = sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY) * NumberOfEntriesToCheck;
  2961. ASSERT(BytesToRead <= sizeof(DirectoryEntries));
  2962. Status = LdrpNativeReadVirtualMemory(Image->ProcessHandle, RemoteDirectoryEntryPointer, &DirectoryEntries[0], BytesToRead, &BytesRead);
  2963. if (!NT_SUCCESS(Status)) {
  2964. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL, "LDR: %s() NtReadVirtualMemory failed.", __FUNCTION__));
  2965. __leave;
  2966. }
  2967. if (BytesRead != BytesToRead) {
  2968. #if DBG
  2969. Line = __LINE__;
  2970. DbgPrintEx(DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL,
  2971. "LDR: %s(): Line %lu NtReadVirtualMemory() succeeded, but returned too few bytes (0x%Ix out of 0x%Ix).\n",
  2972. __FUNCTION__, Line, BytesRead, BytesToRead);
  2973. #endif
  2974. Status = STATUS_UNSUCCESSFUL;
  2975. __leave;
  2976. }
  2977. CountOfReservedManifestIds = 0;
  2978. for (ResourceId = MINIMUM_RESERVED_MANIFEST_RESOURCE_ID;
  2979. ResourceId != MINIMUM_RESERVED_MANIFEST_RESOURCE_ID + NumberOfEntriesToCheck;
  2980. ResourceId += 1
  2981. ) {
  2982. if (DirectoryEntries[ResourceId - MINIMUM_RESERVED_MANIFEST_RESOURCE_ID].Id >= MINIMUM_RESERVED_MANIFEST_RESOURCE_ID
  2983. && DirectoryEntries[ResourceId - MINIMUM_RESERVED_MANIFEST_RESOURCE_ID].Id <= MAXIMUM_RESERVED_MANIFEST_RESOURCE_ID
  2984. ) {
  2985. CountOfReservedManifestIds += 1;
  2986. if (CountOfReservedManifestIds > 1) {
  2987. #if DBG
  2988. DbgPrintEx(
  2989. DPFLTR_LDR_ID,
  2990. DPFLTR_ERROR_LEVEL,
  2991. "LDR: %s() multiple reserved manifest resource ids present\n",
  2992. __FUNCTION__
  2993. );
  2994. #endif
  2995. Status = STATUS_INVALID_PARAMETER;
  2996. __leave;
  2997. }
  2998. }
  2999. }
  3000. }
  3001. if (IdPath[1] == CREATEPROCESS_MANIFEST_RESOURCE_ID && DirectoryEntry->Id != CREATEPROCESS_MANIFEST_RESOURCE_ID) {
  3002. Status = STATUS_RESOURCE_NAME_NOT_FOUND;
  3003. __leave;
  3004. }
  3005. //
  3006. // now get address of langid directory
  3007. //
  3008. RemoteDirectoryAddress = RemoteTopDirectoryAddress + DirectoryEntry->OffsetToDirectory;
  3009. //
  3010. // now read the langid directory and its first entry
  3011. //
  3012. Status = LdrpNativeReadVirtualMemory(Image->ProcessHandle, RemoteDirectoryAddress, Directory, sizeof(DirectoryBuffer), &BytesRead);
  3013. if (!NT_SUCCESS(Status)) {
  3014. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL, "LDR: %s() NtReadVirtualMemory failed.", __FUNCTION__));
  3015. __leave;
  3016. }
  3017. if (BytesRead != sizeof(DirectoryBuffer)) {
  3018. #if DBG
  3019. DbgPrintEx(DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL,
  3020. "LDR: %s(): Line %lu NtReadVirtualMemory() succeeded, but returned too few bytes (0x%Ix out of 0x%Ix).\n",
  3021. __FUNCTION__, Line, BytesRead, BytesToRead);
  3022. #endif
  3023. Status = STATUS_UNSUCCESSFUL;
  3024. __leave;
  3025. }
  3026. if ((Directory->NumberOfNamedEntries + Directory->NumberOfIdEntries) == 0) {
  3027. Status = STATUS_RESOURCE_LANG_NOT_FOUND;
  3028. __leave;
  3029. }
  3030. //
  3031. // look at the langid directory's first entry
  3032. //
  3033. if (DirectoryEntry->DataIsDirectory) {
  3034. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL, "LDR: Third level of resource directory is not expected to be a directory\n"));
  3035. Status = STATUS_INVALID_IMAGE_FORMAT; // REVIEW too strong?
  3036. __leave;
  3037. }
  3038. RemoteDataEntryAddress =
  3039. RemoteTopDirectoryAddress
  3040. + DirectoryEntry->OffsetToData;
  3041. //
  3042. // read the data entry
  3043. //
  3044. Status = LdrpNativeReadVirtualMemory(Image->ProcessHandle, RemoteDataEntryAddress, &DataEntry, sizeof(DataEntry), &BytesRead);
  3045. if (!NT_SUCCESS(Status)) {
  3046. KdPrintEx((DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL, "LDR: %s() NtReadVirtualMemory failed.", __FUNCTION__));
  3047. __leave;
  3048. }
  3049. if (BytesRead != sizeof(DataEntry)) {
  3050. #if DBG
  3051. DbgPrintEx(DPFLTR_LDR_ID, DPFLTR_ERROR_LEVEL,
  3052. "LDR: %s(): Line %lu NtReadVirtualMemory() succeeded, but returned too few bytes (0x%Ix out of 0x%Ix).\n",
  3053. __FUNCTION__, Line, BytesRead, BytesToRead);
  3054. #endif
  3055. Status = STATUS_UNSUCCESSFUL;
  3056. __leave;
  3057. }
  3058. *OutDataEntry = DataEntry;
  3059. Status = STATUS_SUCCESS;
  3060. }
  3061. __finally {
  3062. RtlFreeBuffer(&TopDirectoryBuffer);
  3063. }
  3064. }
  3065. __except (EXCEPTION_EXECUTE_HANDLER) {
  3066. Status = GetExceptionCode();
  3067. }
  3068. Exit:
  3069. #if DBG
  3070. //
  3071. // Fix/raid dcpromo, msiexec, etc..
  3072. // DPFLTR_LEVEL_STATUS filters all forms of resource not found.
  3073. //
  3074. if (DPFLTR_LEVEL_STATUS(Status) == DPFLTR_ERROR_LEVEL) {
  3075. KdPrintEx((
  3076. DPFLTR_LDR_ID,
  3077. DPFLTR_LEVEL_STATUS(Status),
  3078. "LDR: %s(0x%lx, %p, %p[%Id, %Id, %Id], %lu, %p) failed %08x\n",
  3079. __FUNCTION__,
  3080. Flags,
  3081. Image,
  3082. IdPath,
  3083. // 3 is the usual number, type, id/name, language
  3084. (IdPath != NULL && IdPathLength > 0) ? IdPath[0] : 0,
  3085. (IdPath != NULL && IdPathLength > 1) ? IdPath[1] : 0,
  3086. (IdPath != NULL && IdPathLength > 2) ? IdPath[2] : 0,
  3087. IdPathLength,
  3088. OutDataEntry,
  3089. Status
  3090. ));
  3091. KdPrintEx((
  3092. DPFLTR_LDR_ID,
  3093. DPFLTR_LEVEL_STATUS(Status),
  3094. "LDR: %s() returning Status:0x%lx IMAGE_RESOURCE_DATA_ENTRY:{OffsetToData=%#lx, Size=%#lx}\n",
  3095. __FUNCTION__,
  3096. Status,
  3097. (OutDataEntry != NULL) ? OutDataEntry->OffsetToData : 0,
  3098. (OutDataEntry != NULL) ? OutDataEntry->Size : 0
  3099. ));
  3100. }
  3101. #endif
  3102. return Status;
  3103. }
  3104. NTSTATUS
  3105. NTAPI
  3106. LdrAccessOutOfProcessResource(
  3107. IN ULONG Flags,
  3108. IN OUT PLDR_OUT_OF_PROCESS_IMAGE Image, // currently only IN
  3109. IN const IMAGE_RESOURCE_DATA_ENTRY* DataEntry,
  3110. OUT PULONG64 Address OPTIONAL,
  3111. OUT PULONG Size OPTIONAL
  3112. )
  3113. /*++
  3114. Routine Description:
  3115. This function is like LdrAccessResource, but it works on images
  3116. mapped out of process.
  3117. Arguments:
  3118. Flags -
  3119. Image - an opaque object representing an image or file mapped into another process,
  3120. created with LdrCreateOutOfProcessImage.
  3121. DataEntry - Same as LdrAccessResource, but returned by-value from LdrFindOutOfProcessResource
  3122. Address - Same as LdrAccessResource
  3123. Size - Same as LdrAccessResource
  3124. Return Value:
  3125. NTSTATUS
  3126. --*/
  3127. {
  3128. NTSTATUS Status = 0;
  3129. PVOID LocalAddress = 0;
  3130. ULONG_PTR LocalHeaders = 0;
  3131. ASSERT(Image != NULL);
  3132. ASSERT(Image->DllHandle != 0);
  3133. ASSERT(Image->ProcessHandle != NULL);
  3134. if (ARGUMENT_PRESENT(Address)) {
  3135. *Address = 0;
  3136. }
  3137. if (Image->ProcessHandle != NtCurrentProcess()) {
  3138. ASSERT(Image->HeadersBuffer.Buffer != NULL);
  3139. LocalHeaders = (ULONG_PTR)Image->HeadersBuffer.Buffer;
  3140. } else {
  3141. LocalHeaders = (ULONG_PTR)Image->DllHandle;
  3142. }
  3143. if ((Image->Flags & LDR_DLL_MAPPED_AS_DATA) != 0) {
  3144. LocalHeaders = LDR_VIEW_TO_DATAFILE_INTEGER(LocalHeaders);
  3145. }
  3146. Status = LdrpAccessResourceDataNoMultipleLanguage(
  3147. (PVOID)LocalHeaders,
  3148. DataEntry,
  3149. &LocalAddress,
  3150. Size
  3151. );
  3152. if (NT_SUCCESS(Status)
  3153. && ARGUMENT_PRESENT(Address)
  3154. && LocalAddress != NULL
  3155. ) {
  3156. //
  3157. // rebase if out of proc, else for inproc Image->DllHandle - LocalHeaders == 0.
  3158. //
  3159. *Address = LDR_DATAFILE_TO_VIEW_INTEGER(Image->DllHandle)
  3160. + ((ULONG_PTR)LocalAddress)
  3161. - LDR_DATAFILE_TO_VIEW_INTEGER(LocalHeaders)
  3162. ;
  3163. }
  3164. #if DBG
  3165. if (!NT_SUCCESS(Status)) {
  3166. DbgPrintEx(DPFLTR_LDR_ID, DPFLTR_LEVEL_STATUS(Status), "LDR: %s() exiting 0x%08lx\n", __FUNCTION__, Status);
  3167. }
  3168. #endif
  3169. return Status;
  3170. }
  3171. #endif
  3172. NTSTATUS
  3173. LdrEnumResources(
  3174. IN PVOID DllHandle,
  3175. IN const ULONG_PTR* ResourceIdPath,
  3176. IN ULONG ResourceIdPathLength,
  3177. IN OUT PULONG NumberOfResources,
  3178. OUT PLDR_ENUM_RESOURCE_ENTRY Resources OPTIONAL
  3179. )
  3180. {
  3181. NTSTATUS Status;
  3182. PIMAGE_RESOURCE_DIRECTORY TopResourceDirectory;
  3183. PIMAGE_RESOURCE_DIRECTORY TypeResourceDirectory;
  3184. PIMAGE_RESOURCE_DIRECTORY NameResourceDirectory;
  3185. PIMAGE_RESOURCE_DIRECTORY LangResourceDirectory;
  3186. PIMAGE_RESOURCE_DIRECTORY_ENTRY TypeResourceDirectoryEntry;
  3187. PIMAGE_RESOURCE_DIRECTORY_ENTRY NameResourceDirectoryEntry;
  3188. PIMAGE_RESOURCE_DIRECTORY_ENTRY LangResourceDirectoryEntry;
  3189. ULONG TypeDirectoryIndex, NumberOfTypeDirectoryEntries;
  3190. ULONG NameDirectoryIndex, NumberOfNameDirectoryEntries;
  3191. ULONG LangDirectoryIndex, NumberOfLangDirectoryEntries;
  3192. BOOLEAN ScanTypeDirectory;
  3193. BOOLEAN ScanNameDirectory;
  3194. BOOLEAN ReturnThisResource;
  3195. PIMAGE_RESOURCE_DIR_STRING_U ResourceNameString;
  3196. ULONG_PTR TypeResourceNameOrId;
  3197. ULONG_PTR NameResourceNameOrId;
  3198. ULONG_PTR LangResourceNameOrId;
  3199. PLDR_ENUM_RESOURCE_ENTRY ResourceInfo;
  3200. PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry;
  3201. ULONG ResourceIndex, MaxResourceIndex;
  3202. ULONG Size;
  3203. ResourceIndex = 0;
  3204. if (!ARGUMENT_PRESENT( Resources )) {
  3205. MaxResourceIndex = 0;
  3206. }
  3207. else {
  3208. MaxResourceIndex = *NumberOfResources;
  3209. }
  3210. *NumberOfResources = 0;
  3211. TopResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
  3212. RtlImageDirectoryEntryToData( DllHandle,
  3213. TRUE,
  3214. IMAGE_DIRECTORY_ENTRY_RESOURCE,
  3215. &Size
  3216. );
  3217. if (!TopResourceDirectory) {
  3218. return STATUS_RESOURCE_DATA_NOT_FOUND;
  3219. }
  3220. TypeResourceDirectory = TopResourceDirectory;
  3221. TypeResourceDirectoryEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(TypeResourceDirectory+1);
  3222. NumberOfTypeDirectoryEntries = TypeResourceDirectory->NumberOfNamedEntries +
  3223. TypeResourceDirectory->NumberOfIdEntries;
  3224. TypeDirectoryIndex = 0;
  3225. Status = STATUS_SUCCESS;
  3226. for (TypeDirectoryIndex=0;
  3227. TypeDirectoryIndex<NumberOfTypeDirectoryEntries;
  3228. TypeDirectoryIndex++, TypeResourceDirectoryEntry++
  3229. ) {
  3230. if (ResourceIdPathLength > 0) {
  3231. ScanTypeDirectory = LdrpCompareResourceNames_U( ResourceIdPath[ 0 ],
  3232. TopResourceDirectory,
  3233. TypeResourceDirectoryEntry
  3234. ) == 0;
  3235. }
  3236. else {
  3237. ScanTypeDirectory = TRUE;
  3238. }
  3239. if (ScanTypeDirectory) {
  3240. if (!TypeResourceDirectoryEntry->DataIsDirectory) {
  3241. return STATUS_INVALID_IMAGE_FORMAT;
  3242. }
  3243. if (TypeResourceDirectoryEntry->NameIsString) {
  3244. ResourceNameString = (PIMAGE_RESOURCE_DIR_STRING_U)
  3245. ((PCHAR)TopResourceDirectory + TypeResourceDirectoryEntry->NameOffset);
  3246. TypeResourceNameOrId = (ULONG_PTR)ResourceNameString;
  3247. }
  3248. else {
  3249. TypeResourceNameOrId = (ULONG_PTR)TypeResourceDirectoryEntry->Id;
  3250. }
  3251. NameResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
  3252. ((PCHAR)TopResourceDirectory + TypeResourceDirectoryEntry->OffsetToDirectory);
  3253. NameResourceDirectoryEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(NameResourceDirectory+1);
  3254. NumberOfNameDirectoryEntries = NameResourceDirectory->NumberOfNamedEntries +
  3255. NameResourceDirectory->NumberOfIdEntries;
  3256. for (NameDirectoryIndex=0;
  3257. NameDirectoryIndex<NumberOfNameDirectoryEntries;
  3258. NameDirectoryIndex++, NameResourceDirectoryEntry++
  3259. ) {
  3260. if (ResourceIdPathLength > 1) {
  3261. ScanNameDirectory = LdrpCompareResourceNames_U( ResourceIdPath[ 1 ],
  3262. TopResourceDirectory,
  3263. NameResourceDirectoryEntry
  3264. ) == 0;
  3265. }
  3266. else {
  3267. ScanNameDirectory = TRUE;
  3268. }
  3269. if (ScanNameDirectory) {
  3270. if (!NameResourceDirectoryEntry->DataIsDirectory) {
  3271. return STATUS_INVALID_IMAGE_FORMAT;
  3272. }
  3273. if (NameResourceDirectoryEntry->NameIsString) {
  3274. ResourceNameString = (PIMAGE_RESOURCE_DIR_STRING_U)
  3275. ((PCHAR)TopResourceDirectory + NameResourceDirectoryEntry->NameOffset);
  3276. NameResourceNameOrId = (ULONG_PTR)ResourceNameString;
  3277. }
  3278. else {
  3279. NameResourceNameOrId = (ULONG_PTR)NameResourceDirectoryEntry->Id;
  3280. }
  3281. LangResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
  3282. ((PCHAR)TopResourceDirectory + NameResourceDirectoryEntry->OffsetToDirectory);
  3283. LangResourceDirectoryEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(LangResourceDirectory+1);
  3284. NumberOfLangDirectoryEntries = LangResourceDirectory->NumberOfNamedEntries +
  3285. LangResourceDirectory->NumberOfIdEntries;
  3286. LangDirectoryIndex = 0;
  3287. for (LangDirectoryIndex=0;
  3288. LangDirectoryIndex<NumberOfLangDirectoryEntries;
  3289. LangDirectoryIndex++, LangResourceDirectoryEntry++
  3290. ) {
  3291. if (ResourceIdPathLength > 2) {
  3292. ReturnThisResource = LdrpCompareResourceNames_U( ResourceIdPath[ 2 ],
  3293. TopResourceDirectory,
  3294. LangResourceDirectoryEntry
  3295. ) == 0;
  3296. }
  3297. else {
  3298. ReturnThisResource = TRUE;
  3299. }
  3300. if (ReturnThisResource) {
  3301. if (LangResourceDirectoryEntry->DataIsDirectory) {
  3302. return STATUS_INVALID_IMAGE_FORMAT;
  3303. }
  3304. if (LangResourceDirectoryEntry->NameIsString) {
  3305. ResourceNameString = (PIMAGE_RESOURCE_DIR_STRING_U)
  3306. ((PCHAR)TopResourceDirectory + LangResourceDirectoryEntry->NameOffset);
  3307. LangResourceNameOrId = (ULONG_PTR)ResourceNameString;
  3308. }
  3309. else {
  3310. LangResourceNameOrId = (ULONG_PTR)LangResourceDirectoryEntry->Id;
  3311. }
  3312. ResourceDataEntry = (PIMAGE_RESOURCE_DATA_ENTRY)
  3313. ((PCHAR)TopResourceDirectory + LangResourceDirectoryEntry->OffsetToData);
  3314. ResourceInfo = &Resources[ ResourceIndex++ ];
  3315. if (ResourceIndex <= MaxResourceIndex) {
  3316. ResourceInfo->Path[ 0 ].NameOrId = TypeResourceNameOrId;
  3317. ResourceInfo->Path[ 1 ].NameOrId = NameResourceNameOrId;
  3318. ResourceInfo->Path[ 2 ].NameOrId = LangResourceNameOrId;
  3319. ResourceInfo->Data = (PVOID)((ULONG_PTR)DllHandle + ResourceDataEntry->OffsetToData);
  3320. ResourceInfo->Size = ResourceDataEntry->Size;
  3321. ResourceInfo->Reserved = 0;
  3322. }
  3323. else {
  3324. Status = STATUS_INFO_LENGTH_MISMATCH;
  3325. }
  3326. }
  3327. }
  3328. }
  3329. }
  3330. }
  3331. }
  3332. *NumberOfResources = ResourceIndex;
  3333. return Status;
  3334. }
  3335. #ifdef NTOS_USERMODE_RUNTIME
  3336. BOOLEAN
  3337. LdrAlternateResourcesEnabled(
  3338. VOID
  3339. )
  3340. /*++
  3341. Routine Description:
  3342. This function determines if the althernate resources are enabled.
  3343. Arguments:
  3344. None.
  3345. Return Value:
  3346. True - Alternate Resource enabled.
  3347. False - Alternate Resource not enabled.
  3348. --*/
  3349. {
  3350. NTSTATUS Status;
  3351. GET_UI_LANGID();
  3352. if (!UILangId){
  3353. return FALSE;
  3354. }
  3355. if (!InstallLangId){
  3356. Status = NtQueryInstallUILanguage( &InstallLangId);
  3357. if (!NT_SUCCESS( Status )) {
  3358. //
  3359. // Failed to get Intall LangID. AltResource not enabled.
  3360. //
  3361. return FALSE;
  3362. }
  3363. }
  3364. #ifndef MUI_MAGIC
  3365. if (UILangId == InstallLangId) {
  3366. //
  3367. // UI Lang matches Installed Lang. AltResource not enabled.
  3368. //
  3369. return FALSE;
  3370. }
  3371. #endif
  3372. return TRUE;
  3373. }
  3374. PVOID
  3375. LdrGetAlternateResourceModuleHandle(
  3376. IN PVOID Module
  3377. )
  3378. {
  3379. return LdrpGetAlternateResourceModuleHandle(Module, 0);
  3380. }
  3381. PVOID
  3382. LdrpGetAlternateResourceModuleHandle(
  3383. IN PVOID Module,
  3384. IN LANGID LangId
  3385. )
  3386. /*++
  3387. Routine Description:
  3388. This function gets the alternate resource module from the table
  3389. containing the handle.
  3390. Arguments:
  3391. Module - Module of which alternate resource module needs to loaded.
  3392. Return Value:
  3393. Handle of the alternate resource module.
  3394. --*/
  3395. {
  3396. ULONG ModuleIndex;
  3397. if (!LangId) {
  3398. GET_UI_LANGID();
  3399. LangId = UILangId;
  3400. }
  3401. if (!LangId) {
  3402. return NULL;
  3403. }
  3404. for (ModuleIndex = 0;
  3405. ModuleIndex < AlternateResourceModuleCount;
  3406. ModuleIndex++ ){
  3407. if (AlternateResourceModules[ModuleIndex].ModuleBase == Module &&
  3408. AlternateResourceModules[ModuleIndex].LangId == LangId ){
  3409. return AlternateResourceModules[ModuleIndex].AlternateModule;
  3410. }
  3411. }
  3412. return NULL;
  3413. }
  3414. BOOLEAN
  3415. LdrpGetFileVersion(
  3416. IN PVOID ImageBase,
  3417. IN LANGID LangId,
  3418. OUT PULONGLONG Version,
  3419. OUT PVOID *VersionResource,
  3420. OUT ULONG *VersionResSize
  3421. )
  3422. /*++
  3423. Routine Description:
  3424. Get the version stamp out of the VS_FIXEDFILEINFO resource in a PE
  3425. image.
  3426. Arguments:
  3427. ImageBase - supplies the address in memory where the file is mapped in.
  3428. Version - receives 64bit version number, or 0 if the file is not
  3429. a PE image or has no version data.
  3430. Return Value:
  3431. None.
  3432. --*/
  3433. {
  3434. PIMAGE_RESOURCE_DATA_ENTRY DataEntry;
  3435. NTSTATUS Status;
  3436. ULONG_PTR IdPath[3];
  3437. ULONG ResourceSize;
  3438. typedef struct tagVS_FIXEDFILEINFO
  3439. {
  3440. LONG dwSignature; /* e.g. 0xfeef04bd */
  3441. LONG dwStrucVersion; /* e.g. 0x00000042 = "0.42" */
  3442. LONG dwFileVersionMS; /* e.g. 0x00030075 = "3.75" */
  3443. LONG dwFileVersionLS; /* e.g. 0x00000031 = "0.31" */
  3444. LONG dwProductVersionMS; /* e.g. 0x00030010 = "3.10" */
  3445. LONG dwProductVersionLS; /* e.g. 0x00000031 = "0.31" */
  3446. LONG dwFileFlagsMask; /* = 0x3F for version "0.42" */
  3447. LONG dwFileFlags; /* e.g. VFF_DEBUG | VFF_PRERELEASE */
  3448. LONG dwFileOS; /* e.g. VOS_DOS_WINDOWS16 */
  3449. LONG dwFileType; /* e.g. VFT_DRIVER */
  3450. LONG dwFileSubtype; /* e.g. VFT2_DRV_KEYBOARD */
  3451. LONG dwFileDateMS; /* e.g. 0 */
  3452. LONG dwFileDateLS; /* e.g. 0 */
  3453. } VS_FIXEDFILEINFO;
  3454. struct {
  3455. USHORT TotalSize;
  3456. USHORT DataSize;
  3457. USHORT Type;
  3458. WCHAR Name[16]; // L"VS_VERSION_INFO" + unicode null terminator
  3459. VS_FIXEDFILEINFO FixedFileInfo;
  3460. } *Resource;
  3461. *Version = 0;
  3462. IdPath[0] = RT_VERSION;
  3463. IdPath[1] = 1;
  3464. IdPath[2] = LangId;
  3465. try {
  3466. Status = LdrpSearchResourceSection_U(
  3467. ImageBase,
  3468. IdPath,
  3469. 3,
  3470. LDR_FIND_RESOURCE_LANGUAGE_EXACT,
  3471. &DataEntry);
  3472. } except(EXCEPTION_EXECUTE_HANDLER) {
  3473. Status = STATUS_UNSUCCESSFUL;
  3474. }
  3475. if(!NT_SUCCESS(Status)) {
  3476. return FALSE;
  3477. }
  3478. try { // we only interested in current ImageBase, if this DataEntry is not of this one, it should fail.
  3479. Status = LdrpAccessResourceDataNoMultipleLanguage(
  3480. ImageBase,
  3481. DataEntry,
  3482. &Resource,
  3483. &ResourceSize);
  3484. } except(EXCEPTION_EXECUTE_HANDLER) {
  3485. Status = STATUS_UNSUCCESSFUL;
  3486. }
  3487. if(!NT_SUCCESS(Status)) {
  3488. return FALSE;
  3489. }
  3490. try {
  3491. if((ResourceSize >= sizeof(*Resource))
  3492. && !_wcsicmp(Resource->Name,L"VS_VERSION_INFO")) {
  3493. *Version = ((ULONGLONG)Resource->FixedFileInfo.dwFileVersionMS << 32)
  3494. | (ULONGLONG)Resource->FixedFileInfo.dwFileVersionLS;
  3495. if (VersionResource)
  3496. {
  3497. *VersionResource = Resource;
  3498. }
  3499. if (VersionResSize)
  3500. {
  3501. *VersionResSize = ResourceSize;
  3502. }
  3503. } else {
  3504. DbgPrint(("LDR: Warning: invalid version resource\n"));
  3505. return FALSE;
  3506. }
  3507. } except(EXCEPTION_EXECUTE_HANDLER) {
  3508. DbgPrint(("LDR: Exception encountered processing bogus version resource\n"));
  3509. return FALSE;
  3510. }
  3511. return TRUE;
  3512. }
  3513. BOOLEAN
  3514. LdrpGetResourceChecksum(
  3515. IN PVOID Module,
  3516. OUT unsigned char** ppMD5Checksum
  3517. )
  3518. {
  3519. NTSTATUS Status;
  3520. ULONG_PTR IdPath[3];
  3521. ULONG ResourceSize;
  3522. PIMAGE_RESOURCE_DATA_ENTRY DataEntry;
  3523. LONG BlockLen;
  3524. LONG VarFileInfoSize;
  3525. ULONGLONG version;
  3526. typedef struct tagVS_FIXEDFILEINFO
  3527. {
  3528. LONG dwSignature; /* e.g. 0xfeef04bd */
  3529. LONG dwStrucVersion; /* e.g. 0x00000042 = "0.42" */
  3530. LONG dwFileVersionMS; /* e.g. 0x00030075 = "3.75" */
  3531. LONG dwFileVersionLS; /* e.g. 0x00000031 = "0.31" */
  3532. LONG dwProductVersionMS; /* e.g. 0x00030010 = "3.10" */
  3533. LONG dwProductVersionLS; /* e.g. 0x00000031 = "0.31" */
  3534. LONG dwFileFlagsMask; /* = 0x3F for version "0.42" */
  3535. LONG dwFileFlags; /* e.g. VFF_DEBUG | VFF_PRERELEASE */
  3536. LONG dwFileOS; /* e.g. VOS_DOS_WINDOWS16 */
  3537. LONG dwFileType; /* e.g. VFT_DRIVER */
  3538. LONG dwFileSubtype; /* e.g. VFT2_DRV_KEYBOARD */
  3539. LONG dwFileDateMS; /* e.g. 0 */
  3540. LONG dwFileDateLS; /* e.g. 0 */
  3541. } VS_FIXEDFILEINFO;
  3542. struct
  3543. {
  3544. USHORT TotalSize;
  3545. USHORT DataSize;
  3546. USHORT Type;
  3547. WCHAR Name[16]; // L"VS_VERSION_INFO" + unicode null terminator
  3548. // Note that the previous 4 members has 16*2 + 3*2 = 38 bytes.
  3549. // So that compiler will silently add a 2 bytes padding to make
  3550. // FixedFileInfo to align in DWORD boundary.
  3551. VS_FIXEDFILEINFO FixedFileInfo;
  3552. } *Resource;
  3553. typedef struct tagVERBLOCK
  3554. {
  3555. USHORT wTotalLen;
  3556. USHORT wValueLen;
  3557. USHORT wType;
  3558. WCHAR szKey[1];
  3559. // BYTE[] padding
  3560. // WORD value;
  3561. } VERBLOCK;
  3562. VERBLOCK *pVerBlock;
  3563. //
  3564. // We look for Module first with UILangID, then with Netral lang ID. this can cover our current scenario of searching
  3565. // If we want to look for more language version, we can give 0 for a lang ID and we change the LdrpGetFileVersion(cancel
  3566. // LDR_FIND_RESOURCE_LANGUAGE_EXACT)
  3567. //
  3568. if(!LdrpGetFileVersion(Module, UILangId, &version, &Resource, &ResourceSize))
  3569. {
  3570. if(!LdrpGetFileVersion(Module, MUI_NEUTRAL_LANGID, &version, &Resource, &ResourceSize))
  3571. {
  3572. return FALSE;
  3573. }
  3574. }
  3575. ResourceSize -= DWORD_ALIGNMENT(sizeof(*Resource));
  3576. //
  3577. // Get the beginning address of the children of the version information.
  3578. //
  3579. pVerBlock = (VERBLOCK*)(Resource + 1);
  3580. while ((LONG)ResourceSize > 0)
  3581. {
  3582. if (wcscmp(pVerBlock->szKey, L"VarFileInfo") == 0)
  3583. {
  3584. //
  3585. // Find VarFileInfo block. Search the ResourceChecksum block.
  3586. //
  3587. VarFileInfoSize = pVerBlock->wTotalLen;
  3588. BlockLen =DWORD_ALIGNMENT(sizeof(*pVerBlock) -1 + sizeof(L"VarFileInfo"));
  3589. VarFileInfoSize -= BlockLen;
  3590. pVerBlock = (VERBLOCK*)((unsigned char*)pVerBlock + BlockLen);
  3591. while (VarFileInfoSize > 0)
  3592. {
  3593. if (wcscmp(pVerBlock->szKey, L"ResourceChecksum") == 0)
  3594. {
  3595. *ppMD5Checksum = (unsigned char*)DWORD_ALIGNMENT((UINT_PTR)(pVerBlock->szKey) + sizeof(L"ResourceChecksum"));
  3596. return (TRUE);
  3597. }
  3598. BlockLen = DWORD_ALIGNMENT(pVerBlock->wTotalLen);
  3599. pVerBlock = (VERBLOCK*)((unsigned char*)pVerBlock + BlockLen);
  3600. VarFileInfoSize -= BlockLen;
  3601. }
  3602. return (FALSE);
  3603. }
  3604. BlockLen = DWORD_ALIGNMENT(pVerBlock->wTotalLen);
  3605. pVerBlock = (VERBLOCK*)((unsigned char*)pVerBlock + BlockLen);
  3606. ResourceSize -= BlockLen;
  3607. }
  3608. return (FALSE);
  3609. }
  3610. BOOLEAN
  3611. LdrpCalcResourceChecksum(
  3612. IN PVOID Module,
  3613. IN PVOID AlternateModule,
  3614. OUT unsigned char* MD5Checksum
  3615. )
  3616. /*++
  3617. Rountine Description:
  3618. Enumerate resources in the specified module, and generate a MD5 checksum.
  3619. Calculate the checksum only on the based of resource types contained AlternateModule so that
  3620. checksum won't change if unlocalized resource types are changed.
  3621. --*/
  3622. {
  3623. // The top resource directory.
  3624. PIMAGE_RESOURCE_DIRECTORY TopDirectory;
  3625. PIMAGE_RESOURCE_DIRECTORY AltTopDirectory;
  3626. // The resource type directory.
  3627. PIMAGE_RESOURCE_DIRECTORY TypeDirectory;
  3628. // The resource name directory.
  3629. PIMAGE_RESOURCE_DIRECTORY NameDirectory;
  3630. // The resource language directory.
  3631. PIMAGE_RESOURCE_DIRECTORY LangDirectory;
  3632. PIMAGE_RESOURCE_DIRECTORY_ENTRY TypeDirectoryEntry;
  3633. PIMAGE_RESOURCE_DIRECTORY_ENTRY NameDirectoryEntry;
  3634. PIMAGE_RESOURCE_DIRECTORY_ENTRY LangDirectoryEntry;
  3635. PIMAGE_RESOURCE_DIRECTORY_ENTRY AltTypeDirectoryEntry;
  3636. PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry;
  3637. ULONG Size;
  3638. ULONG NumTypeDirectoryEntries;
  3639. ULONG NumNameDirectoryEntries;
  3640. ULONG NumLangDirectoryEntries;
  3641. ULONG AltNumTypeDirectoryEntries;
  3642. PVOID ResourceData;
  3643. ULONG ResourceSize;
  3644. ULONG i, j, k, altI;
  3645. PIMAGE_RESOURCE_DIR_STRING_U ResourceString_U;
  3646. WCHAR ResourceStringName[260] ;
  3647. BOOLEAN fIsTypeFound;
  3648. LANGID ChecksumLangID;
  3649. ULONGLONG Version;
  3650. try
  3651. {
  3652. MD5_CTX ChecksumContext;
  3653. MD5Init(&ChecksumContext);
  3654. //
  3655. // we specify the langauge ID for checksum calculation.
  3656. // First, we search with InstallLangID, If it succeed, InsallID will be used, if not, English used.
  3657. // InatallLangID is already set in LdrAlternateResourcesEnabled and LdrpVerifyAlternateResourceModule.
  3658. //
  3659. ChecksumLangID = MUI_NEUTRAL_LANGID;
  3660. if (InstallLangId != MUI_NEUTRAL_LANGID)
  3661. {
  3662. if (LdrpGetFileVersion(Module, InstallLangId, &Version, NULL, NULL))
  3663. {
  3664. ChecksumLangID = InstallLangId;
  3665. }
  3666. }
  3667. //
  3668. //We first get the Resource Type entry point for AlternateModule, which will be compared Module's resource types.
  3669. //
  3670. AltTopDirectory = (PIMAGE_RESOURCE_DIRECTORY)
  3671. RtlImageDirectoryEntryToData( AlternateModule,
  3672. TRUE,
  3673. IMAGE_DIRECTORY_ENTRY_RESOURCE,
  3674. &Size
  3675. );
  3676. if (!AltTopDirectory)
  3677. {
  3678. return (FALSE);
  3679. }
  3680. AltTypeDirectoryEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(AltTopDirectory+1);
  3681. AltNumTypeDirectoryEntries = AltTopDirectory->NumberOfNamedEntries +
  3682. AltTopDirectory->NumberOfIdEntries;
  3683. //
  3684. // TopDirectory is our reference point to directory offsets.
  3685. //
  3686. TopDirectory = (PIMAGE_RESOURCE_DIRECTORY)
  3687. RtlImageDirectoryEntryToData( Module,
  3688. TRUE,
  3689. IMAGE_DIRECTORY_ENTRY_RESOURCE,
  3690. &Size
  3691. );
  3692. if (!TopDirectory)
  3693. {
  3694. return (FALSE);
  3695. }
  3696. //
  3697. // Point to the children of the TopResourceDirecotry.
  3698. // This is the beginning of the type resource directory.
  3699. //
  3700. TypeDirectoryEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(TopDirectory+1);
  3701. //
  3702. // Get the total number of the types (named resource types + ID resource types)
  3703. //
  3704. NumTypeDirectoryEntries = TopDirectory->NumberOfNamedEntries +
  3705. TopDirectory->NumberOfIdEntries;
  3706. for (i=0; i<NumTypeDirectoryEntries; i++, TypeDirectoryEntry++)
  3707. {
  3708. if (!TypeDirectoryEntry->NameIsString)
  3709. {
  3710. // If the directory type is an ID, check if this is a version info.
  3711. if (TypeDirectoryEntry->Id == RT_VERSION)
  3712. {
  3713. //
  3714. // If this is a version info, just skip it.
  3715. // When calculation checksum for resources, version info should not be
  3716. // included, since they will always be updated when a new version
  3717. // of the file is created.
  3718. //
  3719. continue;
  3720. }
  3721. }
  3722. else
  3723. {
  3724. //
  3725. // when name is string, we need to create new string terminated by zero so we can compare this string
  3726. // with AlternateMoudule's resource types by using LdrpCompareResourceNames_U.
  3727. // the ResourceString_U->Length is not terminated by zero so we use local WCHAR array.
  3728. //
  3729. ResourceString_U = (PIMAGE_RESOURCE_DIR_STRING_U)
  3730. ((PCHAR)TopDirectory + TypeDirectoryEntry->NameOffset);
  3731. if (ResourceString_U->Length < sizeof(ResourceStringName)/sizeof(ResourceStringName[0]) )
  3732. {
  3733. memcpy(ResourceStringName, ResourceString_U->NameString, ResourceString_U->Length* sizeof(WCHAR) );
  3734. ResourceStringName[ResourceString_U->Length] = UNICODE_NULL;
  3735. }
  3736. else
  3737. { //
  3738. //resource string lengh is over maximum resource string length of checksum calculation.
  3739. //the lenght is set by checksum creating tools, not a sdk doc.
  3740. //
  3741. continue;
  3742. }
  3743. }
  3744. for (altI=0; altI <AltNumTypeDirectoryEntries; altI++, AltTypeDirectoryEntry++)
  3745. {
  3746. if(TypeDirectoryEntry->NameIsString)
  3747. {
  3748. fIsTypeFound = LdrpCompareResourceNames_U((ULONG_PTR)ResourceStringName, AltTopDirectory,
  3749. AltTypeDirectoryEntry) == 0;
  3750. }
  3751. else
  3752. {
  3753. fIsTypeFound = LdrpCompareResourceNames_U(TypeDirectoryEntry->Id, AltTopDirectory,
  3754. AltTypeDirectoryEntry) == 0;
  3755. }
  3756. if(fIsTypeFound)
  3757. {
  3758. // resource type in Module is in the AlternateModule.
  3759. break;
  3760. }
  3761. }
  3762. // AltTypeDirectoryEntry -= altI; // this is same with below, but use below for readibility.
  3763. AltTypeDirectoryEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(AltTopDirectory+1);
  3764. // resource type in Module is not in the AlternateModule.
  3765. if (altI >= AltNumTypeDirectoryEntries)
  3766. {
  3767. continue;
  3768. }
  3769. NameDirectory = (PIMAGE_RESOURCE_DIRECTORY)
  3770. ((PCHAR)TopDirectory + TypeDirectoryEntry->OffsetToDirectory);
  3771. //
  3772. // Point to the children of this TypeResourceDirecotry.
  3773. // This will be the beginning of the name resource directory.
  3774. //
  3775. NameDirectoryEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(NameDirectory+1);
  3776. //
  3777. // Get the total number of the names for the specified type (named resource + ID resource)
  3778. //
  3779. NumNameDirectoryEntries = NameDirectory->NumberOfNamedEntries +
  3780. NameDirectory->NumberOfIdEntries;
  3781. for (j=0; j<NumNameDirectoryEntries; j++, NameDirectoryEntry++ )
  3782. {
  3783. LangDirectory = (PIMAGE_RESOURCE_DIRECTORY)
  3784. ((PCHAR)TopDirectory + NameDirectoryEntry->OffsetToDirectory);
  3785. LangDirectoryEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(LangDirectory+1);
  3786. NumLangDirectoryEntries = LangDirectory->NumberOfNamedEntries +
  3787. LangDirectory->NumberOfIdEntries;
  3788. for (k=0; k<NumLangDirectoryEntries; k++, LangDirectoryEntry++)
  3789. {
  3790. NTSTATUS Status;
  3791. if (LangDirectoryEntry->Id != ChecksumLangID)
  3792. {
  3793. //
  3794. // we calculate the resource checksum (1) All Resource && (2) English &&(3) Except Version.
  3795. //
  3796. continue;
  3797. }
  3798. ResourceDataEntry = (PIMAGE_RESOURCE_DATA_ENTRY)
  3799. ((PCHAR)TopDirectory + LangDirectoryEntry->OffsetToData);
  3800. Status = LdrpAccessResourceDataNoMultipleLanguage(
  3801. Module,
  3802. (const PIMAGE_RESOURCE_DATA_ENTRY)ResourceDataEntry,
  3803. &ResourceData,
  3804. &ResourceSize
  3805. );
  3806. if (!NT_SUCCESS(Status))
  3807. {
  3808. return (FALSE);
  3809. }
  3810. MD5Update(&ChecksumContext, (unsigned char*)ResourceData, ResourceSize);
  3811. }
  3812. }
  3813. }
  3814. MD5Final(&ChecksumContext);
  3815. memcpy(MD5Checksum, ChecksumContext.digest, RESOURCE_CHECKSUM_SIZE);
  3816. } except (EXCEPTION_EXECUTE_HANDLER)
  3817. {
  3818. return (FALSE);
  3819. }
  3820. return (TRUE);
  3821. }
  3822. BOOLEAN
  3823. LdrpGetRegValueKey(
  3824. IN HANDLE Handle,
  3825. IN LPWSTR KeyValueName,
  3826. IN ULONG KeyValueType,
  3827. OUT PVOID Buffer,
  3828. IN ULONG BufferSize)
  3829. /*++
  3830. Routine Description:
  3831. This function returns the the registry key value for MUI versioning.
  3832. Arguments:
  3833. Handle - Supplies a handle to the registry which contains MUI versioning
  3834. information.
  3835. KeyValueName - the key name. The values are used to retreive original versiong,
  3836. working version and MUI version.
  3837. KeyValueType - the type of the key value.
  3838. Buffer - pointer to a variable that will receive the retrieved information.
  3839. BufferSize - The size of the buffer.
  3840. Return Value:
  3841. False if the query of the registry fails.
  3842. --*/
  3843. {
  3844. NTSTATUS Status;
  3845. UNICODE_STRING KeyValueString;
  3846. CHAR KeyValueBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 128 * sizeof(WCHAR)];
  3847. PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
  3848. ULONG ResultLength;
  3849. RtlInitUnicodeString(&KeyValueString, KeyValueName);
  3850. KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION)KeyValueBuffer;
  3851. Status = NtQueryValueKey( Handle,
  3852. &KeyValueString,
  3853. KeyValuePartialInformation,
  3854. KeyValueInformation,
  3855. sizeof( KeyValueBuffer ),
  3856. &ResultLength
  3857. );
  3858. if (!NT_SUCCESS(Status) || KeyValueInformation->Type != KeyValueType)
  3859. {
  3860. return (FALSE);
  3861. }
  3862. memcpy(Buffer, KeyValueInformation->Data, BufferSize);
  3863. return (TRUE);
  3864. }
  3865. NTSTATUS
  3866. LdrpCreateKey(
  3867. IN PUNICODE_STRING KeyName,
  3868. IN HANDLE ParentHandle,
  3869. OUT PHANDLE ChildHandle
  3870. )
  3871. /*++
  3872. Routine Description:
  3873. Creates a registry key for writting.
  3874. This is a thin wrapper over NtCreateKey().
  3875. Arguments:
  3876. KeyName - Name of the key to create
  3877. ParentHandle - Handle of parent key
  3878. ChildHandle - Pointer to where the handle is returned
  3879. Return Value:
  3880. Status of create/open
  3881. --*/
  3882. {
  3883. NTSTATUS status;
  3884. OBJECT_ATTRIBUTES objectAttributes;
  3885. //
  3886. // Initialize the OBJECT Attributes to a known value
  3887. //
  3888. InitializeObjectAttributes(
  3889. &objectAttributes,
  3890. KeyName,
  3891. OBJ_CASE_INSENSITIVE,
  3892. ParentHandle,
  3893. NULL
  3894. );
  3895. //
  3896. // Create the key here
  3897. //
  3898. *ChildHandle = 0;
  3899. status = NtCreateKey(
  3900. ChildHandle,
  3901. KEY_READ | KEY_WRITE,
  3902. &objectAttributes,
  3903. 0,
  3904. NULL,
  3905. REG_OPTION_NON_VOLATILE,
  3906. NULL
  3907. );
  3908. return (status);
  3909. }
  3910. NTSTATUS
  3911. LdrpOpenKey(
  3912. IN PUNICODE_STRING KeyName,
  3913. IN HANDLE ParentHandle,
  3914. OUT PHANDLE ChildHandle
  3915. )
  3916. /*++
  3917. Routine Description:
  3918. Open a registry key. This is a thin wrapper of NtOpenKey().
  3919. Arguments:
  3920. KeyName - Name of the key to create
  3921. ParentHandle - Handle of parent key
  3922. ChildHandle - Pointer to where the handle is returned
  3923. Return Value:
  3924. Status of open registry.
  3925. --*/
  3926. {
  3927. NTSTATUS status;
  3928. OBJECT_ATTRIBUTES objectAttributes;
  3929. //
  3930. // Initialize the OBJECT Attributes to a known value
  3931. //
  3932. InitializeObjectAttributes(
  3933. &objectAttributes,
  3934. KeyName,
  3935. OBJ_CASE_INSENSITIVE,
  3936. ParentHandle,
  3937. NULL
  3938. );
  3939. //
  3940. // Create the key here
  3941. //
  3942. *ChildHandle = 0;
  3943. status = NtOpenKey(ChildHandle, KEY_ALL_ACCESS, &objectAttributes);
  3944. return (status);
  3945. }
  3946. BOOLEAN
  3947. LdrpOpenFileVersionKey(
  3948. IN LPWSTR LangID,
  3949. IN LPWSTR BaseDllName,
  3950. IN ULONGLONG AltModuleVersion,
  3951. IN LPWSTR AltModuleVersionStr,
  3952. OUT PHANDLE pHandle)
  3953. /*++
  3954. Routine Description:
  3955. Open the registry key which contains the versioning information for the specified alternate resource module.
  3956. Arguments:
  3957. LangID - The UI langauge of the resource.
  3958. BaseDllName - The name of the base DLL.
  3959. AltModulePath - The full path of the alternate resource module.
  3960. pHandle - The registry key which stores the version information for this alternate resource module
  3961. Return Value:
  3962. Return TRUE if succeeds in opening/creating the key. Otherwise return FALSE.
  3963. --*/
  3964. {
  3965. BOOLEAN Result = FALSE;
  3966. HANDLE NlsHandle = NULL, MuiHandle = NULL, VersionHandle = NULL, LangHandle = NULL, DllKeyHandle = NULL;
  3967. UNICODE_STRING BufferString;
  3968. NTSTATUS Status;
  3969. PKEY_BASIC_INFORMATION KeyInfo;
  3970. ULONG ResultLength, Index;
  3971. CHAR ValueBuffer[sizeof(KEY_BASIC_INFORMATION) + 32];
  3972. WCHAR buffer[32]; // Temp string buffer.
  3973. ULONGLONG CachedAlternateVersion;
  3974. CHAR KeyFullInfoBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION ) + DOS_MAX_PATH_LENGTH * sizeof(WCHAR)];
  3975. PKEY_VALUE_PARTIAL_INFORMATION KeyFullInfo = (PKEY_VALUE_PARTIAL_INFORMATION )KeyFullInfoBuffer;
  3976. ULONG ChecksumDisabled;
  3977. HANDLE UserKeyHandle; // HKEY_CURRENT_USER equivalent
  3978. ULONG rc;
  3979. *pHandle = NULL;
  3980. rc = RtlOpenCurrentUser(MAXIMUM_ALLOWED, &UserKeyHandle);
  3981. if (!NT_SUCCESS(rc))
  3982. {
  3983. return (FALSE);
  3984. }
  3985. // Open registry REG_MUI_PATH
  3986. //
  3987. RtlInitUnicodeString(&BufferString, REG_MUI_PATH);
  3988. if (!NT_SUCCESS(LdrpCreateKey(&BufferString, UserKeyHandle, &NlsHandle)))
  3989. {
  3990. goto Exit;
  3991. }
  3992. //
  3993. // Open/Create registry in REG_MUI_PATH\MUILanguages
  3994. //
  3995. RtlInitUnicodeString(&BufferString, MUI_MUILANGUAGES_KEY_NAME);
  3996. if (!NT_SUCCESS(LdrpCreateKey(&BufferString, NlsHandle, &MuiHandle)))
  3997. {
  3998. goto Exit;
  3999. }
  4000. //
  4001. // Open/Create REG_MUI_PATH\MUILanguages\FileVersions
  4002. //
  4003. RtlInitUnicodeString(&BufferString, MUI_FILE_VERSION_KEY_NAME);
  4004. if (!NT_SUCCESS(LdrpCreateKey(&BufferString, MuiHandle, &VersionHandle)))
  4005. {
  4006. goto Exit;
  4007. }
  4008. if (LdrpGetRegValueKey(VersionHandle, MUI_RC_CHECKSUM_DISABLE_KEY, REG_DWORD, &ChecksumDisabled, sizeof(ChecksumDisabled)) &&
  4009. ChecksumDisabled == 1)
  4010. {
  4011. goto Exit;
  4012. }
  4013. //
  4014. // Open/Create "\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Nls\\MUILanguages\\FileVersions\\<LangID>"
  4015. //
  4016. RtlInitUnicodeString(&BufferString, LangID);
  4017. if (!NT_SUCCESS(LdrpCreateKey(&BufferString, VersionHandle, &LangHandle)))
  4018. {
  4019. goto Exit;
  4020. }
  4021. //
  4022. // Open/Create "\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Nls\\MUILanguages\\FileVersions\\<LandID>\\<Name of DLL>"
  4023. //
  4024. RtlInitUnicodeString(&BufferString, BaseDllName);
  4025. if (!NT_SUCCESS(LdrpCreateKey(&BufferString, LangHandle, &DllKeyHandle)))
  4026. {
  4027. goto Exit;
  4028. }
  4029. if (!LdrpGetRegValueKey(DllKeyHandle, MUI_ALTERNATE_VERSION_KEY, REG_QWORD, &CachedAlternateVersion, sizeof(CachedAlternateVersion)))
  4030. {
  4031. RtlInitUnicodeString(&BufferString, MUI_ALTERNATE_VERSION_KEY);
  4032. Result = NT_SUCCESS(NtSetValueKey(DllKeyHandle, &BufferString, 0, REG_QWORD, &AltModuleVersion, sizeof(AltModuleVersion)));
  4033. if (Result)
  4034. {
  4035. *pHandle = DllKeyHandle;
  4036. }
  4037. goto Exit;
  4038. }
  4039. if (CachedAlternateVersion == AltModuleVersion)
  4040. {
  4041. *pHandle = DllKeyHandle;
  4042. Result = TRUE;
  4043. } else
  4044. {
  4045. //
  4046. // Open/Create "\Registry\Machine\System\CurrentControlSet\Control\Nls\MUILanguages\FileVersions
  4047. // \<LandID>\<Name of DLL>\<AltVersionStr>"
  4048. //
  4049. RtlInitUnicodeString(&BufferString, AltModuleVersionStr);
  4050. Result = NT_SUCCESS(LdrpCreateKey(&BufferString, DllKeyHandle, pHandle));
  4051. }
  4052. Exit:
  4053. if (UserKeyHandle) {NtClose(UserKeyHandle);}
  4054. if (NlsHandle) {NtClose(NlsHandle);}
  4055. if (MuiHandle) {NtClose(MuiHandle);}
  4056. if (VersionHandle) {NtClose(VersionHandle);}
  4057. if (LangHandle) {NtClose(LangHandle);}
  4058. // If DllKeyHandle is the handle that we are going to return,
  4059. // we can not close it.
  4060. if (DllKeyHandle && *pHandle != DllKeyHandle)
  4061. {
  4062. NtClose(DllKeyHandle);
  4063. }
  4064. return (Result);
  4065. }
  4066. VOID
  4067. LdrpConvertVersionString(
  4068. IN ULONGLONG ModuleVersion,
  4069. OUT LPWSTR ModuleVersionStr
  4070. )
  4071. /*++
  4072. Routine Description:
  4073. Convert a 64-bit version information into a Unicode string.
  4074. Arguments:
  4075. ModuleVersion - The 64-b8t version information.
  4076. ModuleVersionStr - The converted string.
  4077. Return Value:
  4078. None.
  4079. --*/
  4080. {
  4081. LPWSTR StringStart = ModuleVersionStr;
  4082. WCHAR digit;
  4083. // Put the null-terminated char at the end of the converted string.
  4084. ModuleVersionStr[16] = L'\0';
  4085. ModuleVersionStr += 15;
  4086. while (ModuleVersionStr >= StringStart)
  4087. {
  4088. digit = (WCHAR)(ModuleVersion & (ULONGLONG)0xf);
  4089. *ModuleVersionStr-- = (digit < 10 ? digit + '0' : (digit - 10) + 'a');
  4090. ModuleVersion >>= 4;
  4091. }
  4092. }
  4093. BOOLEAN
  4094. LdrpCompareResourceChecksum(
  4095. IN LPWSTR LangID,
  4096. IN PVOID Module,
  4097. IN ULONGLONG ModuleVersion,
  4098. IN PVOID AlternateModule,
  4099. IN ULONGLONG AltModuleVersion,
  4100. IN LPWSTR BaseDllName
  4101. )
  4102. /*++
  4103. Routine Description:
  4104. In the case that the version for the original module is different from that
  4105. of the alternate module, check if the alternate module can still be used
  4106. for the original version.
  4107. First, the function will look at the registry to see if there is information
  4108. cached for the module.
  4109. In the case that the information is not cached for this module,
  4110. this function will retrieve the MD5 resource checksum from the alternate
  4111. resource module. And then check if the MD5 resource checksum is embeded
  4112. in the original module. If MD5 resource checksum is not in the original
  4113. moduel, it will enumerate all resources in the module to calculate the
  4114. MD5 checksum.
  4115. Arguments:
  4116. LangID - Supplies a language of the resource to be loaded.
  4117. Module - The original module.
  4118. ModuleVersion - The version for the original version.
  4119. AlternateModule - The alternate module.
  4120. AltModuleVersion - The version for the alternate module.
  4121. BaseDllName - The name of the DLL.
  4122. Return Value:
  4123. Ture if the alternate module can be used.
  4124. Otherwise, return false.
  4125. --*/
  4126. {
  4127. // Flag to indicate if the alternate resource can be used for this module.
  4128. ULONG UseAlternateResource = 0;
  4129. unsigned char* ModuleChecksum; // The 128-bit MD5 resource checksum for the module.
  4130. unsigned char CalculatedModuleChecksum[RESOURCE_CHECKSUM_SIZE]; // The calculated 128-bit MD5 resource checksum for the module.
  4131. unsigned char* AlternateModuleChecksum; // The 128-bit MD5 resource checksum embeded in the alternate module.
  4132. WCHAR ModuleVersionStr[17]; // The string for the 16 heximal digit version.
  4133. WCHAR AltModuleVersionStr[17];
  4134. HANDLE Handle = NULL; // The registry which caches the information for this module.
  4135. // Flag to indicate if we have retrieved or calucated the MD5 resource checksum for the original module successfully.
  4136. BOOLEAN FoundModuleChecksum;
  4137. UNICODE_STRING BufferString;
  4138. //
  4139. // Check the cached information in the registry first.
  4140. //
  4141. LdrpConvertVersionString(AltModuleVersion, AltModuleVersionStr);
  4142. //
  4143. // Open the version information key under:
  4144. // HKCU\Control Panel\International\MUI\FileVersions\<LangID>\<BaseDllName>
  4145. //
  4146. if (LdrpOpenFileVersionKey(LangID, BaseDllName, AltModuleVersion, AltModuleVersionStr, &Handle))
  4147. {
  4148. LdrpConvertVersionString(ModuleVersion, ModuleVersionStr);
  4149. //
  4150. // Try to check if this module exists in version information.
  4151. // If yes, see if the AlternateModule can be used.
  4152. //
  4153. //
  4154. // Get the cached version information in the registry to see if the original module can re-use the alternative module.
  4155. //
  4156. if (LdrpGetRegValueKey(Handle, ModuleVersionStr, REG_DWORD, &UseAlternateResource, sizeof(UseAlternateResource)))
  4157. {
  4158. // Get the cached information. Let's bail and return the cached result in UseAlternativeResource.
  4159. goto exit;
  4160. }
  4161. }
  4162. //
  4163. // When we are here, we know that we either:
  4164. // 1. Can't open the registry key which cached the information. Or
  4165. // 2. This file has never been looked before.
  4166. //
  4167. // Get the resource checksum for the alternate module.
  4168. //
  4169. if (LdrpGetResourceChecksum(AlternateModule, &AlternateModuleChecksum))
  4170. {
  4171. //
  4172. // First, check if the resource checksum is built in the module.
  4173. //
  4174. if (!(FoundModuleChecksum = LdrpGetResourceChecksum(Module, &ModuleChecksum))) {
  4175. //
  4176. // If not, calculate the resource checksum for the current module.
  4177. //
  4178. if (FoundModuleChecksum = LdrpCalcResourceChecksum(Module, AlternateModule, CalculatedModuleChecksum))
  4179. {
  4180. ModuleChecksum = CalculatedModuleChecksum;
  4181. }
  4182. }
  4183. if (FoundModuleChecksum)
  4184. {
  4185. if (memcmp(ModuleChecksum, AlternateModuleChecksum, RESOURCE_CHECKSUM_SIZE) == 0)
  4186. {
  4187. //
  4188. // If the checksums are equal, the working version is the module version.
  4189. //
  4190. UseAlternateResource = 1;
  4191. }
  4192. }
  4193. }
  4194. if (Handle != NULL) {
  4195. // If we find the version registry key successfully, cache the result in the registry.
  4196. //
  4197. // Write the working module information into registry.
  4198. //
  4199. RtlInitUnicodeString(&BufferString, ModuleVersionStr);
  4200. NtSetValueKey(Handle, &BufferString, 0, REG_DWORD, &UseAlternateResource, sizeof(UseAlternateResource));
  4201. }
  4202. exit:
  4203. if (Handle != NULL)
  4204. {
  4205. NtClose(Handle);
  4206. }
  4207. return ((BOOLEAN)(UseAlternateResource));
  4208. }
  4209. BOOLEAN
  4210. LdrpVerifyAlternateResourceModule(
  4211. IN PWSTR szLangIdDir,
  4212. IN PVOID Module,
  4213. IN PVOID AlternateModule,
  4214. IN LPWSTR BaseDllName,
  4215. IN LANGID LangId
  4216. )
  4217. /*++
  4218. Routine Description:
  4219. This function verifies if the alternate resource module has the same
  4220. version of the base module.
  4221. Arguments:
  4222. Module - The handle of the base module.
  4223. AlternateModule - The handle of the alternate resource module
  4224. BaseDllName - The file name of base DLL.
  4225. Return Value:
  4226. TBD.
  4227. --*/
  4228. {
  4229. ULONGLONG ModuleVersion;
  4230. ULONGLONG AltModuleVersion;
  4231. NTSTATUS Status;
  4232. int RetryCount =0;
  4233. LANGID newLangID;
  4234. LANGID preLangID =0;
  4235. if (!LangId) {
  4236. GET_UI_LANGID();
  4237. if (!UILangId){
  4238. return FALSE;
  4239. }
  4240. LangId = UILangId;
  4241. }
  4242. // we don't have reproces with other language ID when it fail.
  4243. if (!LdrpGetFileVersion(AlternateModule, LangId, &AltModuleVersion, NULL, NULL)){
  4244. //
  4245. // Some English language componet is not localized yet all, so we have to search
  4246. // Eng case. ( JPN, GER ... case ?.)
  4247. //
  4248. if (LangId == MUI_NEUTRAL_LANGID ||
  4249. !LdrpGetFileVersion(AlternateModule, MUI_NEUTRAL_LANGID, &AltModuleVersion, NULL, NULL) ){
  4250. return FALSE;
  4251. }
  4252. }
  4253. //
  4254. // when we install localized langneutral as first one, InstalllangID isn't not of code module resource.
  4255. // 0x409(eng) has more chance to be find because code module is Eng. anyway, GetFielVersion
  4256. // will search with language neutral (0) if it fail.
  4257. // 01/14/02; usiing InstalllangID instead of 0, we need to provide solution to localized application
  4258. // mui developer;their code can be same with UI language. REVIST
  4259. //
  4260. while (RetryCount < 3 ) {
  4261. switch(RetryCount) {
  4262. case 0:
  4263. newLangID = MUI_NEUTRAL_LANGID;
  4264. break;
  4265. case 1:
  4266. if (!InstallLangId){
  4267. Status = NtQueryInstallUILanguage( &InstallLangId);
  4268. if (!NT_SUCCESS( Status )) {
  4269. //
  4270. // Failed to get Intall LangID. AltResource not enabled.
  4271. //
  4272. return FALSE;
  4273. }
  4274. }
  4275. newLangID = InstallLangId;
  4276. break;
  4277. case 2:
  4278. if (MUI_NEUTRAL_LANGID != 0x409 ) {// just in case, MUI_NEUTRAL_LANGID isn't Eng.
  4279. newLangID = 0x409;
  4280. }
  4281. break;
  4282. }
  4283. if ( newLangID != preLangID) {
  4284. if (LdrpGetFileVersion(Module, newLangID, &ModuleVersion, NULL, NULL)){
  4285. break;
  4286. }
  4287. }
  4288. preLangID = newLangID;
  4289. RetryCount++;
  4290. }
  4291. if (RetryCount >= 3) {
  4292. return FALSE;
  4293. }
  4294. if (ModuleVersion == AltModuleVersion){
  4295. return TRUE;
  4296. }
  4297. else
  4298. {
  4299. #ifdef USE_RC_CHECKSUM
  4300. return (LdrpCompareResourceChecksum(szLangIdDir, Module, ModuleVersion, AlternateModule, AltModuleVersion, BaseDllName));
  4301. #else
  4302. return FALSE;
  4303. #endif
  4304. }
  4305. }
  4306. BOOLEAN
  4307. LdrpSetAlternateResourceModuleHandle(
  4308. IN PVOID Module,
  4309. IN PVOID AlternateModule,
  4310. IN LANGID LangId
  4311. )
  4312. /*++
  4313. Routine Description:
  4314. This function records the handle of the base module and alternate
  4315. resource module in an array.
  4316. Arguments:
  4317. Module - The handle of the base module.
  4318. AlternateModule - The handle of the alternate resource module
  4319. Return Value:
  4320. TBD.
  4321. --*/
  4322. {
  4323. PALT_RESOURCE_MODULE NewModules;
  4324. if (AlternateResourceModules == NULL){
  4325. //
  4326. // Allocate memory of initial size MEMBLOCKSIZE.
  4327. //
  4328. NewModules = (PALT_RESOURCE_MODULE)RtlAllocateHeap(
  4329. RtlProcessHeap(),
  4330. HEAP_ZERO_MEMORY,
  4331. RESMODSIZE * MEMBLOCKSIZE);
  4332. if (!NewModules){
  4333. return FALSE;
  4334. }
  4335. AlternateResourceModules = NewModules;
  4336. AltResMemBlockCount = MEMBLOCKSIZE;
  4337. }
  4338. else
  4339. if (AlternateResourceModuleCount >= AltResMemBlockCount ){
  4340. //
  4341. // ReAllocate another chunk of memory.
  4342. //
  4343. NewModules = (PALT_RESOURCE_MODULE)RtlReAllocateHeap(
  4344. RtlProcessHeap(),
  4345. 0,
  4346. AlternateResourceModules,
  4347. (AltResMemBlockCount + MEMBLOCKSIZE) * RESMODSIZE
  4348. );
  4349. if (!NewModules){
  4350. return FALSE;
  4351. }
  4352. AlternateResourceModules = NewModules;
  4353. AltResMemBlockCount += MEMBLOCKSIZE;
  4354. }
  4355. AlternateResourceModules[AlternateResourceModuleCount].ModuleBase = Module;
  4356. AlternateResourceModules[AlternateResourceModuleCount].AlternateModule = AlternateModule;
  4357. AlternateResourceModules[AlternateResourceModuleCount].LangId = LangId? LangId : UILangId;
  4358. AlternateResourceModuleCount++;
  4359. return TRUE;
  4360. }
  4361. PVOID
  4362. LdrLoadAlternateResourceModule(
  4363. IN PVOID Module,
  4364. IN LPCWSTR PathToAlternateModule OPTIONAL
  4365. )
  4366. /*++
  4367. Routine Description:
  4368. This function does the acutally loading into memory of the alternate
  4369. resource module, or loads from the table if it was loaded before.
  4370. Arguments:
  4371. Module - The handle of the base module.
  4372. PathToAlternateModule - Optional path from which module is being loaded.
  4373. Return Value:
  4374. Handle to the alternate resource module.
  4375. --*/
  4376. {
  4377. #ifdef MUI_MAGIC
  4378. return LdrpLoadAlternateResourceModule(0, Module, PathToAlternateModule);
  4379. #else
  4380. PVOID AlternateModule, DllBase;
  4381. PLDR_DATA_TABLE_ENTRY Entry;
  4382. HANDLE FileHandle, MappingHandle;
  4383. PIMAGE_NT_HEADERS NtHeaders;
  4384. NTSTATUS Status;
  4385. OBJECT_ATTRIBUTES ObjectAttributes;
  4386. UNICODE_STRING AltDllName;
  4387. PVOID FreeBuffer;
  4388. LPWSTR BaseDllName = NULL, p;
  4389. WCHAR DllPathName[DOS_MAX_PATH_LENGTH];
  4390. ULONG DllPathNameLength, BaseDllNameLength, CopyCount;
  4391. ULONG Digit;
  4392. int i, RetryCount;
  4393. WCHAR AltModulePath[DOS_MAX_PATH_LENGTH];
  4394. WCHAR AltModulePathMUI[DOS_MAX_PATH_LENGTH];
  4395. WCHAR AltModulePathFallback[DOS_MAX_PATH_LENGTH];
  4396. IO_STATUS_BLOCK IoStatusBlock;
  4397. RTL_RELATIVE_NAME_U RelativeName;
  4398. SIZE_T ViewSize;
  4399. LARGE_INTEGER SectionOffset;
  4400. WCHAR LangIdDir[6];
  4401. PVOID ReturnValue = NULL;
  4402. //
  4403. // The full path of the current MUI file that we are searching.
  4404. //
  4405. UNICODE_STRING CurrentAltModuleFile;
  4406. UNICODE_STRING SystemRoot;
  4407. //
  4408. // The current MUI folder that we are searching.
  4409. //
  4410. UNICODE_STRING CurrentAltModulePath;
  4411. WCHAR CurrentAltModulePathBuffer[DOS_MAX_PATH_LENGTH];
  4412. //
  4413. // The string contains the first MUI folder that we will search.
  4414. // This is the folder which lives under the folder of the base DLL.
  4415. // AltDllMUIPath = [the folder of the base DLL] + "\mui" + "\[UI Language]";
  4416. // E.g. if the base DLL is "c:\winnt\system32\ntdll.dll" and UI language is 0411,
  4417. // AltDllMUIPath will be "c:\winnt\system32\mui\0411\"
  4418. //
  4419. UNICODE_STRING AltDllMUIPath;
  4420. WCHAR AltDllMUIPathBuffer[DOS_MAX_PATH_LENGTH];
  4421. //
  4422. // MUI Redir
  4423. //
  4424. UNICODE_STRING BaseDllNameUstr;
  4425. UNICODE_STRING StaticStringAltModulePathRedirected;
  4426. UNICODE_STRING DynamicStringAltModulePathRedirected;
  4427. PUNICODE_STRING FullPathStringFoundAltModulePathRedirected = NULL;
  4428. BOOLEAN fRedirMUI = FALSE;
  4429. PVOID LockCookie = NULL;
  4430. // bail out early if this isn't a MUI-enabled system
  4431. if (!LdrAlternateResourcesEnabled()) {
  4432. return NULL;
  4433. }
  4434. LdrLockLoaderLock(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, NULL, &LockCookie);
  4435. __try {
  4436. //
  4437. // Look at the cache of the alternate module first.
  4438. //
  4439. AlternateModule = LdrpGetAlternateResourceModuleHandle(Module, 0);
  4440. if (AlternateModule == NO_ALTERNATE_RESOURCE_MODULE) {
  4441. //
  4442. // We tried to load this module before but failed. Don't try
  4443. // again in the future.
  4444. //
  4445. ReturnValue = NULL;
  4446. __leave;
  4447. } else if (AlternateModule > 0) {
  4448. //
  4449. // We found the previously loaded match
  4450. //
  4451. ReturnValue = AlternateModule;
  4452. __leave;
  4453. }
  4454. AlternateModule = NULL;
  4455. if (ARGUMENT_PRESENT(PathToAlternateModule)) {
  4456. //
  4457. // Caller suplied path.
  4458. //
  4459. p = wcsrchr(PathToAlternateModule, L'\\');
  4460. if (p == NULL)
  4461. goto error_exit;
  4462. p++;
  4463. DllPathNameLength = (ULONG)(p - PathToAlternateModule) * sizeof(WCHAR);
  4464. RtlCopyMemory(
  4465. DllPathName,
  4466. PathToAlternateModule,
  4467. DllPathNameLength);
  4468. BaseDllName = p;
  4469. BaseDllNameLength = wcslen(p);
  4470. } else {
  4471. //
  4472. // Try to get full dll path from Ldr data table.
  4473. //
  4474. Status = LdrFindEntryForAddress(Module, &Entry);
  4475. if (!NT_SUCCESS(Status))
  4476. goto error_exit;
  4477. DllPathNameLength = Entry->FullDllName.Length - Entry->BaseDllName.Length;
  4478. RtlCopyMemory(
  4479. DllPathName,
  4480. Entry->FullDllName.Buffer,
  4481. DllPathNameLength);
  4482. BaseDllName = Entry->BaseDllName.Buffer;
  4483. BaseDllNameLength = Entry->BaseDllName.Length;
  4484. }
  4485. DllPathName[DllPathNameLength / sizeof(WCHAR)] = UNICODE_NULL;
  4486. //
  4487. // dll redirection for the dll to be loaded xiaoyuw@10/31/2000
  4488. //
  4489. StaticStringAltModulePathRedirected.Buffer = AltModulePath; // reuse the array instead of define another array
  4490. StaticStringAltModulePathRedirected.Length = 0;
  4491. StaticStringAltModulePathRedirected.MaximumLength = sizeof(AltModulePath);
  4492. DynamicStringAltModulePathRedirected.Buffer = NULL;
  4493. DynamicStringAltModulePathRedirected.Length = 0;
  4494. DynamicStringAltModulePathRedirected.MaximumLength = 0;
  4495. BaseDllNameUstr.Buffer = AltModulePathMUI; // reuse the array instead of define another array
  4496. BaseDllNameUstr.Length = 0;
  4497. BaseDllNameUstr.MaximumLength = sizeof(AltModulePathMUI);
  4498. RtlAppendUnicodeToString(&BaseDllNameUstr, BaseDllName);
  4499. RtlAppendUnicodeToString(&BaseDllNameUstr, L".mui");
  4500. Status = RtlDosApplyFileIsolationRedirection_Ustr(
  4501. RTL_DOS_APPLY_FILE_REDIRECTION_USTR_FLAG_RESPECT_DOT_LOCAL,
  4502. &BaseDllNameUstr, NULL,
  4503. &StaticStringAltModulePathRedirected,
  4504. &DynamicStringAltModulePathRedirected,
  4505. &FullPathStringFoundAltModulePathRedirected,
  4506. NULL,NULL, NULL);
  4507. if (!NT_SUCCESS(Status)) // no redirection info found for this string
  4508. {
  4509. if (Status != STATUS_SXS_KEY_NOT_FOUND)
  4510. goto error_exit;
  4511. //
  4512. // Generate the langid directory like "0804\"
  4513. //
  4514. GET_UI_LANGID();
  4515. if (!UILangId){
  4516. goto error_exit;
  4517. }
  4518. CopyCount = 0;
  4519. for (i = 12; i >= 0; i -= 4) {
  4520. Digit = ((UILangId >> i) & 0xF);
  4521. if (Digit >= 10) {
  4522. LangIdDir[CopyCount++] = (WCHAR) (Digit - 10 + L'A');
  4523. } else {
  4524. LangIdDir[CopyCount++] = (WCHAR) (Digit + L'0');
  4525. }
  4526. }
  4527. LangIdDir[CopyCount++] = L'\\';
  4528. LangIdDir[CopyCount++] = UNICODE_NULL;
  4529. //
  4530. // Create the MUI path under the directory of the base DLL.
  4531. //
  4532. AltDllMUIPath.Buffer = AltDllMUIPathBuffer;
  4533. AltDllMUIPath.Length = 0;
  4534. AltDllMUIPath.MaximumLength = sizeof(AltDllMUIPathBuffer);
  4535. RtlAppendUnicodeToString(&AltDllMUIPath, DllPathName); // e.g. "c:\winnt\system32\"
  4536. RtlAppendUnicodeToString(&AltDllMUIPath, L"mui\\"); // e.g. "c:\winnt\system32\mui\"
  4537. RtlAppendUnicodeToString(&AltDllMUIPath, LangIdDir); // e.g. "c:\winnt\system32\mui\0411\"
  4538. CurrentAltModulePath.Buffer = CurrentAltModulePathBuffer;
  4539. CurrentAltModulePath.Length = 0;
  4540. CurrentAltModulePath.MaximumLength = sizeof(CurrentAltModulePathBuffer);
  4541. } else {
  4542. fRedirMUI = TRUE;
  4543. //set CurrentAltModuleFile and CurrentAltModulePath
  4544. CurrentAltModuleFile.Buffer = AltModulePathMUI;
  4545. CurrentAltModuleFile.Length = 0;
  4546. CurrentAltModuleFile.MaximumLength = sizeof(AltModulePathMUI);
  4547. RtlCopyUnicodeString(&CurrentAltModuleFile, FullPathStringFoundAltModulePathRedirected);
  4548. }
  4549. //
  4550. // Try name with .mui extesion first.
  4551. //
  4552. RetryCount = 0;
  4553. while (RetryCount < 3){
  4554. if ( ! fRedirMUI )
  4555. {
  4556. //
  4557. // Once MUI_MAGIC is enabled, we should optimize the search order for system MUI files
  4558. //
  4559. switch (RetryCount)
  4560. {
  4561. case 0:
  4562. //
  4563. // Generate the first path under the folder of the base DLL
  4564. // (e.g. c:\winnt\system32\mui\0804\ntdll.dll.mui)
  4565. //
  4566. CurrentAltModuleFile.Buffer = AltModulePathMUI;
  4567. CurrentAltModuleFile.Length = 0;
  4568. CurrentAltModuleFile.MaximumLength = sizeof(AltModulePathMUI);
  4569. RtlCopyUnicodeString(&CurrentAltModuleFile, &AltDllMUIPath); // e.g. "c:\winnt\system32\mui\0411\"
  4570. RtlCopyUnicodeString(&CurrentAltModulePath, &AltDllMUIPath);
  4571. RtlAppendUnicodeToString(&CurrentAltModuleFile, BaseDllName); // e.g. "c:\winnt\system32\mui\0411\ntdll.dll"
  4572. RtlAppendUnicodeToString(&CurrentAltModuleFile, L".mui"); // e.g. "c:\winnt\system32\mui\0411\ntdll.dll.mui"
  4573. break;
  4574. case 1:
  4575. //
  4576. // Generate the second path c:\winnt\system32\mui\0804\ntdll.dll
  4577. //
  4578. CurrentAltModuleFile.Buffer = AltModulePath;
  4579. CurrentAltModuleFile.Length = 0;
  4580. CurrentAltModuleFile.MaximumLength = sizeof(AltModulePath);
  4581. RtlCopyUnicodeString(&CurrentAltModuleFile, &AltDllMUIPath); // e.g. "c:\winnt\system32\mui\0411\"
  4582. RtlAppendUnicodeToString(&CurrentAltModuleFile, BaseDllName); // e.g. "c:\winnt\system32\mui\0411\ntdll.dll"
  4583. break;
  4584. case 2:
  4585. //
  4586. // Generate path c:\winnt\mui\fallback\0804\foo.exe.mui
  4587. //
  4588. CurrentAltModuleFile.Buffer = AltModulePathFallback;
  4589. CurrentAltModuleFile.Length = 0;
  4590. CurrentAltModuleFile.MaximumLength = sizeof(AltModulePathFallback);
  4591. RtlInitUnicodeString(&SystemRoot, USER_SHARED_DATA->NtSystemRoot); // e.g. "c:\winnt\system32\"
  4592. RtlAppendUnicodeStringToString(&CurrentAltModuleFile, &SystemRoot); // e.g. "c:\winnt\system32\"
  4593. RtlAppendUnicodeToString(&CurrentAltModuleFile, L"\\mui\\fallback\\"); // e.g. "c:\winnt\system32\mui\fallback\"
  4594. RtlAppendUnicodeToString(&CurrentAltModuleFile, LangIdDir); // e.g. "c:\winnt\system32\mui\fallback\0411\"
  4595. RtlCopyUnicodeString(&CurrentAltModulePath, &CurrentAltModuleFile);
  4596. RtlAppendUnicodeToString(&CurrentAltModuleFile, BaseDllName); // e.g. "c:\winnt\system32\mui\fallback\0411\ntdll.dll"
  4597. RtlAppendUnicodeToString(&CurrentAltModuleFile, L".mui"); // e.g. "c:\winnt\system32\mui\fallback\0411\ntdll.dll.mui"
  4598. break;
  4599. }
  4600. }
  4601. if (!RtlDosPathNameToRelativeNtPathName_U(
  4602. CurrentAltModuleFile.Buffer,
  4603. &AltDllName,
  4604. NULL,
  4605. &RelativeName)) {
  4606. goto error_exit;
  4607. }
  4608. FreeBuffer = AltDllName.Buffer;
  4609. if (RelativeName.RelativeName.Length != 0) {
  4610. AltDllName = RelativeName.RelativeName;
  4611. } else {
  4612. RelativeName.ContainingDirectory = NULL;
  4613. }
  4614. InitializeObjectAttributes(
  4615. &ObjectAttributes,
  4616. &AltDllName,
  4617. OBJ_CASE_INSENSITIVE,
  4618. RelativeName.ContainingDirectory,
  4619. NULL
  4620. );
  4621. Status = NtCreateFile(
  4622. &FileHandle,
  4623. (ACCESS_MASK) GENERIC_READ | SYNCHRONIZE | FILE_READ_ATTRIBUTES,
  4624. &ObjectAttributes,
  4625. &IoStatusBlock,
  4626. NULL,
  4627. 0L,
  4628. FILE_SHARE_READ | FILE_SHARE_DELETE,
  4629. FILE_OPEN,
  4630. 0L,
  4631. NULL,
  4632. 0L
  4633. );
  4634. RtlReleaseRelativeName(&RelativeName);
  4635. RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
  4636. if (NT_SUCCESS(Status)) {
  4637. goto CreateSection;
  4638. }
  4639. if (fRedirMUI) { // definitely failed
  4640. goto error_exit;
  4641. }
  4642. if (Status != STATUS_OBJECT_NAME_NOT_FOUND && RetryCount == 0) {
  4643. //
  4644. // Error other than the file name with .mui not found.
  4645. // Most likely directory is missing. Skip file name w/o .mui
  4646. // and goto fallback directory.
  4647. //
  4648. RetryCount++;
  4649. }
  4650. RetryCount++;
  4651. }
  4652. // No alternate resource was found during the iterations. Fail!
  4653. goto error_exit;
  4654. CreateSection:
  4655. Status = NtCreateSection(
  4656. &MappingHandle,
  4657. STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ,
  4658. NULL,
  4659. NULL,
  4660. PAGE_WRITECOPY,
  4661. SEC_COMMIT,
  4662. FileHandle
  4663. );
  4664. NtClose( FileHandle );
  4665. if (!NT_SUCCESS(Status)) {
  4666. goto error_exit;
  4667. }
  4668. SectionOffset.LowPart = 0;
  4669. SectionOffset.HighPart = 0;
  4670. ViewSize = 0;
  4671. DllBase = NULL;
  4672. Status = NtMapViewOfSection(
  4673. MappingHandle,
  4674. NtCurrentProcess(),
  4675. &DllBase,
  4676. 0L,
  4677. 0L,
  4678. &SectionOffset,
  4679. &ViewSize,
  4680. ViewShare,
  4681. 0L,
  4682. PAGE_WRITECOPY
  4683. );
  4684. NtClose(MappingHandle);
  4685. if (!NT_SUCCESS(Status)){
  4686. goto error_exit;
  4687. }
  4688. NtHeaders = RtlImageNtHeader(DllBase);
  4689. if (!NtHeaders) {
  4690. NtUnmapViewOfSection(NtCurrentProcess(), (PVOID) DllBase);
  4691. goto error_exit;
  4692. }
  4693. AlternateModule = LDR_VIEW_TO_DATAFILE(DllBase);
  4694. if(!LdrpVerifyAlternateResourceModule(LangIdDir, Module, AlternateModule, BaseDllName, 0)) {
  4695. NtUnmapViewOfSection(NtCurrentProcess(), (PVOID) DllBase);
  4696. goto error_exit;
  4697. }
  4698. LdrpSetAlternateResourceModuleHandle(Module, AlternateModule, 0);
  4699. ReturnValue = AlternateModule;
  4700. __leave;
  4701. error_exit:
  4702. if (BaseDllName != NULL) {
  4703. //
  4704. // If we looked for a MUI file and couldn't find one keep track. If
  4705. // we couldn't get the base dll name (e.g. someone passing in a
  4706. // mapped image with the low bit set but no path name), we don't want
  4707. // to "remember" that there's no MUI.
  4708. //
  4709. LdrpSetAlternateResourceModuleHandle(Module, NO_ALTERNATE_RESOURCE_MODULE, 0);
  4710. }
  4711. ReturnValue = NULL;
  4712. } __finally {
  4713. Status = LdrUnlockLoaderLock(LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, LockCookie);
  4714. }
  4715. return ReturnValue;
  4716. #endif
  4717. }
  4718. BOOLEAN
  4719. LdrUnloadAlternateResourceModule(
  4720. IN PVOID Module
  4721. )
  4722. /*++
  4723. Routine Description:
  4724. This function unmaps an alternate resource module from the process'
  4725. address space and updates alternate resource module table.
  4726. Arguments:
  4727. Module - handle of the base module.
  4728. Return Value:
  4729. TBD.
  4730. --*/
  4731. {
  4732. ULONG ModuleIndex;
  4733. PALT_RESOURCE_MODULE AltModule;
  4734. NTSTATUS Status;
  4735. PVOID LockCookie = NULL;
  4736. BOOLEAN ReturnValue = TRUE;
  4737. LdrLockLoaderLock(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, NULL, &LockCookie);
  4738. __try {
  4739. if (AlternateResourceModuleCount == 0) {
  4740. ReturnValue = TRUE;
  4741. __leave;
  4742. }
  4743. for (ModuleIndex = AlternateResourceModuleCount;
  4744. ModuleIndex > 0;
  4745. ModuleIndex--) {
  4746. if (AlternateResourceModules[ModuleIndex-1].ModuleBase == Module &&
  4747. AlternateResourceModules[ModuleIndex-1].LangId == UILangId) {
  4748. break;
  4749. }
  4750. }
  4751. if (ModuleIndex == 0) {
  4752. ReturnValue = FALSE;
  4753. __leave;
  4754. }
  4755. //
  4756. // Adjust to the actual index
  4757. //
  4758. ModuleIndex --;
  4759. AltModule = &AlternateResourceModules[ModuleIndex];
  4760. if (AltModule->AlternateModule != NO_ALTERNATE_RESOURCE_MODULE) {
  4761. #ifdef MUI_MAGIC
  4762. if ( AltModule->CMFModule != NULL) {
  4763. NtUnmapViewOfSection(NtCurrentProcess(), AltModule->CMFModule);
  4764. }
  4765. else
  4766. { // when MUI does not use CMF file, we just unmap AltModule.
  4767. #endif
  4768. NtUnmapViewOfSection(
  4769. NtCurrentProcess(),
  4770. LDR_DATAFILE_TO_VIEW(AltModule->AlternateModule));
  4771. #ifdef MUI_MAGIC
  4772. }
  4773. #endif
  4774. }
  4775. if (ModuleIndex != AlternateResourceModuleCount - 1) {
  4776. //
  4777. // Consolidate the array. Skip this if unloaded item
  4778. // is the last element.
  4779. //
  4780. RtlMoveMemory(
  4781. AltModule,
  4782. AltModule + 1,
  4783. (AlternateResourceModuleCount - ModuleIndex - 1) * RESMODSIZE);
  4784. }
  4785. AlternateResourceModuleCount--;
  4786. if (AlternateResourceModuleCount == 0){
  4787. RtlFreeHeap(
  4788. RtlProcessHeap(),
  4789. 0,
  4790. AlternateResourceModules
  4791. );
  4792. AlternateResourceModules = NULL;
  4793. AltResMemBlockCount = 0;
  4794. } else {
  4795. if (AlternateResourceModuleCount < AltResMemBlockCount - MEMBLOCKSIZE) {
  4796. AltModule = (PALT_RESOURCE_MODULE)RtlReAllocateHeap(
  4797. RtlProcessHeap(),
  4798. 0,
  4799. AlternateResourceModules,
  4800. (AltResMemBlockCount - MEMBLOCKSIZE) * RESMODSIZE);
  4801. if (!AltModule) {
  4802. ReturnValue = FALSE;
  4803. __leave;
  4804. }
  4805. AlternateResourceModules = AltModule;
  4806. AltResMemBlockCount -= MEMBLOCKSIZE;
  4807. }
  4808. }
  4809. } __finally {
  4810. LdrUnlockLoaderLock(LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, LockCookie);
  4811. }
  4812. return ReturnValue;
  4813. }
  4814. BOOLEAN
  4815. LdrFlushAlternateResourceModules(
  4816. VOID
  4817. )
  4818. /*++
  4819. Routine Description:
  4820. This function unmaps all the alternate resouce modules for the
  4821. process address space. This function would be used mainly by
  4822. CSRSS, and any sub-systems that are permanent during logon and
  4823. logoff.
  4824. Arguments:
  4825. None
  4826. Return Value:
  4827. TRUE : Successful
  4828. FALSE : Failed
  4829. --*/
  4830. {
  4831. ULONG ModuleIndex;
  4832. PALT_RESOURCE_MODULE AltModule;
  4833. NTSTATUS Status;
  4834. PVOID LockCookie = NULL;
  4835. //
  4836. // Grab the loader lock
  4837. //
  4838. Status = LdrLockLoaderLock(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, NULL, &LockCookie);
  4839. if (!NT_SUCCESS(Status)) {
  4840. // This function erroneously doesn't have any way to communicate failure statuses up so
  4841. // we're stuck with just returning false.
  4842. return FALSE;
  4843. }
  4844. __try {
  4845. if (AlternateResourceModuleCount > 0) {
  4846. //
  4847. // Let's unmap the alternate resource modules from the process
  4848. // address space
  4849. //
  4850. for (ModuleIndex=0;
  4851. ModuleIndex<AlternateResourceModuleCount;
  4852. ModuleIndex++) {
  4853. AltModule = &AlternateResourceModules[ModuleIndex];
  4854. if (AltModule->AlternateModule != NO_ALTERNATE_RESOURCE_MODULE) {
  4855. #ifdef MUI_MAGIC
  4856. if (AltModule->CMFModule)
  4857. NtUnmapViewOfSection(NtCurrentProcess(), AltModule->CMFModule);
  4858. else
  4859. #endif
  4860. NtUnmapViewOfSection(NtCurrentProcess(),
  4861. LDR_DATAFILE_TO_VIEW(AltModule->AlternateModule));
  4862. }
  4863. }
  4864. //
  4865. // Cleanup alternate resource modules memory
  4866. //
  4867. RtlFreeHeap(RtlProcessHeap(), 0, AlternateResourceModules);
  4868. AlternateResourceModules = NULL;
  4869. AlternateResourceModuleCount = 0;
  4870. AltResMemBlockCount = 0;
  4871. }
  4872. //
  4873. // Re-Initialize the UI language for the current process,
  4874. //
  4875. UILangId = 0;
  4876. } __finally {
  4877. LdrUnlockLoaderLock(LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, LockCookie);
  4878. }
  4879. return TRUE;
  4880. }
  4881. #endif