From 00139caf431d455e9e1e0276918509a3a4e7bca9 Mon Sep 17 00:00:00 2001 From: John Reiser Date: Sat, 23 Jan 2021 15:37:52 -0800 Subject: [PATCH] More robust PackWcle::decodeFixups() https://github.com/upx/upx/issues/455 modified: p_wcle.cpp --- src/p_wcle.cpp | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/p_wcle.cpp b/src/p_wcle.cpp index 19d8f172..9b23c441 100644 --- a/src/p_wcle.cpp +++ b/src/p_wcle.cpp @@ -599,23 +599,40 @@ void PackWcle::decodeFixups() set_le32(wrkmem+ic*8,0xFFFFFFFF); // end of 32-bit offset fixups tmpbuf.dealloc(); - // selector fixups and self-relative fixups + // selector fixups then self-relative fixups const upx_byte *selector_fixups = p; - const upx_byte *selfrel_fixups = p; - while (*selfrel_fixups != 0xC3) { - // FIXME: Heuristic defense for running off the end. - // End is unknown, but all-zero definitely is bad. + // Find selfrel_fixups by skipping over selector_fixups. + const upx_byte *q = selector_fixups; + // The code is a subroutine that ends in RET (0xC3). + while (*q != 0xC3) { + // Defend against tampered selector_fixups; see PackWcle::preprocessFixups(). + // selector_fixups[] is x386 code with 9-byte blocks of 2 instructions each: + // "\x8C\xCB\x66\x89\x9D" // mov bx, cs ; mov [xxx+ebp], bx + // "\x8C\xCA\x66\x89\x95" + // and where byte [+1] also can be '\xDA' or '\xDB'. + if (0x8C != q[0] + || 0x66 != q[2] + || 0x89 != q[3]) { // Unexpected; tampering? + // Try to recover by looking for the RET. + upx_byte const *q2 = (upx_byte const *)memchr(q, 0xC3, 9); + if (q2) { // Assume recovery + q = q2; break; + } + } + // Guard against run-away. static unsigned char const blank[9] = {0}; - if (!memcmp(blank, selfrel_fixups, sizeof(blank))) { + if (q > (oimage + ph.u_len - sizeof(blank)) // catastrohpic worst case + || !memcmp(blank, q, sizeof(blank)) // no-good early warning + ) { char msg[50]; snprintf(msg, sizeof(msg), - "bad selfrel_fixups +%#zx", selfrel_fixups - p); + "bad selector_fixups +%#zx", q - selector_fixups); throwCantPack(msg); } - selfrel_fixups += 9; + q += 9; } - selfrel_fixups++; - unsigned selectlen = ptr_diff(selfrel_fixups, selector_fixups)/9; + unsigned selectlen = ptr_diff(q, selector_fixups)/9; + const upx_byte *selfrel_fixups = 1+ q; // Skip the 0xC3 ofixups = New(upx_byte, fixupn*9+1000+selectlen*5); upx_bytep fp = ofixups;