本文介绍如何用IDA进行动态调试及部分ARM指令的学习。
环境:已root的安卓手机一部,IDA pro 6.8,win7系统。
下载样本app,并已确认可调试(debuggable = true),下文不表:
http://pan.baidu.com/s/1jG22HMY
一 手机连接电脑,打开USB调试模式,并在电脑端打开IDA的这个目录:
二 通过adb push android_server /data/local/tmp/ 命令将文件推送至手机:
三 开启服务,注意这个窗口不能关闭:
四 端口转发:
五 打开IDA pro,我这里选择的是 32位的版本。
六 开启远程调试,注意先打开手机上的样本app,调试需要该进程
七 如图设置好 Hostname和 port:
PS:端口是可以改的,有些app会检测端口进行反调试,通过修改端口可以绕过端口检测。
八 点击 OK按钮,并查找样本包名:
九 加载完成后的界面:
十 在右边的 Modules 面板搜索 so文件名:
十一 双击需要进行调试的JNI函数:
十二 设置断点,如图操作,其快捷键是 F2:
十三 打开手机上的样例 app,按下F9,我们发现IDA停在了断点处:
这样,我们就能愉快的进行动态调试了。
下面我们来分析这里面的部分汇编代码:
PUSH {R3,LR} ;将寄存器R3和LR的值分别压入栈中,
R0-R3:通用寄存器,用于函数参数及返回值的传递
LR: 连接返回寄存器,保留函数返回后,下一条应该执行的指令。
POP {R3,PC} ;将栈顶的值分别传递给寄存器R3和PC
PC: 程序寄存器,保存下一条CPU即将执行指令的地址,这里原本LR里面的值传递给了PC寄存器,这样就返回到了当初函数调用的地方。
SP:栈顶指针寄存器,用于存放栈顶地址
按F8进行单步调试,继续分析下一条指令。
我们可以看看按下F8后,SP的值。
CMP R2, #2 ;比较 R2寄存器里面的值与2的大小。比较结果通过标志寄存器来保存。目前R2的值是5,因此两个值是不等的。
执行比较指令后,发现Z的值变为了0,也就是说比较的结果不为0.
继续执行下一条指令:
BEQ loc_D7FD0C60 ;Z标志值为1则跳转,即上一调比较指令的结果为0;否则继续向下执行。
继续往下走,走到这里:
BNE loc_D7FD0C5A ;Z标志值为0则跳转,即上一调比较指令的结果不为0;否则继续向下执行,与BEQ是相反的。
通过上条指令的跳转后,来到了这里:
LDR R1, =(aNormalUser - 0xD7FD0C60) ;把栈上内容载入一寄存器中,执行后,我们看到R1的值是:
ADD R1, PC ; "Normal User"
我们看到IDA后面自己的注释是 "Normal User",也就是说这两条指令的目的是把 字符串 "Normal User" 所在的地址传递给R1,我们执行后看看R1的值:
我们在十六进制面板看看 R1值所对应的是什么:
鼠标在 十六进制面板 点击一下,然后按下G键,输入R1的值:点击OK后,跳转到了这里:
这里确实是存放 "Normal User"这个字符串的地址。
回到汇编窗口,继续执行下面这条指令:
B loc_D7FD0C64 ;无条件跳转到 loc_D7FD0C64 处.
执行后来到了这里:
LDR R2, [R0] ;将R0的值为地址,赋值给R2
此时R0的值 F39312A0,我们看看对应的值是什么:运行后,看看R2的值:可以看到,确实就是 R1地址的值:F362E54C。
下一条指令:MOVS R3, #0x29C
即将 0x29C保存到R3寄存器:
下一条指令:LDR R3, [R2,R3]
将 R2寄存器的值与R3寄存器的值相加,得到一个地址,然后再取改地址上的值给R3;R2 = 0xF362E54C,R3 = 0x0000029C,相加得到这个地址值:
0xF362E7E8,看看是什么:
按下F8,看看R3的值:下一条指令:BLX R3;
BLX 指令从ARM 指令集跳转到指令中所指定的目标地址,并将处理器的工作状态有ARM 状态切换到Thumb 状态,该指令同时将PC 的当前内容保存到寄存器R14 中。因此,当子程序使用Thumb 指令集,而调用者使用ARM 指令集时,可以通过BLX 指令实现子程序的调用和处理器工作状态的切换。
同时,子程序的返回可以通过将寄存器R14 值复制到PC 中来完成。
执行后,看到很多寄存器的值都变化了:下一条指令:
POP {R3,PC} ;将栈顶的值分别传递给R3和PC。
执行后,看看R3和PC的值:
好了,今天就介绍都这里吧,以后遇到看不懂的指令可以直接百度,如果要加深印象,可以动态调试看寄存器的变化或者内存的变化。