在移动应用安全测试中,签名算法的逆向分析是核心挑战之一。本文将以一个实际应用(代号“川观新闻”)为例,手把手带您从抓包到逆向,一步步定位并最终通过 Frida 编写 Hook 脚本,成功拦截其登录接口的签名生成过程。对于想要入门 软件测试 和安全分析的朋友,这是一份不错的实战案例。
一、初步尝试与算法自吐
我们首先尝试使用算法自吐脚本,看看能否直接获取到关键的加密信息。

在手机上打开目标 App,点击登录按钮。

同时在抓包工具中查看加密后的请求参数,然后将抓到的加密字符(如 sign 值)去脚本输出的 log.txt 文件里搜索。

我们尝试搜索手机号、验证码、sign 等各种参数,但都没有在日志中找到匹配项。这表明签名算法可能位于更深的层次,单纯的自吐脚本无法捕获,此时就需要进行更深入的逆向分析了。
二、使用 Objection 进行动态分析
Objection 是一个基于 Frida 的强大运行时移动端测试工具,能帮助我们快速定位关键代码。
1. 注入 Objection Agent
首先,我们需要将 Objection 注入到目标进程中。
objection -g com.sichuanol.cbgc explore

2. Hook 常用字符串方法进行试探
在逆向初期,可以先 Hook 一些常见的方法来观察应用行为。例如,Hook java.lang.String 的 getBytes 方法,看看登录时是否有字符串转换操作。
android hooking watch class_method java.lang.String.getBytes

设置好 Hook 后,在手机上点击登录,观察 Objection 终端的输出。

3. 打印详细参数、返回值与堆栈
我们发现 getBytes 方法确实被触发了。为了获取更多信息,我们可以重新 Hook 并设置打印参数、返回值和调用堆栈。
android hooking watch class_method java.lang.String.getBytes --dump-args --dump-return --dump-backtrace

再次点击登录,查看详细的调用信息。

从输出可以看到,返回值是一个对象,参数是 UTF-8。此时,分析调用堆栈(Backtrace)比参数本身更有价值。
4. 分析调用堆栈,定位可疑类
我们重点查看堆栈信息,寻找与我们业务(登录、签名)相关的类。

堆栈中一个名为 com.sichuanol.cbgc.util.LogShutDown.getAppSign 的方法引起了我们的注意。查看其代码,发现这是一个 native 方法,意味着实现在 so 库中。

同时,堆栈中另一个类 cn.thecover.lib.http.data.entity.HttpRequestEntity 的 getSign 方法也值得关注。查看其源码,发现了与账号(account)、令牌(token)、时间戳(timestamp)等参数相关的逻辑。

这很可能就是生成签名的关键位置。让我们 Hook 这个方法验证一下。
android hooking watch class_method cn.thecover.lib.http.data.entity.HttpRequestEntity.getSign --dump-args --dump-backtrace --dump-return

Hook 之后再次点击登录。

输出显示,getSign 方法被成功调用,并返回了一个十六进制字符串(如 91FA3076599BA45649C8FDCD0E1D0205)。对比抓包数据,这正是请求包中的 sign 字段。这表明我们找对了关键路径。
其实到这一步,如果目标只是获取签名算法结果,我们已经可以直接通过 RPC(远程过程调用)来调用这个 Java 方法了。 下面的步骤将深入到 so 层进行更底层的分析,这对于学习 安全/渗透/逆向 技术非常有帮助。
三、深入 Native 层(SO)分析
既然 getAppSign 是 native 方法,我们直接 Hook 它的实现类 com.sichuanol.cbgc.util.SignManager 中的 getSign 方法。
android hooking watch class_method com.sichuanol.cbgc.util.SignManager.getSign --dump-args --dump-backtrace --dump-return

调用后,我们获得了更清晰的输入输出。

从输出可知,这个 getSign 方法只传入了一个时间戳参数 1632747349093,并且返回值 DA0B7F8E50180C64A3AB0C6078B1EE05 与请求包中的 sign 值一致。

结论很明确:该应用的登录签名仅与时间戳有关,算法相对简单。通过上面的分析,我们知道关键实现在 SignManager 类的 getSign 这个 native 方法中。

四、静态分析 SO 库
我们将目标 App 的 libwtf.so 文件拖入 IDA Pro 进行静态分析。根据 JNI 的命名规则,找到对应的 native 函数:Java_com_sichuanol_cbgc_util_SignManager_getSign。

