Pwn学习随笔( 四 )

其中除第一个表项以外,plt 表的第一条都是跳转到对应的 got 表项,而 got 表项的内容我们可以通过 gdb 来看一下 , 如果函数还没有执行的时候,这里的地址是对应 plt 表项的下一条命令,即 push 0x0(作为参数传入-- 相应函数在 rel.plt 中的偏移)
在想要调用的函数没有被调用过 , 想要调用他的时候,是按照这个过程来调用的
xxx@plt -> xxx@got -> xxx@plt -> 公共@plt -> _dl_runtime_resolve(通过这个函数找到运行时函数的地址)
到这里我们还需要知道

  1. _dl_runtime_resolve 是怎么知道要查找 printf 函数的
  2. _dl_runtime_resolve 找到 printf 函数地址之后,它怎么知道回填到哪个 GOT 表项
第一个问题 , 在 xxx@plt 中,我们在 jmp 之前 push 了一个参数,每个 xxx@plt 的 push 的操作数都不一样 , 那个参数就相当于函数的 id,告诉了 _dl_runtime_resolve 要去找哪一个函数的地址
第二个问题,看 .rel.plt 的位置就对应着 xxx@plt 里 jmp 的地址 , 该位置就是偏移量,使用readelf -r plt读取elf文件中重定位节信息
Pwn学习随笔

文章插图
在 i386 架构下,除了每个函数占用一个 GOT 表项外,GOT 表项还保留了3个公共表项,也即 got 的前3项,分别保存:
  • got [0]: 本 ELF 动态段 (.dynamic 段)的装载地址
  • got [1]:本 ELF 的 link_map 数据结构描述符地址,包含了进行符号解析需要的当前 ELF 对象的信息 。每个 link_map 都是一条双向链表的一个节点 , 而这个链表保存了所有加载的 ELF 对象的信息 。
  • got [2]:指向动态装载器中 _dl_runtime_resolve 函数的指针 。
动态链接器在加载完 ELF 之后,都会将这3地址写到 GOT 表的前3项
Pwn学习随笔

文章插图

Pwn学习随笔

文章插图
通过编写如下所示的代码来帮助理解pltgot这两个表
// gcc -m32 -no-pie -g -o plt plt.c#include <stdio.h>#include <stdlib.h>int main(int argc, char **argv){puts("hello,world");exit(0);}使用objdump -h plt , 查看该文件编译后的所有节的信息
plt:file format elf32-i386Sections:Idx NameSizeVMALMAFile offAlgn 10 .rel.plt000000180804832c0804832c0000032c2**2CONTENTS, ALLOC, LOAD, READONLY, DATA 12 .plt000000400804903008049030000010302**4CONTENTS, ALLOC, LOAD, READONLY, CODE 13 .plt.sec000000300804907008049070000010702**4CONTENTS, ALLOC, LOAD, READONLY, CODE 22 .got000000040804bffc0804bffc00002ffc2**2CONTENTS, ALLOC, LOAD, DATA 23 .got.plt000000180804c0000804c000000030002**2CONTENTS, ALLOC, LOAD, DATA然后打开gdb进行分析,可以看到调用函数puts的地址为0x80491de,在该地址处下一个断点
Pwn学习随笔

文章插图
然后使用si进入puts函数内部
Pwn学习随笔

文章插图
可以看到在 puts@plt 中第一条指令是跳转,0x804c00c其实就是[_GLOBAL_OFFSET_TABLE_ + 12],这个地址刚好位于.got.plt表中,也就是说 , puts@plt 的第一步是去 .got.plt 找地址
23 .got.plt000000180804c0000804c000000030002**2CONTENTS, ALLOC, LOAD, DATA
Pwn学习随笔

文章插图
这时候我们如果再按一次si,可以看到会直接执行到下面的那一行的指令,原因就是:我们之前没有调用过 puts@plt 函数,.got.plt 里面存储的puts的地址就是下一条指令的地址而非真正的地址 , 所以按照之前说的 , 现在要触发链接器找到 puts 函数的地址了
下面的[_GLOBAL_OFFSET_TABLE_ + 4] 处的指令,这个地址0x804c004也是位于.got.plt节中的,查看该地址存储的内容
Pwn学习随笔

文章插图
其中0xf7ffd990指向ld.so的数据段,0xf7fe7b10指向可执行区域 , 简而言之,触发了链接器/加载器,在加载器处理之前,查看之前的.got.plt节中的地址0x804c00c所存储的值为

推荐阅读