Skip to content

Latest commit

 

History

History
239 lines (171 loc) · 9.36 KB

File metadata and controls

239 lines (171 loc) · 9.36 KB

Methodology

Overview

This analysis was conducted in three sequential phases, each building on the previous. No single layer was treated as sufficient on its own — findings were only reported with confidence once corroborated across at least two independent methods.

Phase 1: Static Analysis
        ↓
Phase 2: Dynamic Analysis
        ↓
Phase 3: Cross-verification + Reconciliation

Phase 1 — Static Analysis

1.1 APK Unpacking

The APK was unpacked using standard ZIP extraction to inspect its raw structure:

unzip NetMirror.apk -d netmirror_extract/

Artifacts examined:

  • AndroidManifest.xml — decoded via androguard (AXMLPrinter) for human-readable output
  • META-INF/ contents — inventoried for signing scheme and certificate files
  • classes.dex — counted and noted (single dex, no classes2.dex)
  • assets/index.android.bundle — identified as Hermes bytecode v96

1.2 Java Decompilation

All Java classes decompiled using JADX:

jadx -d netmirror_jadx_out/ NetMirror.apk

Key focus areas:

  • App's own package (app.netmirror.netmirrornew) — 2 classes: MainActivity, MainApplication
  • Third-party libraries — attributed carefully to known open-source packages before drawing conclusions
  • Permission-related classes — traced to React Native library constants, not app logic

Critical methodology note: Every suspicious-looking class was cross-referenced against its likely open-source library origin before being attributed to the app developer. Many initial false positives came from misattributing standard react-native-webview, react-native-system-setting, and react-native-device-info library code as custom malicious logic.

1.3 Hermes Bytecode Disassembly

React Native JS bundles compiled with Hermes cannot be decompiled by JADX. The bundle was disassembled separately using hermes-dec:

pip install hermes-dec
python3 -c "
import sys
sys.argv = ['hbc_disassembler.py', 'assets/index.android.bundle', 'bundle.disasm.txt']
from hermes_dec.disassembly import hbc_disassembler
hbc_disassembler.main()
"

Output: 278,825 lines of disassembly (17MB).

Key searches performed:

Search term Purpose
mobidetect C2 domain pool claim
mpanyinadiingsinsp Affiliate redirect claim
pressfromAPP WebView JS injection claim
dlUrl / stdata / streamDomain Resolver POST flow
READ_SMS / READ_CALL_LOG Permission escalation claim
getAndroidId / getCarrier / getSerialNumber Device fingerprinting claim
isEmulator Anti-analysis evasion claim
KillerApplication / SignatureKiller Signature spoofing claim

Each hit was examined in context (surrounding bytecode) to distinguish actual app logic from library export tables and constants.


Phase 2 — Dynamic Analysis

All dynamic testing was performed on a rooted Pixel 6a running Android 14 with:

  • Frida server 17.8.0 (matching host Frida version)
  • Burp Suite configured as MITM proxy
  • tcpdump for packet-level capture

2.1 HTTP Interception

Burp Suite was configured as a system-wide MITM proxy on the device. The app has no SSL/certificate pinning (confirmed in static analysis), so all TLS traffic was decryptable.

Sessions captured:

  • Cold launch
  • Home screen browsing
  • Video playback (multiple titles)
  • Download flow
  • Background/foreground cycling

All captured requests were exported and sanitized before inclusion in this repository (cookies, private IPs, and precise timestamps were redacted).

Limitation: HTTP Toolkit / Burp Suite only intercepts TCP-based traffic. QUIC (HTTP/3, UDP port 443) bypasses standard MITM proxies and was only visible at the packet layer.

2.2 Packet Capture

Full packet capture was performed alongside HTTP interception to catch any traffic bypassing the proxy (QUIC, direct IP connections, non-standard ports):

# On device (rooted)
adb shell su -c "tcpdump -i any -w /sdcard/netmirror_full.pcap"

# Pull to host
adb pull /sdcard/netmirror_full.pcap ./

Analysis performed with tshark:

# SNI extraction (TLS hostnames, even without decryption)
tshark -r netmirror_full.pcap -Y "tls.handshake.type==1" \
  -T fields -e tls.handshake.extensions_server_name | sort -u

# DNS query extraction
tshark -r netmirror_full.pcap -Y "dns.flags.response==0" \
  -T fields -e dns.qry.name | sort -u

# Protocol breakdown
tshark -r netmirror_full.pcap -q -z io,phs

