一.ELF文件结构
0x01什么是ELF文件
1.linux环境中,二进制可持性文件的类型是ELF(Executable and LinkableFormat)文件。类似windows下的exe
2.elf文件的格式比较简单,我们需要了解的就是elf文件中的各个节、段等概念
3.程序elf的基本信息存在于elf的头部信息中,这些信息包括指令的运行架构入口等等内容,我们可以通过 readelf -h
0x02ELF文件组成
1.elf文件中包含许多个节(section)各个节中存放不同的数据,:这些节的信息存放在节头表中,readelf-S
我们可以在ida中对节点进行查看
0x04elf文件的存储
1.elf文件平时是放在磁盘上 运行时放至在内存中
2.elf文件进入内存时结构 映射
elf文件在加载进入内存时:
elf文件的节(section)会被映射进内存中的段(segment),而这一映射过程遵循的机制是根据各个节的权限来进行映射的,
换句话说,可读可写的节被映射入一个段,只读的节被映射入一个段。
二.延迟绑定机制
0x01动态链接库
1.我们再写程序的过程中会用到系统函数,比如read write open 等等函数
2.这些函数是前辈开发人员已经在系统中定义好的,虽然我们在使用中仅仅只是输入了read 或者 open这样一个字符,但是其实际上是对系统已经定义好的函数进行调用,存放这些函数的库文件就是动态链接库 比如我们写c语言时的 main math 等等库
3.我们对于pwn题接触到的动态链接库就是libc.so文件
0x02静态编译与动态编译
1.什么是编译?
编译按照我的理解就是将程序转换成可执行文件的过程,大家常用的编译器就是这个意思,大家在写程序实际会出现一个框框就是我们所说的可执行文件,系统也有自己的大型编译器,只是我们以前没有感知罢了
2.什么是静态编译和动态编译
类似于c语言中运行程序需要写出头文件和main函数一样,系统在进行编译时,也需要调用系统中封装好的函数,根据pwn1笔记中的内容,我们可以知道系统的主函数和调用有关的内容都分配在开辟好的堆栈空间中.
下面我给大家举一个我自己学到例子,来理解什么是静态编译和动态编译
小明要开一个餐馆(program)餐馆的菜单上有几百种菜肴(函数),小明的餐馆每天都会来很多顾客,每个顾客点的菜都可能不一样。我们知道,每道菜所需要的食材(系统函数)都不一样,这些食材都存放于仓库(动态链接库中。
那么现在问题来了,小明如何保证每个顾客点的菜都能被满足呢?
A.第一种方式:小明把仓库中所有的食材都搬进厨房(静态编译)
这时,小明不需要挪地方(静态)只需要在厨房中就可以工作,但是这会带来冗余,可能厨房中的食材很多都用不上。
B.第二种方式:小明每次遇到新的所需要的食材,才去仓库取(动态编译)
这时,小明可能挪动的比较频繁(动态),但是可以保证厨房里面没那么多可能用不到的东西。
3.实践上的静态和动态编译
A.静态编译的思路就是将所有可能运行到的库函数一同编译到可执行文件中
这一方式的优点就在于在程序运行中不需要依赖动态链接库。适用的场合就是比如你本地编译的程序需要的动态链接库版本比较特殊,如果在别的机器上运行可能对方动态链接库版本和你不一样会出bug,这时候用静态编译。
缺点就是编译过后程序体积很大,编译速度也很慢,
B.动态编译的思路就是逢山开路,遇水架桥,直到遇到需要调用库函数的时候再去动态链接库中寻找。
所以其优点一方面是缩小了执行文件本身的体积,另一方面是加快了编译速度,
缺点是哪怕是很简单的程序,只用到了链接库中的一两条命令,也需要附带一个相对庞大的链接库;二是如果其他计算机上没有安装相应的运行库,则用动态编译的可执行文件就不能运动
0x03延迟绑定机制
1.什么是延迟绑定机制
在学习到这里时我产生了一个疑问,在实际的操作系统底层中采用的是静态还是动态编译,以下是chatgpt的回答
在实际的动态编译过程中,由于每一次编译都要调用系统函数,也就是每一次都要重新寻址,这样就太麻烦了也大大的拖延了计算机的编译效率,回到刚才的例子中
我们可以举出这样一个例子:我们再回去看看小明:小明说我选择第二种方式(动态编译)
但是小明餐馆开业后发现搞不赢,每次都要去仓库找,太麻烦了。于是乎,小明想到:每次我遇到新的食材,我就去仓库找,但是每次找完,我就在小本子(got表)上记录这个食材的地址,这样下一次找就快很多了!
实际上在计算机内部我们采用的也是这样一种方式我们称之为got表
2.什么是got表
这就是linux的延迟绑定机制,而存放这个地址的小本子就是got表。got表全称是Global Offset Table,也就是全局偏移量表。
在程序运行时,got表初始并不保存库函数的地址,只有在第一次调用过后程序才将这一地址保存在got表中。
3.GOT与PLT表
GOT(Global Offset Table,?全局偏移表)
GOT 是数据段用于地址无关代码的 Linux ELF 文件中确定全局变量和外部函数地址的表。ELF 中有 .got 和 .plt.got 两个 GOT 表,.got 表用于全局变量的引用地址,.got.plt 用于保存函数引用的地址,
PLT(Procedure Linkage Table,程序链接表),PLT 是 Linux ELF 文件中用于延迟绑定的表
4.两个表之间的关系以及调用流程
1.got和plt的关系就像兄弟一样,其联系非常紧密 作用大致相同 在正式的了解其的关系之前,我从定义中看到got 是全局变量 plt是函数地址 依据c语言的逻辑 写代码都是先写主函数也就是大家写的 main()猜测应该是先调用plt表 在2中我讲详细说明真正的调用流程
2.真正的调用流程如下图
在开始一次调用之前 PLT表会call 以下动态链接的函数
A.在第一次调用外部函数时,!plt表首先会跳到对应的got表项中。由于并没有被调用过,此时的got表存储的不是目标函数地址,此时的got表中存储的地址是plt表中的一段指令,其作用就是准备一些参数,进行动态解析。
B.跳转回plt表
C.跳转回plt表后,plt表又会跳转回plt的表头,表头内容就是调用动态解析函数,将目标函数地址存放入got表中。
在之后第二次以上的调用后程序已经完成了延迟绑定,got表中已经存储了目标函数地址,直接跳转即可