Windows2000/private/ntos/rtl/ldrrsrc.c
2020-09-30 17:12:32 +02:00

1712 lines
61 KiB
C

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
ldrrsrc.c
Abstract:
Loader API calls for accessing resource sections.
Author:
Steve Wood (stevewo) 16-Sep-1991
--*/
#include "ntrtlp.h"
#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
#pragma alloc_text(PAGE,LdrAccessResource)
#pragma alloc_text(PAGE,LdrpAccessResourceData)
#pragma alloc_text(PAGE,LdrFindEntryForAddress)
#pragma alloc_text(PAGE,LdrFindResource_U)
#pragma alloc_text(PAGE,LdrFindResourceDirectory_U)
#pragma alloc_text(PAGE,LdrpCompareResourceNames_U)
#pragma alloc_text(PAGE,LdrpSearchResourceSection_U)
#pragma alloc_text(PAGE,LdrEnumResources)
#endif
#ifndef NTOS_KERNEL_RUNTIME
PALT_RESOURCE_MODULE AlternateResourceModules;
ULONG AlternateResourceModuleCount;
ULONG AltResMemBlockCount;
LANGID UILangId, InstallLangId;
#define MEMBLOCKSIZE 16
#define RESMODSIZE sizeof(ALT_RESOURCE_MODULE)
#endif
NTSTATUS
LdrAccessResource(
IN PVOID DllHandle,
IN PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry,
OUT PVOID *Address OPTIONAL,
OUT PULONG Size OPTIONAL
)
/*++
Routine Description:
This function locates the address of the specified resource in the specified DLL and returns its address.
Arguments:
DllHandle - Supplies a handle to the image file that the resource is contained in.
ResourceDataEntry - Supplies a pointer to the resource data entry in
the resource data section of the image file specified by the
DllHandle parameter. This pointer should have been one returned
by the LdrFindResource function.
Address - Optional pointer to a variable that will receive the
address of the resource specified by the first two parameters.
Size - Optional pointer to a variable that will receive the size of
the resource specified by the first two parameters.
Return Value:
TBD
--*/
{
RTL_PAGED_CODE();
return LdrpAccessResourceData(
DllHandle,
ResourceDataEntry,
Address,
Size
);
}
NTSTATUS
LdrpAccessResourceData(
IN PVOID DllHandle,
IN PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry,
OUT PVOID *Address OPTIONAL,
OUT PULONG Size OPTIONAL
)
/*++
Routine Description:
This function returns the data necessary to actually examine the contents of a particular resource.
Arguments:
DllHandle - Supplies a handle to the image file that the resource is contained in.
ResourceDataEntry - Supplies a pointer to the resource data entry in
the resource data directory of the image file specified by the
DllHandle parameter. This pointer should have been one returned
by the LdrFindResource function.
Address - Optional pointer to a variable that will receive the address of the resource specified by the first two parameters.
Size - Optional pointer to a variable that will receive the size of the resource specified by the first two parameters.
Return Value:
TBD
--*/
{
PIMAGE_RESOURCE_DIRECTORY ResourceDirectory;
ULONG ResourceSize;
PIMAGE_NT_HEADERS NtHeaders;
ULONG_PTR VirtualAddressOffset;
PIMAGE_SECTION_HEADER NtSection;
RTL_PAGED_CODE();
#ifndef NTOS_KERNEL_RUNTIME
ResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
RtlImageDirectoryEntryToData(DllHandle,
TRUE,
IMAGE_DIRECTORY_ENTRY_RESOURCE,
&ResourceSize
);
if (!ResourceDirectory) {
return( STATUS_RESOURCE_DATA_NOT_FOUND );
}
if ((ULONG_PTR)ResourceDataEntry < (ULONG_PTR) ResourceDirectory ){
DllHandle = LdrLoadAlternateResourceModule (DllHandle, NULL);
} else{
NtHeaders = RtlImageNtHeader(
(PVOID)((ULONG_PTR)DllHandle & ~0x00000001)
);
if (NtHeaders) {
// Find the bounds of the image so we can see if this resource entry is in an alternate
// resource dll.
ULONG_PTR ImageStart = (ULONG_PTR)DllHandle & ~0x00000001;
SIZE_T ImageSize = 0;
if ((ULONG_PTR)DllHandle & 0x00000001) {
// mapped as datafile. Ask mm for the size
NTSTATUS Status;
MEMORY_BASIC_INFORMATION MemInfo;
Status = NtQueryVirtualMemory(NtCurrentProcess(), (PVOID) ImageStart, MemoryBasicInformation, (PVOID)&MemInfo, sizeof(MemInfo), NULL);
if ( !NT_SUCCESS(Status) ) {
ImageSize = 0;
} else {
ImageSize = MemInfo.RegionSize;
}
} else {
ImageSize = ((PIMAGE_NT_HEADERS32)NtHeaders)->OptionalHeader.SizeOfImage;
}
if (!(((ULONG_PTR)ResourceDataEntry >= ImageStart) && ((ULONG_PTR)ResourceDataEntry < (ImageStart + ImageSize)))) {
// Doesn't fall within the specified image. Must be an alternate dll.
DllHandle = LdrLoadAlternateResourceModule (DllHandle, NULL);
}
}
}
if (!DllHandle){
return ( STATUS_RESOURCE_DATA_NOT_FOUND );
}
#endif
try {
ResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
RtlImageDirectoryEntryToData(DllHandle,
TRUE,
IMAGE_DIRECTORY_ENTRY_RESOURCE,
&ResourceSize
);
if (!ResourceDirectory) {
return( STATUS_RESOURCE_DATA_NOT_FOUND );
}
if ((ULONG_PTR)DllHandle & 0x00000001) {
ULONG ResourceRVA;
DllHandle = (PVOID)((ULONG_PTR)DllHandle & ~0x00000001);
NtHeaders = RtlImageNtHeader( DllHandle );
if (NtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
ResourceRVA=((PIMAGE_NT_HEADERS32)NtHeaders)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_RESOURCE ].VirtualAddress;
} else if (NtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
ResourceRVA=((PIMAGE_NT_HEADERS64)NtHeaders)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_RESOURCE ].VirtualAddress;
} else {
ResourceRVA = 0;
}
if (!ResourceRVA) {
return( STATUS_RESOURCE_DATA_NOT_FOUND );
}
VirtualAddressOffset = (ULONG_PTR)DllHandle + ResourceRVA - (ULONG_PTR)ResourceDirectory;
// Now, we must check to see if the resource is not in the
// same section as the resource table. If it's in .rsrc1,
// we've got to adjust the RVA in the ResourceDataEntry
// to point to the correct place in the non-VA data file.
NtSection= RtlSectionTableFromVirtualAddress( NtHeaders, DllHandle, ResourceRVA);
if (!NtSection) {
return( STATUS_RESOURCE_DATA_NOT_FOUND );
}
if ( ResourceDataEntry->OffsetToData > NtSection->Misc.VirtualSize ) {
ULONG rva;
rva = NtSection->VirtualAddress;
NtSection= RtlSectionTableFromVirtualAddress(NtHeaders,
DllHandle,
ResourceDataEntry->OffsetToData
);
VirtualAddressOffset +=
((ULONG_PTR)NtSection->VirtualAddress - rva) -
((ULONG_PTR)RtlAddressInSectionTable ( NtHeaders, DllHandle, NtSection->VirtualAddress ) - (ULONG_PTR)ResourceDirectory);
}
}
else {
VirtualAddressOffset = 0;
}
if (ARGUMENT_PRESENT( Address )) {
*Address = (PVOID)( (PCHAR)DllHandle +
(ResourceDataEntry->OffsetToData - VirtualAddressOffset)
);
}
if (ARGUMENT_PRESENT( Size )) {
*Size = ResourceDataEntry->Size;
}
}
except (EXCEPTION_EXECUTE_HANDLER) {
return GetExceptionCode();
}
return( STATUS_SUCCESS );
}
NTSTATUS LdrFindEntryForAddress(IN PVOID Address, OUT PLDR_DATA_TABLE_ENTRY *TableEntry)
/*++
Routine Description:
This function returns the load data table entry that describes the virtual address range that contains the passed virtual address.
Arguments:
Address - Supplies a 32-bit virtual address.
TableEntry - Supplies a pointer to the variable that will receive the address of the loader data table entry.
Return Value:
Status
--*/
{
PPEB_LDR_DATA Ldr;
PLIST_ENTRY Head, Next;
PLDR_DATA_TABLE_ENTRY Entry;
PIMAGE_NT_HEADERS NtHeaders;
PVOID ImageBase;
PVOID EndOfImage;
Ldr = NtCurrentPeb()->Ldr;
if (Ldr == NULL) {
return( STATUS_NO_MORE_ENTRIES );
}
Head = &Ldr->InMemoryOrderModuleList;
Next = Head->Flink;
while ( Next != Head )
{
Entry = CONTAINING_RECORD( Next, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks );
NtHeaders = RtlImageNtHeader( Entry->DllBase );
if (NtHeaders != NULL) {
ImageBase = (PVOID)Entry->DllBase;
EndOfImage = (PVOID) ((ULONG_PTR)ImageBase + NtHeaders->OptionalHeader.SizeOfImage);
if ((ULONG_PTR)Address >= (ULONG_PTR)ImageBase && (ULONG_PTR)Address < (ULONG_PTR)EndOfImage) {
*TableEntry = Entry;
return( STATUS_SUCCESS );
}
}
Next = Next->Flink;
}
return( STATUS_NO_MORE_ENTRIES );
}
NTSTATUS
LdrFindResource_U(
IN PVOID DllHandle,
IN PULONG_PTR ResourceIdPath,
IN ULONG ResourceIdPathLength,
OUT PIMAGE_RESOURCE_DATA_ENTRY *ResourceDataEntry
)
/*++
Routine Description:
This function locates the address of the specified resource in the specified DLL and returns its address.
Arguments:
DllHandle - Supplies a handle to the image file that the resource is contained in.
ResourceIdPath - Supplies a pointer to an array of 32-bit resource
identifiers. Each identifier is either an integer or a pointer
to a STRING structure that specifies a resource name. The array
is used to traverse the directory structure contained in the
resource section in the image file specified by the DllHandle
parameter.
ResourceIdPathLength - Supplies the number of elements in the
ResourceIdPath array.
ResourceDataEntry - Supplies a pointer to a variable that will
receive the address of the resource data entry in the resource
data section of the image file specified by the DllHandle parameter.
Return Value:
TBD
--*/
{
RTL_PAGED_CODE();
return LdrpSearchResourceSection_U(
DllHandle,
ResourceIdPath,
ResourceIdPathLength,
FALSE, // Look for a leaf node
FALSE,
(PVOID *)ResourceDataEntry
);
}
NTSTATUS
LdrFindResourceDirectory_U(
IN PVOID DllHandle,
IN PULONG_PTR ResourceIdPath,
IN ULONG ResourceIdPathLength,
OUT PIMAGE_RESOURCE_DIRECTORY *ResourceDirectory
)
/*++
Routine Description:
This function locates the address of the specified resource directory in
specified DLL and returns its address.
Arguments:
DllHandle - Supplies a handle to the image file that the resource
directory is contained in.
ResourceIdPath - Supplies a pointer to an array of 32-bit resource
identifiers. Each identifier is either an integer or a pointer
to a STRING structure that specifies a resource name. The array
is used to traverse the directory structure contained in the
resource section in the image file specified by the DllHandle
parameter.
ResourceIdPathLength - Supplies the number of elements in the ResourceIdPath array.
ResourceDirectory - Supplies a pointer to a variable that will receive the address of the resource directory specified by ResourceIdPath in the resource data section of the image file the DllHandle parameter.
Return Value:
TBD
--*/
{
RTL_PAGED_CODE();
return LdrpSearchResourceSection_U(
DllHandle,
ResourceIdPath,
ResourceIdPathLength,
TRUE, // Look for a directory node
FALSE,
(PVOID *)ResourceDirectory
);
}
LONG
LdrpCompareResourceNames_U(
IN ULONG_PTR ResourceName,
IN PIMAGE_RESOURCE_DIRECTORY ResourceDirectory,
IN PIMAGE_RESOURCE_DIRECTORY_ENTRY ResourceDirectoryEntry
)
{
LONG li;
PIMAGE_RESOURCE_DIR_STRING_U ResourceNameString;
if (ResourceName & LDR_RESOURCE_ID_NAME_MASK) {
if (!ResourceDirectoryEntry->NameIsString) {
return( -1 );
}
ResourceNameString = (PIMAGE_RESOURCE_DIR_STRING_U) ((PCHAR)ResourceDirectory + ResourceDirectoryEntry->NameOffset);
li = wcsncmp( (LPWSTR)ResourceName, ResourceNameString->NameString, ResourceNameString->Length);
if (!li && wcslen((PWSTR)ResourceName) != ResourceNameString->Length) {
return( 1 );
}
return(li);
} else {
if (ResourceDirectoryEntry->NameIsString) {
return( 1 );
}
return( (ULONG)(ResourceName - ResourceDirectoryEntry->Name) );
}
}
#define USE_FIRSTAVAILABLE_LANGID (0xFFFFFFFF & ~LDR_RESOURCE_ID_NAME_MASK)
NTSTATUS
LdrpSearchResourceSection_U(
IN PVOID DllHandle,
IN PULONG_PTR ResourceIdPath,
IN ULONG ResourceIdPathLength,
IN BOOLEAN FindDirectoryEntry,
IN BOOLEAN ExactLangMatchOnly,
OUT PVOID *ResourceDirectoryOrData
)
/*++
Routine Description:
This function locates the address of the specified resource in the specified DLL and returns its address.
Arguments:
DllHandle - Supplies a handle to the image file that the resource is contained in.
ResourceIdPath - Supplies a pointer to an array of 32-bit resource
identifiers. Each identifier is either an integer or a pointer
to a null terminated string (PSZ) that specifies a resource
name. The array is used to traverse the directory structure
contained in the resource section in the image file specified by
the DllHandle parameter.
ResourceIdPathLength - Supplies the number of elements in the
ResourceIdPath array.
FindDirectoryEntry - Supplies a boolean that is TRUE if caller is
searching for a resource directory, otherwise the caller is
searching for a resource data entry.
ExactLangMatchOnly - Supplies a boolean that is TRUE if caller is
searching for a resource with, and only with, the language id
specified in ResourceIdPath, otherwise the caller wants the routine
to come up with default when specified langid is not found.
ResourceDirectoryOrData - Supplies a pointer to a variable that will
receive the address of the resource directory or data entry in
the resource data section of the image file specified by the
DllHandle parameter.
Return Value:
TBD
--*/
{
NTSTATUS Status;
PIMAGE_RESOURCE_DIRECTORY LanguageResourceDirectory, ResourceDirectory, TopResourceDirectory;
PIMAGE_RESOURCE_DIRECTORY_ENTRY ResourceDirEntLow;
PIMAGE_RESOURCE_DIRECTORY_ENTRY ResourceDirEntMiddle;
PIMAGE_RESOURCE_DIRECTORY_ENTRY ResourceDirEntHigh;
PIMAGE_RESOURCE_DATA_ENTRY ResourceEntry;
USHORT n, half;
LONG dir;
ULONG size;
ULONG_PTR ResourceIdRetry;
ULONG RetryCount;
LANGID NewLangId;
PULONG_PTR IdPath = ResourceIdPath;
ULONG IdPathLength = ResourceIdPathLength;
BOOLEAN fIsNeutral = FALSE;
LANGID GivenLanguage;
#ifndef NTOS_KERNEL_RUNTIME
LCID DefaultThreadLocale, DefaultSystemLocale;
PVOID AltResourceDllHandle = NULL;
ULONG_PTR UIResourceIdPath[3];
#endif
RTL_PAGED_CODE();
try {
TopResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
RtlImageDirectoryEntryToData(DllHandle,
TRUE,
IMAGE_DIRECTORY_ENTRY_RESOURCE,
&size
);
if (!TopResourceDirectory) {
return( STATUS_RESOURCE_DATA_NOT_FOUND );
}
ResourceDirectory = TopResourceDirectory;
ResourceIdRetry = USE_FIRSTAVAILABLE_LANGID;
RetryCount = 0;
ResourceEntry = NULL;
LanguageResourceDirectory = NULL;
while (ResourceDirectory != NULL && ResourceIdPathLength--) {
// If search path includes a language id, then attempt to
// match the following language ids in this order:
// (0) use given language id
// (1) use primary language of given language id
// (2) use id 0 (neutral resource)
// (3) use default UI language id
// If the PRIMARY language id is ZERO, then ALSO attempt to
// match the following language ids in this order:
// (4) use lang id of TEB if different from user locale
// (5) use UI lang from exe resource
// (6) use primary UI lang from exe resource
// (7) use Install Language
// (8) use lang id from user's locale id
// (9) use primary language of user's locale id
// (10) use lang id from system default locale id
// (11) use primary language of system default locale id
// (12) use US English lang id
// (13) use any lang id that matches requested info
if (ResourceIdPathLength == 0 && IdPathLength == 3) {
LanguageResourceDirectory = ResourceDirectory;
}
if (LanguageResourceDirectory != NULL) {
GivenLanguage = (LANGID)IdPath[ 2 ];
fIsNeutral = (PRIMARYLANGID( GivenLanguage ) == LANG_NEUTRAL);
TryNextLangId:
switch( RetryCount++ ) {
#ifdef NTOS_KERNEL_RUNTIME
case 0: // Use given language id
NewLangId = GivenLanguage;
break;
case 1: // Use primary language of given language id
NewLangId = PRIMARYLANGID( GivenLanguage );
break;
case 2: // Use id 0 (neutral resource)
NewLangId = 0;
break;
case 3: // Use user's default UI language
NewLangId = (LANGID)ResourceIdRetry;
break;
case 4: // Use native UI language
if ( !fIsNeutral ) {
// Stop looking - Not in the neutral case
goto ReturnFailure;
break;
}
NewLangId = PsInstallUILanguageId;
break;
case 5: // Use default system locale
NewLangId = LANGIDFROMLCID(PsDefaultSystemLocaleId);
break;
case 6:
// Use US English language
NewLangId = MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US );
break;
case 7: // Take any lang id that matches
NewLangId = USE_FIRSTAVAILABLE_LANGID;
break;
#else
case 0: // Use given language id
NewLangId = GivenLanguage;
break;
case 1: // Use primary language of given language id
if ( ExactLangMatchOnly) {
// Did not find an exact language match.
// Stop looking.
goto ReturnFailure;
}
NewLangId = PRIMARYLANGID( GivenLanguage );
break;
case 2: // Use id 0 (neutral resource)
NewLangId = 0;
break;
case 3: // Use user's default UI language
if (!UILangId){
Status = NtQueryDefaultUILanguage( &UILangId );
if (!NT_SUCCESS( Status )) {
// Failed reading key. Skip this lookup.
NewLangId = (LANGID)ResourceIdRetry;
break;
}
}
NewLangId = UILangId;
// Arabic/Hebrew MUI files may contain resources with LANG ID different than 401/40d.
// e.g. Comdlg32.dll has two sets of Arabic/Hebrew resources one mirrored (401/40d)
// and one flipped (801/80d).
if( !fIsNeutral &&
((PRIMARYLANGID (GivenLanguage) == LANG_ARABIC) || (PRIMARYLANGID (GivenLanguage) == LANG_HEBREW)) &&
(PRIMARYLANGID (GivenLanguage) == PRIMARYLANGID (NewLangId))
) {
NewLangId = GivenLanguage;
}
if (fIsNeutral || GivenLanguage == NewLangId){
// Load alternate resource dll.
AltResourceDllHandle=LdrLoadAlternateResourceModule(
DllHandle,
NULL);
if (!AltResourceDllHandle){
// Alternate resource dll not available.
// Skip this lookup.
NewLangId = (LANGID)ResourceIdRetry;
break;
}
// Map to alternate resource dll and search
// it instead.
UIResourceIdPath[0]=IdPath[0];
UIResourceIdPath[1]=IdPath[1];
UIResourceIdPath[2]=NewLangId;
Status = LdrpSearchResourceSection_U(
AltResourceDllHandle,
UIResourceIdPath,
3,
FindDirectoryEntry,
TRUE,
(PVOID *)ResourceDirectoryOrData
);
if (NT_SUCCESS(Status)){
// We sucessfully found alternate resource,
// return it.
return Status;
}
}
// Caller does not want alternate resource, or
// alternate resource not found.
NewLangId = (LANGID)ResourceIdRetry;
break;
case 4: // Use langid of ThreadLocale if different from user locale
if ( !fIsNeutral ) {
// Stop looking - Not in the neutral case
goto ReturnFailure;
break;
}
// Try the thread locale language-id if it is different from
// user locale.
if (NtCurrentTeb()){
Status = NtQueryDefaultLocale(
TRUE,
&DefaultThreadLocale
);
if (NT_SUCCESS( Status ) &&
DefaultThreadLocale !=
NtCurrentTeb()->CurrentLocale) {
// Thread locale is different from
// default locale.
NewLangId = LANGIDFROMLCID(NtCurrentTeb()->CurrentLocale);
break;
}
}
NewLangId = (LANGID)ResourceIdRetry;
break;
case 5: // UI language from the executable resource
if (!UILangId){
NewLangId = (LANGID)ResourceIdRetry;
} else {
NewLangId = UILangId;
}
break;
case 6: // Parimary lang of UI language from the executable resource
if (!UILangId){
NewLangId = (LANGID)ResourceIdRetry;
} else {
NewLangId = PRIMARYLANGID( (LANGID) UILangId );
}
break;
case 7: // Use install -native- language
// Thread locale is the same as the user locale, then let's
// try loading the native (install) ui language resources.
if (!InstallLangId){
Status = NtQueryInstallUILanguage(&InstallLangId);
if (!NT_SUCCESS( Status )) {
// Failed reading key. Skip this lookup.
NewLangId = (LANGID)ResourceIdRetry;
break;
}
}
NewLangId = InstallLangId;
break;
case 8: // Use lang id from locale in TEB
if (SUBLANGID( GivenLanguage ) == SUBLANG_SYS_DEFAULT) {
// Skip over all USER locale options
DefaultThreadLocale = 0;
RetryCount += 2;
break;
}
if (NtCurrentTeb() != NULL) {
NewLangId = LANGIDFROMLCID(NtCurrentTeb()->CurrentLocale);
}
break;
case 9: // Use User's default locale
Status = NtQueryDefaultLocale( TRUE, &DefaultThreadLocale );
if (NT_SUCCESS( Status )) {
NewLangId = LANGIDFROMLCID(DefaultThreadLocale);
break;
}
RetryCount++;
break;
case 10: // Use primary language of User's default locale
NewLangId = PRIMARYLANGID( (LANGID)ResourceIdRetry );
break;
case 11: // Use System default locale
Status = NtQueryDefaultLocale( FALSE, &DefaultSystemLocale );
if (!NT_SUCCESS( Status )) {
RetryCount++;
break;
}
if (DefaultSystemLocale != DefaultThreadLocale) {
NewLangId = LANGIDFROMLCID(DefaultSystemLocale);
break;
}
RetryCount += 2;
// fall through
case 13: // Use US English language
NewLangId = MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US );
break;
case 12: // Use primary language of System default locale
NewLangId = PRIMARYLANGID( (LANGID)ResourceIdRetry );
break;
case 14: // Take any lang id that matches
NewLangId = USE_FIRSTAVAILABLE_LANGID;
break;
#endif
default: // No lang ids to match
goto ReturnFailure;
break;
}
// If looking for a specific language id and same as the
// one we just looked up, then skip it.
if (NewLangId != USE_FIRSTAVAILABLE_LANGID &&
NewLangId == ResourceIdRetry
) {
goto TryNextLangId;
}
// Try this new language Id
ResourceIdRetry = (ULONG_PTR)NewLangId;
ResourceIdPath = &ResourceIdRetry;
ResourceDirectory = LanguageResourceDirectory;
}
n = ResourceDirectory->NumberOfNamedEntries;
ResourceDirEntLow = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(ResourceDirectory+1);
if (!(*ResourceIdPath & LDR_RESOURCE_ID_NAME_MASK)) {
ResourceDirEntLow += n;
n = ResourceDirectory->NumberOfIdEntries;
}
if (!n) {
ResourceDirectory = NULL;
goto NotFound;
}
if (LanguageResourceDirectory != NULL &&
*ResourceIdPath == USE_FIRSTAVAILABLE_LANGID
) {
ResourceDirectory = NULL;
ResourceIdRetry = ResourceDirEntLow->Name;
ResourceEntry = (PIMAGE_RESOURCE_DATA_ENTRY)
((PCHAR)TopResourceDirectory +
ResourceDirEntLow->OffsetToData
);
break;
}
ResourceDirectory = NULL;
ResourceDirEntHigh = ResourceDirEntLow + n - 1;
while (ResourceDirEntLow <= ResourceDirEntHigh) {
if ((half = (n >> 1)) != 0) {
ResourceDirEntMiddle = ResourceDirEntLow;
if (*(PUCHAR)&n & 1) {
ResourceDirEntMiddle += half;
}
else {
ResourceDirEntMiddle += half - 1;
}
dir = LdrpCompareResourceNames_U( *ResourceIdPath,
TopResourceDirectory,
ResourceDirEntMiddle
);
if (!dir) {
if (ResourceDirEntMiddle->DataIsDirectory) {
ResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
((PCHAR)TopResourceDirectory +
ResourceDirEntMiddle->OffsetToDirectory
);
}
else {
ResourceDirectory = NULL;
ResourceEntry = (PIMAGE_RESOURCE_DATA_ENTRY)
((PCHAR)TopResourceDirectory +
ResourceDirEntMiddle->OffsetToData
);
}
break;
}
else {
if (dir < 0) {
ResourceDirEntHigh = ResourceDirEntMiddle - 1;
if (*(PUCHAR)&n & 1) {
n = half;
}
else {
n = half - 1;
}
}
else {
ResourceDirEntLow = ResourceDirEntMiddle + 1;
n = half;
}
}
}
else {
if (n != 0) {
dir = LdrpCompareResourceNames_U( *ResourceIdPath,
TopResourceDirectory,
ResourceDirEntLow
);
if (!dir) {
if (ResourceDirEntLow->DataIsDirectory) {
ResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
((PCHAR)TopResourceDirectory +
ResourceDirEntLow->OffsetToDirectory
);
}
else {
ResourceEntry = (PIMAGE_RESOURCE_DATA_ENTRY)
((PCHAR)TopResourceDirectory +
ResourceDirEntLow->OffsetToData
);
}
}
}
break;
}
}
ResourceIdPath++;
}
if (ResourceEntry != NULL && !FindDirectoryEntry) {
*ResourceDirectoryOrData = (PVOID)ResourceEntry;
Status = STATUS_SUCCESS;
}
else
if (ResourceDirectory != NULL && FindDirectoryEntry) {
*ResourceDirectoryOrData = (PVOID)ResourceDirectory;
Status = STATUS_SUCCESS;
}
else {
NotFound:
switch( IdPathLength - ResourceIdPathLength) {
case 3: Status = STATUS_RESOURCE_LANG_NOT_FOUND; break;
case 2: Status = STATUS_RESOURCE_NAME_NOT_FOUND; break;
case 1: Status = STATUS_RESOURCE_TYPE_NOT_FOUND; break;
default: Status = STATUS_INVALID_PARAMETER; break;
}
}
if (Status == STATUS_RESOURCE_LANG_NOT_FOUND &&
LanguageResourceDirectory != NULL
) {
ResourceEntry = NULL;
goto TryNextLangId;
ReturnFailure: ;
Status = STATUS_RESOURCE_LANG_NOT_FOUND;
}
}
except (EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
}
return Status;
}
NTSTATUS
LdrEnumResources(
IN PVOID DllHandle,
IN PULONG_PTR ResourceIdPath,
IN ULONG ResourceIdPathLength,
IN OUT PULONG NumberOfResources,
OUT PLDR_ENUM_RESOURCE_ENTRY Resources OPTIONAL
)
{
NTSTATUS Status;
PIMAGE_RESOURCE_DIRECTORY TopResourceDirectory;
PIMAGE_RESOURCE_DIRECTORY TypeResourceDirectory;
PIMAGE_RESOURCE_DIRECTORY NameResourceDirectory;
PIMAGE_RESOURCE_DIRECTORY LangResourceDirectory;
PIMAGE_RESOURCE_DIRECTORY_ENTRY TypeResourceDirectoryEntry;
PIMAGE_RESOURCE_DIRECTORY_ENTRY NameResourceDirectoryEntry;
PIMAGE_RESOURCE_DIRECTORY_ENTRY LangResourceDirectoryEntry;
ULONG TypeDirectoryIndex, NumberOfTypeDirectoryEntries;
ULONG NameDirectoryIndex, NumberOfNameDirectoryEntries;
ULONG LangDirectoryIndex, NumberOfLangDirectoryEntries;
BOOLEAN ScanTypeDirectory;
BOOLEAN ScanNameDirectory;
BOOLEAN ReturnThisResource;
PIMAGE_RESOURCE_DIR_STRING_U ResourceNameString;
ULONG_PTR TypeResourceNameOrId;
ULONG_PTR NameResourceNameOrId;
ULONG_PTR LangResourceNameOrId;
PLDR_ENUM_RESOURCE_ENTRY ResourceInfo;
PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry;
ULONG ResourceIndex, MaxResourceIndex;
ULONG Size;
ResourceIndex = 0;
if (!ARGUMENT_PRESENT( Resources )) {
MaxResourceIndex = 0;
}
else {
MaxResourceIndex = *NumberOfResources;
}
*NumberOfResources = 0;
TopResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
RtlImageDirectoryEntryToData( DllHandle,
TRUE,
IMAGE_DIRECTORY_ENTRY_RESOURCE,
&Size
);
if (!TopResourceDirectory) {
return STATUS_RESOURCE_DATA_NOT_FOUND;
}
TypeResourceDirectory = TopResourceDirectory;
TypeResourceDirectoryEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(TypeResourceDirectory+1);
NumberOfTypeDirectoryEntries = TypeResourceDirectory->NumberOfNamedEntries +
TypeResourceDirectory->NumberOfIdEntries;
TypeDirectoryIndex = 0;
Status = STATUS_SUCCESS;
for (TypeDirectoryIndex=0;
TypeDirectoryIndex<NumberOfTypeDirectoryEntries;
TypeDirectoryIndex++, TypeResourceDirectoryEntry++
) {
if (ResourceIdPathLength > 0) {
ScanTypeDirectory = LdrpCompareResourceNames_U( ResourceIdPath[ 0 ],
TopResourceDirectory,
TypeResourceDirectoryEntry
) == 0;
}
else {
ScanTypeDirectory = TRUE;
}
if (ScanTypeDirectory) {
if (!TypeResourceDirectoryEntry->DataIsDirectory) {
return STATUS_INVALID_IMAGE_FORMAT;
}
if (TypeResourceDirectoryEntry->NameIsString) {
ResourceNameString = (PIMAGE_RESOURCE_DIR_STRING_U)
((PCHAR)TopResourceDirectory + TypeResourceDirectoryEntry->NameOffset);
TypeResourceNameOrId = (ULONG_PTR)ResourceNameString;
}
else {
TypeResourceNameOrId = (ULONG_PTR)TypeResourceDirectoryEntry->Id;
}
NameResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
((PCHAR)TopResourceDirectory + TypeResourceDirectoryEntry->OffsetToDirectory);
NameResourceDirectoryEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(NameResourceDirectory+1);
NumberOfNameDirectoryEntries = NameResourceDirectory->NumberOfNamedEntries +
NameResourceDirectory->NumberOfIdEntries;
NameDirectoryIndex = 0;
for (NameDirectoryIndex=0;
NameDirectoryIndex<NumberOfNameDirectoryEntries;
NameDirectoryIndex++, NameResourceDirectoryEntry++
) {
if (ResourceIdPathLength > 1) {
ScanNameDirectory = LdrpCompareResourceNames_U( ResourceIdPath[ 1 ],
TopResourceDirectory,
NameResourceDirectoryEntry
) == 0;
}
else {
ScanNameDirectory = TRUE;
}
if (ScanNameDirectory) {
if (!NameResourceDirectoryEntry->DataIsDirectory) {
return STATUS_INVALID_IMAGE_FORMAT;
}
if (NameResourceDirectoryEntry->NameIsString) {
ResourceNameString = (PIMAGE_RESOURCE_DIR_STRING_U)
((PCHAR)TopResourceDirectory + NameResourceDirectoryEntry->NameOffset);
NameResourceNameOrId = (ULONG_PTR)ResourceNameString;
}
else {
NameResourceNameOrId = (ULONG_PTR)NameResourceDirectoryEntry->Id;
}
LangResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
((PCHAR)TopResourceDirectory + NameResourceDirectoryEntry->OffsetToDirectory);
LangResourceDirectoryEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(LangResourceDirectory+1);
NumberOfLangDirectoryEntries = LangResourceDirectory->NumberOfNamedEntries +
LangResourceDirectory->NumberOfIdEntries;
LangDirectoryIndex = 0;
for (LangDirectoryIndex=0;
LangDirectoryIndex<NumberOfLangDirectoryEntries;
LangDirectoryIndex++, LangResourceDirectoryEntry++
) {
if (ResourceIdPathLength > 2) {
ReturnThisResource = LdrpCompareResourceNames_U( ResourceIdPath[ 2 ],
TopResourceDirectory,
LangResourceDirectoryEntry
) == 0;
}
else {
ReturnThisResource = TRUE;
}
if (ReturnThisResource) {
if (LangResourceDirectoryEntry->DataIsDirectory) {
return STATUS_INVALID_IMAGE_FORMAT;
}
if (LangResourceDirectoryEntry->NameIsString) {
ResourceNameString = (PIMAGE_RESOURCE_DIR_STRING_U)
((PCHAR)TopResourceDirectory + LangResourceDirectoryEntry->NameOffset);
LangResourceNameOrId = (ULONG_PTR)ResourceNameString;
}
else {
LangResourceNameOrId = (ULONG_PTR)LangResourceDirectoryEntry->Id;
}
ResourceDataEntry = (PIMAGE_RESOURCE_DATA_ENTRY)
((PCHAR)TopResourceDirectory + LangResourceDirectoryEntry->OffsetToData);
ResourceInfo = &Resources[ ResourceIndex++ ];
if (ResourceIndex <= MaxResourceIndex) {
ResourceInfo->Path[ 0 ].NameOrId = TypeResourceNameOrId;
ResourceInfo->Path[ 1 ].NameOrId = NameResourceNameOrId;
ResourceInfo->Path[ 2 ].NameOrId = LangResourceNameOrId;
ResourceInfo->Data = (PVOID)((ULONG_PTR)DllHandle + ResourceDataEntry->OffsetToData);
ResourceInfo->Size = ResourceDataEntry->Size;
ResourceInfo->Reserved = 0;
}
else {
Status = STATUS_INFO_LENGTH_MISMATCH;
}
}
}
}
}
}
}
*NumberOfResources = ResourceIndex;
return Status;
}
#ifndef NTOS_KERNEL_RUNTIME
BOOLEAN LdrAlternateResourcesEnabled(VOID)
/*++
Routine Description:
This function determines if the althernate resources are enabled.
Return Value:
True - Alternate Resource enabled.
False - Alternate Resource not enabled.
--*/
{
NTSTATUS Status;
if (!UILangId){
Status = NtQueryDefaultUILanguage( &UILangId );
if (!NT_SUCCESS( Status )) {
return FALSE;// Failed to get UI LangID. AltResource not enabled.
}
}
if (!InstallLangId){
Status = NtQueryInstallUILanguage( &InstallLangId);
if (!NT_SUCCESS( Status )) {
return FALSE;// Failed to get Intall LangID. AltResource not enabled.
}
}
if (UILangId == InstallLangId) {
return FALSE;// UI Lang matches Installed Lang. AltResource not enabled.
}
return TRUE;
}
PVOID LdrGetAlternateResourceModuleHandle(IN PVOID Module)
/*++
Routine Description:
This function gets the alternate resource module from the table containing the handle.
Arguments:
Module - Module of which alternate resource module needs to loaded.
Return Value:
Handle of the alternate resource module.
--*/
{
ULONG ModuleIndex;
for (ModuleIndex = 0; ModuleIndex < AlternateResourceModuleCount; ModuleIndex++ ){
if (AlternateResourceModules[ModuleIndex].ModuleBase == Module){
return AlternateResourceModules[ModuleIndex].AlternateModule;
}
}
return NULL;
}
BOOLEAN
LdrpGetFileVersion(
IN PVOID ImageBase,
IN LANGID LangId,
OUT PULONGLONG Version
)
/*++
Routine Description:
Get the version stamp out of the VS_FIXEDFILEINFO resource in a PE image.
Arguments:
ImageBase - supplies the address in memory where the file is mapped in.
Version - receives 64bit version number, or 0 if the file is not a PE image or has no version data.
Return Value:
None.
--*/
{
PIMAGE_RESOURCE_DATA_ENTRY DataEntry;
NTSTATUS Status;
ULONG_PTR IdPath[3];
ULONG ResourceSize;
typedef struct tagVS_FIXEDFILEINFO
{
LONG dwSignature; /* e.g. 0xfeef04bd */
LONG dwStrucVersion; /* e.g. 0x00000042 = "0.42" */
LONG dwFileVersionMS; /* e.g. 0x00030075 = "3.75" */
LONG dwFileVersionLS; /* e.g. 0x00000031 = "0.31" */
LONG dwProductVersionMS; /* e.g. 0x00030010 = "3.10" */
LONG dwProductVersionLS; /* e.g. 0x00000031 = "0.31" */
LONG dwFileFlagsMask; /* = 0x3F for version "0.42" */
LONG dwFileFlags; /* e.g. VFF_DEBUG | VFF_PRERELEASE */
LONG dwFileOS; /* e.g. VOS_DOS_WINDOWS16 */
LONG dwFileType; /* e.g. VFT_DRIVER */
LONG dwFileSubtype; /* e.g. VFT2_DRV_KEYBOARD */
LONG dwFileDateMS; /* e.g. 0 */
LONG dwFileDateLS; /* e.g. 0 */
} VS_FIXEDFILEINFO;
struct {
USHORT TotalSize;
USHORT DataSize;
USHORT Type;
WCHAR Name[16]; // L"VS_VERSION_INFO" + unicode nul
VS_FIXEDFILEINFO FixedFileInfo;
} *Resource;
*Version = 0;
IdPath[0] = 16;
IdPath[1] = 1;
IdPath[2] = LangId;
try {
Status = LdrpSearchResourceSection_U(
ImageBase,
IdPath,
3,
FALSE,
TRUE,
&DataEntry);
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = STATUS_UNSUCCESSFUL;
}
if(!NT_SUCCESS(Status)) {
return FALSE;
}
try {
Status = LdrpAccessResourceData(
ImageBase,
DataEntry,
&Resource,
&ResourceSize);
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = STATUS_UNSUCCESSFUL;
}
if(!NT_SUCCESS(Status)) {
return FALSE;
}
try {
if((ResourceSize >= sizeof(*Resource)) && !_wcsicmp(Resource->Name,L"VS_VERSION_INFO")) {
*Version = ((ULONGLONG)Resource->FixedFileInfo.dwFileVersionMS << 32) | (ULONGLONG)Resource->FixedFileInfo.dwFileVersionLS;
} else {
DbgPrint(("LDR: Warning: invalid version resource\n"));
return FALSE;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
DbgPrint(("LDR: Exception encountered processing bogus version resource\n"));
return FALSE;
}
return TRUE;
}
BOOLEAN LdrpVerifyAlternateResourceModule(IN PVOID Module, IN PVOID AlternateModule)
/*++
Routine Description:
This function verifies if the alternate resource module has the same version of the base module.
Arguments:
Module - The handle of the base module.
AlternateModule - The handle of the alternate resource module
Return Value:
TBD.
--*/
{
ULONGLONG ModuleVersion;
ULONGLONG AltModuleVersion;
NTSTATUS Status;
if (!UILangId){
Status = NtQueryDefaultUILanguage( &UILangId);
if (!NT_SUCCESS( Status )) {
return FALSE;// Failed to get UI LangID. AltResource not enabled.
}
}
if (!LdrpGetFileVersion(AlternateModule, UILangId, &AltModuleVersion)){
return FALSE;
}
if (!InstallLangId){
Status = NtQueryInstallUILanguage (&InstallLangId);
if (!NT_SUCCESS( Status )) {
return FALSE;// Failed to get Install LangID. AltResource not enabled.
}
}
if (!LdrpGetFileVersion(Module, InstallLangId, &ModuleVersion)){
return FALSE;
}
if (ModuleVersion == AltModuleVersion){
return TRUE;
} else{
return FALSE;
}
}
BOOLEAN
LdrpSetAlternateResourceModuleHandle(
IN PVOID Module,
IN PVOID AlternateModule
)
/*++
Routine Description:
This function records the handle of the base module and alternate resource module in an array.
Arguments:
Module - The handle of the base module.
AlternateModule - The handle of the alternate resource module
Return Value:
TBD.
--*/
{
PALT_RESOURCE_MODULE NewModules;
if (AlternateResourceModules == NULL){
// Allocate memory of initial size MEMBLOCKSIZE.
NewModules = RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, RESMODSIZE * MEMBLOCKSIZE);
if (!NewModules){
return FALSE;
}
AlternateResourceModules = NewModules;
AltResMemBlockCount = MEMBLOCKSIZE;
}
else
if (AlternateResourceModuleCount >= AltResMemBlockCount ){
// ReAllocate another chunk of memory.
NewModules = RtlReAllocateHeap(
RtlProcessHeap(),
0,
AlternateResourceModules,
(AltResMemBlockCount + MEMBLOCKSIZE) * RESMODSIZE
);
if (!NewModules){
return FALSE;
}
AlternateResourceModules = NewModules;
AltResMemBlockCount += MEMBLOCKSIZE;
}
AlternateResourceModules[AlternateResourceModuleCount].ModuleBase = Module;
AlternateResourceModules[AlternateResourceModuleCount].AlternateModule = AlternateModule;
AlternateResourceModuleCount++;
return TRUE;
}
PVOID LdrLoadAlternateResourceModule(IN PVOID Module, IN LPCWSTR PathToAlternateModule OPTIONAL)
/*++
Routine Description:
This function does the acutally loading into memory of the alternate resource module, or loads from the table if it was loaded before.
Arguments:
Module - The handle of the base module.
PathToAlternateModule - Optional path from which module is being loaded.
Return Value:
Handle to the alternate resource module.
--*/
{
PVOID AlternateModule, DllBase;
PLDR_DATA_TABLE_ENTRY Entry;
HANDLE FileHandle, MappingHandle;
PIMAGE_NT_HEADERS NtHeaders;
NTSTATUS Status;
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING AltDllName;
PVOID FreeBuffer;
LPWSTR BaseDllName, p;
WCHAR DllPathName[DOS_MAX_PATH_LENGTH];
ULONG DllPathNameLength, BaseDllNameLength, CopyCount;
ULONG Digit;
int i, RetryCount;
WCHAR AltModulePath[DOS_MAX_PATH_LENGTH];
WCHAR AltModulePathMUI[DOS_MAX_PATH_LENGTH];
WCHAR AltModulePathFallback[DOS_MAX_PATH_LENGTH];
IO_STATUS_BLOCK IoStatusBlock;
RTL_RELATIVE_NAME RelativeName;
SIZE_T ViewSize;
LARGE_INTEGER SectionOffset;
WCHAR LangIdDir[6];
UNICODE_STRING AltModulePathList[3];
UNICODE_STRING NtSystemRoot;
if (!LdrAlternateResourcesEnabled()) {
return NULL;
}
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
AlternateModule = LdrGetAlternateResourceModuleHandle(Module);
if (AlternateModule == NO_ALTERNATE_RESOURCE_MODULE){// We tried to load this module before but failed. Don't try again in the future.
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
return NULL;
} else if (AlternateModule > 0){// We found the previously loaded match
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
return AlternateModule;
}
if (ARGUMENT_PRESENT(PathToAlternateModule)){
// Caller suplied path.
CopyCount = wcslen(PathToAlternateModule);
for (p = (LPWSTR) PathToAlternateModule + CopyCount; p > PathToAlternateModule; p--){
if (*(p-1) == L'\\'){
break;
}
}
if (p == PathToAlternateModule){
goto error_exit;
}
DllPathNameLength = (ULONG)(p - PathToAlternateModule) * sizeof(WCHAR);
RtlCopyMemory(DllPathName, PathToAlternateModule, DllPathNameLength);
BaseDllName = p ;
BaseDllNameLength = CopyCount * sizeof(WCHAR) - DllPathNameLength;
} else{// Try to get full dll path from Ldr data table.
Status = LdrFindEntryForAddress(Module, &Entry);
if (!NT_SUCCESS( Status )){
goto error_exit;
}
DllPathNameLength = Entry->FullDllName.Length - Entry->BaseDllName.Length;
RtlCopyMemory(DllPathName, Entry->FullDllName.Buffer, DllPathNameLength);
BaseDllName = Entry->BaseDllName.Buffer;
BaseDllNameLength = Entry->BaseDllName.Length;
}
DllPathName[DllPathNameLength / sizeof(WCHAR)] = UNICODE_NULL;
// Generate the langid directory like "0804\"
if (!UILangId){
Status = NtQueryDefaultUILanguage( &UILangId );
if (!NT_SUCCESS( Status )) {
goto error_exit;
}
}
CopyCount = 0;
for (i = 12; i >= 0; i -= 4)
{
Digit = ((UILangId >> i) & 0xF);
if (Digit >= 10){
LangIdDir[CopyCount++] = (WCHAR) (Digit - 10 + L'A');
} else{
LangIdDir[CopyCount++] = (WCHAR) (Digit + L'0');
}
}
LangIdDir[CopyCount++] = L'\\';
LangIdDir[CopyCount++] = UNICODE_NULL;
// Generate the first path c:\winnt\system32\mui\0804\ntdll.dll.mui
AltModulePathList[1].Buffer = AltModulePath;
AltModulePathList[1].Length = 0;
AltModulePathList[1].MaximumLength = sizeof(AltModulePath);
RtlAppendUnicodeToString(&AltModulePathList[1], DllPathName);
RtlAppendUnicodeToString(&AltModulePathList[1], L"mui\\");
RtlAppendUnicodeToString(&AltModulePathList[1], LangIdDir);
RtlAppendUnicodeToString(&AltModulePathList[1], BaseDllName);
// Generate the first path c:\winnt\system32\mui\0804\ntdll.dll
AltModulePathList[0].Buffer = AltModulePathMUI;
AltModulePathList[0].Length = 0;
AltModulePathList[0].MaximumLength = sizeof(AltModulePathMUI);
RtlCopyUnicodeString(&AltModulePathList[0], &AltModulePathList[1]);
RtlAppendUnicodeToString(&AltModulePathList[0], L".mui");
// Generate path c:\winnt\mui\fallback\0804\foo.exe.mui
AltModulePathList[2].Buffer = AltModulePathFallback;
AltModulePathList[2].Length = 0;
AltModulePathList[2].MaximumLength = sizeof(AltModulePathFallback);
RtlInitUnicodeString(&NtSystemRoot, USER_SHARED_DATA->NtSystemRoot);
RtlAppendUnicodeStringToString(&AltModulePathList[2], &NtSystemRoot);
RtlAppendUnicodeToString(&AltModulePathList[2], L"\\mui\\fallback\\");
RtlAppendUnicodeToString(&AltModulePathList[2], LangIdDir);
RtlAppendUnicodeToString(&AltModulePathList[2], BaseDllName);
RtlAppendUnicodeToString(&AltModulePathList[2], L".mui");
// Try name with .mui extesion first.
RetryCount = 0;
while (RetryCount < sizeof(AltModulePathList)/sizeof(UNICODE_STRING))
{
if (!RtlDosPathNameToNtPathName_U(AltModulePathList[RetryCount].Buffer, &AltDllName, NULL, &RelativeName)){
goto error_exit;
}
FreeBuffer = AltDllName.Buffer;
if ( RelativeName.RelativeName.Length ) {
AltDllName = *(PUNICODE_STRING)&RelativeName.RelativeName;
} else {
RelativeName.ContainingDirectory = NULL;
}
InitializeObjectAttributes(&ObjectAttributes, &AltDllName, OBJ_CASE_INSENSITIVE, RelativeName.ContainingDirectory, NULL);
Status = NtCreateFile(
&FileHandle,
(ACCESS_MASK) GENERIC_READ | SYNCHRONIZE | FILE_READ_ATTRIBUTES,
&ObjectAttributes,
&IoStatusBlock,
NULL,
0L,
FILE_SHARE_READ | FILE_SHARE_DELETE,
FILE_OPEN,
0L,
NULL,
0L
);
RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
if (NT_SUCCESS( Status )){
break;
}
if (Status != STATUS_OBJECT_NAME_NOT_FOUND && RetryCount == 0) {
// Error other than the file name with .mui not found.
// Most likely directory is missing. Skip file name w/o .mui and goto fallback directory.
RetryCount++;
}
RetryCount++;
}
Status = NtCreateSection(&MappingHandle, STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ, NULL, NULL, PAGE_WRITECOPY, SEC_COMMIT, FileHandle);
if (!NT_SUCCESS( Status )){
goto error_exit;
}
NtClose( FileHandle );
SectionOffset.LowPart = 0;
SectionOffset.HighPart = 0;
ViewSize = 0;
DllBase = NULL;
Status = NtMapViewOfSection(MappingHandle, NtCurrentProcess(), &DllBase, 0L, 0L, &SectionOffset, &ViewSize, ViewShare, 0L, PAGE_WRITECOPY);
NtClose( MappingHandle );
if (!NT_SUCCESS( Status )){
goto error_exit;
}
NtHeaders = RtlImageNtHeader( DllBase );
if (!NtHeaders) {
NtUnmapViewOfSection(NtCurrentProcess(), (PVOID) DllBase);
goto error_exit;
}
AlternateModule = (HANDLE)((ULONG_PTR)DllBase | 0x00000001);
// Disable version check now to make testing with an earlier
// localized version easier.
// if(!LdrpVerifyAlternateResourceModule(Module, AlternateModule)){
// NtUnmapViewOfSection(NtCurrentProcess(), (PVOID) DllBase);
// goto error_exit;
// }
LdrpSetAlternateResourceModuleHandle(Module, AlternateModule);
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
return AlternateModule;
error_exit:
LdrpSetAlternateResourceModuleHandle(Module, NO_ALTERNATE_RESOURCE_MODULE);
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
return NULL;
}
BOOLEAN LdrUnloadAlternateResourceModule(IN PVOID Module)
/*++
Routine Description:
This function unmaps an alternate resource module from the process' address space and updates alternate resource module table.
Arguments:
Module - handle of the base module.
Return Value:
TBD.
--*/
{
ULONG ModuleIndex;
PALT_RESOURCE_MODULE AltModule;
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
if (AlternateResourceModuleCount == 0){
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
return TRUE;
}
for (ModuleIndex = AlternateResourceModuleCount; ModuleIndex > 0; ModuleIndex--){
if (AlternateResourceModules[ModuleIndex-1].ModuleBase == Module){
break;
}
}
if (ModuleIndex == 0){
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
return FALSE;
}
// Adjust to the actual index
ModuleIndex --;
AltModule = &AlternateResourceModules[ModuleIndex];
if (AltModule->AlternateModule != NO_ALTERNATE_RESOURCE_MODULE) {
NtUnmapViewOfSection(NtCurrentProcess(), (PVOID)((ULONG_PTR)AltModule->AlternateModule & ~0x00000001));
}
__try {
if (ModuleIndex != AlternateResourceModuleCount - 1){
// Consolidate the array. Skip this if unloaded item is the last element.
RtlMoveMemory(AltModule, AltModule + 1, (AlternateResourceModuleCount - ModuleIndex - 1) * RESMODSIZE);
}
} __except (EXCEPTION_EXECUTE_HANDLER) {
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
return FALSE;
}
AlternateResourceModuleCount --;
if (AlternateResourceModuleCount == 0){
RtlFreeHeap(RtlProcessHeap(), 0, AlternateResourceModules);
AlternateResourceModules = NULL;
AltResMemBlockCount = 0;
}
else
if (AlternateResourceModuleCount < AltResMemBlockCount - MEMBLOCKSIZE){
AltModule = RtlReAllocateHeap(RtlProcessHeap(), 0, AlternateResourceModules, (AltResMemBlockCount - MEMBLOCKSIZE) * RESMODSIZE);
if (!AltModule){
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
return FALSE;
}
AlternateResourceModules = AltModule;
AltResMemBlockCount -= MEMBLOCKSIZE;
}
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
return TRUE;
}
BOOLEAN LdrFlushAlternateResourceModules(VOID)
/*++
Routine Description:
This function unmaps all the alternate resouce modules for the process address space.
This function would be used mainly by CSRSS, and any sub-systems that are permanent during logon and logoff.
Return Value:
TRUE : Successful
FALSE : Failed
--*/
{
ULONG ModuleIndex;
PALT_RESOURCE_MODULE AltModule;
// Grab the loader lock
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
if (AlternateResourceModuleCount > 0) {
// Let's unmap the alternate resource modules from the process address space
for (ModuleIndex=0; ModuleIndex<AlternateResourceModuleCount; ModuleIndex++) {
AltModule = &AlternateResourceModules[ModuleIndex];
if (AltModule->AlternateModule != NO_ALTERNATE_RESOURCE_MODULE) {
NtUnmapViewOfSection(NtCurrentProcess(), (PVOID)((ULONG_PTR)AltModule->AlternateModule & ~0x00000001));
}
}
// Cleanup alternate resource modules memory
RtlFreeHeap(RtlProcessHeap(), 0, AlternateResourceModules);
AlternateResourceModules = NULL;
AlternateResourceModuleCount = 0;
AltResMemBlockCount = 0;
}
// Re-Initialize the UI language for the current process,
// and leave the LoaderLock
UILangId = 0;
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
return TRUE;
}
#endif