Global Offset Table (GOT) and Procedure Linkage Table (PLT) - bin 0x12

本文探讨了动态链接库在程序执行中的作用,特别是PLT(Procedure Linkage Table)和GOT(Global Offset Table)如何允许程序调用动态链接库中的函数。当程序不知道函数的确切地址时,PLT作为一个跳转机制,而GOT存储了这些函数的实际地址。在exploit场景中,GOT可用于实现任意写入和读取,从而影响程序流程,例如通过覆盖GOT条目来改变函数调用的目标。
摘要由CSDN通过智能技术生成

GOT & PLT

we create a binary.

int main(){
    printf("Hello, World!\n");
    printf("DooDoo is here.\n");
    exit(0);
    return 1;
}
$ gcc -o test test.c

With 'ldd' we can see the dynamic libraries referenced from this binary. It also displays the path on my system where my libc binary is.

$ ldd test
	linux-gate.so.1 =>  (0xb7fe4000)
	libc.so.6 => /lib/libc.so.6 (0xb7e99000)
	/lib/ld-linux.so.2 (0xb7fe5000)

This is great, because then the program can be much smaller and libc can receive updates without having to recompile my binary.

But it also means that the addresses in libc might be different for each version.

So how can i compile a binary to assember when I have to know the exact address so I can create a call instruction?

Well this is where the PLT and GOT comes into play. 

During compilation, we don't know the address of puts.

so we just create a function trampoline. We call a location we know where it is, the PLT section. Which contains a jump with the jump location referenced from this list(or table). 

So all we have to do, to be able to use external functions from a library, is somehow write the real address of the libc function in this table.

And this is what's happening when we executing binary. 

As you know by now, an ELF binary is not just plain assembler code. Before our codes get executed, the complicated ELF format is parsed and based on that the process is set up by kernel. And during that time references like that might get resolved.

$ strace test
execve("/usr/bin/test", ["test"], [/* 26 vars */]) = 0
brk(0)                                  = 0x804f000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7fe0000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=13796, ...}) = 0
mmap2(NULL, 13796, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7fdc000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/libc.so.6", O_RDONLY)        = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\320m\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1319176, ...}) = 0
mmap2(NULL, 1329480, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7e97000
mprotect(0xb7fd5000, 4096, PROT_NONE)   = 0
mmap2(0xb7fd6000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x13e) = 0xb7fd6000
mmap2(0xb7fd9000, 10568, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7fd9000
close(3)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7e96000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb7e968d0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0xb7fd6000, 8192, PROT_READ)   = 0
mprotect(0xb7ffe000, 4096, PROT_READ)   = 0
munmap(0xb7fdc000, 13796)               = 0
brk(0)                                  = 0x804f000
brk(0x8070000)                          = 0x8070000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_LARGEFILE) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=1527584, ...}) = 0
mmap2(NULL, 1527584, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7d21000
close(3)                                = 0
open("/usr/share/locale/locale.alias", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=2570, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7fdf000
read(3, "# Locale name alias data base.\n#"..., 1024) = 1024
read(3, " entries are case independent.\n\n"..., 1024) = 1024
read(3, "eucKR\nko_KR\t\tko_KR.eucKR\nlithuan"..., 1024) = 522
read(3, "", 1024)                       = 0
close(3)                                = 0
munmap(0xb7fdf000, 4096)                = 0
open("/usr/lib/locale/zh_CN.UTF-8/LC_IDENTIFICATION", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/lib/locale/zh_CN.utf8/LC_IDENTIFICATION", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/lib/locale/zh_CN/LC_IDENTIFICATION", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/lib/locale/zh.UTF-8/LC_IDENTIFICATION", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/lib/locale/zh.utf8/LC_IDENTIFICATION", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/lib/locale/zh/LC_IDENTIFICATION", O_RDONLY) = -1 ENOENT (No such file or directory)
close(1)                                = 0
close(2)                                = 0
exit_group(1)                           = ?

The GOT is very useful when writing exploit.

1. Arbitrary Write: we have a vulnerability that allows us to write a value anywhere in memory.

For instance, we have an arbitrary write. We can write a value we want anywhere in memory. So you can overwrite the address in the GOT for a certain function. And the next time this victim function is called, the code will redirected to whatever you  entered in the GOT.

2. Arbitrary Read: leaking a value from the memory of the process. For example, with Use-After-Free or Format String vuln.

The addresses of the GOT in your binary is always fixed. So when you can read from an address you control, you can read an entry of the GOT, which is an address in libc, which you can then use to calculate the offsets to other locations of libc. Which is useful when you have to find ROP gadgets or you want to do a ret2libc.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值