某音乐 App 逆向(二):calc签名算法深度逆向分析
免责声明:本文所有分析内容仅供网络安全学习与研究,所有技术均已向相关厂商提交安全报告。请勿将本文技术用于未经授权的商业环境,一切后果自负。
摘要
本文记录了一次由 Claude Code(Anthropic)全程辅助完成的 Android 原生签名算法逆向分析。面对 tmesec_llvm 定制编译器产生的 CFF(控制流扁平化)混淆、Crypto 全量内联以及主动反 Frida 检测,人机协作完成了 14 个版本的 Hook 脚本迭代、22 组数学差分实验,最终通过”搭载实验”+ AttachCurrentThread 两项核心技术实现了 3ms/call 的高性能签名桥接,并用差分分析数学证明了该哈希函数为无法离线复现的自定义非线性函数。本文同时记录了大模型在真实移动安全研究中的具体协作方式与能力边界。
一、分析背景与目标
1.1 前序工作
在前期分析中,我们已完成:
-
请求/响应编解码:M-Encoding m1 = Deflate 压缩 + 5 字节随机前缀(已完全还原) -
签名调用链定位: CgiRequest.g0()→SignRequestHelper.c()→MERJni.calc() -
calc I/O 格式确认: calc(content, params)返回"sign_b64 mask_b64"两段 Base64
1.2 本次分析目标
深入逆向 libmer.so 中 calc() 的签名算法,目标如下:
-
完全还原 sign 和 mask 的生成算法 -
实现 Python 版本,脱离设备独立计算签名 -
如果无法完全还原,构建可靠的 Frida 桥接方案
1.3 大模型辅助工作范式
本次分析的一个显著特点是:全程由 Claude Code 协同完成,而非传统的”人工逐行阅读反汇编”。
人机协作分工
|
|
|
|---|---|
| 人(研究员) |
|
| Claude Code |
|
Claude Code 在本次分析中的具体贡献
1. 静态分析加速:研究员将 radare2 的原始输出(函数列表、基本块统计、交叉引用)直接贴入对话,Claude Code 即刻识别出 CFF 混淆特征、定位 36KB 核心函数,并给出了后续动态分析的优先级建议。
2. Frida 脚本批量生成与迭代:14 个版本的 Hook 脚本均由 Claude Code 生成。每次脚本失败后,研究员将设备端的错误日志和崩溃信息反馈给 Claude Code,后者分析根本原因并直接输出修复版本,形成闭环迭代。平均每轮迭代耗时不超过 5 分钟。
**3. 关键技术突破的”灵感来源”**:
-
搭载技术(Piggyback):在前 4 个版本因 JNI 线程限制持续崩溃后,Claude Code 提出”不要主动调用,搭载真实调用的便车”这一思路,直接打开了受控实验的大门 -
AttachCurrentThread 方案:Claude Code 查阅 JNI 规范后给出了通过 vm->AttachCurrentThread()为任意线程合法创建 JNIEnv 的实现,解决了桥接方案中最核心的线程绑定问题 -
差分分析框架:22 组实验的设计(单字节、位置测试、跨长度核比较)由 Claude Code 系统规划,目标是最大化对算法结构的信息增益
4. JNI vtable 调试:vtable 索引错误(211 → 208)也是 Claude Code 通过分析 JNI 规范和 arm64 指针布局,精确定位并修正的。
5. 本文档撰写:包括技术分析、架构图设计(SVG)、公众号格式化,均在 Claude Code 辅助下完成。
能力边界的客观认识
当然,大模型并非万能。本次分析中 Claude Code 无法替代人工完成的环节包括:
-
在真实设备上执行命令并获取运行时数据 -
判断哪些实验现象属于”值得深入”的异常(需要经验直觉) -
最终的研究伦理与方向把控
这种**”人负责执行与判断,AI 负责生成与推理”的协作模式**,使得整个分析在两天内完成——而同等深度的传统分析通常需要数周。
1.4 分析工作流总览
整个分析分为四个阶段:静态分析、动态追踪、算法分析、桥接方案构建。

二、静态分析:libmer.so 深度解剖
2.1 二进制概览
使用 radare2 对目标库进行静态分析:
# 查看文件基本信息r2 -q -c "iI" libs/lib/arm64-v8a/libmer.so
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
值得注意的是:该 SO 由目标厂商安全团队定制的 LLVM 编译器(tmesec_llvm)编译,在标准编译优化之上叠加了深度代码混淆。这意味着用 IDA Pro、Ghidra 等常规工具打开后,反编译结果会异常混乱,大量逻辑无法正常还原为可读代码。
2.2 calc 函数定位
calc 并非通过标准 JNI 静态方法命名导出(如 Java_com_xxx_MERJni_calc),而是通过 RegisterNatives动态注册——这是一种常见的反 Hook 手段,能让逆向者难以通过符号名直接定位函数地址。

通过 Hook RegisterNatives 本身,可以在 App 启动时动态捕获 calc 的函数指针:
// 拦截 JNI RegisterNatives, 捕获 calc 的运行时地址Interceptor.attach(registerNativesAddr, {onEnter: function(args) {var nMethods = args[3].toInt32();var methods = args[2];for (var i = 0; i < nMethods; i++) {var name = methods.add(i * 24).readPointer().readUtf8String();if (name === "calc") {// 每次运行地址因 ASLR 不同, 但相对 SO 基址的偏移固定 calcFnPtr = methods.add(i * 24 + 16).readPointer(); } } }});
2.3 控制流扁平化(CFF)混淆
calc 函数被 tmesec_llvm 编译器施加了控制流扁平化(Control Flow Flattening)混淆。其核心思想是:将原本有清晰顺序的代码块,全部打散成一个巨大的 switch-case 结构,通过一个”分发变量”控制执行流向,使静态分析工具无法还原原始逻辑。

calc 函数混淆统计:
|
|
|
|---|---|
|
|
0x6e098
|
|
|
|
|
|
580 个 |
|
|
|
|
|
|
580 个基本块对于一个单一函数而言是极高的——正常编写的同等逻辑函数通常只有几十个基本块。这使得 IDA 的 F5 反编译功能在此完全失效。
2.4 调用图分析
通过 radare2 的调用图分析,calc 函数共直接调用 5 个子函数:
|
|
|
|
|---|---|---|
fcn.0x6dd08 |
|
|
fcn.0x13bfd0 |
|
|
fcn.0x8bbb8 |
36.8 KB ★ | 核心计算(主要目标) |
fcn.0x709a4 |
|
|
fcn.0x71f50 |
|
|
核心发现:fcn.0x8bbb8 是整个签名算法的心脏,包含 579 个基本块,大小达 36KB,是我们后续分析的主要目标。
2.5 加密常量搜索
# 搜索 SHA1/MD5 初始化常量, 判断使用了哪些标准算法r2 -q -c "/x 67452301; /x efcdab89; /x 98badcfe; /x 10325476" libmer.so
发现情况:
|
|
|
|
|---|---|---|
0x67452301 |
|
|
0xEFCDAB89 |
|
|
0x98BADCFE |
|
|
0x10325476 |
|
|
0xC3D2E1F0 |
|
|
0x5A827999 |
|
|
0xD76AA478 |
|
|
结论:库中存在 MD5 初始化常量,但 SHA1 常量和 MD5 轮常量缺失。轮常量在运行时由混淆代码动态构造,防止静态提取——这是 tmesec_llvm 的一项关键反提取策略。
三、动态分析:Frida Hook 14 轮迭代
3.1 分析策略
面对高度混淆的目标,我们采用了四种互补的动态分析策略:

-
黑盒(纯 I/O):只 Hook calc 入口/出口,记录输入→输出映射。优点:零干扰;缺点:看不到内部结构 -
灰盒(libc 追踪):Hook memcpy、sprintf等 libc 函数,追踪内部数据流。优点:可见数据流;缺点:噪声和性能问题 -
白盒(标准 API):Hook OpenSSL 导出的 SHA1、HMAC、RSA、EVP_*、MD5_*等函数。结果:全部未命中(Crypto 被完全内联) -
受控实验:精心设计测试用例替换 calc 输入,观察输出变化规律。优点:最高信息量,是最终突破方案
3.2 Hook 脚本迭代历程
我们共开发了 14 个版本的 Hook 脚本,每个版本解决一个特定问题:

