#!/usr/bin/env bash # # inject_frida_gadget.sh — 免 Root Frida Gadget 注入微信 APK # # 用法: # ./inject_frida_gadget.sh [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 [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 "============================================"