链接
编译器驱动程序过程
以c语言为例子
- 首先将头文件预处理等展开,将
.c文件转化为.i文件,.i文件就是头文件展开过后的文件 - 编译器驱动将
.i文件转化为.s的ASCII码的汇编文件 - 将
.s的汇编文件编译成.o的二进制目标文件 - 由链接器将
.o文件链接,形成二进制可执行文件
GOT与PLT
首先说下这两个单词的全称。
plt 全称为过程链接表,英文单词为Procedure Linkage Table
got全称为全局偏移量表,英文名称为Global Offset Table
got表的前三项存放的都是固定的东西。got[0]和got[1]包含动态链接器在解析函数地址时会使用的地址,got[3]存放ld-linux.so的模块入口点,其他位置就存放需要动态绑定的函数。如下图所示
plt表前两项是特殊的,plt[0]跳转到动态链接器中,plt[1]调用__libc_start_main函数。
用一个测试文件先看看情况,代码如下。编译命令为gcc -g -o init.c init
1 |
|
在第一次调用malloc时,跟进看汇编代码,两个jmp一个push,在通过内存看看跳转的地址,其实就是跳转到第二条指令。根据书上所说的,每次调用一个进入一个没有绑定函数的plt中的代码,他的第一条指令总是跳转到第二条指令处。

在将malloc函数的id压入栈后,跳转到plt[0]处,指令就如下两条代码,第一条汇编代码将got[1]内容压入栈中,然后通过got[2]间接的跳转到动态链接器中,也就是函数_dl_runtime_resolve_xsavec,这样就完成了与got表的绑定。

当再次调用malloc函数时,plt的第一条指令就直接跳转到malloc函数处

上面就简单的介绍了延迟绑定的细节(要记住plt -> got -> func),做后在用书上的一副图,感觉这附图画得是真的很详细了

库打桩
这个技术也算是hook技术的一种,一共有三种形式:编译时打桩,链接时打桩和运行时打桩。
统一的测试代码如下
1 | //init.c |
运行打桩
依赖于LD_PRELOAD这个环境变量,动态链接器ld-linux.so会首先搜寻LD_PRELOAD这个库,将下面源代码编译成so文件,在在程序运行时设置LD_PRELOAD这个环境变量就可以进行打桩了(个人认为这种技术是平时用的最多的一种)
测试代码如下,编译命令为gcc -DRUNTIME -shared -fpic -o mymalloc.so mymalloc.c -ldl
1 | // mymalloc.c |
在使用时加上环境变量命令LD_PRELOAD="./mymalloc.so"
链接打桩
测试文件
1 | // mymalloc.c |
编译命令为
1 | gcc -DLINKTIME -c mymalloc.c |
-WI命令告诉编译器将后面的逗号都转换为空格
编译打桩
编译打桩需要自己写一个malloc.h头文件,测试代码与头文件内容如下
1 | // mymalloc.c |
1 | //malloc.h |
使用如下命令编译
1 | gcc -DCOMPILETIME -c mymalloc.c |
-I.这个参数的意思为在预处理编译器在搜索系统目录之前,首先搜索当前目录的malloc.h文件
坑
关于打桩技术我看得迷迷糊糊的,只是知道如何使用,但是具体的过程不清楚(猜测可能和编译器有关系),后面需要将这个坑填上。里面的知识点个人认为在安全方面比较常用。比如各种劫持技术等,其中里面的__real_malloc和__real_free这两个函数在打pwn时经常遇到,没有弄明白过,后面需要抽时间好好探究下这个问题。