以下是关键节点:
v1(trace_calc_crypto.js):Hook 所有 OpenSSL 导出 API → 全部未触发。发现:calc 不调用任何标准 OpenSSL API,所有 Crypto 操作被完全内联进函数体。
v2/v3(trace_calc_sign_mask.js / trace_calc_v3.js):转向 Hook memcpy/memmove 追踪数据流 → 被 /proc/self/maps 读取产生的数百个噪声 memcpy 淹没,实际有效数据难以提取。
v5(trace_calc_io.js,突破!):放弃所有内部 Hook,只捕获 calc 入口/出口。成功捕获 5 次调用的完整 I/O,确认输出格式:sign_raw = 32B(12B prefix + 20B hash),mask_raw = 104B。
💡 Claude Code 分析:在 v4 连续失败后,Claude Code 建议”内部 Hook 方案已触碰性能天花板,应当彻底转向黑盒 I/O 策略——calc 的入口和出口是我们真正需要的全部信息”。这一判断成为整个分析的转折点。
v8(trace_calc_piggyback.js,关键突破!):搭载技术——在真实 calc 调用上替换输入数据,绕过 JNI 线程限制。12 组受控实验全部成功,发现了 hash 仅依赖 content 的关键特性。
💡 Claude Code 提出的核心思路:面对 v6/v7 因 JNI 线程绑定接连崩溃,Claude Code 给出了”搭载(Piggyback)”方案——”我们不需要主动创建调用环境,只需拦截 App 自身发起的调用,在
onEnter时悄悄替换参数,借用它的合法 env 和 jclass。” 这是整个研究中最具创造性的一步。
v9(trace_calc_experiment3.js,数学逆向):22 组精心设计的二进制实验,通过差分分析,数学证明了 hash 为自定义非线性函数,无法通过 I/O 推导还原。
💡 Claude Code 设计的实验矩阵:22 组测试用例由 Claude Code 系统规划,覆盖”单字节遍历 → 位置独立性检验 → 跨长度核比较 → ASCII 递增长度 → 边界值”五个维度,目标是以最少的设备交互次数,获得对算法结构的最大信息增益。
3.3 四大关键问题与解决
问题 1:OpenSSL API 全部未命中
calc 不通过 PLT(过程链接表)调用任何导出的 Crypto 符号。tmesec_llvm 将所有 SHA/MD5/HMAC 操作完全内联进 calc 函数体。
证据:在 memcpy 追踪中能看到 MD5 的 padding 字节(0x80 00 00...),证明 MD5 确实在执行,只是不走导出 API。
应对:放弃 Hook 标准 API 路线,改为追踪数据流和直接 I/O 分析。
问题 2:/proc/self/maps 噪声洪泛
# calc 内部会读取 /proc/self/maps 做反篡改检测:# 73d99c6000-73d9aa0000 r--p ... /data/.../libmer.so# 73d9aa0000-73d9c2a000 r-xp ... /data/.../libmer.so# (数百行, 每行产生 105B / 140B 的 memcpy 调用)
这是 libmer.so 的反篡改检测机制——通过解析 /proc/self/maps 确认自身未被修改或注入。其副作用是在 memcpy Hook 时产生大量噪声数据。
应对:通过偏移过滤(offset 0xa7920)识别噪声来源,最终选择直接去掉 memcpy Hook,采用纯 I/O 方案。
问题 3:JNIEnv 线程局部限制
这是最关键的技术难题。JNIEnv 与线程绑定,无法跨线程传递。从 Frida JS 线程直接调用 native calc 函数会遇到三种情况:
|
|
|
|
|
|---|---|---|---|
|
|
NativeFunction
|
|
|
|
|
Java.perform()
MERJni.calc() |
|
|
|
|
|
|
|
|
|
|
|
|
方案 C(搭载技术 Piggyback) 是重要中间突破——通过拦截 App 发出的真实 calc 调用,在 onEnter 时替换 args[2](content)和 args[3](params)为我们的测试数据,同时利用 App 提供的合法 JNI 环境执行签名。

