PLT/GOT表在ctf pwn题目中的理解与运用 结合CSAPPchap7

由于之前没学过CSAPP第七章的时候,做pwn题时许多概念比如PLT表,GOT表,.BSS段这些概念都不很清楚,按照学长的方法照猫画虎也算是能做出来。如今学习的时候是带着当时的问题学完了这一章。因此做一个小总结,主要是对本章和pwn中关联度较大的部分做一个说明

网上讲到和GOThijack相关的题目时,大多是推荐阅读CSAPP和连接相关的章节。如果你翻到了这篇文章,那你也就不用再学习原著了 😊当然,如果像更深入的了解,还是推荐CSAPP的。因为毕竟不能面面俱到,并且或许也会有理解错的地方。

  1. 理解什么是链接,为什么需要链接

我们写的一个程序(.c)不得不依赖外部库函数,无论是自己写的(比如说自己写的.h文件),还是libc给出的(比如说stdio.h中的printf函数)。要知道这些文件本质上是分布在一个个文件中的代码,储存在硬盘上。而函数调用,是需要运行时地址的。程序运行时,一定是在内存中,而不是硬盘。那么我们写的代码如何在运行时知道这些函数的位置?这就是链接器的工作。此外,连接器还负责对程序中的符号进行解析,这对pwn而言不太重要,但下文也会介绍。

理解了上面的话,链接器的大致工作就清楚了:寻找被程序调用的函数的位置,并记录下来(怎么记录?这就是两种链接方式,后文会讲)

  1. 链接器都做了什么事情?

链接器的主要工作是符号解析重定位

符号解析:记录程序中全局变量,静态变量,函数。目的是将文件中的以上变量,函数的引用和他们的定义联系起来。

重定位::把每个符号(以上讲的符号)和内存位置联系起来,也就是第一部分中我所理解的链接的主要目的,这一行为依赖于符号解析。

  1. ELF文件格式

为了解释以上内容,需要先了解ELF文件格式。在这里我直接用gdb vmmap来解释,比如说这个例子

可以看到:这里对我们有用的结构是:code段,data段、heap段(但本程序没有)

Code段:存放程序代码

Data段:存放程序中已初始化并且不初始化为零的全局变量,静态变量,函数,data段也包含之前提到的.bss段,存放的是未初始化或者初始化为零的全局变量,静态变量。这一段区域不占用内存(不储存符号对应的数值)。这样设置的原因是:为了尽量缩小elf文件大小(bss也称为:better save space)

为什么data段是可写的?很容易明白:因为我们可能修改全局变量的值。

由于符号解析对pwn题目理解意义不大,这里省去不写了。只需要知道这些内容被保存在.data段中即可。

  1. 链接的两种方式:静态链接与动态链接

我们做pwn题时,有时会碰到binary载入ida时左边有一坨函数的情况。这就属于静态链接的ELF。静态链接的含义是:将binary需要的共享库文件中的所有函数全部载入ELF中。(思考一下载入到哪里?函数载入到.code段,函数名称载入到.data段)这种题目由于给出许多函数,往往可以用rop来做。动辄上万个gardet。思考一下这种方式的好处与坏处?好处:程序运行不依赖于目标机器上的库函数(自己已经全部有了)坏处:binary非常大(可能几十MB);如果更新binary需要重新编译(想想如果是操作系统!)

大部分的pwn题目,还是动态链接(例如给出.so文件的binary)

  1. 重定位(静态部分)

这一部分是链接的重头戏。总体来说,此时连接器已经获取了待链接文件中代码节和数据节的确切大小(符号解析所做),需要合并输入模块,并为每个符号分配运行时地址

合并输入模块:由于连接的文件不止一个(.h和.c)需要对这些文件的编译出来的结果的.data,.text段等进行合并操作,成为一个elf

为每个符号分配运行时地址:通过汇编器生成的“重定位条目”解决。这一部分牵涉到相对寻址和绝对寻址两种算法,比较复杂(mei yong)不讲了,其实也用不着。

  1. 重定位(动态链接部分)

为了解决静态链接的问题,开发了动态链接这一思想:即每个应用共享一个内存中的共享库文件,防止产生多个相同的库函数副本。为了完成这一步,在加载时(而不是链接时!)需要动态链接器(又称为解释器)ld-linux.so(在gibc中)。他的主要工作是重定位libc.so的文本和数据段到某个内存段,并重定位binary对其中定义的所有符号的引用(虽然这一动作不是完全由他执行的,后文会讲)。

