android 逆向工程 IDA 学习

来源:互联网 发布:没有域名需要备案吗 编辑:程序博客网 时间:2024/05/16 10:13

android 逆向工程 IDA

Android逆向之旅—动态方式破解apk进阶篇(IDA调试so源码)
http://blog.csdn.net/jiangwei0910410003/article/details/51500328

IDA
一些app会把一些重要的功能放到native层(一般native层使用的是so库文件,.so文件可分为代码.so和数据.so)
IDA可用于调试so文件、从而达到破解app native的目的(调试主要就是查找、定位、调试arm汇编与c代码函数部分)

IDA调试的整体原理
首先在被调试端安放一个程序android_server,用于IDA端和调试设备通信,
因为要附加进程,所以这个程序必须要用root身份运行,这个程序起来之后,就会开启一个端口23946,我们在使用adb forward进行端口转发到远程调试端,这时候IDA就可以和调试端的android_server进行通信了。后面获取设备的进程列表,附加进程,传递调试信息,都可以使用这个通信机制完成即可。IDA可以获取被调试的进程的内存数据,一般是在 /proc/[pid]maps 文件中,所以我们在使用Ctrl+S可以查看所有的so文件的基地址,可以遍历maps文件即可做到

IDA工具使用:
IDA工具静态分析so文件,通过分析arm指令,来获取破解信息,比如打印的log信息
Function Window:
对应的so函数区域,可使用ctrl+f进行函数的搜索
IDA View:
对应的so中代码指令视图,可查看具体函数对应的arm指令代码
Hex View:
对应的so的十六进制数据视图,可查看arm指令对应的数据等

1、将arm指令转化成可读的C语言:
选中需要翻译成C语言的函数,然后按下F5
2、还原JNI函数方法名:(一般JNI函数方法名首先是一个指针加上一个数字)
选中a1变量,然后按一下y键,ennter “JNIEnv *”
3、Shirt+F12快捷键:
速度打开so中所有的字符串内容窗口
4、Ctrl+S快捷键,有两个用途,在正常打开so文件的IDA View视图的时候,可以查看so对应的Segement信息
(Segement开始位置和结束位置就是这个so文件映射到内存的位置)
5、G快捷键:
在IDA调试页面的时候,我们可以使用S键快速跳转到指定的内存位置(该 地址得配合segment的开始位置定位真正的跳转地址–绝对地址)
6、选择函数地址,点击P键就可以看到arm指令源码了
(G键可以在调试界面,快速跳转到指定的绝对地址,进行下断点调试,这里如果跳转到目的地址之后,发现是DCB数据的话,可以在使用P键,进行转化)
7、调试快捷键:F8单步调试,F7单步进入调试
8、点击F9快捷键,或者是点击运行按钮,即可运行程序

