HMAC 的结构决定了密钥是唯一的秘密
HMAC-SHA256 是 App 接口签名的主流方案。它的结构:
HMAC(key, msg) = SHA256( (key ⊕ opad) || SHA256((key ⊕ ipad) || msg) )算法完全公开,唯一的秘密是 key。逆向 HMAC 的本质就是一场"找 key"的游戏——key 藏在哪,决定了你的工作量和路径。
key 的五种藏身之处(按常见度排序)
1. Java 层硬编码字符串
String KEY = "a8f5c3d2e1..."; // 最直接,jadx 全文搜得到搜索技巧:搜 HmacSHA256(Mac.getInstance 的参数)定位使用点,往回追 SecretKeySpec 的构造参数。
2. SO 层硬编码
key 在 native 层,Java 只调 JNI。IDA 里搜字符串,或 hook Mac.init/SecretKeySpec 拿内存中的明文。
3. 拼接派生
key 不是完整存储,运行时拼接:part1 + part2 + 设备信息。识别特征:hook 到的 key 每次不同(含设备/时间成分),或 key 的来源代码里有拼接逻辑。
4. 网络下发
key 从配置接口动态下发(可轮换)。识别:hook 到的 key 和代码里任何常量都对不上,且换设备/重装后变化。对策:抓下发接口,或 hook 使用点拿内存明文。
5. 加密存储
key 加密后存 SO/资源文件,运行时解密。对策:不分析解密算法,hook 解密函数返回或 HMAC 使用点拿明文。
通用杀招:hook 使用点拿明文
无论 key 藏得多深,它最终必须以明文形式进入 HMAC 计算。Java 层的通用 hook:
Java.perform(function() {
var Mac = Java.use('javax.crypto.Mac');
Mac.init.overload('java.security.Key').implementation = function(key) {
var encoded = key.getEncoded();
console.log('[HMAC key] ' + bytesToHex(encoded));
console.log('[algorithm] ' + this.getAlgorithm());
this.init(key);
};
// 顺便拿待签数据
Mac.doFinal.overload('[B').implementation = function(data) {
console.log('[HMAC data] ' + Java.use('java.lang.String').$new(data));
var result = this.doFinal(data);
console.log('[HMAC result] ' + bytesToHex(result));
return result;
};
});跑一次 App,key、待签数据、签名结果三样全到手。拿到 key 后验证:本地用 key 对同样数据算 HMAC,和 App 结果一致 = key 正确且算法标准。
精髓一:待签数据的拼接模板比 key 更重要
拿到 key 只是半场。HMAC 签名的另一半是待签数据的组装格式:
method + "\n" + path + "\n" + timestamp + "\n" + body ← 换行分隔?
key=value&key=value×tamp ← & 分隔?
json 原文 ← 原样?字段顺序、分隔符、URL 编码方式、空值处理,每个细节都影响结果。还原方法:hook 拿到的待签数据原文就是模板——多抓几组,对比字段变化,模板自己浮现。
精髓二:key 的生命周期管理
key 拿到手后的工程问题:
- 静态 key:直接硬编码进你的协议代码
- 动态 key(网络下发):协议流程里加"启动时拉 key"环节,缓存复用
- 设备绑定 key:key 和设备档案绑定存储,账号池管理时成套维护
精髓三:HMAC 的变种识别
偶尔遇到"HMAC 但结果对不上"的情况,排查:
- 双重 HMAC:
HMAC(key1, HMAC(key2, data))——hook doFinal 会看到两次调用 - HMAC 后再编码:结果做了 base64/hex/自定义置换——对比长度和字符集识别
- 非标准 HMAC:自研的"key 混 hash"结构(
sha256(key + data + key)之类)——这种不是 HMAC,是加盐哈希,hook 到的"key"其实是盐
总结
- HMAC 逆向 = 找 key,key 五种藏法:Java/SO 硬编码、拼接派生、网络下发、加密存储
- 通用杀招:hook Mac.init 拿 key 明文,hook doFinal 拿待签数据
- 待签数据的拼接模板和 key 同等重要
- 结果对不上查双重 HMAC、后编码、伪 HMAC
交流微信:run1255