#!/usr/bin/env bash
#
# inject_frida_gadget.sh — 免 Root Frida Gadget 注入微信 APK
#
# 用法:
#   ./inject_frida_gadget.sh <wechat.apk> [output_dir]
#
# 前置:
#   brew install apktool      # APK 拆包/打包
#   pip install lief           # ELF 注入
#   需有 Java keytool + jarsigner（macOS 自带）
#
# 流程:
#   1. apktool 解包微信 APK
#   2. 下载对应架构的 frida-gadget.so
#   3. 用 LIEF 将 gadget 注入微信的 native library
#   4. 写入 gadget 配置（listen 0.0.0.0 随机端口）
#   5. apktool 重新打包
#   6. 签名（debug 签名即可）
#   7. 输出可安装的 APK
#
set -euo pipefail

FRIDA_VERSION="${FRIDA_VERSION:-16.5.9}"
ARCH="${ARCH:-arm64}"
LISTEN_HOST="${LISTEN_HOST:-0.0.0.0}"
LISTEN_PORT="${LISTEN_PORT:-0}"

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

log()  { echo -e "${GREEN}[+]${NC} $*"; }
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
die()  { echo -e "${RED}[✗]${NC} $*" >&2; exit 1; }

APK_PATH="${1:-}"
OUTPUT_DIR="${2:-./output}"
[ -z "$APK_PATH" ] && die "用法: $0 <wechat.apk> [output_dir]"
[ -f "$APK_PATH" ] || die "APK 不存在: $APK_PATH"

command -v apktool >/dev/null || die "apktool 未安装：brew install apktool"
command -v python3 >/dev/null || die "python3 未安装"
python3 -c "import lief" 2>/dev/null || die "lief 未安装：pip install lief"

WORK_DIR=$(mktemp -d)
trap "rm -rf '$WORK_DIR'" EXIT

log "工作目录: $WORK_DIR"
log "APK: $APK_PATH"

# ── Step 1: apktool 解包 ──
log "Step 1/7: 解包 APK..."
apktool d -f -o "$WORK_DIR/unpacked" "$APK_PATH" --no-res 2>/dev/null || \
    apktool d -f -o "$WORK_DIR/unpacked" "$APK_PATH"
log "解包完成"

# ── Step 2: 下载 frida-gadget ──
GADGET_ARCH_MAP_arm64="arm64"
GADGET_ARCH_MAP_arm="arm"
GADGET_ARCH_MAP_x86="x86"
GADGET_ARCH_MAP_x86_64="x86_64"

GADGET_URL="https://github.com/AKA-L/frida-gadget-fix/releases/download/${FRIDA_VERSION}/frida-gadget-${FRIDA_VERSION}-android-${ARCH}.so.xz"
GADGET_FILE="$WORK_DIR/frida-gadget-${ARCH}.so"

if [ -f "$GADGET_FILE" ]; then
    log "Step 2/7: Gadget 已存在，跳过下载"
else
    log "Step 2/7: 下载 frida-gadget ${FRIDA_VERSION} (${ARCH})..."
    curl -sL "$GADGET_URL" -o "$WORK_DIR/gadget.so.xz" || {
        GADGET_URL="https://github.com/AKA-L/frida-gadget-fix/releases/download/${FRIDA_VERSION}/frida-gadget-${FRIDA_VERSION}-android-${ARCH}.so.xz"
        warn "主源失败，尝试备用源..."
        curl -sL "$GADGET_URL" -o "$WORK_DIR/gadget.so.xz" || die "下载 frida-gadget 失败"
    }
    xz -d "$WORK_DIR/gadget.so.xz"
    mv "$WORK_DIR/gadget.so" "$GADGET_FILE"
    log "下载完成: $(du -h "$GADGET_FILE" | cut -f1)"
fi

# ── Step 3: LIEF 注入 gadget ──
LIB_DIR="$WORK_DIR/unpacked/lib/${ARCH}"
[ -d "$LIB_DIR" ] || {
    if [ "$ARCH" = "arm64" ] && [ -d "$WORK_DIR/unpacked/lib/arm64-v8a" ]; then
        LIB_DIR="$WORK_DIR/unpacked/lib/arm64-v8a"
    elif [ "$ARCH" = "arm" ] && [ -d "$WORK_DIR/unpacked/lib/armeabi-v7a" ]; then
        LIB_DIR="$WORK_DIR/unpacked/lib/armeabi-v7a"
    else
        die "未找到 lib/${ARCH} 目录，当前有: $(ls "$WORK_DIR/unpacked/lib/" 2>/dev/null || echo '无')"
    fi
}

