首页
社区
课程
招聘
[原创]Linux x64 inline hook详解及代码实现
2022-5-14 21:15 14094

[原创]Linux x64 inline hook详解及代码实现

2022-5-14 21:15
14094

Linux x64 inline hook 详解及代码实现

前段时间需要写一个libc探针,用来监测libc函数的调用,在google和github上找了很久,Windows和Android平台的inline hook都有比较详细的教程,但是Linux平台的教程比较缺乏,所以决定自己写一篇,思路参考了一篇Windows的inline hook文章,链接放在最后。

什么是inline hook

简单地说,inline hook就是通过直接修改程序的汇编代码来实现hook,举个例子:
假设这是一个函数开头的几条汇编代码

1
2
3
push ebp
mov ebp, esp
sub esp, 0x20

通过inline hook我们可以将其改为这样:

1
2
jmp 0x7fffff898688
...

那么,当执行流进入这个函数时,它就会跳转到0x7fffff898688这个地址,并执行这个地址上的代码,我们可以将jmp的地址指定为hook函数的地址,以达到hook的目的

代码实现

流程

  1. 找到目标函数的地址
  2. 保存目标函数的前几条指令
  3. 构造跳板函数
  4. 修改目标函数的前几条指令跳转到hook函数

找到目标函数的地址

我们的目标是libc中的库函数,所以我们需要先得到libc的基地址
只需要将进程的pid作为参数传入函数就可以得到目标进程的libc基地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//获取libc的基地址
intptr_t get_libc_base(pid_t pid)
{
    intptr_t libcAddr;
    char* buf;
    char* end;
    FILE* fd = fopen("/proc/self/maps", "r");
    if(!fd)
    {
        printf("open maps error!");
        exit(1);
    }
    buf = (char*) malloc(0x100);
    do{
        fgets(buf, 0x100, fd);
    } while(!strstr(buf, "libc-"));
    end = strchr(buf, '-');
    libcAddr = strtol(buf, &end, 16);
    // printf("The process %d's libcbase is: 0x%lx\n", pid, libcAddr);
    fclose(fd);
    return libcAddr;
}

得到libc基地址后,再加上libc函数的偏移地址就可以得到libc函数的实际地址

1
2
3
4
5
6
7
8
9
10
11
//获取目标函数的地址
intptr_t get_target_addr(intptr_t libc_base, const char* func_name)
{
    intptr_t funcAddr;
 
    funcAddr = (size_t)dlsym(0, func_name);
    funcAddr -= get_libc_base(getpid());
    funcAddr += libc_base;
    // printf("function %s address is: 0x%lx\n", func_name, funcAddr);
    return funcAddr;
}

保存目标函数的前几条指令

为什么要保存目标函数的前面几条指令?
如果不在hook函数中调用原函数,那么不保存也行,但是大部分情况下,我们还是需要调用原函数来完成任务的,在后面的步骤中,我们会修改目标函数的前几条指令,如果不保存,就无法再次调用原函数

 

所以保存几条指令合适呢?
保存几条指令取决于要插入什么指令,inline hook常见的插入指令有三种:

  1. jmp offset
  2. jmp reg
  3. push & ret

三种指令各有优缺点
||jmp offset|jmp reg|push & ret|
|:-:|:-:|:-:|:-:|
|优点|编码简单,不改变环境|编码相对简单,跳转距离长|跳转距离长,不改变环境
|缺点|跳转距离短|改变环境(寄存器)|编码复杂
|一般适用|32位|32位或64位|64位

 

我们的inline hook将选择第三种类型的跳转,其硬编码如下所示:

1
2
3
push Low_address                            ;0x68,0,0,0,0
mov dword ptr ss:[rsp+4], high_address      ;0xC7,0x44,0x24,0x04,0,0,0,0
ret                                         ;0xC3