方案 D(AttachCurrentThread) 是最终生产方案:首次 calc 调用时捕获 JavaVM 指针和 jclass 的 GlobalRef,后续 RPC 调用时通过 vm->AttachCurrentThread() 为当前线程创建合法的 JNIEnv,然后直接调用 calcFn。
问题 4:JNI vtable 偏移错误
JNI 函数通过 vtable 间接调用。在 arm64 环境下,每个指针占 8 字节,函数索引需精确:
|
|
|
|
|---|---|---|
|
|
NewByteArray |
|
|
|
GetByteArrayRegion |
|
| 208 | SetByteArrayRegion ✅ |
1664 |
|
|
SetIntArrayRegion
|
|
|
|
GetJavaVM |
|
初始版本误用了 index 211(SetIntArrayRegion),导致 JNI 类型检查失败并 abort。修正为 208 后解决。
四、受控实验:calc 算法特性验证
4.1 搭载实验设计(12 组)
使用上一节的搭载技术,设计了 12 组对照实验,分三组变量进行隔离测试:
|
|
|
|
|
|
|---|---|---|---|---|
|
|
|
{"t":"a"} |
|
|
|
|
|
{"t":"b"} |
|
|
|
|
|
{"t":"a"} |
|
|
|
|
|
{"t":"fixed"} |
|
|
|
|
|
{"t":"fixed"} |
|
|
|
|
|
{"t":"fixed"} |
|
|
|
|
|
{"same":"1"} |
|
|
关键结论:
-
✅ sign_hash 仅依赖 content,不依赖 params 中的 timestamp:相同 content + 不同 timestamp → 完全相同的 hash -
✅ 同会话内的确定性:相同 content 连续调用 3 次 → hash 全部相同 -
✅ mask 结构分层:前 48B 依赖 content,后 56B 依赖内部 gettimeofday()时间戳
4.2 数学逆向实验(22 组)
在确认了基本特性后,通过 22 组精心设计的二进制输入进行差分分析,试图推导出哈希函数的数学结构:
实验数据(部分):
|
|
|
|---|---|
\x00 |
04490685... |
\x01 |
a711df0b... |
\x02 |
19cbc34f... |
\x00\x00 |
47942759... |
\x01\x00 |
374407fe... |
\x00\x01 |
4bee7d20... |
"A" |
9bd44515... |
"AA" |
1448f64f... |
"AAA" |
2bcad242... |
"AAAA" |
6f7a0a0e... |
4.3 差分分析结论
线性检验:
h(\x01) - h(\x00) = Kh(\x02) - h(\x00) = ? 应等于 2K实际: 完全不相等 → 对字节值非线性 ❌h(\xff) - h(\x00) = ? 应等于 255K实际: 完全不匹配 → 非线性 ❌
跨长度核比较:
K[0](1字节输入) ≠ K[0](2字节输入) ≠ K[0](4字节输入)→ 位置贡献随 content 总长度变化, 无法独立分解 ❌
AAAA 预测验证:基于线性假设预测 h("AAAA"),与实际值完全不匹配。
最终结论:
sign_hash是一个自定义的非线性哈希函数:
输出 20 字节(160 位)——与 SHA1 长度相同,但非 SHA1 对 content 字节值非线性 位置贡献随 content 总长度变化 依赖会话特定状态(每次 App 启动不同) 不依赖 params 中的 timestamp 同一会话内具有确定性 非标准 SHA1/MD5/HMAC/CRC 的任何已知组合 核心算法隐藏在 36KB CFF 混淆函数中,无法通过 I/O 推导还原
已通过实验排除的候选算法:
|
|
|
|---|---|
SHA1(content) |
|
MD5(content) |
|
SHA1(MD5(content)) |
|
HMAC-SHA1(key, content) |
|
SHA1(salt + content) |
|
CRC32(content) |
|
|
|
|
MD5(content XOR key) |
|
五、Frida 桥接方案:AttachCurrentThread
由于签名算法包含会话特定状态(无法离线复现),我们最终构建了一套基于 Frida 的 RPC 桥接方案,让 Python 能够按需远程调用设备上 App 的签名函数。
5.1 整体架构

Frida Bridge v2 架构图

整个方案分为两个阶段:初始化(自动等待 App 启动并捕获必要指针)和 调用(通过 RPC 随时请求签名)。
5.2 初始化时序

Bridge 初始化时序图

