百趣云 百趣云的博客

HMAC-SHA256 签名还原:密钥到底藏在哪

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 全文搜得到

搜索技巧:搜 HmacSHA256Mac.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&timestamp                            ← & 分隔?
json 原文                                                ← 原样?

字段顺序、分隔符、URL 编码方式、空值处理,每个细节都影响结果。还原方法:hook 拿到的待签数据原文就是模板——多抓几组,对比字段变化,模板自己浮现。

精髓二:key 的生命周期管理

key 拿到手后的工程问题:

  • 静态 key:直接硬编码进你的协议代码
  • 动态 key(网络下发):协议流程里加"启动时拉 key"环节,缓存复用
  • 设备绑定 key:key 和设备档案绑定存储,账号池管理时成套维护

精髓三:HMAC 的变种识别

偶尔遇到"HMAC 但结果对不上"的情况,排查:

  1. 双重 HMACHMAC(key1, HMAC(key2, data))——hook doFinal 会看到两次调用
  2. HMAC 后再编码:结果做了 base64/hex/自定义置换——对比长度和字符集识别
  3. 非标准 HMAC:自研的"key 混 hash"结构(sha256(key + data + key) 之类)——这种不是 HMAC,是加盐哈希,hook 到的"key"其实是盐

总结

  1. HMAC 逆向 = 找 key,key 五种藏法:Java/SO 硬编码、拼接派生、网络下发、加密存储
  2. 通用杀招:hook Mac.init 拿 key 明文,hook doFinal 拿待签数据
  3. 待签数据的拼接模板和 key 同等重要
  4. 结果对不上查双重 HMAC、后编码、伪 HMAC

交流微信:run1255

By 百趣云 阅读量:39 On