在这里也介绍一下patchelf工具以及原理,能够改变源binary要加载的动态链接器(否则则是本机libc)。在pwn题目中使用patchelf的方法是以下两行

patchelf --set interpreter /home/nicholas/glibc-all-in-one/libs/2.23-0ubuntu11.2_i386/ld-linux.so.2 --set-rpath /home/nicholas/glibc-all-in-one/libs/2.23-0ubuntu11.2_i386 ./binary

这两句话是什么意思?set-interpreter也就是:设置解释器(动态链接器)能够加载我们想要使用的libc到内存,后面跟着动态链接器的绝对路径,下一句是设置relative path也就是glibc的库的位置。这里也就是动态链接器的文件位置的绝对路径。这样就能在执行elf文件时加载我们所需要的libc到内存,从而使用和目标主机上一样的libc了。

接下来终于到了对GOT表和PLT表的解释。

GOT:Globle offset table全局偏移量表,位于数据段,是一个每个条目是8字节地址的数组

PLT:procedure linkage table过程连接表,位于代码段,是一个每个条目是16字节内容的数组。其中PLT[0]储存的信息能用来跳转到动态链接器中,PLT【1】是系统启动函数(__libc_start_main),其余每个条目都负责调用一个具体的函数。

CSAPP上有一个很好的例子能完全阐述PLT和GOT的合作

书上的解释是这样的,后文我会更详细的说明

所谓lazy-binding,也就是上文所说重定位binary不是完全由动态链接器完成的原因,就是在此,在程序中got和plt表也协助动态链接器完成了一部分工作。

在汇编语言中call addvec时,实际上是跳转到该函数(如果是glibc中动态链接的函数)对应的plt表

第一次调用该函数时,正如第一步,第二步所说,进入plt表之后,会先跳转到该函数对应的GOT表的地址处。这里addvec()在GOT中下标为4。此时观察GOT表的地址,它指向的跳转目标是对应PLT表的第二句话。是一句push指令。为什么push 1?待会解释。

Push 1之后,在plt表中,函数又跳转到0X4005A0位置,依然在PLT表中。这句话又将*GOT[1]push到栈上,还是不知道为什么push?哈哈哈哈

接下来一句jmpq GOT[2]跳转回GOT表中,下标为2的条目是动态链接器的地址,相当于调用了另外一个函数来处理刚才我们push到栈上的数据,寻找到我们想要的函数addvec的真实地址。考虑现有的栈上的数据

  1. Addvec的id(数值为1,解释是能被ELF运行时调用的函数中,在左上图显示下是从下标为3的system_start开始的,addvec正好位于第二个,也就是0,1下的1)
  2. *GOT[1],存储的是重定位条目的信息,里面记录了所有能够被重定位函数的段首地址(这里不是很确定)

那么根据这两个栈条目,动态链接器就计算 *GOT[1]+id 得到了addvec的实际地址,并重写到GOT表中。

  1. 之后调用该函数,跳转到plt表之后,会直接跳转到got表上,执行对应的函数。

于是容易看出,在GOThijack中,为什么要修改的是GOT表中的内容?为什么栈溢出时调用puts函数要使用plt表中的puts?能不能用GOT表puts呢?

理解了上述过程,回答就很简单了

因为程序执行流调用函数时,跳转到的位置是plt表,plt表第一条指令是跳转到GOT表,但是,如果修改此jump语句为类似system等函数的地址,程序不会遇到jmp语句,从而卡在这个“地址”处,无法接着执行。相反,修改GOT表的内容,在执行流到了plt时,会跳转到GOT表上的地址执行对应的函数,从而完成了hijack

不能调用GOT中的puts,理由和之前的类似,GOT表无论在修改前后,都只储存一个地址,无法实现函数的“调用” 。GOT表中,只有地址,而没有执行控制!

接下来留两个思考的问题

  1. 下载远程binary到本机运行时,都可以运行成功。但是本机没有源libc的ld,并且GOT和PLT表都是写死在binary中的,那为什么本地binary还找到版本不一样的本机libc并且能运行呢?
  2. 能不能在GOT表未初始化的情况下(也就是上图1第一次调用的情况下)修改GOT表中的内容实现hijack?
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值