PLT/GOT hook原理

2024-03-12  本文已影响0人  feifei_fly

一、ELF(Executable and Linkable Format)

1.1、ELF(Executable and Linkable Format)格式

ELF是一种行业标准的二进制数据封装格式,主要用于封装可执行文件、动态库、object 文件和 core dumps 文件。

ELF文件格式.jpg

1.2、ELF 解析工具

使用 google NDK 对源代码进行编译和链接,生成的动态库或可执行文件都是 ELF 格式的。用 readelf 可以查看 ELF 文件的基本信息,用 objdump 可以查看 ELF 文件的反汇编输出。

工具的位置
arm-linux-androideabi-readelf 和arm-linux-androideabi-objdump 位于Android的NDK目录下

已我的mac为例,路径如下

 /Users/baixuefei/Library/Android/sdk/ndk/16.1.4479499/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/
baixuefei@baixuefeideMacBook-Pro xhook % ./arm-linux-androideabi-readelf -h libtestxhook.so
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           ARM
  Version:                           0x1
  Entry point address:               0x12728
  Start of program headers:          52 (bytes into file)
  Start of section headers:          1146224 (bytes into file)
  Flags:                             0x5000200, Version5 EABI, soft-float ABI
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         9
  Size of section headers:           40 (bytes)
  Number of section headers:         35
  Section header string table index: 33
  

./arm-linux-androideabi-objdump -D libtestxhook.so

