Skip to content

Instantly share code, notes, and snippets.

@masthoon
Created August 18, 2021 18:36
Show Gist options
  • Save masthoon/018527d6e137e4980b63a33c847278e1 to your computer and use it in GitHub Desktop.
Save masthoon/018527d6e137e4980b63a33c847278e1 to your computer and use it in GitHub Desktop.
Extract and decompress parts of RFU firmware
import sys
import struct
import lzma
PJL_ESC = b'\x1b%-12345X'
def parse_pjl_command(fw):
assert(fw[0] == 0x20)
cmd, info = fw[1:256].split(b' ', 1)
name, value = info.split(b'=', 1)
value, _ = value.split(b'\n', 1)
if cmd == b'UPGRADE' and name == b'SIZE':
size = int(value)
print("Size of extracted fw (PJL): 0x{:x}".format(size))
return size
elif cmd == b"COMMENT" and name == b'MODEL':
print("Extract fw of model: {}".format(value))
return 0
# not handled
print("Not handled {} - {} = {}".format(cmd, name, value))
return 0
def extract_pjl(fw):
off = 0
fw_size = 0
# ESC
while fw[off] != 0x1b:
off += 1
# Check ESC
if not fw[off:].startswith(PJL_ESC):
print("Invalid ESC command")
return ''
off += len(PJL_ESC)
while 1:
# Check PJL
if fw[off:].startswith(PJL_ESC):
off += len(PJL_ESC)
while fw[off] in [0xD, 0xA]:
off += 1
break
if not fw[off:].startswith(b'@PJL'):
print("Invalid PJL command")
return ''
off += 4
# Parse it
size = parse_pjl_command(fw[off:])
if size > 0:
assert fw_size == 0, "Already defined fw_size"
fw_size = size
# Check padding
while fw[off] not in [0xD, 0xA]:
off += 1
while fw[off] in [0xD, 0xA]:
off += 1
if fw_size == 0:
# Retry (firmware may have two headers)
return extract_pjl(fw[off:])
fw_max_len = len(fw)
assert(fw_max_len - off >= fw_size)
end_fw = off + fw_size
ext_fw = fw[off:end_fw]
print("Signature Bytes left 0x{:x} [{}...]".format(fw_max_len - end_fw, fw[end_fw:end_fw+40]))
return ext_fw
def u32(s):
"""u32(s) -> int
Unpack 32 bits integer from a little endian str representation
"""
return struct.unpack('<I', s)[0]
def u32b(s):
"""u32(s) -> int
Unpack 32 bits integer from a little endian str representation
"""
return struct.unpack('>I', s)[0]
FW_START = 0x40000
def extract_and_save_lz(fw):
off = 0
id_ = 0
# CHECK magic if starts at 0 or FW_START
if fw[0xc:0xe] != b'\xbe\xac':
fw = fw[FW_START:]
fw = fw[0xc:]
while off < len(fw):
magic = u32(fw[off:off+4])
size = u32b(fw[off+4:off+8])
unk_c = u32(fw[off+0xc:off+0x10])
unk_10 = u32(fw[off+0x10:off+0x14])
unk_14 = u32(fw[off+0x14:off+0x18])
assert(magic & 0xffff == 0xacbe)
print("Magic {:x} Size {:x} UNK (c){:x} (10){:x} (14){:x}".format(magic, size, unk_c, unk_10, unk_14))
if magic & 0x04000000:
# Compressed
print("UNCOMPRESS")
data = lzma.decompress(fw[off + 32:off + 32 + size])
else:
data = fw[off + 8:off + 8 + size]
filename = 'extracted{}_{:x}.re'.format(id_, unk_10)
print("Dumped {} size 0x{:x}".format(filename, len(data)))
f = open(filename, 'wb')
f.write(data)
f.close()
off += size + 0x20
id_ += 1
def main():
filename = sys.argv[1]
fw = open(filename, 'rb').read()
fw = extract_pjl(fw)
if fw:
print("Succesfully extracted PJL command to extracted.pjl.re")
f = open('extracted.pjl.re', 'wb')
f.write(fw)
f.close()
extract_and_save_lz(fw)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment