diff --git a/neolite_unpack.py b/neolite_unpack.py index a56dade..007cc1f 100755 --- a/neolite_unpack.py +++ b/neolite_unpack.py @@ -9,6 +9,7 @@ import pefile import struct import zlib +from zipfile_deflate64.deflate64 import Deflate64 def hexdump(src, length=16, sep='.'): FILTER = ''.join([(len(repr(chr(x))) == 3) and chr(x) or sep for x in range(256)]) @@ -165,7 +166,7 @@ def next_command(self): if command < 2: self.back(0, command + 3) else: - self.back_base(command * 256, 3) + self.back_base(command << 8, 3) case 5: self.back_extend16(4) case 6: self.back_extend16(5) case 7: self.back_offset0(6) @@ -246,6 +247,17 @@ def zlib_uncompress(data, usize): return ret +def deflate64_uncompress(data, usize): + obj = Deflate64() + ret = obj.decompress(data) + + if len(ret) > usize: + raise Exception(f'{len(ret)=:x} {usize=:x}') + + if not obj.eof: + raise Exception + + return ret def is_neolite(data): pe = pefile.PE(data=data) @@ -312,6 +324,37 @@ def print_info(data): v = f'0x{val:x}' print(f'rev.{key:22} {v:>10}') +def fixup_calls(buf, section_size): + data = bytearray(buf) + + i = 0 + while i < len(data) - 4: + if data[i] == 0xE8: # CALL rel32 + i += 1 + addr = int.from_bytes(data[i:i+4], 'little', signed=True) + + # Equivalent to ExeLock runtime logic: + # + # if target is inside section range: + # if addr < 0: + # new = section_size + addr + # else: + # new = addr - current_position + # + if i + addr >= 0 and addr < section_size: + if addr < 0: + new = section_size + addr + else: + new = addr - i + + data[i:i+4] = int(new).to_bytes(4, 'little', signed=True) + + i += 4 + else: + i += 1 + + return bytes(data) + def fixup(data): pe = pefile.PE(data=data) @@ -349,6 +392,7 @@ def fixup(data): zpos_start = pos zpos = zpos_start + exelock = hdr.use_zlib == 2 if hdr.use_zlib: # Decompress the included decompressor. It's just zlib, so we just use # python's zlib, but we need to advance the pos value. @@ -360,7 +404,7 @@ def fixup(data): zpos += hdr.zlib_relocations_sz - uncompress = zlib_uncompress + uncompress = deflate64_uncompress if exelock else zlib_uncompress else: uncompress = neo_uncompress @@ -532,7 +576,14 @@ def fixup(data): if offset > len(new_data): new_data += bytes(offset - len(new_data)) - new_data += sdata + if exelock and section.Characteristics & 0x20000000: # IMAGE_SCN_MEM_EXECUTE + new_data += fixup_calls(sdata, section.SizeOfRawData) + else: + new_data += sdata + if len(sdata) < section.SizeOfRawData: + new_data += bytes(section.SizeOfRawData - len(sdata)) + elif len(sdata) > section.SizeOfRawData: + raise Exception(f'Section {section_name} overflowed') section.PointerToRawData = offset if imports_offset >= section.VirtualAddress and imports_offset < section.VirtualAddress + section.SizeOfRawData: @@ -599,7 +650,10 @@ def fixup(data): # Copy data at end of file to end of file. if hdr.extra_data_csize: if hdr.use_zlib: - udata = zlib.decompress(data[extra_data_offset:extra_data_offset+extra_data_size]) + if exelock: + udata = Deflate64().decompress(data[extra_data_offset:extra_data_offset+extra_data_size]) + else: + udata = zlib.decompress(data[extra_data_offset:extra_data_offset+extra_data_size]) if len(udata) != hdr.extra_data_usize: raise Exception else: diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..acb6c2a --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +pefile==2023.2.7 +zipfile-deflate64==0.2.0