log "Step 3/7: LIEF 注入 Gadget..."

TARGET_SO=$(find "$LIB_DIR" -name "*.so" -not -name "libfrida*" -not -name "libgadget*" | head -1)
[ -z "$TARGET_SO" ] && die "未找到可注入的 .so 文件"
TARGET_NAME=$(basename "$TARGET_SO")
log "  目标 so: $TARGET_NAME"

cp "$GADGET_FILE" "$LIB_DIR/libgadget.so"

python3 << PYEOF
import lief
import sys

target_path = "$TARGET_SO"
binary = lief.parse(target_path)
if binary is None:
    print("LIEF 解析失败: " + target_path)
    sys.exit(1)

existing = [str(lib) for lib in binary.libraries]
if "libgadget.so" in existing:
    print("libgadget.so 已存在于 NEEDED 中，跳过")
else:
    binary.add_library("libgadget.so")
    print("已添加 libgadget.so 到 NEEDED")

binary.write(target_path)
print("LIEF 注入完成: " + target_path)
PYEOF

log "  LIEF 注入完成"

# ── Step 4: 写入 gadget 配置 ──
if [ "$LISTEN_PORT" = "0" ]; then
    LISTEN_PORT=$((RANDOM % 50000 + 10000))
fi

GADGET_CONFIG='{
  "interaction": {
    "type": "listen",
    "address": "'"$LISTEN_HOST"'",
    "port": '"$LISTEN_PORT"',
    "on_port_conflict": "pick-next",
    "on_load": "resume"
  }
}'

CONFIG_SO_NAME="libgadget.config.so"
echo "$GADGET_CONFIG" > "$LIB_DIR/$CONFIG_SO_NAME"
log "Step 4/7: Gadget 配置写入 (port=$LISTEN_PORT)"
log "  配置: $GADGET_CONFIG"

# ── Step 5: apktool 重新打包 ──
log "Step 5/7: 重新打包..."
OUTPUT_UNSIGNED="$WORK_DIR/wechat-gadget-unsigned.apk"
apktool b -o "$OUTPUT_UNSIGNED" "$WORK_DIR/unpacked" 2>/dev/null || \
    apktool b -o "$OUTPUT_UNSIGNED" "$WORK_DIR/unpacked"
log "打包完成"

# ── Step 6: 签名 ──
log "Step 6/7: 签名..."
KEYSTORE="$WORK_DIR/debug.keystore"
keytool -genkey -v -keystore "$KEYSTORE" -alias debug \
    -keyalg RSA -keysize 2048 -validity 10000 \
    -storepass android -keypass android \
    -dname "CN=Debug,OU=Debug,O=Debug,L=Debug,S=Debug,C=CN" \
    2>/dev/null

OUTPUT_SIGNED="$WORK_DIR/wechat-gadget-signed.apk"
jarsigner -verbose -sigalg SHA256withRSA -digestalg SHA-256 \
    -keystore "$KEYSTORE" -storepass android -keypass android \
    -signedjar "$OUTPUT_SIGNED" "$OUTPUT_UNSIGNED" debug \
    2>/dev/null
log "签名完成"

# ── Step 7: 输出 ──
mkdir -p "$OUTPUT_DIR"
FINAL_APK="$OUTPUT_DIR/wechat-frida-gadget.apk"
cp "$OUTPUT_SIGNED" "$FINAL_APK"

log "Step 7/7: 输出完成"
echo ""
echo "============================================"
echo "  免 Root Frida Gadget 注入完成"
echo "============================================"
echo "  输出 APK: $FINAL_APK"
echo "  大小: $(du -h "$FINAL_APK" | cut -f1)"
echo "  Gadget 端口: $LISTEN_PORT"
echo "  Gadget 地址: $LISTEN_HOST:$LISTEN_PORT"
echo ""
echo "  安装到设备:"
echo "    adb install -r '$FINAL_APK'"
echo ""
echo "  连接 Frida (本机):"
echo "    adb forward tcp:$LISTEN_PORT tcp:$LISTEN_PORT"
echo "    frida -H 127.0.0.1:$LISTEN_PORT Gadget"
echo ""
echo "  连接 Frida (远程/WiFi):"
echo "    frida -H <设备IP>:$LISTEN_PORT Gadget"
echo ""
echo "  SDK 配置 (FridaManager):"
echo "    mode='gadget', gadget_port=$LISTEN_PORT"
echo "============================================"
