From 66b78a12b32a6a7b25df79518e383ca14e0e6682 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 17 Mar 2026 19:34:32 +0000 Subject: [PATCH 1/2] Add AGENTS.md with cloud-specific instructions for empty repo Co-authored-by: Mostafa Elbagory --- AGENTS.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..9399b80 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,18 @@ +# AGENTS.md + +## Cursor Cloud specific instructions + +This repository (`tik-api`) is currently an empty skeleton — it contains only a `README.md` with the project title. There is no source code, no dependency manifests (`package.json`, `requirements.txt`, etc.), no configuration files, and no runnable application or test suite. + +### Current state (as of initial setup) + +- **No build system or package manager** is configured. +- **No services** to start, lint, test, or build. +- **No Docker/Compose** configuration exists. + +### When code is added + +Once the project has actual source code and dependencies, this section should be updated with: +- How to install dependencies (update script). +- How to run the dev server, tests, and linter. +- Any non-obvious caveats discovered during setup. From f0c3d524775e0f7d66aa513a553b00330e879667 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 17 Mar 2026 20:39:47 +0000 Subject: [PATCH 2/2] Add TikTok API analysis toolkit: scripts, tools, and documentation - APK signature extraction (shell + Python with JSON output) - APK decompilation scripts (apktool + jadx) - mitmproxy traffic capture with TikTok API filter addon - Frida SSL pinning bypass script - Traffic analysis tool for captured flows - requirements.txt for Python dependencies - Updated README with full documentation - Updated AGENTS.md with cloud-specific instructions Co-authored-by: Mostafa Elbagory --- .gitignore | 26 ++++++ AGENTS.md | 64 +++++++++++-- README.md | 99 +++++++++++++++++++- config/.gitkeep | 0 requirements.txt | 3 + scripts/analyze_traffic.py | 126 +++++++++++++++++++++++++ scripts/capture_traffic.sh | 36 ++++++++ scripts/decompile_apk.sh | 34 +++++++ scripts/extract_signature.sh | 31 +++++++ scripts/extract_tiktok_signature.py | 137 ++++++++++++++++++++++++++++ scripts/ssl_bypass_frida.js | 59 ++++++++++++ scripts/tiktok_proxy_addon.py | 119 ++++++++++++++++++++++++ tools/apk/.gitkeep | 0 tools/traffic/.gitkeep | 0 14 files changed, 723 insertions(+), 11 deletions(-) create mode 100644 .gitignore create mode 100644 config/.gitkeep create mode 100644 requirements.txt create mode 100644 scripts/analyze_traffic.py create mode 100755 scripts/capture_traffic.sh create mode 100755 scripts/decompile_apk.sh create mode 100755 scripts/extract_signature.sh create mode 100644 scripts/extract_tiktok_signature.py create mode 100644 scripts/ssl_bypass_frida.js create mode 100644 scripts/tiktok_proxy_addon.py create mode 100644 tools/apk/.gitkeep create mode 100644 tools/traffic/.gitkeep diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..616c5e7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# APK files +*.apk +*.xapk + +# Decompiled output +tools/apk/decompiled/ + +# Traffic captures +tools/traffic/*.flow +tools/traffic/*.jsonl + +# Signature output +*.signature.json + +# Python +__pycache__/ +*.pyc +.venv/ +venv/ + +# OS +.DS_Store +Thumbs.db + +# mitmproxy +~/.mitmproxy/ diff --git a/AGENTS.md b/AGENTS.md index 9399b80..618b597 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,17 +2,61 @@ ## Cursor Cloud specific instructions -This repository (`tik-api`) is currently an empty skeleton — it contains only a `README.md` with the project title. There is no source code, no dependency manifests (`package.json`, `requirements.txt`, etc.), no configuration files, and no runnable application or test suite. +This repository (`tik-api`) provides tools for analyzing TikTok's Android APK and API communications. -### Current state (as of initial setup) +### Installed Tools -- **No build system or package manager** is configured. -- **No services** to start, lint, test, or build. -- **No Docker/Compose** configuration exists. +| Tool | Version | Path | Purpose | +|------|---------|------|---------| +| Android SDK CLI | latest | `/opt/android-sdk/` | APK management, `adb`, `apksigner`, `aapt` | +| apktool | 2.7.0 | system | APK decompilation (smali + resources) | +| jadx | 1.5.1 | `/opt/jadx/bin/jadx` | APK decompilation (Java source) | +| mitmproxy | 12.2.1 | `~/.local/bin/mitmdump` | HTTPS traffic interception | +| frida | 17.8.2 | `~/.local/bin/frida` | Runtime instrumentation / SSL pinning bypass | +| keytool | JDK 21 | system | Certificate extraction | -### When code is added +### Environment Variables -Once the project has actual source code and dependencies, this section should be updated with: -- How to install dependencies (update script). -- How to run the dev server, tests, and linter. -- Any non-obvious caveats discovered during setup. +These are set in `~/.bashrc`: +``` +ANDROID_HOME=/opt/android-sdk +PATH includes: $ANDROID_HOME/cmdline-tools/latest/bin, $ANDROID_HOME/platform-tools, $ANDROID_HOME/build-tools/35.0.0, /opt/jadx/bin, ~/.local/bin +``` + +### Key Scripts + +- `scripts/extract_signature.sh ` — Extract APK signing certificate (shell) +- `scripts/extract_tiktok_signature.py ` — Full signature analysis with JSON output (Python) +- `scripts/decompile_apk.sh ` — Decompile APK to smali + Java source +- `scripts/capture_traffic.sh [port]` — Start mitmproxy for traffic capture +- `scripts/tiktok_proxy_addon.py` — mitmproxy addon that filters/logs TikTok API traffic +- `scripts/ssl_bypass_frida.js` — Frida script to bypass SSL pinning on TikTok +- `scripts/analyze_traffic.py ` — Analyze captured mitmproxy flow files + +### Caveats + +- **No KVM/hardware virtualization** in cloud VM — Android emulator cannot run with HW accel. Use a physical device or local emulator connected via `adb connect`. +- **TikTok APK not bundled** — download manually and place in `tools/apk/`. The APK cannot be auto-downloaded from CDNs. +- **SSL pinning** — TikTok uses certificate pinning. Use `scripts/ssl_bypass_frida.js` with Frida or patch `network_security_config.xml` via apktool. +- **mitmproxy CA** — for Android 7+, system CA certs require root or APK patching. Install CA from `http://mitm.it` on the device. + +### Workflow: Extract TikTok Signature + +```bash +# Place TikTok APK in tools/apk/ +python3 scripts/extract_tiktok_signature.py tools/apk/tiktok.apk +``` + +### Workflow: Capture API Traffic + +```bash +# 1. Start proxy with TikTok filter addon +mitmdump -p 8080 -s scripts/tiktok_proxy_addon.py + +# 2. On device/emulator: set proxy to :8080, install CA from http://mitm.it +# 3. Run Frida SSL bypass: +frida -U -f com.zhiliaoapp.musically -l scripts/ssl_bypass_frida.js --no-pause + +# 4. Analyze captured traffic: +python3 scripts/analyze_traffic.py tools/traffic/tiktok_api_log.jsonl +``` diff --git a/README.md b/README.md index f842714..7f504e1 100644 --- a/README.md +++ b/README.md @@ -1 +1,98 @@ -# tik-api \ No newline at end of file +# tik-api + +TikTok Android APK analysis and API traffic interception toolkit. + +## Tools + +| Tool | Purpose | +|------|---------| +| `apksigner` | APK signature verification & certificate extraction | +| `apktool` | APK decompilation (smali + resources) | +| `jadx` | APK decompilation (Java source code) | +| `mitmproxy` | HTTPS traffic interception proxy | +| `frida` | Runtime instrumentation & SSL pinning bypass | +| `adb` | Android Debug Bridge | +| `aapt` | Android Asset Packaging Tool | + +## Quick Start + +### 1. Extract APK Signature + +```bash +# Using shell script +bash scripts/extract_signature.sh path/to/tiktok.apk + +# Using Python (full analysis + JSON output) +python3 scripts/extract_tiktok_signature.py path/to/tiktok.apk +``` + +### 2. Decompile APK + +```bash +bash scripts/decompile_apk.sh path/to/tiktok.apk [output-dir] +``` + +### 3. Capture API Traffic + +```bash +# Start proxy with TikTok API filter +mitmdump -p 8080 -s scripts/tiktok_proxy_addon.py + +# On Android device: set Wi-Fi proxy to :8080 +# Install CA certificate: http://mitm.it + +# Bypass SSL pinning with Frida +frida -U -f com.zhiliaoapp.musically -l scripts/ssl_bypass_frida.js --no-pause +``` + +### 4. Analyze Captured Traffic + +```bash +python3 scripts/analyze_traffic.py tools/traffic/capture.flow +``` + +## Directory Structure + +``` +tik-api/ +├── scripts/ +│ ├── extract_signature.sh # APK signature extraction (shell) +│ ├── extract_tiktok_signature.py # APK signature extraction (Python) +│ ├── decompile_apk.sh # APK decompilation +│ ├── capture_traffic.sh # Traffic capture launcher +│ ├── tiktok_proxy_addon.py # mitmproxy TikTok filter addon +│ ├── ssl_bypass_frida.js # Frida SSL pinning bypass +│ └── analyze_traffic.py # Traffic analysis tool +├── tools/ +│ └── apk/ # Place APK files here +├── config/ # Configuration files +├── requirements.txt # Python dependencies +└── AGENTS.md # Cloud agent instructions +``` + +## Requirements + +- Java 11+ (OpenJDK 21 recommended) +- Python 3.10+ +- Android SDK (platform-tools, build-tools) + +### Install Python Dependencies + +```bash +pip3 install -r requirements.txt +``` + +## TikTok API Signature Headers + +Key headers captured by the traffic analysis tools: + +| Header | Description | +|--------|-------------| +| `X-Gorgon` | Request signature (device-generated) | +| `X-Khronos` | Timestamp component of signature | +| `X-Argus` | Enhanced request signature | +| `X-Ladon` | Additional signature parameter | +| `X-SS-STUB` | MD5 hash of POST body | +| `X-SS-REQ-TICKET` | Request timestamp ticket | +| `X-TT-Token` | Session token | +| `X-BD-Client-Key` | Client encryption key | diff --git a/config/.gitkeep b/config/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..7eac60e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +mitmproxy>=12.0.0 +frida-tools>=14.0.0 +requests>=2.31.0 diff --git a/scripts/analyze_traffic.py b/scripts/analyze_traffic.py new file mode 100644 index 0000000..93c4a94 --- /dev/null +++ b/scripts/analyze_traffic.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +""" +Analyze captured mitmproxy traffic flows for TikTok API endpoints. +Usage: python3 scripts/analyze_traffic.py + +Extracts: +- API endpoints and methods +- Request/response headers +- Request parameters and body +- Device signature parameters (X-Gorgon, X-Khronos, X-SS-STUB, etc.) +""" + +import sys +import json +from pathlib import Path + +try: + from mitmproxy.io import FlowReader + from mitmproxy.http import HTTPFlow +except ImportError: + print("Error: mitmproxy not installed. Run: pip3 install mitmproxy") + sys.exit(1) + + +TIKTOK_DOMAINS = [ + "api.tiktokv.com", + "api16-normal-c-useast1a.tiktokv.com", + "api16-normal-c-useast2a.tiktokv.com", + "api22-normal-c-useast1a.tiktokv.com", + "api22-normal-c-useast2a.tiktokv.com", + "log.tiktokv.com", + "mon.tiktokv.com", + "ib.tiktokv.com", + "webcast.tiktok.com", + "mssdk.tiktokv.com", +] + +SIGNATURE_HEADERS = [ + "x-gorgon", + "x-khronos", + "x-ss-stub", + "x-ss-req-ticket", + "x-tt-token", + "x-bd-client-key", + "x-bd-kmsv", + "x-argus", + "x-ladon", + "x-helios", + "x-tyhon", + "x-tt-dm-status", + "passport-sdk-version", +] + + +def analyze_flow(flow_file: str): + flows = [] + with open(flow_file, "rb") as f: + reader = FlowReader(f) + for flow in reader.stream(): + if isinstance(flow, HTTPFlow) and flow.response: + flows.append(flow) + + print(f"\nTotal flows captured: {len(flows)}") + + tiktok_flows = [] + for flow in flows: + host = flow.request.host + if any(domain in host for domain in TIKTOK_DOMAINS) or "tiktok" in host: + tiktok_flows.append(flow) + + print(f"TikTok API flows: {len(tiktok_flows)}\n") + + for i, flow in enumerate(tiktok_flows, 1): + req = flow.request + resp = flow.response + print(f"{'='*60}") + print(f"[{i}] {req.method} {req.url[:120]}") + print(f" Status: {resp.status_code}") + print(f" Host: {req.host}") + print(f" Path: {req.path.split('?')[0]}") + + sig_headers = {} + for h in SIGNATURE_HEADERS: + val = req.headers.get(h) + if val: + sig_headers[h] = val + + if sig_headers: + print(f" Signature Headers:") + for k, v in sig_headers.items(): + print(f" {k}: {v[:80]}{'...' if len(v) > 80 else ''}") + + if req.query: + print(f" Query params: {len(req.query)} params") + for k, v in list(req.query.items())[:10]: + print(f" {k}={v[:60]}{'...' if len(str(v)) > 60 else ''}") + + content_type = resp.headers.get("content-type", "") + if "json" in content_type and resp.content: + try: + body = json.loads(resp.content) + print(f" Response (JSON): {json.dumps(body, ensure_ascii=False)[:200]}...") + except json.JSONDecodeError: + print(f" Response: {len(resp.content)} bytes") + else: + print(f" Response: {len(resp.content)} bytes ({content_type})") + + print() + + +def main(): + if len(sys.argv) < 2: + print(f"Usage: {sys.argv[0]} ") + print(f" Example: {sys.argv[0]} tools/traffic/capture_20260317.flow") + sys.exit(1) + + flow_file = sys.argv[1] + if not Path(flow_file).exists(): + print(f"Error: File not found: {flow_file}") + sys.exit(1) + + analyze_flow(flow_file) + + +if __name__ == "__main__": + main() diff --git a/scripts/capture_traffic.sh b/scripts/capture_traffic.sh new file mode 100755 index 0000000..e5fa265 --- /dev/null +++ b/scripts/capture_traffic.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# Start mitmproxy to capture TikTok API traffic +# Usage: ./scripts/capture_traffic.sh [port] [output-file] +# +# For Android device/emulator: +# 1. Set proxy on device: : +# 2. Install mitmproxy CA: http://mitm.it +# 3. For Android 7+, you need to patch the APK's network_security_config.xml +# or use Frida to bypass SSL pinning + +set -e + +PORT="${1:-8080}" +OUTPUT="${2:-/workspace/tools/traffic/capture_$(date +%Y%m%d_%H%M%S).flow}" + +mkdir -p "$(dirname "$OUTPUT")" + +echo "============================================" +echo " mitmproxy Traffic Capture" +echo "============================================" +echo "" +echo "Listening on port: $PORT" +echo "Output file: $OUTPUT" +echo "" +echo "Configure your device/emulator proxy:" +echo " Host: $(hostname -I | awk '{print $1}')" +echo " Port: $PORT" +echo "" +echo "Install CA certificate on device: http://mitm.it" +echo "" +echo "Press Ctrl+C to stop capture." +echo "" + +mitmdump -p "$PORT" -w "$OUTPUT" \ + --set flow_detail=2 \ + --set console_eventlog_verbosity=info diff --git a/scripts/decompile_apk.sh b/scripts/decompile_apk.sh new file mode 100755 index 0000000..45d5ba5 --- /dev/null +++ b/scripts/decompile_apk.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# Decompile APK using apktool (smali) and jadx (Java source) +# Usage: ./scripts/decompile_apk.sh [output-dir] + +set -e + +APK_PATH="${1:?Usage: $0 [output-dir]}" +OUTPUT_DIR="${2:-/workspace/tools/apk/decompiled}" + +if [ ! -f "$APK_PATH" ]; then + echo "Error: APK file not found: $APK_PATH" + exit 1 +fi + +APK_NAME=$(basename "$APK_PATH" .apk) +SMALI_DIR="$OUTPUT_DIR/${APK_NAME}_smali" +JAVA_DIR="$OUTPUT_DIR/${APK_NAME}_java" + +echo "============================================" +echo " Decompiling: $(basename "$APK_PATH")" +echo "============================================" + +echo "" +echo "[1/2] apktool (smali + resources) -> $SMALI_DIR" +apktool d "$APK_PATH" -o "$SMALI_DIR" -f 2>&1 | tail -3 +echo "" + +echo "[2/2] jadx (Java source) -> $JAVA_DIR" +jadx -d "$JAVA_DIR" "$APK_PATH" --no-res 2>&1 | tail -5 +echo "" + +echo "Done. Output directories:" +echo " Smali: $SMALI_DIR" +echo " Java: $JAVA_DIR" diff --git a/scripts/extract_signature.sh b/scripts/extract_signature.sh new file mode 100755 index 0000000..da72cc0 --- /dev/null +++ b/scripts/extract_signature.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# Extract APK signing certificate, SHA-256 digest, and full certificate info +# Usage: ./scripts/extract_signature.sh + +set -e + +APK_PATH="${1:?Usage: $0 }" + +if [ ! -f "$APK_PATH" ]; then + echo "Error: APK file not found: $APK_PATH" + exit 1 +fi + +echo "============================================" +echo " APK Signature Analysis: $(basename "$APK_PATH")" +echo "============================================" +echo "" + +echo "--- apksigner verify --print-certs ---" +apksigner verify --print-certs "$APK_PATH" 2>&1 | grep -E "^Signer|certificate (DN|SHA)" || true +echo "" + +echo "--- keytool certificate details ---" +keytool -printcert -jarfile "$APK_PATH" 2>&1 +echo "" + +echo "--- Package info (aapt) ---" +aapt dump badging "$APK_PATH" 2>&1 | grep -E "^package:|^application-label:|^sdkVersion:|^targetSdkVersion:" || true +echo "" + +echo "Done." diff --git a/scripts/extract_tiktok_signature.py b/scripts/extract_tiktok_signature.py new file mode 100644 index 0000000..e288f5b --- /dev/null +++ b/scripts/extract_tiktok_signature.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 +""" +Extract TikTok APK signing certificate details. +Usage: python3 scripts/extract_tiktok_signature.py + +Extracts: +- Certificate DN, SHA-256, SHA-1, MD5 digests +- Public key info +- Signing scheme versions (v1/v2/v3) +- Package name, version code, version name +""" + +import subprocess +import sys +import json +import re +from pathlib import Path + + +def run_cmd(cmd: list[str]) -> str: + try: + result = subprocess.run(cmd, capture_output=True, text=True, timeout=30) + return result.stdout + result.stderr + except FileNotFoundError: + return f"Error: {cmd[0]} not found. Install Android SDK build-tools." + except subprocess.TimeoutExpired: + return "Error: Command timed out." + + +def extract_apksigner_info(apk_path: str) -> dict: + output = run_cmd(["apksigner", "verify", "--print-certs", "-v", apk_path]) + info = {"raw_output": output, "signers": [], "schemes": []} + + for match in re.finditer(r"Signer #(\d+) certificate DN: (.+)", output): + signer = {"id": int(match.group(1)), "dn": match.group(2)} + sha256 = re.search(rf"Signer #{signer['id']} certificate SHA-256 digest: (\w+)", output) + sha1 = re.search(rf"Signer #{signer['id']} certificate SHA-1 digest: (\w+)", output) + md5 = re.search(rf"Signer #{signer['id']} certificate MD5 digest: (\w+)", output) + if sha256: + signer["sha256"] = sha256.group(1) + if sha1: + signer["sha1"] = sha1.group(1) + if md5: + signer["md5"] = md5.group(1) + info["signers"].append(signer) + + for scheme in ["v1", "v2", "v3", "v4"]: + if f"Verified using {scheme} scheme" in output: + verified = "true" in output.split(f"Verified using {scheme} scheme")[1][:20].lower() + info["schemes"].append({"scheme": scheme, "verified": verified}) + + return info + + +def extract_aapt_info(apk_path: str) -> dict: + output = run_cmd(["aapt", "dump", "badging", apk_path]) + info = {} + + pkg_match = re.search(r"package: name='([^']+)' versionCode='([^']+)' versionName='([^']+)'", output) + if pkg_match: + info["package_name"] = pkg_match.group(1) + info["version_code"] = pkg_match.group(2) + info["version_name"] = pkg_match.group(3) + + sdk_match = re.search(r"sdkVersion:'(\d+)'", output) + if sdk_match: + info["min_sdk"] = int(sdk_match.group(1)) + + target_match = re.search(r"targetSdkVersion:'(\d+)'", output) + if target_match: + info["target_sdk"] = int(target_match.group(1)) + + label_match = re.search(r"application-label:'([^']+)'", output) + if label_match: + info["app_label"] = label_match.group(1) + + return info + + +def extract_keytool_info(apk_path: str) -> str: + return run_cmd(["keytool", "-printcert", "-jarfile", apk_path]) + + +def main(): + if len(sys.argv) < 2: + print(f"Usage: {sys.argv[0]} ") + print(f"\nExample: {sys.argv[0]} tools/apk/tiktok.apk") + sys.exit(1) + + apk_path = sys.argv[1] + if not Path(apk_path).exists(): + print(f"Error: APK not found: {apk_path}") + sys.exit(1) + + print("=" * 60) + print(f" TikTok APK Signature Analysis") + print(f" File: {apk_path}") + print("=" * 60) + + print("\n[1] Package Information (aapt)") + print("-" * 40) + aapt_info = extract_aapt_info(apk_path) + for k, v in aapt_info.items(): + print(f" {k}: {v}") + + print("\n[2] Signing Certificate (apksigner)") + print("-" * 40) + sig_info = extract_apksigner_info(apk_path) + for signer in sig_info["signers"]: + print(f" Signer #{signer['id']}:") + print(f" DN: {signer['dn']}") + print(f" SHA-256: {signer.get('sha256', 'N/A')}") + print(f" SHA-1: {signer.get('sha1', 'N/A')}") + print(f" MD5: {signer.get('md5', 'N/A')}") + for scheme in sig_info["schemes"]: + print(f" {scheme['scheme']} scheme: {'Verified' if scheme['verified'] else 'Not verified'}") + + print("\n[3] Full Certificate Details (keytool)") + print("-" * 40) + print(extract_keytool_info(apk_path)) + + output_file = Path(apk_path).with_suffix(".signature.json") + result = { + "apk_file": apk_path, + "package_info": aapt_info, + "signature": { + "signers": sig_info["signers"], + "schemes": sig_info["schemes"], + }, + } + with open(output_file, "w") as f: + json.dump(result, f, indent=2, ensure_ascii=False) + print(f"\nJSON output saved to: {output_file}") + + +if __name__ == "__main__": + main() diff --git a/scripts/ssl_bypass_frida.js b/scripts/ssl_bypass_frida.js new file mode 100644 index 0000000..0596b24 --- /dev/null +++ b/scripts/ssl_bypass_frida.js @@ -0,0 +1,59 @@ +/* + * Frida script to bypass SSL pinning on TikTok (com.zhiliaoapp.musically) + * Usage: frida -U -f com.zhiliaoapp.musically -l scripts/ssl_bypass_frida.js --no-pause + * + * This script disables TikTok's SSL certificate pinning to allow + * traffic interception via mitmproxy. + */ + +Java.perform(function () { + console.log("[*] SSL Pinning Bypass - Starting..."); + + // OkHttp3 CertificatePinner bypass + try { + var CertificatePinner = Java.use("okhttp3.CertificatePinner"); + CertificatePinner.check.overload("java.lang.String", "java.util.List").implementation = function (hostname, peerCertificates) { + console.log("[+] OkHttp3 CertificatePinner.check bypassed for: " + hostname); + }; + console.log("[*] OkHttp3 CertificatePinner bypass installed"); + } catch (e) { + console.log("[-] OkHttp3 bypass failed: " + e); + } + + // TrustManagerImpl bypass + try { + var TrustManagerImpl = Java.use("com.android.org.conscrypt.TrustManagerImpl"); + TrustManagerImpl.verifyChain.implementation = function (untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) { + console.log("[+] TrustManagerImpl.verifyChain bypassed for: " + host); + return untrustedChain; + }; + console.log("[*] TrustManagerImpl bypass installed"); + } catch (e) { + console.log("[-] TrustManagerImpl bypass failed: " + e); + } + + // SSLContext bypass + try { + var SSLContext = Java.use("javax.net.ssl.SSLContext"); + SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").implementation = function (keyManager, trustManager, secureRandom) { + console.log("[+] SSLContext.init bypassed"); + var TrustManager = Java.use("javax.net.ssl.X509TrustManager"); + var TrustAllCerts = Java.registerClass({ + name: "com.custom.TrustAllCerts", + implements: [TrustManager], + methods: { + checkClientTrusted: function (chain, authType) { }, + checkServerTrusted: function (chain, authType) { }, + getAcceptedIssuers: function () { return []; } + } + }); + var trustAllCerts = [TrustAllCerts.$new()]; + this.init(keyManager, trustAllCerts, secureRandom); + }; + console.log("[*] SSLContext bypass installed"); + } catch (e) { + console.log("[-] SSLContext bypass failed: " + e); + } + + console.log("[*] SSL Pinning Bypass - Complete"); +}); diff --git a/scripts/tiktok_proxy_addon.py b/scripts/tiktok_proxy_addon.py new file mode 100644 index 0000000..25a52df --- /dev/null +++ b/scripts/tiktok_proxy_addon.py @@ -0,0 +1,119 @@ +""" +mitmproxy addon to filter and log TikTok API traffic in real-time. +Usage: mitmdump -p 8080 -s scripts/tiktok_proxy_addon.py + +Captures TikTok API requests with their: +- Full URL, method, headers +- Device signature headers (X-Gorgon, X-Khronos, X-Argus, etc.) +- Request/response bodies (JSON) +- Writes structured logs to tools/traffic/tiktok_api_log.jsonl +""" + +import json +import os +import time +from datetime import datetime +from mitmproxy import http, ctx + +LOG_DIR = "tools/traffic" +LOG_FILE = os.path.join(LOG_DIR, "tiktok_api_log.jsonl") + +TIKTOK_HOSTS = [ + "tiktokv.com", + "tiktok.com", + "musical.ly", + "byteoversea.com", + "ibytedtos.com", + "byteimg.com", + "ibyteimg.com", + "pstatp.com", + "tiktokcdn.com", +] + +SIGNATURE_HEADERS = [ + "x-gorgon", "x-khronos", "x-ss-stub", "x-ss-req-ticket", + "x-tt-token", "x-bd-client-key", "x-bd-kmsv", + "x-argus", "x-ladon", "x-helios", "x-tyhon", + "x-tt-dm-status", "passport-sdk-version", + "x-tt-store-idc", "x-tt-store-region", + "sdk-version", "x-tt-trace-id", +] + +IMPORTANT_PARAMS = [ + "device_id", "iid", "device_type", "device_brand", + "os_version", "app_version", "version_code", + "channel", "aid", "region", "carrier_region", + "language", "locale", "ac", "os_api", +] + + +def is_tiktok(host: str) -> bool: + return any(domain in host for domain in TIKTOK_HOSTS) + + +class TikTokLogger: + def __init__(self): + os.makedirs(LOG_DIR, exist_ok=True) + self.count = 0 + + def response(self, flow: http.HTTPFlow): + if not is_tiktok(flow.request.host): + return + + self.count += 1 + req = flow.request + resp = flow.response + + entry = { + "timestamp": datetime.utcnow().isoformat(), + "index": self.count, + "method": req.method, + "url": req.url, + "host": req.host, + "path": req.path.split("?")[0], + "status_code": resp.status_code, + } + + sig = {} + for h in SIGNATURE_HEADERS: + val = req.headers.get(h) + if val: + sig[h] = val + if sig: + entry["signature_headers"] = sig + + all_headers = dict(req.headers) + entry["request_headers"] = all_headers + + params = {} + for k, v in req.query.items(): + params[k] = v + if params: + entry["query_params"] = params + device_params = {k: v for k, v in params.items() if k in IMPORTANT_PARAMS} + if device_params: + entry["device_info"] = device_params + + content_type = resp.headers.get("content-type", "") + if "json" in content_type and resp.content: + try: + entry["response_body"] = json.loads(resp.content) + except json.JSONDecodeError: + entry["response_size"] = len(resp.content) + else: + entry["response_size"] = len(resp.content) if resp.content else 0 + + entry["response_headers"] = dict(resp.headers) + + with open(LOG_FILE, "a") as f: + f.write(json.dumps(entry, ensure_ascii=False) + "\n") + + path_short = req.path.split("?")[0] + ctx.log.info( + f"[TikTok #{self.count}] {req.method} {path_short} " + f"-> {resp.status_code} " + f"({'SIGNED' if sig else 'unsigned'})" + ) + + +addons = [TikTokLogger()] diff --git a/tools/apk/.gitkeep b/tools/apk/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tools/traffic/.gitkeep b/tools/traffic/.gitkeep new file mode 100644 index 0000000..e69de29