硬编码的长度为14字节,所以我们要将目标函数大于等于十四字节的最少条数指令保存下来(有点绕口,请仔细揣摩)
但是我们如何知道最少应该保存多少字节的机器码呢? 答案是使用capstone

 

capstone的安装请自行搜索资料,并不是很难安装
通过下面的函数,我们只需要传入一个地址,就能得到应该保存多少字节的机器码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//获取目标函数前几条指令的长度
int get_asm_len(intptr_t target)
{
    csh handle;
    cs_insn* insn;
    size_t count;
    char code[30] = {0};
    int rv;
 
    memcpy((void*)code, (void*)target, 30);
    if(cs_open(CS_ARCH_X86, CS_MODE_64, &handle))
    {
        printf("Error: cs_open\n");
        return -1;
    }
 
    count = cs_disasm(handle, code, 30, 0, 0, &insn);
    if(count)
    {
        for(size_t i = 0; i < count; i++)
        {
            if (insn[i].address >= 14)
            {
                rv = insn[i].address;
                break;
            }
        }
        cs_free(insn, count);
    }
    else
    {
        printf("Error: cs_disasm\n");
        return -1;
    }
    cs_close(&handle);
    return rv;
}

得到函数的返回之后,再使用memcpy从函数开始的地址复制合适个数的字节就行,完整代码会在后面给出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void func_hook(const char* funcname, void* hook_func, void** origin_func)
{
    intptr_t libc_base =  get_libc_base(getpid());
    intptr_t target_addr = get_target_addr(libc_base, funcname);
 
    //根据目标函数的地址确定目标函数所在的页,并将该页的权限改为可读可写可执行
    intptr_t page_start = target_addr & 0xfffffffff000;
    mprotect((void*)page_start, 0x1000, PROT_READ|PROT_WRITE|PROT_EXEC);
 
 
    int asm_len = get_asm_len(target_addr);
    if(asm_len <= 0)
    {
        printf("Error: get_asm_len\n");
        exit(-1);
    }
    char* temp_func = (char*)mmap(NULL, 4096, PROT_WRITE|PROT_EXEC|PROT_READ, MAP_ANON|MAP_PRIVATE, -1, 0);
 
    memcpy((void*)temp_func, (void*)target_addr, asm_len);
    intptr_t y = (intptr_t)target_addr + asm_len;
    //构造push&ret跳转,填入目标地址
    char jmp_code[14] = {0x68,y&0xff,(y&0xff00)>>8,(y&0xff0000)>>16,(y&0xff000000)>>24,
        0xC7,0x44,0x24,0x04,(y&0xff00000000)>>32,(y&0xff0000000000)>>40,(y&0xff000000000000)>>48,
        (y&0xff00000000000000)>>56,0xC3};
    memcpy((void*)(temp_func+asm_len), (void*)jmp_code, 14);
 
    *origin_func = (void*)temp_func;
}

func_hook函数的参数分别是:目标函数的名称,目标函数的hook函数,目标函数的跳板函数(相当与原来的目标函数)
func_hook函数中,我们首先获得了目标函数的地址,然后使用mprotect函数将目标函数所在页的权限改为可读可写可执行,接着获取需要保存的字节数,再分配一块空间来存放保存的指令,在保存指令的后面放一个push&ret跳转,使之回到目标函数保存的指令的后一条指令,这就是跳板函数,执行跳板函数就相当于执行原函数,最后将这个地址赋值给跳板函数

 

一不小心把第二步和第三步都讲了,不过不影响

修改目标函数的前几条指令跳转到hook函数

