JS&Android逆向记录 - 白盒AES
type: channel
link: https://www.xiayutian.xyz/index.php/tag/%E7%99%BD%E7%9B%92AES/
items: 安卓逆向-五菱白盒AES
items: 安卓逆向-瑞幸咖啡白盒AES

安卓逆向-五菱白盒AES
type: item
date: 2025-01-07T17:43:00+08:00
description: 一、五菱白盒AES1. 前置[!sana] 版本：V8.0.14包名：com.cloudy.linglingbang加固：梆梆加固so文件：libencrypt.so涉及到的知识点有白盒AES、DFA、unidbg补环境以及辅助算法还原、unidbg的hook与patch等；可供参考资料：https://bbs.kanxue.com/thread-280335.htmhttps://bbs.kanxue.com/thread-284073.htm文件相关链接：链接: https://pan.baidu.com/s/1xwBKsBhdgXGeAjEYcfWLSA?pwd=sana提取码: sana整体思路参考了杨如画大佬的思路，在此先表示感谢；2. 抓包分析直接点击app会有更新提示与root提示，不更新就会闪退，可以去hook一下dialog或者吐司，我这里图方便就使用算法助手了，同时抓包也有检测，也一同勾上即可抓包，也是可以进入app的；这里的屏蔽关键字弹窗写升级或者更新都行，总之是进入了第一步，可以开始抓包了；抓包结果如下：这里请求体和返回都是密文的，都需要去解决，请求头也有一个签名，后续如果有时间也会说，首先看请求体这两个密文；3. 定位分析首先这个apk是加壳的，所以直接反编译是毛都不会有的，找个大佬帮忙脱一下壳，我会把脱下来的东西放在附件里；直接搜索大法看看有没有效果；这里是一个变量，比较可疑，我们进去查看谁引用了它，按下X或右键查看交叉引用；这里随意选一个，我看最后一个比较炸眼，我选择进去看看；这里就可以看到一些关键词，可以hook看看是不是正确的位置；最开始我是选择直接attach附加的，发现hook不上，换成重启的方式也报错了，这里做了frida的检测，但是检测的不严格，换成魔改版本的就行，这里是启动的自己编译的魔改版frida，使用hluda好像也是能过去的；这里选择hook encrypt函数，看看是不是正确位置，hook代码如下：function hook_enc(){ Java.perform(function () { let CheckCodeUtils = Java.use("com.cloudy.linglingbang.model.request.retrofit2.CheckCodeUtils"); CheckCodeUtils["encrypt"].implementation = function (str,i) { console.log(`CheckCodeUtils.encrypt is called: str=${str},i=${i}`); let result = this["encrypt"](str); console.log(`CheckCodeUtils.encrypt result=${result}`); return result; }; }) }这里最好到要点登录的时候再调用这个函数，可能会多一些输出，不便于我们分析；[Pixel 4::com.cloudy.linglingbang ]-> CheckCodeUtils.encrypt is called: str=mobile=15211112222&password=111111&client_id=2019041810222516127&client_secret=c5ad2a4290faa3df39683865c2e10310&state=v8kTgUYjT8&response_type=token, i=2 CheckCodeUtils.encrypt result=[binary] 触发了，看看与抓包结果是否一致，对比发现大致是一样的，但是将+给换成了空格，这个记住就好；那么位置没错就继续深入看看；定位到具体位置了，很显然是native函数，checkcode是加密，那decheckcode就是解密了，一个个分析，先使用unidbg把算法跑出来再说；4. unidbg复现4.1 unidbg基本框架把unidbg跑起来之后对算法还原是非常有帮助的，并且本文需要dfa，我是更喜欢用unidbg来攻击的，比较省心，先搭一个架子；public class WuLing extends AbstractJni { public static AndroidEmulator emulator; public static Memory memory; public static VM vm; public static Module module; public WuLing() { emulator = AndroidEmulatorBuilder .for32Bit() .addBackendFacto…

安卓逆向-瑞幸咖啡白盒AES
type: item
date: 2025-01-02T15:45:00+08:00
description: 一、瑞幸咖啡白盒AES样本其实已经有很多大佬分享过了，之所以写这样一篇文档是希望理清整体的流程，加深逆向的思想； 仅作技术交流 请勿恶意请求 侵权麻烦联系删除1. 前置[!sana]APP：瑞幸咖啡包名：com.lucky.luckyclient版本：5.0.01加固：360加固目标：q参数这里选择的版本不是新版，因为新版有frida对抗，考虑到复现问题使用了旧版本，这个版本是没有frida检测的；需要前置知晓的知识点大致如下：unidbg简单使用与补环境；AES算法详细流程；参考文档：https://www.yuque.com/nanren-w8l2z/xgu63m/uoff9ovsmhqki9wh白龙大佬的文章，请多多站在巨人的肩膀；差分故障攻击（DFA）；在此默认读者已经有逆向环境所以不会提到环境的东西；2. 抓包抓包环境：charles + socksdroid转发；在我的环境下直接就可以抓包，有对抗的话自行解决抓包问题；这里主要的目标就是q参数，sign也是native层，篇幅足够的话会讨论；关于壳在前置提到过，它是有一个360的加固，如图：在这里不涉及脱壳的内容，其实也非常好脱，我在这里使用的是fart脱的壳；3. 代码定位脱完壳后直接搜索sign，但是并搜不到什么有价值的东西；很明显这是人家的东西，所以这里用其他的方式定位；在这里使用hook hashmap的put方法，可以直接定位到，hook代码如下：function showStacks() { console.log( Java.use("android.util.Log") .getStackTraceString( Java.use("java.lang.Throwable").$new() ) ); } Java.perform(function () { var hashMap = Java.use("java.util.HashMap"); hashMap.put.implementation = function (a, b) { if(a!=null && a.equals("q")){ showStacks(); console.log("hashMap.put: ", a, b); } return this.put(a, b); } })在这里可以先将打印的地方注释掉，因为这个app是有双进程保护的，所以需要重启注入，这样就可以避免还没点击登录却输出了过多信息，hook结果如下：java.lang.Throwable at java.util.HashMap.put(Native Method) at com.lucky.lib.http2.r.a(SourceFile:7) at com.lucky.lib.http2.AbstractLcRequest.getRequestParams(SourceFile:14) at com.lucky.lib.http2.m.b(SourceFile:14) at com.lucky.lib.http2.m.getRequest(SourceFile:1) at com.lucky.lib.http2.AbstractLcRequest.async(SourceFile:5) ···省略部分 at butterknife.internal.c.onClick(SourceFile:4) at android.view.View.performClick(View.java:7259) at android.view.View.performClickInternal(View.java:7236) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930) hashMap.put: q 8pR_VhJIrWrz_t·····省略部分SLoqiKvmgkJGOXxo=根据堆栈信息去找，发现getRequestParams的位置比较像put的位置，而且也有cid的字眼；但这里并不知道它的键究竟是不是我们需要的，而它放入的是看不懂的东西，其实这里是加密了，如何进行测试呢？主动调用即可，getString2这个方法按理来说就应该是解密字符串的方法，传入相应的参数即可；主动调用代码如下：function dec_str(str){ Java.perform(function (){ let StubApp = Java.use("com.stub.StubApp"); …