逆向实战:手把手教你用Frida Hook某小说App的AES解密过程(附完整脚本)

张开发
2026/4/21 17:17:22 15 分钟阅读

分享文章

逆向实战:手把手教你用Frida Hook某小说App的AES解密过程(附完整脚本)
移动应用数据解密实战从AES算法识别到Frida动态插桩最近在研究一款流行小说App的数据传输机制时发现其返回内容采用了加密处理。这激发了我的好奇心——能否通过逆向分析揭开其加密逻辑的面纱本文将分享一套完整的分析思路和实操步骤从字符串定位到最终解密特别适合有一定Android逆向基础的安全爱好者。1. 逆向分析前的准备工作在开始逆向之前我们需要搭建好分析环境并准备好必要的工具。不同于静态分析动态插桩需要更细致的准备工作。基础工具准备Frida动态插桩的核心工具建议安装最新版本adbAndroid调试桥用于设备连接Jadx/Ghidra静态反编译工具Charles/Fiddler网络抓包工具提示建议使用root过的Android设备或模拟器进行分析避免权限限制影响调试过程。配置Frida环境时需要注意客户端和服务端的版本匹配问题。我通常使用以下命令检查版本一致性frida --version adb shell frida-server --version如果遇到版本不匹配的情况可以下载对应版本的frida-server推送到设备adb push frida-server /data/local/tmp/ adb shell chmod 755 /data/local/tmp/frida-server adb shell /data/local/tmp/frida-server 2. 关键字符串定位技巧面对加密的App第一步往往是寻找突破口。字符串搜索是最常用的方法之一但需要讲究策略。2.1 网络请求特征分析通过抓包工具我们可以观察到App的网络请求模式。对于小说类App通常会有一个获取内容的API接口。在我的分析案例中发现了类似novel-content的请求路径但直接在反编译代码中搜索这个字符串却一无所获。这种情况通常有两种可能URL是动态生成的URL来自之前的API响应通过检查网络响应我确认了第二种情况——App先获取了一个包含内容URL的配置列表。这解释了为什么静态搜索没有结果。2.2 登录绕过技巧很多App会对未登录用户限制功能。在逆向分析过程中临时绕过登录验证可以节省时间。通过搜索登录相关字符串可以定位到权限检查的代码位置。以下是一个典型的登录检查Hook示例var AuthHelper Java.use(com.example.app.AuthHelper); AuthHelper.isLoggedIn.implementation function() { return true; // 强制返回已登录状态 }; AuthHelper.isVip.implementation function() { return true; // 强制返回VIP状态 };这种方法虽然简单粗暴但在分析阶段非常实用。不过要注意这仅用于学习研究目的。3. 动态插桩定位解密逻辑当静态分析遇到瓶颈时动态插桩就成为我们的有力武器。Frida提供了强大的运行时Hook能力。3.1 StringBuilder Hook技巧大多数解密后的数据最终都会转换为字符串形式展示。Hook StringBuilder的toString方法是一个有效的捕获点var StringBuilder Java.use(java.lang.StringBuilder); StringBuilder.toString.implementation function() { var result this.toString(); // 过滤输出避免日志过多 if(result.length 100 result.indexOf({) 0) { console.log(Possible JSON content: , result); } return result; };在实际操作中这种方法可能会产生大量输出。关键在于设置合理的过滤条件只捕获可能包含解密数据的字符串。3.2 加密算法特征识别通过上述Hook我发现了包含AES/CBC/PKCS5Padding的字符串输出。这明确指出了App使用的加密算法。AES是一种对称加密算法CBC是其中一种工作模式PKCS5Padding则是填充方式。常见加密算法特征对照表算法关键特征典型应用场景AES密钥长度128/192/256位常见CBC/ECB模式数据传输加密RSA非对称加密密钥对常见PKCS1Padding密钥交换数字签名DES56位密钥已不推荐使用遗留系统兼容识别出算法类型后我们可以更有针对性地寻找相关代码。4. Native层与Java层交互分析现代App常将核心加密逻辑放在Native层实现这增加了分析难度。但通过Frida我们依然可以有效地进行Hook。4.1 Native函数Hook通过调用栈分析我定位到了一个名为NativeBds.dae1的Native方法。虽然实现在.so库中但它最终回调了Java的javax.crypto接口这给了我们Hook的机会var NativeBds Java.use(com.baidu.searchbox.NativeBds); NativeBds.dae1.implementation function(a, b) { console.log(NativeBds.dae1 called with params: , a, b); var result this.dae1(a, b); try { var decryptedStr Java.use(java.lang.String).$new(result); console.log(Decrypted content: , decryptedStr); } catch(e) { console.log(Result is not a byte array: , result); } return result; };这种跨层Hook需要注意数据类型转换。Native方法可能返回byte数组或其他类型需要适当处理。4.2 密钥与IV提取AES-CBC模式除了需要密钥外还需要初始化向量(IV)。通过Hook密钥相关操作我们可以捕获这些关键参数var SecretKeySpec Java.use(javax.crypto.spec.SecretKeySpec); SecretKeySpec.$init.overload([B, java.lang.String).implementation function(keyBytes, algo) { console.log(SecretKeySpec created with algorithm: , algo); console.log(Key bytes (hex): , bytesToHex(keyBytes)); return this.$init(keyBytes, algo); }; var IvParameterSpec Java.use(javax.crypto.spec.IvParameterSpec); IvParameterSpec.$init.overload([B).implementation function(ivBytes) { console.log(IV bytes (hex): , bytesToHex(ivBytes)); return this.$init(ivBytes); }; function bytesToHex(bytes) { return Array.from(bytes).map(b (0 (b 0xFF).toString(16)).slice(-2)).join(); }5. 完整Hook脚本与实战技巧结合上述分析我们可以构建一个完整的Hook脚本自动化解密过程。以下是我在实际项目中使用的脚本框架function hookCryptoOperations() { // 1. Hook密钥生成 var SecretKeySpec Java.use(javax.crypto.spec.SecretKeySpec); SecretKeySpec.$init.overload([B, java.lang.String).implementation function(keyBytes, algo) { console.log([*] SecretKey created - Algorithm: , algo); console.log([*] Key: , bytesToHex(keyBytes)); return this.$init(keyBytes, algo); }; // 2. Hook IV生成 var IvParameterSpec Java.use(javax.crypto.spec.IvParameterSpec); IvParameterSpec.$init.overload([B).implementation function(ivBytes) { console.log([*] IV: , bytesToHex(ivBytes)); return this.$init(ivBytes); }; // 3. Hook Cipher初始化 var Cipher Java.use(javax.crypto.Cipher); Cipher.getInstance.overload(java.lang.String).implementation function(transformation) { console.log([*] Cipher instance requested: , transformation); return this.getInstance(transformation); }; // 4. Hook解密过程 Cipher.doFinal.overload([B).implementation function(input) { console.log([*] Decrypting data, length: , input.length); var result this.doFinal(input); try { var str Java.use(java.lang.String).$new(result); console.log([] Decrypted: , str); } catch(e) { console.log([] Decrypted bytes: , bytesToHex(result)); } return result; }; } Java.perform(function() { hookCryptoOperations(); // 添加其他Hook... });在实际使用中有几个实用技巧值得分享过滤日志合理设置条件判断避免控制台被无关日志淹没错误处理妥善处理异常防止脚本因意外错误中断性能考虑复杂的Hook逻辑可能影响App运行调试时可先注释掉非关键部分逆向分析就像侦探破案需要耐心和细致的观察。记得在一次分析中我花了整整三天才定位到一个关键的密钥生成逻辑最终发现它竟然隐藏在资源文件中。这种经历让我明白逆向工程没有银弹每个App都可能有意想不到的实现方式。

更多文章