From 39da11609eb5d33acdbaf8aba574ef77fcb4b2b2 Mon Sep 17 00:00:00 2001 From: SuperUserek Date: Tue, 24 Feb 2026 07:09:17 +0000 Subject: [PATCH] Scans entire directory where find_keybox_v2.py is located Usage: python find_keybox.py --all --- MSTAR/FindKeyBox/find_keybox_v2.py | 477 ++++++++++++++--------------- 1 file changed, 238 insertions(+), 239 deletions(-) diff --git a/MSTAR/FindKeyBox/find_keybox_v2.py b/MSTAR/FindKeyBox/find_keybox_v2.py index c9375c9..65542a2 100644 --- a/MSTAR/FindKeyBox/find_keybox_v2.py +++ b/MSTAR/FindKeyBox/find_keybox_v2.py @@ -1,240 +1,239 @@ -#!/usr/bin/env python3 -import mmap -import os -import sys -import hashlib -from typing import List, Iterable, Tuple - -PREFIX_HEX = "4D 53 54 41 52 5F 53 45 43 55 52 45 5F 53 54 4F 52 45 5F 46 49 4C 45 5F 4D 41 47 49 43 5F 49 44" -POSTFIX_HEX_LIST = ["00 00"] - -EXTRACT_LEN = 228 -PRINT_HEXVIEW = True -HEXVIEW_WIDTH = 16 -MAX_HITS = 0 - -REQUIRE_POSTFIX_AT_EXTRACT_END = True - -MAX_ZERO_FRACTION = 0.25 -MAX_ZERO_RUN = 16 -TAIL_START = 0x90 -MIN_TAIL_NONZERO_RATIO = 0.70 - -SCRIPT_PATH = os.path.realpath(__file__) -SCRIPT_DIR = os.path.dirname(SCRIPT_PATH) -SCRIPT_NAME = os.path.basename(SCRIPT_PATH) - - -def clean_hex(s: str) -> bytes: - s = s.replace("0x", "").replace("0X", "") - s = "".join(s.split()) - if not s: - return b"" - if len(s) % 2 != 0: - raise ValueError - return bytes.fromhex(s) - - -def iter_all(mm: mmap.mmap, needle: bytes, start: int = 0) -> Iterable[int]: - i = start - while True: - pos = mm.find(needle, i) - if pos == -1: - return - yield pos - i = pos + 1 - - -def is_printable_ascii(b: int) -> bool: - return 32 <= b <= 126 - - -def hexview(data: bytes, base_offset: int = 0, width: int = 16) -> str: - lines = [] - for i in range(0, len(data), width): - chunk = data[i:i + width] - hex_part = " ".join(f"{x:02X}" for x in chunk) - hex_part_padded = hex_part.ljust(width * 3 - 1) - ascii_part = "".join(chr(x) if is_printable_ascii(x) else "." for x in chunk) - lines.append(f"{base_offset + i:08X} {hex_part_padded} |{ascii_part}|") - return "\n".join(lines) - - -def best_postfix_window(postfixes: List[bytes]) -> int: - return max((len(p) for p in postfixes if p), default=0) - - -def find_postfix_after_extract(mm: mmap.mmap, postfixes: List[bytes], prefix_pos: int, file_size: int) -> Tuple[int, bytes]: - start = prefix_pos + EXTRACT_LEN - if start >= file_size: - return -1, b"" - window = best_postfix_window(postfixes) - end = min(file_size, start + window) - for pf in postfixes: - if not pf: - continue - if start + len(pf) > file_size: - continue - pos = mm.find(pf, start, end) - if pos != -1: - return pos, pf - return -1, b"" - - -def max_zero_run(b: bytes) -> int: - best = 0 - cur = 0 - for x in b: - if x == 0: - cur += 1 - if cur > best: - best = cur - else: - cur = 0 - return best - - -def zero_fraction(b: bytes) -> float: - if not b: - return 1.0 - z = sum(1 for x in b if x == 0) - return z / len(b) - - -def nonzero_ratio(b: bytes) -> float: - if not b: - return 0.0 - nz = sum(1 for x in b if x != 0) - return nz / len(b) - - -def passes_filters(block: bytes) -> bool: - if zero_fraction(block) > MAX_ZERO_FRACTION: - return False - if max_zero_run(block) > MAX_ZERO_RUN: - return False - tail = block[TAIL_START:] if TAIL_START < len(block) else b"" - if tail and nonzero_ratio(tail) < MIN_TAIL_NONZERO_RATIO: - return False - return True - - -def safe_prefix_from_filename(path: str) -> str: - base = os.path.basename(path) - root, _ = os.path.splitext(base) - safe = [] - for ch in root: - if ch.isalnum() or ch in "._-": - safe.append(ch) - else: - safe.append("_") - s = "".join(safe).strip("._-") - return s or "file" - - -def is_skipped_file(path: str) -> bool: - base = os.path.basename(path) - if base == SCRIPT_NAME: - return True - if base.endswith("_keybox.bin") or "_keybox_" in base: - return True - return False - - -def out_name_for_file(prefix: str, hit_idx: int) -> str: - if hit_idx == 1: - return f"{prefix}_keybox.bin" - return f"{prefix}_keybox_{hit_idx}.bin" - - -def extract_from_file(path: str, prefix: bytes, postfixes: List[bytes]) -> int: - if not os.path.isfile(path): - return 0 - - file_size = os.path.getsize(path) - if file_size <= 0: - return 0 - - per_file_prefix = safe_prefix_from_filename(path) - saved = 0 - seen = set() - - with open(path, "rb") as f: - mm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) - try: - for ppos in iter_all(mm, prefix, 0): - if ppos + EXTRACT_LEN > file_size: - continue - - qpos, _ = find_postfix_after_extract(mm, postfixes, ppos, file_size) - if qpos == -1: - continue - - if REQUIRE_POSTFIX_AT_EXTRACT_END and qpos != (ppos + EXTRACT_LEN): - continue - - block = mm[ppos:ppos + EXTRACT_LEN] - - if not passes_filters(block): - continue - - h = hashlib.sha256(block).digest() - if h in seen: - continue - seen.add(h) - - saved += 1 - out_name = out_name_for_file(per_file_prefix, saved) - with open(out_name, "wb") as out: - out.write(block) - - print(f"[{os.path.basename(path)} hit {saved}] -> {out_name} prefix@0x{ppos:X} postfix@0x{qpos:X}") - - if PRINT_HEXVIEW: - print(hexview(block, base_offset=ppos, width=HEXVIEW_WIDTH)) - print() - - if MAX_HITS and saved >= MAX_HITS: - break - finally: - mm.close() - - return saved - - -def main() -> int: - try: - prefix = clean_hex(PREFIX_HEX) - postfixes = [clean_hex(x) for x in POSTFIX_HEX_LIST] - except: - return 2 - - postfixes = [p for p in postfixes if p] - if not prefix or not postfixes or EXTRACT_LEN <= 0: - return 2 - - if len(sys.argv) == 2 and sys.argv[1] == "--all": - total = 0 - for name in sorted(os.listdir(SCRIPT_DIR)): - path = os.path.join(SCRIPT_DIR, name) - if not os.path.isfile(path): - continue - if is_skipped_file(path): - continue - total += extract_from_file(path, prefix, postfixes) - return 0 if total else 1 - - if len(sys.argv) == 2: - path = sys.argv[1] - if not os.path.isfile(path): - return 2 - return 0 if extract_from_file(path, prefix, postfixes) else 1 - - print("Usage:") - print(" python find_keybox.py ") - print(" python find_keybox.py --all") - return 2 - - -if __name__ == "__main__": +#!/usr/bin/env python3 +import mmap +import os +import sys +import hashlib +from typing import List, Iterable, Tuple + +PREFIX_HEX = "4D 53 54 41 52 5F 53 45 43 55 52 45 5F 53 54 4F 52 45 5F 46 49 4C 45 5F 4D 41 47 49 43 5F 49 44" +POSTFIX_HEX_LIST = ["00 00"] + +EXTRACT_LEN = 228 +PRINT_HEXVIEW = True +HEXVIEW_WIDTH = 16 +MAX_HITS = 0 + +REQUIRE_POSTFIX_AT_EXTRACT_END = True + +MAX_ZERO_FRACTION = 0.25 +MAX_ZERO_RUN = 16 +TAIL_START = 0x90 +MIN_TAIL_NONZERO_RATIO = 0.70 + +SCRIPT_PATH = os.path.realpath(__file__) +SCRIPT_DIR = os.path.dirname(SCRIPT_PATH) +SCRIPT_NAME = os.path.basename(SCRIPT_PATH) + + +def clean_hex(s: str) -> bytes: + s = s.replace("0x", "").replace("0X", "") + s = "".join(s.split()) + if not s: + return b"" + if len(s) % 2 != 0: + raise ValueError + return bytes.fromhex(s) + + +def iter_all(mm: mmap.mmap, needle: bytes, start: int = 0) -> Iterable[int]: + i = start + while True: + pos = mm.find(needle, i) + if pos == -1: + return + yield pos + i = pos + 1 + + +def is_printable_ascii(b: int) -> bool: + return 32 <= b <= 126 + + +def hexview(data: bytes, base_offset: int = 0, width: int = 16) -> str: + lines = [] + for i in range(0, len(data), width): + chunk = data[i:i + width] + hex_part = " ".join(f"{x:02X}" for x in chunk) + hex_part_padded = hex_part.ljust(width * 3 - 1) + ascii_part = "".join(chr(x) if is_printable_ascii(x) else "." for x in chunk) + lines.append(f"{base_offset + i:08X} {hex_part_padded} |{ascii_part}|") + return "\n".join(lines) + + +def best_postfix_window(postfixes: List[bytes]) -> int: + return max((len(p) for p in postfixes if p), default=0) + +def find_postfix_after_extract(mm: mmap.mmap, postfixes: List[bytes], prefix_pos: int, file_size: int) -> Tuple[int, bytes]: + start = prefix_pos + EXTRACT_LEN + if start >= file_size: + return -1, b"" + window = best_postfix_window(postfixes) + end = min(file_size, start + window) + for pf in postfixes: + if not pf: + continue + if start + len(pf) > file_size: + continue + pos = mm.find(pf, start, end) + if pos != -1: + return pos, pf + return -1, b"" + + +def max_zero_run(b: bytes) -> int: + best = 0 + cur = 0 + for x in b: + if x == 0: + cur += 1 + if cur > best: + best = cur + else: + cur = 0 + return best + + +def zero_fraction(b: bytes) -> float: + if not b: + return 1.0 + z = sum(1 for x in b if x == 0) + return z / len(b) + + +def nonzero_ratio(b: bytes) -> float: + if not b: + return 0.0 + nz = sum(1 for x in b if x != 0) + return nz / len(b) + + +def passes_filters(block: bytes) -> bool: + if zero_fraction(block) > MAX_ZERO_FRACTION: + return False + if max_zero_run(block) > MAX_ZERO_RUN: + return False + tail = block[TAIL_START:] if TAIL_START < len(block) else b"" + if tail and nonzero_ratio(tail) < MIN_TAIL_NONZERO_RATIO: + return False + return True + + +def safe_prefix_from_filename(path: str) -> str: + base = os.path.basename(path) + root, _ = os.path.splitext(base) + safe = [] + for ch in root: + if ch.isalnum() or ch in "._-": + safe.append(ch) + else: + safe.append("_") + s = "".join(safe).strip("._-") + return s or "file" + + +def is_skipped_file(path: str) -> bool: + base = os.path.basename(path) + if base == SCRIPT_NAME: + return True + if base.endswith("_keybox.bin") or "_keybox_" in base: + return True + return False + + +def out_name_for_file(prefix: str, hit_idx: int) -> str: + if hit_idx == 1: + return f"{prefix}_keybox.bin" + return f"{prefix}_keybox_{hit_idx}.bin" + + +def extract_from_file(path: str, prefix: bytes, postfixes: List[bytes]) -> int: + if not os.path.isfile(path): + return 0 + + file_size = os.path.getsize(path) + if file_size <= 0: + return 0 + + per_file_prefix = safe_prefix_from_filename(path) + saved = 0 + seen = set() + + with open(path, "rb") as f: + mm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) + try: + for ppos in iter_all(mm, prefix, 0): + if ppos + EXTRACT_LEN > file_size: + continue + + qpos, _ = find_postfix_after_extract(mm, postfixes, ppos, file_size) + if qpos == -1: + continue + + if REQUIRE_POSTFIX_AT_EXTRACT_END and qpos != (ppos + EXTRACT_LEN): + continue + + block = mm[ppos:ppos + EXTRACT_LEN] + + if not passes_filters(block): + continue + + h = hashlib.sha256(block).digest() + if h in seen: + continue + seen.add(h) + + saved += 1 + out_name = out_name_for_file(per_file_prefix, saved) + with open(out_name, "wb") as out: + out.write(block) + + print(f"[{os.path.basename(path)} hit {saved}] -> {out_name} prefix@0x{ppos:X} postfix@0x{qpos:X}") + + if PRINT_HEXVIEW: + print(hexview(block, base_offset=ppos, width=HEXVIEW_WIDTH)) + print() + + if MAX_HITS and saved >= MAX_HITS: + break + finally: + mm.close() + + return saved + + +def main() -> int: + try: + prefix = clean_hex(PREFIX_HEX) + postfixes = [clean_hex(x) for x in POSTFIX_HEX_LIST] + except: + return 2 + + postfixes = [p for p in postfixes if p] + if not prefix or not postfixes or EXTRACT_LEN <= 0: + return 2 + + if len(sys.argv) == 2 and sys.argv[1] == "--all": + total = 0 + for name in sorted(os.listdir(SCRIPT_DIR)): + path = os.path.join(SCRIPT_DIR, name) + if not os.path.isfile(path): + continue + if is_skipped_file(path): + continue + total += extract_from_file(path, prefix, postfixes) + return 0 if total else 1 + + if len(sys.argv) == 2: + path = sys.argv[1] + if not os.path.isfile(path): + return 2 + return 0 if extract_from_file(path, prefix, postfixes) else 1 + + print("Usage:") + print(" python find_keybox.py ") + print(" python find_keybox.py --all") + return 2 + + +if __name__ == "__main__": raise SystemExit(main()) \ No newline at end of file