arm inline hook简单实现

929 阅读3分钟

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 ;
}

我们执行这个可执行文件:

image.png

3 分析

3.1 hook的实现

我们先看下fopen函数,最开始的汇编代码,fopen是位于libc中

Snipaste_2022-12-26_11-00-43.jpg

我们是怎么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。

Snipaste_2022-12-26_11-00-11.jpg

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
*/