初始化流程:
-
Python spawn 目标 App 进程,同时加载 Frida Agent -
Agent Hook RegisterNatives,捕获calc函数指针 -
App 发出首个 API 请求,触发首次 calc调用 -
Agent 在 onEnter中捕获JavaVM和 jclass 的GlobalRef -
通知 Python “桥接就绪”,后续可接受 RPC 调用
5.3 核心 JavaScript 技术点
// 步骤 1: 首次 calc 调用时捕获 JavaVM 指针var vt = env.readPointer(); // 读取 JNIEnv 的 vtablevar getJavaVM = new NativeFunction( vt.add(219 * 8).readPointer(), // index 219 = GetJavaVM'int', ['pointer', 'pointer']);var vmBuf = Memory.alloc(8);getJavaVM(env, vmBuf);javaVmPtr = vmBuf.readPointer(); // 保存 JavaVM 指针// 步骤 2: 为 jclass 创建 GlobalRef, 防止 GC 回收var newGlobalRef = new NativeFunction( vt.add(21 * 8).readPointer(), // index 21 = NewGlobalRef'pointer', ['pointer', 'pointer']);jclassPtr = newGlobalRef(env, args[1]); // 从 onEnter 的 args[1] 获取// 步骤 3: RPC 调用时, 为当前线程创建新的 JNIEnvvar vmVt = javaVmPtr.readPointer();var attachCurrentThread = new NativeFunction( vmVt.add(4 * 8).readPointer(), // JavaVM vtable index 4 = AttachCurrentThread'int', ['pointer', 'pointer', 'pointer']);var envBuf = Memory.alloc(8);attachCurrentThread(javaVmPtr, envBuf, ptr(0));var newEnv = envBuf.readPointer(); // 获得与当前线程绑定的合法 JNIEnv// 步骤 4: 使用合法 env 直接调用 calcvar calcFn = new NativeFunction(calcFnPtr, 'pointer', ['pointer', 'pointer', 'pointer', 'pointer']);var result = calcFn(newEnv, jclassPtr, contentArr, paramsArr);
关键技术要点总结:
-
** AttachCurrentThread**:JVM 官方 API,为非 JVM 线程(如 Frida Agent 线程)创建合法 JNIEnv,避免了直接使用其他线程的 JNIEnv 导致的崩溃 -
** NewGlobalRef**:将 jclass 从局部引用提升为全局引用,防止 JVM GC 在函数返回后将其回收 -
vtable 精确索引:arm64 环境下指针大小为 8 字节,所有 vtable 访问必须使用 index × 8计算偏移
5.4 性能表现
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
六、已验证的算法细节
6.1 calc 完整数据流
通过多轮实验,我们完整还原了 calc() 的外部行为和内部结构:

执行流程分为四个阶段:
Phase 1:基础哈希
-
读取硬编码 SALT(已在二进制中提取,此处脱敏处理,不予公开) -
hash1 = MD5(SALT)(已验证) -
hash2 = MD5(hash1 + timestamp_ms),时间戳取自gettimeofday()(已验证)
Phase 2:APK 完整性校验
-
读取 /proc/self/maps(反篡改:确认内存映射正常) -
解析 APK zip 结构,提取证书签名哈希( APK_HASH,脱敏处理) -
h_apk = MD5(APK_HASH[2:12])(已验证)
Phase 3:核心哈希计算(36KB CFF 混淆函数)
-
输入:hash1、hash2、h_apk、硬编码固定哈希、content -
内部进行多轮迭代,约 14 轮,在两个内存地址交替写入 20 字节中间值 -
输出:20 字节 sign_hash(自定义非线性算法)+ 104 字节mask_data
Phase 4:签名组装
-
sign_raw = session_prefix(12B) + sign_hash(20B) -
sign_b64 = Base64(sign_raw)(44 字符) -
mask_b64 = Base64(mask_raw)(140 字符) -
返回: "sign_b64 mask_b64"
6.2 Sign 结构详解

Sign 结构详解

sign 由两部分拼接后 Base64 编码:
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
这个设计使得 sign 同时包含了”会话标识”和”内容指纹”:服务端通过解析 sign 前 12 字节可以识别会话,后 20 字节用于验证请求内容的合法性。
6.3 已验证的 MD5 哈希链

MD5 双轮哈希链

