Code is only used for non-page-aligned device drivers. Previously if relocation was happening on page boundary it crashed because only one page had changed protection in load_driver_module. Now code is copied from ntdll and changes protection for all module pages before actual relocation starts. Fixes running MTA: San Andreas game.
From: Rafał Harabień rafalh92@outlook.com Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=28254 Signed-off-by: Rafał Harabień rafalh92@outlook.com --- dlls/ntoskrnl.exe/ntoskrnl.c | 111 ++++++++++++++++++++++++++++------- 1 file changed, 89 insertions(+), 22 deletions(-)
diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c index cf5cde9a82..86111b4cba 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/ntoskrnl.c @@ -3674,6 +3674,84 @@ static LDR_MODULE *find_ldr_module( HMODULE module ) return ldr; }
+/* convert PE image VirtualAddress to Real Address */ +static inline void *get_rva( HMODULE module, DWORD va ) +{ + return (void *)((char *)module + va); +} + +/* Copied from ntdll with checks for page alignment and characteristics removed */ +static NTSTATUS perform_relocations( void *module, SIZE_T len ) +{ + IMAGE_NT_HEADERS *nt; + char *base; + IMAGE_BASE_RELOCATION *rel, *end; + const IMAGE_DATA_DIRECTORY *relocs; + const IMAGE_SECTION_HEADER *sec; + INT_PTR delta; + ULONG protect_old[96], i; + + nt = RtlImageNtHeader( module ); + base = (char *)nt->OptionalHeader.ImageBase; + + assert( module != base ); + + relocs = &nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; + + if (nt->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED) + { + WARN( "Need to relocate module from %p to %p, but there are no relocation records\n", + base, module ); + return STATUS_CONFLICTING_ADDRESSES; + } + + if (!relocs->Size) return STATUS_SUCCESS; + if (!relocs->VirtualAddress) return STATUS_CONFLICTING_ADDRESSES; + + if (nt->FileHeader.NumberOfSections > ARRAY_SIZE( protect_old )) + return STATUS_INVALID_IMAGE_FORMAT; + + sec = (const IMAGE_SECTION_HEADER *)((const char *)&nt->OptionalHeader + + nt->FileHeader.SizeOfOptionalHeader); + for (i = 0; i < nt->FileHeader.NumberOfSections; i++) + { + void *addr = get_rva( module, sec[i].VirtualAddress ); + SIZE_T size = sec[i].SizeOfRawData; + NtProtectVirtualMemory( NtCurrentProcess(), &addr, + &size, PAGE_READWRITE, &protect_old[i] ); + } + + TRACE( "relocating from %p-%p to %p-%p\n", + base, base + len, module, (char *)module + len ); + + rel = get_rva( module, relocs->VirtualAddress ); + end = get_rva( module, relocs->VirtualAddress + relocs->Size ); + delta = (char *)module - base; + + while (rel < end - 1 && rel->SizeOfBlock) + { + if (rel->VirtualAddress >= len) + { + WARN( "invalid address %p in relocation %p\n", get_rva( module, rel->VirtualAddress ), rel ); + return STATUS_ACCESS_VIOLATION; + } + rel = LdrProcessRelocationBlock( get_rva( module, rel->VirtualAddress ), + (rel->SizeOfBlock - sizeof(*rel)) / sizeof(USHORT), + (USHORT *)(rel + 1), delta ); + if (!rel) return STATUS_INVALID_IMAGE_FORMAT; + } + + for (i = 0; i < nt->FileHeader.NumberOfSections; i++) + { + void *addr = get_rva( module, sec[i].VirtualAddress ); + SIZE_T size = sec[i].SizeOfRawData; + NtProtectVirtualMemory( NtCurrentProcess(), &addr, + &size, protect_old[i], &protect_old[i] ); + } + + return STATUS_SUCCESS; +} + /* load the driver module file */ static HMODULE load_driver_module( const WCHAR *name ) { @@ -3683,6 +3761,8 @@ static HMODULE load_driver_module( const WCHAR *name ) int i; INT_PTR delta; ULONG size; + DWORD old; + NTSTATUS status; HMODULE module = LoadLibraryW( name );
if (!module) return NULL; @@ -3697,28 +3777,15 @@ static HMODULE load_driver_module( const WCHAR *name ) if (nt->OptionalHeader.SectionAlignment < info.PageSize || !(nt->FileHeader.Characteristics & IMAGE_FILE_DLL)) { - DWORD old; - IMAGE_BASE_RELOCATION *rel, *end; - - if ((rel = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_BASERELOC, &size ))) - { - TRACE( "%s: relocating from %p to %p\n", wine_dbgstr_w(name), (char *)module - delta, module ); - end = (IMAGE_BASE_RELOCATION *)((char *)rel + size); - while (rel < end && rel->SizeOfBlock) - { - void *page = (char *)module + rel->VirtualAddress; - VirtualProtect( page, info.PageSize, PAGE_EXECUTE_READWRITE, &old ); - rel = LdrProcessRelocationBlock( page, (rel->SizeOfBlock - sizeof(*rel)) / sizeof(USHORT), - (USHORT *)(rel + 1), delta ); - if (old != PAGE_EXECUTE_READWRITE) VirtualProtect( page, info.PageSize, old, &old ); - if (!rel) goto error; - } - /* make sure we don't try again */ - size = FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) + nt->FileHeader.SizeOfOptionalHeader; - VirtualProtect( nt, size, PAGE_READWRITE, &old ); - nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = 0; - VirtualProtect( nt, size, old, &old ); - } + status = perform_relocations(module, nt->OptionalHeader.SizeOfImage); + if (status != STATUS_SUCCESS) + goto error; + + /* make sure we don't try again */ + size = FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) + nt->FileHeader.SizeOfOptionalHeader; + VirtualProtect( nt, size, PAGE_READWRITE, &old ); + nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = 0; + VirtualProtect( nt, size, old, &old ); }
/* make sure imports are relocated too */