arm汇编基础知识
最近一段时间,把arm和elf基础知识学习了下。加深了对于系统,arm汇编,栈操作的理解。
1 工具和资料
推荐工具:ida
推荐周壑的视频:
space.bilibili.com/37877654/ch…
space.bilibili.com/37877654/ch…
2 代码实现
下面实现简易的inline hook,我们对fopen函数实现了hook,把它的前面两条指令进行了hook,hook到了,我们自己写的函数上面,也就是
fun3()函数上,并且在fun3()函数上,进行栈操作,进行了fopen函数被我们hook的几行操作,并且取出了fopen里面的参数。最后返回到原来的
fopen函数,进行了fopen操作。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <bits elf_arm.h="">
#include <elf.h>
#include <dlfcn.h>
#include <sys mman.h="">
uint32_t ret_addr;
uint32_t arg0;
uint32_t arg1;
/*
0 cpsr ------>sp
4 R0
8 R1
c R2
10 R3
14 R4
18 R5
1c R6
20 R7
24 R8
28 R9
2c R10
30 R11
34 R12
38 LR
3c PC
----------
40 ---> hook 前sp的位置
50 R4
R5
R6
LR
*/
void fun3() {
//printf("handle ....\n");
asm("STMFD sp!, {R4-R6,LR}");
asm("sub sp , sp , #0x10");
asm("STMFD sp!, {R0-R12, LR, PC}");
asm("mrs r0, cpsr");
asm("STMFD sp!, {R0}");
asm("ldr r4, [sp, #4]");
asm("str r4, %0":"=m"(arg0));
asm("ldr r4, [sp, #8]");
asm("str r4, %0":"=m"(arg1));
asm("ldr r9, %0"::"m"(ret_addr));
asm("str r9, [sp, #0x3c]");
//asm("add sp , sp , #0x30"); // sub sp, sp, #0x10
//mov r5, r0
asm("ldr r0, [sp, #0x4]");
asm("str r0, [sp, #0x18]");
asm("ldr r0, =0x3f7b8");
asm("LDMFD sp!, {R0}");
asm("msr cpsr, r0");
asm("ldr r0, =0x3f7b8");
asm("add sp , sp , #0x4");
asm("LDMFD sp!, {R1-R12,LR,PC}");
}
int main(){
void* handle = dlopen("libc.so", RTLD_NOW);
void* hook_addr = dlsym(handle, "fopen");
mprotect((void*)((int)hook_addr & 0xfffff000) , 0x1000, PROT_READ|PROT_EXEC|PROT_WRITE);
printf("hook_addr = %p\n", hook_addr);
printf("fun3 = %p\n", fun3);
*(uint32_t *)(hook_addr - 1) = 0xf000f8df; //固定的指令
*(uint32_t *)(hook_addr - 1 + 4) = (uint32_t)fun3;
ret_addr = (uint32_t)hook_addr - 1 + 8 + 1;
//-1 是因为开始是基数地址,+1 是thumb模式
getchar();
FILE *fp = fopen("/data/local/tmp/android_server", "rb");
uint32_t data;
fread(&data, 4, 1, fp);
fclose(fp);
printf("data : %p\n", fp);
printf("arg0 : %s\n", arg0);
printf("arg1 : %s\n", arg1);
return 0 ;
}
我们执行这个可执行文件:
3 分析
3.1 hook的实现
我们先看下fopen函数,最开始的汇编代码,fopen是位于libc中
我们是怎么hook的呢?
*(uint32_t *)(hook_addr - 1) = 0xf000f8df; //固定的指令 ldr pc, [pc]
*(uint32_t *)(hook_addr - 1 + 4) = (uint32_t)fun3;
第一句,我们执行了ldr pc, [pc],就是把当前pc地址的值,读进来。我们处于thumb模式,读pc,永远读的是当前pc+4,所以,就是读的F69F0C0C这个地址的值,这个值,被我们的第二句给写入了fun3的地址。这里为啥先减1呢,是因为我们读的值(hook_addr = 0xf69f0c09)是奇数,所以减一。
这两句执行后的效果是:把fun3的地址读到当前pc中,然后程序下一步,就会通过pc进入到fun3函数中。
我们动态调试的时候,fopen函数已经这样被我们hook。
3.2 fun3函数的实现
我们把fopen函数的前几句汇编代码给hook了,压根没执行,所以我们需要在fun3函数里先执行这几句指令。
asm("STMFD sp!, {R4-R6,LR}"); // PUSH {R4-R6,LR}
asm("sub sp , sp , #0x10"); // SUB SP, SP, #0x10
asm("ldr r0, [sp, #0x4]"); // 下面两句相当于 MOV R5, R0
asm("str r0, [sp, #0x18]");
asm("ldr r0, =0x3f7b8"); //LDR R0, =(__stack_chk_guard_ptr - 0x51C14)
这样,我们就执行了fopen函数原先的代码。
剩下的其他代码,就是我们自己hook的汇编指令,前面是压栈,后面是出栈。并且读取了fopen的函数参数。
整个栈的结构大约是这样:
/*
0 cpsr ------>sp
4 R0
8 R1
c R2
10 R3
14 R4
18 R5
1c R6
20 R7
24 R8
28 R9
2c R10
30 R11
34 R12
38 LR
3c PC
----------
40 R4 ---> hook 前sp的位置
R5
R6
LR
*/