Android逆向之旅—最右App的签名算法解析(ARM指令学习不舍篇)

Android技术篇 尼古拉斯.赵四 586℃

一、逆向分析

本文继续来看最后一篇介绍ARM指令学习,之前的两篇文章已经详细介绍了ARM指令的基础知识,本文继续把剩下来的所有都介绍完了,首先一定要去看前面的基础篇和进阶篇,不然我都很难保证你看这篇文章会不会懵逼!我们还是IDA动态调试到我们想要的地方:

在这里看到和进阶篇很类似,初始化一个数值然后减去一个值在和一个值进行与操作,这里有了一个新指令就是EOR,就是异或操作,而这里的R0,R1,R3还是之前介绍的四个常量值:

这里看到依然是利用左移获取地址,然后在用LDMDB命令获取内存中连续的数据,这个指令在进阶篇中已经介绍了,这时候我们可以看一下R5的内存数据:

这里发现了又是一个密钥库,我们还是把这个库数据弄出来:

那么这一段代码翻译之后的C代码就是:

然后就是后面的ROR操作了,我们直接打印结果看看:

动态调试看看结果:

看到这里的动态调试结果一致,说明翻译的C代码没问题,这里也发现了两条穿插指令,在之前的进阶篇介绍了穿插指令辨别方法就是看看指令操作后的结果是否参与了后面指令的操作,这里看到操作后的R4并没有参与后面的ROR操作,继续往下走,看到穿插指令操作的R4就用到了地方了:

看到动态调试的结果:

看到结果是一致的,继续往下走:

中间没啥可说的了,和之前类似就略过了,直接看最后的ROR操作结果,这里也是一样轮训四次:

看到结果是一致的,说明我们对于第三部分的循环分析没有问题,继续看第四部分的分析,依然是个循环操作:

这个操作和第三个循环部分非常类似,这里不再多介绍了,继续往下看:

这里发现了一个新指令就是ORN是或非操作,然后看到LDMDB命令,查看R5的内存数据是个密钥库,保存到本地:

然后打印第一次ROR操作结果验证结果:

实现的C代码打印结果:

结果一致,说明分析没问题,继续往下走:

中间过程和前面的介绍很类似,就不多说了直接看最后的ROR结果,打印C代码结果:

看到结果一致,这里就不多说了,继续往下走:

这时候F7往下走发现到了BGE指令之后又跳转到开始位置从新走一遍了,这里查看之前的指令寄存器值发现这里还有一个大循环,每次增量是0x40也就是64,说明在我们之前分析的四个循环外面还有个大循环,那么这个大循环干嘛的?我们跳转回去之后再一次来到第一个循环部分,查看R11寄存器的内容,因为之前分析之后发现R11就是存放操作的数据:

这是外面大循环的第二次了,继续跳过看看第三次:

这时候发现内容中好像是我们传递进来的需要加密操作的数据,回过头看看第一次循环的数据:

的确是我们传递进来的数据,这时候我们应该猜想这个外面的大循环是干嘛的了,是对加密数据进行分割,单位是64个字节,如果最后一部分不足64就用0补充:

这个就是我们操作数,这个是外部传进来的需要加密的参数数据,这里为了调试方便就转化成字节了,这里看到的确是进行分割操作了,那么外部的大循环循环一次内部的四个小循环的结果就不一样,而最后的四个小循环执行之后的结果会进行加操作:

这里我们可以查看每次的结果和打印的值:

再看看C代码实现的打印结果:

这里看到打印的结果是正确的,因为外部大循环还有好几次操作,这就不一一打印证明结果了,最后看看返回值是否和我们程序打印的log日志一样,因为外部还有一层循环,所以我们直接等循环之后看看最终的相加结果:

二、对比输出结果

过完了外部循环之后再看看R8,R9,R10,R12,然后我们看看打印的值:

这时候发现了加密串就是这四个寄存器中的值,只是是倒叙的,所以我们可以代码处理一下:

我们在5070这个函数调用之后打印值看看,也是这个加密串信息:

所以到这里我们就成功的把加密算法用C代码实现了,其实用C代码实现了,其他语言就没压力了。

三、Hook批量数据发现加密规律

在回过头看看这个是5070的加密函数功能,其实在这个之前还有一些加密信息,我们用三个字符串调用他的加密native方法

然后用frida去hook他的so代码:

这里我们有了一种新的hook未导出函数的方法:就是利用已有的导出函数地址计算出未导出函数的地址这样操作就方便了,而且这个公式大家记住它,比如这里的导出函数JNI_OnLoad在IDA中偏移地址是0x488C,那么如果要hook未导出函数0x5070就这么操作即可,然后我们运行hook脚本看看打印的三个字符串最后的信息:

