样本:找的一个低版本的 v5.0.01
首先,总所周知,这个 app 是加壳的,360 加固,整体型加固,直接用 frida 就可以将壳给脱下来
脚本的话可以用 yang 神写的:https://github.com/lasting-yang/frida_dump
|
|
抓包
首先抓一下首页的这个包

发现在请求参数中有 sign,q 这个加密参数,响应这个包也是加密的
sign
首先来分析以下 sign 这个参数,首先来搜索,发现并没啥用,搜不到一点

那就换一种方式,hook 一下 hashMap,经常会有一些 app 会使用这个数据结构来存储数据
|
|
hook 的结果如下:
|
|
所以,自然就应该去 getRequestParams 看看

它是通过 StubApp 这个类调用 getString2 这个方法来完成字符串解密的,所以,我也可以通过主动调用的方式,来查看 put 进去的是什么字符串
|
|
得到如下的结果:
|
|
添加上注释,这样好看一点,把 jadx 的反混淆开一下,这个又是 a,又是 b 的看着好混

sign 这个参数的生成还会用到 cid,uid,q 的值,这个后面再分析,现在主要看 sign 这个参数,一个一个来,不然容易混乱
进入 C10604r.m24314a 这个方法

发现好像还是在构造参数,应该还是上面的 cid,uid,q 这三个值,放到一个 map 的结构中,最后通过 C10584c.f247939b.m23838a 这个方法进行加密,继续跟进去

发现最后会调用 md5_crypt 这个方法,而这个函数是一个 native 层的函数

继续 hook 这个函数,看一下需要哪些参数,但是这儿的参数类型是字节数组的类型,需要进行转换一下
|
|
得到如下的结果:
|
|
经过多次测试,cid 和 uid 是不变的,变的是 q 这个参数,目前先不管这个参数,后面会分析 上面加载的 so 名字也被加密函数给加密了,hook 一下
|
|
但是不知道 md5_crypt 这个函数的地址是哪儿,hook 一下 registerNative 这个构造函数
|
|
使用 unidbg 模拟调用一下,unidbg 用来分析算法感觉挺好用的
|
|
还不用补环境,得到的值跟 hook 的值一样

直接跳转到 SetByteArrayRegion,地址:0x1abb7 这个地方

doMD5sign 这个函数就非常可疑了,而且可以分析到 v41 就是入参,他拼接了一个长度为 20 的字符串,可以来 hook 一下
|
|
他后面添加了一个固定的盐值: dJLdCJiVnDvM9JUpsom9,长度为 20

继续跟进去,发现第一行就是 md5 这个函数,第一个参数为明文,第二个参数为明文的长度,第三个参数为加密过后的结果
继续 hook 地址 0x13E3C,得到加密后的结果为:

去在线网站加密一下,看看跟标准的 md5 加密有什么不同

得到的结果跟标准的 md5 结果是一样的,但是为什么跟 unidbg 之前模拟的结果不太一样呢
发现它会使用到 bytesToInt 这个方法

很可能是在这里面做了处理,进去看一下

前面一大堆都没做啥,主要是最后的 v8,将传进来的四个字节组合成大端序的 32 位整数
|
|
刚好就能够得到正确的值了

q
接下来就应该看一下 q 这个参数是从哪儿生成的了

跟到 C10584c.m24187b 这个函数中,然后一直跟一直跟,就能够发现是 `

接下来进行 hook localAESWork4Api 这个方法
|
|
可以知道 i2的值如果为 0,代表加密;如果为 1,代表解密
也就是说请求参数中是加密,返回值就是解密了
hook 的结果如下:
|
|
接下来就应该去 ida 中进行分析了,看一下这个是什么 aes,是否进行了魔改,是否是白盒 aes,他的算法逻辑也是在 cryptoDD.so 中进行加密的
继续使用 unidbg 对这一部分进行模拟,发起调用
|
|
以下是调用结果,连环境都不用补
调用结果一致,下面开始进行算法分析
首先由这个名字我们就知道这个是 AES,算法,接着就需要看一下到底是 ECB/CBC 模式?什么填充方式,密钥是什么?iv 是什么?
将参数换成 0123456789abedef0123456789abedef,如果重复了,就代表是 ECB 模式,没有 iv 的参与

好的,这个就是 ECB 模式,相同的明文加密得到相同的密文,没有 iv,接下来应该验证的是什么填充方式,密钥是什么了
跳转到 0x18903


跟进 wbaes_encrypt_ecb,第一个参数为输入的明文,第二个参数为明文的长度,第三个参数为输出,第四个参数为工作模式
对这个函数进行 hook 一下
|
|

首先看到明文的填充方式是 pkcs7,可以由末尾填充的 0x10 进行推测到
接下来继续走,发现有 aes128_enc_wb_coff 和 aes128_enc_wb_xlc 这两个方法,不确定是哪个方法触发了,直接 unidbg 中 hook 一下
|
|
发现在 aes128_enc_wb_coff 断住了 3 次, aes128_enc_wb_xlc 一次都没断住就出结果了,说明走的是 aes128_enc_wb_coff 这个方法
跟进去之后发现一直在进行查表操作,可以确定就是白盒 AES 了
对于白盒 AES,一般是以下三个步骤:
- 找轮
- 找时机,找具体第几轮做故障注入
- 找 state
对于找轮,可以看见代码里面符号还没去,可以找到
wbShiftRows这个函数,它在 AES 算法中,十轮运算都会存在,是一个比较好的选择 进入 hook 一下,同时也要注意需要把明文输入改成 16 个字节以内
|
|

接下来开始 dfa 攻击,在第 9 轮注入我们的故障密文, wbShiftRows 唯一的参数既做输入又做输出,所以可以针对 0x14F98 这个地址的第一个参数进行修改
|
|
当然,这仅仅只是一个故障密文,为了更好的还原出密钥,需要收集多个密文才行
所有代码如下:
|
|
接着就收集到了多个密文

将所有搜集到的密文放到一个文件中,注意,第一行需要放 dfa 攻击之前的密文
|
|
然后,就能够得到最后一轮的密钥

用 stark工具恢复出初始密钥
|
|
接着,就能够得到初始密钥是 644A4C64434A69566E44764D394A5570


得到的值跟 unidbg 模拟的值一致