移动安全

使用 Frida 抓取 Android 加密参数与解密明文

· 移动安全
Max AI 专属导读

文章 AI 总结

用更短的路径抓住本文重点

  • 针对Android应用加密分析,通过Frida动态插桩Hook javax.crypto关键类,实时捕获密钥、IV及解密明文。
  • 完整流程涵盖环境准备、ADB连接、frida-server部署及脚本注入四步,需Root权限与USB调试支持。
  • 核心脚本通过拦截SecretKeySpec、IvParameterSpec和Cipher.doFinal,以十六进制输出密钥与解密数据。
  • 实际应用中可扩展Hook Cipher.init记录操作模式,结合调用栈追踪定位业务加密入口,辅助协议还原。
  • 操作需在授权测试环境进行,仅适用于Root设备,抓取结果可用于接口重放与安全评估。

一、环境准备#

开始之前,请确认以下环境已经准备就绪:

  • 一台已经 Root 的 Android 设备或模拟器,本文使用的是 MuMu 模拟器和 Android 12。
  • PC 端已经安装 adb,并配置好环境变量。
  • PC 端已经安装 Python 3 和 frida-tools,可通过下面命令安装:
pip install frida-tools
  • 已经下载与设备架构匹配的 frida-server,可在 Frida Releases 页面获取。

二、连接 ADB 设备#

先确保 ADB 服务正常,并且能够识别目标设备:

adb kill-server
adb start-server
adb devices

命令说明如下:

  • adb kill-server:关闭当前 ADB 服务进程,避免旧连接干扰。
  • adb start-server:重新启动 ADB 服务。
  • adb devices:列出所有已连接的设备。看到设备序列号,并且状态为 device,就表示连接成功。

如果出现 unauthorized,需要在手机或模拟器中点击「允许 USB 调试」。如果没有设备,请检查连接、驱动和模拟器 ADB 设置。

三、部署并启动 frida-server#

frida-server 推送到设备的 /data/local/tmp/ 目录,赋予执行权限,然后以 root 权限在后台运行:

adb push frida-server /data/local/tmp/
adb shell chmod 755 /data/local/tmp/frida-server
adb shell "su -c /data/local/tmp/frida-server &"

如果命令执行后没有明显报错,通常说明 frida-server 已经启动。后续可以通过 frida-ps -U 检查 Frida 是否能够正常连接设备。

四、注入 Hook 脚本#

使用 -U 指定 USB 设备,使用 -F 附加到当前前台应用,使用 -l 加载 Hook 脚本:

frida.exe -U -F -l tt.js

参数说明:

  • -U:连接 USB 设备或模拟器。
  • -F:附加到当前前台应用。
  • -l tt.js:加载本地 Hook 脚本。

如果需要指定包名启动应用,可以使用 -f <package> 替代 -F,并加上 --no-pause 让应用自动继续运行。

五、Hook 脚本解析#

完整脚本如下。核心思路是 Hook javax.crypto 相关关键类,拦截密钥创建、IV 构造,以及 Cipher.doFinal 的输出。

"use strict";
setInterval(function() {}, 1000);

function toHex(bytes) {
    if (!bytes) return "(null)";
    var hex = [];
    for (var i = 0; i < bytes.length; i++) {
        var b = bytes[i] & 0xFF;
        hex.push(('0' + b.toString(16)).slice(-2));
    }
    return hex.join(' ');
}

Java.perform(function() {
    console.log("[*] Hook 已激活...");

    // 抓密钥
    var SecretKeySpec = Java.use("javax.crypto.spec.SecretKeySpec");
    SecretKeySpec.$init.overload('[B', 'java.lang.String').implementation = function(key, algo) {
        console.log("\n[密钥] 算法: " + algo + "  Hex: " + toHex(key));
        return this.$init(key, algo);
    };

    // 抓 IV
    var IvParameterSpec = Java.use("javax.crypto.spec.IvParameterSpec");
    IvParameterSpec.$init.overload('[B').implementation = function(iv) {
        console.log("[IV] Hex: " + toHex(iv));
        return this.$init(iv);
    };

    // 抓解密结果,不再调用 getOpMode,避免异常
    var Cipher = Java.use("javax.crypto.Cipher");
    Cipher.doFinal.overload('[B').implementation = function(input) {
        var result = this.doFinal(input);   // 直接调用原始方法,安全
        try {
            // 只打印长度较大的结果,大概率是解密后的业务数据
            if (result.length > 64) {
                console.log("\n[解密输出] 长度: " + result.length);
                console.log("[解密输出] Hex: " + toHex(result));
                try {
                    var str = Java.use("java.lang.String").$new(result, "UTF-8");
                    console.log("[解密输出] 明文: " + str);
                } catch (e) {
                    console.log("[解密输出] 非 UTF-8 文本");
                }
            }
        } catch (e) {
            console.log("[!] 打印异常: " + e);
        }
        return result;
    };
});

