前言

在移动应用安全评估(Penetration Testing)中,请求体加密请求头签名是阻碍自动化扫描和逻辑测试的两大拦路虎。传统的分析路径通常是“苦力活”:脱壳 -> 反编译 -> 人工阅读混淆代码 -> 扣取算法 -> 编写 Burp 插件。

随着 MCP (Model Context Protocol) 的成熟,我们有了新的解法。本文将展示如何结合 Frida 动态分析IDA Pro + MCP,在几乎不编写代码的情况下,利用 AI 全自动化还原复杂的参数拼接逻辑,并生成流量中转脚本,实现对目标 APP 防护体系的降维打击。


1. 初始侦察:加密与验签的特征识别

在对目标 APP 进行抓包分析时,我们捕获到了如下特征的 HTTP 请求:

  • Header: 包含 Sign (签名), Timestamp (时间戳), Nonce (随机数) 等字段。

  • Body: 是一串 Base64 编码的乱码,推测为 AES/DES 加密后的密文。

  • 防护机制: 修改 Body 或 Header 中的任何字符,服务器均返回 401 UnauthorizedSign Error

我们的目标是搭建一个中间人攻击(MITM)环境,使得我们在 Burp Suite Repeater 中发送明文 JSON,由中间件自动完成加密和签名,从而透明地测试业务逻辑。


2. 动态分析:Frida Hook 定位关键点

在静态分析之前,利用 Frida 进行“盲打”是效率最高的方式。我们需要定位两个关键点:

  1. 加密算法与密钥(通常是 AES)。

  2. 签名生成逻辑(通常是 MD5/SHA)。

我们使用自定义的 Frida 脚本对 Java 加密层 javax.crypto.Cipher 和摘要层 java.security.MessageDigest 进行 Hook。

2.1 Frida Hook 脚本示例

JavaScript

// crypto_monitor.js
Java.perform(function() {
    var Cipher = Java.use("javax.crypto.Cipher");
    var MessageDigest = Java.use("java.security.MessageDigest");
    var String = Java.use("java.lang.String");
    var ByteString = Java.use("com.android.okhttp.okio.ByteString"); // 用于打印 byte[]

    // Hook AES 加密
    Cipher.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec').implementation = function(opmode, key, iv) {
        if (opmode == 1) { // ENCRYPT_MODE
            console.log("\n[+] Cipher.init called (Encrypt)");
            console.log("    Key: " + ByteString.of(key.getEncoded()).hex());
            console.log("    IV:  " + ByteString.of(iv.getIV()).hex());
        }
        return this.init(opmode, key, iv);
    };

    // Hook MD5 签名 update
    MessageDigest.update.overload('[B').implementation = function(input) {
        var str = String.$new(input);
        console.log("\n[+] MessageDigest.update called");
        console.log("    Algorithm: " + this.getAlgorithm());
        console.log("    Input (Plaintext): " + str); 
        // 打印堆栈,辅助定位调用位置
        // console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
        return this.update(input);
    };
});

2.2 动态运行结果

运行脚本后,我们在 Logcat 中捕获到了关键信息:

  1. AES Key/IV: 成功获取,确认为 AES-CBC 模式。

  2. Sign 明文: 在 MessageDigest.update 中捕获到一串长字符串: appKey=android&nonce=82910&params=age=18&name=admin&role=user&timestamp=1736489000&salt=Dk28A

关键发现: 虽然我们拿到了签名前的明文,但发现其中的 params 部分(即请求体参数)与原始 JSON {"name":"admin", "age":18, "role":"user"} 的顺序不一致。原始 JSON 是无序的,但签名明文是按 Key 的字母顺序排序并拼接的。

如果手动去反编译代码确认这个排序逻辑,在重度混淆的 APP 中会非常痛苦。


3. 静态分析 2.0:IDA Pro + MCP 的自动化推理

为了确认具体的拼接逻辑(是仅排序 Key?还是递归排序?分隔符是什么?),我们将脱壳后的 APK 拖入 IDA Pro。

3.1 混淆代码的困境

定位到签名生成函数 a.b.c.d(Map map),代码经过了严重的混淆:

Java

// 反编译后的伪代码(人工阅读极其困难)
public static String a(Map<String, String> p0) {
    TreeMap v0 = new TreeMap(p0); // 1
    StringBuilder v1 = new StringBuilder();
    Iterator v2 = v0.entrySet().iterator();
    while(v2.hasNext()) {
        Map.Entry v3 = (Map.Entry)v2.next();
        if(!TextUtils.isEmpty((CharSequence)v3.getValue())) {
            v1.append((String)v3.getKey()).append("=").append((String)v3.getValue()).append("&");
        }
    }
    // ... 添加 Salt ...
    return b.md5(v1.toString());
}

虽然这段代码对于经验丰富的逆向人员来说尚可识别,但在大规模分析中效率极低。

3.2 MCP 的降维打击

我们启动 MCP Agent(连接到 IDA 的上下文),直接将反汇编代码片段“投喂”给 AI(如 Claude 3.5 Sonnet 或 GPT-4),并下达指令。

Prompt:

“分析当前函数的参数处理逻辑。这是一个生成签名的函数。请告诉我它是如何处理输入的 Map 对象的?特别是关于排序和拼接的细节。”

MCP AI Response:

通过分析代码逻辑,该函数执行了以下操作:

  1. 排序:将输入的 Map 转换为 TreeMap。在 Java 中,TreeMap 默认会根据 Key 的**自然字典序(A-Z)**进行排序。

  2. 过滤:遍历排序后的键值对,如果 Value 为空(null 或空字符串),则跳过。

  3. 拼接:使用 key=value 的格式拼接,不同键值对之间使用 & 连接。

  4. 加盐:最后拼接了固定的 Salt 值。

结论:AI 准确地提取了逻辑——TreeMap 排序 + 键值对拼接。这与我们动态分析的猜测一致,且确认了具体的过滤规则。


4. 自动化利用:Python 中间件脚本生成

搞清楚了所有逻辑(AES 密钥、参数排序规则、Salt 值),我们需要编写一个 Python 脚本作为 Burp Suite 的上游代理或插件服务。

我们继续使用 AI,将上述逻辑描述给它,让它生成一个 Flask 服务,配合 flasktestheader.py(一种常见的 Burp 联动模式)使用。

Prompt:

“写一个 Python Flask 脚本。

  1. 接收 POST JSON 请求。

  2. 将 JSON 第一层 Key 按字母顺序排序,拼接成 key=value&... 格式。

  3. 加上 Salt Dk28A 后计算 MD5,放入 Sign 头。

  4. 使用 AES-CBC (Key='...', IV='...') 加密原始 JSON,作为请求体。

  5. 转发请求给目标服务器。”

4.1 AI 生成的自动化脚本 (Artifact)

Python

import hashlib
import json
import time
import requests
from flask import Flask, request, jsonify
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import base64

app = Flask(__name__)

# 配置信息 (从 Frida Hook 中获取)
AES_KEY = b'1234567890123456' # 示例 Key
AES_IV  = b'1234567890123456' # 示例 IV
SALT    = "Dk28A"
TARGET_URL = "https://api.target-app.com/v1/verify"

def calculate_sign(data_dict):
    """
    AI 分析得出的签名逻辑:TreeMap排序 + 拼接
    """
    # 1. 字典序排序
    sorted_keys = sorted(data_dict.keys())
    
    # 2. 拼接字符串
    sign_str = ""
    for k in sorted_keys:
        val = data_dict[k]
        if val: # 过滤空值
            sign_str += f"{k}={val}&"
            
    # 3. 加盐
    sign_str += f"salt={SALT}"
    
    # 4. MD5
    return hashlib.md5(sign_str.encode('utf-8')).hexdigest()

def aes_encrypt(plaintext):
    """
    AES-CBC-PKCS7 加密
    """
    cipher = AES.new(AES_KEY, AES.MODE_CBC, AES_IV)
    padded_text = pad(plaintext.encode('utf-8'), AES.block_size)
    encrypted = cipher.encrypt(padded_text)
    return base64.b64encode(encrypted).decode('utf-8')

@app.route('/encrypt_proxy', methods=['POST'])
def proxy_request():
    try:
        # 1. 获取 Burp 发来的明文 JSON
        payload = request.json
        
        # 2. 计算签名
        # 注意:通常签名包含时间戳,这里模拟添加
        payload['timestamp'] = str(int(time.time()))
        sign = calculate_sign(payload)
        
        # 3. 加密 Body
        # 注意:通常签名时用的是明文参数,但发包时 Body 是加密的
        # 这里的 payload 需要转为字符串后加密
        encrypted_body = aes_encrypt(json.dumps(payload))
        
        # 4. 构造最终请求头
        headers = {
            "Sign": sign,
            "Timestamp": payload['timestamp'],
            "Content-Type": "application/json"
        }
        
        # 5. (可选) 直接发包或返回给 Burp 插件处理
        # 这里演示直接发包
        # r = requests.post(TARGET_URL, data=encrypted_body, headers=headers)
        # return r.text, r.status_code
        
        # 返回加密后的数据给 Burp 插件 (AutoRepeater 等)
        return jsonify({
            "encrypted_body": encrypted_body,
            "sign": sign,
            "timestamp": payload['timestamp']
        })

    except Exception as e:
        return str(e), 500

if __name__ == '__main__':
    app.run(port=8888)

5. 最终效果与 Burp 联动

配置流程如下:

  1. Burp Suite: 配置插件(如 BurpCrypto 或自定义 Python 插件),将 Repeater 的流量 hook 到 http://localhost:8888/encrypt_proxy

  2. 操作: 在 Repeater 中修改明文 JSON {"role": "admin"}

  3. 结果:

    • 插件自动调用 Flask 服务。

    • Flask 服务完成排序、签名、加密。

    • 最终发出的请求带有合法的 Sign 和加密的 Body。

    • 服务端返回 200 OK,业务逻辑验证通过。

6. 总结

本次实战展示了现代逆向工程的高效范式:

  1. Hooker/Frida: 快速获取加密参数(Key, IV, Input/Output),做“黑盒”验证。

  2. IDA + MCP: 利用 AI 阅读混淆代码,快速“白盒”确认逻辑细节(如排序规则),替代人工反混淆。

  3. LLM 代码生成: 自动编写胶水代码,连接测试工具与目标 APP。

通过这种 "Frida 定点 + MCP 辅助 + AI 编码" 的组合拳,我们成功在不编写一行复杂业务代码的情况下,实现了对高防护 APP 的全自动化绕过。这不仅极大地降低了逆向门槛,也为安全测试人员节省了宝贵的时间。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