常用的ARM指令集知识
IDA打开so之后是汇编指令代码,arm指令中的寻址方式,寄存器,常用指令:
1、arm指令中的寻址方式
1>. 立即数寻址
也叫立即寻址,是一种特殊的寻址方式,操作数本身包含在指令中,只要取出指令也就取到了操作数。这个操作数叫做立即数,对应的寻址方式叫做立即寻址
MOV R0,#64;//R0 ← 64
2>. 寄存器寻址
寄存器寻址就是利用寄存器中的数值作为操作数,也称为寄存器直接寻址。
ADD R0,R1, R2;//R0 ← R1 + R2
3>. 寄存器间接寻址
寄存器间接寻址就是把寄存器中的值作为地址,再通过这个地址去取得操作数,操作数本身存放在存储器中。
LDR R0,[R1];//R0 ←[R1]
4>. 寄存器偏移寻址
ARM指令集特有的寻址方式,它是在寄存器寻址得到操作数后再进行移位操作,得到最终的操作数。
MOV R0,R2,LSL #3;//R0←R2*8 => R2的值左移3位,结果赋给R0。
5>. 寄存器基址、变址寻址
寄存器基址变址寻址又称为基址变址寻址,它是在寄存器间接寻址的基础上扩展来的。它将寄存器(该寄存器一般称作基址寄存器)中的值与指令中给出的地址偏移量相加,从而得到一个地址,通过这个地址取得操作数。
LDR R0,[R1,#4] ;//R0 ←[R1 + 4],将R1的内容加上4形成操作数的地址,取得的操作数存入寄存器R0中。
6>. 多寄存器寻址
可以一次完成多个寄存器值的传送
LDMIA R0,{R1,R2,R3,R4} ;//R1←[R0],R2←[R0+4],R3←[R0+8],R4←[R0+12]
7>. 堆栈寻址
堆栈是一种数据结构,按先进后出(First In Last Out,FILO)的方式工作,使用堆栈指针(Stack Pointer, SP)指示当前的操作位置,堆栈指针总是指向栈顶。
STMFD SP!,{R1-R7, LR};//将R1-R7, LR压入堆栈。满递减堆栈 full decrease。
LDMED SP!,{R1-R7, LR};//将堆栈中的数据取回到R1-R7, LR寄存器。空递减堆栈 empty decrease。

2、ARM中的寄存器
R0-R3:用于函数参数及返回值的传递
R4-R6, R8, R10-R11:没有特殊规定,就是普通的通用寄存器
R7:
栈帧指针(Frame Pointer).指向前一个保存的栈帧(stack frame)和链接寄存器(link register – lr)在栈上的地址。
R9:操作系统保留
R12: IP(intra-procedure scratch )
R13: SP(stack pointer)栈顶指针
R14: LR(link register)存放函数的返回地址。
R15: PC(program counter)指向当前指令地址。

3、ARM中的常用指令:
ADD 加指令
SUB 减指令
STR 把寄存器内容存到栈上去
LDR 把栈上内容载入一寄存器中,W 是一个可选的指令宽度说明符。它不会影响为此指令的行为,它只是确保生成32位指令
BL 执行函数调用,并把使lr指向调用者(caller)的下一条指令,即函数的返回地址
BLX 同上,但是在ARM和thumb指令集间切换。
CMP 指令进行比较两个操作数的大小

gdb和gdbserver在调试的时候,必须注入到被调试的程序进程中,要调试一个应用进程的话,必须要注入他内部,IDA调试so也是这个原理,他需要注入(Attach附加)进程,才能进行调试,但IDA没有自己弄了一个类似于gdbserver这样的工具,那就是android_server了,所以他需要运行在设备中,保证和PC端的IDA进行通信,比如获取设备的进程信息,具体进程的so内存地址,调试信息等
相当于执行:
1、在IDA安装目录下获取android_server命令文件,
2、把android_server保存到设备的/data目录下,修改一下他的运行权限,然后必须在root环境下运行,因为他要做注入进程操作,必须要root
3、配置端口
adb forward tcp:远端设备端口号(进行调试程序端) tcp:本地设备端口(被调试程序端)
(IDA进行连接的时候,发现IDA他把这个端口设置死了,就是23946)

IDA进行调试so的步骤(双IDA调试),分两种情况
破解apk的流程:
1、通过解压apk文件,得到对应的so文件,然后使用IDA工具打开so,找到指定的native层函数
2、通过IDA中的一些快捷键:F5,Ctrl+S,Y等键来静态分析函数的arm指令,大致了解函数的执行流程
此为静态调试
3、然后再次打开一个IDA来进行调试so(动态(运行)调试):

1、IDA调试无反调试的so代码步骤:
1、把IDA安装目录中的android_server拷贝到设备的指定目录中,修改android_server的权限,并且用root方式运行起来,监听23946端口
2、使用adb forward命令进行端口的转发,将设备被调试端的端口转发到远程调试端中
3、双开IDA工具,一个是用来打开so文件,进行文件分析,比如简单分析arm指令代码,知道大体逻辑,还有就是找到具体函数的相对位置等信息,还有一个IDA是用来调试so文件的,我们在Debugger选项中设置Debugger Option,然后附加需要调试的进程
4、进入调试页面之后,通过Ctrl+S和G快捷键,定位到需要调试的关键函数,进行下断点
5、点击运行或者快捷键F9,触发程序的关键函数,然后进入断点,使用F8单步调试,F7单步跳入调试,在调试的过程中主要观察BL,BLX指令,以及CMP和CBZ等比较指令,然后在查看具体的寄存器的值。

2、IDA调试有反调试的so代码步骤:
1、查看apk是否为可调式状态,可以使用aapt命令查看他的AndroidManifest.xml文件中的android:debuggeable属性是否为true,如果不是debug状态,那么就需要手动的添加这个属性,然后回编译,在签名打包从新安装
查看android:debuggable属性,命令行执行:
aapt xmltrace myapp.apk AndroidManifest.xml > ./myapp.txt
start myapp.txt
回编译:java -jar apktool.jar b -d out -o debug.apk
签名apk:java -jar .\sign\signapk.jar .\sign\testkey.x509.pem .\sign\testkey.pk8 debug.apk debug.sig.apk
2、使用adb shell am start -D -n com.sample.myapp/.MainActivity 命令启动程序,出于wait Debug状态
System.loadLibrary方法之前加入waitForDebugger代码即可,或用am命令可以启动一个程序,用debug方式启动,使程序停在static{ System.loadLibrary(“*“); };之前
3、打开IDA,进行进程附加,进入到调试页面
4、使用 jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700 命令attach之前的debug状态,让程序正常运行
5、设置Debug Option选项,设置Suspend on library start/exit/Suspend on library load/unload/Suspend on process entry point选项
6、点击运行按钮或者F9键,程序运行停止在linker模块中,这时候表示so文件加载进来了,我们通过Ctrl+S和G键跳转到JNI_OnLoad函数出,进行下断点
7、然后继续运行,进入JNI_OnLoad断点处,使用F8进行单步调试,F7进行单步跳入调试,找到反调试代码处
8、然后使用二进制软件修改反调试代码为nop指令,即00值
9、修改之后,在替换原来的so文件,进行回编译,从新签名打包安装即可
10、按照上面的无反调试的so代码步骤即可

//.init_array是一个so最先加载的一个段信息,时机最早,现在一般so解密操作都是在这里做的
//JNI_OnLoad是so被System.loadLibrary调用的时候执行,他的时机要早于哪些native方法执行,但是没有.init_array时机早

反调试策略原理:
IDA是使用android_server在root环境下注入到被调试的进程中,那么这里用到一个技术就是Linux中的ptrace,Android中如果一个进程被另外一个进程ptrace了之后,在他的status文件中有一个字段:TracerPid可以标识是被哪个进程trace了,可以使用命令查看我们的被调试的进行信息:
status文件在:/proc/[mypid]/status

反调试检测(安全检测技术):
现在很多应用防止别的进程调试或者注入,通常会用自我检测装置,原理就是循环检测/proc/[mypid]/status文件,查看他的TracerPid字段是否为0,如果不为0,表示被其他进程trace了,那么这时候就直接退出程序。因为现在的IDA调试时需要进程的注入,进程注入现在都是使用Linux中的ptrace机制,那么这里的TracePid就可以记录trace的pid,我们可以发现我们的程序被那个进程注入了,或者是被他在调试。进而采取一些措施

android设备端操作(命令行窗口1):
1、adb install myapp.apk // 安装应用
2、adb shell am start -D -n “com.sample.myapp/.MainActivity” //前提apk中的 AndroidManifest.xml中debuggable为true
3、adb shell
4、ps | grep myapp // 查看运行端口号
5、adb push android_server /data/local/tmp //将IDA中android_server文件push到android设备
6、adb shell
7、chmod 755 android_server
// 改变执行权限,此时设备上运行的应用会等待附加调试后才会继续执行下去

命令行窗口2:
1、adb forward tcp:23946 tcp23946
2、jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=5005

运行IDA
1、设置Debugger->Attach-> Remote ARMLinux/Android debugger->Debug options
Hostname为localhost port:23946
Debugger setup 界面,勾选一下两项:
Suspend on process entry point
Suspend on thread start/ext
2、选中android设备中自己要调试的进程,如name: com.sample.myapp的进程
3、命令行窗口2、执行:
jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=5005 //jdb命令就是为了让命令行窗口1中的am start * 指令开启的应用继续执行
4、在IDA界面,找到想要设置好断点后地址设置断点,按F9继续执行下去,刚才的jdb命令就会看到结果

原创粉丝点击