这是一个非常有趣的lab。
你可以用gdb,和反编译破解作者设置的密码。当然,作者提供了不带实现的c源码和(估计使用0g优化级别)。降低难度。
phase1
code:
0000000000400ee0 :
400ee0: 48 83 ec 08 sub $0x8,%rsp
400ee4: be 00 24 40 00 mov $0x402400,%esi # 关键代码
400ee9: e8 4a 04 00 00 callq 401338
400eee: 85 c0 test %eax,%eax
400ef0: 74 05 je 400ef7
400ef2: e8 43 05 00 00 callq 40143a
400ef7: 48 83 c4 08 add $0x8,%rsp
400efb: c3 retq
做了挺多无用功,比如靠函数名就能才猜到的功能没必要去反推c源码。也了解了elf文件,知道如何定义程序入口。最后使用gdb查看地址找到密码:
0x402400: "Border relations with Canada have never been better."
phase2
000000000040145c : # (%rsp) in %rsi offse comp to 400efe
40145c: 48 83 ec 18 sub $0x18,%rsp # 400efe-64
401460: 48 89 f2 mov %rsi,%rdx # rdx=rsp-40 save arg2
401463: 48 8d 4e 04 lea 0x4(%rsi),%rcx
401467: 48 8d 46 14 lea 0x14(%rsi),%rax
40146b: 48 89 44 24 08 mov %rax,0x8(%rsp)
401470: 48 8d 46 10 lea 0x10(%rsi),%rax
401474: 48 89 04 24 mov %rax,(%rsp) #
401478: 4c 8d 4e 0c lea 0xc(%rsi),%r9 #
40147c: 4c 8d 46 08 lea 0x8(%rsi),%r8 #
401480: be c3 25 40 00 mov $0x4025c3,%esi
401485: b8 00 00 00 00 mov $0x0,%eax
40148a: e8 61 f7 ff ff callq 400bf0 <__isoc99_sscanf@plt>
40148f: 83 f8 05 cmp $0x5,%eax
401492: 7f 05 jg 401499
401494: e8 a1 ff ff ff callq 40143a
401499: 48 83 c4 18 add $0x18,%rsp
40149d: c3 retq
read_six_number,根据传入的是栈指针,以及汇编一直在基于栈做赋值之类的运算,可以推测出arg2是数组。再用内存查看对0x4025c3,可以看到是"%d %d ..." 六个d。结合sscanf函数的意义,可以理解为把input,即参数0,以指定格式给到六个参数中,这六个参数应该是数组。
这里可以gdb然后画个图方便理解。
再看phase2就比较简单了。
0000000000400efc :
400efc: 55 push %rbp
400efd: 53 push %rbx
400efe: 48 83 ec 28 sub $0x28,%rsp # -40
400f02: 48 89 e6 mov %rsp,%rsi
400f05: e8 52 05 00 00 callq 40145c
400f0a: 83 3c 24 01 cmpl $0x1,(%rsp)
400f0e: 74 20 je 400f30
400f10: e8 25 05 00 00 callq 40143a
400f15: eb 19 jmp 400f30
400f17: 8b 43 fc mov -0x4(%rbx),%eax
400f1a: 01 c0 add %eax,%eax
400f1c: 39 03 cmp %eax,(%rbx)
400f1e: 74 05 je 400f25
400f20: e8 15 05 00 00 callq 40143a
400f25: 48 83 c3 04 add $0x4,%rbx
400f29: 48 39 eb cmp %rbp,%rbx
400f2c: 75 e9 jne 400f17 # jump to loop
400f2e: eb 0c jmp 400f3c # jump to exit
400f30: 48 8d 5c 24 04 lea 0x4(%rsp),%rbx
400f35: 48 8d 6c 24 18 lea 0x18(%rsp),%rbp
400f3a: eb db jmp 400f17
400f3c: 48 83 c4 28 add $0x28,%rsp
400f40: 5b pop %rbx
400f41: 5d pop %rbp
400f42: c3 retq
就是数组开头必须是1,然后依次*2.我们也可以根据-4,+4,+0x18猜到这是一个int 数组。
结果:1 2 4 8 16 32
That's number 2. Keep going!
phase3
应该是lab里面最简单的一个了。方法本地分配两个变量,假设0x8(%rsp)=a,0xc(%rsp)=b,那么只需要满足公式:8a+0x400f7c=b即可,那么设置a=0,指令会跳转到0x400f7c处,可以看到直接赋值%rax 0xcf,那么我们只需要另b=207就行了。400fbe地址处
400f43: 48 83 ec 18 sub $0x18,%rsp
400f47: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx
400f4c: 48 8d 54 24 08 lea 0x8(%rsp),%rdx
400f51: be cf 25 40 00 mov $0x4025cf,%esi
400f56: b8 00 00 00 00 mov $0x0,%eax
400f5b: e8 90 fc ff ff callq 400bf0 <__isoc99_sscanf@plt>
400f60: 83 f8 01 cmp $0x1,%eax
400f63: 7f 05 jg 400f6a
400f65: e8 d0 04 00 00 callq 40143a
400f6a: 83 7c 24 08 07 cmpl $0x7,0x8(%rsp) # arg1<=7
400f6f: 77 3c ja 400fad
400f71: 8b 44 24 08 mov 0x8(%rsp),%eax
400f75: ff 24 c5 70 24 40 00 jmpq *0x402470(,%rax,8)
400f7c: b8 cf 00 00 00 mov $0xcf,%eax
400f81: eb 3b jmp 400fbe
400f83: b8 c3 02 00 00 mov $0x2c3,%eax
400f88: eb 34 jmp 400fbe
400f8a: b8 00 01 00 00 mov $0x100,%eax
400f8f: eb 2d jmp 400fbe
400f91: b8 85 01 00 00 mov $0x185,%eax
400f96: eb 26 jmp 400fbe
400f98: b8 ce 00 00 00 mov $0xce,%eax
400f9d: eb 1f jmp 400fbe
400f9f: b8 aa 02 00 00 mov $0x2aa,%eax
400fa4: eb 18 jmp 400fbe
400fa6: b8 47 01 00 00 mov $0x147,%eax
400fab: eb 11 jmp 400fbe
400fad: e8 88 04 00 00 callq 40143a
400fb2: b8 00 00 00 00 mov $0x0,%eax
400fb7: eb 05 jmp 400fbe
400fb9: b8 37 01 00 00 mov $0x137,%eax
400fbe: 3b 44 24 0c cmp 0xc(%rsp),%eax
400fc2: 74 05 je 400fc9
400fc4: e8 71 04 00 00 callq 40143a
400fc9: 48 83 c4 18 add $0x18,%rsp
400fcd: c3 retq
答案:0 207
Halfway there!
phase4
000000000040100c :
40100c: 48 83 ec 18 sub $0x18,%rsp # arg1=0 arg0<=e
401010: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx # 参数1
401015: 48 8d 54 24 08 lea 0x8(%rsp),%rdx # 参数0
40101a: be cf 25 40 00 mov $0x4025cf,%esi
40101f: b8 00 00 00 00 mov $0x0,%eax
401024: e8 c7 fb ff ff callq 400bf0 <__isoc99_sscanf@plt>
401029: 83 f8 02 cmp $0x2,%eax
40102c: 75 07 jne 401035
40102e: 83 7c 24 08 0e cmpl $0xe,0x8(%rsp)
401033: 76 05 jbe 40103a
401035: e8 00 04 00 00 callq 40143a
40103a: ba 0e 00 00 00 mov $0xe,%edx
40103f: be 00 00 00 00 mov $0x0,%esi
401044: 8b 7c 24 08 mov 0x8(%rsp),%edi
401048: e8 81 ff ff ff callq c # arg0, 0, e
40104d: 85 c0 test %eax,%eax
40104f: 75 07 jne 401058
401051: 83 7c 24 0c 00 cmpl $0x0,0xc(%rsp)
401056: 74 05 je 40105d
401058: e8 dd 03 00 00 callq 40143a
40105d: 48 83 c4 18 add $0x18,%rsp
401061: c3 retq
func4
0000000000400fce : # x in %rdi , y in %rsi z in %rdx
400fce: 48 83 ec 08 sub $0x8,%rsp
400fd2: 89 d0 mov %edx,%eax
400fd4: 29 f0 sub %esi,%eax
400fd6: 89 c1 mov %eax,%ecx
400fd8: c1 e9 1f shr $0x1f,%ecx
400fdb: 01 c8 add %ecx,%eax
400fdd: d1 f8 sar %eax
400fdf: 8d 0c 30 lea (%rax,%rsi,1),%ecx
400fe2: 39 f9 cmp %edi,%ecx
400fe4: 7e 0c jle 400ff2
400fe6: 8d 51 ff lea -0x1(%rcx),%edx
400fe9: e8 e0 ff ff ff callq 400fce
400fee: 01 c0 add %eax,%eax
400ff0: eb 15 jmp 401007
400ff2: b8 00 00 00 00 mov $0x0,%eax
400ff7: 39 f9 cmp %edi,%ecx
400ff9: 7d 0c jge 401007
400ffb: 8d 71 01 lea 0x1(%rcx),%esi
400ffe: e8 cb ff ff ff callq 400fce
401003: 8d 44 00 01 lea 0x1(%rax,%rax,1),%eax
401007: 48 83 c4 08 add $0x8,%rsp
40100b: c3 retq
这一题要结合phase4 和fun4来看。
首先看sscanf的参数,%rsi
在0x4025c
f处,gdb查看是要求两个整数,根据40102c
行也能得出此结论。40102e
行说明参数需要0小于等于14,然后0,和e被当作参数1,2传递给fun4.再根据fun4的汇编反汇编一下,经过几版优化,基本可以得出它的功能如上,一直用z模2,直到z==x
,否则返回2*fun(x,y,--z)
,其实else不用管它,因为如果你x传递0,那么0*任何数都会返回0。而且结合401051
行。发现第二个输入的数,也要是0,那么就更不会走第二个情况了。
感觉这题需要的debug会比较多一点。
答案:0 0
鄙人可能错误的反汇编
int fun4(int x, int y, int z) //arg0,0,e ,should return 0
{
z %=2;//offset%2
if(z == x){
return 0;
}
else{
return 2*fun(x,y,--z);
}
}
So you got that one. Try this one.
成功的喜悦~
phase5
0000000000401062 :
401062: 53 push %rbx
401063: 48 83 ec 20 sub $0x20,%rsp
401067: 48 89 fb mov %rdi,%rbx
40106a: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax #40
401071: 00 00
401073: 48 89 44 24 18 mov %rax,0x18(%rsp) # 24 金丝雀值
401078: 31 c0 xor %eax,%eax # eax=0;
40107a: e8 9c 02 00 00 callq 40131b
40107f: 83 f8 06 cmp $0x6,%eax
401082: 74 4e je 4010d2
401084: e8 b1 03 00 00 callq 40143a
401089: eb 47 jmp 4010d2
~ 40108b: 0f b6 0c 03 movzbl (%rbx,%rax,1),%ecx
~ 40108f: 88 0c 24 mov %cl,(%rsp)
~ 401092: 48 8b 14 24 mov (%rsp),%rdx
~ 401096: 83 e2 0f and $0xf,%edx
~ 401099: 0f b6 92 b0 24 40 00 movzbl 0x4024b0(%rdx),%edx
~ 4010a0: 88 54 04 10 mov %dl,0x10(%rsp,%rax,1)
~ 4010a4: 48 83 c0 01 add $0x1,%rax
~ 4010a8: 48 83 f8 06 cmp $0x6,%rax
~ 4010ac: 75 dd jne 40108b
~ 4010ae: c6 44 24 16 00 movb $0x0,0x16(%rsp)
~ 4010b3: be 5e 24 40 00 mov $0x40245e,%esi
~ 4010b8: 48 8d 7c 24 10 lea 0x10(%rsp),%rdi
~ 4010bd: e8 76 02 00 00 callq 401338
4010c2: 85 c0 test %eax,%eax
4010c4: 74 13 je 4010d9
4010c6: e8 6f 03 00 00 callq 40143a
4010cb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
4010d0: eb 07 jmp 4010d9
4010d2: b8 00 00 00 00 mov $0x0,%eax
4010d7: eb b2 jmp 40108b
4010d9: 48 8b 44 24 18 mov 0x18(%rsp),%rax
4010de: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
4010e5: 00 00
4010e7: 74 05 je 4010ee
4010e9: e8 42 fa ff ff callq 400b30 <__stack_chk_fail@plt>
4010ee: 48 83 c4 20 add $0x20,%rsp
4010f2: 5b pop %rbx
4010f3: c3 retq
比较核心的代码就是我用‘~’号标记出来的。大致的意思是取你输入字符的最后一个字节,保留低4位,在加上基地址0x4024b0
取到一个字符,凑6个成字符串,再和0x40245e
处的字符比较,一样退出,否则引爆。
比较难懂的是40106a
行mov %fs:0x28,%rax
。搜索得知这是利用段寄存器获得随机值。还有4010a4: add $0x1,%rax
的目的。弄明白这两个基本没有问题。
这题没有固定答案,只要你输入的字符串每个字节低四位依次等于9fe567
的二进制表示就行。我的答案:9/.567
Good work! On to the next...
phase6
这是六个语句中最长的一句。总共有三个部分。
部分1
4010f4: 41 56 push %r14
4010f6: 41 55 push %r13
4010f8: 41 54 push %r12
4010fa: 55 push %rbp
4010fb: 53 push %rbx
4010fc: 48 83 ec 50 sub $0x50,%rsp
401100: 49 89 e5 mov %rsp,%r13
401103: 48 89 e6 mov %rsp,%rsi
401106: e8 51 03 00 00 callq 40145c
40110b: 49 89 e6 mov %rsp,%r14
40110e: 41 bc 00 00 00 00 mov $0x0,%r12d
401114: 4c 89 ed mov %r13,%rbp # --------------
401117: 41 8b 45 00 mov 0x0(%r13),%eax
40111b: 83 e8 01 sub $0x1,%eax
40111e: 83 f8 05 cmp $0x5,%eax
401121: 76 05 jbe 401128 #
401123: e8 12 03 00 00 callq 40143a
401128: 41 83 c4 01 add $0x1,%r12d
40112c: 41 83 fc 06 cmp $0x6,%r12d
401130: 74 21 je 401153
401132: 44 89 e3 mov %r12d,%ebx
401135: 48 63 c3 movslq %ebx,%rax
401138: 8b 04 84 mov (%rsp,%rax,4),%eax
40113b: 39 45 00 cmp %eax,0x0(%rbp)
40113e: 75 05 jne 401145 # 2
401140: e8 f5 02 00 00 callq 40143a
401145: 83 c3 01 add $0x1,%ebx
401148: 83 fb 05 cmp $0x5,%ebx
40114b: 7e e8 jle 401135
40114d: 49 83 c5 04 add $0x4,%r13
401151: eb c1 jmp 401114 # (%r13+4)-1<=5
401153: 48 8d 74 24 18 lea 0x18(%rsp),%rsi # [5]
401158: 4c 89 f0 mov %r14,%rax # [0]
40115b: b9 07 00 00 00 mov $0x7,%ecx
401160: 89 ca mov %ecx,%edx
401162: 2b 10 sub (%rax),%edx # [i]= 7- [i] i start with 0
401164: 89 10 mov %edx,(%rax)
401166: 48 83 c0 04 add $0x4,%rax # i++
40116a: 48 39 f0 cmp %rsi,%rax # while (i!=5) loop.
40116d: 75 f1 jne 401160
检测你输入的数是否大于7,或者是否有重复的。这里有for循环嵌套比较难看懂。接着把每个数-=7。
部分2
40116f: be 00 00 00 00 mov $0x0,%esi # rsi = i
401174: eb 21 jmp 401197
# --------------------------------
401176: 48 8b 52 08 mov 0x8(%rdx),%rdx # amazing code!
40117a: 83 c0 01 add $0x1,%eax
40117d: 39 c8 cmp %ecx,%eax
40117f: 75 f5 jne 401176
# --------------------------------
401181: eb 05 jmp 401188
# -----------------------------------------------
401183: ba d0 32 60 00 mov $0x6032d0,%edx
# ------------------------------------内循环 i 找到a[i]>1的,同时对i++
401188: 48 89 54 74 20 mov %rdx,0x20(%rsp,%rsi,2)
40118d: 48 83 c6 04 add $0x4,%rsi
401191: 48 83 fe 18 cmp $0x18,%rsi # i < 6
401195: 74 14 je 4011ab # jump out from loop
401197: 8b 0c 34 mov (%rsp,%rsi,1),%ecx
40119a: 83 f9 01 cmp $0x1,%ecx
40119d: 7e e4 jle 401183
# -----------------------------------------------
# ------------------------------------else
40119f: b8 01 00 00 00 mov $0x1,%eax
4011a4: ba d0 32 60 00 mov $0x6032d0,%edx
4011a9: eb cb jmp 401176
将栈上分配6个变量存放链表指针。这里功能最少确是最复杂的。因为引入了复杂数据结构(即使是最简单的),所以让反汇编起来难以理解,特别是双重循环的关系。到底谁是外循环,谁又是内循环,哪个寄存器控制着跳出条件?然后就是慢慢完善c语言,最后让c和汇编的逻辑一一对应。
从gdb看得出来是链表
(gdb) x/6xg 0x7fffffffde40
0x7fffffffde40: 0x00000000006032d0 0x0000000000603320
0x7fffffffde50: 0x0000000000603310 0x0000000000603300
0x7fffffffde60: 0x00000000006032f0 0x00000000006032e0
x/12xg 0x6032d0
0x6032d0 : 0x000000010000014c 0x00000000006032e0
0x6032e0 : 0x00000002000000a8 0x00000000006032f0
0x6032f0 : 0x000000030000039c 0x0000000000603300
0x603300 : 0x00000004000002b3 0x0000000000603310
0x603310 : 0x00000005000001dd 0x0000000000603320
0x603320 : 0x00000006000001bb 0x0000000000000000
部分3
4011ab: 48 8b 5c 24 20 mov 0x20(%rsp),%rbx # b=node1
4011b0: 48 8d 44 24 28 lea 0x28(%rsp),%rax #
4011b5: 48 8d 74 24 50 lea 0x50(%rsp),%rsi #
4011ba: 48 89 d9 mov %rbx,%rcx
4011bd: 48 8b 10 mov (%rax),%rdx #
4011c0: 48 89 51 08 mov %rdx,0x8(%rcx) #
4011c4: 48 83 c0 08 add $0x8,%rax #
4011c8: 48 39 f0 cmp %rsi,%rax # not
4011cb: 74 05 je 4011d2
4011cd: 48 89 d1 mov %rdx,%rcx # rcx
4011d0: eb eb jmp 4011bd # 0x6032d0 332
4011d2: 48 c7 42 08 00 00 00 movq $0x0,0x8(%rdx) # *(d->next)=0
4011d9: 00
4011da: bd 05 00 00 00 mov $0x5,%ebp
4011df: 48 8b 43 08 mov 0x8(%rbx),%rax
4011e3: 8b 00 mov (%rax),%eax
4011e5: 39 03 cmp %eax,(%rbx)
4011e7: 7d 05 jge 4011ee
4011e9: e8 4c 02 00 00 callq 40143a
4011ee: 48 8b 5b 08 mov 0x8(%rbx),%rbx
4011f2: 83 ed 01 sub $0x1,%ebp
4011f5: 75 e8 jne 4011df
4011f7: 48 83 c4 50 add $0x50,%rsp
4011fb: 5b pop %rbx
4011fc: 5d pop %rbp
4011fd: 41 5c pop %r12
4011ff: 41 5d pop %r13
401201: 41 5e pop %r14
401203: c3 retq
其实这一部分还是可以根据4011d9: 00
分成2部分。第一部分把0x6032d0
处的链表连接起来。具体是node6->node1;其余从小到大依次连接。
mov 0x8(%rbx),%rax
mov (%rax),%eax
cmp %eax,(%rbx)
jge 4011ee
callq 40143a
第二部分(上面代码)就是要求前面后四位的要大于后面的。要求node1~node6 数据后4位从大到小排列。即对
(gdb) x/12xg 0x6032d0
0x6032d0 : 0x000000010000014c 0x0000000000603320
0x6032e0 : 0x00000002000000a8 0x00000000006032f0
0x6032f0 : 0x000000030000039c 0x0000000000603300
0x603300 : 0x00000004000002b3 0x0000000000603310
0x603310 : 0x00000005000001dd 0x0000000000603320
0x603320 : 0x00000006000001bb 0x0000000000603310
排列。而这个数据是在第二部放置的。
应该的顺序:3 4 5 6 1 2。然而在部分1会对所有输入-=7。
所以答案:4 3 2 1 6 5Congratulations! You've defused the bomb!
至此,bomblab主线完成,据说还有一个secret phase .弃。
Welcome to my fiendish little bomb. You have 6 phases with which to blow yourself up. Have a nice day!
Phase 1 defused. How about the next one?
That's number 2. Keep going!
Halfway there!
So you got that one. Try this one.
Good work! On to the next...
Congratulations! You've defused the bomb!