Skip to content

Instantly share code, notes, and snippets.

@cyxx
Created November 5, 2022 14:57
Show Gist options
  • Save cyxx/37089e3a3b72a623a2b7974dfd96f77c to your computer and use it in GitHub Desktop.
Save cyxx/37089e3a3b72a623a2b7974dfd96f77c to your computer and use it in GitHub Desktop.
Deobfuscate Another World DOS executable.
#!/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