From 7ba5bce8a975316b28694528c2a1a51734b89d1d Mon Sep 17 00:00:00 2001 From: Mike Date: Sun, 8 Feb 2026 17:42:32 +0200 Subject: [PATCH] DRMLab Project --- tools/decrypt.py | 56 +++++++++ tools/dump_buildprop.py | 269 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 325 insertions(+) create mode 100644 tools/decrypt.py create mode 100644 tools/dump_buildprop.py diff --git a/tools/decrypt.py b/tools/decrypt.py new file mode 100644 index 0000000..840356a --- /dev/null +++ b/tools/decrypt.py @@ -0,0 +1,56 @@ +from Crypto.Cipher import AES +from Crypto.Util.Padding import pad +import sys +import os + + +def decrypt_file(input_file, key_hex): + try: + key = bytes.fromhex(key_hex) + except ValueError: + print("[-] Invalid AES key") + return + + with open(input_file, "rb") as f: + encrypted_data = f.read() + + cipher = AES.new(key, AES.MODE_ECB) + dec_data = cipher.decrypt(pad(encrypted_data, 16)) + + if b"INNER_MSTAR" not in dec_data: + print("[-] AES key not working") + return + + payload = None + for offset in (64, 96): + candidate = dec_data[offset:] + if b"CHAI" in candidate or b"kbox" in candidate: + payload = candidate + break + + if payload is None: + payload = dec_data[64:] + + if b"CHAI" in payload: + out_data = payload + elif b"kbox" in payload: + out_data = payload[:128] + else: + out_data = payload[:32] + + base, _ = os.path.splitext(input_file) + out_file = f"{base}_encrypted.dat" + + with open(out_file, "wb") as f: + f.write(out_data) + + print(f"[+] Saved {out_file}") + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print(f"Usage: python3 {sys.argv[0]} ") + sys.exit(1) + + aes_key = input("AES key (hex): ").strip() + decrypt_file(sys.argv[1], aes_key) diff --git a/tools/dump_buildprop.py b/tools/dump_buildprop.py new file mode 100644 index 0000000..a9010df --- /dev/null +++ b/tools/dump_buildprop.py @@ -0,0 +1,269 @@ +#!/usr/bin/env python3 +import argparse, os, re, sys +from typing import Dict, List, Tuple + +RE_BLOCK = re.compile( + br'(?mi)^(#\s*begin[^\n]*build\s+properties[^\n]*\n)(.*?)(^#\s*end[^\n]*build\s+properties[^\n]*$)', + re.DOTALL, +) + +BASE_CANONICAL: List[Tuple[str, bool]] = [ + ("# begin build properties", True), + ("# autogenerated by buildinfo.sh", True), + ("ro.build.id", False), + ("ro.build.display.id", False), + ("ro.build.version.incremental", False), + ("ro.build.version.sdk", False), + ("ro.build.version.preview_sdk", False), + ("ro.build.version.codename", False), + ("ro.build.version.all_codenames", False), + ("ro.build.version.release", False), + ("ro.build.version.security_patch", False), + ("ro.build.version.base_os", False), + ("ro.build.date", False), + ("ro.build.date.utc", False), + ("ro.build.type", False), + ("ro.build.user", False), + ("ro.build.host", False), + ("ro.build.tags", False), + ("ro.build.flavor", False), + ("ro.build.system_root_image", False), + ("ro.build.ab_update", False), + ("ro.product.model", False), + ("ro.product.brand", False), + ("ro.product.name", False), + ("ro.product.device", False), + ("ro.product.board", False), + ("# ro.product.cpu.abi and ro.product.cpu.abi2 are obsolete,", True), + ("# use ro.product.cpu.abilist instead.", True), + ("ro.product.cpu.abi", False), + ("ro.product.cpu.abi2", False), + ("ro.product.cpu.abilist", False), + ("ro.product.cpu.abilist32", False), + ("ro.product.cpu.abilist64", False), + ("ro.product.manufacturer", False), + ("ro.product.locale", False), + ("ro.wifi.channels", False), + ("ro.board.platform", False), + ("# ro.build.product is obsolete; use ro.product.device", True), + ("ro.build.product", False), + ("# Do not try to parse description, fingerprint, or thumbprint", True), + ("ro.build.description", False), + ("ro.build.fingerprint", False), + ("ro.build.thumbprint", False), + ("ro.build.characteristics", False), + ("# end build properties", True), +] + +GENERIC_TOKENS = {"", "unknown", "generic", "aosp", "android", "android_device"} + +def is_generic(v: str) -> bool: + s = (v or "").strip().lower() + return s in GENERIC_TOKENS or s.startswith("generic-") or s == "test-keys" + +def parse_props(text: str) -> Dict[str, str]: + d: Dict[str, str] = {} + for raw in text.splitlines(): + line = raw.strip() + if not line or line.startswith("#") or "=" not in line: + continue + k, v = line.split("=", 1) + d[k.strip()] = v.strip() + return d + +def block_kind(header: str, body: str) -> str: + h = (header + body).lower() + return "common" if "common build properties" in h or "buildinfo_common.sh" in h else "base" + +def find_blocks(data: bytes): + blocks = [] + for m in RE_BLOCK.finditer(data): + header = m.group(1).decode(errors="ignore") + body = m.group(2).decode(errors="ignore") + footer = m.group(3).decode(errors="ignore") + text = (header + body + footer).replace("\r\n","\n") + props = parse_props(text) + blocks.append({"text": text, "props": props, "kind": block_kind(header, body)}) + return blocks + +SOC_VENDOR_PATTERNS = { + "mstar": ["mstar", "cv6a", "msd6a", "mst", "cva"], + "mediatek":["mediatek", "mtk", "mt5", "mt6", "mt8"], + "amlogic": ["amlogic", "s905", "s912", "s922", "a113", "g12"], + "qualcomm":["qcom", "qualcomm", "msm", "sdm", "sm"], + "hisilicon":["hisilicon", "kirin", "hi37", "hi38", "hi35"], + "rockchip":["rockchip", "rk3", "rk33", "rk35", "px3", "px5"], +} + +def detect_soc_vendor(props: Dict[str,str]) -> str: + hay = " ".join([ + props.get("ro.board.platform",""), + props.get("ro.hardware",""), + props.get("ro.soc.manufacturer",""), + props.get("ro.soc.model",""), + props.get("ro.product.board",""), + ]).lower() + for vendor, pats in SOC_VENDOR_PATTERNS.items(): + if any(p in hay for p in pats): + return vendor.capitalize() + return props.get("ro.soc.manufacturer","").strip() + +FINGERPRINT_KEYS = [ + "ro.build.fingerprint", + "ro.system.build.fingerprint", + "ro.product.build.fingerprint", + "ro.vendor.build.fingerprint", + "ro.odm.build.fingerprint", + "ro.oem.build.fingerprint", + "ro.bootimage.build.fingerprint", +] + +def first_non_generic(*vals: str) -> str: + for v in vals: + if v and not is_generic(v): + return v + return vals[0] if vals else "" + +def derive_company_from_fp(fp: str) -> str: + if not fp or "/" not in fp: + return "" + head = fp.split("/", 1)[0].strip() + return head[:1].upper() + head[1:] if head else "" + +def detect_form_factor(all_props: Dict[str,str]) -> str: + text = " ".join([ + all_props.get("ro.build.characteristics",""), + all_props.get("ro.build.flavor",""), + all_props.get("ro.build.fingerprint",""), + all_props.get("ro.product.model",""), + all_props.get("ro.product.name",""), + all_props.get("ro.product.device",""), + all_props.get("ro.product.system.model",""), + all_props.get("ro.product.system.name",""), + ]).lower() + if any(t in text for t in [" tv", "/tv", "mitv", "atv", "dvb", "android-tv", "google/atv"]): + return "tv" + if "tablet" in text or "pad" in text or "tab" in text: + return "tablet" + if "watch" in text or "wear" in text: + return "watch" + if "automotive" in text or "car" in text or "ivi" in text: + return "automotive" + return "" + +def compose_props(base: Dict[str,str], commons: Dict[str,str], prefer_soc: bool, + prefer_board_device: bool, force_ff: str|None) -> Dict[str,str]: + merged = dict(commons) + merged.update(base) + out = dict(base) + + fp = base.get("ro.build.fingerprint","") + if not fp or is_generic(fp) or "/" not in fp: + out["ro.build.fingerprint"] = first_non_generic(*(commons.get(k,"") for k in FINGERPRINT_KEYS[1:])) or fp + + for tail in ["brand","manufacturer","model","device","name","board"]: + dst = f"ro.product.{tail}" + cur = base.get(dst, "") + cand = first_non_generic( + commons.get(f"ro.product.system.{tail}",""), + commons.get(f"ro.product.product.{tail}",""), + commons.get(f"ro.product.vendor.{tail}",""), + commons.get(f"ro.product.odm.{tail}",""), + commons.get(f"ro.product.oem.{tail}",""), + merged.get(dst,""), + cur, + ) + out[dst] = cand or cur + + if prefer_board_device: + dev = out.get("ro.product.device","") + if is_generic(dev) or dev in {"croods","android"}: + repl = first_non_generic( + merged.get("ro.product.board",""), + merged.get("ro.soc.model",""), + merged.get("ro.hardware",""), + dev, + ) + out["ro.product.device"] = repl or dev + + fp_final = out.get("ro.build.fingerprint","") + fp_brand = derive_company_from_fp(fp_final) + soc_vendor = detect_soc_vendor(merged) + if prefer_soc and soc_vendor: + out["ro.product.brand"] = soc_vendor + out["ro.product.manufacturer"] = soc_vendor + else: + if is_generic(out.get("ro.product.brand","")) and fp_brand: + out["ro.product.brand"] = fp_brand + if is_generic(out.get("ro.product.manufacturer","")) and fp_brand: + out["ro.product.manufacturer"] = fp_brand + + if is_generic(out.get("ro.product.board","")): + out["ro.product.board"] = first_non_generic( + merged.get("ro.product.board",""), + merged.get("ro.soc.model",""), + merged.get("ro.board.platform",""), + out.get("ro.product.device",""), + ) + if not out.get("ro.board.platform",""): + out["ro.board.platform"] = first_non_generic( + merged.get("ro.board.platform",""), + merged.get("ro.hardware",""), + merged.get("ro.soc.model",""), + ) + + if force_ff: + out["ro.build.characteristics"] = force_ff + else: + cur_ff = out.get("ro.build.characteristics","").strip().lower() + if cur_ff in {"", "default", "nosdcard"}: + auto = detect_form_factor({**merged, **out}) + if auto: + out["ro.build.characteristics"] = auto + + return out + +def render_canonical(props: Dict[str,str]) -> str: + lines: List[str] = [] + for token, is_comment in BASE_CANONICAL: + lines.append(token if is_comment else f"{token}={props.get(token,'')}") + return "\n".join(lines).strip() + "\n" + +def main(): + ap = argparse.ArgumentParser(description="Compose canonical build.prop (fills brand/device/fingerprint/form-factor).") + ap.add_argument("file") + ap.add_argument("--outdir", default="out") + ap.add_argument("--prefer-soc", action="store_true") + ap.add_argument("--prefer-board-device", action="store_true") + ap.add_argument("--form-factor", choices=["auto","tv","tablet","phone","watch","automotive"], default="auto") + args = ap.parse_args() + + with open(args.file, "rb") as f: + data = f.read() + + blocks = find_blocks(data) + if not blocks: + print("No build properties blocks found.") + sys.exit(1) + + base = next((b for b in blocks if b["kind"]=="base"), blocks[0]) + commons: Dict[str,str] = {} + for b in blocks: + if b["kind"] == "common": + commons.update(b["props"]) + + force_ff = None if args.form_factor == "auto" else args.form_factor + composed = compose_props(base["props"], commons, args.prefer_soc, args.prefer_board_device, force_ff) + txt = render_canonical(composed) + + os.makedirs(args.outdir, exist_ok=True) + outp = os.path.join(args.outdir, "best_build_composed.prop") + with open(outp, "w", encoding="utf-8") as fo: + fo.write(txt) + + print("Done:", outp) + +if __name__ == "__main__": + main() + +