这里看到转换字符串:

fourbrother => Y0MTBlODcxrZDfourbrothe

AAA => Y0MTBlODcxAAAZDYOMTB

bianmameili112223 => YOMTBlODcxi112223ZDbianmameil

通过这三组数据就可以发现规律了:data => Y0MTBlODcx data[10, length] ZD data[0, 10]

所以我们再次把请求参数信息构造一下第一步加密处理:

然后把加密后的数据生成对应的字节数组作为上面解密的5070函数执行参数即可获取之后的加密数据了。这样就可以构造出最右App的网络请求sign字段值了,我们可以hook最右app的参数信息:

这里我们用frida去hook最右app的so函数方法看看他的参数信息,在执行5070之前的参数形式就是我们上面分析的加密样式!

四、加密总结

到这里我们就分析完了最右应用的签名算法过程,整个过程其实回头看并没有那么复杂,首先是Java层构造了网络请求参数的json数据字节数组传给native层,第一次加密用 data => Y0MTBlODcx data[10, length] ZD data[0, 10] ; 然后在调用我们前三篇介绍的那个5070加密函数进行加密,加密函数主要操作是:

1、将需要操作的数据按照64个字节为一组分段操作,最后一组不足64的用0补齐

2、然后就开始了一个大循环对每段数据都进行操作,大循环内部有四个小循环操作,每个循环都是循环四次,每次都有自己一个密钥库,主要是与或非操作

当然在这个过程中我们也深入了解了常用的位操作的ARM指令知识点:

1、位指令

AND指令进行逻辑”与”操作;
ORR指令进行逻辑”或”操作;
EOR指令进行逻辑”异或”操作;==》F = ^AB + A^B
ORN指令进行逻辑”或非”操作;==》F = ^(A+B)
BIC指令进行”RN AND NOT RM”操作

2、批量存储读取指令
LDM:(load  much)多数据加载,将地址上的值加载到寄存器上
STM:(store much)多数据存储,将寄存器的值存到地址上
主要用途:现场保护、数据复制、参数传送等,共有8种模式(前面4种用于数据块的传输,后面4种是堆栈操作)如下:
IA:每次传送后地址加4,其中的寄存器从左到右执行,例如:STMIA R0,{R1,LR} 先存R1,再存LR
IB:每次传送前地址加4,同上
DA:每次传送后地址减4,其中的寄存器从右到左执行,例如:STMDA R0,{R1,LR} 先存LR,再存R1
DB:每次传送前地址减4,同上
FD:满递减堆栈 (每次传送前地址减4)
FA:满递增堆栈 (每次传送后地址减4)
ED:空递减堆栈 (每次传送前地址加4)
EA:空递增堆栈 (每次传送后地址加4)
注意:其中在数据块的传输中是STMMDB和LDMIA对应,STMMIA和LDMDB对应,而在堆栈操作是STMFD和LDMFD对应,STMFA和LDMFA对应

3、穿插指令

这个在我们分析的过程中的确可以发现在你分析的过程中突然多了几条和前后指令没关系的指令,主要通过指令的返回值是否参与后面指令的执行结果,没有的话就是穿插指令,不要被穿插指令混淆

 

本文的目的只有一个就是学习逆向分析技巧,如果有人利用本文技术进行非法操作带来的后果都是操作者自己承担,和本文以及本文作者没有任何关系,本文涉及到的代码项目可以去编码美丽小密圈自取,欢迎加入小密圈一起学习探讨技术

 

五、总结

本文到此为止就介绍完了最右这款App的签名算法,但是不是最终篇,最终篇会在全面总结一下从拿到最右App到最终解密的整个操作过程,为什么我们会找到5070这个加密函数?这个在之前说过会在最终篇介绍,而且会介绍IDA的常用方法和问题解决方案,当然这三篇文章大家一定要详细对照着样本进行操作,这样对熟悉ARM指令非常有用,这三篇文章的目的只有一个就是通过这个样本给大家从0开始学习ARM指令,后面很多应用的加密方法都是放在底层,ARM指令是必须要要掌握的知识点,感谢大家支持期待最终篇!

 

《Android应用安全防护和逆向分析》

点击立即购买:京东  天猫  

更多内容:点击这里

关注微信公众号,最新技术干货实时推送

转载请注明:尼古拉斯.赵四 » Android逆向之旅—最右App的签名算法解析(ARM指令学习不舍篇)

喜欢 (4)or分享 (0)