Scans entire directory where find_keybox_v2.py is located

Usage: python find_keybox.py --all
This commit is contained in:
SuperUserek 2026-02-24 07:09:17 +00:00
parent 8b307ad021
commit 39da11609e

View File

@ -1,240 +1,239 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import mmap import mmap
import os import os
import sys import sys
import hashlib import hashlib
from typing import List, Iterable, Tuple 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" 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"] POSTFIX_HEX_LIST = ["00 00"]
EXTRACT_LEN = 228 EXTRACT_LEN = 228
PRINT_HEXVIEW = True PRINT_HEXVIEW = True
HEXVIEW_WIDTH = 16 HEXVIEW_WIDTH = 16
MAX_HITS = 0 MAX_HITS = 0
REQUIRE_POSTFIX_AT_EXTRACT_END = True REQUIRE_POSTFIX_AT_EXTRACT_END = True
MAX_ZERO_FRACTION = 0.25 MAX_ZERO_FRACTION = 0.25
MAX_ZERO_RUN = 16 MAX_ZERO_RUN = 16
TAIL_START = 0x90 TAIL_START = 0x90
MIN_TAIL_NONZERO_RATIO = 0.70 MIN_TAIL_NONZERO_RATIO = 0.70
SCRIPT_PATH = os.path.realpath(__file__) SCRIPT_PATH = os.path.realpath(__file__)
SCRIPT_DIR = os.path.dirname(SCRIPT_PATH) SCRIPT_DIR = os.path.dirname(SCRIPT_PATH)
SCRIPT_NAME = os.path.basename(SCRIPT_PATH) SCRIPT_NAME = os.path.basename(SCRIPT_PATH)
def clean_hex(s: str) -> bytes: def clean_hex(s: str) -> bytes:
s = s.replace("0x", "").replace("0X", "") s = s.replace("0x", "").replace("0X", "")
s = "".join(s.split()) s = "".join(s.split())
if not s: if not s:
return b"" return b""
if len(s) % 2 != 0: if len(s) % 2 != 0:
raise ValueError raise ValueError
return bytes.fromhex(s) return bytes.fromhex(s)
def iter_all(mm: mmap.mmap, needle: bytes, start: int = 0) -> Iterable[int]: def iter_all(mm: mmap.mmap, needle: bytes, start: int = 0) -> Iterable[int]:
i = start i = start
while True: while True:
pos = mm.find(needle, i) pos = mm.find(needle, i)
if pos == -1: if pos == -1:
return return
yield pos yield pos
i = pos + 1 i = pos + 1
def is_printable_ascii(b: int) -> bool: def is_printable_ascii(b: int) -> bool:
return 32 <= b <= 126 return 32 <= b <= 126
def hexview(data: bytes, base_offset: int = 0, width: int = 16) -> str: def hexview(data: bytes, base_offset: int = 0, width: int = 16) -> str:
lines = [] lines = []
for i in range(0, len(data), width): for i in range(0, len(data), width):
chunk = data[i:i + width] chunk = data[i:i + width]
hex_part = " ".join(f"{x:02X}" for x in chunk) hex_part = " ".join(f"{x:02X}" for x in chunk)
hex_part_padded = hex_part.ljust(width * 3 - 1) hex_part_padded = hex_part.ljust(width * 3 - 1)
ascii_part = "".join(chr(x) if is_printable_ascii(x) else "." for x in chunk) 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}|") lines.append(f"{base_offset + i:08X} {hex_part_padded} |{ascii_part}|")
return "\n".join(lines) return "\n".join(lines)
def best_postfix_window(postfixes: List[bytes]) -> int: def best_postfix_window(postfixes: List[bytes]) -> int:
return max((len(p) for p in postfixes if p), default=0) 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]:
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
start = prefix_pos + EXTRACT_LEN if start >= file_size:
if start >= file_size: return -1, b""
return -1, b"" window = best_postfix_window(postfixes)
window = best_postfix_window(postfixes) end = min(file_size, start + window)
end = min(file_size, start + window) for pf in postfixes:
for pf in postfixes: if not pf:
if not pf: continue
continue if start + len(pf) > file_size:
if start + len(pf) > file_size: continue
continue pos = mm.find(pf, start, end)
pos = mm.find(pf, start, end) if pos != -1:
if pos != -1: return pos, pf
return pos, pf return -1, b""
return -1, b""
def max_zero_run(b: bytes) -> int:
def max_zero_run(b: bytes) -> int: best = 0
best = 0 cur = 0
cur = 0 for x in b:
for x in b: if x == 0:
if x == 0: cur += 1
cur += 1 if cur > best:
if cur > best: best = cur
best = cur else:
else: cur = 0
cur = 0 return best
return best
def zero_fraction(b: bytes) -> float:
def zero_fraction(b: bytes) -> float: if not b:
if not b: return 1.0
return 1.0 z = sum(1 for x in b if x == 0)
z = sum(1 for x in b if x == 0) return z / len(b)
return z / len(b)
def nonzero_ratio(b: bytes) -> float:
def nonzero_ratio(b: bytes) -> float: if not b:
if not b: return 0.0
return 0.0 nz = sum(1 for x in b if x != 0)
nz = sum(1 for x in b if x != 0) return nz / len(b)
return nz / len(b)
def passes_filters(block: bytes) -> bool:
def passes_filters(block: bytes) -> bool: if zero_fraction(block) > MAX_ZERO_FRACTION:
if zero_fraction(block) > MAX_ZERO_FRACTION: return False
return False if max_zero_run(block) > MAX_ZERO_RUN:
if max_zero_run(block) > MAX_ZERO_RUN: return False
return False tail = block[TAIL_START:] if TAIL_START < len(block) else b""
tail = block[TAIL_START:] if TAIL_START < len(block) else b"" if tail and nonzero_ratio(tail) < MIN_TAIL_NONZERO_RATIO:
if tail and nonzero_ratio(tail) < MIN_TAIL_NONZERO_RATIO: return False
return False return True
return True
def safe_prefix_from_filename(path: str) -> str:
def safe_prefix_from_filename(path: str) -> str: base = os.path.basename(path)
base = os.path.basename(path) root, _ = os.path.splitext(base)
root, _ = os.path.splitext(base) safe = []
safe = [] for ch in root:
for ch in root: if ch.isalnum() or ch in "._-":
if ch.isalnum() or ch in "._-": safe.append(ch)
safe.append(ch) else:
else: safe.append("_")
safe.append("_") s = "".join(safe).strip("._-")
s = "".join(safe).strip("._-") return s or "file"
return s or "file"
def is_skipped_file(path: str) -> bool:
def is_skipped_file(path: str) -> bool: base = os.path.basename(path)
base = os.path.basename(path) if base == SCRIPT_NAME:
if base == SCRIPT_NAME: return True
return True if base.endswith("_keybox.bin") or "_keybox_" in base:
if base.endswith("_keybox.bin") or "_keybox_" in base: return True
return True return False
return False
def out_name_for_file(prefix: str, hit_idx: int) -> str:
def out_name_for_file(prefix: str, hit_idx: int) -> str: if hit_idx == 1:
if hit_idx == 1: return f"{prefix}_keybox.bin"
return f"{prefix}_keybox.bin" return f"{prefix}_keybox_{hit_idx}.bin"
return f"{prefix}_keybox_{hit_idx}.bin"
def extract_from_file(path: str, prefix: bytes, postfixes: List[bytes]) -> int:
def extract_from_file(path: str, prefix: bytes, postfixes: List[bytes]) -> int: if not os.path.isfile(path):
if not os.path.isfile(path): return 0
return 0
file_size = os.path.getsize(path)
file_size = os.path.getsize(path) if file_size <= 0:
if file_size <= 0: return 0
return 0
per_file_prefix = safe_prefix_from_filename(path)
per_file_prefix = safe_prefix_from_filename(path) saved = 0
saved = 0 seen = set()
seen = set()
with open(path, "rb") as f:
with open(path, "rb") as f: mm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
mm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) try:
try: for ppos in iter_all(mm, prefix, 0):
for ppos in iter_all(mm, prefix, 0): if ppos + EXTRACT_LEN > file_size:
if ppos + EXTRACT_LEN > file_size: continue
continue
qpos, _ = find_postfix_after_extract(mm, postfixes, ppos, file_size)
qpos, _ = find_postfix_after_extract(mm, postfixes, ppos, file_size) if qpos == -1:
if qpos == -1: continue
continue
if REQUIRE_POSTFIX_AT_EXTRACT_END and qpos != (ppos + EXTRACT_LEN):
if REQUIRE_POSTFIX_AT_EXTRACT_END and qpos != (ppos + EXTRACT_LEN): continue
continue
block = mm[ppos:ppos + EXTRACT_LEN]
block = mm[ppos:ppos + EXTRACT_LEN]
if not passes_filters(block):
if not passes_filters(block): continue
continue
h = hashlib.sha256(block).digest()
h = hashlib.sha256(block).digest() if h in seen:
if h in seen: continue
continue seen.add(h)
seen.add(h)
saved += 1
saved += 1 out_name = out_name_for_file(per_file_prefix, saved)
out_name = out_name_for_file(per_file_prefix, saved) with open(out_name, "wb") as out:
with open(out_name, "wb") as out: out.write(block)
out.write(block)
print(f"[{os.path.basename(path)} hit {saved}] -> {out_name} prefix@0x{ppos:X} postfix@0x{qpos:X}")
print(f"[{os.path.basename(path)} hit {saved}] -> {out_name} prefix@0x{ppos:X} postfix@0x{qpos:X}")
if PRINT_HEXVIEW:
if PRINT_HEXVIEW: print(hexview(block, base_offset=ppos, width=HEXVIEW_WIDTH))
print(hexview(block, base_offset=ppos, width=HEXVIEW_WIDTH)) print()
print()
if MAX_HITS and saved >= MAX_HITS:
if MAX_HITS and saved >= MAX_HITS: break
break finally:
finally: mm.close()
mm.close()
return saved
return saved
def main() -> int:
def main() -> int: try:
try: prefix = clean_hex(PREFIX_HEX)
prefix = clean_hex(PREFIX_HEX) postfixes = [clean_hex(x) for x in POSTFIX_HEX_LIST]
postfixes = [clean_hex(x) for x in POSTFIX_HEX_LIST] except:
except: return 2
return 2
postfixes = [p for p in postfixes if p]
postfixes = [p for p in postfixes if p] if not prefix or not postfixes or EXTRACT_LEN <= 0:
if not prefix or not postfixes or EXTRACT_LEN <= 0: return 2
return 2
if len(sys.argv) == 2 and sys.argv[1] == "--all":
if len(sys.argv) == 2 and sys.argv[1] == "--all": total = 0
total = 0 for name in sorted(os.listdir(SCRIPT_DIR)):
for name in sorted(os.listdir(SCRIPT_DIR)): path = os.path.join(SCRIPT_DIR, name)
path = os.path.join(SCRIPT_DIR, name) if not os.path.isfile(path):
if not os.path.isfile(path): continue
continue if is_skipped_file(path):
if is_skipped_file(path): continue
continue total += extract_from_file(path, prefix, postfixes)
total += extract_from_file(path, prefix, postfixes) return 0 if total else 1
return 0 if total else 1
if len(sys.argv) == 2:
if len(sys.argv) == 2: path = sys.argv[1]
path = sys.argv[1] if not os.path.isfile(path):
if not os.path.isfile(path): return 2
return 2 return 0 if extract_from_file(path, prefix, postfixes) else 1
return 0 if extract_from_file(path, prefix, postfixes) else 1
print("Usage:")
print("Usage:") print(" python find_keybox.py <file>")
print(" python find_keybox.py <file>") print(" python find_keybox.py --all")
print(" python find_keybox.py --all") return 2
return 2
if __name__ == "__main__":
if __name__ == "__main__":
raise SystemExit(main()) raise SystemExit(main())