这一步比较简单,我们只需要使用push&ret编码覆盖目标函数的开始几条指令,使之跳转到我们写好的hook函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//替换目标函数的前len个字节,使之跳转到hook函数
void change_bytes(intptr_t addr, const char code[], int len)
{
    memcpy((void*)addr, code, len);
}
 
 
void func_hook(const char* funcname, void* hook_func, void** origin_func)
{
    intptr_t libc_base =  get_libc_base(getpid());
    intptr_t target_addr = get_target_addr(libc_base, funcname);
 
    //根据目标函数的地址确定目标函数所在的页,并将该页的权限改为可读可写可执行
    intptr_t page_start = target_addr & 0xfffffffff000;
    mprotect((void*)page_start, 0x1000, PROT_READ|PROT_WRITE|PROT_EXEC);
 
 
    int asm_len = get_asm_len(target_addr);
    if(asm_len <= 0)
    {
        printf("Error: get_asm_len\n");
        exit(-1);
    }
    char* temp_func = (char*)mmap(NULL, 4096, PROT_WRITE|PROT_EXEC|PROT_READ, MAP_ANON|MAP_PRIVATE, -1, 0);
 
    memcpy((void*)temp_func, (void*)target_addr, asm_len);
    intptr_t y = (intptr_t)target_addr + asm_len;
    //构造push&ret跳转,填入目标地址
    char jmp_code[14] = {0x68,y&0xff,(y&0xff00)>>8,(y&0xff0000)>>16,(y&0xff000000)>>24,
        0xC7,0x44,0x24,0x04,(y&0xff00000000)>>32,(y&0xff0000000000)>>40,(y&0xff000000000000)>>48,
        (y&0xff00000000000000)>>56,0xC3};
    memcpy((void*)(temp_func+asm_len), (void*)jmp_code, 14);
 
    *origin_func = (void*)temp_func;
 
    intptr_t x = (intptr_t)hook_func;
    char hard_code[14] = {0x68,x&0xff,(x&0xff00)>>8,(x&0xff0000)>>16,(x&0xff000000)>>24,
        0xC7,0x44,0x24,0x04,(x&0xff00000000)>>32,(x&0xff0000000000)>>40,(x&0xff000000000000)>>48,
        (x&0xff00000000000000)>>56,0xC3};
    change_bytes(target_addr, hard_code, 14);
}

在第二步的代码后面加三行代码就行

最终代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#include<stdio.h>
#include<stdlib.h>
#include<capstone/capstone.h>
#include<unistd.h>
#include<sys/types.h>
#include<dlfcn.h>
#include<sys/mman.h>
#include<string.h>
#include<stdint.h>
 
