最近在解决应用程序崩溃问题时,需要反汇编进行异常代码的定位,所以需要系统的再学习一下汇编指令。
环境
ARM汇编指令系统
基本知识
PC is at 0xb6c46cf8
LR is at 0x63f28
pc : [<b6c46cf8>] lr : [<00063f28>] psr: a0000030
sp : bee21b68 ip : 000781ec fp : bee21c5c
r10: 00077528 r9 : 01d7ec99 r8 : 00000fa8
r7 : 00000000 r6 : 00000001 r5 : 00000001 r4 : 0007ab3c
r3 : 00000000 r2 : b6fad000 r1 : 00000000 r0 : 00000000
PC:程序计数器,保存正在执行的指令
LR:连接返回寄存器,保留函数返回后,下一条应执行的指令
sp:栈顶指针
psr:
ip:
fp:
rn:寄存器
push:入栈
pop:出栈
movw:低地址16位赋值
movt:高地址16位赋值
寻址方式
ADD R0,R1, #0x3F;立即寻址;前缀#表示一个立即数,0x十六进制,0b二进制,不加则为十进制
ADD R0,R1,R2 ;寄存器寻址;把寄存器R1和R2中的数据相加存入R0寄存器
LDR R0,[R1] ;寄存器间接寻址;R1里面存的是操作数的存储地址,需要间接的从这个地址上取得数据
LDR R0,[R1, #4] ;变址寻址;R1里面是基准地址,需要与后面的偏移量相加才能得到操作数的地址
LDR R0,[R1, R2] ;与上面相同,R1和R2里面的地址相加就得到操作数的地址
ARM指令集
MOV 将源操作数传送到目的操作数
ADD 相加
SUB 相减
MUL 相乘
AND 逻辑与
ORR 逻辑或
EOR 逻辑异或
BIC 位清除
CMP 比较
STR 字存储,把一个32位源操作数传送到存储器中,STR R0,[R1,#4] ;(R0)-> ((R1)+4)
LDR 字加载,把存储器中一个32位字数据传送到操作数中,ldr r1, [r7, #4] ;((R7)+4) -> (R1)
B 跳转不返回
BL 跳转返回,BL在跳转之前会把下一条指令地址保存在LR中,程序可返回跳转点
BX BLX 跳转并切换处理器状态,系统调用会用到切换处理器状态
后面遇到继续添加。。。
代码
1、最简单的main函数:
#include <stdio.h>
void main()
{
printf("hello\n");
}
arm-linux-gnueabihf-gcc main.c -o main
arm-linux-gnueabihf-objdump -d main > main.s
000083d0 <main>:
83d0: b580 push {r7, lr}
83d2: af00 add r7, sp, #0
83d4: f248 4034 movw r0, #33844 ; 0x8434
83d8: f2c0 0000 movt r0, #0
83dc: f7ff ef70 blx 82c0 <_init+0x20>
83e0: bd80 pop {r7, pc}
83e2: bf00 nop
2、增加变量
void fun()
{
return;
}
void main()
{
int a;
int b;
int c;
printf("hello %d,%d,%d\n",a,b,c);
fun();
}
000083d4 <fun>:
83d4: b480 push {r7}
83d6: af00 add r7, sp, #0
83d8: bf00 nop
83da: 46bd mov sp, r7
83dc: f85d 7b04 ldr.w r7, [sp], #4
83e0: 4770 bx lr
83e2: bf00 nop
000083e4 <main>:
83e4: b580 push {r7, lr}
83e6: b084 sub sp, #16
83e8: af00 add r7, sp, #0
83ea: 687b ldr r3, [r7, #4]
83ec: 68ba ldr r2, [r7, #8]
83ee: 68f9 ldr r1, [r7, #12]
83f0: f248 4058 movw r0, #33880 ; 0x8458
83f4: f2c0 0000 movt r0, #0
83f8: f7ff ef64 blx 82c4 <_init+0x20>
83fc: f7ff ffea bl 83d4 <fun>
8400: 3710 adds r7, #16
8402: 46bd mov sp, r7
8404: bd80 pop {r7, pc}
8406: bf00 nop
把man函数压栈后,sp栈顶指针指回了main函数位置。所以看到局部变量的空间位置如上图所示。
分析一个函数
函数代码:
static void signal_bus_err(int sig)
{
printf("Signal Error! Signal is %d \n", sig);
show_func_list();
exit(-1);
}
反汇编代码:
00008604 <signal_bus_err>:
8604: b580 push {r7, lr} ; push把r7和lr压栈
8606: b082 sub sp, #8 ; sp指向栈底函数exit,减8表示栈顶回退8字节就指向了系统调用printf
8608: af00 add r7, sp, #0 ; add r7指向栈顶
860a: 4673 mov r3, lr ; mov r3把lr返回地址传送给r3
860c: 6078 str r0, [r7, #4] ; str r0是把传入的参数r0保存在r7加4字节的地址上面
860e: 4619 mov r1, r3 ; 把lr返回地址传送给r1
8610: f248 6005 movw r0, #34309 ; 0x8605 ; 把本函数地址给参数r0低地址
8614: f2c0 0000 movt r0, #0 ; 高地址给0
8618: f7ff ff5a bl 84d0 <__cyg_profile_func_enter> 跳转到函数中执行然后返回
861c: 6879 ldr r1, [r7, #4] 把传入的参数驱除给参数r1
861e: f248 7090 movw r0, #34704 ; 0x8790 ; 参数r0指向地址0x8790,存的字符串
8622: f2c0 0000 movt r0, #0 ; 高地址给0
8626: f7ff eeb4 blx 8390 <_init+0x20> ; 跳到系统函数执行并切换处理器状态,然后返回
862a: f7ff ff7d bl 8528 <show_func_list> ; 跳到函数,没有参数
862e: f04f 30ff mov.w r0, #4294967295 ; r0参数值为-1,所以十进制是4294967295
8632: f7ff eed2 blx 83d8 <_init+0x68> ; 跳系统函数exit,参数为-1,并切换处理器状态然后返回
8636: bf00 nop ; 没啥了
分析:如上
注:学习过程中如有错误还请指出