链接
编译器驱动程序过程
以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时经常遇到,没有弄明白过,后面需要抽时间好好探究下这个问题。