//获取libc的基地址
intptr_t get_libc_base(pid_t pid)
{
    intptr_t libcAddr;
    char* buf;
    char* end;
    FILE* fd = fopen("/proc/self/maps", "r");
    if(!fd)
    {
        printf("open maps error!");
        exit(1);
    }
    buf = (char*) malloc(0x100);
    do{
        fgets(buf, 0x100, fd);
    } while(!strstr(buf, "libc-"));
    end = strchr(buf, '-');
    libcAddr = strtol(buf, &end, 16);
    // printf("The process %d's libcbase is: 0x%lx\n", pid, libcAddr);
    fclose(fd);
    return libcAddr;
}
 
 
//获取目标函数的地址
intptr_t get_target_addr(intptr_t libc_base, const char* func_name)
{
    intptr_t funcAddr;
 
    funcAddr = (size_t)dlsym(0, func_name);
    funcAddr -= get_libc_base(getpid());
    funcAddr += libc_base;
    // printf("function %s address is: 0x%lx\n", func_name, funcAddr);
    return funcAddr;
}
 
 
//获取目标函数前几条指令的长度
int get_asm_len(intptr_t target)
{
    csh handle;
    cs_insn* insn;
    size_t count;
    char code[30] = {0};
    int rv;
 
    memcpy((void*)code, (void*)target, 30);
    if(cs_open(CS_ARCH_X86, CS_MODE_64, &handle))
    {
        printf("Error: cs_open\n");
        return -1;
    }
 
    count = cs_disasm(handle, code, 30, 0, 0, &insn);
    if(count)
    {
        for(size_t i = 0; i < count; i++)
        {
            if (insn[i].address >= 14)
            {
                rv = insn[i].address;
                break;
            }
        }
        cs_free(insn, count);
    }
    else
    {
        printf("Error: cs_disasm\n");
        return -1;
    }
    cs_close(&handle);
    return rv;
}
 
 
//替换目标函数的前len个字节,使之跳转到hook函数
void change_bytes(intptr_t addr, const char code[], int len)
{
    memcpy((void*)addr, code, len);
}
 
 
void func_hook(const char* funcname, void* hook_func, void** origin_func)
{
    intptr_t libc_base =  get_libc_base(getpid());
    intptr_t target_addr = get_target_addr(libc_base, funcname);
 
    //根据目标函数的地址确定目标函数所在的页,并将该页的权限改为可读可写可执行
    intptr_t page_start = target_addr & 0xfffffffff000;
    mprotect((void*)page_start, 0x1000, PROT_READ|PROT_WRITE|PROT_EXEC);
 
 
    int asm_len = get_asm_len(target_addr);
    if(asm_len <= 0)
    {
        printf("Error: get_asm_len\n");
        exit(-1);
    }
    char* temp_func = (char*)mmap(NULL, 4096, PROT_WRITE|PROT_EXEC|PROT_READ, MAP_ANON|MAP_PRIVATE, -1, 0);
 
    memcpy((void*)temp_func, (void*)target_addr, asm_len);
    intptr_t y = (intptr_t)target_addr + asm_len;
    //构造push&ret跳转,填入目标地址
    char jmp_code[14] = {0x68,y&0xff,(y&0xff00)>>8,(y&0xff0000)>>16,(y&0xff000000)>>24,
        0xC7,0x44,0x24,0x04,(y&0xff00000000)>>32,(y&0xff0000000000)>>40,(y&0xff000000000000)>>48,
        (y&0xff00000000000000)>>56,0xC3};
    memcpy((void*)(temp_func+asm_len), (void*)jmp_code, 14);
 
    *origin_func = (void*)temp_func;
 
    intptr_t x = (intptr_t)hook_func;
    char hard_code[14] = {0x68,x&0xff,(x&0xff00)>>8,(x&0xff0000)>>16,(x&0xff000000)>>24,
        0xC7,0x44,0x24,0x04,(x&0xff00000000)>>32,(x&0xff0000000000)>>40,(x&0xff000000000000)>>48,
        (x&0xff00000000000000)>>56,0xC3};
    change_bytes(target_addr, hard_code, 14);
}
 
static FILE* (*origin_fopen)(const char* filename, const char* mod);
static int   (*origin_fclose)(FILE* fp);
 
static FILE* fopen_hook(const char* filename, const char* mod)
{
    FILE* rv = origin_fopen(filename, mod);
    printf("fopen(\"%s\", \"%s\")\n\treturn: %p\n", filename, mod, rv);
    return rv;
}
 
static int fclose_hook(FILE* fp)
{
    int rv = origin_fclose(fp);
    printf("fclose(%p)\n\treturn: %d\n", fp, rv);
    return rv;
}
 
int main()
{
    func_hook("fopen", (void*)fopen_hook, (void**)&origin_fopen);
    func_hook("fclose", (void*)fclose_hook, (void**)&origin_fclose);
 
    FILE* file = fopen("sample.txt", "r");
    if (file != NULL)
    {
        fclose(file);
    }
 
    return 0;
}

在最后我定义了fopen和fclose的hook函数,并在main函数中安装了hook
编译并运行

1
2
gcc inline_hook.c -o inline_hook -lcapstone -ldl
./inline_hook

输出如下:
开始的两个fopen是因为先安装了fopen的hook,然后在安装fclose的hook的时候调用了fopen,触发了fopen的hook

1
2
3
4
5
6
7
8
fopen("/proc/self/maps", "r")
        return: 0x559e93d8f2a0
