Signed-off-by: Jeff Smith <whydoubt(a)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();
+}
--
2.23.0