Skip to content

Phase 10: Android real-usage hardening (scan-apk + 10-step + smali tier + jadx tuning) - #27

Merged
sp3arm4n merged 7 commits into
mainfrom
claude-code
May 12, 2026
Merged

Phase 10: Android real-usage hardening (scan-apk + 10-step + smali tier + jadx tuning)#27
sp3arm4n merged 7 commits into
mainfrom
claude-code

Conversation

@sp3arm4n

Copy link
Copy Markdown
Owner

Summary

KakaoTalk 26.3.2 (215MB / 18 DEX) 실측에서 다섯 가지 실사용 결함이 드러남 — 진행 가시화 부재 / APK 명령어 분산 / jadx timeout 시 디스크 폐기 / jadx 단일 실패점 / 패키지 필터 약속. Phase 10이 다섯 단위로 모두 닫음.

  • P10-1 558febe 10-step 라이브 진행 출력 (stderr) — 매 단계 [N/10] 시작/종료 + apktool/jadx subprocess 진입 안내. --quiet로 억제 가능
  • P10-2 4685b5b venomhook scan-apk 단일 진입점 — argparse alias로 android-audit 동작 그대로 노출. offset-static --apk / offset-e2e --apk는 deprecation 경고만 추가
  • P10-3 5b5c98b graceful jadx timeout — JadxResult.partial: bool 추가, timeout 시 raise 대신 partial 결과 반환. code_audit_report.partial 전파 + HTML "⚠ 부분 결과" 배너
  • P10-4 6638ebc smali_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 구분
  • P10-5 3019d67 jadx 튜닝 (--jadx-threads + --jadx-fast) — jadx 1.5.4 batch-include 옵션 부재 발견 후 정직한 pivot. selective decompile 약속은 smali tier(P10-4)가 실질적으로 대신
  • 추가로 d2c44ff에 Phase 9 follow-up --jadx-timeout CLI 노출도 포함

Test plan

  • 단위 테스트 804 → 834 (+30) — 단위별 라이브 검증
    • 0: P10-1 (logging은 기존 테스트 통과로 검증)
    • 2: P10-2 (scan-apk alias same pipeline, alias inherits all flags)
    • 3: P10-3 (timeout returns partial, no-output still raises, pipeline propagates partial)
    • 22: P10-4 (4 룰별 발화, 3rd-party skip, app_package override, merge semantics, evidence_tier 라운드 트립)
    • 3: P10-5 (--jadx-threads / --jadx-fast 전달, fast_mode -m simple 추가, default 미추가)
  • KakaoTalk 26.3.2 실측 — 51 manifest + 234 코드 (java 103 + smali 신규 131) findings 산출, partial 보고서 정상
  • DivaApplication.apk 빠른 회귀 — [1/10] ... [10/10] ... step 출력 정상, --quiet 억제 정상
  • README + Wiki 5페이지 갱신 (scan-apk 중심 재편, 10-step 다이어그램, MANIFEST-010 + 2-tier code rules 표)
  • (수동) 5 APK 회귀 batch-audit skill로 재실행 — evidence_tier smali 라벨이 기존 보고서에 추가됨

Codex review handoff

outputs/intent.md — 단위별 의도/리스크/체크리스트.

🤖 Generated with Claude Code

sp3arm4n and others added 7 commits May 13, 2026 00:10
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>
@sp3arm4n
sp3arm4n merged commit c25507e into main May 12, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant