From 27055b536d729ff942abbeb8242f2deb34f7630e Mon Sep 17 00:00:00 2001 From: Krys Kamieniecki Date: Sat, 7 Dec 2024 20:37:52 -0500 Subject: [PATCH] Added brute force decompressor, and a gz save tool to extract compressed payload --- python/bin/brute_force_decompress.py | 46 ++++++++++++++++++++++++++++ python/bin/gz_save_tool.py | 30 ++++++++++++++++++ python/deca/deca/ff_adf.py | 13 +++++++- 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 python/bin/brute_force_decompress.py create mode 100644 python/bin/gz_save_tool.py diff --git a/python/bin/brute_force_decompress.py b/python/bin/brute_force_decompress.py new file mode 100644 index 0000000..a29d62f --- /dev/null +++ b/python/bin/brute_force_decompress.py @@ -0,0 +1,46 @@ +import sys + +from deca.db_processor import vfs_structure_open, VfsProcessor +from deca.ff_adf import AdfDatabase +from deca.file import ArchiveFile +import os +import io +import zlib +import zstandard as zstd +import re +import json +import numpy as np + + +def main(): + fn = sys.argv[1] + + with open(fn, 'rb') as f: + buf = f.read() + + for offset in range(0, len(buf), 2): + try: + tmp_buf_c = buf[offset:] + tmp_buf_u = zlib.decompress(tmp_buf_c, -15) + + if len(tmp_buf_u) > 64: + print(f"zlib: offset={offset}, sz={len(tmp_buf_u)}, {tmp_buf_u[0:4]}") + except: + pass + + + + for offset in range(0, len(buf), 2): + try: + tmp_buf_c = buf[offset:] + dc = zstd.ZstdDecompressor() + tmp_buf_u = dc.decompress(tmp_buf_c) + + if len(tmp_buf_u) > 64: + print(f"zstd: offset={offset}, sz={len(tmp_buf_u)}, {tmp_buf_u[0:4]}") + except: + pass + + +if __name__ == "__main__": + main() diff --git a/python/bin/gz_save_tool.py b/python/bin/gz_save_tool.py new file mode 100644 index 0000000..a832eb3 --- /dev/null +++ b/python/bin/gz_save_tool.py @@ -0,0 +1,30 @@ +import sys + +from deca.ff_adf import AdfDatabase +import zstandard as zstd + + +def main(): + fn = sys.argv[1] + + adf_database = AdfDatabase() + + with open(fn, 'rb') as f: + buf = f.read() + + adf = adf_database._load_adf(buf) + + if adf.instance_count == 1: + adf_value = adf.table_instance_full_values[0].value + if isinstance(adf_value, dict) and "SaveData" in adf_value: + if adf_database.type_map_def[adf_value["SaveData"].value.type_id].name == b"SaveTrap": + tmp_buf_c = buf[adf.max_file_position + 4:] + dc = zstd.ZstdDecompressor() + tmp_buf_u = dc.decompress(tmp_buf_c) + + with open(fn + ".extracted", "wb") as f: + f.write(tmp_buf_u) + + +if __name__ == "__main__": + main() diff --git a/python/deca/deca/ff_adf.py b/python/deca/deca/ff_adf.py index 7b2f2e1..e54d5a4 100644 --- a/python/deca/deca/ff_adf.py +++ b/python/deca/deca/ff_adf.py @@ -226,7 +226,7 @@ def deserialize(self, f, nt): # 0x7515A207 == hashlittle2("float044") # 0xC609F663 == hashlittle2("double088") # 0x8955583E == hashlittle2("String588") - + typedef_s8 = 0x580D0A62 typedef_u8 = 0x0ca2821d typedef_s16 = 0xD13FCF93 @@ -865,6 +865,7 @@ def __init__(self): self.nametable_count = None self.nametable_offset = None self.total_size = None + self.max_file_position = 0 self.unknown = [] @@ -946,6 +947,8 @@ def deserialize(self, fp, map_typedef=None, process_instances=True): header = fp.read(0x40) + self.max_file_position = max(self.max_file_position, fp.tell()) + fh = ArchiveFile(io.BytesIO(header)) if len(header) < 0x40: @@ -976,6 +979,8 @@ def deserialize(self, fp, map_typedef=None, process_instances=True): self.comment = fp.read_strz() + self.max_file_position = max(self.max_file_position, fp.tell()) + # name table self.table_name = [[0, b''] for i in range(self.nametable_count)] fp.seek(self.nametable_offset) @@ -984,6 +989,8 @@ def deserialize(self, fp, map_typedef=None, process_instances=True): for i in range(self.nametable_count): self.table_name[i][1] = fp.read(self.table_name[i][0] + 1)[0:-1] + self.max_file_position = max(self.max_file_position, fp.tell()) + # string hash self.table_stringhash = [StringHash() for i in range(self.stringhash_count)] self.map_stringhash = {} @@ -992,6 +999,8 @@ def deserialize(self, fp, map_typedef=None, process_instances=True): self.table_stringhash[i].deserialize(fp, self.table_name) self.map_stringhash[self.table_stringhash[i].value_hash] = self.table_stringhash[i] + self.max_file_position = max(self.max_file_position, fp.tell()) + # typedef self.table_typedef = [TypeDef() for i in range(self.typedef_count)] @@ -1037,6 +1046,8 @@ def deserialize(self, fp, map_typedef=None, process_instances=True): # except Exception as exp: # print(exp) + self.max_file_position = max(self.max_file_position, fp.tell()) + class AdfDatabase: def __init__(self, vfs=None):