plt.got表学习记录
作者:2021级enicky
一.简介plt和got表
1.plt
PLT : 程序链接表(PLT,Procedure Link Table)
2.got
GOT : 全局偏移表(GOT, Global Offset Table)
plt以及got的缘由都来自于文件的动态链接,简言之:
-
需要存放外部函数的数据段 —— PLT -
获取数据段存放函数地址的一小段额外代码 —— GOT
如果可执行文件中调用多个动态库函数,那每个函数都需要这两样东西,这样每样东西就形成一个表,每个函数使用中的一项。
存放函数地址的数据表,称为全局偏移表(GOT, Global Offset Table),而那个额外代码段表,称为程序链接表(PLT,Procedure Link Table)。
注:更详细的解释可以查看大佬博客,这里就不做深入解释。
https://blog.csdn.net/farmwang/article/details/73556017
二.保护机制
RELRO(RELocation Read Only)
在Linux中有两种RELRO模式:"Partial RELRO" 和 "Full RELRO"。Linux中Partical RELRO默认开启
Partial RELRO:
特点:
-
该ELF文件的各个部分被重新排序。内数据段(internal data sections)(如.got,.dtors等)置于程序数据段(program's data sections)(如.data和.bss)之前; -
无 plt 指向的GOT是只读的; -
GOT表可写(应该是与上面有所区别的)。
Full RELRO:简言之,会导致GOT表完全不可写。
-
支持Partial模式的所有功能; -
整个GOT表映射为只读的。
三.调用过程
分为第一次和第二次调用:
第一次调用:
在函数调用时,程序会call plt表中的地址,而plt表会跳转got表中的地址同时给plt表项的后六个字节,这个时候plt表得知got表没有自己想要jmp的地址,转身亲自去找需要跳转的地址,在这个过程还会将某个数压入栈中,这个数可以看出需要调用函数的ID,之后跳转回plt执行_dl_runtime_resolve函数(会根据函数的ID寻找对应函数),找到以后,plt表会将找到的函数地址给got表,然后plt跳转到相应的函数地址,成功执行call命令。
第二次调用(同一个函数):
plt会跳转到got表寻找函数地址,got中因为第一次调用时与函数绑定,可以直接跳转到相应的函数地址。
延迟绑定
这就是为什么在第一次调用时got表中没有函数地址。
当函数第一次被用到时才进行绑定(符号査找、重定位等),如果没有用到则不进行绑定。
为了提到cpude效率,在程序加载时并不会解析所有函数,而是在某个函数被调用时通过plt和got来对函数解析,然后将获得的函数地址放在got中,下一次调用就会直接使用got中的函数地址来对函数进行调用。
四.调试过程
以下列代码为例进行调试
#include <stdio.h>
void print_banner()
{
printf("Welcome to World of PLT and GOTn");
}
int main(void)
{
print_banner();
return 0;
}
使用命令编译查看;
gcc -Wall -g -o test.o -c test.c -m32
gcc -o test test.o -m32
objdump -d test.o
printf () 和函数是在 glibc 动态库里面的,只有当程序运行起来的时候才能确定地址,所以此时的 printf () 函数先用 fc ff ff ff 也就是有符号数的 -4 代替
程序运行时,printf的地址就会被存储到data段(data段中存储printf地址相当于printf的got表),(相当于printf的plt表)printf_sub就会跳转到data段中printf地址,最后到printf函数。
执行过程如上所示。
参考博客
https://blog.csdn.net/MrTreebook/article/details/124391848
https://zhuanlan.zhihu.com/p/130271689
https://eli0t-g.github.io/post/plt%E3%80%81got%E5%92%8C%E5%BB%B6%E8%BF%9F%E7%BB%91%E5%AE%9A/
---------------------------------------------------------------------
gdb+pwndbg动态调试学习笔记
作者:2021级g1ycine
前言
在对二进制文件进行逆向分析的时候,gdb的插件pwndbg是一个不错的工具,具有许多功能,这个插件扩展了gdb,使得调试更加得心应手。在使用pwndbg进行调试之前,我们得先学习一些基础的常用指令。
常用指令
基本指令
1、i (info) 查看信息,以下几个比较常用
-
i b (info break) 查看所有断点信息(编号、断点位置) -
i r (info registers) 查看各个寄存器当前的值 -
i f (info function) 查看所有函数名,需保留符号
2、backtrace 查看调用栈
3、q (quit) 退出程序
执行指令
1、r (run) 开始调试,自动跳到第一个断点,若没有断点,直到执行完代码或错误停止
2、c (continue) 继续执行,直至遇到断点或错误停止
3、s (step) 单步步进,相当于step into
4、n (next) 单步步过,相当于step over
断点指令
1、b *(0x123456) 在0x123456地址处设置一个断点
2、b main 在main函数入口处设置一个断点
3、delete 5 删除编号为5的断点
4、disable/enable 5 禁用/启用编号为5的断点
5、i b (info break) 查看所有断点信息(编号、断点位置)
显示指令
1、x /nuf 查看内存指令(f和u的顺序可以互换,也可以只有一个或者不带n)
-
n代表显示几个单元
-
u代表一个单元几个字节,b(一个字节),h(两个字节),w(四个字节),g(八个字节)
-
f代表显示数据的格式
x //十六进制格式 d //十进制格式 u //十六进制格式显示无符号整型。o //八进制格式 t //二进制格式 c //字符格式 f //浮点数格式 s //字符串格式 i //显示汇编指令
2、p (print) 打印指令
-
p main 打印main函数的地址 -
p &a 打印变量a的地址 -
p *(0x123456) 查看0x123456地址的值
题目分析
攻防世界 no-strings-attached
我们先把文件丢进Exepeinfo中,查看文件信息,如图,是一个32位的ELF文件
利用IDA进行分析
找到main函数,F5查看伪代码
跟进main函数中的每个函数看看,最后在authenticate()中发现了关键信息decrypt()
在后面的if语句中,跟进unk_8048B44是success,unk_8048BA4是access denied,而if判断条件是wcscmp比较函数,s2由函数decrypt()可得出
接下来利用gdb进行动态调试
将文件载入gdb,并在函数authenticate()下断点
r 运行至断点,进入函数authenticate()
n 单步执行authenticate(),直到call完decrypt()
i r 查看当前寄存器
通过查看ida的汇编指令可知,此时eax指向的字符串即为函数decrypt()的返回值
x/5ws $eax 查看eax中的字符串
5:显示5行数据
s:以字符串格式显示
w:4字节形式
参考博客
https://blog.csdn.net/Breeze_CAT/article/details/103789233
原文始发于微信公众号(火炬木攻防实验室):plt.got表学习记录 & gdb+pwndbg动态调试学习笔记
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论