fopen("/proc/self/maps", "r")
        return: 0x559e93d8f2a0
fopen("sample.txt", "r")
        return: 0x559e93d8f2a0
fclose(0x559e93d8f2a0)
        return: 0

跨进程hook

以上的代码实现了本进程的hook,如果我们想要跨进程hook,可以将代码稍作修改并编译成so,然后使用so注入工具将其注入到其他进程

 

修改后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include<stdio.h>
#include<stdlib.h>
#include<capstone/capstone.h>
#include<unistd.h>
#include<sys/types.h>
#include<dlfcn.h>
#include<sys/mman.h>
#include<string.h>
#include<stdint.h>
 
//获取libc的基地址
intptr_t get_libc_base(pid_t pid)
{
    intptr_t libcAddr;
    char* buf;
    char* end;
    FILE* fd = fopen("/proc/self/maps", "r");
    if(!fd)
    {
        printf("open maps error!");
        exit(1);
    }
    buf = (char*) malloc(0x100);
    do{
        fgets(buf, 0x100, fd);
    } while(!strstr(buf, "libc-"));
    end = strchr(buf, '-');
    libcAddr = strtol(buf, &end, 16);
    // printf("The process %d's libcbase is: 0x%lx\n", pid, libcAddr);
    fclose(fd);
    return libcAddr;
}
 
 
//获取目标函数的地址
intptr_t get_target_addr(intptr_t libc_base, const char* func_name)
{
    intptr_t funcAddr;
 
    funcAddr = (size_t)dlsym(0, func_name);
    funcAddr -= get_libc_base(getpid());
    funcAddr += libc_base;
    // printf("function %s address is: 0x%lx\n", func_name, funcAddr);
    return funcAddr;
}
 
 
//获取目标函数前几条指令的长度
int get_asm_len(intptr_t target)
{
    csh handle;
    cs_insn* insn;
    size_t count;
    char code[30] = {0};
    int rv;
 
    memcpy((void*)code, (void*)target, 30);
    if(cs_open(CS_ARCH_X86, CS_MODE_64, &handle))
    {
        printf("Error: cs_open\n");
        return -1;
    }
 
    count = cs_disasm(handle, code, 30, 0, 0, &insn);
    if(count)
    {
        for(size_t i = 0; i < count; i++)
        {
            if (insn[i].address >= 14)
            {
                rv = insn[i].address;
                break;
            }
        }
        cs_free(insn, count);
    }
    else
    {
        printf("Error: cs_disasm\n");
        return -1;
    }
    cs_close(&handle);
    return rv;
}
 
 
//替换目标函数的前len个字节,使之跳转到hook函数
void change_bytes(intptr_t addr, const char code[], int len)
{
    memcpy((void*)addr, code, len);
}
 
 
void func_hook(const char* funcname, void* hook_func, void** origin_func)
{
    intptr_t libc_base =  get_libc_base(getpid());
    intptr_t target_addr = get_target_addr(libc_base, funcname);
 
    //根据目标函数的地址确定目标函数所在的页,并将该页的权限改为可读可写可执行
    intptr_t page_start = target_addr & 0xfffffffff000;
    mprotect((void*)page_start, 0x1000, PROT_READ|PROT_WRITE|PROT_EXEC);
 
 
    int asm_len = get_asm_len(target_addr);
    if(asm_len <= 0)
    {
        printf("Error: get_asm_len\n");
        exit(-1);
    }
    char* temp_func = (char*)mmap(NULL, 4096, PROT_WRITE|PROT_EXEC|PROT_READ, MAP_ANON|MAP_PRIVATE, -1, 0);
 
    memcpy((void*)temp_func, (void*)target_addr, asm_len);
    intptr_t y = (intptr_t)target_addr + asm_len;
    //构造push&ret跳转,填入目标地址
    char jmp_code[14] = {0x68,y&0xff,(y&0xff00)>>8,(y&0xff0000)>>16,(y&0xff000000)>>24,
        0xC7,0x44,0x24,0x04,(y&0xff00000000)>>32,(y&0xff0000000000)>>40,(y&0xff000000000000)>>48,
        (y&0xff00000000000000)>>56,0xC3};
    memcpy((void*)(temp_func+asm_len), (void*)jmp_code, 14);
 
    *origin_func = (void*)temp_func;
 
    intptr_t x = (intptr_t)hook_func;
    char hard_code[14] = {0x68,x&0xff,(x&0xff00)>>8,(x&0xff0000)>>16,(x&0xff000000)>>24,
        0xC7,0x44,0x24,0x04,(x&0xff00000000)>>32,(x&0xff0000000000)>>40,(x&0xff000000000000)>>48,
        (x&0xff00000000000000)>>56,0xC3};
    change_bytes(target_addr, hard_code, 14);
}
 
