你能够用gdb,和反编译破解做者设置的密码。固然,做者提供了不带实现的c源码和(估计使用0g优化级别)。下降难度。node
code:数组
0000000000400ee0 <phase_1>: 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 <strings_not_equal> 400eee: 85 c0 test %eax,%eax 400ef0: 74 05 je 400ef7 <phase_1+0x17> 400ef2: e8 43 05 00 00 callq 40143a <explode_bomb> 400ef7: 48 83 c4 08 add $0x8,%rsp 400efb: c3 retq
作了挺多无用功,好比靠函数名就能才猜到的功能不必去反推c源码。也了解了elf文件,知道如何定义程序入口。最后使用gdb查看地址找到密码:数据结构
0x402400: "Border relations with Canada have never been better."
函数
000000000040145c <read_six_numbers>: # (%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 <read_six_numbers+0x3d> 401494: e8 a1 ff ff ff callq 40143a <explode_bomb> 401499: 48 83 c4 18 add $0x18,%rsp 40149d: c3 retq
read_six_number,根据传入的是栈指针,以及汇编一直在基于栈作赋值之类的运算,能够推测出arg2是数组。再用内存查看对0x4025c3,能够看到是"%d %d ..." 六个d。结合sscanf函数的意义,能够理解为把input,即参数0,以指定格式给到六个参数中,这六个参数应该是数组。oop
这里能够gdb而后画个图方便理解。优化
再看phase2就比较简单了。this
0000000000400efc <phase_2>: 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 <read_six_numbers> 400f0a: 83 3c 24 01 cmpl $0x1,(%rsp) 400f0e: 74 20 je 400f30 <phase_2+0x34> 400f10: e8 25 05 00 00 callq 40143a <explode_bomb> 400f15: eb 19 jmp 400f30 <phase_2+0x34> 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 <phase_2+0x29> 400f20: e8 15 05 00 00 callq 40143a <explode_bomb> 400f25: 48 83 c3 04 add $0x4,%rbx 400f29: 48 39 eb cmp %rbp,%rbx 400f2c: 75 e9 jne 400f17 <phase_2+0x1b> # jump to loop 400f2e: eb 0c jmp 400f3c <phase_2+0x40> # 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 <phase_2+0x1b> 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 数组。debug
结果:1 2 4 8 16 323d
That's number 2. Keep going!
指针
应该是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 <phase_3+0x27> 400f65: e8 d0 04 00 00 callq 40143a <explode_bomb> 400f6a: 83 7c 24 08 07 cmpl $0x7,0x8(%rsp) # arg1<=7 400f6f: 77 3c ja 400fad <phase_3+0x6a> 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 <phase_3+0x7b> 400f83: b8 c3 02 00 00 mov $0x2c3,%eax 400f88: eb 34 jmp 400fbe <phase_3+0x7b> 400f8a: b8 00 01 00 00 mov $0x100,%eax 400f8f: eb 2d jmp 400fbe <phase_3+0x7b> 400f91: b8 85 01 00 00 mov $0x185,%eax 400f96: eb 26 jmp 400fbe <phase_3+0x7b> 400f98: b8 ce 00 00 00 mov $0xce,%eax 400f9d: eb 1f jmp 400fbe <phase_3+0x7b> 400f9f: b8 aa 02 00 00 mov $0x2aa,%eax 400fa4: eb 18 jmp 400fbe <phase_3+0x7b> 400fa6: b8 47 01 00 00 mov $0x147,%eax 400fab: eb 11 jmp 400fbe <phase_3+0x7b> 400fad: e8 88 04 00 00 callq 40143a <explode_bomb> 400fb2: b8 00 00 00 00 mov $0x0,%eax 400fb7: eb 05 jmp 400fbe <phase_3+0x7b> 400fb9: b8 37 01 00 00 mov $0x137,%eax 400fbe: 3b 44 24 0c cmp 0xc(%rsp),%eax 400fc2: 74 05 je 400fc9 <phase_3+0x86> 400fc4: e8 71 04 00 00 callq 40143a <explode_bomb> 400fc9: 48 83 c4 18 add $0x18,%rsp 400fcd: c3 retq
答案:0 207
Halfway there!
000000000040100c <phase_4>: 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 <phase_4+0x29> 40102e: 83 7c 24 08 0e cmpl $0xe,0x8(%rsp) 401033: 76 05 jbe 40103a <phase_4+0x2e> 401035: e8 00 04 00 00 callq 40143a <explode_bomb> 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 <func4> # arg0, 0, e 40104d: 85 c0 test %eax,%eax 40104f: 75 07 jne 401058 <phase_4+0x4c> 401051: 83 7c 24 0c 00 cmpl $0x0,0xc(%rsp) 401056: 74 05 je 40105d <phase_4+0x51> 401058: e8 dd 03 00 00 callq 40143a <explode_bomb> 40105d: 48 83 c4 18 add $0x18,%rsp 401061: c3 retq
func4
0000000000400fce <func4>: # 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 <func4+0x24> 400fe6: 8d 51 ff lea -0x1(%rcx),%edx 400fe9: e8 e0 ff ff ff callq 400fce <func4> 400fee: 01 c0 add %eax,%eax 400ff0: eb 15 jmp 401007 <func4+0x39> 400ff2: b8 00 00 00 00 mov $0x0,%eax 400ff7: 39 f9 cmp %edi,%ecx 400ff9: 7d 0c jge 401007 <func4+0x39> 400ffb: 8d 71 01 lea 0x1(%rcx),%esi 400ffe: e8 cb ff ff ff callq 400fce <func4> 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.
成功的喜悦~
0000000000401062 <phase_5>: 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 <string_length> 40107f: 83 f8 06 cmp $0x6,%eax 401082: 74 4e je 4010d2 <phase_5+0x70> 401084: e8 b1 03 00 00 callq 40143a <explode_bomb> 401089: eb 47 jmp 4010d2 <phase_5+0x70> ~ 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 <phase_5+0x29> ~ 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 <strings_not_equal> 4010c2: 85 c0 test %eax,%eax 4010c4: 74 13 je 4010d9 <phase_5+0x77> 4010c6: e8 6f 03 00 00 callq 40143a <explode_bomb> 4010cb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) 4010d0: eb 07 jmp 4010d9 <phase_5+0x77> 4010d2: b8 00 00 00 00 mov $0x0,%eax 4010d7: eb b2 jmp 40108b <phase_5+0x29> 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 <phase_5+0x8c> 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...
这是六个语句中最长的一句。总共有三个部分。
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 <read_six_numbers> 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 <phase_6+0x34> # 401123: e8 12 03 00 00 callq 40143a <explode_bomb> 401128: 41 83 c4 01 add $0x1,%r12d 40112c: 41 83 fc 06 cmp $0x6,%r12d 401130: 74 21 je 401153 <phase_6+0x5f> 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 <phase_6+0x51> # 2 401140: e8 f5 02 00 00 callq 40143a <explode_bomb> 401145: 83 c3 01 add $0x1,%ebx 401148: 83 fb 05 cmp $0x5,%ebx 40114b: 7e e8 jle 401135 <phase_6+0x41> 40114d: 49 83 c5 04 add $0x4,%r13 401151: eb c1 jmp 401114 <phase_6+0x20> # (%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 <phase_6+0x6c>
检测你输入的数是否大于7,或者是否有重复的。这里有for循环嵌套比较难看懂。接着把每一个数-=7。
40116f: be 00 00 00 00 mov $0x0,%esi # rsi = i 401174: eb 21 jmp 401197 <phase_6+0xa3> # -------------------------------- 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 <phase_6+0x82> # -------------------------------- 401181: eb 05 jmp 401188 <phase_6+0x94> # ----------------------------------------------- 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 <phase_6+0xb7> # 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 <phase_6+0x8f> # ----------------------------------------------- # ------------------------------------else 40119f: b8 01 00 00 00 mov $0x1,%eax 4011a4: ba d0 32 60 00 mov $0x6032d0,%edx 4011a9: eb cb jmp 401176 <phase_6+0x82>
将栈上分配6个变量存放链表指针。这里功能最少确是最复杂的。由于引入了复杂数据结构(即便是最简单的),因此让反汇编起来难以理解,特别是双重循环的关系。到底谁是外循环,谁又是内循环,哪一个寄存器控制着跳出条件?而后就是慢慢完善c语言,最后让c和汇编的逻辑一一对应。
从gdb看得出来是链表
(gdb) x/6xg 0x7fffffffde40 0x7fffffffde40: 0x00000000006032d0 0x0000000000603320 0x7fffffffde50: 0x0000000000603310 0x0000000000603300 0x7fffffffde60: 0x00000000006032f0 0x00000000006032e0 x/12xg 0x6032d0 0x6032d0 <node1>: 0x000000010000014c 0x00000000006032e0 0x6032e0 <node2>: 0x00000002000000a8 0x00000000006032f0 0x6032f0 <node3>: 0x000000030000039c 0x0000000000603300 0x603300 <node4>: 0x00000004000002b3 0x0000000000603310 0x603310 <node5>: 0x00000005000001dd 0x0000000000603320 0x603320 <node6>: 0x00000006000001bb 0x0000000000000000
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 <phase_6+0xde> 4011cd: 48 89 d1 mov %rdx,%rcx # rcx 4011d0: eb eb jmp 4011bd <phase_6+0xc9> # 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 <phase_6+0xfa> 4011e9: e8 4c 02 00 00 callq 40143a <explode_bomb> 4011ee: 48 8b 5b 08 mov 0x8(%rbx),%rbx 4011f2: 83 ed 01 sub $0x1,%ebp 4011f5: 75 e8 jne 4011df <phase_6+0xeb> 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 <phase_6+0xfa> callq 40143a <explode_bomb>
第二部分(上面代码)就是要求前面后四位的要大于后面的。要求node1~node6 数据后4位从大到小排列。即对
(gdb) x/12xg 0x6032d0 0x6032d0 <node1>: 0x000000010000014c 0x0000000000603320 0x6032e0 <node2>: 0x00000002000000a8 0x00000000006032f0 0x6032f0 <node3>: 0x000000030000039c 0x0000000000603300 0x603300 <node4>: 0x00000004000002b3 0x0000000000603310 0x603310 <node5>: 0x00000005000001dd 0x0000000000603320 0x603320 <node6>: 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!