From: Bernhard Loos Subject: [PATCH 2/2] ntdll: deal with images which override the section table during mapping Message-Id: Date: Wed, 12 Oct 2011 14:37:36 +0200 --- dlls/kernel32/tests/loader.c | 75 +++++++++++++++++++++++++++++++++++++++++- dlls/ntdll/virtual.c | 33 ++++++++++++++++-- 2 files changed, 103 insertions(+), 5 deletions(-) diff --git a/dlls/kernel32/tests/loader.c b/dlls/kernel32/tests/loader.c index a257695..b99b8ff 100644 --- a/dlls/kernel32/tests/loader.c +++ b/dlls/kernel32/tests/loader.c @@ -34,7 +34,7 @@ static PVOID RVAToAddr(DWORD_PTR rva, HMODULE module) return ((char*) module) + rva; } -static const struct +static struct { WORD e_magic; /* 00: MZ Header signature */ WORD unused[29]; @@ -703,6 +703,7 @@ static void test_section_mapping(void) char temp_path[MAX_PATH]; char dll_name[MAX_PATH]; BOOL ret; + int i; GetSystemInfo(&si); @@ -768,6 +769,78 @@ static void test_section_mapping(void) SetLastError(0xdeadbeef); hlib = LoadLibrary(dll_name); ok(hlib == 0 && GetLastError() == ERROR_BAD_EXE_FORMAT, "LoadLibrary succeded or wrong error: %d\n", GetLastError()); + + + + GetTempFileName(temp_path, "ldr", 0, dll_name); + + /*trace("creating %s\n", dll_name);*/ + hfile = CreateFileA(dll_name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); + if (hfile == INVALID_HANDLE_VALUE) + { + ok(0, "could not create %s\n", dll_name); + return; + } + + dos_header.e_lfanew = si.dwPageSize - sizeof(DWORD) - sizeof(IMAGE_FILE_HEADER) + - sizeof(IMAGE_OPTIONAL_HEADER); + SetLastError(0xdeadbeef); + ret = WriteFile(hfile, &dos_header, sizeof(dos_header), &written, NULL); + ok(ret, "WriteFile error %d\n", GetLastError()); + total = written; + + assert(dos_header.e_lfanew - total < sizeof(filler)); + SetLastError(0xdeadbeef); + ret = WriteFile(hfile, filler, dos_header.e_lfanew - total, &written, NULL); + ok(ret, "WriteFile error %d\n", GetLastError()); + total += written; + + nt_header.FileHeader.NumberOfSections = 3; + + nt_header.OptionalHeader.SizeOfImage = si.dwPageSize * (nt_header.FileHeader.NumberOfSections + 1); + nt_header.OptionalHeader.SizeOfHeaders = si.dwPageSize; + SetLastError(0xdeadbeef); + ret = WriteFile(hfile, &nt_header, sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER), &written, NULL); + ok(ret, "WriteFile error %d\n", GetLastError()); + total += written; + SetLastError(0xdeadbeef); + ret = WriteFile(hfile, &nt_header.OptionalHeader, sizeof(IMAGE_OPTIONAL_HEADER), &written, NULL); + ok(ret, "WriteFile error %d\n", GetLastError()); + total += written; + + section.SizeOfRawData = sizeof(section_data); + section.PointerToRawData = si.dwPageSize + nt_header.OptionalHeader.FileAlignment; + section.Misc.VirtualSize = section.SizeOfRawData; + for (i = 0; i < nt_header.FileHeader.NumberOfSections; i++) + { + section.VirtualAddress = si.dwPageSize * (i + 1); + SetLastError(0xdeadbeef); + ret = WriteFile(hfile, §ion, sizeof(section), &written, NULL); + ok(ret, "WriteFile error %d\n", GetLastError()); + total += written; + } + + assert(si.dwPageSize + nt_header.OptionalHeader.FileAlignment - total < sizeof(filler)); + SetLastError(0xdeadbeef); + ret = WriteFile(hfile, filler, si.dwPageSize + nt_header.OptionalHeader.FileAlignment - total, &written, NULL); + ok(ret, "WriteFile error %d\n", GetLastError()); + total += written; + + /* section data */ + SetLastError(0xdeadbeef); + ret = WriteFile(hfile, section_data, sizeof(section_data), &written, NULL); + ok(ret, "WriteFile error %d\n", GetLastError()); + total += written; + + CloseHandle(hfile); + + SetLastError(0xdeadbeef); + hlib = LoadLibrary(dll_name); + ok(hlib != 0, "LoadLibrary error %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = FreeLibrary(hlib); + ok(ret, "FreeLibrary error %d\n", GetLastError()); } START_TEST(loader) diff --git a/dlls/ntdll/virtual.c b/dlls/ntdll/virtual.c index f17f9dc..33e032e 100644 --- a/dlls/ntdll/virtual.c +++ b/dlls/ntdll/virtual.c @@ -1114,6 +1114,7 @@ static NTSTATUS map_image( HANDLE hmapping, int fd, char *base, SIZE_T total_siz IMAGE_DOS_HEADER *dos; IMAGE_NT_HEADERS *nt; IMAGE_SECTION_HEADER *sec; + IMAGE_SECTION_HEADER *section_table = NULL; IMAGE_DATA_DIRECTORY *imports; NTSTATUS status = STATUS_CONFLICTING_ADDRESSES; int i; @@ -1159,8 +1160,10 @@ static NTSTATUS map_image( HANDLE hmapping, int fd, char *base, SIZE_T total_siz header_end = ptr + ROUND_SIZE( 0, header_size ); memset( ptr + header_size, 0, header_end - (ptr + header_size) ); if ((char *)(nt + 1) > header_end) goto error; - sec = (IMAGE_SECTION_HEADER*)((char*)&nt->OptionalHeader+nt->FileHeader.SizeOfOptionalHeader); - if ((char *)(sec + nt->FileHeader.NumberOfSections) > header_end) goto error; + section_table = (IMAGE_SECTION_HEADER*)((char*)&nt->OptionalHeader+nt->FileHeader.SizeOfOptionalHeader); + /* make sure we mapped enough memory */ + if ((char *)(section_table + nt->FileHeader.NumberOfSections) > header_end) goto error; + /* windows is only interested in the size as given by OptionalHeader.SizeOfHeaders */ SizeOfHeaders = ROUND_SIZE( 0, nt->OptionalHeader.SizeOfHeaders ); imports = nt->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_IMPORT; @@ -1182,7 +1185,7 @@ static NTSTATUS map_image( HANDLE hmapping, int fd, char *base, SIZE_T total_siz if (nt->OptionalHeader.FileAlignment != nt->OptionalHeader.SectionAlignment) goto error; for (i = 0; i < nt->FileHeader.NumberOfSections; i++) { - if (sec[i].VirtualAddress != sec[i].PointerToRawData) + if (section_table[i].VirtualAddress != section_table[i].PointerToRawData) goto error; /* Windows refuses to load in that case too */ } @@ -1194,6 +1197,24 @@ static NTSTATUS map_image( HANDLE hmapping, int fd, char *base, SIZE_T total_siz goto done; } + if ((char *)(section_table + nt->FileHeader.NumberOfSections) > ptr + SizeOfHeaders) + { + /* part of the section table is outside of SizeOfHeaders + * at this point, it's likely that a section will get mapped over + * the section table (Borderlands does this) + * Not sure how it works on Windows, but we make a copy of the + * table at this point */ + sec = RtlAllocateHeap( virtual_heap, 0, sizeof(*sec) * nt->FileHeader.NumberOfSections); + if (!sec) + { + status = STATUS_NO_MEMORY; + goto error; + } + memcpy( sec, section_table, sizeof(*sec) * nt->FileHeader.NumberOfSections); + section_table = sec; + } + else + sec = section_table; /* map all the sections */ @@ -1334,7 +1355,7 @@ static NTSTATUS map_image( HANDLE hmapping, int fd, char *base, SIZE_T total_siz VIRTUAL_SetProt( view, ptr, ROUND_SIZE( 0, header_size ), VPROT_COMMITTED | VPROT_READ ); - sec = (IMAGE_SECTION_HEADER*)((char *)&nt->OptionalHeader+nt->FileHeader.SizeOfOptionalHeader); + sec = section_table; for (i = 0; i < nt->FileHeader.NumberOfSections; i++, sec++) { SIZE_T size; @@ -1366,6 +1387,8 @@ static NTSTATUS map_image( HANDLE hmapping, int fd, char *base, SIZE_T total_siz } done: + if (section_table && (char *)section_table != (char *)&nt->OptionalHeader+nt->FileHeader.SizeOfOptionalHeader) + RtlFreeHeap( virtual_heap, 0, section_table ); view->mapping = dup_mapping; server_leave_uninterrupted_section( &csVirtual, &sigset ); @@ -1377,6 +1400,8 @@ static NTSTATUS map_image( HANDLE hmapping, int fd, char *base, SIZE_T total_siz return STATUS_SUCCESS; error: + if (section_table && (char *)section_table != (char *)&nt->OptionalHeader+nt->FileHeader.SizeOfOptionalHeader) + RtlFreeHeap( virtual_heap, 0, section_table ); if (view) delete_view( view ); server_leave_uninterrupted_section( &csVirtual, &sigset ); if (dup_mapping) NtClose( dup_mapping );