static FILE* (*origin_fopen)(const char* filename, const char* mod);
static int   (*origin_fclose)(FILE* fp);
 
static FILE* fopen_hook(const char* filename, const char* mod)
{
    FILE* rv = origin_fopen(filename, mod);
    printf("fopen(\"%s\", \"%s\")\n\treturn: %p\n", filename, mod, rv);
    return rv;
}
 
static int fclose_hook(FILE* fp)
{
    int rv = origin_fclose(fp);
    printf("fclose(%p)\n\treturn: %d\n", fp, rv);
    return rv;
}
 
//so被加载后会首先执行这里的代码
__attribute__((constructor))
void load()
{
    func_hook("fopen", (void*)fopen_hook, (void**)&origin_fopen);
    func_hook("fclose", (void*)fclose_hook, (void**)&origin_fclose);
 
    printf("inject suceessfully\n");
}

将其编译为so文件

1
gcc -shared -fPIC libhook.c -o libhook.so -ldl -lcapstone

新建一个sample.c文件,文件内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<stdio.h>
 
int main()
{
    getchar();
 
    for(size_t i = 0; i < 10; i++)
    {
        FILE* file = fopen("sample.txt", "r");
        if(file != NULL)
        {
            fclose(file);
        }
    }
}

编译并运行sample

1
2
gcc sample.c -o sample
./sample

去github找一个开源的so注入工具 linux_so_injector,按引导构建好项目
新开一个终端,使用ps获取sample的pid,然后使用injector注入so

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
wenling@wenling-virtual-machine ~/D/md-file> ps -a
    PID TTY          TIME CMD
   1586 tty2     00:34:31 Xorg
   1627 tty2     00:00:00 gnome-session-b
   1701 tty2     00:00:00 fcitx <defunct>
  29605 pts/0    00:00:00 fish
  38689 pts/3    00:00:01 fish
  42875 pts/1    00:00:00 fish
  42980 pts/0    00:00:00 sample
  43076 pts/2    00:00:00 fish
  43165 pts/2    00:00:00 ps
wenling@wenling-virtual-machine ~/D/md-file> sudo ~/Downloads/github/linux_so_injector/injector libhook.so 42980
[sudo] password for wenling:
injector: size of code to inject 209
injector: Path of Shared object to be injected : /home/wenling/Documents/md-file/libhook.so
injector: Return address : 7f1d19551fd2
injector: RIP Register : 5591c73fef2a

显示注入成功后,sample的输出如下:
除去最前面的两次fopen,刚好hook了十次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
fopen("/proc/self/maps", "r")
    return: 0x5591c8b1d940
fopen("/proc/self/maps", "r")
    return: 0x5591c8b1d940
inject suceessfully
fopen("sample.txt", "r")
    return: 0x5591c8b1d940
fclose(0x5591c8b1d940)
    return: 0
fopen("sample.txt", "r")
    return: 0x5591c8b1d940
fclose(0x5591c8b1d940)
    return: 0
