Phase 10: Android real-usage hardening (scan-apk + 10-step + smali tier + jadx tuning) - #27
Merged
Conversation
Discovered during the KakaoTalk 26.3.2 (215MB / 18 DEX) regression run: jadx hit the hardcoded 600s default and the pipeline degraded to "jadx timed out after 600s" with an empty code_audit_report. Manifest audit + HTML + PoC bundle continued correctly, but every CODE-* rule silently produced zero findings. This change wires JadxConfig.timeout_sec through to the CLI so a pentester running a large multi-DEX APK can pass --jadx-timeout 1800 without modifying the runner module. venomhook android-audit --apk big.apk --jadx-timeout 1800 ... Argparse default stays None so untouched callers keep the existing 600s behavior. JadxConfig is only instantiated when --jadx-path or --jadx-timeout is set, preserving the "no kwargs -> None" default that analyze_apk's signature expects. Tests +3: - --jadx-timeout 1800 reaches analyze_apk's jadx_config - --jadx-path + --jadx-timeout coexist on the same config - neither flag set -> jadx_config remains None (default path) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Discovered during the KakaoTalk 26.3.2 run: a 30-minute jadx pass
showed nothing on stderr, so the operator had no signal whether the
pipeline was alive, hung, or done. CLI usage felt opaque.
This commit makes the 9-step analyze_apk flow narrate itself:
[1/9] APK 메타데이터 추출 ...
[1/9] APK 메타데이터 추출 (완료 — 0.0s)
[2/9] .so 추출 ...
[4/9] AndroidManifest decode (apktool) ...
↳ apktool subprocess 실행 중 — 대용량 APK는 수 분 소요
[5/9] DEX → Java 디컴파일 (jadx) ...
↳ jadx subprocess 실행 중 — 진행률은 표시되지 않으며 대용량
multi-DEX APK는 수십 분 가능 (default timeout 600s)
[6/9] JNI bridge correlation ...
[7/9] Manifest 감사 + PoC 생성 ...
[8/9] Code 감사 (Java sources) ...
[9/9] 네이티브 string categorize + symbol attribution ...
Each step logs start + elapsed seconds on completion. Skipped steps
get a "[N/9] X (건너뜀 — reason)" line so the operator can see *why*
a stage was bypassed (apktool unavailable, use_jadx=False, no .so,
etc.) without grepping warnings.
Sub-process steps (apktool / jadx) get an extra indented hint that
the subprocess is running silently — live progress forwarding is out
of scope for this unit (would require Popen-based plumbing that
collides with existing subprocess.run mocks); the hint at least tells
the operator the long silence is expected, not a hang.
CLI logging level was effectively INFO already; --verbose still wins
(DEBUG) and --quiet now drops to WARNING so step output is suppressed
when the operator wants pure JSON/HTML pipelining.
No behavior change beyond logging output. 804 existing tests pass
unmodified.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Before this change three different subcommands could take --apk:
- venomhook offset-static --apk ... (APK -> .so -> Ghidra)
- venomhook offset-e2e --apk ... (APK -> .so -> Ghidra -> Frida)
- venomhook android-audit --apk ... (manifest + code + PoC + HTML)
The README listed four further variants of android-audit (--no-jadx,
--code-audit-json, --severity-threshold, full mode) so a new operator
asked "which command should I run on this APK?" — there was no single
answer.
This commit picks android-audit as the canonical entry point and adds
`scan-apk` as a recommended alias. Both share the same parser, same
action, same flags. The deprecated offset-* APK modes still work but
emit a one-line warning pointing at scan-apk so users converge.
Tests +2:
- scan-apk alias produces the same findings as android-audit for
the same input (manifest, PoC, severity in stdout)
- scan-apk inherits every audit_parser flag (--apk-lib, --jadx-timeout,
--severity-threshold, etc.) — argparse exit code 2 would fire if
alias plumbing were wrong
Documentation update (README / Wiki) follows in the Phase 10 close-out
commit so all 5 unit changes land in the same docs pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jadx_runner used to raise on subprocess.TimeoutExpired even when tens
of thousands of .java files had already been written to disk. The
KakaoTalk 26.3.2 run proved this: 18 DEX, 1800s timeout, 43,364 .java
files on disk — and the pipeline produced an empty code_audit_report
because the runner discarded all that work.
This change turns timeout into a downgrade rather than a failure:
- JadxResult gains a ``partial: bool`` field
- On TimeoutExpired:
* if java_files > 0 → return JadxResult(partial=True, returncode=-1)
* if java_files == 0 → still raise (nothing for code_audit to do)
- android_pipeline propagates the partial flag to
CodeAuditReport.partial so HTML / JSON consumers can warn the
reader that an empty rule bucket may reflect missing input
- audit_html_report renders a "⚠ 부분 결과 — jadx 디컴파일 타임아웃"
banner above the code audit severity bar when partial
Re-running the KakaoTalk audit with this change auto-produces the
103 code findings (CODE-001 47, CODE-002 9, CODE-003 23, CODE-005 24)
that previously required a manual code_audit invocation against the
leftover sources.
Cache SCHEMA_VERSION stays at 4 — partial defaults to False on load
so older payloads round-trip cleanly.
Tests +3:
- run_jadx returns partial=True when timeout produced .java files
- run_jadx still raises when timeout produced nothing
- analyze_apk propagates partial to code_audit_report and adds
a Korean timeout warning to the analysis warnings
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
apktool always produces smali/ + smali_classes*/ directories regardless
of how packed / obfuscated the APK is, but we used to read only the
AndroidManifest.xml from that output and throw the rest away. When
jadx then failed or timed out (KakaoTalk-scale, OLLVM-obfuscated,
Bangcle-packed), code_audit produced zero findings — even though the
operator had 1.7GB of smali on disk.
This commit adds src/venomhook/smali_audit.py: same rule IDs as the
.java code audit, but matching against smali bytecode text:
CODE-001 const-string ... "http://..." (medium)
CODE-003 const-string ... "MD5"/"DES"/... (high)
CODE-005 invoke Landroid/os/Environment;->getExternalStorage* (medium)
CODE-006 sget Landroid/content/Context;->MODE_WORLD_* (high)
CODE-002 (WebView setJavaScriptEnabled) and CODE-004 (cred logs) stay
.java-tier only — smali patterns for those are too noisy.
CodeFinding gains evidence_tier ∈ {"java", "smali"} so HTML / JSON
consumers can label each row. Default is "java" so existing tests
and external consumers see no shape change; smali-tier findings
omit the field on serialize only when default to keep older JSON
dumps compact.
Pipeline integration:
- Step 9 (new): smali_audit runs on apktool_out whenever the
decoded dir exists (always, when apktool ran). Conservatively
capped at 200 findings per rule so a 200K-file KakaoTalk smali
tree doesn't drown the report
- merge_code_reports() consolidates .java + smali findings;
.java wins on (rule_id, class_fqn) overlap because its line
text is more readable
- existing Step 9 (native string categorize) renumbers to Step 10;
_PIPELINE_TOTAL = 10 now
DEFAULT_THIRD_PARTY_PREFIXES is reused so Kotlin stdlib / AndroidX /
Google SDK / OkHttp / etc. smali paths are skipped. The app_package
override ensures first-party code under a path matching a common
prefix bucket (e.g. com.kakao.*) is still scanned.
Tests +22 (831 total):
- iter_smali_dirs picks up every smali_classes*/ subdir
- all 4 rules fire on synthetic smali fixtures
- third-party skip works; app_package override lets first-party through
- merge_code_reports semantics (only-java, only-smali, java-wins-on-
overlap, distinct findings concat, partial flag OR)
- CodeFinding.evidence_tier default + round-trip
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
P10-5 was scoped as "selective decompile via app-package filter" so
jadx skips library code. Investigation found jadx 1.5.4 has no batch
package-include CLI option — only --single-class for one class at a
time. A multi-invocation workaround would be slower, not faster.
Pivoting honestly: deliver the two jadx-side tuning knobs that DO
move the wall-clock needle on large APKs, and rely on Phase 10-4
(smali tier) as the actual fallback robustness layer the original
P10-5 was meant to enable.
--jadx-threads N Override -j (jadx default: 4). Bumping on a
many-core box parallelises decompilation.
--jadx-fast Append "-m simple" so jadx skips structure-
restoring passes (goto-style linear output).
Output .java is uglier but VenomHook rules
match against literal const-strings + method
calls, so audit findings are unaffected.
30-50%% wall-clock saving on obfuscated APKs.
Both knobs propagate through CLI -> JadxConfig -> subprocess cmd
line. JadxConfig defaults stay safe (threads=None -> jadx's own 4,
fast_mode=False -> "auto" structure restoration), so existing tests
and external callers see no behavior change.
Honest scope note: KakaoTalk-class APKs may still time out even
with --jadx-fast --jadx-threads 16; the smali tier (Phase 10-4) is
the actual robustness guarantee — it always runs on apktool output
regardless of how jadx finishes.
Tests +3:
- --jadx-threads / --jadx-fast reach JadxConfig via CLI
- fast_mode=True appends -m simple in the command
- default fast_mode=False does NOT emit -m
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…x tuning) - Lead bullets: scan-apk as the single APK entry point; 10 manifest rules; 2-tier code audit (java + smali); MASVS HTML grouping; 10-step live stderr progress output - Common Commands restructured: separate "Android APK 분석" (scan-apk-first) and "네이티브 바이너리 분석" sections. Adds --jadx-timeout / --jadx-threads / --jadx-fast / --apk-lib all examples for the KakaoTalk-scale workflow - Project Layout updated: smali_audit.py, rule_taxonomy.py listed; test count bumped to 834+ - Deprecation note on offset-static --apk / offset-e2e --apk Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
KakaoTalk 26.3.2 (215MB / 18 DEX) 실측에서 다섯 가지 실사용 결함이 드러남 — 진행 가시화 부재 / APK 명령어 분산 / jadx timeout 시 디스크 폐기 / jadx 단일 실패점 / 패키지 필터 약속. Phase 10이 다섯 단위로 모두 닫음.
558febe10-step 라이브 진행 출력 (stderr) — 매 단계 [N/10] 시작/종료 + apktool/jadx subprocess 진입 안내.--quiet로 억제 가능4685b5bvenomhook scan-apk단일 진입점 — argparse alias로android-audit동작 그대로 노출.offset-static --apk/offset-e2e --apk는 deprecation 경고만 추가5b5c98bgraceful jadx timeout —JadxResult.partial: bool추가, timeout 시 raise 대신 partial 결과 반환.code_audit_report.partial전파 + HTML "⚠ 부분 결과" 배너6638ebcsmali_audit 모듈 (Tier 1 폴백) — apktool smali_classes*/ 위에서 4 룰 (CODE-001/003/005/006). jadx 완전 실패에도 코드 결함 산출 보장. 실측: KakaoTalk java 103 + smali 신규 131 = merged 234.CodeFinding.evidence_tier라벨로 java/smali 구분3019d67jadx 튜닝 (--jadx-threads+--jadx-fast) — jadx 1.5.4 batch-include 옵션 부재 발견 후 정직한 pivot. selective decompile 약속은 smali tier(P10-4)가 실질적으로 대신d2c44ff에 Phase 9 follow-up--jadx-timeoutCLI 노출도 포함Test plan
[1/10] ... [10/10] ...step 출력 정상,--quiet억제 정상evidence_tiersmali 라벨이 기존 보고서에 추가됨Codex review handoff
outputs/intent.md— 단위별 의도/리스크/체크리스트.🤖 Generated with Claude Code