[PATCH 1/3] mspatcha: Implement NormalizeFileForPatchSignature.

Jeff Smith whydoubt at gmail.com
Fri May 22 11:21:34 CDT 2020


Signed-off-by: Jeff Smith <whydoubt at 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, &section_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, &section_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




More information about the wine-devel mailing list