从对抗到出洞:某金融APP 实战渗透与 Frida 反检测绕过(Rpc + Flask + AutoDecoder)
字数 4054 2025-10-18 11:17:50
某金融APP通信协议逆向分析与自动化加解密实战教学文档
文档概述
本教学文档基于一篇实战技术文章,详细讲解了如何对一个采取了高强度加密和反调试措施的金融类Android APP进行渗透测试。核心挑战在于破解其自定义的通信加密协议,并绕过其基于Frida的检测机制。最终目标是实现一个自动化的加解密服务(RPC Server),便于安全研究人员进行接口重放、漏洞挖掘和安全性评估。
技术栈关键词: Android逆向、Frida、RPC、Flask、AES、HMAC、TLS握手、反调试绕过
第一章:挑战与目标
1.1 面临的挑战
- 反Frida检测: APP内置了检测机制,当Frida注入后,一旦调用
Java.use等关键API,应用会立即闪退。 - 复杂的通信加密: 请求和响应体均为加密的JSON格式,加密算法位于Native层(C/C++),而非Java层,增加了分析难度。
- 动态密钥: 加密所用的密钥(AES密钥、IV等)并非硬编码在代码中,而是通过一个类似TLS握手的过程动态生成,每次启动APP都会变化。
1.2 渗透目标
- 绕过反Frida检测,成功将Frida注入到目标APP中。
- 逆向分析出完整的通信数据加解密流程。
- 定位并提取动态生成的加密密钥。
- 构建一个自动化RPC服务,提供加密和解密接口,支持对抓取的数据包进行解密分析和对修改后的数据进行加密重放。
第二章:技术分析与破解流程
2.1 阶段一:绕过Frida反调试检测
问题现象: Frida可以注入,但执行Java.use()时APP崩溃。
解决方案(二选一):
方案A:编译Frida Java Bridge
- 原理: 官方的Frida
Java.use等API特征明显,容易被检测。通过编译Frida的frida-java-bridge模块,可以改变这些API的底层实现方式,从而绕过基于特征码的检测。 - 操作步骤:
- 参考提供的视频教程,搭建Node.js环境。
- 使用
frida-compile工具将自定义的Agent脚本(包含对Java层的操作)编译成一个单独的、优化过的_agent.js文件。 - 注入时,直接加载这个编译后的
_agent.js文件,而非原始的、特征明显的Frida API脚本。
方案B:使用ZygiskFrida
- 原理: 这是一个开源项目,它将Frida集成到Magisk的Zygisk模块中。它通过修改Android系统底层(Zygote进程)的加载方式,实现了更深层次的注入和更隐蔽的存在,从而有效规避了常规的Frida检测。
- 操作步骤:
- 确保手机已解锁Bootloader并刷入Magisk(支持Zygisk)。
- 安装
ZygiskFrida模块并重启手机。 - 之后即可像正常使用Frida一样进行注入,检测功能已被绕过。
推荐: 对于新手,方案B(ZygiskFrida) 更为简单可靠。
2.2 阶段二:逆向分析加密流程
1. 抓包初步分析
- 请求/响应体格式:
{"key": "Base64EncodedEncryptedData"} - 关键请求头:
X-Emp-Signature,用于数据签名验证。
2. 定位签名算法(突破口)
- 使用逆向工具(如Jadx-GUI)反编译APP,全局搜索字符串
X-Emp-Signature。 - 定位到
initHttpRequest方法,发现其调用了encryptHMAC方法。 - 分析
encryptHMAC,确认其使用HmacSha1(或可配置的国密SM3)算法对数据进行签名。
3. 定位请求体加密入口
- 由于加密可能在Native层,直接阅读Java代码逻辑更高效。在Jadx中查找
initHttpRequest方法的调用者,向上追溯。 - 最终定位到
sendRequest方法,其中调用了handleRequestBody方法来处理请求体加密。 - 关键代码逻辑分析(
handleRequestBody):byte[] rnc = new byte[16];...new SecureRandom().nextBytes(rnc);生成一个16字节的随机数(RNC)。- 将RNC与原始的JSON请求体字节数组合并(
RNC + RequestBody)。 - 调用
AESCipher.encrypt(rncAndBody)对合并后的数据进行AES加密。 - 将加密结果与一个序列号(
mSequence)拼接,然后计算HMAC签名。 - 最终结构为:
HMAC签名 || 序列号 || AES加密结果。 - 将整个结果进行Base64编码,并封装成
{"key": "base64String"}的格式。
4. 解密流程推理
响应体的解密是加密的逆过程:
- Base64解码响应体。
- 分离出HMAC签名(用于验证数据完整性)、序列号和AES加密数据。
- 使用正确的AES密钥和IV解密数据。
- 解密后的数据前16字节是RNC,后面才是真正的JSON响应体。
2.3 阶段三:追踪动态密钥生成
核心问题: AES加密所需的clientKey_和clientIv_是动态的。
1. 密钥生成点定位
- 在Jadx中追踪
AESCipher类,发现其clientKey_和clientIv_的赋值在一个initSecret方法中。 initSecret方法的核心是这行代码:byte[] allSecret = PRFCipher.PRF(ms2, HMac.TLS_MD_CLIENT_SERVER_KEYIVMAC_CONST(), ms2RncRnsSeed, R2.attr.arrowHeadLength);- 这行代码是标准的TLS-PRF(Pseudo-Random Function) 实现,用于从主密钥(
ms2)和随机数种子(ms2RncRnsSeed)派生出会话所需的多个密钥。
2. 逆向TLS握手流程
- 继续追溯
ms2(主密钥)的来源,最终定位到handleServerKeyExchange方法。该方法处理服务器发送的密钥交换信息(如RSA公钥加密的预主密钥)。 - 进一步向上追溯,发现一个关键的HTTP请求(例如
/api/facility/serverHello),该请求的响应数据被用于计算主密钥ms2。 - 结论: APP启动时,会与服务器进行一次自定义的、简化版的TLS握手。通过这次握手,客户端和服务器协商出共享的主密钥
ms2,然后双方使用相同的PRF函数,派生出完全一致的AES密钥、IV和HMAC密钥。
2.4 阶段四:Frida RPC实现自动化
由于密钥动态生成,硬编码密钥到Python脚本是不可行的。因此,采用Frida RPC(Remote Procedure Call)技术,在APP内存中直接调用其Java加密/解密方法。
1. Frida脚本核心任务
编写一个Frida脚本(agent.js),暴露(export)几个RPC函数给外部调用:
getKeys(): 获取当前会话的动态密钥(clientKey,clientIv,serverKey,serverIv,hmacKey)。encryptData(data): 接收明文字符串,调用APP自身的handleRequestBody逻辑,返回加密后的Base64字符串。decryptData(encryptedBase64): 接收加密的Base64字符串,调用APP自身的解密逻辑,返回明文字符串。
示例代码片段(概念性):
Java.perform(function () {
var AESCipher = Java.use("com.xxx.AESCipher");
var CryptoManager = Java.use("com.xxx.CryptoManager");
// RPC函数:获取密钥
rpc.exports.getKeys = function () {
return {
clientKey: Array.from(AESCipher.clientKey_.value),
clientIv: Array.from(AESCipher.clientIv_.value),
// ... 其他密钥
};
};
// RPC函数:加密
rpc.exports.encryptData = function (plainText) {
var result = CryptoManager.handleRequestBody(plainText);
return result;
};
});
2. Python端与Flask服务器
- 使用Python的
frida库连接到设备上的APP进程,并加载上述agent.js脚本。 - 利用
flask框架创建一个轻量级的Web服务器。 - 为每个RPC函数创建一个对应的API端点(endpoint)。
示例Flask端点:
import frida
from flask import Flask, request, jsonify
app = Flask(__name__)
session = None
script = None
@app.route('/encrypt', methods=['POST'])
def encrypt():
data = request.json.get('data')
encrypted_data = script.exports.encrypt(data)
return jsonify({'encrypted_data': encrypted_data})
@app.route('/decrypt', methods=['POST'])
def decrypt():
encrypted_data = request.json.get('data')
decrypted_data = script.exports.decrypt(encrypted_data)
return jsonify({'decrypted_data': decrypted_data})
if __name__ == '__main__':
# 连接Frida并加载脚本的代码...
device = frida.get_usb_device()
pid = device.spawn(["com.xxx.financialapp"])
session = device.attach(pid)
with open("agent.js", "r") as f:
script_code = f.read()
script = session.create_script(script_code)
script.load()
device.resume(pid)
app.run(host='0.0.0.0', port=5000)
第三章:实战操作流程总结
- 环境准备: 安装Frida、Python、ADB。Root手机并配置ZygiskFrida以绕过检测。
- 逆向分析: 使用Jadx静态分析APP,理清
handleRequestBody、AESCipher、PRFCipher等关键类的调用关系。 - 编写Frida RPC脚本: 根据分析结果,编写
agent.js,暴露getKeys,encryptData,decryptData等RPC函数。 - 部署Flask RPC服务器: 编写Python脚本,连接Frida,加载JS脚本,并启动Flask服务。
- 渗透测试:
- 抓包: 使用Burp Suite或Charles抓取APP流量,得到加密的数据包。
- 解密: 将抓到的加密数据(
{"key": "..."}中的值)发送到本地Flask服务器的/decrypt接口,即可获得明文。 - 分析/修改: 分析明文JSON,发现潜在漏洞(如参数污染、越权等)。
- 加密重放: 将修改后的明文发送到
/encrypt接口,获得新的加密数据,替换原数据包中的内容,进行重放攻击。
第四章:核心知识点与工具清单
- 核心知识点:
- Frida动态插桩与反检测绕过
- Android Java层与Native层逆向分析
- TLS握手与密钥交换协议(PRF)
- AES对称加密与HMAC签名
- Frida RPC机制
- Flask Web框架
- 工具清单:
- 逆向分析: Jadx-GUI
- 动态调试: Frida, ZygiskFrida
- 抓包工具: Burp Suite, Charles
- 开发环境: Python 3, Flask库, frida-tools
- Android环境: 已Root的Android手机/模拟器
通过以上步骤,您可以系统地完成对该金融APP的通信协议破解,并建立起一个强大的自动化测试平台。请注意,本文档仅用于安全研究和学习目的,请勿用于非法活动。