5.1 工具函数 toHex#

toHex 用来把字节数组格式化为以空格分隔的十六进制字符串,方便肉眼识别密钥、IV 等二进制数据。

关键点如下:

  • b & 0xFF:避免 Java 字节有符号导致的负数问题。
  • ('0' + b.toString(16)).slice(-2):保证每个字节都是两位十六进制。

5.2 抓取密钥:SecretKeySpec.$init#

SecretKeySpec 是 Java 标准库中构造对称密钥最常用的类。它的构造函数 (byte[], String) 中,两个参数分别是密钥字节和算法名,例如 AES、DES、HmacSHA256 等。

这里通过下面方式精准匹配重载:

SecretKeySpec.$init.overload('[B', 'java.lang.String')

在调用前打印算法和 Hex 格式的密钥,然后再执行:

return this.$init(key, algo);

这样既能拿到关键参数,又不会破坏原有业务逻辑。

5.3 抓取 IV:IvParameterSpec.$init#

对于 CBC、CFB、OFB、GCM 等加密模式,IV 或 Nonce 通常会通过 IvParameterSpec(byte[]) 传入。

因此,Hook 它的构造函数,就可以拿到明文 IV:

IvParameterSpec.$init.overload('[B').implementation = function(iv) {
    console.log("[IV] Hex: " + toHex(iv));
    return this.$init(iv);
};

5.4 抓取解密输出:Cipher.doFinal([B)#

Cipher.doFinal 是加解密流程的最终执行点。Hook 它可以同时拦截加密结果和解密结果。

脚本中的处理逻辑如下:

  • 先调用原始方法拿到结果,再打印,保证原有流程不被破坏。
  • 使用长度过滤,只打印 length > 64 的输出。经验上,这类长度的结果更可能是解密后的业务数据,例如 JSON、XML 等;短输出更多可能是哈希、签名或其他噪声数据。
  • 尝试使用 UTF-8 解码成明文字符串。如果失败,则只保留 Hex 输出。

六、常见扩展思路#

根据实际分析需求,可以在这份脚本基础上继续扩展:

  1. Hook Cipher.init:获取 opmode、Key、IV,把每次调用的上下文完整串起来。其中 opmode = 1 通常表示加密,opmode = 2 通常表示解密。
  2. 打印调用栈:在关键 Hook 点中通过 Log.getStackTraceString(Throwable.$new()) 反查业务调用位置。
  3. 区分加密和解密输出:配合 Cipher.init 的模式记录,分别标记 [加密输出][解密输出]
  4. Hook MessageDigestMac:抓取 MD5、SHA、HMAC 的输入输出,用来定位签名算法。
  5. 配合 Objection 或 Wallbreaker:做动态类查看和反射分析,辅助定位加密入口。

七、关于拉流地址的说明#

拉流地址通常可以按照下面思路拼接:

直播基础地址(固定部分) + Hook 得到的拉流标识(stream id / key) = 完整拉流地址

分析时可以重点关注以下内容:

  • 基础地址可以通过抓包工具定位,例如 Charles、Fiddler、mitmproxy 等。
  • 常见直播协议包括 rtmp://、HTTP-FLV、HLS,也就是 m3u8 等,需要按目标平台的实际格式拼接。
  • 部分平台的 URL 还会携带签名、时间戳、token 等参数,需要配合其他 Hook 点或接口分析补全。

八、小结#

整个流程可以概括为四步:

连接设备 → 部署 frida-server → 注入脚本 → 读取 Hook 日志

通过这套通用 Hook 脚本,基本可以覆盖大部分基于 javax.crypto 的 Android 应用加密实现,快速拿到密钥、IV 与解密明文。

这些信息可以帮助我们进一步进行接口重放、协议还原和安全评估。实际使用时,建议只在授权测试环境中操作,避免对第三方业务造成影响。

使用 Frida 抓取 Android 加密参数与解密明文

https://maxs.eu.org/posts/348f48da2d50.html
本文作者
马小酷
发布于
更新于
版权协议
CC BY-NC-SA 4.0

转载或引用本文时请遵守许可协议,注明出处、不得用于商业用途!

Comments

评论

欢迎留下你的看法,也欢迎补充不同视角。