Created
November 5, 2022 14:57
-
-
Save cyxx/37089e3a3b72a623a2b7974dfd96f77c to your computer and use it in GitHub Desktop.
Deobfuscate Another World DOS executable.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# | |
# Deobfuscate Another World DOS executable. | |
# | |
# The 20kb executable of the game can be divided in three main sections. | |
# | |
# 1. From offset 0x20, LZ91 packed data (https://bellard.org/lzexe.html). Each byte is xor'ed with the previous byte. | |
# 2. Some relocation code that jumps to the LZ91 decompression routine. Each byte of the x86 bytecode is off by 0xD. | |
# 3. The start code that patches the relocation code. | |
# | |
# The script reverses these operations and outputs a new "ANOTHER_.EXE" file that can be uncompressed by existing DOS unpackers (e.g. https://bencastricum.nl/unp/). | |
# | |
import struct | |
# Output LZ91 executable with relocation and start code stripped out | |
LZ91_ONLY = True | |
# The x86 start code is used as a signature check | |
# | |
# mov bx, 3 | |
# mov bp, bx | |
# mov cx, 0xF1 | |
# nop | |
# mov si, 0x20 | |
# add si, bx | |
# cld | |
# L1: | |
# add byte ptr cs:[si], 0xD | |
# inc si | |
# loop L1 | |
# | |
SIG = b'\xbb\x03\x00\x8b\xeb\xb9\xf1\x00\x90\xbe\x20\x00\x03\xf3\xfc\x2e\x80\x04\x0d\x46\xe2\xf9' | |
def output(buf, size): | |
with open('ANOTHER_.EXE', 'wb') as f: | |
f.write(buf[:size]) | |
with open('ANOTHER.EXE', 'rb') as f: | |
buf = bytearray(f.read()) | |
f.close() | |
assert buf[0x00:0x02] == b'MZ' | |
assert buf[0x1C:0x20] == b'DIET' | |
headersize = struct.unpack('<H', buf[0x8:0xA])[0] | |
IP, CS = struct.unpack('<HH', buf[0x14:0x18]) | |
assert IP >= 0xA | |
offset = (headersize + CS) << 4 | |
# Ensure the start code matches | |
assert buf[offset + IP:offset + IP + len(SIG)] == SIG | |
if LZ91_ONLY: | |
SS, SP, IP, CS = struct.unpack('<HHHH', buf[offset + 0x3:offset + 0xB]) | |
# Fix up header | |
buf[0x0E:0x12] = struct.pack('<HH', SS, SP) | |
buf[0x14:0x18] = struct.pack('<HH', IP, CS) | |
# Fix up signature | |
buf[0x1C:0x20] = b'LZ91' | |
# Include the last relocation entry | |
offset += 2 | |
# xor... | |
key = 0xD | |
for x in range(0x20, offset): | |
b = key ^ buf[x] | |
key = buf[x] | |
buf[x] = b | |
# Strip out start and relocation code | |
output(buf, offset) | |
else: | |
# Change 'add byte ptr cs:[si], 0xD' to nop | |
buf[offset + IP + 0xF:offset + IP + 0x13] = b'\x90\x90\x90\x90' | |
# add... | |
offset += IP + len(SIG) | |
for x in range(offset, offset + 0xF1): | |
buf[x] = (buf[x] + 0xD) & 0xFF | |
output(buf, len(buf)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment