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.
85 lines
3.3 KiB
Python
85 lines
3.3 KiB
Python
#!/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()
|