MD5 双轮链已被完整验证:
Step 1: MD5(SALT) = hash1 [已验证 ✅]Step 2: MD5(hash1 + timestamp_ms) = hash2 [已验证 ✅]APK: MD5(APK_HASH[2:12]) = h_apk [已验证 ✅]
注:SALT 值和 APK_HASH 为版本特定的硬编码值,已向厂商报告,本文不予公开完整值。
七、使用指南(研究环境)
⚠️ 以下内容仅供安全研究参考,需在已获授权的测试设备上进行。
7.1 环境准备
# 安装 Python 依赖pip3 install frida==17.8.2 frida-tools==17.8.2# 部署 frida-server 到测试设备adb push frida-server-17.8.2-android-arm64 /data/local/tmp/frida-serveradb shell "chmod +x /data/local/tmp/frida-server"adb shell "su -c '/data/local/tmp/frida-server -D &'"# 验证连接frida-ps -U | grep <package_name>
7.2 桥接方案使用示例
#!/usr/bin/env python3# 注意: 仅在授权测试设备上运行# 1. 启动桥接 (spawn 模式会重启 App)bridge = FridaCalcBridge()bridge.connect(spawn=True)bridge.wait_ready(timeout=60) # 等待 App 初始化并触发首次 calc# 2. 请求签名计算content = build_request_body("some.api.Method", "method_name", {"param": "value"})params = build_params() # 自动填入当前时间戳result = bridge.calc(content, params)print(f"sign: {result['sign']}") # 44 字符 Base64print(f"mask: {result['mask']}") # 140 字符 Base64# 3. 清理bridge.close()
八、安全防护机制评估
该 App 部署了多层安全机制,各层强度不一:
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
Java.perform() 后自杀,但纯 native hook 不受影响 |
/proc/self/maps
|
|
|
|
|
|
|
|
|
|
|
|
|
|
RegisterNatives
|
|
|
|
|
|
|
|
|
总体评价:整体防护的强点在于编译期混淆(tmesec_llvm)和 Crypto 内联,这两项对静态分析构成了严重障碍。但运行时防护相对薄弱——纯 native 的 Frida Hook 完全不受反 Frida 检测影响。
九、结论
9.1 目标达成情况
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AttachCurrentThread
|
|
|
|
|
|
|
|
|
9.2 技术贡献
本次分析产出了四项可复用的技术方法:
-
搭载实验技术(Piggyback):解决了无法直接从外部线程调用 native JNI 函数的问题,适用于所有需要在 App 进程内进行受控实验的场景 -
AttachCurrentThread桥接模式:解决了 JNIEnv 线程局部限制,实现了高性能 RPC——这是一个通用的 Frida 桥接范式,可应用于任何需要从外部驱动 JNI 函数的场景 -
差分分析方法:通过 22 组受控实验,用数学方法排除了大量候选算法,即使无法破解也能精确描述算法特性 -
JNI vtable 索引校正:在 arm64 环境下精确验证了 SetByteArrayRegion的正确偏移(index 208),并总结了常用 JNI 函数的 vtable 索引表
9.3 大模型辅助逆向:效率与范式的变化
本次分析是一次完整的”人机协作逆向”实践,从中可以总结出大模型在移动安全研究中的价值边界:
大模型擅长的:
-
代码生成与迭代:Frida 脚本从需求到可运行代码,秒级响应;错误修复有理有据,不是蒙 -
知识密集型推理:JNI 规范、vtable 布局、arm64 调用约定——这类需要大量参考手册的推理,大模型有明显优势 -
实验设计:给定约束条件(只能替换输入,无法直接调试),自动规划最优的实验矩阵 -
多假设管理:同时维护”MD5链 / 自定义哈希 / HMAC 变体”等多个假设,随数据动态排除
大模型的局限:
-
无法直接操作设备,所有执行都依赖人在物理世界完成 -
对某些高度上下文依赖的运行时现象(如特定内存布局、时序竞争)需要人反复描述才能准确理解 -
复杂的 CFF 反混淆仍依赖专业工具(Ghidra 插件),大模型难以直接处理几十 KB 的原始汇编
整体效率提升:同等深度的逆向分析,传统方式通常需要数周;本次人机协作在 两天内完成了从零到可用桥接方案的全流程。大模型的核心价值在于消除了”知道方向但不知道怎么写”的摩擦,让研究员可以把精力集中在真正需要人类经验的判断上。
9.4 后续研究方向
-
静态逆向 36KB 核心函数:使用 Ghidra + 去混淆插件(如 D810)系统分析 fcn.0x8bbb8,尝试还原真实算法 -
Unicorn 模拟执行:提取 libmer.so的calc函数代码段,在 Unicorn 引擎中模拟执行,彻底脱离设备 -
服务端验证研究:分析服务端对 sign/mask 的校验逻辑和容错窗口,评估整体安全性 -
detect()函数逆向:分析反调试/完整性检测函数的具体实现逻辑
分析完成时间:2026-03-18
使用工具:Frida 17.8.2 · radare2 · jadx · Python 3.10
测试环境:Android 测试设备(已 root)
夜雨聆风