defscan_directory(root_dir): for java_file in Path(root_dir).rglob("*.java"): try: content = java_file.read_text(errors='ignore') for name, pattern in PATTERNS.items(): formatchin re.finditer(pattern, content, re.IGNORECASE): line_no = content[:match.start()].count('\n') + 1 print(f"[{name}] {java_file}:{line_no} → {match.group()[:80]}") except Exception as e: pass
if __name__ == "__main__": import sys scan_directory(sys.argv[1] iflen(sys.argv) > 1else".")
七、Smali 代码阅读基础
当 JADX 反编译失败(如混淆过度的类)时,需要直接阅读 Smali 代码:
Smali 指令集分类速查
# ===== 移动与赋值指令 ===== move vA, vB # vA = vB move/from16 vA, vB # vA = vB(vA 用 8 位索引,vB 用 16 位索引) move-object vA, vB # 对象引用赋值 const/4 vA, #+B # vA = B(4 位有符号常量,范围 -8~7) const/16 vA, #+BBBB # vA = BBBB(16 位有符号常量) const vA, #+BBBBBBBB # vA = BBBBBBBB(32 位有符号常量) const-string vA, "string"# vA = "string"
# ===== 字段操作指令 ===== iget vA, vB, field_id # vA = vB.field(读取实例字段) iput vA, vB, field_id # vB.field = vA(写入实例字段) sget vA, field_id # vA = Class.field(读取静态字段) sput vA, field_id # Class.field = vA(写入静态字段)
# ===== 返回指令 ===== return-void # 无返回值 return vA # 返回 32 位值 return-wide vA # 返回 64 位值 return-object vA # 返回对象引用
# ===== 条件跳转指令 ===== if-eq vA, vB, :label# if (vA == vB) goto label if-ne vA, vB, :label# if (vA != vB) goto label if-lt vA, vB, :label# if (vA < vB) goto label if-ge vA, vB, :label# if (vA >= vB) goto label if-gt vA, vB, :label# if (vA > vB) goto label if-le vA, vB, :label# if (vA <= vB) goto label if-eqz vA, :label# if (vA == 0) goto label if-nez vA, :label# if (vA != 0) goto label if-ltz vA, :label# if (vA < 0) goto label if-gez vA, :label# if (vA >= 0) goto label
# ===== 数组操作 ===== aget vA, vB, vC # vA = vB[vC] aput vA, vB, vC # vB[vC] = vA new-array vA, vB, type # vA = new type[vB] array-length vA, vB # vA = vB.length
# ===== 对象操作 ===== new-instance vA, type # vA = new type() check-cast vA, type # vA = (type) vA instance-of vA, vB, type # vA = (vB instanceof type)
defbatch_decompile(apk_dir, output_dir): """批量反编译目录中的 APK""" for apk in os.listdir(apk_dir): if apk.endswith('.apk'): output_path = os.path.join(output_dir, apk.replace('.apk', '')) subprocess.run([ 'jadx', '--deobf', '--show-bad-code', os.path.join(apk_dir, apk), '-d', output_path ])
defsearch_patterns(source_dir, patterns): """在反编译输出中搜索模式""" import re results = {} for root, dirs, files in os.walk(source_dir): for file in files: if file.endswith('.java'): filepath = os.path.join(root, file) withopen(filepath, 'r', errors='ignore') as f: content = f.read() for name, pattern in patterns.items(): matches = re.findall(pattern, content) if matches: if name notin results: results[name] = [] results[name].append({ 'file': filepath, 'matches': matches }) return results