反汇编系统学习记录

最近在解决应用程序崩溃问题时,需要反汇编进行异常代码的定位,所以需要系统的再学习一下汇编指令。

环境

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 ; 没啥了

分析:如上

注:学习过程中如有错误还请指出

猜你喜欢

转载自blog.csdn.net/TSZ0000/article/details/94020721