fopen("sample.txt", "r")
    return: 0x5591c8b1d940
fclose(0x5591c8b1d940)
    return: 0
fopen("sample.txt", "r")
    return: 0x5591c8b1d940
fclose(0x5591c8b1d940)
    return: 0
fopen("sample.txt", "r")
    return: 0x5591c8b1d940
fclose(0x5591c8b1d940)
    return: 0
fopen("sample.txt", "r")
    return: 0x5591c8b1d940
fclose(0x5591c8b1d940)
    return: 0
fopen("sample.txt", "r")
    return: 0x5591c8b1d940
fclose(0x5591c8b1d940)
    return: 0
fopen("sample.txt", "r")
    return: 0x5591c8b1d940
fclose(0x5591c8b1d940)
    return: 0
fopen("sample.txt", "r")
    return: 0x5591c8b1d940
fclose(0x5591c8b1d940)
    return: 0
fopen("sample.txt", "r")
    return: 0x5591c8b1d940
fclose(0x5591c8b1d940)
    return: 0

缺陷

  1. 没有实现重定位
  2. 多线程下无法保证安全性

没有实现重定位

让我们来看看malloc函数的开头几条指令:

1
2
3
4
5
6
7
0x7ffff7fec4a0 <malloc>:    endbr64
0x7ffff7fec4a4 <malloc+4>:    push   rbp
0x7ffff7fec4a5 <malloc+5>:    push   rbx
0x7ffff7fec4a6 <malloc+6>:    mov    rbx,rdi
0x7ffff7fec4a9 <malloc+9>:    sub    rsp,0x8
0x7ffff7fec4ad <malloc+13>:    mov    rdx,QWORD PTR [rip+0x11c3c]        # 0x7ffff7ffe0f0 <alloc_end>
0x7ffff7fec4b4 <malloc+20>:    mov    rax,QWORD PTR [rip+0x11c3d]        # 0x7ffff7ffe0f8 <alloc_ptr>

mov rdx,QWORD PTR [rip+0x11c3c] 这条指令使用了rip偏移寻址,需要用到当前的rip寄存器的值,如果我们贸然将其保存到跳板函数,当程序执行到跳板函数时,rip已经变了
要想得到正确的执行结果,就必须修改这条指令的偏移

多线程下无法保证安全性

当我们将so注入某个进程时,有可能出现安装hook的同时,进程的其他线程正好执行到了目标函数,这是程序就有可能崩溃

 

以上两个问题的答案都可以在参考文章中找到答案,本文旨在介绍inline hook,就不针对这两个问题做代码实现了

参考

Windows inline hook详解


阿里云助力开发者!2核2G 3M带宽不限流量!6.18限时价,开 发者可享99元/年,续费同价!

收藏
点赞12
打赏
分享
最新回复 (7)
雪    币: 380
活跃值: (825)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
gxh1911 2022-5-14 21:29
2
1
tql!!! orz
雪    币: 75
活跃值: (6912)
能力值: ( LV13,RANK:383 )
在线值:
发帖
回帖
粉丝
winmt 8 2022-5-15 08:20
3
0
学习一下
雪    币: 6026
活跃值: (3940)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
pxhb 2 2022-5-15 08:54
4
0
感谢分享
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
wx_lth 2023-3-20 17:00
5
0
哈喽,这里不需要保存原始的执行环境吗,push pop之类的,那我们的hook函数破坏了堆栈和寄存器的话,原始函数怎么执行呢?
雪    币: 213
活跃值: (210)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
海绵卜O卜 2023-6-6 15:02
6
0
牛的。大佬受我一拜。
雪    币: 3428
活跃值: (3482)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
fengyunabc 1 2023-6-6 15:13
7
0
感谢分享。
雪    币: 19759
活跃值: (29377)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
秋狝 2023-6-7 09:06
8
1
感谢分享
游客
登录 | 注册 方可回帖
返回