android平台arm指令学习和调试
一、Ndk下内联汇编
跟vc下一样,ndk编译环境下也能使用内联汇编,如下:
include
intmy_thumb(intdummy)
{
__asm__(
“movr0,#1\t\n”
“movr1,#2\t\n”
“addr0,r0,r1\t\n”
“bxlr”
);
}
intmain()
{
intn=my_thumb(12);
printf(“result:%08x”,n);
return0;
}
再看看使用标签的例子:
#include
intmy_thumb(intdummy)
{
__asm(
“movr0,#0x1\t\n”
“ldrr0,__start\t\n”
“adrr0,__start\t\n”
“ldrr0,=__start\t\n”
“__start:\t\n”
“nop\t\n”
);
}
intmain()
{
intn=my_thumb(12);
printf(“result:%08x”,n);
return0;
}
二、编译
创建android.mk文件,然后使用ndk-build进行编译
1)编译一个可执行程序,Android.mk文件的内容如下:
LOCAL_PATH:=$(callmy-dir)
include$(CLEAR_VARS)
LOCAL_MODULE:=helloa
LOCAL_SRC_FILES:=testHello.c
include$(BUILD_EXECUTABLE)
2)编译一个so,android.mk文件内容如下:
LOCAL_PATH:=$(callmy-dir)
include$(CLEAR_VARS)
LOCAL_MODULE:=testJniSo
LOCAL_SRC_FILES:=testJniSo.c
include$(BUILD_SHARED_LIBRARY)
3)必须有一个jni目录,目录结构:
4)这样之后,就可以在cmd窗口中,切换到代码目录,使用ndk-build命令进行编译。
三、调试可执行程序
1)Push编译好的elf到样机中
2)使用gdbserver启动该文件
“/data/local/gdbserver:12345/data/local/helloa”
3)adbforwardtcp:12345tcp:12345
4)启动gdb
5)Targetremote127.0.0.1:12345
6)Ni是单步,setdisassemble-nexton下一句指令显示反汇编,使用setarmforce-modearm或者setarmforce-modethumb让gdb切换thumb和arm代码显示。
7)Display/i$pc显示当前的代码
8)Continue就是windbg的f5,od的f9
9)Infobreakpoints显示断点,而delete删除断点disable禁用断点
10)Disas0xAAAA,+20(20字节的数据)显示反汇编
11)调试比较
Gdbarm和ida反汇编比较:
四、调试so
以爱加密为例,进行调试,范例是看雪求助帖给出的,链接如下:
http://bbs.pediy.com/showthread.php?t=184375
调试方法
1)So中实现的方法,程序跑起来再附加的话,该方法可能已经执行完。看雪上给出方法步骤较多,应用和底层都调试,有点复杂如下:
我试出来的方法,对so文件进行修改,让其在入口处直接调用它本身。需要注意的是,必须在so的入口函数处修改,如果在内部调用函数修改,app会自动退出。
2)gdb的调试,使用ndk下自带的gdbserver和gdb进行调试
3)将修改的so导入到手机该应用的lib目录下,覆盖之前的so文件。然后运行app,出现只有边界的黑框。Gdbserver附加,使用set命令,将bl调用自身的命令改回去,则可以进行正常调试了。
4)模块基地址的获取cat/proc/pid/maps找到
5)Dump内存的命令式dumpbinarymemoryc:\xxxstartAdendAd
6)小细节要注意的是:.init_proc的地址是0x31cb9,ida直接点过去,是一堆数据,摁c无法转成代码
因为arm指令和thumb指令是2字节或者4字节对齐的,所以,改地址加1应该是真正的地址。将该地址修改为调用自身,然后gdb附加,成功断下来,然后输入“ir$lr”查看返回值,如下
该地址恰巧是在linker模块中,
此处的在linker中的代码如下:
7)至于为什么系统的linker会调用.init_proc,不得而知。该so的oep为空,没有section表,但是有dynamicsection表。自己写一个so,并没有函数“init_proc”的导出,需要再研究下elf文件格式。
自解码
本身的so是经过加密的,解密的代码是在.init_proc+1的位置,字节码的代码较长,部分代码如下:
1)windows下逃避断点的方法,就是利用“codeshadow”的方式,在申请内存,然后将代码拷贝到指定的申请的空间上执行,这样,直接断原代码是断不到的。但是,直接搜索二进制就可以找到这样的镜像代码。
如下,之前应该运行的代码:
拷贝到其它地方执行的代码:
2)计算指定函数的汇编指令长度:代码要进行拷贝执行,必须计算拷贝代码的长度。爱加密计算拷贝代码的长度方式是通过call来计算的。函数实现的排布如下:
__asmcalltargetFunc
xxxxxx
xxxxxx
xxxxxx
xxxxxx
xxxxxx
xxxxxx
VoidtargetFunc
而call的指令是:
00f0是指令opcode,c0与跳转的距离有关,计算公式是(targetAddr–curAddr)/2–2=opdataLen。那么知道c0就知道中间xxx代码的长度了。
3)爱加密处理的数据有三块,第一块未知,第二块是压缩过的指令数据,第三块是需要拷贝到堆上的代
android平台arm指令学习和调 相关文章:
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)
- Windows CE 进程、线程和内存管理(二)(11-09)