[PATCH v2] ntoskrnl.exe: fix handling relocations on page boundary
Rafał H
rafalh92 at outlook.com
Sun Feb 17 11:34:47 CST 2019
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.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=28254
Signed-off-by: Rafał Harabień <rafalh92 at outlook.com>
---
dlls/ntoskrnl.exe/ntoskrnl.c | 110 ++++++++++++++++++++++++++++-------
1 file changed, 89 insertions(+), 21 deletions(-)
diff --git a/dlls/ntoskrnl.exe/ntoskrnl.c b/dlls/ntoskrnl.exe/ntoskrnl.c
index 4b62660750..97e9549bd1 100644
--- a/dlls/ntoskrnl.exe/ntoskrnl.c
+++ b/dlls/ntoskrnl.exe/ntoskrnl.c
@@ -25,6 +25,7 @@
#include "wine/port.h"
#include <stdarg.h>
+#include <assert.h>
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
@@ -3223,6 +3224,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 )
{
@@ -3232,6 +3311,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;
@@ -3246,28 +3327,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;
+ status = perform_relocations(module, nt->OptionalHeader.SizeOfImage);
+ if (status != STATUS_SUCCESS)
+ goto error;
- 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 );
- }
+ /* 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 */
--
2.17.1
More information about the wine-devel
mailing list