Linux hook 技术一个简单demo分析
#include <stdio.h>
#include <unistd.h>
int main(){
printf("the pid is %d\n",getpid());
return 0;
}
gcc -o gotTest main.c
readelf -a gotTest
可以得到如下结果:
ELF 头:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
类别: ELF64
数据: 2 补码,小端序 (little endian)
Version: 1 (current)
OS/ABI: UNIX - System V
ABI 版本: 0
类型: DYN (共享目标文件)
系统架构: Advanced Micro Devices X86-64
版本: 0x1
入口点地址: 0x1080
程序头起点: 64 (bytes into file)
Start of section headers: 14760 (bytes into file)
标志: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 13
Size of section headers: 64 (bytes)
Number of section headers: 31
Section header string table index: 30
节头:
[号] 名称 类型 地址 偏移量
大小 全体大小 旗标 链接 信息 对齐
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 0000000000000318 00000318
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.gnu.propert NOTE 0000000000000338 00000338
0000000000000020 0000000000000000 A 0 0 8
[ 3] .note.gnu.build-i NOTE 0000000000000358 00000358
0000000000000024 0000000000000000 A 0 0 4
[ 4] .note.ABI-tag NOTE 000000000000037c 0000037c
0000000000000020 0000000000000000 A 0 0 4
[ 5] .gnu.hash GNU_HASH 00000000000003a0 000003a0
0000000000000024 0000000000000000 A 6 0 8
[ 6] .dynsym DYNSYM 00000000000003c8 000003c8
00000000000000c0 0000000000000018 A 7 1 8
[ 7] .dynstr STRTAB 0000000000000488 00000488
000000000000008b 0000000000000000 A 0 0 1
[ 8] .gnu.version VERSYM 0000000000000514 00000514
0000000000000010 0000000000000002 A 6 0 2
[ 9] .gnu.version_r VERNEED 0000000000000528 00000528
0000000000000020 0000000000000000 A 7 1 8
[10] .rela.dyn RELA 0000000000000548 00000548
00000000000000c0 0000000000000018 A 6 0 8
[11] .rela.plt RELA 0000000000000608 00000608
0000000000000030 0000000000000018 AI 6 24 8
[12] .init PROGBITS 0000000000001000 00001000
000000000000001b 0000000000000000 AX 0 0 4
[13] .plt PROGBITS 0000000000001020 00001020
0000000000000030 0000000000000010 AX 0 0 16
[14] .plt.got PROGBITS 0000000000001050 00001050
0000000000000010 0000000000000010 AX 0 0 16
[15] .plt.sec PROGBITS 0000000000001060 00001060
0000000000000020 0000000000000010 AX 0 0 16
[16] .text PROGBITS 0000000000001080 00001080
0000000000000185 0000000000000000 AX 0 0 16
[17] .fini PROGBITS 0000000000001208 00001208
000000000000000d 0000000000000000 AX 0 0 4
[18] .rodata PROGBITS 0000000000002000 00002000
0000000000000013 0000000000000000 A 0 0 4
[19] .eh_frame_hdr PROGBITS 0000000000002014 00002014
0000000000000044 0000000000000000 A 0 0 4
[20] .eh_frame PROGBITS 0000000000002058 00002058
0000000000000108 0000000000000000 A 0 0 8
[21] .init_array INIT_ARRAY 0000000000003db0 00002db0
0000000000000008 0000000000000008 WA 0 0 8
[22] .fini_array FINI_ARRAY 0000000000003db8 00002db8
0000000000000008 0000000000000008 WA 0 0 8
[23] .dynamic DYNAMIC 0000000000003dc0 00002dc0
00000000000001f0 0000000000000010 WA 7 0 8
[24] .got PROGBITS 0000000000003fb0 00002fb0
0000000000000050 0000000000000008 WA 0 0 8
[25] .data PROGBITS 0000000000004000 00003000
0000000000000010 0000000000000000 WA 0 0 8
[26] .bss NOBITS 0000000000004010 00003010
0000000000000008 0000000000000000 WA 0 0 1
[27] .comment PROGBITS 0000000000000000 00003010
000000000000002b 0000000000000001 MS 0 0 1
[28] .symtab SYMTAB 0000000000000000 00003040
0000000000000630 0000000000000018 29 46 8
[29] .strtab STRTAB 0000000000000000 00003670
0000000000000218 0000000000000000 0 0 1
[30] .shstrtab STRTAB 0000000000000000 00003888
000000000000011a 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
There are no section groups in this file.
程序头:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040
0x00000000000002d8 0x00000000000002d8 R 0x8
INTERP 0x0000000000000318 0x0000000000000318 0x0000000000000318
0x000000000000001c 0x000000000000001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000638 0x0000000000000638 R 0x1000
LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000
0x0000000000000215 0x0000000000000215 R E 0x1000
LOAD 0x0000000000002000 0x0000000000002000 0x0000000000002000
0x0000000000000160 0x0000000000000160 R 0x1000
LOAD 0x0000000000002db0 0x0000000000003db0 0x0000000000003db0
0x0000000000000260 0x0000000000000268 RW 0x1000
DYNAMIC 0x0000000000002dc0 0x0000000000003dc0 0x0000000000003dc0
0x00000000000001f0 0x00000000000001f0 RW 0x8
NOTE 0x0000000000000338 0x0000000000000338 0x0000000000000338
0x0000000000000020 0x0000000000000020 R 0x8
NOTE 0x0000000000000358 0x0000000000000358 0x0000000000000358
0x0000000000000044 0x0000000000000044 R 0x4
GNU_PROPERTY 0x0000000000000338 0x0000000000000338 0x0000000000000338
0x0000000000000020 0x0000000000000020 R 0x8
GNU_EH_FRAME 0x0000000000002014 0x0000000000002014 0x0000000000002014
0x0000000000000044 0x0000000000000044 R 0x4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
GNU_RELRO 0x0000000000002db0 0x0000000000003db0 0x0000000000003db0
0x0000000000000250 0x0000000000000250 R 0x1
Section to Segment mapping:
段节...
00
01 .interp
02 .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
03 .init .plt .plt.got .plt.sec .text .fini
04 .rodata .eh_frame_hdr .eh_frame
05 .init_array .fini_array .dynamic .got .data .bss
06 .dynamic
07 .note.gnu.property
08 .note.gnu.build-id .note.ABI-tag
09 .note.gnu.property
10 .eh_frame_hdr
11
12 .init_array .fini_array .dynamic .got
Dynamic section at offset 0x2dc0 contains 27 entries:
标记 类型 名称/值
0x0000000000000001 (NEEDED) 共享库:[libc.so.6]
0x000000000000000c (INIT) 0x1000
0x000000000000000d (FINI) 0x1208
0x0000000000000019 (INIT_ARRAY) 0x3db0
0x000000000000001b (INIT_ARRAYSZ) 8 (bytes)
0x000000000000001a (FINI_ARRAY) 0x3db8
0x000000000000001c (FINI_ARRAYSZ) 8 (bytes)
0x000000006ffffef5 (GNU_HASH) 0x3a0
0x0000000000000005 (STRTAB) 0x488
0x0000000000000006 (SYMTAB) 0x3c8
0x000000000000000a (STRSZ) 139 (bytes)
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000015 (DEBUG) 0x0
0x0000000000000003 (PLTGOT) 0x3fb0
0x0000000000000002 (PLTRELSZ) 48 (bytes)
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0x608
0x0000000000000007 (RELA) 0x548
0x0000000000000008 (RELASZ) 192 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000000000001e (FLAGS) BIND_NOW
0x000000006ffffffb (FLAGS_1) 标志: NOW PIE
0x000000006ffffffe (VERNEED) 0x528
0x000000006fffffff (VERNEEDNUM) 1
0x000000006ffffff0 (VERSYM) 0x514
0x000000006ffffff9 (RELACOUNT) 3
0x0000000000000000 (NULL) 0x0
重定位节 '.rela.dyn' at offset 0x548 contains 8 entries:
偏移量 信息 类型 符号值 符号名称 + 加数
000000003db0 000000000008 R_X86_64_RELATIVE 1160
000000003db8 000000000008 R_X86_64_RELATIVE 1120
000000004008 000000000008 R_X86_64_RELATIVE 4008
000000003fd8 000100000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_deregisterTMClone + 0
000000003fe0 000400000006 R_X86_64_GLOB_DAT 0000000000000000 __libc_start_main@GLIBC_2.2.5 + 0
000000003fe8 000500000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
000000003ff0 000600000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_registerTMCloneTa + 0
000000003ff8 000700000006 R_X86_64_GLOB_DAT 0000000000000000 __cxa_finalize@GLIBC_2.2.5 + 0
重定位节 '.rela.plt' at offset 0x608 contains 2 entries:
偏移量 信息 类型 符号值 符号名称 + 加数
000000003fc8 000200000007 R_X86_64_JUMP_SLO 0000000000000000 getpid@GLIBC_2.2.5 + 0
000000003fd0 000300000007 R_X86_64_JUMP_SLO 0000000000000000 printf@GLIBC_2.2.5 + 0
The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.
Symbol table '.dynsym' contains 8 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND getpid@GLIBC_2.2.5 (2)
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.2.5 (2)
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)
5: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
6: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable
7: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@GLIBC_2.2.5 (2)
Symbol table '.symtab' contains 66 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000318 0 SECTION LOCAL DEFAULT 1
2: 0000000000000338 0 SECTION LOCAL DEFAULT 2
3: 0000000000000358 0 SECTION LOCAL DEFAULT 3
4: 000000000000037c 0 SECTION LOCAL DEFAULT 4
5: 00000000000003a0 0 SECTION LOCAL DEFAULT 5
6: 00000000000003c8 0 SECTION LOCAL DEFAULT 6
7: 0000000000000488 0 SECTION LOCAL DEFAULT 7
8: 0000000000000514 0 SECTION LOCAL DEFAULT 8
9: 0000000000000528 0 SECTION LOCAL DEFAULT 9
10: 0000000000000548 0 SECTION LOCAL DEFAULT 10
11: 0000000000000608 0 SECTION LOCAL DEFAULT 11
12: 0000000000001000 0 SECTION LOCAL DEFAULT 12
13: 0000000000001020 0 SECTION LOCAL DEFAULT 13
14: 0000000000001050 0 SECTION LOCAL DEFAULT 14
15: 0000000000001060 0 SECTION LOCAL DEFAULT 15
16: 0000000000001080 0 SECTION LOCAL DEFAULT 16
17: 0000000000001208 0 SECTION LOCAL DEFAULT 17
18: 0000000000002000 0 SECTION LOCAL DEFAULT 18
19: 0000000000002014 0 SECTION LOCAL DEFAULT 19
20: 0000000000002058 0 SECTION LOCAL DEFAULT 20
21: 0000000000003db0 0 SECTION LOCAL DEFAULT 21
22: 0000000000003db8 0 SECTION LOCAL DEFAULT 22
23: 0000000000003dc0 0 SECTION LOCAL DEFAULT 23
24: 0000000000003fb0 0 SECTION LOCAL DEFAULT 24
25: 0000000000004000 0 SECTION LOCAL DEFAULT 25
26: 0000000000004010 0 SECTION LOCAL DEFAULT 26
27: 0000000000000000 0 SECTION LOCAL DEFAULT 27
28: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
29: 00000000000010b0 0 FUNC LOCAL DEFAULT 16 deregister_tm_clones
30: 00000000000010e0 0 FUNC LOCAL DEFAULT 16 register_tm_clones
31: 0000000000001120 0 FUNC LOCAL DEFAULT 16 __do_global_dtors_aux
32: 0000000000004010 1 OBJECT LOCAL DEFAULT 26 completed.8061
33: 0000000000003db8 0 OBJECT LOCAL DEFAULT 22 __do_global_dtors_aux_fin
34: 0000000000001160 0 FUNC LOCAL DEFAULT 16 frame_dummy
35: 0000000000003db0 0 OBJECT LOCAL DEFAULT 21 __frame_dummy_init_array_
36: 0000000000000000 0 FILE LOCAL DEFAULT ABS main.c
37: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
38: 000000000000215c 0 OBJECT LOCAL DEFAULT 20 __FRAME_END__
39: 0000000000000000 0 FILE LOCAL DEFAULT ABS
40: 0000000000003db8 0 NOTYPE LOCAL DEFAULT 21 __init_array_end
41: 0000000000003dc0 0 OBJECT LOCAL DEFAULT 23 _DYNAMIC
42: 0000000000003db0 0 NOTYPE LOCAL DEFAULT 21 __init_array_start
43: 0000000000002014 0 NOTYPE LOCAL DEFAULT 19 __GNU_EH_FRAME_HDR
44: 0000000000003fb0 0 OBJECT LOCAL DEFAULT 24 _GLOBAL_OFFSET_TABLE_
45: 0000000000001000 0 FUNC LOCAL DEFAULT 12 _init
46: 0000000000001200 5 FUNC GLOBAL DEFAULT 16 __libc_csu_fini
47: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab
48: 0000000000004000 0 NOTYPE WEAK DEFAULT 25 data_start
49: 0000000000000000 0 FUNC GLOBAL DEFAULT UND getpid@@GLIBC_2.2.5
50: 0000000000004010 0 NOTYPE GLOBAL DEFAULT 25 _edata
51: 0000000000001208 0 FUNC GLOBAL HIDDEN 17 _fini
52: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@@GLIBC_2.2.5
53: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_
54: 0000000000004000 0 NOTYPE GLOBAL DEFAULT 25 __data_start
55: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
56: 0000000000004008 0 OBJECT GLOBAL HIDDEN 25 __dso_handle
57: 0000000000002000 4 OBJECT GLOBAL DEFAULT 18 _IO_stdin_used
58: 0000000000001190 101 FUNC GLOBAL DEFAULT 16 __libc_csu_init
59: 0000000000004018 0 NOTYPE GLOBAL DEFAULT 26 _end
60: 0000000000001080 47 FUNC GLOBAL DEFAULT 16 _start
61: 0000000000004010 0 NOTYPE GLOBAL DEFAULT 26 __bss_start
62: 0000000000001169 39 FUNC GLOBAL DEFAULT 16 main
63: 0000000000004010 0 OBJECT GLOBAL HIDDEN 25 __TMC_END__
64: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable
65: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@@GLIBC_2.2
Histogram for `.gnu.hash' bucket list length (total of 2 buckets):
Length Number % of total Coverage
0 1 ( 50.0%)
1 1 ( 50.0%) 100.0%
Version symbols section '.gnu.version' contains 8 entries:
地址:0x0000000000000514 Offset: 0x000514 Link: 6 (.dynsym)
000: 0 (*本地*) 0 (*本地*) 2 (GLIBC_2.2.5) 2 (GLIBC_2.2.5)
004: 2 (GLIBC_2.2.5) 0 (*本地*) 0 (*本地*) 2 (GLIBC_2.2.5)
Version needs section '.gnu.version_r' contains 1 entry:
地址:0x0000000000000528 Offset: 0x000528 Link: 7 (.dynstr)
000000: Version: 1 文件:libc.so.6 计数:1
0x0010: Name: GLIBC_2.2.5 标志:无 版本:2
Displaying notes found in: .note.gnu.property
所有者 Data size Description
GNU 0x00000010 NT_GNU_PROPERTY_TYPE_0
Properties: x86 feature: IBT, SHSTK
Displaying notes found in: .note.gnu.build-id
所有者 Data size Description
GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring)
Build ID: c72301e0c49b0cd407f0056210312787d9224273
Displaying notes found in: .note.ABI-tag
所有者 Data size Description
GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag)
OS: Linux, ABI: 3.2.0
重点关注节头以下信息:
[13] .plt PROGBITS 0000000000001020 00001020
0000000000000030 0000000000000010 AX 0 0 16
[14] .plt.got PROGBITS 0000000000001050 00001050
0000000000000010 0000000000000010 AX 0 0 16
[15] .plt.sec PROGBITS 0000000000001060 00001060
0000000000000020 0000000000000010 AX 0 0 16
[24] .got PROGBITS 0000000000003fb0 00002fb0
0000000000000050 0000000000000008 WA 0 0 8
从以上表可以看出:
其中:
0X1020 是.plt表在程序中的偏移位置。
0X1050 是.plt.got表在程序中的偏移位置。
0X1060 是.plt.sec表在程序中的偏移位置。
0X3fb0 是.got表在程序中的偏移位置。
我们可以看到,getpid和printf函数都在这个.got表中,其中偏移量是他们在表中的地址,信息是他们实际的地址,由于程序未启动,地址还没加载,所以显示的并不是程序的实际地址。
那么这个got表和got.plt表到底是怎么运作的呢?
首先,当一个程序第一次调用一个外部函数时,就会跳转到.plt表(注意,不是.got.plt),而这个表中包含有一些代码,这些代码总共有两个作用:
(1)调用链接器来解析某个外部函数的地址, 并填充到.got.plt中, 然后跳转到该函数。
(2)在.got.plt中查找并跳转到对应外部函数(如果已经填充过)。
相对的,.got.plt也同样具有两个功能:
1)如果在之前查找过该符号,内容为外部函数的具体地址。
2)如果没查找过, 则内容为跳转回.plt的代码。
所以当你首次调用某个外部函数时,其流程为code → .plt → .got.plt → .plt→.got.plt→target function
结合上图可更好的理解整个过程。
接下来要hook函数就很简单了,只需要将运行中的got.plt表中对应的地址覆盖为我们自己的函数地址,当调用时,自然就调用到我们自己的函数了。
2.2.2 got/plt hook 实现
接下来我们来实现一下hook的过程
首先,将测试代码改造一下,改造后测试代码如下:
#include <stdio.h>
#include <unistd.h>
#include <stdbool>
int mygetpid(){
return 12306;
}
int main(){
while(true){
printf("the pid is %d\n",getpid());
sleep(1);
}
return 0;
}
改造后的代码,每隔一段时间就会打印一下pid,然后我们还新增了一个函数,用于到时候替换用,我们再用readelf -a 来查看一下编译成的执行文件的elf情况如下:
首先是.got.plt表
接下来是.symtab,.symtab是c程序的符号表,其中包含有各种程序的符号,其内容如下:
我们可以看到,getpid函数和我们自己编写的mygetpid函数在这个表中都可以看到,由于getpid是外部引用函数,其地址是使用时动态加载,所以此时为0,接下来的内容就很明确了,我们只需要把.got.plt表中,位置为0X3fc0的值,覆写成我们自己的mygetpid函数的地址,就可以hook住getpid函数了。
那么我们应该怎么才能修改程序运行时候的内存地址呢,我们都知道,linux秉承的是万物皆文件的原则,程序在运行时候,其内存会映射为一个/proc/$pid/mem文件,修改这个文件,等于修改程序内存(其实这样说不够严谨,差不多是这个意思)。
于是我们可以编写个程序用来修改程序运行时候的内存,代码如下:
vim inject.c
--------------------------------------------
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc,char* argv[]) {
int pid = atoi(argv[1]);
unsigned long offset = 0x601018;
unsigned long myfunctionaddr = 0x4005b6;
char filename[32];
snprintf(filename, sizeof(filename),"/proc/%d/mem",pid);
int fd = open(filename, O_RDWR|O_SYNC);
lseek(fd,offset,SEEK_SET);
write(fd,&myfunctionaddr, sizeof(unsigned long));
return 0;
}
-----------------------------------
gcc -o inect inject.c
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通