ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结

1 前言

最近博主在学习Android逆向的时候,参照吾爱破解论坛的《教我兄弟学Android逆向系列课程》学习的时候,学到第8章《教我兄弟学Android逆向08 IDA爆破签名验证》的时候,开始上手 ida pro 反汇编 so 库,在动手修改 so 库指令的时候遇到了困难,经过一番研究,终于搞懂了在 ida pro 中修改 so 库中 arm 汇编指令的方法,并完成了课后习题:

爆破李华Demo中的用户名和密码 要求输入任意用户名和密码 会提示登陆成功

ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第1张图片

于是写下这篇博客记录研究过程,并把被作者一笔带过的最核心问题如何修改so库讲清楚。
附上原吾爱破解教程地址:

《教我兄弟学Android逆向系列课程+附件导航帖》
《教我兄弟学Android逆向08 IDA爆破签名验证》

2 准备

(1) 下载 ida pro 7.0

原贴中使用的是ida pro 6.6,博主使用的是 ida pro 7.0,这一版是免安装的绿色版,下载地址:

IDA Pro 7.0 绿色版

(2) 下载 Android Killer

AndroidKiller_v1.3.1.zip

(3) 下载夜神模拟器

如果有真机也可以使用真机,这里我们使用夜神模拟器:

夜神模拟器官网

(4) 下载demo

原贴中的demo:黑宝宝.apk 下载地址:

链接:https://pan.baidu.com/s/1h6pX2ARE3qtiKiYbcnJ-3g 密码:duv5

(5) 下载 jni.h

下载链接:https://pan.baidu.com/s/1n16NEx67zLHfGtVpU-CKAA 密码:7xg6

(6) Arm指令手册

https://download.csdn.net/download/fuchaosz/12243691

(7) arm指令转换网站

Arm汇编指令转机器码网站:http://armconverter.com/

3 SO库的选择

首先把 黑宝宝.apk 拖进 Android Killer 中反编译,我们可以看到在 lib 中有三个 libJniTes.so :
ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第2张图片

armeabi     : 32位 arm cpu 库,几乎所有手机都支持
armeabi-v7a : 64位 arm cpu 库,现在我们买的手机基本上都是64位的cpu了
x86         : 在电脑上运行的模拟器或者基于Intel x86的平板电脑上用的

这里要说一下历史,在电脑CPU芯片领域,Intel独霸天下,x86代表32位cpu,x86_64代表64位的cpu,cpu位数增加不仅意味着运算速度更快,同时还代表着可以使用的内存更大,例如32位系统最大只能使用4G内存,所以windos xp上装8G内存条完全是浪费;但在手机领域arm才是龙头老大,可以说垄断了市场,因为arm制定了标准,所以,我们经常听说的高通骁龙芯片,虽然芯片是高通生产的,但却是arm的架构和指令集,因为我们的app到底层最终都被翻译成了arm指令。
这次,我们选择 armeabi 目录下的 libJniTes.so
ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第3张图片

因为这是几乎所有手机都支持的,而且网上资料是最多的,并且这三个版本的库在指令上有很多差别,例如:在 armeabi 中跳转指令是 BNE:
ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第4张图片
但在 armeabi-v7a 中跳转指令却是 CBNZ:
ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第5张图片
在 x86 中跳转指令则是 jnz:

ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第6张图片
所以,作为初学者,我们选择 armeabi 下的 libJniTest.so 库作为研究对象,便于在网上查找资料。
另外,值得一提的是,以前很多 app 只提供 armeabi 版本的 so 库,所以在安卓模拟器上很多app运行不了,尤其是很多游戏无法在模拟器上运行,好在现在主流安卓模拟器已经可以运行 armeabi 库了,例如夜神模拟器,所以,这次我们修改后的 armeabi 下的 libJniTest.so 是可以运行在夜神模拟器上的。

4 引出问题

参照 《教我兄弟学Android逆向08 IDA爆破签名验证》 这篇文章一步一步来,先将 黑宝宝.apk 拖入 Android Killer 中反编译,然后再将 armeabi 目录下的 libJniTest.so 用 ida pro 7.0 打开,接着在 Export 窗口中搜索 check 函数,接着导入 jni.h,接着替换 _JniEnvenv 两个参数,然后就到了关键的地方,在函数流程图中,作者要改变函数流程,要将 BNE 改为 BEQ,如下图:
ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第7张图片
ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第8张图片
看关键的代码:

cmp R0,#0
BNE loc_F62

cmp 是比较两个数是否相等
loc_F62 代表的函数是 “签名不一致,退出程序”
BNE 指不相等的时候跳转(branch not equal)
BEQ 指相等的时候跳转(branch equal)

结合反汇编后的C代码和函数流程图,我们可以很明显的知道这段代码的含义就是:如果签名不相等,则跳转到loc_F62,即直接退出程序。很明显我们回编译后的apk签名和作者预设的签名是绝对不可能一致的,所以会直接退出程序。

搞清楚了逻辑,那么爆破这个apk的签名其实就是把 BNE loc_F62 改为 BEQ loc_F62,即签名一致则跳转到 loc_F62退出程序,我们的签名和作者签名一定不同,所以程序不会直接退出,爆破成功。

接下来就是核心的地方:如何将 BNE 改为 BEQ ?

参照原贴方法,鼠标点击BNE那一行,然后View->Open subviews->Hex dump 查看那一行指令对应的机器码:

ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第9张图片
ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第10张图片
10 D1上点右键Edit,修改为10 D0:
ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第11张图片
接着右键apply changes:
ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第12张图片
再回到函数流程图,可以看到 BNE已经变为BEQ

ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第13张图片
那么问题来了:为什么要这样改?

我们可以看到上图第2个BEQ loc_F74命令对应的机器码是09 D0:
ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第14张图片
铺垫结束,问题来了:我们要修改操作码而不是操作数,为什么是把10 D1改为10 D0,而不是改为09 D1?

这个问题是最核心的问题,但是原帖里面作者一带而过了,加入现在我要把BNE指令改为无条件跳转指令 B 又该怎么改呢?

博主在这里卡了一天,最后经过实践得出了结论,所以写了这篇博客。下面我们来深入探讨这个问题。

5 深入问题

先来回顾一下《汇编》和《计算机组成原理》的内容,计算机只认识0和1,所有的操作听歌、看视频、打游戏等等,归根结底都是对0和1进行操作,在Intel x86架构中,一条指令是32位,因为32位CPU一次必须读入32位,不多不少,这一次读入的32个0或1就是一条指令,那么一秒钟可以读多少次呢,这就是cpu的主频决定的,所以理论上,cpu主频越高,一秒钟执行的指令越多,性能越高。接着思考,cpu是如何知道这32个数字(一条指令)中哪些数字代表操作,哪些数字代表被操作的数据呢,这就是指令格式,x86指令格式如下:

一条指令(32位) = 操作码(前16位):操作数(后16位)

操作码代表做哪些运算,例如:ADD、MOV、B 等等
操作数就代表被操作的数据,即可以是数据也可以是地址。

前面16位操作码排列组合起来就是指令集,每家厂商对指令集是不同,arm指令集也是同理,这就是汇编指令和机器码的关系。当然,实际指令格式不一定是这样平分的,但都是由操作码和操作数两部分组成的。

回到正题,我们梳理一下:

BNE loc_F62  对应机器码 10 D1 对应二进制 0001 0000 1101  0001
BEQ loc_F74  对应机器码 09 D0 对应二进制 0000 1001 1101  0000

根据上面的知识,我们要改的是操作码,所以应该是把 操作码 10 改为 09 才对呀,即改为 09 D1 而不是 10 D0,博主被这个问题深深的卡住了,于是查阅资料,搜到了arm的官方指令手册,查看了官方给的机器码表,结果更懵逼了。

打开arm指令手册,并没有BNE、BEQ指令,再看B指令的解释,翻到A6-3.1:

ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第15张图片
对照下面的条件码表:
ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第16张图片
通过官方手册和条件码表,我们组合起来就得到BNE指令的机器码:

1101 + 0001 + 8_bit_signed_offset

可是ida pro里面 BNE loc_F62 对应的正确的机器码明明是:

0001 0000 1101 0001

这就把博主整崩溃了,经过一天的谷歌百度,再经过反复实践,博主得出以下结论(敲黑板、划重点、记笔记),该结论没有找到确切资料,但抓住老鼠就是好猫。

6 结论

ida pro 反汇编 Android SO库(armeabi版本) ,一条汇编指令如果由4个十六进制数组成的,则前面2个(十六进制数)是操作数,后面2个(十六进制数)才是操作码,即so库的指令格式如下:

一条SO的汇编指令(16位) = 操作数(前8位) :操作码(后8位)

划重点:
1、一条指令是4个十六进制数
2、前面2个是操作数,后面2个才是操作码
3、如果你发现一条指令是8个十六进制数(例如BL),那么以上结论不适用
4、SO库的字节数是不能变的,我们只能替换,不能增删

注意:4位十六进制机器码的指令都遵循上述规律,但是8位十六进制的指令规律还没有总结出来,如果你知道了,欢迎给我留言。

7 解决问题

根据以上结论,我们很容易就能理解为什么要把 D1 改为 D0 了,因为操作码是后面两位,根据arm手册和条件码表,我们很容易组合出来BNE的操作码是D1BEQ 的操作码是 D0,所以,正确的操作是把 10 D1 改为 10 D0 ,问题解决。

如果我们每次修改指令都要去查arm手册的话,那是very dan疼的,那英文太酸爽,arm生怕多写几句浪费纸,所以,我们在实际操作中有以下两种方法来修改指令:

(1) 方法一:照葫芦画瓢

