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.
From: ajkhoury aidankhoury@gmail.com
This clears up much of the todo list for mspatcha. GetFilePatchSignature* and NormalizeFileForPatchSignature have now been implemented in these changes. --- dlls/mspatcha/Makefile.in | 1 + dlls/mspatcha/md5.c | 255 +++ dlls/mspatcha/md5.h | 21 + dlls/mspatcha/mspatcha_main.c | 257 +++- dlls/mspatcha/pa19.c | 2738 +++++++++++++++++++++++++++------ dlls/mspatcha/pa19.h | 15 + include/patchapi.h | 12 + 7 files changed, 2763 insertions(+), 536 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 89d6367b39d..0e8663d2a2d 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
diff --git a/dlls/mspatcha/md5.c b/dlls/mspatcha/md5.c new file mode 100644 index 00000000000..88c9b6fcfff --- /dev/null +++ b/dlls/mspatcha/md5.c @@ -0,0 +1,255 @@ + +/* MD5 algorithm + * + * This code is derived from ntdll/crypt.c + */ + +#include <string.h> +#include <basetsd.h> /* for WORDS_BIGENDIAN */ + +#include "md5.h" + +typedef struct { + unsigned int i[2]; + unsigned int buf[4]; + unsigned char in[64]; +} MD5_CTX; + +#ifndef WORDS_BIGENDIAN +#define byteReverse(buf, len) /* Nothing */ +#else +/* Note: this code is harmless on little-endian machines. */ +static void byteReverse( unsigned char *buf, unsigned int nints ) +{ + unsigned int t; + do { + t = ((unsigned)buf[3] << 8 | buf[2]) << 16 | + ((unsigned)buf[1] << 8 | buf[0]); + *(unsigned int *)buf = t; + buf += 4; + } while (--nints); +} +#endif + +/* #define F1( x, y, z ) (x & y | ~x & z) */ +#define F1( x, y, z ) (z ^ (x & (y ^ z))) +#define F2( x, y, z ) F1( z, x, y ) +#define F3( x, y, z ) (x ^ y ^ z) +#define F4( x, y, z ) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP( f, w, x, y, z, data, s ) \ + ( w += f( x, y, z ) + data, w = w << s | w >> (32 - s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5_Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void MD5Transform( unsigned int buf[4], const unsigned int in[16] ) +{ + unsigned int a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP( F1, a, b, c, d, in[0] + 0xd76aa478, 7 ); + MD5STEP( F1, d, a, b, c, in[1] + 0xe8c7b756, 12 ); + MD5STEP( F1, c, d, a, b, in[2] + 0x242070db, 17 ); + MD5STEP( F1, b, c, d, a, in[3] + 0xc1bdceee, 22 ); + MD5STEP( F1, a, b, c, d, in[4] + 0xf57c0faf, 7 ); + MD5STEP( F1, d, a, b, c, in[5] + 0x4787c62a, 12 ); + MD5STEP( F1, c, d, a, b, in[6] + 0xa8304613, 17 ); + MD5STEP( F1, b, c, d, a, in[7] + 0xfd469501, 22 ); + MD5STEP( F1, a, b, c, d, in[8] + 0x698098d8, 7 ); + MD5STEP( F1, d, a, b, c, in[9] + 0x8b44f7af, 12 ); + MD5STEP( F1, c, d, a, b, in[10] + 0xffff5bb1, 17 ); + MD5STEP( F1, b, c, d, a, in[11] + 0x895cd7be, 22 ); + MD5STEP( F1, a, b, c, d, in[12] + 0x6b901122, 7 ); + MD5STEP( F1, d, a, b, c, in[13] + 0xfd987193, 12 ); + MD5STEP( F1, c, d, a, b, in[14] + 0xa679438e, 17 ); + MD5STEP( F1, b, c, d, a, in[15] + 0x49b40821, 22 ); + + MD5STEP( F2, a, b, c, d, in[1] + 0xf61e2562, 5 ); + MD5STEP( F2, d, a, b, c, in[6] + 0xc040b340, 9 ); + MD5STEP( F2, c, d, a, b, in[11] + 0x265e5a51, 14 ); + MD5STEP( F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20 ); + MD5STEP( F2, a, b, c, d, in[5] + 0xd62f105d, 5 ); + MD5STEP( F2, d, a, b, c, in[10] + 0x02441453, 9 ); + MD5STEP( F2, c, d, a, b, in[15] + 0xd8a1e681, 14 ); + MD5STEP( F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20 ); + MD5STEP( F2, a, b, c, d, in[9] + 0x21e1cde6, 5 ); + MD5STEP( F2, d, a, b, c, in[14] + 0xc33707d6, 9 ); + MD5STEP( F2, c, d, a, b, in[3] + 0xf4d50d87, 14 ); + MD5STEP( F2, b, c, d, a, in[8] + 0x455a14ed, 20 ); + MD5STEP( F2, a, b, c, d, in[13] + 0xa9e3e905, 5 ); + MD5STEP( F2, d, a, b, c, in[2] + 0xfcefa3f8, 9 ); + MD5STEP( F2, c, d, a, b, in[7] + 0x676f02d9, 14 ); + MD5STEP( F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20 ); + + MD5STEP( F3, a, b, c, d, in[5] + 0xfffa3942, 4 ); + MD5STEP( F3, d, a, b, c, in[8] + 0x8771f681, 11 ); + MD5STEP( F3, c, d, a, b, in[11] + 0x6d9d6122, 16 ); + MD5STEP( F3, b, c, d, a, in[14] + 0xfde5380c, 23 ); + MD5STEP( F3, a, b, c, d, in[1] + 0xa4beea44, 4 ); + MD5STEP( F3, d, a, b, c, in[4] + 0x4bdecfa9, 11 ); + MD5STEP( F3, c, d, a, b, in[7] + 0xf6bb4b60, 16 ); + MD5STEP( F3, b, c, d, a, in[10] + 0xbebfbc70, 23 ); + MD5STEP( F3, a, b, c, d, in[13] + 0x289b7ec6, 4 ); + MD5STEP( F3, d, a, b, c, in[0] + 0xeaa127fa, 11 ); + MD5STEP( F3, c, d, a, b, in[3] + 0xd4ef3085, 16 ); + MD5STEP( F3, b, c, d, a, in[6] + 0x04881d05, 23 ); + MD5STEP( F3, a, b, c, d, in[9] + 0xd9d4d039, 4 ); + MD5STEP( F3, d, a, b, c, in[12] + 0xe6db99e5, 11 ); + MD5STEP( F3, c, d, a, b, in[15] + 0x1fa27cf8, 16 ); + MD5STEP( F3, b, c, d, a, in[2] + 0xc4ac5665, 23 ); + + MD5STEP( F4, a, b, c, d, in[0] + 0xf4292244, 6 ); + MD5STEP( F4, d, a, b, c, in[7] + 0x432aff97, 10 ); + MD5STEP( F4, c, d, a, b, in[14] + 0xab9423a7, 15 ); + MD5STEP( F4, b, c, d, a, in[5] + 0xfc93a039, 21 ); + MD5STEP( F4, a, b, c, d, in[12] + 0x655b59c3, 6 ); + MD5STEP( F4, d, a, b, c, in[3] + 0x8f0ccc92, 10 ); + MD5STEP( F4, c, d, a, b, in[10] + 0xffeff47d, 15 ); + MD5STEP( F4, b, c, d, a, in[1] + 0x85845dd1, 21 ); + MD5STEP( F4, a, b, c, d, in[8] + 0x6fa87e4f, 6 ); + MD5STEP( F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10 ); + MD5STEP( F4, c, d, a, b, in[6] + 0xa3014314, 15 ); + MD5STEP( F4, b, c, d, a, in[13] + 0x4e0811a1, 21 ); + MD5STEP( F4, a, b, c, d, in[4] + 0xf7537e82, 6 ); + MD5STEP( F4, d, a, b, c, in[11] + 0xbd3af235, 10 ); + MD5STEP( F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15 ); + MD5STEP( F4, b, c, d, a, in[9] + 0xeb86d391, 21 ); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +/****************************************************************************** + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +static void MD5_Initialize( MD5_CTX *ctx ) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->i[0] = ctx->i[1] = 0; +} + +/****************************************************************************** + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +static void MD5_Update( MD5_CTX *ctx, const unsigned char *buf, unsigned int len ) +{ + unsigned int t; + + /* Update bitcount */ + t = ctx->i[0]; + + if ((ctx->i[0] = t + (len << 3)) < t) + ctx->i[1]++; /* Carry from low to high */ + + ctx->i[1] += len >> 29; + t = (t >> 3) & 0x3f; + + /* Handle any leading odd-sized chunks */ + if (t) + { + unsigned char *p = (unsigned char *)ctx->in + t; + t = 64 - t; + + if (len < t) + { + memcpy( p, buf, len ); + return; + } + + memcpy( p, buf, t ); + byteReverse( ctx->in, 16 ); + + MD5Transform( ctx->buf, (unsigned int *)ctx->in ); + + buf += t; + len -= t; + } + + /* Process data in 64-byte chunks */ + while (len >= 64) + { + memcpy( ctx->in, buf, 64 ); + byteReverse( ctx->in, 16 ); + + MD5Transform( ctx->buf, (unsigned int *)ctx->in ); + + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + memcpy( ctx->in, buf, len ); +} + +/****************************************************************************** + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +static void MD5_Finalize( MD5_CTX *ctx, unsigned char digest[MD5DIGESTLEN] ) +{ + unsigned int count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->i[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) + { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset( p, 0, count ); + byteReverse( ctx->in, 16 ); + MD5Transform( ctx->buf, (unsigned int *)ctx->in ); + + /* Now fill the next block with 56 bytes */ + memset( ctx->in, 0, 56 ); + } + else + { + /* Pad block to 56 bytes */ + memset( p, 0, count - 8 ); + } + + byteReverse( ctx->in, 14 ); + + /* Append length in bits and transform */ + ((unsigned int *)ctx->in)[14] = ctx->i[0]; + ((unsigned int *)ctx->in)[15] = ctx->i[1]; + + MD5Transform( ctx->buf, (unsigned int *)ctx->in ); + byteReverse( (unsigned char *)ctx->buf, 4 ); + memcpy( digest, ctx->buf, 16 ); +} + +void ComputeMD5Hash(const void *data, unsigned int len, unsigned char digest[MD5DIGESTLEN]) +{ + MD5_CTX ctx; + + MD5_Initialize(&ctx); + MD5_Update(&ctx, data, len); + MD5_Finalize(&ctx, digest); +} 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 1f6653da161..a0a9c90dfe1 100644 --- a/dlls/mspatcha/mspatcha_main.c +++ b/dlls/mspatcha/mspatcha_main.c @@ -3,6 +3,7 @@ * * Copyright 2011 David Hedberg for CodeWeavers * Copyright 2019 Conor McCarthy (implementations) + * 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 @@ -19,14 +20,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * TODO - * - Special processing of 32-bit executables is not supported, so this - * version cannot patch 32-bit .exe and .dll files. See pa19.c for details. * - Implement interleaved decoding when PATCH_OPTION_INTERLEAVE_FILES was * 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,10 +29,12 @@ #include "windef.h" #include "winbase.h" #include "winnls.h" +#include "winternl.h" #include "patchapi.h" #include "wine/debug.h"
#include "pa19.h" +#include "md5.h"
WINE_DEFAULT_DEBUG_CHANNEL(mspatcha);
@@ -205,68 +202,236 @@ BOOL WINAPI ApplyPatchToFileByBuffers(PBYTE patch_file_view, ULONG patch_file_s return err == ERROR_SUCCESS; }
+ /***************************************************** - * GetFilePatchSignatureA (MSPATCHA.@) + * NormalizeFileForPatchSignature (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) +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 - %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; + 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; +} + +#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'; }
/***************************************************** - * GetFilePatchSignatureW (MSPATCHA.@) + * GetFilePatchSignatureByBuffer (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 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 - %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; + 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; }
/***************************************************** * 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_table, + ULONG retain_range_count, PPATCH_RETAIN_RANGE retain_range_table, + 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_table, + retain_range_count, retain_range_table, + 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.@) + * GetFilePatchSignatureA (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 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 - %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 = 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; }
/***************************************************** - * NormalizeFileForPatchSignature (MSPATCHA.@) + * GetFilePatchSignatureW (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) +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 - %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; + 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; } diff --git a/dlls/mspatcha/pa19.c b/dlls/mspatcha/pa19.c index dc6ef79e2b2..ec4af0d5282 100644 --- a/dlls/mspatcha/pa19.c +++ b/dlls/mspatcha/pa19.c @@ -2,6 +2,7 @@ * PatchAPI PA19 file handlers * * Copyright 2019 Conor McCarthy + * 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 @@ -18,18 +19,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> @@ -48,19 +39,8 @@ 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 +#define PATCH_EXTRA_HAS_PATCH_TRANSFORMS 0x20000000
/*********************************************************************************** * PatchAPI PA19 file header @@ -72,7 +52,7 @@ static UINT32 compute_zero_crc32(UINT32 crc, INT_PTR len) * UVLI rebase; (present if PATCH_OPTION_NO_REBASE is not set; used on 32-bit executables) * UVLI unpatched_size; * UINT32 crc32_patched; - * BYTE input_file_count; + * BYTE old_file_count; * * For each source file: * SVLI (patched_size - unpatched_size); @@ -98,36 +78,55 @@ 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; +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 */ + + /* private file info fields */ + const BYTE *patch_stream_start; int next_i; int next_r; };
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; /* 0x2C */ + ULONG compression_window_size; /* 0x30 */ + + /* decode header state 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 ( \ @@ -142,125 +141,1667 @@ struct patch_file_header { | PATCH_OPTION_NO_CHECKSUM \ | PATCH_OPTION_NO_RESTIMEFIX \ | PATCH_OPTION_NO_TIMESTAMP \ + | PATCH_OPTION_INTERLEAVE_FILES \ | PATCH_OPTION_EXTRA_FLAGS)
-/* read a byte-aligned little-endian UINT32 from input and set error if eof - */ -static inline UINT32 read_raw_uint32(struct patch_file_header *ph) +static void DECLSPEC_NORETURN throw_pe_fmt_exception(void) { - const BYTE *src = ph->src; + RaiseException(0xE0000001, 0, 0, NULL); + for (;;) { /* silence compiler warning */ } +}
- ph->src += 4; - if (ph->src > ph->end) - { - ph->err = ERROR_PATCH_CORRUPT; +static IMAGE_NT_HEADERS32 UNALIGNED *get_nt_header(const void *image_base, size_t image_size) +{ + IMAGE_DOS_HEADER UNALIGNED *dos_hdr; + IMAGE_NT_HEADERS32 UNALIGNED *nt_headers; + const ULONG_PTR image_end = (ULONG_PTR)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 (((ULONG_PTR)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; +} + +static ULONG image_rva_to_file_offset( + IMAGE_NT_HEADERS32 UNALIGNED *nt_header, ULONG rva, PUCHAR image_base, ULONG image_size) +{ + IMAGE_SECTION_HEADER UNALIGNED *section_table; + ULONG section_count; + ULONG i; + + if ( rva < nt_header->OptionalHeader.SizeOfHeaders ) { + return rva; + } + + section_table = IMAGE_FIRST_SECTION(nt_header); + section_count = nt_header->FileHeader.NumberOfSections; + for (i = 0; i < section_count; i++) { + if ((PUCHAR)§ion_table[i] < image_base + || (PUCHAR)§ion_table[i+1] > &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; +} + +static 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 *data_directory; + + if (directory_entry >= nt_header->OptionalHeader.NumberOfRvaAndSizes) { return 0; } - return src[0] - | (src[1] << 8) - | (src[2] << 16) - | (src[3] << 24); + + data_directory = &nt_header->OptionalHeader.DataDirectory[directory_entry]; + if ((PUCHAR)data_directory < image_base + || (PUCHAR)&data_directory[1] > &image_base[image_size]) { + throw_pe_fmt_exception(); + } + + if (directory_size != NULL) + *directory_size = data_directory->Size; + return data_directory->VirtualAddress; }
-/* Read a variable-length integer from a sequence of bytes terminated by - * a value with bit 7 set. Set error if invalid or eof */ -static UINT64 read_uvli(struct patch_file_header *ph) +static ULONG image_directory_offset_and_size( + IMAGE_NT_HEADERS32 UNALIGNED *nt_header, USHORT directory_entry, ULONG *directory_size, + PUCHAR mapped_image, ULONG image_size) { - const BYTE *vli = ph->src; - UINT64 n; - ptrdiff_t i; - ptrdiff_t limit = min(ph->end - vli, 9); + ULONG rva, offset = 0; + rva = image_directory_rva_and_size(nt_header, directory_entry, directory_size, mapped_image, image_size); + if (rva) { + offset = image_rva_to_file_offset(nt_header, rva, mapped_image, image_size); + } + return offset; +}
- if (ph->src >= ph->end) - { - ph->err = ERROR_PATCH_CORRUPT; +static PVOID image_rva_to_mapped_address( + IMAGE_NT_HEADERS32 UNALIGNED *nt_headers, ULONG rva, + PVOID mapped_image, ULONG image_size) +{ + const ULONG offset = image_rva_to_file_offset(nt_headers, rva, mapped_image, image_size); + if (offset && offset < image_size) { + return (PVOID)((PUCHAR)mapped_image + offset); + } + return NULL; +} + +static PVOID image_directory_mapped_address( + IMAGE_NT_HEADERS32 UNALIGNED *nt_headers, USHORT directory_entry, + ULONG *directory_size, PUCHAR mapped_image, ULONG image_size) +{ + ULONG dir_rva; + ULONG dir_size; + ULONG dir_offset; + PUCHAR mapped_address; + + dir_rva = image_directory_rva_and_size(nt_headers, directory_entry, &dir_size, mapped_image, image_size); + if (!dir_rva) { + return NULL; + } + + dir_offset = image_rva_to_file_offset(nt_headers, dir_rva, mapped_image, image_size); + if (!dir_offset || dir_offset >= image_size) { + return NULL; + } + + mapped_address = &mapped_image[dir_offset]; + + if (&mapped_address[dir_size] < mapped_address) { + throw_pe_fmt_exception(); + } + + if (&mapped_address[dir_size] > &mapped_image[image_size]) { + return NULL; + } + + if (directory_size != NULL) { + *directory_size = dir_size; + } + + return (PVOID)mapped_address; +} + +static void patch_transform_PE_mark_non_executable( + IMAGE_NT_HEADERS32 UNALIGNED *nt_header, PUCHAR mapped_image, ULONG image_size, + BYTE *hintmap, BOOL force) +{ + IMAGE_SECTION_HEADER UNALIGNED *section_table; + ULONG section_size_raw; + ULONG directory_offset; + ULONG directory_rva; + ULONG directory_size; + ULONG i; + + /* 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); + } + } + } +} + +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 (!low) { + return old_rva; + } + + mid = low - 1; + return old_rva + (LONG)(entries[mid].new_rva - entries[mid].old_rva); +} + +struct reloc_entry { + ULONG rva; + UCHAR type; + USHORT hiadj; +}; + +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; +} + +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; + + 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); + + /* 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) + { + if (nt_headers->FileHeader.Machine == IMAGE_FILE_MACHINE_I386) + { + 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; + } + + /* advance by 3 so next loop we advance to the next dword */ + p += 4-1; + } + } + } + + return; + } + + /* update the hint map with the base reloc directory */ + memset(&hintmap[reloc_dir_offset], 0x03, reloc_dir_size); + + /* 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; + } + + /* 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 ((reloc_block->SizeOfBlock <= (ULONG)reloc_dir_remaining) && + (reloc_block->SizeOfBlock > sizeof(IMAGE_BASE_RELOCATION))) { + + 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) { + + 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); }
- n = vli[0] & 0x7F; - for (i = 1; i < limit && vli[i - 1] < 0x80; ++i) - n += (UINT64)(vli[i] & 0x7F) << (7 * i); + reloc_dir_remaining = (LONG)reloc_dir_size;
- if (vli[i - 1] < 0x80) + /* 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) { - TRACE("exceeded maximum vli size\n"); - ph->err = ERROR_PATCH_CORRUPT; - return 0; + 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) + { + 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); }
- ph->src += i; + 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;
- return n; + /* advance by 3 so next loop we advance to the next dword */ + p += 4-1; + } + } + } }
-/* Signed variant of the above. First byte sign flag is 0x40. - */ -static INT64 read_svli(struct patch_file_header *ph) +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; + } + } + } + } +} + +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. */ +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 = get_nt_header(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; +} + +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; +} + +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; +} + +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 */ +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 (LOWORD(sum) + HIWORD(sum)) + ((DWORD)(LOWORD(sum) + HIWORD(sum)) >> 16); +} + +/* Performs changes to normalize the old PE image */ +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); + + if (old_file_buffer && old_file_size) + { + nt_headers = get_nt_header(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; +} + +/* read a byte-aligned little-endian UINT16 from input and set error if eof */ +static inline UINT16 read_raw_uint16(struct patch_file_header *ph) { - const BYTE *vli = ph->src; - INT64 n; - ptrdiff_t i; - ptrdiff_t limit = min(ph->end - vli, 9); + const BYTE *src;
- if (ph->src >= ph->end) + if ((ph->src + sizeof(UINT16)) > ph->end) { ph->err = ERROR_PATCH_CORRUPT; return 0; }
- n = vli[0] & 0x3F; - for (i = 1; i < limit && vli[i - 1] < 0x80; ++i) - n += (INT64)(vli[i] & 0x7F) << (7 * i - 1); + src = ph->src; + ph->src += sizeof(UINT16); + + return src[0] | (src[1] << 8); +}
- if (vli[i - 1] < 0x80) +/* read a byte-aligned little-endian UINT32 from input and set error if eof */ +static inline UINT32 read_raw_uint32(struct patch_file_header *ph) +{ + const BYTE *src; + + if ((ph->src + sizeof(UINT32)) > ph->end) { - TRACE("exceeded maximum vli size\n"); ph->err = ERROR_PATCH_CORRUPT; return 0; }
- if (vli[0] & 0x40) - n = -n; + src = ph->src; + ph->src += sizeof(UINT32);
- ph->src += i; + return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); +}
- return n; +/* Read a variable-length integer from a sequence of bytes terminated by + * a value with bit 7 set. Set error if invalid or eof */ +static ULONG read_uvli(struct patch_file_header *ph) +{ + const BYTE *p = ph->src; + ULONG shift = 0; + ULONG value = 0; + do { + value |= ((*p & 0x7F) << shift); + shift += 7; + } while ((*p++ & 0x80) == 0 && (shift < 32)); + ph->src = p; + return value; }
-static int __cdecl compare_ignored_range(const void *a, const void *b) +/* Signed variant of the above. First byte sign flag is 0x40. + */ +static LONG read_svli(struct patch_file_header *ph) { - 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 BYTE *p = ph->src; + ULONG shift = 6; + LONG value = *p & 0x3F; + if ((*p++ & 0x80) == 0) { + do { + value |= ((*p & 0x7F) << shift); + shift += 7; + } while ((*p++ & 0x80) == 0 && (shift < 32)); + } + if (*ph->src & 0x40) + value = -value; + ph->src = p; + return value; }
-static int __cdecl compare_retained_range_old(const void *a, const void *b) +static int __cdecl compare_ignored_range(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; + const LONG diff = ((PATCH_IGNORE_RANGE*)a)->OffsetInOldFile + - ((PATCH_IGNORE_RANGE*)b)->OffsetInOldFile; + if (diff > 0) return 1; + else if (diff < 0) return -1; + else return 0; }
-static int __cdecl compare_retained_range_new(const void *a, const void *b) +static int __cdecl compare_retained_range_old(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 diff = ((PATCH_RETAIN_RANGE*)a)->OffsetInOldFile + - ((PATCH_RETAIN_RANGE*)b)->OffsetInOldFile; + if (diff > 0) return 1; + else if (diff < 0) return -1; + else return 0; }
-static int read_header(struct patch_file_header *ph, const BYTE *buf, size_t size) +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_ranges;
ph->src = buf; ph->end = buf + size; @@ -268,49 +1809,91 @@ 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); - if ((ph->flags & PATCH_OPTION_SUPPORTED_FLAGS) != ph->flags) + + /* the meaning of PATCH_OPTION_NO_TIMESTAMP is inverted for decoding */ + ph->flags ^= PATCH_OPTION_NO_TIMESTAMP; + + /* validate the decoded flags */ + if ((ph->flags & ~PATCH_OPTION_SUPPORTED_FLAGS) != 0) { FIXME("unsupported option flag(s): 0x%08lx\n", ph->flags & ~PATCH_OPTION_SUPPORTED_FLAGS); ph->err = ERROR_PATCH_PACKAGE_UNSUPPORTED; 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); + WARN("extra flag field is set!\n"); + ph->extra_flags = read_raw_uint32(ph); }
- /* 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); + }
- /* 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_REBASE) == 0) { - TRACE("skipping rebase field\n"); - (void)read_uvli(ph); + 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); + } + } + + if ((ph->flags & PATCH_OPTION_NO_RESTIMEFIX) == 0) + { + 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; + 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 %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) + 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; @@ -319,112 +1902,225 @@ static int read_header(struct patch_file_header *ph, const BYTE *buf, size_t siz 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 (ph->flags & PATCH_OPTION_INTERLEAVE_FILES) + { + ph->interleave_map = heap_calloc(ph->old_file_count, sizeof(PPATCH_INTERLEAVE_MAP)); + if (ph->interleave_map == NULL) + { + ph->err = ERROR_OUTOFMEMORY; + return -1; + } + } + + for (fileno = 0; fileno < ph->old_file_count; ++fileno) { + fi = &ph->file_table[fileno];
- delta = (ptrdiff_t)read_svli(ph); - fi->input_size = ph->patched_size + delta; + 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) + fi->ignore_range_array = heap_calloc(fi->ignore_range_count, sizeof(PATCH_IGNORE_RANGE)); + if (fi->ignore_range_array == NULL) { - ir->OffsetInOldFile += fi->ignore_table[i - 1].OffsetInOldFile - + fi->ignore_table[i - 1].LengthInBytes; + 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 entries */ + 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; + WARN("%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; + } } - fi->stream_size = (size_t)read_uvli(ph); + + /* decode interleave info */ + if (ph->flags & PATCH_OPTION_INTERLEAVE_FILES) + { + interleave_count = (ULONG)read_uvli(ph); + if (interleave_count) + { + interleave_ranges = (PATCH_INTERLEAVE_MAP *)heap_alloc( + sizeof(((PATCH_INTERLEAVE_MAP *)0)->Range) * (interleave_count - 1) + + sizeof(PATCH_INTERLEAVE_MAP)); + + if (interleave_ranges == NULL) + { + ph->err = ERROR_OUTOFMEMORY; + return -1; + } + + interleave_ranges->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_ranges->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_ranges->Range[i].OldOffset = next_old_offset; + interleave_ranges->Range[i].OldLength = next_old_length; + + if (interleave_ranges->Range[i].OldOffset > fi->old_size + || (interleave_ranges->Range[i].OldOffset + + interleave_ranges->Range[i].OldLength) > fi->old_size) + { + ph->err = ERROR_PATCH_CORRUPT; + return -1; + } + + if (i >= (interleave_ranges->CountRanges - 1)) + { + interleave_ranges->Range[i].NewLength = remaining_length; + } + else + { + delta_length = (LONG)read_svli(ph) << 15; + new_length += delta_length; + interleave_ranges->Range[i].NewLength = new_length; + remaining_length -= new_length; + } + } + + ph->interleave_map[fileno] = interleave_ranges; + } + } + + fi->patch_stream_size = (size_t)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) @@ -438,7 +2134,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) { + for (i = 0; i < ph->old_file_count; i++) { + if (ph->interleave_map[i]) { + heap_free(ph->interleave_map[i]); + ph->interleave_map[i] = NULL; + } + } + + heap_free(ph->interleave_map); + ph->interleave_map = 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 @@ -451,348 +2175,389 @@ 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) +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; - *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 - */ -static ULONG next_zeroed_range(struct input_file_info *fi, size_t index, ULONG old_file_size, ULONG *end) -{ - 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); - - if (start_i < start_r) - { - start = start_i; - *end = end_i; - ++fi->next_i; - } - else - { - start = start_r; - *end = end_r; - ++fi->next_r; + PUCHAR buffer, ptr; + ULONG i; + ULONG offset_in_file; + ULONG allocation_size; + + 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 */ -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; - - qsort(fi->retain_table, fi->retain_range_count, sizeof(fi->retain_table[0]), compare_retained_range_new); - fi->next_r = 0; + memcpy(new_file_buf + retain_range_array[i].OffsetInNewFile, + retain_buffer, + retain_range_array[i].LengthInBytes);
- 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; + retain_buffer += retain_range_array[i].LengthInBytes; } - return crc32; }
-DWORD apply_patch_to_file_by_buffers(const BYTE *patch_file_view, const ULONG patch_file_size, +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, + 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, const BOOL test_header_only) { DWORD err = ERROR_SUCCESS; - struct input_file_info *file_info; - struct patch_file_header ph; - size_t buf_size; + 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; + ULONG retain_range_count; + PATCH_RETAIN_RANGE *retain_range_array;
- 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; + if (new_file_actual_size != NULL) { + *new_file_actual_size = 0; }
- if (old_file_view == NULL) - old_file_size = 0; - - if (read_header(&ph, patch_file_view, patch_file_size)) - { - err = ph.err; - goto free_patch_header; + if (new_file_time != NULL) { + new_file_time->dwLowDateTime = 0; + new_file_time->dwHighDateTime = 0; }
- if (new_file_size != NULL) - *new_file_size = (ULONG)ph.patched_size; + if (pnew_file_buf == NULL) { + if (!test_header_only && !(apply_option_flags & APPLY_OPTION_TEST_ONLY)) { + return ERROR_INVALID_PARAMETER; + }
- if (new_file_buf != NULL && new_file_buf_size < ph.patched_size) - { - err = ERROR_INSUFFICIENT_BUFFER; - goto free_patch_header; + } else { + in_new_file_buf = *pnew_file_buf; }
- 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 (old_file_view == NULL) { + old_file_size = 0; } - if (file_info->stream_size == 0 && (apply_option_flags & APPLY_OPTION_FAIL_IF_EXACT)) - { - err = ERROR_PATCH_NOT_NECESSARY; - goto free_patch_header; + + /* read the patch file header */ + if (read_patch_header(&ph, patch_file_view, patch_file_size)) { + err = ph.err; + goto cleanup; } - 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; + + 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 (test_header_only) - goto free_patch_header; + if (new_file_actual_size != NULL) { + *new_file_actual_size = (ULONG)ph.patched_size; + }
- /* 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; + if (in_new_file_buf != NULL && new_file_buf_size < ph.patched_size) { + err = ERROR_INSUFFICIENT_BUFFER; + goto cleanup; }
- buf_size = old_file_size + ph.patched_size; - decode_buf = new_file_buf; - if (new_file_buf == NULL || new_file_buf_size < buf_size) + 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) - { + if (decode_buf == NULL) { err = GetLastError(); - goto free_patch_header; + goto cleanup; } + new_file_buf = &decode_buf[old_file_size]; }
- if (old_file_view != NULL) - memcpy(decode_buf, old_file_view, file_info->input_size); + if (old_file_view != NULL) { + old_file_buf = decode_buf; + memcpy(old_file_buf, old_file_view, old_file_size); + }
- zero_fill_ignored_ranges(decode_buf, file_info); - zero_fill_retained_ranges(decode_buf, decode_buf + file_info->input_size, file_info); + patched = FALSE; + retain_buffer = NULL; + found_file = NULL;
- 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 + /* Check if the old file matches the new file */ + if (old_file_size == ph.patched_size) { - err = ERROR_PATCH_CORRUPT; - goto free_decode_buf; + file_info = &ph.file_table[0]; + retain_range_array = file_info->retain_range_array; + retain_range_count = file_info->retain_range_count; + if (retain_range_count) + { + retain_buffer = save_retained_ranges(old_file_buf, old_file_size, + retain_range_array, retain_range_count, TRUE); + if (!retain_buffer) { + err = GetLastError(); + goto cleanup; + } + + for (i = 0; i < retain_range_count; i++) + { + if (retain_range_array[i].OffsetInNewFile <= old_file_size + && (retain_range_array[i].OffsetInNewFile + retain_range_array[i].LengthInBytes) <= old_file_size) { + memset(&old_file_buf[retain_range_array[i].OffsetInNewFile], 0, retain_range_array[i].LengthInBytes); + patched = TRUE; + } + } + } + + old_crc32 = RtlComputeCrc32(0, old_file_buf, old_file_size); + normalize_result = NORMALIZE_RESULT_FAILURE; + if (old_crc32 != ph.patched_crc32) { + 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); + } + + if (old_crc32 == ph.patched_crc32) { + + /* check if patching is necessary. */ + if ((apply_option_flags & APPLY_OPTION_FAIL_IF_EXACT) == 0 + && ((apply_option_flags & APPLY_OPTION_FAIL_IF_CLOSE) == 0 + || normalize_result >= NORMALIZE_RESULT_SUCCESS_MODIFIED)) + { + 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); + apply_retained_ranges(new_file_buf, retain_range_array, retain_range_count, retain_buffer); + goto done; + } + + } else { + err = ERROR_PATCH_NOT_NECESSARY; + } + + goto cleanup; + } + + if (retain_buffer) { + VirtualFree(retain_buffer, 0, MEM_RELEASE); + retain_buffer = NULL; + } }
- if(err != ERROR_SUCCESS) + /* 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) { - if (err == ERROR_PATCH_DECODE_FAILURE) - FIXME("decode failure: data corruption or bug.\n"); - goto free_decode_buf; - } + file_info = &ph.file_table[fileno];
- apply_retained_ranges(old_file_view, decode_buf + file_info->input_size, file_info); + if (file_info->old_size == old_file_size) + { + if (patched) { + memcpy(old_file_buf, old_file_view, old_file_size); + patched = FALSE; + }
- 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 (file_info->retain_range_count && (apply_option_flags & APPLY_OPTION_TEST_ONLY) == 0) + { + 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 | APPLY_OPTION_FAIL_IF_EXACT)) != 0) { + err = ERROR_PATCH_NOT_NECESSARY; + } + goto cleanup; + } + + if (file_info->patch_stream_size != 0) + { + /* 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 */ + FIXME("interleaved LZXD decompression is not supported.\n"); + err = ERROR_PATCH_PACKAGE_UNSUPPORTED; + goto cleanup; + } + + /* apply the necessary patches */ + 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); + } + + /* decode the compressed patch data */ + if (err == ERROR_SUCCESS) { + 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) + { + 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; + } + + /* 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; + } }
- /* 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; + /* 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); + +done: if (!(apply_option_flags & APPLY_OPTION_TEST_ONLY)) { - if (new_file_buf == NULL) - { + if (in_new_file_buf == NULL) { + out_new_file_buf = decode_buf; /* caller will VirtualFree the buffer */ - new_file_buf = decode_buf; - *pnew_file_buf = new_file_buf; + *pnew_file_buf = out_new_file_buf; + } else { + out_new_file_buf = in_new_file_buf; } - memmove(new_file_buf, decode_buf + old_file_size, ph.patched_size); + memmove(out_new_file_buf, new_file_buf, ph.patched_size); }
- if (new_file_time != NULL) - { - new_file_time->dwLowDateTime = 0; - new_file_time->dwHighDateTime = 0; - - /* 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); +cleanup: + if (retain_buffer) { + VirtualFree(retain_buffer, 0, MEM_RELEASE); }
-free_decode_buf: - if(decode_buf != NULL && decode_buf != new_file_buf) + 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, +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) @@ -805,13 +2570,12 @@ BOOL apply_patch_to_file_by_handles(HANDLE patch_file_hndl, HANDLE old_file_hndl const BYTE *old_buf = NULL; BYTE *new_buf = NULL; ULONG new_size; - FILETIME new_time; + FILETIME new_file_time; BOOL res = FALSE; 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 (test_header_only || (apply_option_flags & APPLY_OPTION_TEST_ONLY)) { new_file_hndl = INVALID_HANDLE_VALUE; } else if (SetFilePointer(new_file_hndl, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER @@ -821,8 +2585,7 @@ BOOL apply_patch_to_file_by_handles(HANDLE patch_file_hndl, HANDLE old_file_hndl return FALSE; }
- if (patch_file_hndl == INVALID_HANDLE_VALUE) - { + if (patch_file_hndl == INVALID_HANDLE_VALUE) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } @@ -836,8 +2599,7 @@ BOOL apply_patch_to_file_by_handles(HANDLE patch_file_hndl, HANDLE old_file_hndl }
patch_map = CreateFileMappingW(patch_file_hndl, NULL, PAGE_READONLY, 0, 0, NULL); - if (patch_map == NULL) - { + if (patch_map == NULL) { /* Last error set by API */ return FALSE; } @@ -845,16 +2607,14 @@ BOOL apply_patch_to_file_by_handles(HANDLE patch_file_hndl, HANDLE old_file_hndl if (old_file_hndl != INVALID_HANDLE_VALUE) { old_map = CreateFileMappingW(old_file_hndl, NULL, PAGE_READONLY, 0, 0, NULL); - if (old_map == NULL) - { + if (old_map == NULL) { err = GetLastError(); goto close_patch_map; } }
patch_buf = MapViewOfFile(patch_map, FILE_MAP_READ, 0, 0, (SIZE_T)patch_size.QuadPart); - if (patch_buf == NULL) - { + if (patch_buf == NULL) { err = GetLastError(); goto close_old_map; } @@ -862,34 +2622,32 @@ BOOL apply_patch_to_file_by_handles(HANDLE patch_file_hndl, HANDLE old_file_hndl if (old_size.QuadPart) { old_buf = MapViewOfFile(old_map, FILE_MAP_READ, 0, 0, (SIZE_T)old_size.QuadPart); - if (old_buf == NULL) - { + if (old_buf == NULL) { err = GetLastError(); goto unmap_patch_buf; } }
- 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); + 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_file_time, + apply_option_flags, progress_fn, progress_ctx, + test_header_only);
- if(err) + if (err) { goto free_new_buf; + }
res = TRUE;
- if(new_file_hndl != INVALID_HANDLE_VALUE) - { - DWORD Written = 0; - res = WriteFile(new_file_hndl, new_buf, new_size, &Written, NULL); - + if (new_file_hndl != INVALID_HANDLE_VALUE) { + DWORD bytes_written = 0; + res = WriteFile(new_file_hndl, new_buf, new_size, &bytes_written, NULL); if (!res) err = GetLastError(); - else if (new_time.dwLowDateTime || new_time.dwHighDateTime) - SetFileTime(new_file_hndl, &new_time, NULL, &new_time); + else if (new_file_time.dwLowDateTime || new_file_time.dwHighDateTime) + SetFileTime(new_file_hndl, &new_file_time, NULL, &new_file_time); }
free_new_buf: diff --git a/dlls/mspatcha/pa19.h b/dlls/mspatcha/pa19.h index 0b368849267..199c06b320f 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_table, ULONG ignore_range_count, + const PATCH_RETAIN_RANGE *retain_range_table, 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, @@ -35,3 +48,5 @@ BOOL apply_patch_to_file(LPCWSTR patch_file_name, LPCWSTR old_file_name, LPCWSTR const ULONG apply_option_flags, PATCH_PROGRESS_CALLBACK *progress_fn, void *progress_ctx, const BOOL test_header_only); + + diff --git a/include/patchapi.h b/include/patchapi.h index 1ccdf9dec70..a1d1e45183b 100644 --- a/include/patchapi.h +++ b/include/patchapi.h @@ -36,9 +36,21 @@ 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
+/* 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_TRANSFORM_PE_RESOURCE_2 0x00000100 +#define PATCH_TRANSFORM_PE_IRELOC_2 0x00000200 +#define PATCH_EXTRA_HAS_LZX_WINDOW_SIZE 0x00010000 + #define APPLY_OPTION_FAIL_IF_EXACT 0x00000001 #define APPLY_OPTION_FAIL_IF_CLOSE 0x00000002 #define APPLY_OPTION_TEST_ONLY 0x00000004
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=137491
Your paranoid android.
=== debian11 (build log) ===
../wine/dlls/mspatcha/pa19.c:1421:25: error: label at end of compound statement Task: The win32 Wine build failed
=== debian11b (build log) ===
../wine/dlls/mspatcha/pa19.c:1421:25: error: label at end of compound statement Task: The wow64 Wine build failed
1. This is a very large change for one commit. It really needs to be broken up into smaller commits, possibly even over multiple MRs. 2. This MR contains no tests. 3. This MR re-orders functions within pa19.c for no clear reason. 4. This MR removes a lot of FIXME stubs, but it would probably be more beneficial to just convert them to TRACEs.
1. Yes, it is a lot of code, which most is required for proper patching to work. I'm not sure exactly sure how to split up the bulk of it. 2. Testing this is somewhat difficult since a patch file and associated "old" file would be needed to test the patch transformations. Could you provide a suggestion on how similar tests are conducted? 3. I did not re-order any functions, did you thoroughly study my changes? A lot of code was removed, not re-ordered. 4. FIXME stubs were replaced with warnings/traces except for the previous stubs. I.e.: https://gitlab.winehq.org/wine/wine/-/merge_requests/3870/diffs#acda5f5d040f...
I would appreciate some guidance and constructive advice rather than a list of things that are wrong with these changes :)
1. It is not necessary to go from no support to full support in one patch or even one MR. To give a more concrete example: the implementation of `NormalizeFileForPatchSignature` and its dependencies could be broken out into a patch by itself. `GetFilePatchSignature` functions depend on it, but it does not depend on them. Note: more breaking down may be needed, but that would be a good start. 2. Again using `NormalizeFileForPatchSignature` as an example, it takes a buffer as input (so no need to pass in a file). I have test cases for this function (as well as the `GetFilePatchSignature` functions) in a branch on my local machine that I could provide if you are still having difficulty. 3. Sorry, it was actually `mspatcha_main.c` that had the re-ordering issue.
From: - `GetFilePatchSignatureA` - `GetFilePatchSignatureW` - `GetFilePatchSignatureByHandle` - `GetFilePatchSignatureByBuffer` - `NormalizeFileForPatchSignature`
To: - `NormalizeFileForPatchSignature` - `GetFilePatchSignatureByBuffer` - `GetFilePatchSignatureByHandle` - `GetFilePatchSignatureA` - `GetFilePatchSignatureW`
4. Neither `NormalizeFileForPatchSignature` nor `normalize_old_file_image` contain any TRACE. At the very least, I would expect one of these to output the full parameter list.
Hopefully this gives you some clearer direction than my initial response. I (and hopefully others) will be better able to provide more detailed feedback after the above issues are addressed.
1. I will split the normalization and patch logic into separate patches. 2. That would be amazing if you could provide your test cases! Would be so kind as to share a patch? 3. The reordering was due to dependence, `GetFilePatchSignatureA` depends on `GetFilePatchSignatureByHandle` which depends on`GetFilePatchSignatureByBuffer` which depends on `NormalizeFileForPatchSignature`. 4. I will add some better traces. Whilst debugging I found traces to be unhelpful and overly verbose. I prefer single stepping/breakpoint debugging as it is far better for actual debugging, especially for this case.
Thanks for the help, I'll get this done tomorrow!
@ajkhoury for 3, you can use a method that is defined later if the method is defined in a header file.
@whydoubt while I agree splitting into multiple commits is good, I'm not convinced about splitting into multiple MRs
@Fox2Code That is very true. It made more sense to rearrange based on dependence to me as most are trained to follow this paradigm in C, however I can revert the re-ordering if it is required.
@ajkhoury I will try to get you those test cases soon.
Also, I suggest using the existing MD5 functions from ntdll. You can see how other libraries have done this in: - dlls/msi/msi.c - dlls/msv1_0/main.c - dlls/bcrypt/bcrypt_main.c - dlls/bcrypt/bcrypt_internal.h
@whydoubt Thank you :)
I noticed this in `msi.c` when I was adding the MD5 checksum code, but it felt like such an ugly hack to me that I decided to borrow the code itself. I believe it is an ugly hack because it looks like the structure `MD5_CTX` has been copied all over wherever MD5 hash is needed, when it should honestly be opaque. After looking into it further, it appears it follows the exact implementation defined in rfc1321. Albeit, I still very much disapprove of redefining the `MD5_CTX` wherever MD5 hashing is required...
Yeah, it does appear far from ideal. Perhaps (at some point) the structure and function prototypes should be placed in a private header that can then be included in all the places needed.