1. 关键函数伪代码分析
该函数的逻辑可以概括为以下几步:
- 获取
AppSign、account、token、timestamp 等字符串。
- 将这些字符串拼接成一个新的字符串。
- 对拼接后的字符串进行 MD5 摘要计算。
- 将 MD5 结果转换为 32 位十六进制字符串并返回。
以下是核心伪代码逻辑:
// 拼接字符串: appSign + account + token + timestamp
strcat(v18, v10); // appSign
strcat(v18, v11); // account
strcat(v18, v12); // token
strcat(v18, v14); // timestamp
// 计算MD5
MD5Digest(v18, v22, v30);
// 转换为32位Hex字符串
get32MD5String(v30, v29);

因此,我们最终要 Hook 的底层函数就是 MD5Digest 和 get32MD5String。
2. 定位函数地址
在 IDA 中查看 MD5Digest 的调用处,发现是 BLX 指令跳转。

继续双击跳转,找到真正的 MD5Digest 函数体。


最终,我们定位到 MD5Digest 函数的实际实现代码。

记下其偏移地址:0xC90。用同样的方法,我们找到 get32MD5String 函数的偏移地址:0x8BC。

五、编写 Frida Hook 脚本
有了函数偏移地址,我们就可以编写 Frida 脚本直接 Hook 这两个底层函数,从而监控签名算法的原始输入和输出。这对于理解 Android/iOS 原生层安全机制至关重要。
// 工具函数:打印参数,如果是内存指针则进行hexdump
function print_arg(addr){
var module = Process.findRangeByAddress(addr);
if(module != null) return hexdump(addr) + "\n";
return ptr(addr) + "\n";
}
// 通用Hook函数
function hook_native_addr(funcPtr, paramsNum){
var module = Process.findModuleByAddress(funcPtr);
Interceptor.attach(funcPtr, {
onEnter: function(args){
this.logs = [];
this.params = [];
this.logs.push("call " + module.name + "!" + ptr(funcPtr).sub(module.base) + "\n");
for(let i = 0; i < paramsNum; i++){
this.params.push(args[i]);
this.logs.push("this.args" + i + " onEnter: " + print_arg(args[i]));
}
}, onLeave: function(retval){
for(let i = 0; i < paramsNum; i++){
this.logs.push("this.args" + i + " onLeave: " + print_arg(this.params[i]));
}
this.logs.push("retval onLeave: " + print_arg(retval) + "\n");
console.log(this.logs);
}
});
}
// 主逻辑:获取so基址,计算函数地址并进行Hook
var soAddr = Module.findBaseAddress("libwtf.so");
var MD5Digest = soAddr.add(0xC90 + 1); // +1 用于Thumb模式
hook_native_addr(MD5Digest, 3); // MD5Digest 有3个参数
var get32MD5String = soAddr.add(0x8BC + 1);
hook_native_addr(get32MD5String, 2); // get32MD5String 有2个参数
脚本解析与优化建议
print_arg 函数:智能判断地址类型,如果是可读内存则用 hexdump 输出内容,否则直接打印地址值。
hook_native_addr 函数:通用的 Native 函数 Hook 模板,可记录函数调用时的参数和返回时的结果。
- 地址偏移:脚本中在偏移地址上
+1 是因为 ARM 的 Thumb 指令集模式,确保跳转到正确的指令头。
- 优化建议:
- 日志控制:在高频调用场景下,可以考虑按需打印,避免日志爆炸。
- 错误处理:可添加
try-catch 块,增强脚本的健壮性。
- 参数解析:可根据函数原型进一步解析参数,例如将
MD5Digest 的输入字符串打印出来。
总结
通过本次实战,我们完成了一个完整的 Android 应用签名算法分析链条:
- 动态探测:使用 Objection Hook Java 层,快速定位到关键的签名方法。
- 静态分析:利用 IDA Pro 逆向
so 库,理清算法逻辑(字符串拼接 + MD5),并定位关键函数地址。
- 代码实现:编写 Frida 脚本,直接 Hook Native 层函数,实现算法监控或干预。
这种方法不仅适用于登录签名,也广泛适用于各类 Android/iOS 应用的加密函数定位与分析,是移动安全研究人员和逆向工程师的必备技能。希望这个案例能为你带来启发,欢迎在 云栈社区 分享你的实践经验和遇到的问题。