既然我们已经知道改指令就是改后面2个数,那就so easy了,我们只要在ida pro里面查看目标指令的机器码后面2个数,然后照着改就可以了,例如:我们接着要把BEQ loc_F62 改为 B loc_F62 呢,B指令是无条件跳转指令,首先鼠标点击到其他 B 指令上,View->Open subviews->Hex dump 查看机器码为14 E0

ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第17张图片
ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第18张图片
接着我们将 10 D0 改为 10 E0,这样 BEQ loc_F62 就又被改为了 B loc_F62
ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第19张图片
ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第20张图片
改动成功,成功应用了我们总结的知识,这里只是个演示,改动完后记得还原。

(2) 方法二:在线转换机器码

我们还可以打开以下网站:

http://armconverter.com/

直接输入我们想要的指令,直接转换为机器码,一个小技巧,如果需要操作数的话可以用#0,例如我们想知道B指令的机器码,则可以构造一条语句 B #0 就可以了
ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第21张图片
从这个网站我们也可以发现一点,就是ida pro反编译SO库用的是 Thumb-2 指令集,这个指令集是 arm 指令集的子集,标准arm指令集是操作码后跟操作数,这个thumb-2指令集反过来了,坑啊,关键是网上还查不到资料。

8 完成课后练习

理论知识学习完了,接着我们来完成课后习题:

爆破李华Demo中的用户名和密码 要求输入任意用户名和密码 会提示登陆成功

通过第4节的C语言代码我们可以看到,check这个函数很简单,除了检验签名外,就是当你输入用户名koudai和密码black后显示登陆成功,只要一个输入不对,就登陆失败,博主看了网上很多破解教程,都是把原来等于koudai 和 等于black的判断逻辑改为不等于,这样有个很明显的问题,虽然输入其他用户名密码可以登陆成功,但当我们用户名输入koudai 或 密码输入 black就会登陆失败,不符合题意:要求输入任意用户名和密码 会提示登陆成功。

所以,这里我们要改函数调用逻辑,要求不管输入什么,都要跳转到正确的函数中,还记得上节提到的B指令吗,这就是无条件跳转指令,下面开始。

首先解决输入任意用户名调到正确的函数,先看流程图:
ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第22张图片
很明显不管输入什么,最后必须无条件跳转到loc_F74所在的函数,那就简单了,直接BEQ loc_F74改为B loc_F74,还记得怎么改为B指令吗,这里把09 D0改为09 E0
ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第23张图片
改完后我们发现,无论输入什么都会进入校验密码的函数模块,那么用户名输入问题解决,下面处理密码输入。

仔细看上面的流程图,校验密码的函数有个函数首地址loc_F74,但是登陆成功的函数块没有首地址,反而是登陆失败的函数有首地址loc_F8C,所以照葫芦画瓢行不通了,怎么办呢?

博主在这里也卡了一下,后来想了一下,BNE loc_F8C这条指令真是多余,要是没有这条指令不就可以直接进入登陆成功的函数块了吗,那么问题又来了:怎么清除一条指令?

前面说了,so库只能替换,不能增删,网上搜了一下,说是有个nop指令用于清除目标指令,就是00 00,于是直接把这条指令的4个数全部改为0,即04 D1改为00 00,然后就可以了,看改完后的流程图:

ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第24张图片
看流程图,不管用户名和密码输入什么,都会显示登陆成功,至此,作业完成,接着把ida pro中的改动保存到so文件:

ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第25张图片
关闭ida pro,将修改后的libJniTest.so文件替换掉原来armeabi目录下的so文件,接着删除armeabi-v7a和x86目录,再回编译后安装到夜神模拟器,输入任意字符串都可以提示登陆成功:

ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第26张图片
ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第27张图片
ida pro 反汇编 Android so 库后修改 arm 汇编指令的方法总结_第28张图片

9 总结

通过本篇文章的学习,我们掌握了4个十六进制数指令的格式和修改方法,同时我们学会了修改 B系列 跳转指令,并且明白了为什么要这样改,以后就可以随心所欲的修改函数的流程了,所以,很多高级语言的语法和保护措施在底层都失效了,这就是无法无天的底层,也是搞底层的乐趣。

当然,8个十六进制数的指令(例如BL指令)我们还没有弄清楚规律,但是目前已经够用了,所以,以后再来研究,如果你弄清楚了,欢迎在下方给我留言。

10 补充

如果你在实践中遇到了 ida pro 7.0 无法显示中文的问题,请参照下面这篇博客解决:

ida pro 7.0 无法显示中文的问题解决方法

如果你在用 Android Killer 回编译的时候遇到错误导致回编译失败,请参照下面这篇文章解决:

Android Killer反编译失败:No resource identifier found for attribute 问题解决方法

11 转载请注明来自“梧桐那时雨”的博客:https://blog.csdn.net/fuchaosz/article/details/104804026

Tips:
如果觉得这篇博客对你有帮助或者喜欢博主的写作风格,就关注一下博主或者给博主留个言呗,鼓励博主创作出更多优质博客,Thank you.

你可能感兴趣的:(Android逆向笔记,反编译,ida,so修改)