This clears up much of the todo list for mspatcha. GetFilePatchSignature* and NormalizeFileForPatchSignature have now been implemented in these changes.
These changes bring better support for things like the Adobe Acrobat installer without the need for things like the winetricks mspatcha.dll native dll override. Still needed is support for interleaved streams in the LZXD decompression logic, however this is a great start to better supporting software installers that use the Windows interface for creating and applying patches to files.
This is the beginning of the fixes required for bug 12501: https://bugs.winehq.org/show_bug.cgi?id=12501
-- v7: mspatcha: Fix binary to hex string conversions for GetFilePatchSignatureByBuffer
From: ajkhoury aidankhoury@gmail.com
These changes add the implementation for the NormalizeFileForPatchSignature API, and lays the foundation for proper 32-bit image file patch support. --- dlls/mspatcha/Makefile.in | 3 +- dlls/mspatcha/mspatcha_main.c | 23 ++- dlls/mspatcha/pa19.c | 347 ++++++++++++++++++++++++++++++++++ dlls/mspatcha/pa19.h | 13 ++ dlls/mspatcha/pecoff.c | 155 +++++++++++++++ dlls/mspatcha/pecoff.h | 44 +++++ 6 files changed, 577 insertions(+), 8 deletions(-) create mode 100644 dlls/mspatcha/pecoff.c create mode 100644 dlls/mspatcha/pecoff.h
diff --git a/dlls/mspatcha/Makefile.in b/dlls/mspatcha/Makefile.in index 89d6367b39d..9358e7a9a2a 100644 --- a/dlls/mspatcha/Makefile.in +++ b/dlls/mspatcha/Makefile.in @@ -6,6 +6,7 @@ EXTRADLLFLAGS = -Wb,--prefer-native C_SRCS = \ lzxd_dec.c \ mspatcha_main.c \ - pa19.c + pa19.c \ + pecoff.c
RC_SRCS = version.rc diff --git a/dlls/mspatcha/mspatcha_main.c b/dlls/mspatcha/mspatcha_main.c index 1f6653da161..4d24d4b573a 100644 --- a/dlls/mspatcha/mspatcha_main.c +++ b/dlls/mspatcha/mspatcha_main.c @@ -261,12 +261,21 @@ BOOL WINAPI GetFilePatchSignatureByBuffer(PBYTE file_buf, ULONG file_size, ULONG /***************************************************** * NormalizeFileForPatchSignature (MSPATCHA.@) */ -INT WINAPI NormalizeFileForPatchSignature(PVOID file_buffer, ULONG file_size, ULONG flags, PATCH_OPTION_DATA *options, - 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) +INT WINAPI NormalizeFileForPatchSignature( + PVOID file_buffer, ULONG file_size, + ULONG option_flags, PATCH_OPTION_DATA *option_data, + ULONG new_coff_base, ULONG new_coff_time, + ULONG ignore_range_count, PPATCH_IGNORE_RANGE ignore_range_array, + ULONG retain_range_count, PPATCH_RETAIN_RANGE retain_range_array) { - FIXME("stub - %p, %lu, %lx, %p, %lu, %lu, %lu, %p, %lu, %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; + int result; + + result = normalize_old_file_image( + file_buffer, file_size, + option_flags, option_data, + new_coff_base, new_coff_time, + ignore_range_array, ignore_range_count, + retain_range_array, retain_range_count); + + return result; } diff --git a/dlls/mspatcha/pa19.c b/dlls/mspatcha/pa19.c index dc6ef79e2b2..529a4dee553 100644 --- a/dlls/mspatcha/pa19.c +++ b/dlls/mspatcha/pa19.c @@ -43,6 +43,7 @@ #include "patchapi.h"
#include "pa19.h" +#include "pecoff.h" #include "lzxd_dec.h"
WINE_DEFAULT_DEBUG_CHANNEL(mspatcha); @@ -621,6 +622,352 @@ static DWORD compute_target_crc32(struct input_file_info *fi, const BYTE *new_fi return crc32; }
+/* Fixup a given mapped image's relocation table for a new image base. */ +BOOL rebase_image( + IMAGE_NT_HEADERS32 UNALIGNED *nt_headers, + PUCHAR mapped_image_base, ULONG mapped_image_size, ULONG new_image_base) +{ + BOOL result; + IMAGE_BASE_RELOCATION UNALIGNED *reloc_block; + IMAGE_BASE_RELOCATION UNALIGNED *reloc_block_base; + PUCHAR reloc_fixup; + LONG delta; + LONG reloc_dir_remaining; + ULONG reloc_dir_size; + USHORT UNALIGNED *reloc; + ULONG reloc_count; + PUCHAR mapped_image_end; + + result = FALSE; + mapped_image_end = &mapped_image_base[mapped_image_size]; + delta = (LONG)(new_image_base - nt_headers->OptionalHeader.ImageBase); + + reloc_dir_size = 0; + reloc_block = image_directory_mapped_address(nt_headers, IMAGE_DIRECTORY_ENTRY_BASERELOC, + &reloc_dir_size, mapped_image_base, mapped_image_size); + if (!reloc_block + || !reloc_dir_size + || ((PUCHAR)reloc_block > (mapped_image_end - sizeof(IMAGE_BASE_RELOCATION)))) { + return result; + } + + nt_headers->OptionalHeader.ImageBase = (DWORD)new_image_base; + result = TRUE; + + reloc_dir_remaining = (LONG)reloc_dir_size; + while (reloc_dir_remaining > 0) { + + if (reloc_block->SizeOfBlock <= (ULONG)reloc_dir_remaining + && reloc_block->SizeOfBlock > sizeof(IMAGE_BASE_RELOCATION)) + { + reloc_block_base = (IMAGE_BASE_RELOCATION UNALIGNED *)(mapped_image_base + + image_rva_to_file_offset(nt_headers, reloc_block->VirtualAddress, mapped_image_base, mapped_image_size)); + if (reloc_block_base) + { + reloc = (USHORT UNALIGNED *)((PUCHAR)reloc_block + sizeof(IMAGE_BASE_RELOCATION)); + reloc_count = (reloc_block->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT); + while (reloc_count--) { + if ((PUCHAR)reloc > mapped_image_end) { + break; + } + + reloc_fixup = (PUCHAR)reloc_block_base + (*reloc & 0x0FFF); + if (reloc_fixup < mapped_image_end) + { + switch (*reloc >> 12) + { + case IMAGE_REL_BASED_HIGH: + *(USHORT UNALIGNED *)reloc_fixup = (USHORT)(((*(USHORT UNALIGNED *)reloc_fixup << 16) + delta) >> 16); + break; + case IMAGE_REL_BASED_LOW: + *(USHORT UNALIGNED *)reloc_fixup = (USHORT)(*(SHORT UNALIGNED *)reloc_fixup + delta); + break; + case IMAGE_REL_BASED_HIGHLOW: + if (reloc_fixup + sizeof(USHORT) <= mapped_image_end) { + *(LONG UNALIGNED *)reloc_fixup += delta; + } + break; + case IMAGE_REL_BASED_HIGHADJ: + ++reloc; + --reloc_count; + if ((PUCHAR)reloc <= (mapped_image_end - sizeof(USHORT))) + *(USHORT UNALIGNED *)reloc_fixup = (USHORT)(((*(USHORT UNALIGNED *)reloc_fixup << 16) + (SHORT)*reloc + delta + 0x8000) >> 16); + break; + default: + + } + } + + ++reloc; + } + } + } + + reloc_dir_remaining -= reloc_block->SizeOfBlock; + reloc_block = (IMAGE_BASE_RELOCATION UNALIGNED *)((PUCHAR)reloc_block + reloc_block->SizeOfBlock); + + if ((PUCHAR)reloc_block > (mapped_image_end - sizeof(IMAGE_BASE_RELOCATION))) { + throw_pe_fmt_exception(); + } + } + + return result; +} + +/* Remove all bound imports for a given mapped image. */ +BOOL unbind_image( + IMAGE_NT_HEADERS32 UNALIGNED *nt_headers, PUCHAR mapped_image, ULONG image_size) +{ + BOOL result; + PUCHAR mapped_image_end; + IMAGE_IMPORT_DESCRIPTOR UNALIGNED *import_desc; + IMAGE_BOUND_IMPORT_DESCRIPTOR UNALIGNED *bound_imports; + ULONG bound_imports_size; + IMAGE_DATA_DIRECTORY UNALIGNED *bound_import_data_dir; + IMAGE_THUNK_DATA32 UNALIGNED *original_thunk; + IMAGE_THUNK_DATA32 UNALIGNED *bound_thunk; + IMAGE_SECTION_HEADER UNALIGNED *section_table; + ULONG section_count; + + result = FALSE; + mapped_image_end = mapped_image + image_size; + + /* Erase bound import data directory. */ + bound_imports = image_directory_mapped_address(nt_headers, IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT, + &bound_imports_size, mapped_image, image_size); + if (bound_imports) + { + memset(bound_imports, 0, bound_imports_size); + + bound_import_data_dir = &nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT]; + if ((PUCHAR)bound_import_data_dir < mapped_image + || (PUCHAR)bound_import_data_dir > (mapped_image_end - sizeof(IMAGE_DATA_DIRECTORY))) { + throw_pe_fmt_exception(); + } + + bound_import_data_dir->VirtualAddress = 0; + bound_import_data_dir->Size = 0; + result = TRUE; + } + + /* Zero out needed import descriptor fields */ + import_desc = image_directory_mapped_address(nt_headers, IMAGE_DIRECTORY_ENTRY_IMPORT, NULL, mapped_image, image_size); + if (import_desc) + { + while ((PUCHAR)import_desc < (mapped_image_end - sizeof(IMAGE_IMPORT_DESCRIPTOR)) + && import_desc->Characteristics) + { + /* TimeDateStamp field is -1 if bound */ + if (import_desc->TimeDateStamp) { + import_desc->TimeDateStamp = 0; + result = TRUE; + + original_thunk = image_rva_to_mapped_address(nt_headers, import_desc->OriginalFirstThunk, mapped_image, image_size); + bound_thunk = image_rva_to_mapped_address(nt_headers, import_desc->FirstThunk, mapped_image, image_size); + if (original_thunk && bound_thunk) + { + for (; original_thunk->u1.AddressOfData; original_thunk++, bound_thunk++) + { + if ((PUCHAR)original_thunk >= mapped_image_end + || (PUCHAR)bound_thunk >= mapped_image_end) + break; + *bound_thunk = *original_thunk; + } + } + } + + if (import_desc->ForwarderChain) { + import_desc->ForwarderChain = 0; + result = TRUE; + } + + ++import_desc; + } + } + + /* Mark the .idata section as writable */ + section_table = IMAGE_FIRST_SECTION(nt_headers); + section_count = nt_headers->FileHeader.NumberOfSections; + for (ULONG i = 0; i < section_count; i++) + { + if ((PUCHAR)§ion_table[i] < mapped_image + || (PUCHAR)§ion_table[i] > (mapped_image_end - sizeof(IMAGE_SECTION_HEADER))) { + throw_pe_fmt_exception(); + } + + /* tolower hack check for ".idata " */ + if ((*((ULONGLONG UNALIGNED *)§ion_table[i].Name) | 0x2020202020202020) == 0x202061746164692E) { + if ((section_table[i].Characteristics & IMAGE_SCN_MEM_WRITE) == 0) { + result = TRUE; + section_table[i].Characteristics |= (IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE); + } + + break; + } + } + + return result; +} + +/* Force all lock prefixes to the x86 LOCK (F0h) opcode in a given mapped image. */ +BOOL normalize_lock_prefixes_in_image( + IMAGE_NT_HEADERS32 UNALIGNED *nt_headers, PUCHAR mapped_image, ULONG image_size) +{ + BOOL result = FALSE; + PUCHAR mapped_image_end; + IMAGE_LOAD_CONFIG_DIRECTORY32 UNALIGNED *loadcfg; + ULONG UNALIGNED *lock_prefix_table; + + mapped_image_end = &mapped_image[image_size]; + + loadcfg = image_directory_mapped_address(nt_headers, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, + NULL, mapped_image, image_size); + if (loadcfg && loadcfg->LockPrefixTable) + { + if (loadcfg->LockPrefixTable < nt_headers->OptionalHeader.ImageBase) { + throw_pe_fmt_exception(); + } + + lock_prefix_table = image_rva_to_mapped_address(nt_headers, + loadcfg->LockPrefixTable - nt_headers->OptionalHeader.ImageBase, mapped_image, image_size); + + if (lock_prefix_table) + { + while ((PUCHAR)lock_prefix_table <= (mapped_image_end - sizeof(ULONG)) + && *lock_prefix_table) + { + PUCHAR const p = image_rva_to_mapped_address(nt_headers, + *lock_prefix_table - nt_headers->OptionalHeader.ImageBase, mapped_image, image_size); + + if (p && *p != 0xF0) { + *p = 0xF0; + result = TRUE; + } + + ++lock_prefix_table; + } + } + } + + return result; +} + +/* derived from imagehlp for calculating a new coff image checksum. */ +static WORD calc_chksum( + DWORD initial_value, PUCHAR buffer, DWORD size_in_bytes) +{ + DWORD sum; + DWORD words_remaining; + DWORD partial_words; + DWORD partial_sum; + + sum = initial_value; + words_remaining = size_in_bytes / sizeof(WORD); + + for (; words_remaining; sum += HIWORD(partial_sum) + LOWORD(partial_sum)) + { + partial_words = words_remaining; + if (words_remaining > 0x10000) { + partial_words = 0x10000; + } + + words_remaining -= partial_words; + partial_sum = 0; + do { + partial_sum += *(WORD UNALIGNED *)buffer; + buffer += sizeof(WORD); + --partial_words; + } while (partial_words); + } + + if (size_in_bytes & 1) { + sum += *buffer; + } + + return (WORD)(HIWORD(sum) + LOWORD(sum)); +} + +/* Normalizes a given 32-bit PE image to render a stream that is common. */ +int normalize_old_file_image( + BYTE *old_file_buffer, ULONG old_file_size, + ULONG option_flags, PATCH_OPTION_DATA *option_data, + ULONG new_image_base, ULONG new_image_time, + const PATCH_IGNORE_RANGE *ignore_range_array, ULONG ignore_range_count, + const PATCH_RETAIN_RANGE *retain_range_array, ULONG retain_range_count) +{ + int result = 0; + ULONG i; + IMAGE_NT_HEADERS32 UNALIGNED *nt_headers; + + UNREFERENCED_PARAMETER(option_data); + + TRACE("normalizing image at 0x%p with options 0x%lX, new base 0x%lX, new time %lu", + old_file_buffer, option_flags, new_image_base, new_image_time); + + if (old_file_buffer && old_file_size) + { + nt_headers = image_get_nt_headers(old_file_buffer, old_file_size); + if (nt_headers) + { + if ((option_flags & PATCH_OPTION_NO_REBASE) == 0) { + if (new_image_time != 0 && nt_headers->FileHeader.TimeDateStamp != new_image_time) { + nt_headers->FileHeader.TimeDateStamp = new_image_time; + result = NORMALIZE_RESULT_SUCCESS; + } + + if (new_image_base != 0 && nt_headers->OptionalHeader.ImageBase != new_image_base) { + result |= rebase_image(nt_headers, old_file_buffer, old_file_size, new_image_base); + } + } + + if ((option_flags & PATCH_OPTION_NO_BINDFIX) == 0) { + result |= unbind_image(nt_headers, old_file_buffer, old_file_size); + } + + if ((option_flags & PATCH_OPTION_NO_LOCKFIX) == 0) { + result |= normalize_lock_prefixes_in_image(nt_headers, old_file_buffer, old_file_size); + } + + if ((option_flags & PATCH_OPTION_NO_CHECKSUM) != 0) { + if (nt_headers->OptionalHeader.CheckSum) { + nt_headers->OptionalHeader.CheckSum = 0; + result = NORMALIZE_RESULT_SUCCESS; + } + + } else { + if (result) { + nt_headers->OptionalHeader.CheckSum = 0; + nt_headers->OptionalHeader.CheckSum = calc_chksum(0, old_file_buffer, old_file_size) + old_file_size; + } + } + } + + for (i = 0; i < ignore_range_count; ++i) { + if (ignore_range_array[i].OffsetInOldFile <= old_file_size + && (ignore_range_array[i].OffsetInOldFile + ignore_range_array[i].LengthInBytes) <= old_file_size) + { + memset(&old_file_buffer[ignore_range_array[i].OffsetInOldFile], + 0, + ignore_range_array[i].LengthInBytes); + result = NORMALIZE_RESULT_SUCCESS; + } + } + + for (i = 0; i < retain_range_count; ++i) { + if (retain_range_array[i].OffsetInOldFile <= old_file_size + && (retain_range_array[i].OffsetInOldFile + retain_range_array[i].LengthInBytes) <= old_file_size) + { + memset(&old_file_buffer[retain_range_array[i].OffsetInOldFile], + 0, + retain_range_array[i].LengthInBytes); + result = NORMALIZE_RESULT_SUCCESS; + } + } + } + + return result + 1; +} + DWORD apply_patch_to_file_by_buffers(const BYTE *patch_file_view, const ULONG patch_file_size, const BYTE *old_file_view, ULONG old_file_size, BYTE **pnew_file_buf, const ULONG new_file_buf_size, ULONG *new_file_size, diff --git a/dlls/mspatcha/pa19.h b/dlls/mspatcha/pa19.h index 0b368849267..e8a1fd3a51d 100644 --- a/dlls/mspatcha/pa19.h +++ b/dlls/mspatcha/pa19.h @@ -18,6 +18,19 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+enum NORMALIZE_RESULT { + NORMALIZE_RESULT_FAILURE = 0, + NORMALIZE_RESULT_SUCCESS = 1, + NORMALIZE_RESULT_SUCCESS_MODIFIED = 2 +}; + +int normalize_old_file_image( + BYTE *old_file_mapped, ULONG old_file_size, + ULONG option_flags, PATCH_OPTION_DATA *option_data, + ULONG new_image_base, ULONG new_image_time, + const PATCH_IGNORE_RANGE *ignore_range_array, ULONG ignore_range_count, + const PATCH_RETAIN_RANGE *retain_range_array, ULONG retain_range_count); + DWORD apply_patch_to_file_by_buffers(const BYTE *patch_file_view, const ULONG patch_file_size, const BYTE *old_file_view, ULONG old_file_size, BYTE **new_file_buf, const ULONG new_file_buf_size, ULONG *new_file_size, diff --git a/dlls/mspatcha/pecoff.c b/dlls/mspatcha/pecoff.c new file mode 100644 index 00000000000..0bb240250a7 --- /dev/null +++ b/dlls/mspatcha/pecoff.c @@ -0,0 +1,155 @@ +/* + * PatchAPI PE/COFF image helpers. + * + * Copyright 2023 Aidan Khoury + * + * 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 "pecoff.h" +#include "winbase.h" + +void DECLSPEC_NORETURN throw_pe_fmt_exception(void) +{ + RaiseException(0xE0000001, 0, 0, NULL); + for (;;) { /* silence compiler warning */ } +} + +IMAGE_NT_HEADERS32 UNALIGNED *image_get_nt_headers(const void *image_base, size_t image_size) +{ + IMAGE_DOS_HEADER UNALIGNED *dos_hdr; + IMAGE_NT_HEADERS32 UNALIGNED *nt_headers; + const UCHAR *const image_end = (PUCHAR)image_base + image_size; + + if (image_size >= 0x200) { + dos_hdr = (IMAGE_DOS_HEADER *)image_base; + if (dos_hdr->e_magic == IMAGE_DOS_SIGNATURE && dos_hdr->e_lfanew < image_size) { + nt_headers = (IMAGE_NT_HEADERS32 *)((ULONG_PTR)dos_hdr + dos_hdr->e_lfanew); + if (((PUCHAR)nt_headers + sizeof(IMAGE_NT_HEADERS32)) <= image_end) { + if (nt_headers->Signature == IMAGE_NT_SIGNATURE + && nt_headers->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + return nt_headers; + } + } + } + } + + return NULL; +} + +ULONG image_rva_to_file_offset( + IMAGE_NT_HEADERS32 UNALIGNED *nt_headers, ULONG rva, PUCHAR image_base, ULONG image_size) +{ + IMAGE_SECTION_HEADER UNALIGNED *section_table; + ULONG section_count; + ULONG i; + + if ( rva < nt_headers->OptionalHeader.SizeOfHeaders ) { + return rva; + } + + section_table = IMAGE_FIRST_SECTION(nt_headers); + section_count = nt_headers->FileHeader.NumberOfSections; + for (i = 0; i < section_count; i++) { + if ((PUCHAR)§ion_table[i] < image_base + || ((PUCHAR)§ion_table[i] + sizeof(IMAGE_SECTION_HEADER)) > &image_base[image_size]) { + throw_pe_fmt_exception(); + } + + if (rva >= section_table[i].VirtualAddress + && rva < (section_table[i].VirtualAddress + section_table[i].SizeOfRawData)) { + return (section_table[i].PointerToRawData + (rva - section_table[i].VirtualAddress)); + } + } + + return 0; +} + +ULONG image_directory_rva_and_size( + IMAGE_NT_HEADERS32 UNALIGNED *nt_header, USHORT directory_entry, ULONG *directory_size, + PUCHAR image_base, ULONG image_size) +{ + IMAGE_DATA_DIRECTORY UNALIGNED *data_directory; + + if (directory_entry >= nt_header->OptionalHeader.NumberOfRvaAndSizes) { + return 0; + } + + data_directory = &nt_header->OptionalHeader.DataDirectory[directory_entry]; + if ((PUCHAR)data_directory < image_base + || ((PUCHAR)data_directory + sizeof(IMAGE_DATA_DIRECTORY)) > &image_base[image_size]) { + throw_pe_fmt_exception(); + } + + if (directory_size != NULL) { + *directory_size = data_directory->Size; + } + return data_directory->VirtualAddress; +} + +ULONG image_directory_offset_and_size( + IMAGE_NT_HEADERS32 UNALIGNED *nt_header, USHORT directory_entry, ULONG *directory_size, + PUCHAR image_base, ULONG image_size) +{ + ULONG rva, offset = 0; + rva = image_directory_rva_and_size(nt_header, directory_entry, directory_size, image_base, image_size); + if (rva) { + offset = image_rva_to_file_offset(nt_header, rva, image_base, image_size); + } + return offset; +} + +PVOID image_rva_to_mapped_address( + IMAGE_NT_HEADERS32 UNALIGNED *nt_headers, ULONG rva, PVOID image_base, ULONG image_size) +{ + const ULONG offset = image_rva_to_file_offset(nt_headers, rva, image_base, image_size); + if (offset && offset < image_size) { + return (PVOID)((PUCHAR)image_base + offset); + } + return NULL; +} + +PVOID image_directory_mapped_address( + IMAGE_NT_HEADERS32 UNALIGNED *nt_headers, USHORT directory_entry, ULONG *directory_size, + PUCHAR image_base, ULONG image_size) +{ + ULONG dir_rva; + ULONG dir_size; + PVOID mapped_address; + + dir_rva = image_directory_rva_and_size(nt_headers, directory_entry, &dir_size, image_base, image_size); + if (!dir_rva) { + return NULL; + } + + mapped_address = image_rva_to_mapped_address(nt_headers, dir_rva, image_base, image_size); + if (!mapped_address) { + return NULL; + } + + if (((PUCHAR)mapped_address + dir_size) < (PUCHAR)mapped_address) { + throw_pe_fmt_exception(); + } + + if (((PUCHAR)mapped_address + dir_size) > &image_base[image_size]) { + return NULL; + } + + if (directory_size != NULL) { + *directory_size = dir_size; + } + + return mapped_address; +} diff --git a/dlls/mspatcha/pecoff.h b/dlls/mspatcha/pecoff.h new file mode 100644 index 00000000000..1664a2fe777 --- /dev/null +++ b/dlls/mspatcha/pecoff.h @@ -0,0 +1,44 @@ +/* + * PatchAPI PE/COFF image helpers. + * + * Copyright 2023 Aidan Khoury + * + * 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" + +void DECLSPEC_NORETURN throw_pe_fmt_exception(void); + +IMAGE_NT_HEADERS32 UNALIGNED *image_get_nt_headers(const void *image_base, size_t image_size); + +ULONG image_rva_to_file_offset( + IMAGE_NT_HEADERS32 UNALIGNED *nt_headers, ULONG rva, PUCHAR image_base, ULONG image_size); + +ULONG image_directory_rva_and_size( + IMAGE_NT_HEADERS32 UNALIGNED *nt_header, USHORT directory_entry, ULONG *directory_size, + PUCHAR image_base, ULONG image_size); + +ULONG image_directory_offset_and_size( + IMAGE_NT_HEADERS32 UNALIGNED *nt_header, USHORT directory_entry, ULONG *directory_size, + PUCHAR image_base, ULONG image_size); + +PVOID image_rva_to_mapped_address( + IMAGE_NT_HEADERS32 UNALIGNED *nt_headers, ULONG rva, PVOID image_base, ULONG image_size); + +PVOID image_directory_mapped_address( + IMAGE_NT_HEADERS32 UNALIGNED *nt_headers, USHORT directory_entry, ULONG *directory_size, + PUCHAR image_base, ULONG image_size);
From: ajkhoury aidankhoury@gmail.com
This commit adds implementations for the GetFilePatchSignature* routines, using NormalizeFileForPatchSignature for the bulk of the work. --- dlls/mspatcha/Makefile.in | 1 + dlls/mspatcha/md5.c | 31 +++++ dlls/mspatcha/md5.h | 21 ++++ dlls/mspatcha/mspatcha_main.c | 220 +++++++++++++++++++++++++++++----- dlls/mspatcha/pa19.c | 2 +- include/patchapi.h | 1 + 6 files changed, 244 insertions(+), 32 deletions(-) create mode 100644 dlls/mspatcha/md5.c create mode 100644 dlls/mspatcha/md5.h
diff --git a/dlls/mspatcha/Makefile.in b/dlls/mspatcha/Makefile.in index 9358e7a9a2a..081534f6b95 100644 --- a/dlls/mspatcha/Makefile.in +++ b/dlls/mspatcha/Makefile.in @@ -5,6 +5,7 @@ EXTRADLLFLAGS = -Wb,--prefer-native
C_SRCS = \ lzxd_dec.c \ + md5.c \ mspatcha_main.c \ pa19.c \ pecoff.c diff --git a/dlls/mspatcha/md5.c b/dlls/mspatcha/md5.c new file mode 100644 index 00000000000..8c51d84eccc --- /dev/null +++ b/dlls/mspatcha/md5.c @@ -0,0 +1,31 @@ + +/* MD5 algorithm + * + * This code is derived from ntdll/crypt.c + */ + +#include <string.h> +#include <basetsd.h> /* for WORDS_BIGENDIAN */ +#include <windef.h> + +#include "md5.h" + +struct md5_ctx { + unsigned int i[2]; + unsigned int buf[4]; + unsigned char in[64]; + unsigned char digest[MD5DIGESTLEN]; +}; + +extern void WINAPI MD5Init( struct md5_ctx * ); +extern void WINAPI MD5Update( struct md5_ctx *, const unsigned char *, unsigned int ); +extern void WINAPI MD5Final( struct md5_ctx * ); + +void ComputeMD5Hash(const void *data, unsigned int len, unsigned char digest[MD5DIGESTLEN]) +{ + struct md5_ctx ctx; + MD5Init(&ctx); + MD5Update(&ctx, data, len); + MD5Final(&ctx); + memcpy(digest, ctx.digest, MD5DIGESTLEN); +} diff --git a/dlls/mspatcha/md5.h b/dlls/mspatcha/md5.h new file mode 100644 index 00000000000..41408ea8194 --- /dev/null +++ b/dlls/mspatcha/md5.h @@ -0,0 +1,21 @@ +/* + * MD5 algorithm + * + * 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 + */ + +#define MD5DIGESTLEN 16 + +void ComputeMD5Hash(const void *data, unsigned int len, unsigned char digest[MD5DIGESTLEN]); diff --git a/dlls/mspatcha/mspatcha_main.c b/dlls/mspatcha/mspatcha_main.c index 4d24d4b573a..a350dc117b9 100644 --- a/dlls/mspatcha/mspatcha_main.c +++ b/dlls/mspatcha/mspatcha_main.c @@ -25,8 +25,6 @@ * used or the old file exceeds the lzxd window size. * - APPLY_OPTION_FAIL_IF_CLOSE is ignored. Normalization of 32-bit PE files * is required for checking this. - * - GetFilePatchSignature* and NormalizeFileForPatchSignature require a - * solution to the above 32-bit exe problem. */
#include <stdarg.h> @@ -34,9 +32,11 @@ #include "windef.h" #include "winbase.h" #include "winnls.h" +#include "winternl.h" #include "patchapi.h" #include "wine/debug.h"
+#include "md5.h" #include "pa19.h"
WINE_DEFAULT_DEBUG_CHANNEL(mspatcha); @@ -54,6 +54,16 @@ static WCHAR *strdupAW(const char *src) return dst; }
+#define NIBBLE_CHAR(n) ((n) < 0xA) ? ('0' + (n)) : ('a' + ((n) - 0xA)) +static inline void bin2hex(const unsigned char *bin, char *hexstr, size_t maxcount) +{ + for (size_t i = 0; i < maxcount; i++) { + *hexstr++ = NIBBLE_CHAR((*bin >> 4) & 0xf); + *hexstr++ = NIBBLE_CHAR(*bin++ & 0xf); + } + *hexstr = '\0'; +} + /***************************************************** * TestApplyPatchToFileA (MSPATCHA.@) */ @@ -208,54 +218,202 @@ BOOL WINAPI ApplyPatchToFileByBuffers(PBYTE patch_file_view, ULONG patch_file_s /***************************************************** * GetFilePatchSignatureA (MSPATCHA.@) */ -BOOL WINAPI GetFilePatchSignatureA(LPCSTR filename, ULONG flags, PVOID data, ULONG ignore_range_count, - PPATCH_IGNORE_RANGE ignore_range, ULONG retain_range_count, - PPATCH_RETAIN_RANGE retain_range, ULONG bufsize, LPSTR buffer) +BOOL WINAPI GetFilePatchSignatureA( + LPCSTR filename, ULONG option_flags, PVOID option_data, + ULONG ignore_range_count, PPATCH_IGNORE_RANGE ignore_range_array, + ULONG retain_range_count, PPATCH_RETAIN_RANGE retain_range_array, + ULONG signature_bufsize, LPSTR signature_buf) { - FIXME("stub - %s, %lx, %p, %lu, %p, %lu, %p, %lu, %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; + BOOL success = FALSE; + HANDLE file_hndl; + + file_hndl = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (file_hndl != INVALID_HANDLE_VALUE) + { + success = GetFilePatchSignatureByHandle( + file_hndl, option_flags,option_data, + ignore_range_count, ignore_range_array, + retain_range_count, retain_range_array, + signature_bufsize, signature_buf); + + CloseHandle(file_hndl); + } + + return success; }
/***************************************************** * GetFilePatchSignatureW (MSPATCHA.@) */ -BOOL WINAPI GetFilePatchSignatureW(LPCWSTR filename, ULONG flags, PVOID data, ULONG ignore_range_count, - PPATCH_IGNORE_RANGE ignore_range, ULONG retain_range_count, - PPATCH_RETAIN_RANGE retain_range, ULONG bufsize, LPWSTR buffer) +BOOL WINAPI GetFilePatchSignatureW( + LPCWSTR filename, ULONG option_flags, PVOID option_data, + ULONG ignore_range_count, PPATCH_IGNORE_RANGE ignore_range_array, + ULONG retain_range_count, PPATCH_RETAIN_RANGE retain_range_array, + ULONG signature_bufsize, LPWSTR signature_buf) { - FIXME("stub - %s, %lx, %p, %lu, %p, %lu, %p, %lu, %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; + BOOL success = FALSE; + HANDLE file_hndl; + char ascii_buffer[40]; + + file_hndl = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (file_hndl != INVALID_HANDLE_VALUE) + { + success = GetFilePatchSignatureByHandle( + file_hndl, option_flags,option_data, + ignore_range_count, ignore_range_array, + retain_range_count, retain_range_array, + sizeof(ascii_buffer), ascii_buffer); + + if (success) { + if ((signature_bufsize / sizeof(WCHAR)) >= (strlen(ascii_buffer) + 1)) { + success = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, + ascii_buffer, -1, signature_buf, signature_bufsize / sizeof(WCHAR)) != 0; + + } else { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + success = FALSE; + } + } + + CloseHandle(file_hndl); + } + + return success; }
/***************************************************** * GetFilePatchSignatureByHandle (MSPATCHA.@) */ -BOOL WINAPI GetFilePatchSignatureByHandle(HANDLE handle, ULONG flags, PVOID options, ULONG ignore_range_count, - PPATCH_IGNORE_RANGE ignore_range, ULONG retain_range_count, - PPATCH_RETAIN_RANGE retain_range, ULONG bufsize, LPSTR buffer) +BOOL WINAPI GetFilePatchSignatureByHandle( + HANDLE file_handle, ULONG option_flags, PVOID option_data, + ULONG ignore_range_count, PPATCH_IGNORE_RANGE ignore_range_array, + ULONG retain_range_count, PPATCH_RETAIN_RANGE retain_range_array, + ULONG signature_bufsize, LPSTR signature_buf) { - FIXME("stub - %p, %lx, %p, %lu, %p, %lu, %p, %lu, %p\n", handle, flags, options, - ignore_range_count, ignore_range, retain_range_count, retain_range, bufsize, buffer); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; + BOOL success = FALSE; + HANDLE file_writable_mapping = NULL; + PVOID file_writable_buf = NULL; + DWORD file_size = 0; + DWORD file_size_hi = 0; + + file_size = GetFileSize(file_handle, &file_size_hi); + + /* Cannot support files over 4GiB in size. */ + if (file_size == 0xFFFFFFFF) { + if (GetLastError() == ERROR_SUCCESS) { + SetLastError(ERROR_FILE_TOO_LARGE); + } + return FALSE; + + } else if (file_size_hi != 0) { + SetLastError(ERROR_FILE_TOO_LARGE); + return FALSE; + } + + /* No file size? Nothing to do; return success.*/ + if (file_size == 0) { + return TRUE; + } + + /* Create a writable file mapping for the given file handle. */ + file_writable_mapping = CreateFileMappingA(file_handle, NULL, PAGE_WRITECOPY, 0, 0, NULL); + if (file_writable_mapping) { + file_writable_buf = MapViewOfFile(file_writable_mapping, FILE_MAP_COPY, 0, 0, 0); + CloseHandle(file_writable_mapping); + if (file_writable_buf) { + success = TRUE; + } + } + + if (success) + { + /* Get the file patch signature for the mapped file. */ + success = GetFilePatchSignatureByBuffer( + file_writable_buf, file_size, + option_flags, option_data, + ignore_range_count, ignore_range_array, + retain_range_count, retain_range_array, + signature_bufsize, signature_buf); + + /* Unmapped the writable file buffer. */ + UnmapViewOfFile(file_writable_buf); + } + + /* Handle errors appropriately. */ + if (!success) { + if (GetLastError() == ERROR_SUCCESS) { + SetLastError(ERROR_EXTENDED_ERROR); + } + } + + return success; }
/***************************************************** * GetFilePatchSignatureByBuffer (MSPATCHA.@) */ -BOOL WINAPI GetFilePatchSignatureByBuffer(PBYTE file_buf, ULONG file_size, ULONG flags, PVOID options, - ULONG ignore_range_count, PPATCH_IGNORE_RANGE ignore_range, - ULONG retain_range_count, PPATCH_RETAIN_RANGE retain_range, - ULONG bufsize, LPSTR buffer) +BOOL WINAPI GetFilePatchSignatureByBuffer( + PBYTE file_buffer, ULONG file_size, + ULONG option_flags, PVOID option_data, + ULONG ignore_range_count, PPATCH_IGNORE_RANGE ignore_range_array, + ULONG retain_range_count, PPATCH_RETAIN_RANGE retain_range_array, + ULONG signature_bufsize, LPSTR signature_buf) { - FIXME("stub - %p, %lu, %lx, %p, %lu, %p, %lu, %p, %lu, %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; + BOOL success; + INT result; + UINT32 filecrc; + unsigned char filehash[MD5DIGESTLEN]; + + TRACE("getting file patch signature for buffer 0x%p of size 0x%lX", file_buffer, file_size); + + /* Normalize the given mapped file image. */ + result = NormalizeFileForPatchSignature( + file_buffer, file_size, + option_flags, option_data, + 0x10000000, 0x10000000, + ignore_range_count, ignore_range_array, + retain_range_count, retain_range_array); + + if (result == NORMALIZE_RESULT_FAILURE) { + success = FALSE; + } else { + success = TRUE; + } + + if (success) { + if (option_flags & PATCH_OPTION_SIGNATURE_MD5) { + if (signature_bufsize >= (MD5DIGESTLEN*2+1)) { + /* calculate MD5 hash of file buffer. */ + ComputeMD5Hash(file_buffer, (unsigned int)file_size, filehash); + bin2hex(filehash, signature_buf, MD5DIGESTLEN); + + } else { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + success = FALSE; + } + + } else { + if (signature_bufsize >= (sizeof(UINT32)*2+1)) { + /* calculate CRC32 checksum of file buffer. */ + filecrc = RtlComputeCrc32(0, file_buffer, file_size); + bin2hex((const unsigned char *)&filecrc, signature_buf, sizeof(UINT32)); + + } else { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + success = FALSE; + } + } + } + + if (!success) { + if (GetLastError() == ERROR_SUCCESS) { + SetLastError(ERROR_EXTENDED_ERROR); + } + } + + return success; }
/***************************************************** diff --git a/dlls/mspatcha/pa19.c b/dlls/mspatcha/pa19.c index 529a4dee553..ad8a41f0527 100644 --- a/dlls/mspatcha/pa19.c +++ b/dlls/mspatcha/pa19.c @@ -901,7 +901,7 @@ int normalize_old_file_image(
UNREFERENCED_PARAMETER(option_data);
- TRACE("normalizing image at 0x%p with options 0x%lX, new base 0x%lX, new time %lu", + TRACE("normalizing image at 0x%p with options 0x%lX, new base 0x%lX, new time %lu\n", old_file_buffer, option_flags, new_image_base, new_image_time);
if (old_file_buffer && old_file_size) diff --git a/include/patchapi.h b/include/patchapi.h index 1ccdf9dec70..95401545688 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
From: ajkhoury aidankhoury@gmail.com
--- dlls/mspatcha/mspatcha_main.c | 89 +- dlls/mspatcha/pa19.c | 2217 ++++++++++++++++++++++++++------- dlls/mspatcha/pa19.h | 14 +- include/patchapi.h | 3 + 4 files changed, 1818 insertions(+), 505 deletions(-)
diff --git a/dlls/mspatcha/mspatcha_main.c b/dlls/mspatcha/mspatcha_main.c index a350dc117b9..1d8744a85e4 100644 --- a/dlls/mspatcha/mspatcha_main.c +++ b/dlls/mspatcha/mspatcha_main.c @@ -67,7 +67,8 @@ static inline void bin2hex(const unsigned char *bin, char *hexstr, size_t maxcou /***************************************************** * TestApplyPatchToFileA (MSPATCHA.@) */ -BOOL WINAPI TestApplyPatchToFileA(LPCSTR patch_file, LPCSTR old_file, ULONG apply_flags) +BOOL WINAPI TestApplyPatchToFileA( + LPCSTR patch_file, LPCSTR old_file, ULONG apply_option_flags) { BOOL ret; WCHAR *patch_fileW, *old_fileW = NULL; @@ -78,36 +79,42 @@ BOOL WINAPI TestApplyPatchToFileA(LPCSTR patch_file, LPCSTR old_file, ULONG appl HeapFree(GetProcessHeap(), 0, patch_fileW); return FALSE; } - ret = apply_patch_to_file(patch_fileW, old_fileW, NULL, apply_flags, NULL, NULL, TRUE); + + ret = apply_patch_to_file(patch_fileW, old_fileW, NULL, + apply_option_flags | APPLY_OPTION_TEST_ONLY, NULL, NULL); + HeapFree(GetProcessHeap(), 0, patch_fileW); HeapFree(GetProcessHeap(), 0, old_fileW); return ret; }
-BOOL WINAPI TestApplyPatchToFileW(LPCWSTR patch_file_name, LPCWSTR old_file_name, ULONG apply_option_flags) +BOOL WINAPI TestApplyPatchToFileW( + LPCWSTR patch_file_name, LPCWSTR old_file_name, ULONG apply_option_flags) { - return apply_patch_to_file(patch_file_name, old_file_name, NULL, apply_option_flags, NULL, NULL, TRUE); + return apply_patch_to_file(patch_file_name, old_file_name, NULL, + apply_option_flags | APPLY_OPTION_TEST_ONLY, NULL, NULL); }
-BOOL WINAPI TestApplyPatchToFileByHandles(HANDLE patch_file_hndl, HANDLE old_file_hndl, ULONG apply_option_flags) +BOOL WINAPI TestApplyPatchToFileByHandles( + HANDLE patch_file_hndl, HANDLE old_file_hndl, ULONG apply_option_flags) { return apply_patch_to_file_by_handles(patch_file_hndl, old_file_hndl, NULL, - apply_option_flags, NULL, NULL, TRUE); + apply_option_flags | APPLY_OPTION_TEST_ONLY, NULL, NULL); }
-BOOL WINAPI TestApplyPatchToFileByBuffers(BYTE *patch_file_buf, ULONG patch_file_size, +BOOL WINAPI TestApplyPatchToFileByBuffers( + BYTE *patch_file_buf, ULONG patch_file_size, BYTE *old_file_buf, ULONG old_file_size, - ULONG* new_file_size, - ULONG apply_option_flags) + ULONG* new_file_size, ULONG apply_option_flags) { /* NOTE: windows preserves last error on success for this function, but no apps are known to depend on it */
- DWORD err = apply_patch_to_file_by_buffers(patch_file_buf, patch_file_size, - old_file_buf, old_file_size, - NULL, 0, new_file_size, NULL, - apply_option_flags, - NULL, NULL, - TRUE); + DWORD err = apply_patch_to_file_by_buffers( + patch_file_buf, patch_file_size, + old_file_buf, old_file_size, + NULL, 0, new_file_size, NULL, + apply_option_flags | APPLY_OPTION_TEST_ONLY, + NULL, NULL);
SetLastError(err);
@@ -117,7 +124,8 @@ BOOL WINAPI TestApplyPatchToFileByBuffers(BYTE *patch_file_buf, ULONG patch_file /***************************************************** * ApplyPatchToFileExA (MSPATCHA.@) */ -BOOL WINAPI ApplyPatchToFileExA(LPCSTR patch_file, LPCSTR old_file, LPCSTR new_file, ULONG apply_flags, +BOOL WINAPI ApplyPatchToFileExA( + LPCSTR patch_file, LPCSTR old_file, LPCSTR new_file, ULONG apply_option_flags, PPATCH_PROGRESS_CALLBACK progress_fn, PVOID progress_ctx) { BOOL ret = FALSE; @@ -131,7 +139,8 @@ BOOL WINAPI ApplyPatchToFileExA(LPCSTR patch_file, LPCSTR old_file, LPCSTR new_f if (!(new_fileW = strdupAW(new_file))) goto free_wstrs;
- ret = apply_patch_to_file(patch_fileW, old_fileW, new_fileW, apply_flags, progress_fn, progress_ctx, FALSE); + ret = apply_patch_to_file(patch_fileW, old_fileW, new_fileW, + apply_option_flags, progress_fn, progress_ctx);
HeapFree(GetProcessHeap(), 0, new_fileW); free_wstrs: @@ -143,7 +152,8 @@ free_wstrs: /***************************************************** * ApplyPatchToFileA (MSPATCHA.@) */ -BOOL WINAPI ApplyPatchToFileA(LPCSTR patch_file, LPCSTR old_file, LPCSTR new_file, ULONG apply_flags) +BOOL WINAPI ApplyPatchToFileA( + LPCSTR patch_file, LPCSTR old_file, LPCSTR new_file, ULONG apply_flags) { return ApplyPatchToFileExA(patch_file, old_file, new_file, apply_flags, NULL, NULL); } @@ -151,11 +161,11 @@ BOOL WINAPI ApplyPatchToFileA(LPCSTR patch_file, LPCSTR old_file, LPCSTR new_fil /***************************************************** * ApplyPatchToFileW (MSPATCHA.@) */ -BOOL WINAPI ApplyPatchToFileW(LPCWSTR patch_file_name, LPCWSTR old_file_name, LPCWSTR new_file_name, - ULONG apply_option_flags) +BOOL WINAPI ApplyPatchToFileW( + LPCWSTR patch_file_name, LPCWSTR old_file_name, LPCWSTR new_file_name, ULONG apply_option_flags) { - return apply_patch_to_file(patch_file_name, old_file_name, new_file_name, apply_option_flags, - NULL, NULL, FALSE); + return apply_patch_to_file(patch_file_name, old_file_name, new_file_name, + apply_option_flags, NULL, NULL); }
/***************************************************** @@ -165,50 +175,45 @@ BOOL WINAPI ApplyPatchToFileByHandles(HANDLE patch_file_hndl, HANDLE old_file_hn ULONG apply_option_flags) { return apply_patch_to_file_by_handles(patch_file_hndl, old_file_hndl, new_file_hndl, - apply_option_flags, NULL, NULL, FALSE); + apply_option_flags, NULL, NULL); }
/***************************************************** * ApplyPatchToFileExW (MSPATCHA.@) */ -BOOL WINAPI ApplyPatchToFileExW(LPCWSTR patch_file_name, LPCWSTR old_file_name, LPCWSTR new_file_name, - ULONG apply_option_flags, - PPATCH_PROGRESS_CALLBACK progress_fn, PVOID progress_ctx) +BOOL WINAPI ApplyPatchToFileExW( + LPCWSTR patch_file_name, LPCWSTR old_file_name, LPCWSTR new_file_name, + ULONG apply_option_flags, PPATCH_PROGRESS_CALLBACK progress_fn, PVOID progress_ctx) { - return apply_patch_to_file(patch_file_name, old_file_name, new_file_name, apply_option_flags, - progress_fn, progress_ctx, FALSE); + return apply_patch_to_file(patch_file_name, old_file_name, new_file_name, + apply_option_flags, progress_fn, progress_ctx); }
/***************************************************** * ApplyPatchToFileByHandlesEx (MSPATCHA.@) */ -BOOL WINAPI ApplyPatchToFileByHandlesEx(HANDLE patch_file_hndl, HANDLE old_file_hndl, HANDLE new_file_hndl, - ULONG apply_option_flags, - PPATCH_PROGRESS_CALLBACK progress_fn, - PVOID progress_ctx) +BOOL WINAPI ApplyPatchToFileByHandlesEx( + HANDLE patch_file_hndl, HANDLE old_file_hndl, HANDLE new_file_hndl, + ULONG apply_option_flags, PPATCH_PROGRESS_CALLBACK progress_fn, PVOID progress_ctx) { return apply_patch_to_file_by_handles(patch_file_hndl, old_file_hndl, new_file_hndl, - apply_option_flags, progress_fn, progress_ctx, FALSE); + apply_option_flags, progress_fn, progress_ctx); }
/***************************************************** * ApplyPatchToFileByBuffers (MSPATCHA.@) */ -BOOL WINAPI ApplyPatchToFileByBuffers(PBYTE patch_file_view, ULONG patch_file_size, - PBYTE old_file_view, ULONG old_file_size, - PBYTE* new_file_buf, ULONG new_file_buf_size, ULONG* new_file_size, - FILETIME* new_file_time, - ULONG apply_option_flags, - PPATCH_PROGRESS_CALLBACK progress_fn, PVOID progress_ctx) +BOOL WINAPI ApplyPatchToFileByBuffers( + PBYTE patch_file_view, ULONG patch_file_size, PBYTE old_file_view, ULONG old_file_size, + PBYTE* new_file_buf, ULONG new_file_buf_size, ULONG* new_file_size, FILETIME* new_file_time, + ULONG apply_option_flags, PPATCH_PROGRESS_CALLBACK progress_fn, PVOID progress_ctx) { /* NOTE: windows preserves last error on success for this function, but no apps are known to depend on it */
DWORD err = apply_patch_to_file_by_buffers(patch_file_view, patch_file_size, old_file_view, old_file_size, new_file_buf, new_file_buf_size, new_file_size, new_file_time, - apply_option_flags, - progress_fn, progress_ctx, - FALSE); + apply_option_flags, progress_fn, progress_ctx);
SetLastError(err);
diff --git a/dlls/mspatcha/pa19.c b/dlls/mspatcha/pa19.c index ad8a41f0527..55248156b39 100644 --- a/dlls/mspatcha/pa19.c +++ b/dlls/mspatcha/pa19.c @@ -18,18 +18,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * TODO - * - Normalization of 32-bit PE executable files and reversal of special - * processing of these executables is not implemented. - * Without normalization, old files cannot be validated for patching. The - * function NormalizeFileForPatchSignature() in Windows could be used to work - * out exactly how normalization works. - * Most/all of the special processing seems to be relocation of targets for - * some jump/call instructions to match more of the old file and improve - * compression. Patching of 64-bit exes works because mspatchc.dll does not - * implement special processing of them. In 32-bit patches, the variable - * named here 'unknown_count' seems to indicate presence of data related to - * reversing the processing. The changes that must be reversed occur at some, - * but not all, of the positions listed in the PE .reloc table. + * - Implement interleaved decoding when PATCH_OPTION_INTERLEAVE_FILES was + * used or the old file exceeds the lzxd window size. */
#include <stdarg.h> @@ -49,19 +39,17 @@ WINE_DEFAULT_DEBUG_CHANNEL(mspatcha);
#define PA19_FILE_MAGIC 0x39314150 -#define PATCH_OPTION_EXTRA_FLAGS 0x80000000 - -static UINT32 compute_zero_crc32(UINT32 crc, INT_PTR len) -{ - static const BYTE zero_buffer[1024]; - - while (len) - { - crc = RtlComputeCrc32(crc, zero_buffer, min(len, sizeof(zero_buffer))); - len -= min(len, sizeof(zero_buffer)); - } - return crc; -} +#define PATCH_OPTION_EXTRA_FLAGS PATCH_OPTION_RESERVED1 + +/* patch header extra flags field values */ +#define PATCH_TRANSFORM_NO_RELOCS 0x00000001 +#define PATCH_TRANSFORM_NO_IMPORTS 0x00000002 +#define PATCH_TRANSFORM_NO_EXPORTS 0x00000004 +#define PATCH_TRANSFORM_NO_RELJMPS 0x00000008 +#define PATCH_TRANSFORM_NO_RELCALLS 0x00000010 +#define PATCH_TRANSFORM_NO_RESOURCE 0x00000020 +#define PATCH_EXTRA_HAS_LZX_WINDOW_SIZE 0x00010000 +#define PATCH_EXTRA_HAS_PATCH_TRANSFORMS 0x20000000
/*********************************************************************************** * PatchAPI PA19 file header @@ -99,36 +87,53 @@ static UINT32 compute_zero_crc32(UINT32 crc, INT_PTR len) * UINT32 crc_hack; (rounds out the entire file crc32 to 0) */
+struct patch_transform_entry { + ULONG old_rva; + ULONG new_rva; +}; +struct patch_transform_table { + ULONG count; + ULONG allocated; + struct patch_transform_entry *entries; + BYTE *unused; +};
-#define MAX_RANGES 255 - -struct input_file_info { - size_t input_size; - DWORD crc32; - BYTE ignore_range_count; - BYTE retain_range_count; - PATCH_IGNORE_RANGE ignore_table[MAX_RANGES]; - PATCH_RETAIN_RANGE retain_table[MAX_RANGES]; - size_t unknown_count; - size_t stream_size; - const BYTE *stream_start; - int next_i; - int next_r; +struct old_file_info { + HANDLE old_file_handle; /* 0x00 */ + ULONG old_size; /* 0x04 */ + ULONG old_crc32; /* 0x08 */ + ULONG patch_stream_size; /* 0x0C */ + ULONG ignore_range_count; /* 0x10 */ + PATCH_IGNORE_RANGE *ignore_range_array; /* 0x14 */ + ULONG retain_range_count; /* 0x18 */ + PATCH_RETAIN_RANGE *retain_range_array; /* 0x1C */ + struct patch_transform_table xfrm_tbl; /* 0x20 */ + + /* wine private file info fields */ + const BYTE *patch_stream_start; };
struct patch_file_header { - DWORD flags; - DWORD timestamp; - size_t patched_size; - DWORD patched_crc32; - unsigned input_file_count; - struct input_file_info *file_table; + ULONG signature; /* 0x00 */ + ULONG flags; /* 0x04 */ + ULONG extra_flags; /* 0x08 */ + ULONG new_image_base; /* 0x0C */ + ULONG new_image_time; /* 0x10 */ + ULONG new_res_time; /* 0x14 */ + ULONG new_file_time; /* 0x18 */ + ULONG patched_size; /* 0x1C */ + ULONG patched_crc32; /* 0x20 */ + ULONG old_file_count; /* 0x24 */ + struct old_file_info *file_table; /* 0x28 */ + PPATCH_INTERLEAVE_MAP *interleave_map_array;/* 0x2C */ + ULONG compression_window_size; /* 0x30 */ + + /* wine private patch header fields */ const BYTE *src; const BYTE *end; - DWORD err; + ULONG err; };
- /* Currently supported options. Some such as PATCH_OPTION_FAIL_IF_BIGGER don't * affect decoding but can get recorded in the patch file anyway */ #define PATCH_OPTION_SUPPORTED_FLAGS ( \ @@ -143,6 +148,7 @@ struct patch_file_header { | PATCH_OPTION_NO_CHECKSUM \ | PATCH_OPTION_NO_RESTIMEFIX \ | PATCH_OPTION_NO_TIMESTAMP \ + | PATCH_OPTION_INTERLEAVE_FILES \ | PATCH_OPTION_EXTRA_FLAGS)
@@ -231,37 +237,47 @@ static INT64 read_svli(struct patch_file_header *ph)
static int __cdecl compare_ignored_range(const void *a, const void *b) { - LONG delta = ((PATCH_IGNORE_RANGE*)a)->OffsetInOldFile - ((PATCH_IGNORE_RANGE*)b)->OffsetInOldFile; - if (delta > 0) - return 1; - if (delta < 0) - return -1; - return 0; + const LONG delta = ((const PATCH_IGNORE_RANGE*)a)->OffsetInOldFile + - ((const PATCH_IGNORE_RANGE*)b)->OffsetInOldFile; + if (delta > 0) return 1; + else if (delta < 0) return -1; + else return 0; }
static int __cdecl compare_retained_range_old(const void *a, const void *b) { - LONG delta = ((PATCH_RETAIN_RANGE*)a)->OffsetInOldFile - ((PATCH_RETAIN_RANGE*)b)->OffsetInOldFile; - if (delta > 0) - return 1; - if (delta < 0) - return -1; - return 0; -} - -static int __cdecl compare_retained_range_new(const void *a, const void *b) -{ - LONG delta = ((PATCH_RETAIN_RANGE*)a)->OffsetInNewFile - ((PATCH_RETAIN_RANGE*)b)->OffsetInNewFile; - if (delta > 0) - return 1; - if (delta < 0) - return -1; - return 0; + const LONG delta = ((const PATCH_RETAIN_RANGE*)a)->OffsetInOldFile + - ((const PATCH_RETAIN_RANGE*)b)->OffsetInOldFile; + if (delta > 0) return 1; + else if (delta < 0) return -1; + else return 0; }
-static int read_header(struct patch_file_header *ph, const BYTE *buf, size_t size) +static int read_patch_header(struct patch_file_header *ph, const BYTE *buf, size_t size) { - unsigned fileno; + ULONG signature; + ULONG fileno; + struct old_file_info *fi; + ULONG i; + ULONG window_shift; + LONG delta; + LONG delta_new; + ULONG delta_old; + LONG delta_next; + LONG delta_length; + LONG length_next; + ULONG next_offset; + ULONG old_rva; + ULONG new_rva; + ULONG length; + ULONG next_old_offset; + ULONG total_old_offset; + ULONG next_old_length; + ULONG total_old_length; + ULONG remaining_length; + ULONG new_length; + ULONG interleave_count; + PATCH_INTERLEAVE_MAP *interleave_map;
ph->src = buf; ph->end = buf + size; @@ -269,14 +285,20 @@ static int read_header(struct patch_file_header *ph, const BYTE *buf, size_t siz ph->file_table = NULL; ph->err = ERROR_SUCCESS;
- if (read_raw_uint32(ph) != PA19_FILE_MAGIC) + signature = read_raw_uint32(ph); + ph->signature = signature; + if (signature != PA19_FILE_MAGIC) { - TRACE("no PA19 signature\n"); + WARN("no PA19 signature\n"); ph->err = ERROR_PATCH_CORRUPT; return -1; }
ph->flags = read_raw_uint32(ph); + + /* the meaning of PATCH_OPTION_NO_TIMESTAMP is inverted for decoding */ + ph->flags ^= PATCH_OPTION_NO_TIMESTAMP; + if ((ph->flags & PATCH_OPTION_SUPPORTED_FLAGS) != ph->flags) { FIXME("unsupported option flag(s): 0x%08lx\n", ph->flags & ~PATCH_OPTION_SUPPORTED_FLAGS); @@ -284,148 +306,295 @@ static int read_header(struct patch_file_header *ph, const BYTE *buf, size_t siz return -1; }
- /* additional 32-bit flag field */ + /* extra 32-bit flags field */ if (ph->flags & PATCH_OPTION_EXTRA_FLAGS) { - TRACE("skipping extra flag field\n"); - (void)read_raw_uint32(ph); + ph->extra_flags = read_raw_uint32(ph); + WARN("extra flags field is set: 0x%lX\n", ph->extra_flags); }
- /* the meaning of PATCH_OPTION_NO_TIMESTAMP is inverted for decoding */ - if(ph->flags & PATCH_OPTION_NO_TIMESTAMP) - ph->timestamp = read_raw_uint32(ph); + /* decode new file time field */ + if ((ph->flags & PATCH_OPTION_NO_TIMESTAMP) == 0) + { + ph->new_file_time = read_raw_uint32(ph); + } + + /* read the new coff image base and time used for normalization */ + if ((ph->flags & PATCH_OPTION_NO_REBASE) == 0) + { + ph->new_image_base = ((ULONG)*(UINT16 UNALIGNED *)ph->src) << 16; + ph->src += sizeof(UINT16); + + TRACE("new file requires rebasing to 0x%lX\n", ph->new_image_base); + + if (ph->new_file_time) { + delta = read_svli(ph); + ph->new_image_time = ph->new_file_time - delta; + } else { + ph->new_image_time = 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)) + if ((ph->flags & PATCH_OPTION_NO_RESTIMEFIX) == 0) { - TRACE("skipping rebase field\n"); - (void)read_uvli(ph); + if (ph->new_image_time) { + delta = read_svli(ph); + ph->new_res_time = ph->new_image_time - delta; + } else { + ph->new_res_time = read_raw_uint32(ph); + } }
- ph->patched_size = (size_t)read_uvli(ph); - TRACE("patched file size will be %u\n", (unsigned)ph->patched_size); + ph->patched_size = read_uvli(ph); + TRACE("patched file size will be %lu\n", (ULONG)ph->patched_size); ph->patched_crc32 = read_raw_uint32(ph);
- ph->input_file_count = *ph->src; - ++ph->src; - TRACE("patch supports %u old file(s)\n", ph->input_file_count); - /* if no old file used, input_file_count is still 1 */ - if (ph->input_file_count == 0) + if (ph->extra_flags & PATCH_EXTRA_HAS_LZX_WINDOW_SIZE) { + window_shift = (ULONG)*ph->src; + ++ph->src; + + if (window_shift > 31) { + ERR("invalid compression window shift!\n"); + ph->err = ERROR_EXTENDED_ERROR; + return -1; + } + + ph->compression_window_size = (ULONG)1ul << window_shift; + TRACE("compression window size: %lu\n", ph->compression_window_size); + } + + ph->old_file_count = (ULONG)*ph->src; + ++ph->src; + TRACE("patch supports %lu old file(s)\n", ph->old_file_count); + /* if no old file used, old_file_count is still 1 */ + if (ph->old_file_count == 0) { ph->err = ERROR_PATCH_CORRUPT; return -1; }
- if (ph->err != ERROR_SUCCESS) + if (ph->err != ERROR_SUCCESS) { return -1; + }
- ph->file_table = heap_calloc(ph->input_file_count, sizeof(struct input_file_info)); + ph->file_table = heap_calloc(ph->old_file_count, sizeof(struct old_file_info)); if (ph->file_table == NULL) { ph->err = ERROR_OUTOFMEMORY; return -1; }
- for (fileno = 0; fileno < ph->input_file_count; ++fileno) { - struct input_file_info *fi = ph->file_table + fileno; - ptrdiff_t delta; - unsigned i; + /* If the interleave files option is set, allocate memory for the interleave map */ + if (ph->flags & PATCH_OPTION_INTERLEAVE_FILES) + { + ph->interleave_map_array = heap_calloc(ph->old_file_count, sizeof(PPATCH_INTERLEAVE_MAP)); + if (ph->interleave_map_array == NULL) { + ph->err = ERROR_OUTOFMEMORY; + return -1; + } + }
- delta = (ptrdiff_t)read_svli(ph); - fi->input_size = ph->patched_size + delta; + for (fileno = 0; fileno < ph->old_file_count; ++fileno) + { + fi = &ph->file_table[fileno]; + + delta = read_svli(ph); + fi->old_size = ph->patched_size + delta;
- fi->crc32 = read_raw_uint32(ph); + fi->old_crc32 = read_raw_uint32(ph);
+ /* decode ignore range table */ fi->ignore_range_count = *ph->src; ++ph->src; - TRACE("found %u range(s) to ignore\n", fi->ignore_range_count); - - for (i = 0; i < fi->ignore_range_count; ++i) { - PATCH_IGNORE_RANGE *ir = fi->ignore_table + i; - - ir->OffsetInOldFile = (LONG)read_svli(ph); - ir->LengthInBytes = (ULONG)read_uvli(ph); + if (fi->ignore_range_count) + { + TRACE("found %lu range(s) to ignore\n", fi->ignore_range_count);
- if (i != 0) - { - ir->OffsetInOldFile += fi->ignore_table[i - 1].OffsetInOldFile - + fi->ignore_table[i - 1].LengthInBytes; + fi->ignore_range_array = heap_calloc(fi->ignore_range_count, sizeof(PATCH_IGNORE_RANGE)); + if (fi->ignore_range_array == NULL) { + ph->err = ERROR_OUTOFMEMORY; + return -1; } - if (ir->OffsetInOldFile > fi->input_size - || ir->OffsetInOldFile + ir->LengthInBytes > fi->input_size - || ir->LengthInBytes > fi->input_size) + + next_offset = 0; + for (i = 0; i < fi->ignore_range_count; ++i) { - ph->err = ERROR_PATCH_CORRUPT; - return -1; + delta = (LONG)read_svli(ph); + length = (ULONG)read_uvli(ph); + + fi->ignore_range_array[i].OffsetInOldFile = next_offset + delta; + fi->ignore_range_array[i].LengthInBytes = length; + + if (fi->ignore_range_array[i].OffsetInOldFile > fi->old_size + || (fi->ignore_range_array[i].OffsetInOldFile + length) > fi->old_size) + { + ph->err = ERROR_PATCH_CORRUPT; + return -1; + } + + next_offset += delta + length; } }
+ /* decode retain range table */ fi->retain_range_count = *ph->src; ++ph->src; - TRACE("found %u range(s) to retain\n", fi->retain_range_count); - - for (i = 0; i < fi->retain_range_count; ++i) { - PATCH_RETAIN_RANGE *rr = fi->retain_table + i; - - rr->OffsetInOldFile = (LONG)read_svli(ph); - if (i != 0) - rr->OffsetInOldFile += - fi->retain_table[i - 1].OffsetInOldFile + fi->retain_table[i - 1].LengthInBytes; - - rr->OffsetInNewFile = rr->OffsetInOldFile + (LONG)read_svli(ph); - rr->LengthInBytes = (ULONG)read_uvli(ph); + if (fi->retain_range_count) + { + TRACE("found %lu range(s) to retain\n", fi->retain_range_count);
- if (rr->OffsetInOldFile > fi->input_size - || rr->OffsetInOldFile + rr->LengthInBytes > fi->input_size - || rr->OffsetInNewFile > ph->patched_size - || rr->OffsetInNewFile + rr->LengthInBytes > ph->patched_size - || rr->LengthInBytes > ph->patched_size) + fi->retain_range_array = heap_calloc(fi->retain_range_count, sizeof(PATCH_RETAIN_RANGE)); + if (fi->retain_range_array == NULL) { - ph->err = ERROR_PATCH_CORRUPT; + ph->err = ERROR_OUTOFMEMORY; return -1; }
- /* ranges in new file must be equal and in the same order for all source files */ - if (fileno != 0) + next_offset = 0; + for (i = 0; i < fi->retain_range_count; ++i) { - PATCH_RETAIN_RANGE *rr_0 = ph->file_table[0].retain_table + i; - if (rr->OffsetInNewFile != rr_0->OffsetInNewFile - || rr->LengthInBytes != rr_0->LengthInBytes) + delta = (LONG)read_svli(ph); + delta_new = (LONG)read_svli(ph); + length = (ULONG)read_uvli(ph); + + fi->retain_range_array[i].OffsetInOldFile = next_offset + delta; + fi->retain_range_array[i].OffsetInNewFile = next_offset + delta + delta_new; + fi->retain_range_array[i].LengthInBytes = length; + + if (fi->retain_range_array[i].OffsetInOldFile > fi->old_size + || (fi->retain_range_array[i].OffsetInOldFile + fi->retain_range_array[i].LengthInBytes) > fi->old_size + || fi->retain_range_array[i].OffsetInNewFile > ph->patched_size + || (fi->retain_range_array[i].OffsetInNewFile + fi->retain_range_array[i].LengthInBytes) > ph->patched_size) { ph->err = ERROR_PATCH_CORRUPT; return -1; } + + /* ranges in new file must be equal and in the same order for all source files */ + if (fileno + && (fi->retain_range_array[i].OffsetInNewFile != ph->file_table[0].retain_range_array[i].OffsetInNewFile + || fi->retain_range_array[i].LengthInBytes != ph->file_table[0].retain_range_array[i].LengthInBytes)) + { + ph->err = ERROR_PATCH_CORRUPT; + return -1; + } + + next_offset += delta + length; } }
- fi->unknown_count = (size_t)read_uvli(ph); - if (fi->unknown_count) + /* decode patch transform table */ + fi->xfrm_tbl.count = (ULONG)read_uvli(ph); + if (fi->xfrm_tbl.count) { - FIXME("special processing of 32-bit executables not implemented.\n"); - ph->err = ERROR_PATCH_PACKAGE_UNSUPPORTED; - return -1; + TRACE("%lu patch transform entries found\n", fi->xfrm_tbl.count); + + fi->xfrm_tbl.entries = heap_calloc(fi->xfrm_tbl.count, sizeof(struct patch_transform_entry)); + if (fi->xfrm_tbl.entries == NULL) { + ph->err = ERROR_OUTOFMEMORY; + return -1; + } + + fi->xfrm_tbl.unused = NULL; + + old_rva = 0; + new_rva = 0; + for (i = 0; i < fi->xfrm_tbl.count; i++) + { + delta_old = read_uvli(ph); + delta_new = read_svli(ph); + + fi->xfrm_tbl.entries[i].old_rva = old_rva + delta_old; + fi->xfrm_tbl.entries[i].new_rva = new_rva + delta_new; + + old_rva += delta_old; + new_rva += delta_new; + } + } + + /* decode interleave map */ + if (ph->flags & PATCH_OPTION_INTERLEAVE_FILES) + { + interleave_count = (ULONG)read_uvli(ph); + if (interleave_count) + { + interleave_map = (PATCH_INTERLEAVE_MAP *)heap_alloc( + sizeof(((PATCH_INTERLEAVE_MAP *)0)->Range) * (interleave_count - 1) + + sizeof(PATCH_INTERLEAVE_MAP)); + + if (interleave_map == NULL) + { + ph->err = ERROR_OUTOFMEMORY; + return -1; + } + + interleave_map->CountRanges = interleave_count; + + total_old_offset = 0; + next_old_offset = 0; + total_old_length = 0; + next_old_length = 0; + new_length = 0; + remaining_length = ph->patched_size; + + for (i = 0; i < interleave_map->CountRanges; i++) + { + delta_next = (LONG)read_svli(ph); + length_next = (LONG)read_svli(ph); + + total_old_offset += delta_next; + total_old_length += length_next; + + next_old_offset += total_old_offset; + next_old_length += total_old_length; + + interleave_map->Range[i].OldOffset = next_old_offset; + interleave_map->Range[i].OldLength = next_old_length; + + if (interleave_map->Range[i].OldOffset > fi->old_size + || (interleave_map->Range[i].OldOffset + + interleave_map->Range[i].OldLength) > fi->old_size) + { + ph->err = ERROR_PATCH_CORRUPT; + return -1; + } + + if (i >= (interleave_map->CountRanges - 1)) + { + interleave_map->Range[i].NewLength = remaining_length; + } + else + { + delta_length = (LONG)read_svli(ph) << 15; + new_length += delta_length; + interleave_map->Range[i].NewLength = new_length; + remaining_length -= new_length; + } + } + + ph->interleave_map_array[fileno] = interleave_map; + } } - fi->stream_size = (size_t)read_uvli(ph); + + fi->patch_stream_size = (ULONG)read_uvli(ph); }
- for (fileno = 0; fileno < ph->input_file_count; ++fileno) + /* sort range tables and calculate patch stream start for each file */ + for (fileno = 0; fileno < ph->old_file_count; ++fileno) { - struct input_file_info *fi = ph->file_table + fileno; - - qsort(fi->ignore_table, fi->ignore_range_count, sizeof(fi->ignore_table[0]), compare_ignored_range); - qsort(fi->retain_table, fi->retain_range_count, sizeof(fi->retain_table[0]), compare_retained_range_old); + fi = &ph->file_table[fileno]; + qsort(fi->ignore_range_array, fi->ignore_range_count, sizeof(fi->ignore_range_array[0]), compare_ignored_range); + qsort(fi->retain_range_array, fi->retain_range_count, sizeof(fi->retain_range_array[0]), compare_retained_range_old);
- fi->stream_start = ph->src; - ph->src += fi->stream_size; + fi->patch_stream_start = ph->src; + ph->src += fi->patch_stream_size; }
/* skip the crc adjustment field */ ph->src = min(ph->src + 4, ph->end);
{ - UINT32 crc = RtlComputeCrc32(0, buf, ph->src - buf) ^ 0xFFFFFFFF; - if (crc != 0) + if (RtlComputeCrc32(0, buf, ph->src - buf) ^ 0xFFFFFFFF) { TRACE("patch file crc32 failed\n"); if (ph->src < ph->end) @@ -439,7 +608,35 @@ static int read_header(struct patch_file_header *ph, const BYTE *buf, size_t siz
static void free_header(struct patch_file_header *ph) { - heap_free(ph->file_table); + ULONG i; + + if (ph->interleave_map_array) { + for (i = 0; i < ph->old_file_count; i++) { + if (ph->interleave_map_array[i]) { + heap_free(ph->interleave_map_array[i]); + ph->interleave_map_array[i] = NULL; + } + } + + heap_free(ph->interleave_map_array); + ph->interleave_map_array = NULL; + } + + if (ph->file_table) { + for (i = 0; i < ph->old_file_count; i++) { + if (ph->file_table[i].ignore_range_array) { + heap_free(ph->file_table[i].ignore_range_array); + ph->file_table[i].ignore_range_array = NULL; + } + if (ph->file_table[i].retain_range_array) { + heap_free(ph->file_table[i].retain_range_array); + ph->file_table[i].retain_range_array = NULL; + } + } + + heap_free(ph->file_table); + ph->file_table = NULL; + } }
#define TICKS_PER_SEC 10000000 @@ -452,178 +649,67 @@ static void posix_time_to_file_time(ULONG timestamp, FILETIME *ft) ft->dwHighDateTime = (DWORD)(ticks >> 32); }
-/* Get the next range to ignore in the old file. - * fi->next_i must be initialized before use */ -static ULONG next_ignored_range(const struct input_file_info *fi, size_t index, ULONG old_file_size, ULONG *end) -{ - ULONG start = old_file_size; - *end = old_file_size; - /* if patching is unnecessary, the ignored ranges are skipped during crc calc */ - if (fi->next_i < fi->ignore_range_count && fi->stream_size != 0) - { - start = fi->ignore_table[fi->next_i].OffsetInOldFile; - *end = max(start + fi->ignore_table[fi->next_i].LengthInBytes, index); - start = max(start, index); - } - return start; -} - -/* Get the next range to retain from the old file. - * fi->next_r must be initialized before use */ -static ULONG next_retained_range_old(const struct input_file_info *fi, size_t index, ULONG old_file_size, ULONG *end) -{ - ULONG start = old_file_size; - *end = old_file_size; - if (fi->next_r < fi->retain_range_count) - { - start = fi->retain_table[fi->next_r].OffsetInOldFile; - *end = max(start + fi->retain_table[fi->next_r].LengthInBytes, index); - start = max(start, index); - } - return start; -} - -/* Get the next range to retain in the new file. - * fi->next_r must be initialized before use */ -static ULONG next_retained_range_new(const struct input_file_info *fi, size_t index, ULONG new_file_size, ULONG *end) -{ - ULONG start = new_file_size; - *end = new_file_size; - if (fi->next_r < fi->retain_range_count) - { - start = fi->retain_table[fi->next_r].OffsetInNewFile; - *end = max(start + fi->retain_table[fi->next_r].LengthInBytes, index); - start = max(start, index); - } - return start; -} - -/* Find the next range in the old file which must be assumed zero-filled during crc32 calc +/* Save the retain ranges into an allocated buffer. */ -static ULONG next_zeroed_range(struct input_file_info *fi, size_t index, ULONG old_file_size, ULONG *end) +static PUCHAR save_retained_ranges( + const BYTE *file_buf, ULONG file_size, + const PATCH_RETAIN_RANGE *retain_range_array, ULONG retain_range_count, + BOOL from_new) { - ULONG start = old_file_size; - ULONG end_i; - ULONG start_i; - ULONG end_r; - ULONG start_r; - - *end = old_file_size; - - start_i = next_ignored_range(fi, index, old_file_size, &end_i); - start_r = next_retained_range_old(fi, index, old_file_size, &end_r); + PUCHAR buffer, ptr; + ULONG i; + ULONG offset_in_file; + ULONG allocation_size;
- if (start_i < start_r) - { - start = start_i; - *end = end_i; - ++fi->next_i; - } - else - { - start = start_r; - *end = end_r; - ++fi->next_r; + allocation_size = 0; + for (i = 0; i < retain_range_count; i++) { + allocation_size += retain_range_array[i].LengthInBytes; } - return start; -} - -/* Use the crc32 of the input file to match the file with an entry in the patch file table - */ -struct input_file_info *find_matching_old_file(const struct patch_file_header *ph, const BYTE *old_file_view, ULONG old_file_size) -{ - unsigned i;
- for (i = 0; i < ph->input_file_count; ++i) - { - DWORD crc32 = 0; - ULONG index; + buffer = VirtualAlloc(NULL, allocation_size, MEM_COMMIT, PAGE_READWRITE); + if (buffer) { + ptr = buffer; + for (i = 0; i < retain_range_count; i++) { + if (from_new) { + offset_in_file = retain_range_array[i].OffsetInNewFile; + } else { + offset_in_file = retain_range_array[i].OffsetInOldFile; + }
- if (ph->file_table[i].input_size != old_file_size) - continue; + if (offset_in_file <= file_size + && (offset_in_file + retain_range_array[i].LengthInBytes) <= file_size) + { + memcpy(ptr, &file_buf[offset_in_file], retain_range_array[i].LengthInBytes); + }
- ph->file_table[i].next_i = 0; - for (index = 0; index < old_file_size; ) - { - ULONG end; - ULONG start = next_zeroed_range(ph->file_table + i, index, old_file_size, &end); - crc32 = RtlComputeCrc32(crc32, old_file_view + index, start - index); - crc32 = compute_zero_crc32(crc32, end - start); - index = end; + ptr += retain_range_array[i].LengthInBytes; } - if (ph->file_table[i].crc32 == crc32) - return ph->file_table + i; - } - return NULL; -} - -/* Zero-fill ignored ranges in the old file data for decoder matching - */ -static void zero_fill_ignored_ranges(BYTE *old_file_buf, const struct input_file_info *fi) -{ - size_t i; - for (i = 0; i < fi->ignore_range_count; ++i) - { - memset(old_file_buf + fi->ignore_table[i].OffsetInOldFile, - 0, - fi->ignore_table[i].LengthInBytes); } -}
-/* Zero-fill retained ranges in the old file data for decoder matching - */ -static void zero_fill_retained_ranges(BYTE *old_file_buf, BYTE *new_file_buf, const struct input_file_info *fi) -{ - size_t i; - for (i = 0; i < fi->retain_range_count; ++i) - { - memset(old_file_buf + fi->retain_table[i].OffsetInOldFile, - 0, - fi->retain_table[i].LengthInBytes); - } + return buffer; }
-/* Copy the retained ranges to the new file buffer +/* Copy the saved retained ranges to the new file buffer */ -static void apply_retained_ranges(const BYTE *old_file_buf, BYTE *new_file_buf, const struct input_file_info *fi) +static void apply_retained_ranges( + BYTE *new_file_buf, const PATCH_RETAIN_RANGE *retain_range_array, + const ULONG retain_range_count, const BYTE *retain_buffer) { - size_t i; - - if (old_file_buf == NULL) + if (retain_buffer == NULL) return;
- for (i = 0; i < fi->retain_range_count; ++i) + for (ULONG i = 0; i < retain_range_count; ++i) { - memcpy(new_file_buf + fi->retain_table[i].OffsetInNewFile, - old_file_buf + fi->retain_table[i].OffsetInOldFile, - fi->retain_table[i].LengthInBytes); - } -} - -/* Compute the crc32 for the new file, assuming zero for the retained ranges - */ -static DWORD compute_target_crc32(struct input_file_info *fi, const BYTE *new_file_buf, ULONG new_file_size) -{ - DWORD crc32 = 0; - ULONG index; + memcpy(new_file_buf + retain_range_array[i].OffsetInNewFile, + retain_buffer, + retain_range_array[i].LengthInBytes);
- qsort(fi->retain_table, fi->retain_range_count, sizeof(fi->retain_table[0]), compare_retained_range_new); - fi->next_r = 0; - - for (index = 0; index < new_file_size; ) - { - ULONG end; - ULONG start = next_retained_range_new(fi, index, new_file_size, &end); - ++fi->next_r; - crc32 = RtlComputeCrc32(crc32, new_file_buf + index, start - index); - crc32 = compute_zero_crc32(crc32, end - start); - index = end; - } - return crc32; + retain_buffer += retain_range_array[i].LengthInBytes; + } }
/* Fixup a given mapped image's relocation table for a new image base. */ -BOOL rebase_image( +static BOOL rebase_image( IMAGE_NT_HEADERS32 UNALIGNED *nt_headers, PUCHAR mapped_image_base, ULONG mapped_image_size, ULONG new_image_base) { @@ -715,7 +801,7 @@ BOOL rebase_image( }
/* Remove all bound imports for a given mapped image. */ -BOOL unbind_image( +static BOOL unbind_image( IMAGE_NT_HEADERS32 UNALIGNED *nt_headers, PUCHAR mapped_image, ULONG image_size) { BOOL result; @@ -810,7 +896,7 @@ BOOL unbind_image( }
/* Force all lock prefixes to the x86 LOCK (F0h) opcode in a given mapped image. */ -BOOL normalize_lock_prefixes_in_image( +static BOOL normalize_lock_prefixes_in_image( IMAGE_NT_HEADERS32 UNALIGNED *nt_headers, PUCHAR mapped_image, ULONG image_size) { BOOL result = FALSE; @@ -968,181 +1054,1407 @@ int normalize_old_file_image( return result + 1; }
-DWORD apply_patch_to_file_by_buffers(const BYTE *patch_file_view, const ULONG patch_file_size, - const BYTE *old_file_view, ULONG old_file_size, - BYTE **pnew_file_buf, const ULONG new_file_buf_size, ULONG *new_file_size, - FILETIME *new_file_time, - const ULONG apply_option_flags, - PATCH_PROGRESS_CALLBACK *progress_fn, void *progress_ctx, - const BOOL test_header_only) +static void patch_transform_PE_mark_non_executable( + IMAGE_NT_HEADERS32 UNALIGNED *nt_header, PUCHAR mapped_image, ULONG image_size, + BYTE *hintmap, BOOL force) { - DWORD err = ERROR_SUCCESS; - struct input_file_info *file_info; - struct patch_file_header ph; - size_t buf_size; - BYTE *new_file_buf = NULL; - BYTE *decode_buf = NULL; + IMAGE_SECTION_HEADER UNALIGNED *section_table; + ULONG section_size_raw; + ULONG directory_offset; + ULONG directory_rva; + ULONG directory_size; + ULONG i;
- if (pnew_file_buf == NULL) - { - if (!test_header_only && !(apply_option_flags & APPLY_OPTION_TEST_ONLY)) - return ERROR_INVALID_PARAMETER; - } - else - { - new_file_buf = *pnew_file_buf; + /* Mark all non-executable sections in the hint map */ + section_table = IMAGE_FIRST_SECTION(nt_header); + for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++) { + if ((section_table[i].Characteristics & IMAGE_SCN_MEM_EXECUTE) == 0 + && section_table[i].PointerToRawData < image_size) { + section_size_raw = section_table[i].SizeOfRawData; + if (section_size_raw > (image_size - section_table[i].PointerToRawData)) + section_size_raw = image_size - section_table[i].PointerToRawData; + memset(&hintmap[section_table[i].PointerToRawData], 0x01, section_size_raw); + } + } + + /* Mark headers in the hint map */ + if (nt_header->OptionalHeader.SizeOfHeaders > image_size) { + nt_header->OptionalHeader.SizeOfHeaders = image_size; + } + memset(hintmap, 0x03, nt_header->OptionalHeader.SizeOfHeaders); + + /* Mark all other non-executable regions in the hint map */ + for (i = 0; i < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++) { + directory_rva = nt_header->OptionalHeader.DataDirectory[i].VirtualAddress; + directory_size = nt_header->OptionalHeader.DataDirectory[i].Size; + if (directory_rva && directory_size) { + directory_offset = image_rva_to_file_offset(nt_header, directory_rva, mapped_image, image_size); + if ((directory_offset || force) && directory_offset < image_size) { + if (directory_size > image_size - directory_offset) { + directory_size = image_size - directory_offset; + } + memset(&hintmap[directory_offset], 0x03, directory_size); + } + } } +}
- if (old_file_view == NULL) - old_file_size = 0; +static ULONG get_new_rva_from_xfrm_table(struct patch_transform_table *xfrm_tbl, ULONG old_rva) +{ + ULONG mid, low = 0, high = xfrm_tbl->count; + struct patch_transform_entry *const entries = xfrm_tbl->entries; + + /* Use a binary search to locate the new rva. */ + while (low < high) { + mid = (low + high) >> 1; + if (entries[mid].old_rva >= old_rva) { + if (entries[mid].old_rva == old_rva) { + return old_rva + (LONG)(entries[mid].new_rva - entries[mid].old_rva); + } + high = mid; + } else { + low = mid + 1; + } + }
- if (read_header(&ph, patch_file_view, patch_file_size)) - { - err = ph.err; - goto free_patch_header; + if (!low) { + return old_rva; }
- if (new_file_size != NULL) - *new_file_size = (ULONG)ph.patched_size; + mid = low - 1; + return old_rva + (LONG)(entries[mid].new_rva - entries[mid].old_rva); +}
- if (new_file_buf != NULL && new_file_buf_size < ph.patched_size) - { - err = ERROR_INSUFFICIENT_BUFFER; - goto free_patch_header; - } +struct reloc_entry { + ULONG rva; + UCHAR type; + USHORT hiadj; +};
- file_info = find_matching_old_file(&ph, old_file_view, old_file_size); - if (file_info == NULL) - { - err = ERROR_PATCH_WRONG_FILE; - goto free_patch_header; - } - if (file_info->input_size != old_file_size) - { - err = ERROR_PATCH_CORRUPT; - goto free_patch_header; - } - if (file_info->stream_size == 0 && (apply_option_flags & APPLY_OPTION_FAIL_IF_EXACT)) - { - err = ERROR_PATCH_NOT_NECESSARY; - goto free_patch_header; - } - if (file_info->stream_size != 0 - && file_info->input_size > ((ph.flags & PATCH_OPTION_USE_LZX_LARGE) ? MAX_LARGE_WINDOW : MAX_NORMAL_WINDOW)) - { - /* interleaved by default but not the same as PATCH_OPTION_INTERLEAVE_FILES */ - FIXME("interleaved LZXD decompression is not supported.\n"); - err = ERROR_PATCH_PACKAGE_UNSUPPORTED; - goto free_patch_header; - } +static int __cdecl reloc_entry_compare(const void *a, const void *b) +{ + const LONG diff = ((const struct reloc_entry *)a)->rva + - ((const struct reloc_entry *)b)->rva; + if (diff > 0) return 1; + else if (diff < 0) return -1; + else return 0; +}
- if (test_header_only) - goto free_patch_header; +static void patch_transform_PE_relocations( + IMAGE_NT_HEADERS32 UNALIGNED *nt_headers, PUCHAR mapped_image, ULONG image_size, + struct patch_transform_table *xfrm_tbl, BYTE *hintmap) +{ + PUCHAR mapped_image_end; + ULONG image_base_va; + ULONG reloc_dir_offset; + ULONG reloc_dir_size; + LONG reloc_dir_remaining; + ULONG reloc_target_va; + ULONG reloc_target_rva; + USHORT reloc_type_offset; + UCHAR reloc_type; + USHORT reloc_offset; + ULONG reloc_fixup_rva; + PUCHAR reloc_fixup; + IMAGE_BASE_RELOCATION UNALIGNED *reloc_block; + IMAGE_BASE_RELOCATION UNALIGNED *reloc_block_base; + ULONG reloc_block_base_va; + ULONG reloc_count; + USHORT UNALIGNED *reloc; + USHORT UNALIGNED *reloc_start; + ULONG new_rva; + struct reloc_entry *reloc_entries; + ULONG reloc_entry_count; + ULONG reloc_entry_index; + IMAGE_SECTION_HEADER UNALIGNED *section_table; + ULONG section_count; + ULONG i; + PUCHAR p;
- /* missing lzxd stream means it's a header test extract */ - if (file_info->stream_start + file_info->stream_size > ph.end) - { - err = ERROR_PATCH_NOT_AVAILABLE; - goto free_patch_header; - } + mapped_image_end = mapped_image + image_size; + image_base_va = nt_headers->OptionalHeader.ImageBase; + reloc_dir_offset = image_directory_offset_and_size( + nt_headers, IMAGE_DIRECTORY_ENTRY_BASERELOC, &reloc_dir_size, mapped_image, image_size);
- buf_size = old_file_size + ph.patched_size; - decode_buf = new_file_buf; - if (new_file_buf == NULL || new_file_buf_size < buf_size) + /* Even if relocations do not exist in this PE, then scan the mapped image for + possible relocations that point to within valid mapped image bounds. */ + if (!reloc_dir_offset || (reloc_dir_offset + reloc_dir_size) > image_size) { - /* decode_buf must have room for both files, so allocate a new buffer if - * necessary. This will be returned to the caller if new_file_buf == NULL */ - decode_buf = VirtualAlloc(NULL, buf_size, MEM_COMMIT, PAGE_READWRITE); - if (decode_buf == NULL) + if (nt_headers->FileHeader.Machine == IMAGE_FILE_MACHINE_I386) { - err = GetLastError(); - goto free_patch_header; - } - } - - if (old_file_view != NULL) - memcpy(decode_buf, old_file_view, file_info->input_size); + IMAGE_SECTION_HEADER UNALIGNED *const first_section = IMAGE_FIRST_SECTION(nt_headers); + ULONG const first_section_va = image_base_va + first_section->VirtualAddress; + ULONG const image_end_va = image_base_va + nt_headers->OptionalHeader.SizeOfImage; + for (p = &mapped_image[first_section->PointerToRawData]; p < (mapped_image_end - 4); p++) { + reloc_target_va = *(ULONG UNALIGNED *)p; + + /* is this a possible rva? */ + if (reloc_target_va >= first_section_va && reloc_target_va < image_end_va) { + *(ULONG UNALIGNED *)(hintmap + (p - mapped_image)) |= 0x01010101; + + reloc_target_rva = reloc_target_va - image_base_va; + new_rva = get_new_rva_from_xfrm_table(xfrm_tbl, reloc_target_rva); + if (new_rva != reloc_target_rva) { + *(ULONG UNALIGNED *)p = image_base_va + new_rva; + }
- zero_fill_ignored_ranges(decode_buf, file_info); - zero_fill_retained_ranges(decode_buf, decode_buf + file_info->input_size, file_info); + /* advance by 3 so next loop we advance to the next dword */ + p += 4-1; + } + } + }
- if (file_info->stream_size != 0) - { - err = decode_lzxd_stream(file_info->stream_start, file_info->stream_size, - decode_buf, ph.patched_size, file_info->input_size, - ph.flags & PATCH_OPTION_USE_LZX_LARGE, - progress_fn, progress_ctx); - } - else if (file_info->input_size == ph.patched_size) - { - /* files are identical so copy old to new. copying is avoidable but rare */ - memcpy(decode_buf + file_info->input_size, decode_buf, ph.patched_size); - } - else - { - err = ERROR_PATCH_CORRUPT; - goto free_decode_buf; + return; } + + /* update the hint map with the base reloc directory */ + memset(&hintmap[reloc_dir_offset], 0x03, reloc_dir_size);
- if(err != ERROR_SUCCESS) - { - if (err == ERROR_PATCH_DECODE_FAILURE) - FIXME("decode failure: data corruption or bug.\n"); - goto free_decode_buf; + /* allocate memory for reloc cache entries */ + reloc_entries = VirtualAlloc(NULL, sizeof(struct reloc_entry) * (reloc_dir_size / sizeof(USHORT)), + MEM_COMMIT, PAGE_READWRITE); + if (!reloc_entries) { + return; }
- apply_retained_ranges(old_file_view, decode_buf + file_info->input_size, file_info); + /* loop through the relocation table, updating the new rvas. */ + reloc_entry_count = 0; + reloc_block = (IMAGE_BASE_RELOCATION UNALIGNED *)&mapped_image[reloc_dir_offset]; + reloc_dir_remaining = (LONG)reloc_dir_size; + while (reloc_dir_remaining > 0) { + if ((PUCHAR)reloc_block > (mapped_image_end - sizeof(IMAGE_BASE_RELOCATION))) { + throw_pe_fmt_exception(); + }
- if (ph.patched_crc32 != compute_target_crc32(file_info, decode_buf + file_info->input_size, ph.patched_size)) - { - err = ERROR_PATCH_CORRUPT; - goto free_decode_buf; - } + if ((reloc_block->SizeOfBlock <= (ULONG)reloc_dir_remaining) && + (reloc_block->SizeOfBlock > sizeof(IMAGE_BASE_RELOCATION))) {
- /* retained ranges must be ignored for this test */ - if ((apply_option_flags & APPLY_OPTION_FAIL_IF_EXACT) - && file_info->input_size == ph.patched_size - && memcmp(decode_buf, decode_buf + file_info->input_size, ph.patched_size) == 0) - { - err = ERROR_PATCH_NOT_NECESSARY; - goto free_decode_buf; - } + reloc_block_base = (IMAGE_BASE_RELOCATION UNALIGNED *)(mapped_image + + image_rva_to_file_offset(nt_headers, reloc_block->VirtualAddress, mapped_image, image_size)); + if (reloc_block_base) {
- if (!(apply_option_flags & APPLY_OPTION_TEST_ONLY)) - { - if (new_file_buf == NULL) + reloc_block_base_va = reloc_block->VirtualAddress + image_base_va; + reloc = (PUSHORT)((ULONG_PTR)reloc_block + sizeof(IMAGE_BASE_RELOCATION)); + reloc_count = (reloc_block->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT); + while (reloc_count--) { + if ((PUCHAR)reloc > (mapped_image_end - sizeof(USHORT))) { + throw_pe_fmt_exception(); + } + + reloc_type_offset = *reloc; + reloc_type = (UCHAR)(reloc_type_offset >> 12); + if (reloc_type != IMAGE_REL_BASED_ABSOLUTE) { + + reloc_offset = (USHORT)(reloc_type_offset & 0xFFF); + reloc_fixup = (PUCHAR)reloc_block_base + reloc_offset; + reloc_fixup_rva = reloc_block_base_va + reloc_offset - image_base_va; + + reloc_entries[reloc_entry_count].rva = get_new_rva_from_xfrm_table(xfrm_tbl, reloc_fixup_rva); + reloc_entries[reloc_entry_count].type = reloc_type; + + switch (reloc_type) { + + case IMAGE_REL_BASED_HIGH: + case IMAGE_REL_BASED_LOW: + if (reloc_fixup < mapped_image_end) { + *(USHORT UNALIGNED *)(hintmap + (reloc_fixup - mapped_image)) |= 0x0101; + } + break; + + case IMAGE_REL_BASED_HIGHLOW: + if (reloc_fixup < (mapped_image_end - sizeof(LONG))) { + *(ULONG UNALIGNED *)(hintmap + (reloc_fixup - mapped_image)) |= 0x01010101; + + reloc_target_va = *(ULONG UNALIGNED *)reloc_fixup; + reloc_target_rva = reloc_target_va - image_base_va; + + new_rva = get_new_rva_from_xfrm_table(xfrm_tbl, reloc_target_rva); + if (new_rva != reloc_target_rva) { + *(ULONG UNALIGNED *)reloc_fixup = image_base_va + new_rva; + } + } + break; + + case IMAGE_REL_BASED_HIGHADJ: + if (reloc_fixup < mapped_image_end) { + *(USHORT UNALIGNED *)(hintmap + (reloc_fixup - mapped_image)) |= 0x0101; + } + + ++reloc; + --reloc_count; + + reloc_entries[reloc_entry_count].hiadj = *reloc; + break; + } + + ++reloc_entry_count; + } + + ++reloc; + } + } + } + + reloc_dir_remaining -= reloc_block->SizeOfBlock; + reloc_block = (IMAGE_BASE_RELOCATION UNALIGNED *)((ULONG_PTR)reloc_block + reloc_block->SizeOfBlock); + } + + /* sort reloc entries by rva. */ + if (reloc_entry_count > 1) { + qsort(reloc_entries, reloc_entry_count, sizeof(struct reloc_entry), reloc_entry_compare); + } + + reloc_dir_remaining = (LONG)reloc_dir_size; + + /* calculate the remaining reloc directory size using the ".reloc" section */ + section_table = IMAGE_FIRST_SECTION(nt_headers); + section_count = nt_headers->FileHeader.NumberOfSections; + for (i = 0; i < section_count; i++) { + if ((PUCHAR)§ion_table[i] < mapped_image + || (PUCHAR)§ion_table[i] > (mapped_image_end - sizeof(IMAGE_SECTION_HEADER))) { + throw_pe_fmt_exception(); + } + + /* tolower hack check for ".reloc " */ + if ((*(ULONGLONG UNALIGNED *)§ion_table[i].Name | 0x2020202020202020) == 0x2020636F6C65722E) { + if (reloc_dir_offset >= section_table[i].PointerToRawData + && reloc_dir_offset < (section_table[i].PointerToRawData + + section_table[i].SizeOfRawData)) { + reloc_dir_remaining = + (section_table[i].PointerToRawData + section_table[i].SizeOfRawData) + - reloc_dir_offset; + } + } + } + + reloc_dir_remaining &= ~1; /* make even value */ + reloc_block = (IMAGE_BASE_RELOCATION UNALIGNED *)(mapped_image + reloc_dir_offset); + reloc_entry_index = 0; + while (reloc_dir_remaining > sizeof(IMAGE_BASE_RELOCATION) + && reloc_entry_index < reloc_entry_count) + { + reloc_block->VirtualAddress = (DWORD)(reloc_entries[reloc_entry_index].rva & 0xFFFFF000); + reloc_start = (PUSHORT)((ULONG_PTR)reloc_block + sizeof(IMAGE_BASE_RELOCATION)); + reloc = reloc_start; + reloc_dir_remaining -= sizeof(IMAGE_BASE_RELOCATION); + while (reloc_dir_remaining > 0 + && (reloc_entry_index < reloc_entry_count) + && (reloc_entries[reloc_entry_index].rva & 0xFFFFF000) == reloc_block->VirtualAddress) { - /* caller will VirtualFree the buffer */ - new_file_buf = decode_buf; - *pnew_file_buf = new_file_buf; + reloc_type_offset = ((USHORT)reloc_entries[reloc_entry_index].type << 12) | + ((USHORT)reloc_entries[reloc_entry_index].rva & 0xFFF); + *reloc++ = reloc_type_offset; + + reloc_dir_remaining -= sizeof(USHORT); + + if (reloc_dir_remaining > 0 + && ((ULONG)reloc_entries[reloc_entry_index].type << 12) == IMAGE_REL_BASED_HIGHADJ) { + + *reloc++ = reloc_entries[reloc_entry_index].hiadj; + reloc_dir_remaining -= sizeof(USHORT); + } + + ++reloc_entry_index; + } + + if (reloc_dir_remaining > 0 && ((ULONG_PTR)reloc & 2) != 0) { + *reloc++ = 0; + reloc_dir_remaining -= sizeof(USHORT); + } + + reloc_block->SizeOfBlock = (ULONG)(((ULONG_PTR)reloc - (ULONG_PTR)reloc_start) + sizeof(IMAGE_BASE_RELOCATION)); + reloc_block = (IMAGE_BASE_RELOCATION UNALIGNED *)((ULONG_PTR)reloc_block + reloc_block->SizeOfBlock); + } + + VirtualFree(reloc_entries, 0, MEM_RELEASE); +} + +static void patch_transform_PE_infer_relocs_x86( + IMAGE_NT_HEADERS32 UNALIGNED *nt_headers, PUCHAR mapped_image_base, ULONG mapped_image_size, + struct patch_transform_table *xfrm_tbl, BYTE *hintmap) +{ + const ULONG image_base_va = nt_headers->OptionalHeader.ImageBase; + const ULONG image_end_va = image_base_va + nt_headers->OptionalHeader.SizeOfImage; + const PUCHAR mapped_image_end = &mapped_image_base[mapped_image_size]; + PUCHAR p = &mapped_image_base[nt_headers->OptionalHeader.SizeOfHeaders]; + for (; p < (mapped_image_end - 4); p++) { + + /* Is this a possible valid rva? */ + const ULONG reloc_target_va = *(ULONG UNALIGNED *)p; + if (reloc_target_va >= image_base_va && reloc_target_va < image_end_va) { + + /* Check the hintmap. */ + const ULONG infer_mask = *(ULONG UNALIGNED *)(hintmap + (p - mapped_image_base)); + if ((infer_mask & 0x02020202) == 0) { + + const ULONG reloc_target_rva = reloc_target_va - image_base_va; + const ULONG new_rva = get_new_rva_from_xfrm_table(xfrm_tbl, reloc_target_rva); + if (new_rva != reloc_target_rva) { + *(ULONG UNALIGNED *)p = image_base_va + new_rva; + } + + *(ULONG UNALIGNED *)(hintmap + (p - mapped_image_base)) = infer_mask | 0x01010101; + + /* advance by 3 so next loop we advance to the next dword */ + p += 4-1; + } + } + } +} + +static void patch_transform_PE_infer_relocs( + IMAGE_NT_HEADERS32 UNALIGNED *nt_headers, PUCHAR mapped_image_base, ULONG mapped_image_size, + struct patch_transform_table *xfrm_tbl, BYTE *hintmap) +{ + ULONG reloc_dir_offset; + ULONG reloc_dir_size = 0; + + reloc_dir_offset = image_directory_offset_and_size(nt_headers, IMAGE_DIRECTORY_ENTRY_BASERELOC, + &reloc_dir_size, mapped_image_base, mapped_image_size); + if (reloc_dir_offset && reloc_dir_size + reloc_dir_offset <= mapped_image_size) { + patch_transform_PE_relocations(nt_headers, mapped_image_base, mapped_image_size, xfrm_tbl, hintmap); + } + + switch(nt_headers->FileHeader.Machine) { + case IMAGE_FILE_MACHINE_I386: + patch_transform_PE_infer_relocs_x86(nt_headers, mapped_image_base, mapped_image_size, xfrm_tbl, hintmap); + break; + + default: + break; + } +} + +static void patch_transform_PE_imports( + IMAGE_NT_HEADERS32 UNALIGNED *nt_headers, PUCHAR mapped_image_base, ULONG mapped_image_size, + struct patch_transform_table *xfrm_tbl, BYTE* hintmap) +{ + ULONG import_dir_offset; + ULONG import_dir_size; + IMAGE_IMPORT_DESCRIPTOR UNALIGNED *import_desc; + ULONG thunk_offset; + IMAGE_THUNK_DATA32 UNALIGNED *thunk_data_start; + IMAGE_THUNK_DATA32 UNALIGNED *thunk_data; + IMAGE_IMPORT_BY_NAME UNALIGNED *import_by_name; + ULONG import_by_name_offset; + + import_dir_offset = image_directory_offset_and_size(nt_headers, IMAGE_DIRECTORY_ENTRY_IMPORT, + &import_dir_size, mapped_image_base, mapped_image_size); + + /* Does the mapped image contain imports? */ + if (!import_dir_offset || (import_dir_offset + import_dir_size) > mapped_image_size) { + return; + } + + /* Update the hint map of the import directory. */ + memset(&hintmap[import_dir_offset], 0x03, import_dir_size); + + /* Loop through the import table, updating the new rvas. */ + import_desc = (IMAGE_IMPORT_DESCRIPTOR UNALIGNED *)(mapped_image_base + import_dir_offset); + while (import_desc->OriginalFirstThunk) { + if (import_desc->TimeDateStamp == 0) { /* 0 if not bound */ + + thunk_offset = image_rva_to_file_offset(nt_headers, import_desc->OriginalFirstThunk, + mapped_image_base, mapped_image_size); + thunk_data_start = (IMAGE_THUNK_DATA32 UNALIGNED *)(mapped_image_base + thunk_offset); + thunk_data = thunk_data_start; + while (thunk_data->u1.Ordinal != 0) { + if (!IMAGE_SNAP_BY_ORDINAL32(thunk_data->u1.Ordinal)) { + import_by_name_offset = image_rva_to_file_offset(nt_headers, thunk_data->u1.AddressOfData, + mapped_image_base, mapped_image_size); + import_by_name = (IMAGE_IMPORT_BY_NAME UNALIGNED *)(mapped_image_base + import_by_name_offset); + memset(&hintmap[import_by_name_offset], 0x03, + sizeof(IMAGE_IMPORT_BY_NAME) + strlen((char*)import_by_name->Name)); + thunk_data->u1.AddressOfData = get_new_rva_from_xfrm_table(xfrm_tbl, thunk_data->u1.AddressOfData); + } + thunk_data++; + } + memset(&hintmap[thunk_offset], 0x03, ((size_t)thunk_data - (size_t)thunk_data_start)); + + thunk_offset = image_rva_to_file_offset(nt_headers, import_desc->FirstThunk, + mapped_image_base, mapped_image_size); + thunk_data_start = (IMAGE_THUNK_DATA32 UNALIGNED *)(mapped_image_base + thunk_offset); + thunk_data = thunk_data_start; + while (thunk_data->u1.Ordinal != 0) { + if (!IMAGE_SNAP_BY_ORDINAL32(thunk_data->u1.Ordinal)) { + import_by_name_offset = image_rva_to_file_offset(nt_headers, thunk_data->u1.AddressOfData, + mapped_image_base, mapped_image_size); + import_by_name = (IMAGE_IMPORT_BY_NAME UNALIGNED *)(mapped_image_base + import_by_name_offset); + memset(&hintmap[import_by_name_offset], 0x03, + sizeof(IMAGE_IMPORT_BY_NAME) + strlen((char*)import_by_name->Name)); + thunk_data->u1.AddressOfData = get_new_rva_from_xfrm_table(xfrm_tbl, thunk_data->u1.AddressOfData); + } + thunk_data++; + } + memset(&hintmap[thunk_offset], 0x03, ((size_t)thunk_data - (size_t)thunk_data_start)); + } + + import_desc->Name = get_new_rva_from_xfrm_table(xfrm_tbl, import_desc->Name); + import_desc->OriginalFirstThunk = get_new_rva_from_xfrm_table(xfrm_tbl, import_desc->OriginalFirstThunk); + import_desc->FirstThunk = get_new_rva_from_xfrm_table(xfrm_tbl, import_desc->FirstThunk); + + import_desc++; + } +} + +static void patch_transform_PE_exports( + IMAGE_NT_HEADERS32 UNALIGNED *nt_headers, PUCHAR mapped_image_base, ULONG mapped_image_size, + struct patch_transform_table *xfrm_tbl, BYTE *hintmap) +{ + IMAGE_EXPORT_DIRECTORY UNALIGNED *export_directory; + ULONG UNALIGNED *export; + ULONG export_count; + PUCHAR mapped_image_end; + ULONG offset; + ULONG export_dir_offset; + ULONG export_dir_size; + + mapped_image_end = mapped_image_base + mapped_image_size; + + export_dir_offset = image_directory_offset_and_size(nt_headers, IMAGE_DIRECTORY_ENTRY_EXPORT, + &export_dir_size, mapped_image_base, mapped_image_size); + + /* Does the mapped image contain exports? */ + if (!export_dir_offset || (export_dir_offset + export_dir_size) > mapped_image_size) { + return; + } + + /* Update the hint map of the export directory. */ + memset(hintmap + export_dir_offset, 0x03, export_dir_size); + + export_directory = (IMAGE_EXPORT_DIRECTORY UNALIGNED *)(mapped_image_base + export_dir_offset); + + /* Update the hint map and rvas for the AddressOfFunctions table. */ + export_count = export_directory->NumberOfFunctions; + offset = image_rva_to_file_offset(nt_headers, export_directory->AddressOfFunctions, + mapped_image_base, mapped_image_size); + if ((offset + export_count * sizeof(ULONG)) <= mapped_image_size) { + memset(&hintmap[offset], 0x03, export_count * sizeof(ULONG)); + } + export = (PULONG)(mapped_image_base + offset); + while (export_count--) { + if ((PUCHAR)&export[1] > mapped_image_end) { + break; + } + *export = get_new_rva_from_xfrm_table(xfrm_tbl, *export); + export++; + } + + /* Update the hint map and rvas for the AddressOfNames table. */ + export_count = export_directory->NumberOfNames; + offset = image_rva_to_file_offset(nt_headers, export_directory->AddressOfNames, + mapped_image_base, mapped_image_size); + if ((offset + export_count * sizeof(ULONG)) <= mapped_image_size) { + memset(&hintmap[offset], 0x03, export_count * sizeof(ULONG)); + } + export = (PULONG)(mapped_image_base + offset); + while (export_count--) { + if ((PUCHAR)&export[1] > mapped_image_end) { + break; + } + *export = get_new_rva_from_xfrm_table(xfrm_tbl, *export); + export++; + } + + /* Update the hint map for the AddressOfNameOrdinals table. */ + export_count = export_directory->NumberOfNames; + offset = image_rva_to_file_offset(nt_headers, export_directory->AddressOfNameOrdinals, + mapped_image_base, mapped_image_size); + if ((offset + export_count * sizeof(USHORT)) <= mapped_image_size) { + memset(&hintmap[offset], 0x03, export_count * sizeof(USHORT)); + } + + /* Update export directory rvas. */ + export_directory->Name = get_new_rva_from_xfrm_table(xfrm_tbl, export_directory->Name); + export_directory->AddressOfFunctions = get_new_rva_from_xfrm_table(xfrm_tbl, export_directory->AddressOfFunctions); + export_directory->AddressOfNames = get_new_rva_from_xfrm_table(xfrm_tbl, export_directory->AddressOfNames); + export_directory->AddressOfNameOrdinals = get_new_rva_from_xfrm_table(xfrm_tbl, export_directory->AddressOfNameOrdinals); +} + +static void patch_transform_PE_relative_jmps( + IMAGE_NT_HEADERS32 UNALIGNED *nt_headers, PUCHAR mapped_image_base, ULONG mapped_image_size, + struct patch_transform_table *xfrm_tbl, BYTE *hintmap) +{ + ULONG image_size; + IMAGE_SECTION_HEADER UNALIGNED *section_table; + ULONG section_count; + PBYTE section_start, section_end; + ULONG section_size; + ULONG section_offset; + ULONG section_rva; + LONG disp, new_disp; + ULONG ins_rva, target_rva; + ULONG new_ins_rva, new_target_rva; + ULONG target_offset; + ULONG i, j; + PBYTE p, hint; + BYTE opcode; + BOOL needs_patch; + + image_size = nt_headers->OptionalHeader.SizeOfImage; + section_table = IMAGE_FIRST_SECTION(nt_headers); + section_count = nt_headers->FileHeader.NumberOfSections; + + for (i = 0; i < section_count; i++) { + if ((PUCHAR)§ion_table[i] < mapped_image_base + || (PUCHAR)§ion_table[i+1] > &mapped_image_base[mapped_image_size]) { + throw_pe_fmt_exception(); + } + + /* If this section is executable, then scan it for relative jump instructions. */ + if (section_table[i].Characteristics & IMAGE_SCN_MEM_EXECUTE) { + + section_rva = section_table[i].VirtualAddress; + section_size = min(section_table[i].Misc.VirtualSize, section_table[i].SizeOfRawData); + section_offset = section_table[i].PointerToRawData; + section_start = (PUCHAR)mapped_image_base + section_offset; + + if (section_offset < mapped_image_size + && (section_offset + section_size) <= mapped_image_size + && nt_headers->FileHeader.Machine == IMAGE_FILE_MACHINE_I386) + { + section_end = section_start + section_size - 5; /* 5 is size of relative jmp instruction */ + + for (p = section_start; p < section_end; p++) + { + if ((*p == 0xE9) /* jmp rel32 (E9) */ + || (*p == 0x0F && (p[1] & 0xF0) == 0x80 && (++p < section_end)) /* jcc rel32 (0F 8?) */ + ) + { + /* Check the hint map in case this has already been patched. */ + needs_patch = FALSE; + hint = &hintmap[section_offset + ((ULONG_PTR)p - (ULONG_PTR)section_start) - 1]; + if ((*p & 0xF0) != 0x80 || (*hint & 1) == 0) { + hint++; + j = 0; + while ((*hint++ & 1) == 0) { + if (++j >= 5) { + needs_patch = TRUE; + break; + } + } + } + + if (needs_patch) { + disp = *(LONG UNALIGNED *)(p + 1); + if (disp > 127 || disp < -128) + { + ins_rva = section_rva + (ULONG)((p + 5) - section_start); + target_rva = ins_rva + disp; + if (target_rva < image_size) + { + target_offset = image_rva_to_file_offset(nt_headers, target_rva, + mapped_image_base, mapped_image_size); + if ((hintmap[target_offset] & 1) == 0) { + new_target_rva = get_new_rva_from_xfrm_table(xfrm_tbl, target_rva); + new_ins_rva = get_new_rva_from_xfrm_table(xfrm_tbl, ins_rva); + new_disp = new_target_rva - new_ins_rva; + if (new_disp > 127 || new_disp < -128) { + if (new_disp != disp) { + *(LONG UNALIGNED *)(p + 1) = new_disp; + } + } + else { + opcode = *p; + if (opcode == 0xE9) { + *p = 0xEB; + p[1] = (char)new_disp; + } else { + *p = (char)new_disp; + *(p - 1) = (opcode & 0x0F) | 0x70; + } + } + p += 4; + } + } + } + } + } + } + } + } + } +} + +static void patch_transform_PE_relative_calls( + IMAGE_NT_HEADERS32 UNALIGNED *nt_headers, PUCHAR mapped_image_base, ULONG mapped_image_size, + struct patch_transform_table *xfrm_tbl, PUCHAR hintmap) +{ + IMAGE_SECTION_HEADER UNALIGNED *section_table; + ULONG sizeof_image; + PUCHAR section_start, section_end; + ULONG section_size; + ULONG section_offset; + ULONG section_rva; + LONG disp, new_disp; + ULONG ins_rva, target_rva; + ULONG new_ins_rva, new_target_rva; + ULONG target_offset; + ULONG i, j; + PUCHAR p, hint; + BOOL needs_patch; + + sizeof_image = nt_headers->OptionalHeader.SizeOfImage; + section_table = IMAGE_FIRST_SECTION(nt_headers); + + for (i = 0; i < nt_headers->FileHeader.NumberOfSections; i++) { + if ((PUCHAR)§ion_table[i] < mapped_image_base + || (PUCHAR)§ion_table[i+1] > &mapped_image_base[mapped_image_size]) { + throw_pe_fmt_exception(); + } + + /* If this section is executable, then scan it for relative jump instructions. */ + if (section_table[i].Characteristics & IMAGE_SCN_MEM_EXECUTE) { + + section_rva = section_table[i].VirtualAddress; + section_size = min(section_table[i].Misc.VirtualSize, section_table[i].SizeOfRawData); + section_offset = section_table[i].PointerToRawData; + section_start = (PUCHAR)mapped_image_base + section_offset; + + if (section_offset < mapped_image_size + && (section_offset + section_size) <= mapped_image_size + && nt_headers->FileHeader.Machine == IMAGE_FILE_MACHINE_I386) + { + section_end = section_start + section_size - 5; /* 5 is size of relative jmp instruction */ + + for (p = section_start; p < section_end; p++) + { + if (*p == 0xE8) /* call rel32 (E8) */ + { + /* Check the hint map in case this has already been patched. */ + needs_patch = FALSE; + j = 0; + hint = &hintmap[section_offset + (p - section_start)]; + while ((*hint++ & 1) == 0) { + if (++j >= 5) { + needs_patch = TRUE; + break; + } + } + + if (needs_patch) { + disp = *(LONG UNALIGNED *)(p + 1); + ins_rva = section_rva + (ULONG)((ULONG_PTR)p - (ULONG_PTR)section_start) + 5; + target_rva = ins_rva + disp; + if (target_rva < sizeof_image) { + target_offset = image_rva_to_file_offset(nt_headers, target_rva, + mapped_image_base, mapped_image_size); + if ((hintmap[target_offset] & 1) == 0) { + new_target_rva = get_new_rva_from_xfrm_table(xfrm_tbl, target_rva); + new_ins_rva = get_new_rva_from_xfrm_table(xfrm_tbl, ins_rva); + new_disp = new_target_rva - new_ins_rva; + if (new_disp != disp) { + *(LONG UNALIGNED *)(p + 1) = new_disp; + } + p += 4; + } + } + } + } + } + } + } + } +} + +struct transform_resource_context { /* i386 | x64 */ + struct patch_transform_table* xfrm_tbl; /* 0x00 | 0x00 */ + PUCHAR res_dir_base; /* 0x04 | 0x08 */ + PUCHAR res_dir_end; /* 0x08 | 0x10 */ + ULONG res_dir_size; /* 0x0C | 0x18 */ + ULONG cursor; /* 0x10 | 0x1C */ + ULONG new_res_time; /* 0x14 | 0x20 */ + ULONG sizeof_image; /* 0x18 | 0x24 */ + PUCHAR image_base; /* 0x1C | 0x28 */ + ULONG old_rva; /* 0x20 | 0x30 */ + ULONG new_rva; /* 0x24 | 0x34 */ + BOOL out_of_bounds; /* 0x28 | 0x38 */ +}; + +static void transform_resources_recursive_old_compat( + struct transform_resource_context *ctx, + IMAGE_RESOURCE_DIRECTORY UNALIGNED *res_dir) +{ + ULONG entry_count, i; + IMAGE_RESOURCE_DIRECTORY_ENTRY UNALIGNED *entry; + IMAGE_RESOURCE_DATA_ENTRY UNALIGNED *res_data; + IMAGE_RESOURCE_DIRECTORY UNALIGNED *recursive_res_dir; + ULONG offset_to_data; + ULONG name_offset; + ULONG new_offset; + ULONG new_rva; + + if (((PUCHAR)res_dir + sizeof(IMAGE_RESOURCE_DIRECTORY)) < ctx->res_dir_end) { + + ctx->cursor += sizeof(IMAGE_RESOURCE_DIRECTORY); + + if (ctx->cursor <= ctx->res_dir_size) { + + if (res_dir->TimeDateStamp != ctx->new_res_time) { + res_dir->TimeDateStamp = ctx->new_res_time; + } + + entry_count = res_dir->NumberOfNamedEntries + res_dir->NumberOfIdEntries; + entry = (IMAGE_RESOURCE_DIRECTORY_ENTRY UNALIGNED *)((PUCHAR)res_dir + sizeof(IMAGE_RESOURCE_DIRECTORY)); + + for (i = 0; i < entry_count; i++, entry++) { + if ((PUCHAR)entry >= (ctx->res_dir_end - sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY))) { + break; + } + + offset_to_data = entry->OffsetToData & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY; + if (entry->OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY) { + /* Recurse into next resource directory. */ + recursive_res_dir = (IMAGE_RESOURCE_DIRECTORY UNALIGNED *)&ctx->res_dir_base[offset_to_data]; + if ((PUCHAR)recursive_res_dir >= ctx->image_base) { + transform_resources_recursive_old_compat(ctx, recursive_res_dir); + } + + } else { + /* Update rva of valid resource data entry. */ + res_data = (IMAGE_RESOURCE_DATA_ENTRY UNALIGNED *)&ctx->res_dir_base[offset_to_data]; + if ((PUCHAR)res_data > ctx->res_dir_base + && (PUCHAR)res_data < (ctx->res_dir_end - sizeof(IMAGE_RESOURCE_DATA_ENTRY))) + { + ctx->cursor += res_data->Size; + if (ctx->cursor > ctx->res_dir_size) { + return; + } + + new_rva = get_new_rva_from_xfrm_table(ctx->xfrm_tbl, res_data->OffsetToData); + if (res_data->OffsetToData != new_rva) { + res_data->OffsetToData = new_rva; + } + } + } + + new_offset = get_new_rva_from_xfrm_table(ctx->xfrm_tbl, offset_to_data + ctx->old_rva) - ctx->new_rva; + if (offset_to_data != new_offset) { + entry->OffsetToData ^= (new_offset ^ entry->OffsetToData) & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY; + } + + if (entry->Name & IMAGE_RESOURCE_NAME_IS_STRING) { + name_offset = entry->Name & ~IMAGE_RESOURCE_NAME_IS_STRING; + new_offset = get_new_rva_from_xfrm_table(ctx->xfrm_tbl, name_offset + ctx->old_rva) - ctx->new_rva; + if (name_offset != new_offset) { + entry->Name ^= (new_offset ^ entry->Name) & ~IMAGE_RESOURCE_NAME_IS_STRING; + } + } + + ctx->cursor += sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY); + if (ctx->cursor > ctx->res_dir_size) { + return; + } + } } - memmove(new_file_buf, decode_buf + old_file_size, ph.patched_size); } +}
- if (new_file_time != NULL) +static void patch_transform_PE_resources_old_compat( + IMAGE_NT_HEADERS32 UNALIGNED *nt_headers, PUCHAR mapped_image, ULONG image_size, + ULONG new_res_time, struct patch_transform_table *xfrm_tbl) +{ + struct transform_resource_context ctx; + const PUCHAR mapped_image_end = &mapped_image[image_size]; + + ctx.res_dir_base = image_directory_mapped_address(nt_headers, IMAGE_DIRECTORY_ENTRY_RESOURCE, + &ctx.res_dir_size, mapped_image, image_size); + if (ctx.res_dir_base && ctx.res_dir_size >= sizeof(IMAGE_RESOURCE_DIRECTORY)) { + ctx.res_dir_end = ctx.res_dir_base + ctx.res_dir_size; + if (ctx.res_dir_end >= mapped_image_end) { + ctx.res_dir_end = mapped_image_end; + } + + ctx.cursor = 0; + ctx.out_of_bounds = FALSE; + + ctx.old_rva = image_directory_rva_and_size(nt_headers, IMAGE_DIRECTORY_ENTRY_RESOURCE, + NULL, mapped_image, image_size); + ctx.new_rva = get_new_rva_from_xfrm_table(xfrm_tbl, ctx.old_rva); + + ctx.new_res_time = new_res_time; + ctx.sizeof_image = nt_headers->OptionalHeader.SizeOfImage; + ctx.image_base = mapped_image; + ctx.xfrm_tbl = xfrm_tbl; + + if (ctx.res_dir_base >= mapped_image) { + transform_resources_recursive_old_compat(&ctx, + (IMAGE_RESOURCE_DIRECTORY UNALIGNED *)ctx.res_dir_base); + } + } +} + +static void transform_resources_recursive( + struct transform_resource_context *ctx, + IMAGE_RESOURCE_DIRECTORY UNALIGNED *res_dir) +{ + IMAGE_RESOURCE_DIRECTORY_ENTRY UNALIGNED *entry; + IMAGE_RESOURCE_DATA_ENTRY UNALIGNED *res_data; + IMAGE_RESOURCE_DIRECTORY UNALIGNED *recursive_res_dir; + ULONG i; + ULONG entry_count; + ULONG offset_to_data; + ULONG name_offset; + ULONG new_offset; + ULONG new_rva; + + if ((PUCHAR)res_dir < ctx->res_dir_base + || ((PUCHAR)res_dir + sizeof(IMAGE_RESOURCE_DIRECTORY)) > ctx->res_dir_end) { + ctx->out_of_bounds = TRUE; + return; + } + + ctx->cursor += sizeof(IMAGE_RESOURCE_DIRECTORY); + if (ctx->cursor > ctx->res_dir_size) { + ctx->out_of_bounds = TRUE; + return; + } + + if (res_dir->TimeDateStamp != ctx->new_res_time) { + res_dir->TimeDateStamp = ctx->new_res_time; + } + + entry = (IMAGE_RESOURCE_DIRECTORY_ENTRY UNALIGNED *)((PUCHAR)res_dir + sizeof(IMAGE_RESOURCE_DIRECTORY)); + entry_count = res_dir->NumberOfNamedEntries + res_dir->NumberOfIdEntries; + + for (i = 0; i < entry_count; i++, entry++) { + if (((PUCHAR)entry + sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY)) > ctx->res_dir_end) { + ctx->out_of_bounds = TRUE; + return; + } + + ctx->cursor += sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY); + if (ctx->cursor > ctx->res_dir_size) { + ctx->out_of_bounds = TRUE; + return; + } + + offset_to_data = entry->OffsetToData & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY; + if (entry->OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY) { + if (ctx->res_dir_size >= sizeof(IMAGE_RESOURCE_DIRECTORY) + && (offset_to_data + sizeof(IMAGE_RESOURCE_DIRECTORY)) <= ctx->res_dir_size) { + + /* Recurse into next resource directory. */ + recursive_res_dir = (IMAGE_RESOURCE_DIRECTORY UNALIGNED *)&ctx->res_dir_base[offset_to_data]; + if ((PUCHAR)recursive_res_dir >= ctx->res_dir_base) { + transform_resources_recursive(ctx, recursive_res_dir); + } else { + ctx->out_of_bounds = TRUE; + } + + if (ctx->cursor > ctx->res_dir_size) { + ctx->out_of_bounds = TRUE; + return; + } + } else { + ctx->out_of_bounds = TRUE; + } + + } else { + /* Update rva of valid resource data entry. */ + res_data = (IMAGE_RESOURCE_DATA_ENTRY UNALIGNED *)&ctx->res_dir_base[offset_to_data]; + if ((PUCHAR)res_data < ctx->res_dir_base + && ((PUCHAR)res_data + sizeof(IMAGE_RESOURCE_DATA_ENTRY)) > ctx->res_dir_end) { + ctx->out_of_bounds = TRUE; + } else { + + new_rva = get_new_rva_from_xfrm_table(ctx->xfrm_tbl, res_data->OffsetToData); + if (res_data->OffsetToData != new_rva) { + res_data->OffsetToData = new_rva; + } + } + } + + new_offset = get_new_rva_from_xfrm_table(ctx->xfrm_tbl, offset_to_data + ctx->old_rva) - ctx->new_rva; + if (offset_to_data != new_offset) { + entry->OffsetToData ^= (new_offset ^ entry->OffsetToData) & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY; + } + + if (entry->Name & IMAGE_RESOURCE_NAME_IS_STRING) { + name_offset = entry->Name & ~IMAGE_RESOURCE_NAME_IS_STRING; + new_offset = get_new_rva_from_xfrm_table(ctx->xfrm_tbl, name_offset + ctx->old_rva) - ctx->new_rva; + if (entry->NameOffset != new_offset) { + entry->Name ^= (new_offset ^ entry->Name) & ~IMAGE_RESOURCE_NAME_IS_STRING; + } + } + } +} + +static BOOL patch_transform_PE_resources( + IMAGE_NT_HEADERS32 UNALIGNED *nt_headers, PUCHAR mapped_image, SIZE_T image_size, + ULONG new_res_time, struct patch_transform_table *xfrm_tbl) +{ + BOOL result; + struct transform_resource_context ctx; + + ctx.res_dir_base = image_directory_mapped_address(nt_headers, IMAGE_DIRECTORY_ENTRY_RESOURCE, + &ctx.res_dir_size, mapped_image, image_size); + if (ctx.res_dir_base) { + ctx.res_dir_end = ctx.res_dir_base + ctx.res_dir_size; + ctx.cursor = 0; + ctx.image_base = mapped_image; + ctx.sizeof_image = nt_headers->OptionalHeader.SizeOfImage; + ctx.old_rva = image_directory_rva_and_size(nt_headers, IMAGE_DIRECTORY_ENTRY_RESOURCE, + NULL, mapped_image, image_size); + ctx.new_rva = get_new_rva_from_xfrm_table(xfrm_tbl, ctx.old_rva); + ctx.new_res_time = new_res_time; + ctx.xfrm_tbl = xfrm_tbl; + ctx.out_of_bounds = FALSE; + transform_resources_recursive(&ctx, (IMAGE_RESOURCE_DIRECTORY UNALIGNED *)ctx.res_dir_base); + result = !ctx.out_of_bounds; + } else { + result = TRUE; + } + + return result; +} + +static BOOL patch_coff_image( + PULONG transform_option_flags, IMAGE_NT_HEADERS32 UNALIGNED *nt_headers, + PUCHAR old_file_buffer, ULONG old_file_size, ULONG new_res_time, + struct patch_transform_table* xfrm_tbl, unsigned char *hintmap) +{ + PUCHAR local_hintmap = NULL; + const ULONG transform_flags = *transform_option_flags; + + /* initialize the hintmap */ + if (!hintmap) { + local_hintmap = VirtualAlloc(NULL, old_file_size, MEM_COMMIT, PAGE_READWRITE); + if (!local_hintmap) { + return FALSE; + } + hintmap = local_hintmap; + } + memset(hintmap, 0x00, old_file_size); + + /* Order of transformations may matter here! */ + if (transform_flags & (PATCH_TRANSFORM_PE_RESOURCE_2 | PATCH_TRANSFORM_PE_IRELOC_2)) + { + patch_transform_PE_mark_non_executable(nt_headers, old_file_buffer, old_file_size, hintmap, FALSE); + + if ((transform_flags & PATCH_TRANSFORM_NO_IMPORTS) == 0) { + patch_transform_PE_imports(nt_headers, old_file_buffer, old_file_size, xfrm_tbl, hintmap); + } + + if ((transform_flags & PATCH_TRANSFORM_NO_EXPORTS) == 0) { + patch_transform_PE_exports(nt_headers, old_file_buffer, old_file_size, xfrm_tbl, hintmap); + } + + if ((transform_flags & PATCH_TRANSFORM_NO_RESOURCE) == 0) { + patch_transform_PE_resources(nt_headers, old_file_buffer, old_file_size, new_res_time, xfrm_tbl); + } + + if ((transform_flags & PATCH_TRANSFORM_NO_RELOCS) == 0) { + if (transform_flags & PATCH_TRANSFORM_PE_IRELOC_2) { + patch_transform_PE_infer_relocs(nt_headers, old_file_buffer, old_file_size, xfrm_tbl, hintmap); + } else { + patch_transform_PE_relocations(nt_headers, old_file_buffer, old_file_size, xfrm_tbl, hintmap); + } + } + + if ((transform_flags & PATCH_TRANSFORM_NO_RELJMPS) == 0) { + patch_transform_PE_relative_jmps(nt_headers, old_file_buffer, old_file_size, xfrm_tbl, hintmap); + } + + if ((transform_flags & PATCH_TRANSFORM_NO_RELCALLS) == 0) { + patch_transform_PE_relative_calls(nt_headers, old_file_buffer, old_file_size, xfrm_tbl, hintmap); + } + + } else { + + patch_transform_PE_mark_non_executable(nt_headers, old_file_buffer, old_file_size, hintmap, TRUE); + + if ((transform_flags & PATCH_TRANSFORM_NO_RELOCS) == 0) { + patch_transform_PE_relocations(nt_headers, old_file_buffer, old_file_size, xfrm_tbl, hintmap); + } + + if ((transform_flags & PATCH_TRANSFORM_NO_IMPORTS) == 0) { + patch_transform_PE_imports(nt_headers, old_file_buffer, old_file_size, xfrm_tbl, hintmap); + } + + if ((transform_flags & PATCH_TRANSFORM_NO_EXPORTS) == 0) { + patch_transform_PE_exports(nt_headers, old_file_buffer, old_file_size, xfrm_tbl, hintmap); + } + + if ((transform_flags & PATCH_TRANSFORM_NO_RELJMPS) == 0) { + patch_transform_PE_relative_jmps(nt_headers, old_file_buffer, old_file_size, xfrm_tbl, hintmap); + } + + if ((transform_flags & PATCH_TRANSFORM_NO_RELCALLS) == 0) { + patch_transform_PE_relative_calls(nt_headers, old_file_buffer, old_file_size, xfrm_tbl, hintmap); + } + + if ((transform_flags & PATCH_TRANSFORM_NO_RESOURCE) == 0) { + patch_transform_PE_resources_old_compat(nt_headers, old_file_buffer, old_file_size, new_res_time, xfrm_tbl); + } + } + + if (local_hintmap != NULL) { + VirtualFree(local_hintmap, 0, MEM_RELEASE); + } + + return TRUE; +} + +/* Performs patches on 32-bit PE images. */ +static DWORD perform_patches_on_old_file_image( + PULONG transform_option_flags, PVOID old_file_buffer, ULONG old_file_size, + ULONG new_file_res_time, struct patch_transform_table* xfrm_tbl) +{ + DWORD err = ERROR_SUCCESS; + IMAGE_NT_HEADERS32 UNALIGNED *nt_headers; + + nt_headers = image_get_nt_headers(old_file_buffer, old_file_size); + if (nt_headers) { + *transform_option_flags |= PATCH_EXTRA_HAS_PATCH_TRANSFORMS; + if (!patch_coff_image(transform_option_flags, nt_headers, old_file_buffer, + old_file_size, new_file_res_time, xfrm_tbl, NULL)) { + err = GetLastError(); + } + } else { + err = ERROR_BAD_EXE_FORMAT; + } + + return err; +} + +DWORD apply_patch_to_file_by_buffers( + const BYTE *patch_file_view, const ULONG patch_file_size, + const BYTE *old_file_view, ULONG old_file_size, + BYTE **pnew_file_buf, const ULONG new_file_buf_size, + ULONG *new_file_actual_size, FILETIME *new_file_time, + const ULONG apply_option_flags, + PATCH_PROGRESS_CALLBACK *progress_fn, void *progress_ctx) +{ + DWORD err = ERROR_SUCCESS; + struct patch_file_header ph = {0}; + struct old_file_info *file_info; + struct old_file_info *found_file; + ULONG fileno; + ULONG i; + int normalize_result; + BOOL patched; + ULONG old_crc32; + ULONG patched_crc32; + ULONG buf_size; + BYTE *in_new_file_buf = NULL; + BYTE *out_new_file_buf = NULL; + BYTE *new_file_buf = NULL; + BYTE *old_file_buf = NULL; + BYTE *decode_buf = NULL; + BYTE *retain_buffer = NULL; + + if (new_file_actual_size != NULL) { + *new_file_actual_size = 0; + } + + if (new_file_time != NULL) { new_file_time->dwLowDateTime = 0; new_file_time->dwHighDateTime = 0; + } + + if (pnew_file_buf == NULL) { + if (!(apply_option_flags & APPLY_OPTION_TEST_ONLY)) { + return ERROR_INVALID_PARAMETER; + }
- /* the meaning of PATCH_OPTION_NO_TIMESTAMP is inverted for decoding */ - if (ph.flags & PATCH_OPTION_NO_TIMESTAMP) - posix_time_to_file_time(ph.timestamp, new_file_time); + } else { + in_new_file_buf = *pnew_file_buf; }
-free_decode_buf: - if(decode_buf != NULL && decode_buf != new_file_buf) + if (old_file_view == NULL) { + old_file_size = 0; + } + + /* read the patch file header */ + if (read_patch_header(&ph, patch_file_view, patch_file_size)) { + err = ph.err; + goto cleanup; + } + + if ((ph.flags & PATCH_OPTION_NO_TIMESTAMP) == 0) { + TRACE("new file time is %lX\n", ph.new_file_time); + if (new_file_time != NULL) { + if (ph.new_file_time) { + posix_time_to_file_time(ph.new_file_time, new_file_time); + } + } + } + + if (new_file_actual_size != NULL) { + *new_file_actual_size = (ULONG)ph.patched_size; + } + + if (in_new_file_buf != NULL && new_file_buf_size < ph.patched_size) { + err = ERROR_INSUFFICIENT_BUFFER; + goto cleanup; + } + + buf_size = ph.patched_size + old_file_size; + decode_buf = in_new_file_buf; + if (in_new_file_buf == NULL || new_file_buf_size < buf_size) + { + /* decode_buf must have room for both files, so allocate a new buffer if + * necessary. This will be returned to the caller if new_file_buf == NULL */ + decode_buf = VirtualAlloc(NULL, buf_size, MEM_COMMIT, PAGE_READWRITE); + if (decode_buf == NULL) { + err = GetLastError(); + goto cleanup; + } + new_file_buf = &decode_buf[old_file_size]; + } + + if (old_file_view != NULL) { + old_file_buf = decode_buf; + memcpy(old_file_buf, old_file_view, old_file_size); + } + + patched = FALSE; + retain_buffer = NULL; + found_file = NULL; + + /* Check if the old file matches the new file. */ + if (old_file_size == ph.patched_size) + { + file_info = &ph.file_table[0]; + if (file_info->retain_range_count) + { + /* Save the new file retain ranges for applying later. */ + retain_buffer = save_retained_ranges(old_file_buf, old_file_size, + file_info->retain_range_array, file_info->retain_range_count, TRUE); + if (!retain_buffer) { + err = GetLastError(); + goto cleanup; + } + + /* Zero out retain ranges for crc32 checksum verification. */ + for (i = 0; i < file_info->retain_range_count; i++) { + if (file_info->retain_range_array[i].OffsetInNewFile <= old_file_size + && (file_info->retain_range_array[i].OffsetInNewFile + + file_info->retain_range_array[i].LengthInBytes) <= old_file_size) { + memset(&old_file_buf[file_info->retain_range_array[i].OffsetInNewFile], + 0, + file_info->retain_range_array[i].LengthInBytes); + patched = TRUE; + } + } + } + + /* Verify if the file matches the expected crc32 checksum. */ + old_crc32 = RtlComputeCrc32(0, old_file_buf, old_file_size); + normalize_result = NORMALIZE_RESULT_FAILURE; + if (old_crc32 != ph.patched_crc32) { + + /* Try again with normalizations applied to the file. */ + normalize_result = normalize_old_file_image(old_file_buf, old_file_size, + ph.flags, NULL, ph.new_image_base, ph.new_image_time, + NULL, 0, NULL, 0); + if (normalize_result == NORMALIZE_RESULT_FAILURE) { + err = ERROR_PATCH_CORRUPT; + goto cleanup; + } else if (normalize_result >= NORMALIZE_RESULT_SUCCESS_MODIFIED) { + patched = TRUE; + } + + old_crc32 = RtlComputeCrc32(0, old_file_buf, old_file_size); + } + + /* Did we calculate the expected crc32 checksum? */ + if (old_crc32 == ph.patched_crc32) { + + /* Check if patching was necessary. */ + if ((apply_option_flags & APPLY_OPTION_FAIL_IF_CLOSE) != 0 + || ((apply_option_flags & APPLY_OPTION_FAIL_IF_EXACT) != 0 && patched)) + { + err = ERROR_PATCH_NOT_NECESSARY; + goto cleanup; + } + + if (!(apply_option_flags & APPLY_OPTION_TEST_ONLY) && ph.patched_size) { + /* Files are identical so copy old to new. */ + memcpy(new_file_buf, old_file_buf, old_file_size); + found_file = file_info; + } + } + + /* Cleanup the retain buffer if expected match was not found. */ + if (!found_file && retain_buffer) { + VirtualFree(retain_buffer, 0, MEM_RELEASE); + retain_buffer = NULL; + } + } + + /* If the old file didn't match the new file, then check if the old file + * matches one of the files in the patch file table. + */ + for (fileno = 0; !found_file && fileno < ph.old_file_count; ++fileno) + { + file_info = &ph.file_table[fileno]; + + if (file_info->old_size == old_file_size) + { + if (patched) { + memcpy(old_file_buf, old_file_view, old_file_size); + patched = FALSE; + } + + if (file_info->retain_range_count && !(apply_option_flags & APPLY_OPTION_TEST_ONLY)) { + retain_buffer = save_retained_ranges(old_file_buf, old_file_size, + file_info->retain_range_array, file_info->retain_range_count, FALSE); + if (!retain_buffer) { + err = GetLastError(); + goto cleanup; + } + } + + normalize_result = normalize_old_file_image(old_file_buf, old_file_size, + ph.flags, NULL, ph.new_image_base, ph.new_image_time, + file_info->ignore_range_array, file_info->ignore_range_count, + file_info->retain_range_array, file_info->retain_range_count); + if (normalize_result == NORMALIZE_RESULT_FAILURE) { + err = ERROR_PATCH_CORRUPT; + goto cleanup; + } else if (normalize_result >= NORMALIZE_RESULT_SUCCESS_MODIFIED) { + patched = TRUE; + } + + old_crc32 = RtlComputeCrc32(0, old_file_buf, old_file_size); + if (old_crc32 == file_info->old_crc32) + { + /* Check if patching is necessary. */ + if (!ph.patched_size) { + if (!old_file_size && ((apply_option_flags & APPLY_OPTION_FAIL_IF_CLOSE) != 0 + || (apply_option_flags & APPLY_OPTION_FAIL_IF_EXACT) != 0)) { + err = ERROR_PATCH_NOT_NECESSARY; + } + goto cleanup; + } + + if (file_info->patch_stream_size != 0) + { + /* Check if only testing is necessary. */ + if (apply_option_flags & APPLY_OPTION_TEST_ONLY) { + goto cleanup; + } + + /* Missing lzxd stream means it's a header test extract. */ + if ((file_info->patch_stream_start + file_info->patch_stream_size) > ph.end) { + err = ERROR_PATCH_NOT_AVAILABLE; + goto cleanup; + } + + if (file_info->old_size > ((ph.flags & PATCH_OPTION_USE_LZX_LARGE) + ? MAX_LARGE_WINDOW : MAX_NORMAL_WINDOW)) + { + /* interleaved by default but not the same as PATCH_OPTION_INTERLEAVE_FILES + * TODO: Add interleaved stream support. + */ + FIXME("interleaved LZXD decompression is not supported.\n"); + err = ERROR_PATCH_PACKAGE_UNSUPPORTED; + goto cleanup; + } + + /* Apply the necessary patches according to the patch transform table. */ + if (file_info->xfrm_tbl.count) { + err = perform_patches_on_old_file_image(&ph.extra_flags, old_file_buf, + file_info->old_size, ph.new_res_time, &file_info->xfrm_tbl); + } + + if (err == ERROR_SUCCESS) { + + /* Decode the compressed patch data. + * N.B. This will copy the decoded data into the new file buffer. + */ + err = decode_lzxd_stream( + file_info->patch_stream_start, file_info->patch_stream_size, + decode_buf, ph.patched_size, file_info->old_size, + ph.flags & PATCH_OPTION_USE_LZX_LARGE, + progress_fn, progress_ctx); + } + + if (err == ERROR_SUCCESS) { + + /* Check for expected patched file crc32 checksum. + * + * N.B. This must be done with the retain and ignore ranges zeroed, + * in other words the retain ranges must be applied after checking + * for the expected checksum. + */ + patched_crc32 = RtlComputeCrc32(0, &decode_buf[file_info->old_size], ph.patched_size); + if (patched_crc32 == ph.patched_crc32) { + found_file = file_info; + break; + } + } + + } else { + + /* Check if patching was not necessary. */ + if (old_file_size == ph.patched_size && + (apply_option_flags & APPLY_OPTION_FAIL_IF_CLOSE) != 0 ) { + err = ERROR_PATCH_NOT_NECESSARY; + goto cleanup; + } + + /* Chck if only testing is necessary. */ + if (apply_option_flags & APPLY_OPTION_TEST_ONLY) { + goto cleanup; + } + + /* Files should be identical, so copy old to new. */ + memcpy(new_file_buf, old_file_buf, old_file_size); + found_file = file_info; + break; + } + } + } + + if (retain_buffer) { + VirtualFree(retain_buffer, 0, MEM_RELEASE); + retain_buffer = NULL; + } + } + + /* Check if no suitable matching file was found for patching. */ + if (!found_file) { + err = ERROR_PATCH_WRONG_FILE; + goto cleanup; + } + + /* Apply the retained ranges to the new file. */ + apply_retained_ranges(new_file_buf, + found_file->retain_range_array, found_file->retain_range_count, retain_buffer); + + if (!(apply_option_flags & APPLY_OPTION_TEST_ONLY)) + { + if (in_new_file_buf == NULL) { + out_new_file_buf = decode_buf; + /* caller will VirtualFree the buffer */ + *pnew_file_buf = out_new_file_buf; + } else { + out_new_file_buf = in_new_file_buf; + } + memmove(out_new_file_buf, new_file_buf, ph.patched_size); + } + +cleanup: + if (retain_buffer) { + VirtualFree(retain_buffer, 0, MEM_RELEASE); + } + + if (decode_buf != NULL && decode_buf != *pnew_file_buf) { VirtualFree(decode_buf, 0, MEM_RELEASE); + }
-free_patch_header: free_header(&ph);
return err; }
-BOOL apply_patch_to_file_by_handles(HANDLE patch_file_hndl, HANDLE old_file_hndl, HANDLE new_file_hndl, - const ULONG apply_option_flags, - PATCH_PROGRESS_CALLBACK *progress_fn, void *progress_ctx, - const BOOL test_header_only) +BOOL apply_patch_to_file_by_handles( + HANDLE patch_file_hndl, HANDLE old_file_hndl, HANDLE new_file_hndl, + ULONG apply_option_flags, PATCH_PROGRESS_CALLBACK *progress_fn, void *progress_ctx) { LARGE_INTEGER patch_size; LARGE_INTEGER old_size; @@ -1157,7 +2469,7 @@ BOOL apply_patch_to_file_by_handles(HANDLE patch_file_hndl, HANDLE old_file_hndl DWORD err = ERROR_SUCCESS;
/* truncate the output file if required, or set the handle to invalid */ - if (test_header_only || (apply_option_flags & APPLY_OPTION_TEST_ONLY)) + if (apply_option_flags & APPLY_OPTION_TEST_ONLY) { new_file_hndl = INVALID_HANDLE_VALUE; } @@ -1218,10 +2530,8 @@ BOOL apply_patch_to_file_by_handles(HANDLE patch_file_hndl, HANDLE old_file_hndl
err = apply_patch_to_file_by_buffers(patch_buf, (ULONG)patch_size.QuadPart, old_buf, (ULONG)old_size.QuadPart, - &new_buf, 0, &new_size, - &new_time, - apply_option_flags, progress_fn, progress_ctx, - test_header_only); + &new_buf, 0, &new_size, &new_time, + apply_option_flags, progress_fn, progress_ctx);
if(err) goto free_new_buf; @@ -1261,10 +2571,9 @@ close_patch_map: return res; }
-BOOL apply_patch_to_file(LPCWSTR patch_file_name, LPCWSTR old_file_name, LPCWSTR new_file_name, - const ULONG apply_option_flags, - PATCH_PROGRESS_CALLBACK *progress_fn, void *progress_ctx, - const BOOL test_header_only) +BOOL apply_patch_to_file( + LPCWSTR patch_file_name, LPCWSTR old_file_name, LPCWSTR new_file_name, + const ULONG apply_option_flags, PATCH_PROGRESS_CALLBACK *progress_fn, void *progress_ctx) { HANDLE patch_hndl; HANDLE old_hndl = INVALID_HANDLE_VALUE; @@ -1289,7 +2598,7 @@ BOOL apply_patch_to_file(LPCWSTR patch_file_name, LPCWSTR old_file_name, LPCWSTR } }
- if (!test_header_only && !(apply_option_flags & APPLY_OPTION_TEST_ONLY)) + if (!(apply_option_flags & APPLY_OPTION_TEST_ONLY)) { new_hndl = CreateFileW(new_file_name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); if (new_hndl == INVALID_HANDLE_VALUE) @@ -1299,7 +2608,7 @@ BOOL apply_patch_to_file(LPCWSTR patch_file_name, LPCWSTR old_file_name, LPCWSTR } }
- res = apply_patch_to_file_by_handles(patch_hndl, old_hndl, new_hndl, apply_option_flags, progress_fn, progress_ctx, test_header_only); + res = apply_patch_to_file_by_handles(patch_hndl, old_hndl, new_hndl, apply_option_flags, progress_fn, progress_ctx); if(!res) err = GetLastError();
diff --git a/dlls/mspatcha/pa19.h b/dlls/mspatcha/pa19.h index e8a1fd3a51d..008b0f80bc4 100644 --- a/dlls/mspatcha/pa19.h +++ b/dlls/mspatcha/pa19.h @@ -36,15 +36,11 @@ DWORD apply_patch_to_file_by_buffers(const BYTE *patch_file_view, const ULONG pa BYTE **new_file_buf, const ULONG new_file_buf_size, ULONG *new_file_size, FILETIME *new_file_time, const ULONG apply_option_flags, - PATCH_PROGRESS_CALLBACK *progress_fn, void *progress_ctx, - const BOOL test_header_only); + PATCH_PROGRESS_CALLBACK *progress_fn, void *progress_ctx);
-BOOL apply_patch_to_file_by_handles(HANDLE patch_file_hndl, HANDLE old_file_hndl, HANDLE new_file_hndl, - const ULONG apply_option_flags, - PATCH_PROGRESS_CALLBACK *progress_fn, void *progress_ctx, - const BOOL test_header_only); +BOOL apply_patch_to_file_by_handles( + HANDLE patch_file_hndl, HANDLE old_file_hndl, HANDLE new_file_hndl, + ULONG apply_option_flags, PATCH_PROGRESS_CALLBACK *progress_fn, void *progress_ctx);
BOOL apply_patch_to_file(LPCWSTR patch_file_name, LPCWSTR old_file_name, LPCWSTR new_file_name, - const ULONG apply_option_flags, - PATCH_PROGRESS_CALLBACK *progress_fn, void *progress_ctx, - const BOOL test_header_only); + const ULONG apply_option_flags, PATCH_PROGRESS_CALLBACK *progress_fn, void *progress_ctx); diff --git a/include/patchapi.h b/include/patchapi.h index 95401545688..b2302987fc8 100644 --- a/include/patchapi.h +++ b/include/patchapi.h @@ -40,6 +40,9 @@ extern "C" { #define PATCH_OPTION_INTERLEAVE_FILES 0x40000000 #define PATCH_OPTION_RESERVED1 0x80000000
+#define PATCH_TRANSFORM_PE_RESOURCE_2 0x00000100 +#define PATCH_TRANSFORM_PE_IRELOC_2 0x00000200 + #define APPLY_OPTION_FAIL_IF_EXACT 0x00000001 #define APPLY_OPTION_FAIL_IF_CLOSE 0x00000002 #define APPLY_OPTION_TEST_ONLY 0x00000004
From: ajkhoury aidankhoury@gmail.com
--- dlls/mspatcha/tests/Makefile.in | 3 +- dlls/mspatcha/tests/signature.c | 404 ++++++++++++++++++++++++++++++++ 2 files changed, 406 insertions(+), 1 deletion(-) create mode 100644 dlls/mspatcha/tests/signature.c
diff --git a/dlls/mspatcha/tests/Makefile.in b/dlls/mspatcha/tests/Makefile.in index ef1d19271c7..c855492c1a5 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 00000000000..f2af59e38b5 --- /dev/null +++ b/dlls/mspatcha/tests/signature.c @@ -0,0 +1,404 @@ +/* + * Unit tests for Patch API functions + * + * Copyright 2023 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 BOOL (WINAPI *pGetFilePatchSignatureByBuffer)(PBYTE, ULONG, ULONG, PVOID, ULONG, + PPATCH_IGNORE_RANGE, ULONG, PPATCH_RETAIN_RANGE, ULONG, LPSTR); + +static BYTE array[1024]; + +static void init_function_pointers(void) +{ + HMODULE mspatcha = LoadLibraryA("mspatcha.dll"); + if (!mspatcha) + { + win_skip("mspatcha.dll not found\n"); + return; + } + pNormalizeFileForPatchSignature = (void *)GetProcAddress(mspatcha, "NormalizeFileForPatchSignature"); + pGetFilePatchSignatureByBuffer = (void *)GetProcAddress(mspatcha, "GetFilePatchSignatureByBuffer"); +} + +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) + { + skip("NormalizeFileForPatchSignature not found\n"); + 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) + { + skip("NormalizeFileForPatchSignature not found\n"); + 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) +{ + const static 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; + } td[] = + { + {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; + UINT i; + + if (!pNormalizeFileForPatchSignature) + { + skip("NormalizeFileForPatchSignature not found\n"); + return; + } + + for (i = 0; i < ARRAY_SIZE(td); i++) + { + winetest_push_context("%u", i); + + memset(array, 0xcc, 512); + setup_pe(array, &header); + if (td[i].init_magic_64) + header->OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR64_MAGIC; + header->FileHeader.TimeDateStamp = td[i].init_timestamp; + header->OptionalHeader.CheckSum = td[i].init_checksum; + result = pNormalizeFileForPatchSignature(array, td[i].buffer_size, td[i].flags, NULL, + td[i].image_base, td[i].timestamp, 0, NULL, 0, NULL); + + ok(result == td[i].exp_result, "Expected %d, got %d\n", td[i].exp_result, result); + ok(header->FileHeader.TimeDateStamp == td[i].exp_timestamp, + "Expected timestamp %#lx, got %#lx\n", + td[i].exp_timestamp, header->FileHeader.TimeDateStamp); + ok(header->OptionalHeader.CheckSum == td[i].exp_checksum, + "Expected checksum %#lx, got %#lx\n", + td[i].exp_checksum, header->OptionalHeader.CheckSum); + + winetest_pop_context(); + } +} + +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) + { + skip("NormalizeFileForPatchSignature not found\n"); + 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 %#lx, got %#lx\n", + image_base_new, header->OptionalHeader.ImageBase); + ok(*reloc_target == reloc_target_exp, "Expected %#lx, got %#lx\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 %#lx, got %#lx\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 %#lx, got %#lx\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 %#lx, got %#lx\n", + 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) + { + skip("NormalizeFileForPatchSignature not found\n"); + 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 CRC32 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 %#lx\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 %#lx\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 %#lx\n", + 0x10000000, header->FileHeader.TimeDateStamp); + ok(header->OptionalHeader.CheckSum == 0x9dd2, "Expected %#x, got %#lx\n", + 0x9dd2, header->OptionalHeader.CheckSum); + ok(header->OptionalHeader.ImageBase == 0x10000000, "Expected %#x, got %#lx\n", + 0x10000000, header->OptionalHeader.ImageBase); +} + +START_TEST(signature) +{ + init_function_pointers(); + + test_normalize_ignore_range(); + test_normalize_retain_range(); + test_normalize_flags(); + test_normalize_rebase(); + test_signature_by_buffer(); +}
From: ajkhoury aidankhoury@gmail.com
--- dlls/mspatcha/mspatcha_main.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-)
diff --git a/dlls/mspatcha/mspatcha_main.c b/dlls/mspatcha/mspatcha_main.c index 1d8744a85e4..9a184cf148d 100644 --- a/dlls/mspatcha/mspatcha_main.c +++ b/dlls/mspatcha/mspatcha_main.c @@ -54,14 +54,28 @@ static WCHAR *strdupAW(const char *src) return dst; }
-#define NIBBLE_CHAR(n) ((n) < 0xA) ? ('0' + (n)) : ('a' + ((n) - 0xA)) +static inline char nibble2char(unsigned char n) +{ + return (char)((n) < 0xA) ? ('0' + (n)) : ('a' + ((n) - 0xA)); +} + static inline void bin2hex(const unsigned char *bin, char *hexstr, size_t maxcount) { - for (size_t i = 0; i < maxcount; i++) { - *hexstr++ = NIBBLE_CHAR((*bin >> 4) & 0xf); - *hexstr++ = NIBBLE_CHAR(*bin++ & 0xf); + size_t i, n = 0; + for (i = 0; i < maxcount; i++) { + hexstr[n++] = nibble2char((bin[i] >> 4) & 0xf); + hexstr[n++] = nibble2char((bin[i] & 0xf)); + } + hexstr[n] = '\0'; +} + +static inline void dword2hex(unsigned int value, char *hexstr) +{ + size_t i; + for (i = 8; i > 0; --i, value >>= 4) { + hexstr[i-1] = nibble2char((value & 0xf)); } - *hexstr = '\0'; + hexstr[8] = '\0'; }
/***************************************************** @@ -403,7 +417,7 @@ BOOL WINAPI GetFilePatchSignatureByBuffer( if (signature_bufsize >= (sizeof(UINT32)*2+1)) { /* calculate CRC32 checksum of file buffer. */ filecrc = RtlComputeCrc32(0, file_buffer, file_size); - bin2hex((const unsigned char *)&filecrc, signature_buf, sizeof(UINT32)); + dword2hex(filecrc, signature_buf);
} else { SetLastError(ERROR_INSUFFICIENT_BUFFER);
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=137686
Your paranoid android.
=== build (build log) ===
error: patch failed: dlls/windows.applicationmodel/tests/application.c:61 Task: Patch failed to apply
=== debian11 (build log) ===
error: patch failed: dlls/windows.applicationmodel/tests/application.c:61 Task: Patch failed to apply
=== debian11b (build log) ===
error: patch failed: dlls/windows.applicationmodel/tests/application.c:61 Task: Patch failed to apply