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(); +}
Signed-off-by: Jeff Smith whydoubt@gmail.com --- dlls/mspatcha/mspatcha_main.c | 46 +++++++++---- dlls/mspatcha/signature.c | 117 ++++++++++++++++++++++++++++++++ dlls/mspatcha/signature.h | 15 ++++ dlls/mspatcha/tests/signature.c | 68 +++++++++++++++++++ include/patchapi.h | 1 + 5 files changed, 235 insertions(+), 12 deletions(-)
diff --git a/dlls/mspatcha/mspatcha_main.c b/dlls/mspatcha/mspatcha_main.c index 34dc46b216..bda761adfe 100644 --- a/dlls/mspatcha/mspatcha_main.c +++ b/dlls/mspatcha/mspatcha_main.c @@ -231,10 +231,19 @@ BOOL WINAPI GetFilePatchSignatureA(LPCSTR filename, ULONG flags, PVOID data, ULO PPATCH_IGNORE_RANGE ignore_range, ULONG retain_range_count, PPATCH_RETAIN_RANGE retain_range, ULONG bufsize, LPSTR buffer) { - FIXME("stub - %s, %x, %p, %u, %p, %u, %p, %u, %p\n", debugstr_a(filename), flags, data, + BOOL ret; + WCHAR *filenameW; + + TRACE("%s, %x, %p, %u, %p, %u, %p, %u, %p\n", debugstr_a(filename), flags, data, ignore_range_count, ignore_range, retain_range_count, retain_range, bufsize, buffer); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; + + if (!(filenameW = strdupAW(filename))) + return FALSE; + ret = get_patch_signature(filenameW, flags, ignore_range_count, ignore_range, + retain_range_count, retain_range, bufsize, buffer); + + HeapFree(GetProcessHeap(), 0, filenameW); + return ret; }
/***************************************************** @@ -244,10 +253,21 @@ BOOL WINAPI GetFilePatchSignatureW(LPCWSTR filename, ULONG flags, PVOID data, UL PPATCH_IGNORE_RANGE ignore_range, ULONG retain_range_count, PPATCH_RETAIN_RANGE retain_range, ULONG bufsize, LPWSTR buffer) { - FIXME("stub - %s, %x, %p, %u, %p, %u, %p, %u, %p\n", debugstr_w(filename), flags, data, + BOOL ret; + LPSTR abuffer; + + TRACE("%s, %x, %p, %u, %p, %u, %p, %u, %p\n", debugstr_w(filename), flags, data, ignore_range_count, ignore_range, retain_range_count, retain_range, bufsize, buffer); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; + + if (!(abuffer = HeapAlloc(GetProcessHeap(), 0, bufsize / 2))) + return FALSE; + ret = get_patch_signature(filename, flags, ignore_range_count, ignore_range, + retain_range_count, retain_range, bufsize / 2, abuffer); + if (ret) + MultiByteToWideChar(CP_ACP, 0, abuffer, -1, buffer, bufsize); + + HeapFree(GetProcessHeap(), 0, abuffer); + return ret; }
/***************************************************** @@ -257,10 +277,11 @@ BOOL WINAPI GetFilePatchSignatureByHandle(HANDLE handle, ULONG flags, PVOID opti PPATCH_IGNORE_RANGE ignore_range, ULONG retain_range_count, PPATCH_RETAIN_RANGE retain_range, ULONG bufsize, LPSTR buffer) { - FIXME("stub - %p, %x, %p, %u, %p, %u, %p, %u, %p\n", handle, flags, options, + TRACE("%p, %x, %p, %u, %p, %u, %p, %u, %p\n", handle, flags, options, ignore_range_count, ignore_range, retain_range_count, retain_range, bufsize, buffer); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; + + return get_patch_signature_by_handle(handle, flags, ignore_range_count, ignore_range, + retain_range_count, retain_range, bufsize, buffer); }
/***************************************************** @@ -271,10 +292,11 @@ BOOL WINAPI GetFilePatchSignatureByBuffer(PBYTE file_buf, ULONG file_size, ULONG ULONG retain_range_count, PPATCH_RETAIN_RANGE retain_range, ULONG bufsize, LPSTR buffer) { - FIXME("stub - %p, %u, %x, %p, %u, %p, %u, %p, %u, %p\n", file_buf, file_size, flags, options, + TRACE("%p, %u, %x, %p, %u, %p, %u, %p, %u, %p\n", file_buf, file_size, flags, options, ignore_range_count, ignore_range, retain_range_count, retain_range, bufsize, buffer); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; + + return get_patch_signature_by_buffer(file_buf, file_size, flags, ignore_range_count, ignore_range, + retain_range_count, retain_range, bufsize, buffer); }
/***************************************************** diff --git a/dlls/mspatcha/signature.c b/dlls/mspatcha/signature.c index 6f6c085de7..434615b8c4 100644 --- a/dlls/mspatcha/signature.c +++ b/dlls/mspatcha/signature.c @@ -19,14 +19,131 @@ */
#include <stdarg.h> +#include <stdlib.h>
#include "windef.h" +#include "winternl.h" #include "wine/heap.h"
#include "patchapi.h"
#include "signature.h"
+typedef struct +{ + unsigned int i[2]; + unsigned int buf[4]; + unsigned char in[64]; + unsigned char digest[16]; +} MD5_CTX; + +extern VOID WINAPI MD5Init( MD5_CTX *); +extern VOID WINAPI MD5Update( MD5_CTX *, const unsigned char *, unsigned int ); +extern VOID WINAPI MD5Final( MD5_CTX *); + +BOOL get_patch_signature(LPCWSTR filename, ULONG flags, + ULONG ignore_range_count, PPATCH_IGNORE_RANGE ignore_range, + ULONG retain_range_count, PPATCH_RETAIN_RANGE retain_range, + ULONG bufsize, LPSTR buffer) +{ + HANDLE file_handle; + BOOL res; + DWORD err = ERROR_SUCCESS; + + file_handle = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (file_handle == INVALID_HANDLE_VALUE) + return FALSE; + + res = get_patch_signature_by_handle(file_handle, flags, ignore_range_count, ignore_range, + retain_range_count, retain_range, bufsize, buffer); + + err = GetLastError(); + CloseHandle(file_handle); + SetLastError(err); + + return res; +} + +BOOL get_patch_signature_by_handle(HANDLE file_handle, ULONG flags, + ULONG ignore_range_count, PPATCH_IGNORE_RANGE ignore_range, + ULONG retain_range_count, PPATCH_RETAIN_RANGE retain_range, + ULONG bufsize, LPSTR buffer) +{ + LPVOID file_buf; + LARGE_INTEGER file_size; + DWORD err; + BOOL res; + + if (file_handle == INVALID_HANDLE_VALUE) + { + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + + file_size.QuadPart = 0; + if (!GetFileSizeEx(file_handle, &file_size)) + return FALSE; + + file_buf = heap_alloc(file_size.QuadPart); + if (!file_buf) + return FALSE; + + res = ReadFile(file_handle, file_buf, file_size.QuadPart, NULL, NULL); + if (res) + res = get_patch_signature_by_buffer(file_buf, file_size.QuadPart, flags, + ignore_range_count, ignore_range, retain_range_count, retain_range, bufsize, buffer); + + err = GetLastError(); + heap_free(file_buf); + SetLastError(err); + + return res; +} + +BOOL get_patch_signature_by_buffer(PVOID file_buffer, ULONG file_size, ULONG flags, + ULONG ignore_range_count, PPATCH_IGNORE_RANGE ignore_range, + ULONG retain_range_count, PPATCH_RETAIN_RANGE retain_range, + ULONG bufsize, LPSTR buffer) +{ + if (!normalize_for_patch_signature(file_buffer, file_size, flags, 0x10000000, 0x10000000, + ignore_range_count, ignore_range, retain_range_count, retain_range)) + return FALSE; + + if (flags & PATCH_OPTION_SIGNATURE_MD5) + { + static const char hex[16] = "0123456789abcdef"; + MD5_CTX ctx; + UINT i; + + MD5Init(&ctx); + MD5Update(&ctx, file_buffer, file_size); + MD5Final(&ctx); + + if (bufsize < 33) + goto insufficient_buffer; + for (i = 0; i < 16; i++) + { + buffer[i*2] = hex[ctx.digest[i] >> 4]; + buffer[i*2+1] = hex[ctx.digest[i] & 0xf]; + } + buffer[32] = 0; + } + else + { + DWORD crc = RtlComputeCrc32(0, file_buffer, file_size); + + if (bufsize < 9) + goto insufficient_buffer; + _ltoa(crc, buffer, 16); + } + + return TRUE; + +insufficient_buffer: + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; +} + /* * Check for PE32 signatures and return file offset if found. */ diff --git a/dlls/mspatcha/signature.h b/dlls/mspatcha/signature.h index b55ab84b7c..fb18f7e288 100644 --- a/dlls/mspatcha/signature.h +++ b/dlls/mspatcha/signature.h @@ -18,6 +18,21 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+BOOL get_patch_signature(LPCWSTR filename, ULONG flags, + ULONG ignore_range_count, PPATCH_IGNORE_RANGE ignore_range, + ULONG retain_range_count, PPATCH_RETAIN_RANGE retain_range, + ULONG bufsize, LPSTR buffer); + +BOOL get_patch_signature_by_handle(HANDLE file_handle, ULONG flags, + ULONG ignore_range_count, PPATCH_IGNORE_RANGE ignore_range, + ULONG retain_range_count, PPATCH_RETAIN_RANGE retain_range, + ULONG bufsize, LPSTR buffer); + +BOOL get_patch_signature_by_buffer(PVOID file_buffer, ULONG file_size, ULONG flags, + ULONG ignore_range_count, PPATCH_IGNORE_RANGE ignore_range, + ULONG retain_range_count, PPATCH_RETAIN_RANGE retain_range, + ULONG bufsize, LPSTR buffer); + DWORD get_pe_offset(LPCVOID file_buffer, ULONG file_size);
BOOL normalize_pe(PVOID file_buffer, ULONG file_size, DWORD pe_offset, diff --git a/dlls/mspatcha/tests/signature.c b/dlls/mspatcha/tests/signature.c index bd2f9dc8be..ad570ef735 100644 --- a/dlls/mspatcha/tests/signature.c +++ b/dlls/mspatcha/tests/signature.c @@ -24,6 +24,8 @@
static BOOL (WINAPI *pNormalizeFileForPatchSignature)(PVOID, ULONG, ULONG, PATCH_OPTION_DATA*, ULONG, ULONG, ULONG, PPATCH_IGNORE_RANGE, ULONG, PPATCH_RETAIN_RANGE); +static BOOL (WINAPI *pGetFilePatchSignatureByBuffer)(PBYTE, ULONG, ULONG, PVOID, ULONG, + PPATCH_IGNORE_RANGE, ULONG, PPATCH_RETAIN_RANGE, ULONG, LPSTR);
static BYTE array[1024];
@@ -36,6 +38,7 @@ static BOOL init_function_pointers(void) return FALSE; } pNormalizeFileForPatchSignature = (void *)GetProcAddress(mspatcha, "NormalizeFileForPatchSignature"); + pGetFilePatchSignatureByBuffer = (void *)GetProcAddress(mspatcha, "GetFilePatchSignatureByBuffer");
return TRUE; } @@ -307,6 +310,70 @@ static void test_normalize_rebase(void) image_base_initial, header->OptionalHeader.ImageBase); }
+static void test_signature_by_buffer(void) +{ + PIMAGE_NT_HEADERS32 header; + BOOL result; + DWORD err; + char buf[33]; + + if (!pGetFilePatchSignatureByBuffer) + return; + + /* Test CRC32 signature */ + memset(array, 0xcc, 8); + buf[0] = '\0'; + result = pGetFilePatchSignatureByBuffer(array, 8, 0, NULL, 0, NULL, 0, NULL, 9, buf); + ok(result == TRUE, "Expected %d, got %d\n", TRUE, result); + ok(!strcmp(buf, "58ea8bb8"), "Expected %s, got %s\n", "58ea8bb8", buf); + + /* Test MD5 signature w/ insufficient buffer */ + memset(array, 0xcc, 8); + buf[0] = '\0'; + SetLastError(0xdeadbeef); + result = pGetFilePatchSignatureByBuffer(array, 8, 0, NULL, 0, NULL, 0, NULL, 8, buf); + err = GetLastError(); + ok(result == FALSE, "Expected %d, got %d\n", FALSE, result); + ok(err == ERROR_INSUFFICIENT_BUFFER, "Expected ERROR_INSUFFICIENT_BUFFER, got %#x\n", err); + ok(!buf[0], "Got unexpected %s\n", buf); + + /* Test MD5 signature */ + memset(array, 0xcc, 8); + buf[0] = '\0'; + result = pGetFilePatchSignatureByBuffer(array, 8, PATCH_OPTION_SIGNATURE_MD5, NULL, + 0, NULL, 0, NULL, 33, buf); + ok(result == TRUE, "Expected %d, got %d\n", TRUE, result); + ok(!strcmp(buf, "7bffa66e1c861fcbf38426d134508908"), "Expected %s, got %s\n", + "7bffa66e1c861fcbf38426d134508908", buf); + + /* Test MD5 signature w/ insufficient buffer */ + memset(array, 0xcc, 8); + buf[0] = '\0'; + SetLastError(0xdeadbeef); + result = pGetFilePatchSignatureByBuffer(array, 8, PATCH_OPTION_SIGNATURE_MD5, NULL, + 0, NULL, 0, NULL, 32, buf); + err = GetLastError(); + ok(result == FALSE, "Expected %d, got %d\n", FALSE, result); + ok(err == ERROR_INSUFFICIENT_BUFFER, "Expected ERROR_INSUFFICIENT_BUFFER, got %#x\n", err); + ok(!buf[0], "Got unexpected %s\n", buf); + + /* Test signature of PE32 executable image */ + memset(array, 0, 1024); + setup_pe_with_sections(array, &header, NULL); + header->FileHeader.TimeDateStamp = 0xdeadbeef; + header->OptionalHeader.CheckSum = 0xdeadbeef; + header->OptionalHeader.ImageBase = 0x400000; + result = pGetFilePatchSignatureByBuffer(array, 1024, 0, NULL, 0, NULL, 0, NULL, 9, buf); + ok(result == TRUE, "Expected %d, got %d\n", TRUE, result); + ok(!strcmp(buf, "f953f764"), "Expected %s, got %s\n", "f953f764", buf); + ok(header->FileHeader.TimeDateStamp == 0x10000000, "Expected %#x, got %#x\n", + 0x10000000, header->FileHeader.TimeDateStamp); + ok(header->OptionalHeader.CheckSum == 0x9dd2, "Expected %#x, got %#x\n", + 0x9dd2, header->OptionalHeader.CheckSum); + ok(header->OptionalHeader.ImageBase == 0x10000000, "Expected %#x, got %#x\n", + 0x10000000, header->OptionalHeader.ImageBase); +} + START_TEST(signature) { if (!init_function_pointers()) @@ -316,4 +383,5 @@ START_TEST(signature) test_normalize_retain_range(); test_normalize_flags(); test_normalize_rebase(); + test_signature_by_buffer(); } diff --git a/include/patchapi.h b/include/patchapi.h index 1ccdf9dec7..9540154568 100644 --- a/include/patchapi.h +++ b/include/patchapi.h @@ -36,6 +36,7 @@ extern "C" { #define PATCH_OPTION_NO_CHECKSUM 0x00200000 #define PATCH_OPTION_NO_RESTIMEFIX 0x00400000 #define PATCH_OPTION_NO_TIMESTAMP 0x00800000 +#define PATCH_OPTION_SIGNATURE_MD5 0x01000000 #define PATCH_OPTION_INTERLEAVE_FILES 0x40000000 #define PATCH_OPTION_RESERVED1 0x80000000
Signed-off-by: Jeff Smith whydoubt@gmail.com --- As mentioned in dlls/mspatcha/tests/apply_patch.c, it is difficult to create good test cases for PA19 deltas suitable for the test suite. So no test cases for this yet. I am working on additional mspatcha changes, and I hope to get something in there that covers the changes in this patch.
dlls/mspatcha/pa19.c | 45 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-)
diff --git a/dlls/mspatcha/pa19.c b/dlls/mspatcha/pa19.c index 0ed60f4981..20729e94f0 100644 --- a/dlls/mspatcha/pa19.c +++ b/dlls/mspatcha/pa19.c @@ -43,6 +43,7 @@ #include "patchapi.h"
#include "pa19.h" +#include "signature.h" #include "lzxd_dec.h"
WINE_DEFAULT_DEBUG_CHANNEL(mspatcha); @@ -69,7 +70,9 @@ static UINT32 compute_zero_crc32(UINT32 crc, INT_PTR len) * UINT32 options; * UINT32 options_2; (present if PATCH_OPTION_EXTRA_FLAGS set) * UINT32 timestamp; (if PATCH_OPTION_NO_TIMESTAMP is SET in options) - * UVLI rebase; (present if PATCH_OPTION_NO_REBASE is not set; used on 32-bit executables) + * rebase fields: (present if PATCH_OPTION_NO_REBASE is not set; used on 32-bit executables) + * UINT16 image_base; (PE ImageBase of new file >> 16) + * SVLI time_delta; (timestamp - time_delta: PE TimeDateStamp of new file) * UVLI unpatched_size; * UINT32 crc32_patched; * BYTE input_file_count; @@ -118,6 +121,8 @@ struct input_file_info { struct patch_file_header { DWORD flags; DWORD timestamp; + DWORD image_base; + DWORD pe_timestamp; size_t patched_size; DWORD patched_crc32; unsigned input_file_count; @@ -163,6 +168,19 @@ static inline UINT32 read_raw_uint32(struct patch_file_header *ph) | (src[3] << 24); }
+static inline UINT16 read_raw_uint16(struct patch_file_header *ph) +{ + const BYTE *src = ph->src; + + ph->src += 2; + if (ph->src > ph->end) + { + ph->err = ERROR_PATCH_CORRUPT; + return 0; + } + return src[0] | (src[1] << 8); +} + /* Read a variable-length integer from a sequence of bytes terminated by * a value with bit 7 set. Set error if invalid or eof */ static UINT64 read_uvli(struct patch_file_header *ph) @@ -294,12 +312,15 @@ static int read_header(struct patch_file_header *ph, const BYTE *buf, size_t siz if(ph->flags & PATCH_OPTION_NO_TIMESTAMP) ph->timestamp = read_raw_uint32(ph);
- /* not sure what this value is for, but its absence seems to mean only that timestamps - * in the decompressed 32-bit exe are not modified */ if (!(ph->flags & PATCH_OPTION_NO_REBASE)) { - TRACE("skipping rebase field\n"); - (void)read_uvli(ph); + ph->image_base = (ULONG)read_raw_uint16(ph) << 16; + ph->pe_timestamp = ph->timestamp - (LONG)read_svli(ph); + } + else + { + ph->image_base = 0; + ph->pe_timestamp = 0; }
ph->patched_size = (size_t)read_uvli(ph); @@ -635,6 +656,8 @@ DWORD apply_patch_to_file_by_buffers(const BYTE *patch_file_view, const ULONG pa size_t buf_size; BYTE *new_file_buf = NULL; BYTE *decode_buf = NULL; + BYTE *norm_file_view = NULL; + DWORD pe_offset;
if (pnew_file_buf == NULL) { @@ -664,6 +687,16 @@ DWORD apply_patch_to_file_by_buffers(const BYTE *patch_file_view, const ULONG pa goto free_patch_header; }
+ pe_offset = get_pe_offset(old_file_view, old_file_size); + if (pe_offset) + { + norm_file_view = heap_alloc(old_file_size); + memcpy(norm_file_view, old_file_view, old_file_size); + normalize_pe(norm_file_view, old_file_size, pe_offset, + ph.flags, ph.image_base, ph.pe_timestamp); + old_file_view = norm_file_view; + } + file_info = find_matching_old_file(&ph, old_file_view, old_file_size); if (file_info == NULL) { @@ -788,6 +821,8 @@ free_decode_buf:
free_patch_header: free_header(&ph); + if (norm_file_view) + heap_free(norm_file_view);
return err; }