-
-
[原创]浅尝ollvm轻度混淆后的加密算法分析
-
发表于: 2021-1-5 22:22 5804
-
该题源自看雪高研3W班9月第三题。
目标app只有一个很朴素的界面。点击“CHECK”按钮会在下方不断打印加密后的字符串。目标就是分析整个加密流程。
由于Yang神手下留情,整个加密流程其实并不复杂,但是由于使用了ollvm的字符串混淆及控制流平坦化,为静态分析增加了不少难度。这种时候就要善于借助各种其他工具和技术来加快分析过程。很多时候,我们并不需要弄清一个变量的值在运行时被解密的具体细节,可以直接在运行时hook出其解密后的值即可。也往往不需要分析明白一个关键加密算法函数中的每个子函数的作用,如果发现了某种常用算法的特征(不一定要完全匹配,可能是变种),就应该大胆猜测并及时做重放测试(CyberChef真的很方便)。毕竟猜错了没什么损失,猜对了可能挽回几年阳寿。
先将apk拖入GDA中,定位到点击按钮的处理函数
uUIDCheckS0为点击CHECK按钮后生成的值,生成算法包含在函数UUIDCheckSum中,传入参数是由RandomStringUtils.randomAlphanumeric(36)生成的一个36个字符的随机字符串。
可以利用Log确定每次调用UUIDCheckSum的入参和返回值
由于UUIDCheckSum是native函数,其实现在so中。将apk解压后把唯一的so文件拖入ida。
在函数窗口搜索,定位到UUIDCheckSum具体实现。
本来想先定位返回值再回溯,结果发现ida f5识别出来的方法没有返回值。猜测函数没有被正确识别,比如函数尾部被截断了之类的。
回到汇编代码,查看并定位到函数尾部
结果发现函数尾部接着的是一段.datadiv_decode开头的代码,之前学习视频里有提到这是一段解密用的代码段。
看了下调用位置,在.init_array中
观察了一下该段代码反汇编后的结果,
看起来是对几个字符串解密,比如stru_37010,byte_37090, byte_370A0, stru_370B0。可以用frida hook得到解密后的值,看看哪些比较有用。
比较感兴趣的是stru_37010解密后字符串:
0123456789-_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
看到这串排列整齐的字符序列,首先想到会不会是用到了某种base64编码。先去定位下用到这串字符串的位置。
两处定位看反编译的结果都比较诡异,可能是因为加密的值被优化了
但看汇编代码,确实是在这里调用的。
根据调用栈逐层往上找,发现是在UUIDCheckSum中的sub_F9B8方法里用到。
那么,猜测一下,这个sub_F9B8是以
0123456789-_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
为编码表的base64加密函数?需要验证一下
我先用frida写了两个hook函数,一个hook了sub_F9B8函数,还有一个hook 在sub_F9B8之前调用的sub_FCB4函数。因为sub_FCB4以之前生成的随机数作为输入,很可能sub_FCB4会用到处理后的结果。
Hook函数的实现
Hook上后点击按钮,得到了结果
生成的随机数:
经过sub_FCB4处理后的中间结果:
sub_F9B8的第二个传入参数:
打印出的最终结果(Logcat)
到CyberChef上验证猜想。由于猜测sub_F9B8是base64,所以将中间值
2n5ixhQ{-oLqe-4nEi-Xr3dv-u6Rq4uo`ga3作为输入,设定下编码表。发现结果果然符合最终结果
运气不错,验证成功!
接下来就只要研究sub_FCB4就行了。粗略地观察了一下反汇编后的代码,是对传入的随机字符串进行逐字节处理。
先取一次运行的输入输出值,放在010editor中做下参照。
输入:
输出:
由于函数不太复杂,打算先静态分析下。
首先
输出的第23个字节是输入的第24个字节
然后定位到代码
如果index 不为8、13、14、18、24则结果就是原字节异或1(除最后两个字节外)。
如果是14
输出字节为52
其他的第8、13、18、24字节值都为45
还剩最后两个字节,在函数尾部处理。
这里v14和v16的值不太好看出来,决定用ida trace看看。
在trace的log中定位到处理最后两个字节的位置
先看最后一个字节,
这个字节是通过一个字符串指定偏移处的值得到,偏移值存在X8寄存器中。
逐步往前跟踪X8寄存器值的来源,定位到
也就是是W20^W21。
W20是之前处理的输入字符串的最后一个字节。而W21来源于[SP,#0x50+var_44]。这个位置也是异或后结果保存的地方。所以猜测可能之前处理过程中也会不断将输入的字节逐一异或处理。
搜索了一下,果然有很多。
第一次用0xFF异或输入字符串的第一个字节,将结果与第二个字节异或,以此类推。
然后之前那些特殊位置的字节不参与处理。获取最终结果的低四位值。
同理发现倒数第二个字节的来源是将输入的字节逐个相加
获取最后的和的低四位值。
这部分算法用Java还原出来如下
得到的结果再经过之前分析的自定义Base64编码处理下就可以得到最终的结果了
附件附上目标apk和相应的还原代码
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)