Signed-off-by: Jeff Smith whydoubt@gmail.com --- dlls/mspatcha/Makefile.in | 3 +- dlls/mspatcha/mspatcha_main.c | 8 +- dlls/mspatcha/signature.c | 291 +++++++++++++++++++++++++++++ dlls/mspatcha/signature.h | 29 +++ dlls/mspatcha/tests/Makefile.in | 3 +- dlls/mspatcha/tests/signature.c | 319 ++++++++++++++++++++++++++++++++ 6 files changed, 648 insertions(+), 5 deletions(-) create mode 100644 dlls/mspatcha/signature.c create mode 100644 dlls/mspatcha/signature.h create mode 100644 dlls/mspatcha/tests/signature.c
diff --git a/dlls/mspatcha/Makefile.in b/dlls/mspatcha/Makefile.in index a380f3f1a4..c0e3fc6adb 100644 --- a/dlls/mspatcha/Makefile.in +++ b/dlls/mspatcha/Makefile.in @@ -6,6 +6,7 @@ EXTRADLLFLAGS = -mno-cygwin C_SRCS = \ lzxd_dec.c \ mspatcha_main.c \ - pa19.c + pa19.c \ + signature.c
RC_SRCS = version.rc diff --git a/dlls/mspatcha/mspatcha_main.c b/dlls/mspatcha/mspatcha_main.c index 167c3fda9b..34dc46b216 100644 --- a/dlls/mspatcha/mspatcha_main.c +++ b/dlls/mspatcha/mspatcha_main.c @@ -38,6 +38,7 @@ #include "wine/debug.h"
#include "pa19.h" +#include "signature.h"
WINE_DEFAULT_DEBUG_CHANNEL(mspatcha);
@@ -283,8 +284,9 @@ INT WINAPI NormalizeFileForPatchSignature(PVOID file_buffer, ULONG file_size, UL ULONG new_coff_base, ULONG new_coff_time, ULONG ignore_range_count, PPATCH_IGNORE_RANGE ignore_range, ULONG retain_range_count, PPATCH_RETAIN_RANGE retain_range) { - FIXME("stub - %p, %u, %x, %p, %u, %u, %u, %p, %u, %p\n", file_buffer, file_size, flags, options, new_coff_base, + TRACE("%p, %u, %x, %p, %u, %u, %u, %p, %u, %p\n", file_buffer, file_size, flags, options, new_coff_base, new_coff_time, ignore_range_count, ignore_range, retain_range_count, retain_range); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return 0; + + return normalize_for_patch_signature(file_buffer, file_size, flags, new_coff_base, new_coff_time, + ignore_range_count, ignore_range, retain_range_count, retain_range); } diff --git a/dlls/mspatcha/signature.c b/dlls/mspatcha/signature.c new file mode 100644 index 0000000000..6f6c085de7 --- /dev/null +++ b/dlls/mspatcha/signature.c @@ -0,0 +1,291 @@ +/* + * PatchAPI patch signature calculation + * + * Copyright 2020 Jeff Smith + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <stdarg.h> + +#include "windef.h" +#include "wine/heap.h" + +#include "patchapi.h" + +#include "signature.h" + +/* + * Check for PE32 signatures and return file offset if found. + */ +DWORD get_pe_offset(LPCVOID file_buffer, ULONG file_size) +{ + PIMAGE_NT_HEADERS32 image; + PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)file_buffer; + + if (file_size < 512 || dos_header->e_magic != IMAGE_DOS_SIGNATURE) + return 0; + image = (PIMAGE_NT_HEADERS32)((PBYTE)file_buffer + dos_header->e_lfanew); + if (image->Signature != IMAGE_NT_SIGNATURE || + image->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) + return 0; + + return dos_header->e_lfanew; +} + +struct image_ctx +{ + PVOID data; + DWORD data_size; + PIMAGE_NT_HEADERS32 image; + PIMAGE_SECTION_HEADER first_header; + DWORD section_count; + PIMAGE_DATA_DIRECTORY data_dir; +}; + +static void init_image_ctx(struct image_ctx *ctx, PVOID data, DWORD data_size, DWORD pe_offset) +{ + ctx->data = data; + ctx->data_size = data_size; + ctx->image = (PIMAGE_NT_HEADERS32)((PBYTE)data + pe_offset); + ctx->first_header = (PIMAGE_SECTION_HEADER) + ((PBYTE)&ctx->image->OptionalHeader + ctx->image->FileHeader.SizeOfOptionalHeader); + ctx->section_count = ctx->image->FileHeader.NumberOfSections; + ctx->data_dir = ctx->image->OptionalHeader.DataDirectory; +} + +static PIMAGE_SECTION_HEADER match_section(struct image_ctx *ctx, DWORD address, PDWORD section_offset) +{ + PIMAGE_SECTION_HEADER header; + + for (header = ctx->first_header; header < ctx->first_header + ctx->section_count; header++) + { + if (address >= header->VirtualAddress) + { + DWORD offset = address - header->VirtualAddress; + if (offset < header->SizeOfRawData) + { + *section_offset = offset; + return header; + } + } + } + + return NULL; +} + +static PVOID rva_to_pointer(struct image_ctx *ctx, DWORD address) +{ + PIMAGE_SECTION_HEADER header; + DWORD section_offset; + + header = match_section(ctx, address, §ion_offset); + if (!header) + return NULL; + + return (PBYTE)ctx->data + header->PointerToRawData + section_offset; +} + +struct reloc_entry { + BYTE type; + DWORD rva; +}; + +static void decode_reloc_table(PBYTE reloc_data, size_t reloc_data_size, + struct reloc_entry **reloc_table, DWORD *reloc_count) +{ + PIMAGE_BASE_RELOCATION base_block; + DWORD block_entries; + PWORD type_offset; + DWORD i; + + DWORD total_entries = 0; + DWORD offset = 0; + + /* Calculate number of entries */ + while (offset < reloc_data_size) + { + base_block = (PIMAGE_BASE_RELOCATION)(reloc_data + offset); + if (base_block->SizeOfBlock < 8) + break; + total_entries += (base_block->SizeOfBlock - sizeof(*base_block)) / sizeof(WORD); + offset += base_block->SizeOfBlock; + } + + if (offset != reloc_data_size) + return; + + /* Allocate space for entries */ + *reloc_table = heap_calloc(total_entries, sizeof(**reloc_table)); + + total_entries = 0; + offset = 0; + + /* Decode entries */ + while (offset < reloc_data_size) + { + base_block = (PIMAGE_BASE_RELOCATION)(reloc_data + offset); + block_entries = (base_block->SizeOfBlock - sizeof(*base_block)) / sizeof(WORD); + type_offset = (PWORD)(base_block + 1); + + for (i = 0; i < block_entries; i++) + { + if (type_offset[i]) + { + (*reloc_table)[total_entries].type = (type_offset[i] >> 12) & 0xf; + (*reloc_table)[total_entries].rva = (type_offset[i] & 0xfff) + + base_block->VirtualAddress; + total_entries++; + } + } + + offset += base_block->SizeOfBlock; + } + + *reloc_count = total_entries; +} + +static void rebase_pe(struct image_ctx *ctx, DWORD base_adjust, PIMAGE_DATA_DIRECTORY base_reloc_dir) +{ + PVOID reloc_data; + struct reloc_entry *reloc_table = NULL; + DWORD reloc_count = 0; + DWORD i; + + reloc_data = rva_to_pointer(ctx, base_reloc_dir->VirtualAddress); + if (!reloc_data) + return; + + decode_reloc_table(reloc_data, base_reloc_dir->Size, &reloc_table, &reloc_count); + if (!reloc_table) + return; + + for (i = 0; i < reloc_count; i++) + { + PVOID reloc_pointer = rva_to_pointer(ctx, reloc_table[i].rva); + if (reloc_table[i].type == IMAGE_REL_BASED_HIGHLOW && reloc_pointer) + *(PDWORD)reloc_pointer += base_adjust; + } + + heap_free(reloc_table); +} + +/* + * Normalize PE32 executable images. + * + * TODO: + * - Support PATCH_OPTION_NO_BINDFIX and PATCH_OPTION_NO_LOCKFIX flags. + */ +BOOL normalize_pe(PVOID file_buffer, ULONG file_size, DWORD pe_offset, + ULONG flags, ULONG new_coff_base, ULONG new_coff_time) +{ + struct image_ctx ctx; + BOOL changed = FALSE; + + init_image_ctx(&ctx, file_buffer, file_size, pe_offset); + + /* Update PE timestamp as needed */ + if (!(flags & PATCH_OPTION_NO_REBASE) && new_coff_time != 0 && + new_coff_time != ctx.image->FileHeader.TimeDateStamp) + { + ctx.image->FileHeader.TimeDateStamp = new_coff_time; + changed = TRUE; + } + + /* Rebase PE image as needed */ + if (!(flags & PATCH_OPTION_NO_REBASE) && new_coff_base != 0) + { + PIMAGE_DATA_DIRECTORY base_reloc_dir = ctx.data_dir + IMAGE_DIRECTORY_ENTRY_BASERELOC; + DWORD base_adjust = new_coff_base - ctx.image->OptionalHeader.ImageBase; + PIMAGE_SECTION_HEADER header; + DWORD section_offset = 0; + + header = match_section(&ctx, base_reloc_dir->VirtualAddress, §ion_offset); + section_offset += base_reloc_dir->Size; + if (base_adjust && header && section_offset <= header->SizeOfRawData && + (header->PointerToRawData + section_offset <= file_size)) + { + ctx.image->OptionalHeader.ImageBase = new_coff_base; + rebase_pe(&ctx, base_adjust, base_reloc_dir); + changed = TRUE; + } + } + + /* Update PE checksum as needed */ + if (flags & PATCH_OPTION_NO_CHECKSUM) + { + if (ctx.image->OptionalHeader.CheckSum != 0) + { + ctx.image->OptionalHeader.CheckSum = 0; + changed = TRUE; + } + } + else if (changed) + { + PWORD wptr = (PWORD)file_buffer; + DWORD sum = 0; + DWORD i; + + for (i = 0; i < file_size; i += 2, wptr++) + { + /* Skip both words of the old checksum */ + if (wptr == (PWORD)&ctx.image->OptionalHeader.CheckSum) + { + i += 2; + wptr++; + continue; + } + + sum += *wptr; + if (HIWORD(sum) != 0) + sum = LOWORD(sum) + HIWORD(sum); + } + ctx.image->OptionalHeader.CheckSum = sum + file_size; + } + + return changed; +} + +INT normalize_for_patch_signature(PVOID file_buffer, ULONG file_size, + ULONG flags, ULONG new_coff_base, ULONG new_coff_time, + ULONG ignore_range_count, PPATCH_IGNORE_RANGE ignore_range, + ULONG retain_range_count, PPATCH_RETAIN_RANGE retain_range) +{ + BOOL changed = FALSE; + DWORD pe_offset; + DWORD i; + + pe_offset = get_pe_offset(file_buffer, file_size); + if (pe_offset) + changed = normalize_pe(file_buffer, file_size, pe_offset, flags, new_coff_base, new_coff_time); + + for (i = 0; i < ignore_range_count; i++) + { + if (ignore_range[i].OffsetInOldFile + ignore_range[i].LengthInBytes > file_size) + continue; + memset((PBYTE)file_buffer + ignore_range[i].OffsetInOldFile, 0, ignore_range[i].LengthInBytes); + changed = TRUE; + } + + for (i = 0; i < retain_range_count; i++) + { + if (retain_range[i].OffsetInOldFile + retain_range[i].LengthInBytes > file_size) + continue; + memset((PBYTE)file_buffer + retain_range[i].OffsetInOldFile, 0, retain_range[i].LengthInBytes); + changed = TRUE; + } + + return (changed) ? 2 : 1; +} diff --git a/dlls/mspatcha/signature.h b/dlls/mspatcha/signature.h new file mode 100644 index 0000000000..b55ab84b7c --- /dev/null +++ b/dlls/mspatcha/signature.h @@ -0,0 +1,29 @@ +/* + * PatchAPI patch signature calculation + * + * Copyright 2020 Jeff Smith + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +DWORD get_pe_offset(LPCVOID file_buffer, ULONG file_size); + +BOOL normalize_pe(PVOID file_buffer, ULONG file_size, DWORD pe_offset, + ULONG flags, ULONG new_coff_base, ULONG new_coff_time); + +INT normalize_for_patch_signature(PVOID file_buffer, ULONG file_size, + ULONG flags, ULONG new_coff_base, ULONG new_coff_time, + ULONG ignore_range_count, PPATCH_IGNORE_RANGE ignore_range, + ULONG retain_range_count, PPATCH_RETAIN_RANGE retain_range); diff --git a/dlls/mspatcha/tests/Makefile.in b/dlls/mspatcha/tests/Makefile.in index ef1d19271c..c855492c1a 100644 --- a/dlls/mspatcha/tests/Makefile.in +++ b/dlls/mspatcha/tests/Makefile.in @@ -2,7 +2,8 @@ TESTDLL = mspatcha.dll IMPORTS = mspatcha
C_SRCS = \ - apply_patch.c + apply_patch.c \ + signature.c
RC_SRCS = \ mspatcha.rc diff --git a/dlls/mspatcha/tests/signature.c b/dlls/mspatcha/tests/signature.c new file mode 100644 index 0000000000..bd2f9dc8be --- /dev/null +++ b/dlls/mspatcha/tests/signature.c @@ -0,0 +1,319 @@ +/* + * Unit tests for Patch API functions + * + * Copyright 2020 Jeff Smith + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "wine/test.h" + +#include "patchapi.h" + +static BOOL (WINAPI *pNormalizeFileForPatchSignature)(PVOID, ULONG, ULONG, PATCH_OPTION_DATA*, ULONG, + ULONG, ULONG, PPATCH_IGNORE_RANGE, ULONG, PPATCH_RETAIN_RANGE); + +static BYTE array[1024]; + +static BOOL init_function_pointers(void) +{ + HMODULE mspatcha = LoadLibraryA("mspatcha.dll"); + if (!mspatcha) + { + win_skip("mspatcha.dll not found\n"); + return FALSE; + } + pNormalizeFileForPatchSignature = (void *)GetProcAddress(mspatcha, "NormalizeFileForPatchSignature"); + + return TRUE; +} + +static void test_normalize_ignore_range(void) +{ + PATCH_IGNORE_RANGE ir_bad_good[] = { + /* Range partially outside the region to normalize */ + {7, 2}, + /* Range within the region to normalize */ + {3, 2} + }; + + BOOL result; + + if (!pNormalizeFileForPatchSignature) + return; + + /* Skip partially out-of-bounds ignore */ + memcpy(array, "abcdefgh", 8); + result = pNormalizeFileForPatchSignature(array, 8, 0, NULL, 0, 0, 1, ir_bad_good, 0, NULL); + ok(result == 1, "Expected %d, got %d\n", 1, result); + ok(!memcmp(array, "abcdefgh", 8), "Buffer should not have been modified\n"); + + /* Skip partially out-of-bounds ignore, apply in-bounds ignore */ + memcpy(array, "abcdefgh", 8); + result = pNormalizeFileForPatchSignature(array, 8, 0, NULL, 0, 0, 2, ir_bad_good, 0, NULL); + ok(result == 2, "Expected %d, got %d\n", 2, result); + ok(!memcmp(array, "abc\0\0fgh", 8), "Buffer not modified correctly\n"); + + /* Blanking a region already consisting of 0's is considered a change */ + memset(array, 0, 8); + result = pNormalizeFileForPatchSignature(array, 8, 0, NULL, 0, 0, 2, ir_bad_good, 0, NULL); + ok(result == 2, "Expected %d, got %d\n", 2, result); + ok(!memcmp(array, "\0\0\0\0\0\0\0\0", 8), "Buffer should not have been modified\n"); +} + +static void test_normalize_retain_range(void) +{ + PATCH_RETAIN_RANGE rr_bad_good[] = { + /* Range partially outside the region to normalize */ + {7, 2, 3}, + /* Range within the region to normalize */ + {1, 2, 5} + }; + + BOOL result; + + if (!pNormalizeFileForPatchSignature) + return; + + /* Skip partially out-of-bounds retain */ + memcpy(array, "abcdefgh", 8); + result = pNormalizeFileForPatchSignature(array, 8, 0, NULL, 0, 0, 0, NULL, 1, rr_bad_good); + ok(result == 1, "Expected %d, got %d\n", 1, result); + ok(!memcmp(array, "abcdefgh", 8), "Buffer should not have been modified\n"); + + /* Skip partially out-of-bounds retain, apply in-bounds retain */ + memcpy(array, "abcdefgh", 8); + result = pNormalizeFileForPatchSignature(array, 8, 0, NULL, 0, 0, 0, NULL, 2, rr_bad_good); + ok(result == 2, "Expected %d, got %d\n", 2, result); + ok(!memcmp(array, "a\0\0defgh", 8), "Buffer not modified correctly\n"); + + /* Blanking a region already consisting of 0's is considered a change */ + memset(array, 0, 8); + result = pNormalizeFileForPatchSignature(array, 8, 0, NULL, 0, 0, 0, NULL, 2, rr_bad_good); + ok(result == 2, "Expected %d, got %d\n", 2, result); + ok(!memcmp(array, "\0\0\0\0\0\0\0\0", 8), "Buffer should not have been modified\n"); +} + +static void setup_pe(PVOID buffer, PIMAGE_NT_HEADERS32 *header) +{ + PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)buffer; + PIMAGE_NT_HEADERS32 nt_header = (PIMAGE_NT_HEADERS32)(dos_header + 1); + + dos_header->e_magic = IMAGE_DOS_SIGNATURE; + dos_header->e_lfanew = sizeof(*dos_header); + + nt_header->Signature = IMAGE_NT_SIGNATURE; + nt_header->FileHeader.NumberOfSections = 0; + nt_header->FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER32); + nt_header->OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR32_MAGIC; + nt_header->OptionalHeader.NumberOfRvaAndSizes = IMAGE_NUMBEROF_DIRECTORY_ENTRIES; + + if (header) + *header = nt_header; +} + +static void setup_pe_with_sections(PVOID buffer, PIMAGE_NT_HEADERS32 *header, PDWORD *reloc_target) +{ + PIMAGE_SECTION_HEADER section_headers; + PIMAGE_BASE_RELOCATION base_relocation; + PWORD type_offset; + DWORD dir_entry = IMAGE_DIRECTORY_ENTRY_BASERELOC; + + DWORD code_rva_base = 0x1000; + DWORD reloc_rva_base = 0x2000; + DWORD code_raw_base = 0x200; + DWORD reloc_raw_base = 0x300; + DWORD image_base_init = 0x400000; + WORD reloc_target_offset = 0x30; + + setup_pe(buffer, header); + + (*header)->FileHeader.NumberOfSections = 2; + (*header)->OptionalHeader.ImageBase = image_base_init; + (*header)->OptionalHeader.DataDirectory[dir_entry].VirtualAddress = reloc_rva_base; + (*header)->OptionalHeader.DataDirectory[dir_entry].Size = 12; + section_headers = (PIMAGE_SECTION_HEADER)((*header) + 1); + memcpy(section_headers[0].Name, ".text\0\0\0", 8); + section_headers[0].Misc.VirtualSize = 0xf2; + section_headers[0].VirtualAddress = code_rva_base; + section_headers[0].SizeOfRawData = 0x100; + section_headers[0].PointerToRawData = code_raw_base; + memcpy(section_headers[1].Name, ".reloc\0\0", 8); + section_headers[1].Misc.VirtualSize = 0xf2; + section_headers[1].VirtualAddress = reloc_rva_base; + section_headers[1].SizeOfRawData = 0x100; + section_headers[1].PointerToRawData = reloc_raw_base; + base_relocation = (PIMAGE_BASE_RELOCATION)(&array[reloc_raw_base]); + base_relocation->VirtualAddress = code_rva_base; + base_relocation->SizeOfBlock = 12; + type_offset = (PWORD)(base_relocation + 1); + *type_offset = (IMAGE_REL_BASED_HIGHLOW << 12) | reloc_target_offset; + + if (reloc_target) + *reloc_target = (PDWORD)(&array[code_raw_base + reloc_target_offset]); +} + +static void test_normalize_flags(void) +{ + struct { + BOOL init_magic_64; + DWORD init_checksum; + DWORD init_timestamp; + ULONG buffer_size; + ULONG flags; + ULONG image_base; + ULONG timestamp; + BOOL exp_result; + DWORD exp_checksum; + DWORD exp_timestamp; + } test[] = + { + {FALSE, 0xdeadbeef, 0xdeadbeef, + 512, 0, 0, 0, + 1, 0xdeadbeef, 0xdeadbeef}, + /* No rebase */ + {FALSE, 0xdeadbeef, 0xdeadbeef, + 512, PATCH_OPTION_NO_REBASE, 0, 0, + 1, 0xdeadbeef, 0xdeadbeef}, + + /* Blank checksum */ + {FALSE, 0xdeadbeef, 0xdeadbeef, + 512, PATCH_OPTION_NO_CHECKSUM, 0, 0, + 2, 0, 0xdeadbeef}, + /* Blank checksum, no rebase */ + {FALSE, 0xdeadbeef, 0xdeadbeef, + 512, PATCH_OPTION_NO_CHECKSUM | PATCH_OPTION_NO_REBASE, 0, 0, + 2, 0, 0xdeadbeef}, + /* Blank checksum, already 0 */ + {FALSE, 0, 0xdeadbeef, + 512, PATCH_OPTION_NO_CHECKSUM, 0, 0, + 1, 0, 0xdeadbeef}, + /* Blank checksum fail - filesize too small */ + {FALSE, 0xdeadbeef, 0xdeadbeef, + 511, PATCH_OPTION_NO_CHECKSUM, 0, 0, + 1, 0xdeadbeef, 0xdeadbeef}, + /* Blank checksum fail - PE32+ magic */ + {TRUE, 0xdeadbeef, 0xdeadbeef, + 512, PATCH_OPTION_NO_CHECKSUM, 0, 0, + 1, 0xdeadbeef, 0xdeadbeef}, + + /* Set timestamp */ + {FALSE, 0xdeadbeef, 0xdeadbeef, + 512, 0, 0, 0x12345678, + 2, 0xa61e, 0x12345678}, + /* Set timestamp, no rebase */ + {FALSE, 0xdeadbeef, 0xdeadbeef, + 512, PATCH_OPTION_NO_REBASE, 0, 0x12345678, + 1, 0xdeadbeef, 0xdeadbeef}, + /* Set timestamp, to same value */ + {FALSE, 0xdeadbeef, 0x12345678, + 512, 0, 0, 0x12345678, + 1, 0xdeadbeef, 0x12345678}, + /* Set timestamp, no_rebase, blank checksum */ + {FALSE, 0xdeadbeef, 0xdeadbeef, + 512, PATCH_OPTION_NO_CHECKSUM | PATCH_OPTION_NO_REBASE, 0, 0x12345678, + 2, 0, 0xdeadbeef}, + }; + + PIMAGE_NT_HEADERS32 header; + BOOL result; + DWORD i; + + if (!pNormalizeFileForPatchSignature) + return; + + for (i = 0; i < ARRAY_SIZE(test); i++) + { + memset(array, 0xcc, 512); + setup_pe(array, &header); + if (test[i].init_magic_64) + header->OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR64_MAGIC; + header->FileHeader.TimeDateStamp = test[i].init_timestamp; + header->OptionalHeader.CheckSum = test[i].init_checksum; + result = pNormalizeFileForPatchSignature(array, test[i].buffer_size, test[i].flags, NULL, + test[i].image_base, test[i].timestamp, 0, NULL, 0, NULL); + + ok(result == test[i].exp_result, "[%d] Expected %d, got %d\n", i, test[i].exp_result, result); + ok(header->FileHeader.TimeDateStamp == test[i].exp_timestamp, + "[%d] Expected timestamp %#x, got %#x\n", i, + test[i].exp_timestamp, header->FileHeader.TimeDateStamp); + ok(header->OptionalHeader.CheckSum == test[i].exp_checksum, + "[%d] Expected checksum %#x, got %#x\n", i, + test[i].exp_checksum, header->OptionalHeader.CheckSum); + } +} + +static void test_normalize_rebase(void) +{ + PIMAGE_NT_HEADERS32 header; + BOOL result; + PDWORD reloc_target; + DWORD image_base_initial; + DWORD image_base_new = 0x500000; + DWORD reloc_target_value = 0x3fffffff; + DWORD reloc_target_exp; + + if (!pNormalizeFileForPatchSignature) + return; + + memset(array, 0, 1024); + setup_pe_with_sections(array, &header, &reloc_target); + *reloc_target = reloc_target_value; + reloc_target_exp = reloc_target_value + (image_base_new - header->OptionalHeader.ImageBase); + result = pNormalizeFileForPatchSignature(array, 1024, 0, NULL, image_base_new, 0, 0, NULL, 0, NULL); + ok(result == 2, "Expected %d, got %d\n", 2, result); + ok(header->OptionalHeader.ImageBase == image_base_new, "Expected %#x, got %#x\n", + image_base_new, header->OptionalHeader.ImageBase); + ok(*reloc_target == reloc_target_exp, "Expected %#x, got %#x\n", reloc_target_exp, *reloc_target); + + /* Relocation table extends beyond virtual size, but within raw data size */ + memset(array, 0, 1024); + setup_pe_with_sections(array, &header, NULL); + header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress += 0xf4; + result = pNormalizeFileForPatchSignature(array, 1024, 0, NULL, image_base_new, 0, 0, NULL, 0, NULL); + ok(result == 2, "Expected %d, got %d\n", 2, result); + ok(header->OptionalHeader.ImageBase == image_base_new, "Expected %#x, got %#x\n", + image_base_new, header->OptionalHeader.ImageBase); + + /* Relocation table starts within raw data size, but ends beyond */ + memset(array, 0, 1024); + setup_pe_with_sections(array, &header, NULL); + image_base_initial = header->OptionalHeader.ImageBase; + header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress += 0xf8; + result = pNormalizeFileForPatchSignature(array, 1024, 0, NULL, image_base_new, 0, 0, NULL, 0, NULL); + ok(result == 1, "Expected %d, got %d\n", 2, result); + ok(header->OptionalHeader.ImageBase == image_base_initial, "Expected %#x, got %#x\n", + image_base_initial, header->OptionalHeader.ImageBase); + + /* Relocation table extends beyond end of file */ + memset(array, 0, 1024); + setup_pe_with_sections(array, &header, NULL); + image_base_initial = header->OptionalHeader.ImageBase; + result = pNormalizeFileForPatchSignature(array, 779, 0, NULL, image_base_new, 0, 0, NULL, 0, NULL); + ok(result == 1, "Expected %d, got %d\n", 1, result); + ok(header->OptionalHeader.ImageBase == image_base_initial, "Expected %#x, got %#x\n", + image_base_initial, header->OptionalHeader.ImageBase); +} + +START_TEST(signature) +{ + if (!init_function_pointers()) + return; + + test_normalize_ignore_range(); + test_normalize_retain_range(); + test_normalize_flags(); + test_normalize_rebase(); +}