00012928 <Java_com_example_testxhook_XHookUtils_sayHello>:
   12928:   b580        push    {r7, lr}
   1292a:   466f        mov r7, sp
   1292c:   b082        sub sp, #8
   1292e:   9001        str r0, [sp, #4]
   12930:   9100        str r1, [sp, #0]
   12932:   f011 ec5e   blx 241f0 <__ThumbV7PILongThunk__ZNSt9exceptionD2Ev+0x104>
   12936:   b002        add sp, #8
   12938:   bd80        pop {r7, pc}

1.3ELF 格式文件构成

image

二、动态链接过程

2.1、静态链接和动态链接

程序对于外部函数的调用需要在生成可执行文件时将外部函数链接到程序中,链接的方式分为静态链接和动态链接。

2.2、 链接过程

当需要使用一个 Native 库(.so 文件)的时候,我们需要调用dlopen("libname.so")来加载这个库。

在我们调用了dlopen("libname.so")之后,系统首先会检查缓存中已加载的 ELF 文件列表。如果未加载则执行加载过程,如果已加载则计数加一,忽略该调用。然后系统会用从 libname.so 的dynamic节区中读取其所依赖的库,按照相同的加载逻辑,把未在缓存中的库加入加载列表。

下面命令来查看一个库的依赖:

readelf -d <library> | grep NEEDED

可以看到libtestxhook.so 依赖liblog.so、libm.so、libdl.so、libc.so四个外部库

baixuefei@baixuefeideMacBook-Pro xhook % ./arm-linux-androideabi-readelf -d ./libtestxhook.so 

Dynamic section at offset 0x2632c contains 27 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [liblog.so]
 0x00000001 (NEEDED)                     Shared library: [libm.so]
 0x00000001 (NEEDED)                     Shared library: [libdl.so]
 0x00000001 (NEEDED)                     Shared library: [libc.so]
 0x0000000e (SONAME)                     Library soname: [libtestxhook.so]
 0x0000001e (FLAGS)                      BIND_NOW
 0x6ffffffb (FLAGS_1)                    Flags: NOW
 0x00000011 (REL)                        0x9fcc
 0x00000012 (RELSZ)                      11744 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x6ffffffa (RELCOUNT)                   1002
 0x00000017 (JMPREL)                     0xe234
 0x00000002 (PLTRELSZ)                   1200 (bytes)
 0x00000003 (PLTGOT)                     0x274a0
 0x00000014 (PLTREL)                     REL
 0x00000006 (SYMTAB)                     0x210
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000005 (STRTAB)                     0x4e74
 0x0000000a (STRSZ)                      20824 (bytes)
 0x6ffffef5 (GNU_HASH)                   0x2cc8
 0x00000004 (HASH)                       0x3b8c
 0x0000001a (FINI_ARRAY)                 0x27324
 0x0000001c (FINI_ARRAYSZ)               8 (bytes)
 0x6ffffff0 (VERSYM)                     0x27d0
 0x6ffffffe (VERNEED)                    0x2c88
 0x6fffffff (VERNEEDNUM)                 2
 0x00000000 (NULL)                       0x0

2.2.1、 系统是如何加载的 ELF 文件的

2.2.2、 “.got”和“.plt”它们的具体含义

简单来说就是在数据段的地址表,假定我们有一些代码段的指令引用一些地址变量,编译器会引用 GOT 表来替代直接引用绝对地址,因为绝对地址在编译期是无法知道的,只有重定位后才会得到 ,GOT 自己本身将会包含函数引用的绝对地址。

PLT 不同于 GOT,它位于代码段,动态库的每一个外部函数都会在 PLT 中有一条记录,每一条 PLT记录都是一小段可执行代码。一般来说,外部代码都是在调用 PLT 表里的记录,然后 PLT 的相应记录会负责调用实际的函数。我们一般把这种设定叫作“蹦床”(Trampoline)。

2.2.3、首次加载外部函数

PLT 和 GOT 记录是一一对应的,并且 GOT 表第一次解析后会包含调用函数的实际地址。

[图片上传失败...(image-ea86bd-1710343090921)]

(1)跳转 GOT 表的指令(jmp *GOT[n])
(2)为上面提到的第 0 条解析地址函数准备参数。
(3)调用 PLT[0],这里 resovler 的实际地址是存储在 GOT[2] 。

在解析前 GOT[n] 会直接指向 jmp *GOT[n] 的下一条指令。在解析完成后,我们就得到了 func 的实际地址,动态加载器会将这个地址填入 GOT[n],然后调用 func。

外部函数第一次被调用过程

2.2.4、第二次加载外部函数

[图片上传失败...(image-8843a2-1710343090921)]
当第一次调用发生后,之后再调用函数 func 就高效简单很多。首先调用 PLT[n],然后执行 jmp *GOT[n]。GOT[n] 直接指向 func

image

三、PLT Hook基本原理

PLT Hook就是改变了原来的relocation后的地址。主要流程:

四、运行时加载(dlopen、dlsysm、dlerror、dlclose)

运行时加载so库,让程序自己在运行时控制加载指定的模块,并且可以在不需要该模块时将其卸载。

动态库的装载通过4个函数完成:打开动态库(dlopen)、查找符号(dlsym)、错误处理(dlerror)以及关闭动态库(dlclose)。

这几个API的实现是在/lib/libdl.so.2里面,它们的声明和相关常量在系统标准头文件<dlfcn.h>。

4.1、dlopen()

dlopen()函数用来打开一个动态库,并将其加载到进程的地址空间,完成初始化过程,它的原型定义为

void *dlopen(const char *filename, int flags);

dlopen的返回值是被加载的模块的句柄,这个句柄在后面使用dlsym或者dlclose时要用到。如果加载模块失败,则返回NULL。如果被加载的模块之间有依赖关系,比如模块A依赖于模块B,那么程序员必须手工加载被依赖的模块,比如先加载B,再加载A。

(1)第一个参数是被加载动态库的路径,如果这个路径是绝对路径(以”/”开始的路径),则该函数会将尝试直接打开动态库;如果是相对路径,那么dlopen()会尝试在以一定的顺序查找该动态库文件

(2)第二个参数flag表示函数符号的解析方式,其中RTLD_LAZY和RTLD_NOW必须要设置其中一种

4.2、dlsym()

dlsym函数就是运行时装载的核心,通过这个函数找到所需要运行的符号,函数原型如下:

void *dlsym(void *handle, const char *symbol); 

两个参数,第一个参数是有dlopen()返回的动态库的句柄;第二个参数即所查找的符号的名字,一个以\0结尾的C字符串。

如果查找的符号是个函数,那么它返回函数的地址;如果是个变量,它返回变量的地址;如果这个符号是个常量,那么它返回的是该常量的值。

4.3、dlerror()

每次我们调用dlopen()、dlsym()或dlclose()以后,都可以调用dlerror()函数来判断上一次调用是否成功。dlerror()的返回值类型是char*,如果返回NULL,则表示上一次调用成功,如果不是,则返回相应的错误消息。

4.4、dlclose()

dlclose()的作用跟dlopen()刚好相反,它的作用是将上一个已经加载的模块卸载。系统会维持一个加载引用计数器,每次使用dlopen()加载某模块时,相应的计数器加一;每次使用dlclose()卸载某模块时,相应计数器减一。只有当计数器值减到0时,模块才被真正的卸载掉。

参考文章

xhook
PLT Hook基本原理
PLT HOOK
GOT表和PLT表
[Android Native Hook技术你知道多少?](https://zhuanlan.zhihu.com/p/13269

解决函数名冲突问题(dlopen,dlsym,dlclose)
ca

上一篇 下一篇

猜你喜欢

热点阅读