feat: apk-reverse Agent Skill

Progressive-disclosure skill for Android APK reverse engineering:
recon, ad removal, membership limits, dex surgical patching, repack/sign,
runtime data, Frida, environment setup, and a hard-won failure catalogue.
This commit is contained in:
inliver233
2026-09-19 21:25:35 +08:00
commit c66658b6b3
33 changed files with 3713 additions and 0 deletions
+84
View File
@@ -0,0 +1,84 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""One-shot builder: copy the battle-tested tools from the project workspace into
the skill, stripping machine-specific hardcoding.
Run from anywhere: python build_scripts.py <project_work_dir> <skill_scripts_dir>
"""
import os
import re
import shutil
import sys
SRC = sys.argv[1] if len(sys.argv) > 1 else r'E:\ciyuan\work'
DST = sys.argv[2] if len(sys.argv) > 2 else r'E:\ciyuan\apk-reverse\scripts'
# file -> (new name, list of (pattern, replacement))
COPY = {
r'tools\dex_strpatch.py': ('dex_strpatch.py', []),
r'tools\dex_classdiff.py': ('dex_classdiff.py', []),
r'tools\patch_smali.py': ('patch_smali.py', []),
r'tools\repack.py': ('repack.py', [
# strip machine-specific defaults; make them explicit args
(re.compile(r'KS_PATH\s*=\s*r?["\'][^"\']*["\']'), 'KS_PATH = None # pass --ks on the command line'),
(re.compile(r"workdir'?,?\s*default=r?['\"][^'\"]*['\"]"), "workdir', default=None"),
]),
r'tools\cyc_proxy.py': ('usb_net_proxy.py', []),
r'tools\mk_video_prefs.py': ('datastore_inject.py', []),
r'tools\probe_adverts.py': ('probe_api.py', []),
r'tools\grab_crash.py': ('grab_crash.py', []),
}
def clean(text):
"""Remove absolute paths that point at one particular machine."""
text = re.sub(r"r?'[A-Za-z]:\\\\[^'\"]*'", "''", text)
text = re.sub(r'"[A-Za-z]:\\\\[^"]*"', '""', text)
return text
def main():
os.makedirs(DST, exist_ok=True)
os.makedirs(os.path.join(DST, 'dexpatch'), exist_ok=True)
for rel, (new, subs) in COPY.items():
src = os.path.join(SRC, rel)
if not os.path.isfile(src):
print('[skip missing] %s' % rel)
continue
t = open(src, encoding='utf-8', errors='replace').read()
for pat, rep in subs:
t = pat.sub(rep, t)
out = os.path.join(DST, new)
with open(out, 'w', encoding='utf-8', newline='\n') as f:
f.write(t)
print('[copied] %-24s -> %s' % (rel, new))
# dexlib2 patcher sources
for rel, new in [(r'tools\dexpatch\PatchMethod.java', 'PatchMethod.java'),
(r'tools\dexpatch\PatchClasses7v2.java', 'PatchMethodExample.java')]:
src = os.path.join(SRC, rel)
if os.path.isfile(src):
t = open(src, encoding='utf-8', errors='replace').read()
with open(os.path.join(DST, 'dexpatch', new), 'w',
encoding='utf-8', newline='\n') as f:
f.write(t)
print('[copied] %-24s -> dexpatch/%s' % (rel, new))
else:
print('[skip missing] %s' % rel)
# cp.txt content -> smali_cp.txt template
cp = os.path.join(SRC, 'tools', 'dexpatch', 'cp.txt')
if os.path.isfile(cp):
raw = open(cp, encoding='utf-8').read().strip()
jars = [j for j in raw.split(';') if j.strip()]
with open(os.path.join(DST, 'smali_cp.txt'), 'w', encoding='utf-8', newline='\n') as f:
f.write('# One jar path per line. Adjust the directory to your own tools.\n')
f.write('# Needed: smali baksmali dexlib2 util antlr-runtime stringtemplate '
'jcommander guava\n')
for j in jars:
f.write(os.path.basename(j) + '\n')
print('[wrote] smali_cp.txt (%d jars)' % len(jars))
if __name__ == '__main__':
main()