The full .pcap file is not included in this repository for privacy reasons (contains device IP, precise timing data, and session metadata). Extracted summaries (SNI list, DNS list) are included instead.

2.3 Frida Instrumentation

The app was spawned under Frida to hook key Java methods at runtime:

frida -U -f app.netmirror.netmirrornew -l hook.js --no-pause

Important discovery: This app uses com.android.okhttp (the legacy AOSP-bundled OkHttp fork), not the standalone okhttp3 library. Any Frida hooks targeting okhttp3.* class paths will silently fail. Hooks must target com.android.okhttp.* instead.

Methods hooked:

  • PermissionsModule.requestPermission — catch any runtime permission requests
  • PermissionsModule.requestMultiplePermissions — multi-permission variant
  • Activity.requestPermissions — raw framework-level fallback
  • IntentModule.openURL — catch Linking.openURL() calls (dlUrl handoff)
  • AsyncStorageModule.multiGet / multiSet — storage read/write activity
  • WebView.loadUrl — hidden WebView navigation
  • OkHttpClient.newCall — all outbound HTTP requests

Test sequence for each run:

  1. Cold launch → 30 second idle
  2. Home screen browse
  3. Video playback
  4. Forced network error (throttle mid-stream)
  5. Download button tap
  6. App backgrounded → foregrounded

2.4 Filesystem Extraction

App data extracted directly from device storage (requires root):

adb shell su -c "cp -r /data/data/app.netmirror.netmirrornew /sdcard/netmirror_data_dump"
adb pull /sdcard/netmirror_data_dump ./

Files examined:

  • databases/RKStorage — React Native AsyncStorage (SQLite)
    sqlite3 RKStorage "SELECT key, value FROM catalystLocalStorage;"
  • shared_prefs/*.xml — Native Android SharedPreferences

Phase 3 — Cross-verification

Each finding from static analysis was cross-checked against dynamic results:

If static found X Dynamic check
String/domain in bundle Did it appear in traffic captures?
Permission in code Did Frida hook fire? Was it in manifest?
Network endpoint Was it in Burp capture AND/OR pcap SNI/DNS?
Library method Was it called from app code, or just exported by the library?
Class claimed to exist Was it in JADX class tree? Could it be searched in 4,814 classes?

Findings were classified as:

  • Confirmed — present in code AND observed in live behavior
  • ⚠️ Unconfirmed — present in code but not triggered dynamically (capability, not active use)
  • False positive — attributed to wrong source (library vs app) or does not exist at all

Tools & Versions

Tool Version Purpose
JADX 1.5.1 Java decompilation
hermes-dec 0.1.7 Hermes bytecode disassembly
Frida 17.8.0 Runtime instrumentation
Burp Suite Community Edition HTTP interception
tcpdump Device-native Packet capture
tshark / Wireshark 4.x Packet analysis
androguard 4.x AndroidManifest.xml parsing
adb Platform Tools Device communication
Python 3.12 Scripting / analysis automation
SQLite3 3.x AsyncStorage database reading

Test Device: Rooted Google Pixel 6a, Android 14


Known Limitations

1. Single APK snapshot This analysis covers one specific version of NetMirror. The app has no Play Store presence and is distributed via direct links — version history is not available. A future update could change behavior significantly.

2. Server-side gating Several behaviors (affiliate redirect, potential second-stage payload) may be gated server-side by geography, IP reputation, device profile, or A/B testing. Not triggering them in this test environment does not prove they never fire.

3. Hermes disassembly vs decompilation hermes-dec produces bytecode disassembly (human-readable opcodes), not reconstructed JavaScript source. Function boundaries are visible, but complex logic analysis requires manual tracing through the instruction stream — less readable than decompiled Java.

4. QUIC traffic content QUIC (HTTP/3) traffic was visible at the packet level (DNS + IP conversations) but could not be decrypted without TLS session keys. Content of these connections is unconfirmed.

5. Native libraries This APK contains minimal native code. No deep analysis of native libraries was performed beyond confirming that the specific native libraries claimed in prior reports (libSignatureKiller.so, xhook) do not exist.

6. Frida OkHttp hook gap The OkHttpClient.newCall hook was attached successfully but produced no output during testing. This is unexplained — possible that the build's network calls are routed differently than expected (e.g., via HttpURLConnection directly rather than through the OkHttp Request builder pattern). HTTP Toolkit proxy capture was used as the primary source of network content instead.


How to Reproduce

See Reproduciability/instructions/setup_instructions.md for full step-by-step reproduction guide.