Commit 32298b1d authored by Dmitry Timoshkov's avatar Dmitry Timoshkov Committed by Vitaly Lipatov

ntdll: Add support for loading MUI resources. (eterbug #15663)

parent d06edb96
Pipeline #8877 failed
...@@ -4004,6 +4004,8 @@ NTSTATUS WINAPI LdrUnloadDll( HMODULE hModule ) ...@@ -4004,6 +4004,8 @@ NTSTATUS WINAPI LdrUnloadDll( HMODULE hModule )
MODULE_FlushModrefs(); MODULE_FlushModrefs();
} }
unload_MUI_module( hModule );
TRACE("END\n"); TRACE("END\n");
} }
else else
......
...@@ -81,6 +81,7 @@ extern void RELAY_SetupDLL( HMODULE hmod ) DECLSPEC_HIDDEN; ...@@ -81,6 +81,7 @@ extern void RELAY_SetupDLL( HMODULE hmod ) DECLSPEC_HIDDEN;
extern void SNOOP_SetupDLL( HMODULE hmod ) DECLSPEC_HIDDEN; extern void SNOOP_SetupDLL( HMODULE hmod ) DECLSPEC_HIDDEN;
extern const WCHAR windows_dir[] DECLSPEC_HIDDEN; extern const WCHAR windows_dir[] DECLSPEC_HIDDEN;
extern const WCHAR system_dir[] DECLSPEC_HIDDEN; extern const WCHAR system_dir[] DECLSPEC_HIDDEN;
extern void unload_MUI_module( HMODULE ) DECLSPEC_HIDDEN;
extern void (FASTCALL *pBaseThreadInitThunk)(DWORD,LPTHREAD_START_ROUTINE,void *) DECLSPEC_HIDDEN; extern void (FASTCALL *pBaseThreadInitThunk)(DWORD,LPTHREAD_START_ROUTINE,void *) DECLSPEC_HIDDEN;
extern const struct unix_funcs *unix_funcs DECLSPEC_HIDDEN; extern const struct unix_funcs *unix_funcs DECLSPEC_HIDDEN;
......
...@@ -283,34 +283,6 @@ NTSTATUS WINAPI DECLSPEC_HOTPATCH LdrFindResourceDirectory_U( HMODULE hmod, cons ...@@ -283,34 +283,6 @@ NTSTATUS WINAPI DECLSPEC_HOTPATCH LdrFindResourceDirectory_U( HMODULE hmod, cons
} }
/**********************************************************************
* LdrFindResource_U (NTDLL.@)
*/
NTSTATUS WINAPI DECLSPEC_HOTPATCH LdrFindResource_U( HMODULE hmod, const LDR_RESOURCE_INFO *info,
ULONG level, const IMAGE_RESOURCE_DATA_ENTRY **entry )
{
const void *res;
NTSTATUS status;
__TRY
{
if (info) TRACE( "module %p type %s name %s lang %04x level %d\n",
hmod, debugstr_w((LPCWSTR)info->Type),
level > 1 ? debugstr_w((LPCWSTR)info->Name) : "",
level > 2 ? info->Language : 0, level );
status = find_entry( hmod, info, level, &res, FALSE );
if (status == STATUS_SUCCESS) *entry = res;
}
__EXCEPT_PAGE_FAULT
{
return GetExceptionCode();
}
__ENDTRY;
return status;
}
/* don't penalize other platforms with stuff needed on i386 for compatibility */ /* don't penalize other platforms with stuff needed on i386 for compatibility */
#ifdef __i386__ #ifdef __i386__
NTSTATUS WINAPI DECLSPEC_HIDDEN access_resource( HMODULE hmod, const IMAGE_RESOURCE_DATA_ENTRY *entry, NTSTATUS WINAPI DECLSPEC_HIDDEN access_resource( HMODULE hmod, const IMAGE_RESOURCE_DATA_ENTRY *entry,
...@@ -351,6 +323,14 @@ static inline NTSTATUS access_resource( HMODULE hmod, const IMAGE_RESOURCE_DATA_ ...@@ -351,6 +323,14 @@ static inline NTSTATUS access_resource( HMODULE hmod, const IMAGE_RESOURCE_DATA_
return status; return status;
} }
static BOOL map_MUI_module(HMODULE *hmod);
NTSTATUS WINAPI DECLSPEC_HIDDEN access_MUI_resource( HMODULE hmod, const IMAGE_RESOURCE_DATA_ENTRY *entry,
void **ptr, ULONG *size )
{
map_MUI_module( &hmod );
return access_resource( hmod, entry, ptr, size );
}
/********************************************************************** /**********************************************************************
* LdrAccessResource (NTDLL.@) * LdrAccessResource (NTDLL.@)
* *
...@@ -368,7 +348,7 @@ __ASM_STDCALL_FUNC( LdrAccessResource, 16, ...@@ -368,7 +348,7 @@ __ASM_STDCALL_FUNC( LdrAccessResource, 16,
"pushl 16(%ebp)\n\t" "pushl 16(%ebp)\n\t"
"pushl 12(%ebp)\n\t" "pushl 12(%ebp)\n\t"
"pushl 8(%ebp)\n\t" "pushl 8(%ebp)\n\t"
"call " __ASM_STDCALL("access_resource",16) "\n\t" "call " __ASM_STDCALL("access_MUI_resource",16) "\n\t"
"leave\n\t" "leave\n\t"
"ret $16" "ret $16"
) )
...@@ -376,7 +356,7 @@ __ASM_STDCALL_FUNC( LdrAccessResource, 16, ...@@ -376,7 +356,7 @@ __ASM_STDCALL_FUNC( LdrAccessResource, 16,
NTSTATUS WINAPI LdrAccessResource( HMODULE hmod, const IMAGE_RESOURCE_DATA_ENTRY *entry, NTSTATUS WINAPI LdrAccessResource( HMODULE hmod, const IMAGE_RESOURCE_DATA_ENTRY *entry,
void **ptr, ULONG *size ) void **ptr, ULONG *size )
{ {
return access_resource( hmod, entry, ptr, size ); return access_MUI_resource( hmod, entry, ptr, size );
} }
#endif #endif
...@@ -420,3 +400,296 @@ NTSTATUS WINAPI RtlFindMessage( HMODULE hmod, ULONG type, ULONG lang, ...@@ -420,3 +400,296 @@ NTSTATUS WINAPI RtlFindMessage( HMODULE hmod, ULONG type, ULONG lang,
} }
return STATUS_MESSAGE_NOT_FOUND; return STATUS_MESSAGE_NOT_FOUND;
} }
/* MUI resource cache */
static int MUI_cache_count, MUI_cache_size;
struct MUI_cache_entry
{
HMODULE hmod;
HMODULE hmod_MUI;
};
static struct MUI_cache_entry *MUI_cache;
static RTL_CRITICAL_SECTION MUI_cache_section;
static RTL_CRITICAL_SECTION_DEBUG MUI_cache_section_debug =
{
0, 0, &MUI_cache_section,
{ &MUI_cache_section_debug.ProcessLocksList, &MUI_cache_section_debug.ProcessLocksList },
0, 0, { (DWORD_PTR)(__FILE__ ": MUI_cache_section") }
};
static RTL_CRITICAL_SECTION MUI_cache_section = { &MUI_cache_section_debug, -1, 0, 0, 0, 0 };
typedef struct _MUI_RC_CONFIG
{
DWORD dwSignature;
DWORD dwSize;
DWORD dwVersion;
DWORD unknown1[3];
DWORD dwFileType;
BYTE pServiceChecksum[16];
BYTE pChecksum[16];
DWORD unknown2[6];
DWORD dwTypeNameMainOffset;
DWORD dwTypeNameMainSize;
DWORD dwTypeIDMainOffset;
DWORD dwTypeIDMainSize;
DWORD dwTypeNameMUIOffset;
DWORD dwTypeNameMUISize;
DWORD dwTypeIDMUIOffset;
DWORD dwTypeIDMUISize;
DWORD unknown3[2];
DWORD dwLanguageNameOffset;
DWORD dwLanguageNameSize;
} MUI_RC_CONFIG;
static BOOL map_MUI_module(HMODULE *hmod)
{
BOOL ret = FALSE;
RtlEnterCriticalSection(&MUI_cache_section);
if (MUI_cache_count)
{
int min = 0, max = MUI_cache_count - 1;
while (min <= max)
{
int pos = (min + max) / 2;
if ((ULONG_PTR)*hmod > (ULONG_PTR)MUI_cache[pos].hmod) min = pos + 1;
else if ((ULONG_PTR)*hmod < (ULONG_PTR)MUI_cache[pos].hmod) max = pos - 1;
else
{
TRACE("found hmod %p => %p in MUI cache\n", *hmod, MUI_cache[pos].hmod_MUI);
*hmod = MUI_cache[pos].hmod_MUI;
ret = TRUE;
break;
}
}
}
RtlLeaveCriticalSection(&MUI_cache_section);
return ret;
}
static void add_MUI_module(HMODULE hmod, HMODULE hmod_MUI)
{
int pos = 0;
RtlEnterCriticalSection(&MUI_cache_section);
if (MUI_cache_count)
{
int min = 0, max = MUI_cache_count - 1;
while (min <= max)
{
pos = (min + max) / 2;
if ((ULONG_PTR)hmod > (ULONG_PTR)MUI_cache[pos].hmod) min = pos + 1;
else if ((ULONG_PTR)hmod < (ULONG_PTR)MUI_cache[pos].hmod) max = pos - 1;
else
{
TRACE("hmod %p already in MUI cache\n", hmod);
RtlLeaveCriticalSection(&MUI_cache_section);
return;
}
}
pos = min;
}
TRACE("adding %p => %p at pos %d\n", hmod, hmod_MUI, pos);
if (MUI_cache_count == MUI_cache_size)
{
struct MUI_cache_entry *new_MUI_cache;
int new_MUI_cache_size;
if (!MUI_cache_size)
{
new_MUI_cache_size = 16;
new_MUI_cache = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(*MUI_cache) * new_MUI_cache_size);
}
else
{
new_MUI_cache_size = MUI_cache_size * 2;
new_MUI_cache = RtlReAllocateHeap(GetProcessHeap(), 0, MUI_cache,
sizeof(*MUI_cache) * new_MUI_cache_size);
}
if (!new_MUI_cache)
{
RtlLeaveCriticalSection(&MUI_cache_section);
return;
}
MUI_cache = new_MUI_cache;
MUI_cache_size = new_MUI_cache_size;
}
memmove(&MUI_cache[pos + 1], &MUI_cache[pos], sizeof(*MUI_cache) * (MUI_cache_count - pos));
MUI_cache[pos].hmod = hmod;
MUI_cache[pos].hmod_MUI = hmod_MUI;
MUI_cache_count++;
RtlLeaveCriticalSection(&MUI_cache_section);
}
void unload_MUI_module(HMODULE hmod)
{
RtlEnterCriticalSection(&MUI_cache_section);
if (MUI_cache_count)
{
int min = 0, max = MUI_cache_count - 1;
while (min <= max)
{
int pos = (min + max) / 2;
if ((ULONG_PTR)hmod > (ULONG_PTR)MUI_cache[pos].hmod) min = pos + 1;
else if ((ULONG_PTR)hmod < (ULONG_PTR)MUI_cache[pos].hmod) max = pos - 1;
else
{
TRACE("found hmod %p => %p in MUI cache\n", hmod, MUI_cache[pos].hmod_MUI);
NtUnmapViewOfSection( NtCurrentProcess(), MUI_cache[pos].hmod_MUI );
MUI_cache_count--;
memmove(&MUI_cache[pos], &MUI_cache[pos + 1], sizeof(*MUI_cache) * (MUI_cache_count - pos));
break;
}
}
}
RtlLeaveCriticalSection(&MUI_cache_section);
}
static NTSTATUS load_MUI_module(HMODULE *hmod, BOOL *is_MUI)
{
const void *res = NULL;
MUI_RC_CONFIG *mui;
LDR_RESOURCE_INFO mui_rsrc;
NTSTATUS status;
*is_MUI = FALSE;
mui_rsrc.Type = (ULONG_PTR)L"MUI";
mui_rsrc.Name = 1;
mui_rsrc.Language = MAKELANGID( LANG_NEUTRAL, SUBLANG_NEUTRAL );
if ((status = find_entry( *hmod, &mui_rsrc, 3, &res, FALSE )) == STATUS_SUCCESS)
{
if (!access_resource( *hmod, res, (void **)&mui, NULL ))
{
const WCHAR *lang = (const WCHAR *)((const BYTE *)mui + mui->dwLanguageNameOffset);
char buf[MAX_PATH * sizeof(WCHAR) + sizeof(UNICODE_STRING)];
MEMORY_SECTION_NAME *name = (MEMORY_SECTION_NAME *)buf;
OBJECT_ATTRIBUTES attr;
HANDLE file, mapping;
IO_STATUS_BLOCK io;
SIZE_T size;
WCHAR *p, *file_part;
HMODULE hmod_MUI;
*is_MUI = TRUE;
if (mui->dwSignature != 0xfecdfecd)
return STATUS_MUI_INVALID_RC_CONFIG;
status = NtQueryVirtualMemory( NtCurrentProcess(), *hmod, MemoryMappedFilenameInformation,
name, sizeof(buf), NULL );
if (status)
{
ERR("NtQueryVirtualMemory(%p) => %08lx\n", *hmod, status);
return status;
}
p = wcsrchr( name->SectionFileName.Buffer, '\\' );
if (!p) p = name->SectionFileName.Buffer;
file_part = p + wcslen( lang ) + 1;
memmove( file_part, p, (wcslen(p) + 1) * sizeof(WCHAR) );
wcscpy( p + 1, lang );
file_part[0] = '\\';
wcscat( p, L".mui" );
name->SectionFileName.Length = wcslen( name->SectionFileName.Buffer ) * sizeof(WCHAR);
name->SectionFileName.MaximumLength = name->SectionFileName.Length + sizeof(WCHAR);
attr.Length = sizeof(attr);
attr.RootDirectory = 0;
attr.Attributes = OBJ_CASE_INSENSITIVE;
attr.ObjectName = &name->SectionFileName;
attr.SecurityDescriptor = NULL;
attr.SecurityQualityOfService = NULL;
status = NtOpenFile( &file, GENERIC_READ | SYNCHRONIZE, &attr, &io,
FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE );
if (status)
{
ERR("NtOpenFile(%s) => %08lx\n", debugstr_us( &name->SectionFileName ), status);
return status;
}
status = NtCreateSection( &mapping, STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ,
NULL, NULL, PAGE_READONLY, SEC_IMAGE, file );
NtClose( file );
if (status)
{
ERR("NtCreateSection => %08lx\n", status);
return status;
}
hmod_MUI = NULL;
size = 0;
status = NtMapViewOfSection( mapping, NtCurrentProcess(), (void **)&hmod_MUI, 0, 0, NULL, &size,
ViewShare, 0, PAGE_READONLY );
NtClose( mapping );
if (status == STATUS_IMAGE_NOT_AT_BASE) status = STATUS_SUCCESS;
if (status)
{
ERR("NtMapViewOfSection => %08lx\n", status);
return status;
}
add_MUI_module(*hmod, hmod_MUI);
*hmod = hmod_MUI;
}
}
return status;
}
/**********************************************************************
* LdrFindResource_U (NTDLL.@)
*/
NTSTATUS WINAPI DECLSPEC_HOTPATCH LdrFindResource_U( HMODULE hmod, const LDR_RESOURCE_INFO *info,
ULONG level, const IMAGE_RESOURCE_DATA_ENTRY **entry )
{
const void *res;
NTSTATUS status;
__TRY
{
if (info) TRACE( "module %p type %s name %s lang %04x level %d\n",
hmod, debugstr_w((LPCWSTR)info->Type),
level > 1 ? debugstr_w((LPCWSTR)info->Name) : "",
level > 2 ? info->Language : 0, level );
status = find_entry( hmod, info, level, &res, FALSE );
if (status == STATUS_SUCCESS) *entry = res;
else
{
BOOL is_MUI;
status = load_MUI_module( &hmod, &is_MUI );
if (status == STATUS_SUCCESS)
{
status = find_entry( hmod, info, level, &res, FALSE );
if (status == STATUS_SUCCESS) *entry = res;
}
else if (is_MUI)
ERR( "module %p has MUI_RC_CONFIG but .mui resource file was not found\n", hmod );
}
}
__EXCEPT_PAGE_FAULT
{
return GetExceptionCode();
}
__ENDTRY;
return status;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment