#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Extract strings, URLs and vendor markers from dex files without a decompiler. Fast recon: dex strings are stored as plain MUTF-8, so ASCII substrings can be found by scanning the raw bytes. This answers most classification questions in seconds -- which dex holds the app's code, which ad SDKs ship with it, what endpoints it calls. Usage ----- # all URLs across a directory of dex files python dex_strings.py --urls # find which dex files contain a marker (ASCII substring) python dex_strings.py --find 'openadsdk|com.qq.e|anythink' --per-file # dump a class/package inventory per dex (type descriptors) python dex_strings.py --classes 'Lcom/example/app/' # raw string-table dump, length-filtered python dex_strings.py --strings --min 8 --max 60 # compare two dex files' string tables (what changed after a patch) python dex_strings.py --diff a.dex b.dex Notes ----- * A marker in the string table means the class/URL is *present*, not that the app *uses* that feature. Confirm with runtime behavior before acting. * `--urls` output is the fastest way to see an app's whole API surface and its third-party endpoints at once. """ import argparse import glob import os import re import struct import sys URL_RE = re.compile(rb'https?://[A-Za-z0-9\.\-_:/%\.\?=&~#]{4,200}') ASCII_RE = re.compile(rb'[\x20-\x7e]{4,200}') def uleb128(data, off): r = 0 s = 0 while True: b = data[off] off += 1 r |= (b & 0x7F) << s if not (b & 0x80): break s += 7 return r, off def dex_strings(path): """Yield the dex string table in table order.""" data = open(path, 'rb').read() if data[:4] != b'dex\n': return n = struct.unpack('