binary bombs
拆除“binary bombs”。一个“binary bombs”(二进制炸弹,下文将简称为炸弹)是一个Linux可执行程序,包含了6个阶段(或层次、关卡)。炸弹运行的每个阶段要求你输入一个特定字符串,你的输入符合程序预期的输入,该阶段的炸弹就被拆除引信即解除了,否则炸弹“爆炸”打印输出 “BOOM!!!”。实验的目标是拆除尽可能多的炸弹层次。 每个炸弹阶段考察了机器级程序语言的一个不同方面,难度逐级递增
另外还有一个隐藏阶段,只有在第4阶段的解后附加一特定字符串后才会出现
拆除“binary bombs”,增强对程序的机器级表示、汇编语言、调试器和逆向工程等方面原理与技能的掌握。
· 阶段1:字符串比较
· 阶段2:循环
· 阶段3:条件/分支
· 阶段4:递归调用和栈
· 阶段5:指针
· 阶段6:链表/指针/结构
构造自己的bomb
phase_1
首先调用objdump –d bomb > bomb_disas.txt 对bomb进行反汇编并将汇编源代码输出到boomb_disas.txt文本文件中。
查看该汇编源代码文件,可以在main函数中找到如下语句,从而得知第一关的处理程序包含在main()函数所调用的函数phase_1()中,判断的过程可以参照bomb.c文件源码。
在反汇编代码中寻找这个子函数phase_1:
接下来,使用gdb调试bomb二进制文件:gdb bomb后,运行break phase_1,也就是在phase_1函数处设置一个断点,然后运行run,开始调试。运行disas phase_1,得到如下信息:
可以看到%esi中装入的就是待比较的目标字符串地址:
接下来,去设置断点去检测这个答案是否正确,我们kill当前程序的调试。再在explode_bomb处设置断点break explode_bomb,然后run开始运行,按照提示输入这个字符串。
第一关解除。
phase_2
按照第一关的思路和步骤进行调试:
000000000400f0c <phase_2>:
400f0c: 55 push %rbp//把数据压入栈
400f0d: 53 push %rbx//把数据压入栈
400f0e: 48 83 ec 28 sub $0x28,%rsp//把栈指针减少40,提供局部变量空间
400f12: 48 89 e6 mov %rsp,%rsi//把栈指针状态存入%rsi中
400f15: e8 e5 06 00 00 callq 4015ff <read_six_numbers>// 调用函数read_six_numbers,读取6个数字,此时(%rsp)已被赋值
400f1a: 83 3c 24 00 cmpl $0x0,(%rsp)//(%rsp)的值即输入的第一个参数的值,将(%rsp)的值与0比较,需要等于0来避免bomb。
400f1e: 79 24 jns 400f44 <phase_2+0x38>//若相等则跳转
400f20: e8 a4 06 00 00 callq 4015c9 <explode_bomb>//否则爆炸
400f25: eb 1d jmp 400f44 <phase_2+0x38>//jmp指令无条件跳转400f44
400f27: 89 d8 mov %ebx,%eax //%eax=ebx
400f29: 03 45 fc add -0x4(%rbp),%eax //%eax=%eax+num1。
400f2c: 39 45 00 cmp %eax,0x0(%rbp) //比较%eax和num2
400f2f: 74 05 je 400f36 <phase_2+0x2a> //相等
400f31: e8 93 06 00 00 callq 4015c9 <explode_bomb> //不相等
400f36: 83 c3 01 add $0x1,%ebx //%ebx+1
400f39: 48 83 c5 04 add $0x4,%rbp //下一个num地址
400f3d: 83 fb 06 cmp $0x6,%ebx //比较
400f40: 75 e5 jne 400f27 <phase_2+0x1b> //不相等
400f42: eb 0c jmp 400f50 <phase_2+0x44> //相等
400f44: 48 8d 6c 24 04 lea 0x4(%rsp),%rbp//将%rsp+4代表的地址移入%rbx,即第2个输入数的地址
400f49: bb 01 00 00 00 mov $0x1,%ebx //%ebx=1
400f4e: eb d7 jmp 400f27 <phase_2+0x1b>//跳转
400f50: 48 83 c4 28 add $0x28,%rsp //释放空间
400f54: 5b pop %rbx
400f55: 5d pop %rbp
400f56: c3 retq
分析汇编代码:
由
我们需要输入6个数,并且第一个数(num1=0)需要等于0来避免bomb。
接下来跳转到0x400f44地址处的指令,%rbp=(%rsp)地址的偏移4处的值,也就是num2。%ebx=1。跳转到0x400f27,开始循环。
%eax=ebx=1。%eax=%eax+num1。比较%eax和num2,相等不会bomb,所以num2=1。接下来add指令,%ebx+=1,%rbp+=4,也就是%rbp=num3。跳转到循环,直到%ebx>6。
第二次循环,%eax=ebx=2。%eax=%eax+num2=3。比较%eax和num3。避免bomb,所以num3=3。
同理,推得这六个数:0 1 3 6 10 15
接下来,去设置断点去检测这个答案是否正确,我们kill当前程序的调试。再在explode_bomb处设置断点break explode_bomb,然后run开始运行,按照提示输入这六个数。
第二关解除。
phase_3
0000000000400f57 <phase_3>:
400f57: 48 83 ec 18 sub $0x18,%rsp//把栈指针减少24,提供局部变量空间
400f5b: 48 8d 4c 24 08 lea 0x8(%rsp),%rcx//加载地址,将0x8(%rsp)设为num2
400f60: 48 8d 54 24 0c lea 0xc(%rsp),%rdx//加载地址,将0xc(%rsp)设为num1
400f65: be 21 28 40 00 mov $0x402821,%esi//经测试,0x4025cf对应字符串"%d %d"
400f6a: b8 00 00 00 00 mov $0x0,%eax//初始化%eax
400f6f: e8 bc fc ff ff callq 400c30 <__isoc99_sscanf@plt>
400f74: 83 f8 01 cmp $0x1,%eax
400f77: 7f 05 jg 400f7e <phase_3+0x27>//jg:有符号大于则跳转,说明scanf输入数据的个数必须大于1
400f79: e8 4b 06 00 00 callq 4015c9 <explode_bomb>//否则爆炸
400f7e: 83 7c 24 0c 07 cmpl $0x7,0xc(%rsp)
400f83: 77 66 ja 400feb <phase_3+0x94>//ja:无符号大于则跳转,至爆炸,说明num1为无符号数,大于0且需要小于等于7,所以num1=[0,7]
400f85: 8b 44 24 0c mov 0xc(%rsp),%eax //将num1储存到%eax中
400f89: ff 24 c5 80 25 40 00 jmpq *0x402580(,%rax,8) //根据该指令和后续的指令格式,很容易判断此处是switch语句的跳转表
400f90: b8 00 00 00 00 mov $0x0,%eax //1
400f95: eb 05 jmp 400f9c <phase_3+0x45>
400f97: b8 aa 00 00 00 mov $0xaa,%eax //0
400f9c: 2d e2 02 00 00 sub $0x2e2,%eax
400fa1: eb 05 jmp 400fa8 <phase_3+0x51>
400fa3: b8 00 00 00 00 mov $0x0,%eax //2
400fa8: 05 e2 01 00 00 add $0x1e2,%eax
400fad: eb 05 jmp 400fb4 <phase_3+0x5d>
400faf: b8 00 00 00 00 mov $0x0,%eax //3
400fb4: 2d 6b 01 00 00 sub $0x16b,%eax
400fb9: eb 05 jmp 400fc0 <phase_3+0x69>
400fbb: b8 00 00 00 00 mov $0x0,%eax // 4
400fc0: 05 6b 01 00 00 add $0x16b,%eax
400fc5: eb 05 jmp 400fcc <phase_3+0x75>
400fc7: b8 00 00 00 00 mov $0x0,%eax // 5
400fcc: 2d 6b 01 00 00 sub $0x16b,%eax
400fd1: eb 05 jmp 400fd8 <phase_3+0x81>
400fd3: b8 00 00 00 00 mov $0x0,%eax //6
400fd8: 05 6b 01 00 00 add $0x16b,%eax
400fdd: eb 05 jmp 400fe4 <phase_3+0x8d>
400fdf: b8 00 00 00 00 mov $0x0,%eax //7
400fe4: 2d 6b 01 00 00 sub $0x16b,%eax
400fe9: eb 0a jmp 400ff5 <phase_3+0x9e>
400feb: e8 d9 05 00 00 callq 4015c9 <explode_bomb>
400ff0: b8 00 00 00 00 mov $0x0,%eax
400ff5: 83 7c 24 0c 05 cmpl $0x5,0xc(%rsp) //比较num1和5
400ffa: 7f 06 jg 401002 <phase_3+0xab> //不相等就爆炸
400ffc: 3b 44 24 08 cmp 0x8(%rsp),%eax //比较%eax的值和num2
401000: 74 05 je 401007 <phase_3+0xb0>//je:若相等则跳转至结束
401002: e8 c2 05 00 00 callq 4015c9 <explode_bomb>
401007: 48 83 c4 18 add $0x18,%rsp //释放空间
40100b: c3 retq
分析汇编代码:
得知输入了两个数,推测%rcx=num2,%rdx=num1。输入数的个数必须大于1,否则bomb。如果num1>0x7,bomb。
由jmpq 0x402580(,%rax,8)可知,间接跳转,再根据后面一系列jmp,推测此次是switch语句。查找跳转表的存储地址:
在switch case出来后,计算出的%eax=num2,否则bomb。
Cmpl $0x5,0xc(%rsp) 所以num1=5,否则bomb。
将num1=5代入,得%eax=-363。
接下来,去设置断点去检测这个答案是否正确,我们kill当前程序的调试。再在explode_bomb处设置断点break explode_bomb,然后run开始运行,按照提示输入5 -363。
第三关解除。
phase_4
000000000040103f <phase_4>:
40103f: 48 83 ec 18 sub $0x18,%rsp//开辟空间
401043: 48 8d 4c 24 08 lea 0x8(%rsp),%rcx//加载地址,将0x8(%rsp)设为num2
401048: 48 8d 54 24 0c lea 0xc(%rsp),%rdx//加载地址,将0xc(%rsp)设为num1
40104d: be 21 28 40 00 mov $0x402821,%esi //scanf函数输入格式, %d %d
401052: b8 00 00 00 00 mov $0x0,%eax
401057: e8 d4 fb ff ff callq 400c30 <__isoc99_sscanf@plt>
40105c: 83 f8 02 cmp $0x2,%eax //当scanf输入数据个数不等于2时,跳转至爆炸
40105f: 75 07 jne 401068 <phase_4+0x29>
401061: 83 7c 24 0c 0e cmpl $0xe,0xc(%rsp)//jdb:无符号小于等于跳转,当num1小于等于14时,跳转至40106d,否则爆炸,所以num1的限制条件为[0,14]
401066: 76 05 jbe 40106d <phase_4+0x2e>
401068: e8 5c 05 00 00 callq 4015c9 <explode_bomb>
40106d: ba 0e 00 00 00 mov $0xe,%edx //func4参数z
401072: be 00 00 00 00 mov $0x0,%esi//func4参数y
401077: 8b 7c 24 0c mov 0xc(%rsp),%edi//func4参数x
40107b: e8 8c ff ff ff callq 40100c <func4> // 传参x=num1,y=0,z=14
401080: 83 f8 15 cmp $0x15,%eax //返回值比较
401083: 75 07 jne 40108c <phase_4+0x4d> //不等于21就爆炸
401085: 83 7c 24 08 15 cmpl $0x15,0x8(%rsp)
40108a: 74 05 je 401091 <phase_4+0x52>
40108c: e8 38 05 00 00 callq 4015c9 <explode_bomb>
401091: 48 83 c4 18 add $0x18,%rsp //释放空间
401095: c3 retq
000000000040100c <func4>:
40100c: 53 push %rbx
40100d: 89 d0 mov %edx,%eax
40100f: 29 f0 sub %esi,%eax
401011: 89 c3 mov %eax,%ebx
401013: c1 eb 1f shr $0x1f,%ebx
401016: 01 d8 add %ebx,%eax
401018: d1 f8 sar %eax
40101a: 8d 1c 30 lea (%rax,%rsi,1),%ebx
40101d: 39 fb cmp %edi,%ebx
40101f: 7e 0c jle 40102d <func4+0x21>
401021: 8d 53 ff lea -0x1(%rbx),%edx
401024: e8 e3 ff ff ff callq 40100c <func4>
401029: 01 d8 add %ebx,%eax
40102b: eb 10 jmp 40103d <func4+0x31>
40102d: 89 d8 mov %ebx,%eax
40102f: 39 fb cmp %edi,%ebx
401031: 7d 0a jge 40103d <func4+0x31>
401033: 8d 73 01 lea 0x1(%rbx),%esi
401036: e8 d1 ff ff ff callq 40100c <func4>
40103b: 01 d8 add %ebx,%eax
40103d: 5b pop %rbx
40103e: c3 retq
分析汇编代码:
我们需要输入两个数。
cmpl $0xe ,0xc(%rsp) 得知:num1小于等于14。
%edx = 14,%esi = 0 ,%edi = num1 作为参数执行func4。Func4的返回值%eax=21,否则bomb。比较num2和21,num2=21,否则bomb。
分析func4:
要使返回值t=21,传入的num1=x=6。
所以num1=6,num2=21。
接下来,去设置断点去检测这个答案是否正确,我们kill当前程序的调试。再在explode_bomb处设置断点break explode_bomb,然后run开始运行,按照提示输入6 21。
第四关解除。
phase_5
0000000000401096 <phase_5>:
401096: 48 83 ec 18 sub $0x18,%rsp //开辟空间
40109a: 48 8d 4c 24 08 lea 0x8(%rsp),%rcx//加载地址,将0x8(%rsp)设为num2
40109f: 48 8d 54 24 0c lea 0xc(%rsp),%rdx//加载地址,将0xc(%rsp)设为num1
4010a4: be 21 28 40 00 mov $0x402821,%esi //d d格式,需要输入两个数
4010a9: b8 00 00 00 00 mov $0x0,%eax //初始化%eax
4010ae: e8 7d fb ff ff callq 400c30 <__isoc99_sscanf@plt>
4010b3: 83 f8 01 cmp $0x1,%eax // 输入个数大于1,否则跳转到爆炸
4010b6: 7f 05 jg 4010bd <phase_5+0x27>
4010b8: e8 0c 05 00 00 callq 4015c9 <explode_bomb>
4010bd: 8b 44 24 0c mov 0xc(%rsp),%eax //%eax=num1(结合后面代码来看,num1是第一个取数的下标)
4010c1: 83 e0 0f and $0xf,%eax //取出来的num1的低四位与0xf作比较,若相等,则跳转到爆炸函数,否则,执行下一步,由此可见,num1的低四位不为1111。
4010c4: 89 44 24 0c mov %eax,0xc(%rsp)
4010c8: 83 f8 0f cmp $0xf,%eax
4010cb: 74 2c je 4010f9 <phase_5+0x63>
4010cd: b9 00 00 00 00 mov $0x0,%ecx // 赋值,%ecx=0,%edx=0,%edx+=1。
4010d2: ba 00 00 00 00 mov $0x0,%edx
4010d7: 83 c2 01 add $0x1,%edx //计数器+1
4010da: 48 98 cltq
4010dc: 8b 04 85 c0 25 40 00 mov 0x4025c0(,%rax,4),%eax //eax=*(4rax+0x4025c0),此处以4*eax为步长,推测0x4025c0是一个数组的首地址,rax是上次取的数组里的数
4010e3: 01 c1 add %eax,%ecx //%ecx存储累加结果
4010e5: 83 f8 0f cmp $0xf,%eax //比较15和 数组取出来的数
4010e8: 75 ed jne 4010d7 <phase_5+0x41> //不相等继续循环,所以最后取出来的数是15
4010ea: 89 44 24 0c mov %eax,0xc(%rsp)
4010ee: 83 fa 07 cmp $0x7,%edx //与7比较,不相等就爆炸。所以取七个数,最后一个数是15
4010f1: 75 06 jne 4010f9 <phase_5+0x63>
4010f3: 3b 4c 24 08 cmp 0x8(%rsp),%ecx //累计结果和num2比较,不相等就爆炸
4010f7: 74 05 je 4010fe <phase_5+0x68>
4010f9: e8 cb 04 00 00 callq 4015c9 <explode_bomb>
4010fe: 48 83 c4 18 add $0x18,%rsp
401102: c3 retq
同样需要输入两个数,输入的参数个数必须大于1个。%rdx=num1,%rcx=num2
把取出来的num1的低四位与0xf作比较,若相等,则跳转到爆炸函数,否则,执行下一步,由此可见,参数1的低四位不为1111。
赋值,%ecx=0,%edx=0,%edx+=1。
而后执行操作得到eax=(4rax+0x4025c0),此处以4eax为步长,推测0x4025c0是一个数组的首地址,接着把存储在地址*(0x4025c0+4eax)处的值与%ecx相加存储在%ecx中。
接着把%eax存储的值(应该是数组的序号)与0xf也就是15作比较,若不等于,则跳到<phase_5+65>处继续循环,否则,接着执行后续代码,由此可知,<phase_5+65>处开始应该是一个循环,每次循环取出数组中的一个值累加存储到%ecx中,最后一次取出的值应该是15,接着把%edx与7作比较,不相等则爆炸,反之,执行后续代码。
我们打印出数组连续的16位:
所以我们最后一个取到的数是15,一共取7个数。初次%rax=num1,取数组里面的A[num1],第二次取A[ %rax ],此时%rax = %eax =A[num1]。
数组序列:
取数顺序:
逆推,取七个数,也就是A[num1] = 0,num1=8。%ecx = 0+10+1+2+14+6+15=48
而num2=%ecx=48,否则爆炸。
所以num1 = 8,num2=48。
接下来,去设置断点去检测这个答案是否正确,我们kill当前程序的调试。再在explode_bomb处设置断点break explode_bomb,然后run开始运行,按照提示输入8 48。
第五关解除。
phase_6
0000000000401103 <phase_6>:
401103: 41 55 push %r13
401105: 41 54 push %r12
401107: 55 push %rbp
401108: 53 push %rbx
401109: 48 83 ec 58 sub $0x58,%rsp // 开辟空间
40110d: 48 8d 74 24 30 lea 0x30(%rsp),%rsi
401112: e8 e8 04 00 00 callq 4015ff <read_six_numbers> // 读取输入的六个数
401117: 4c 8d 6c 24 30 lea 0x30(%rsp),%r13
40111c: 41 bc 00 00 00 00 mov $0x0,%r12d
401122: 4c 89 ed mov %r13,%rbp //初始 %rbp=%r13=%rsp
401125: 41 8b 45 00 mov 0x0(%r13),%eax //%eax=num[i]
401129: 83 e8 01 sub $0x1,%eax
40112c: 83 f8 05 cmp $0x5,%eax
40112f: 76 05 jbe 401136 <phase_6+0x33>//无符号数比较,说明num为无符号数,即大于等于0,401129~40112f num[i]-1<=5,所以num[i]<=6
401131: e8 93 04 00 00 callq 4015c9 <explode_bomb>
401136: 41 83 c4 01 add $0x1,%r12d
40113a: 41 83 fc 06 cmp $0x6,%r12d
40113e: 75 07 jne 401147 <phase_6+0x44>
401140: be 00 00 00 00 mov $0x0,%esi
401145: eb 42 jmp 401189 <phase_6+0x86>
401147: 44 89 e3 mov %r12d,%ebx //401136~401147 退出大循环的条件:6个数字全部遍历到
40114a: 48 63 c3 movslq %ebx,%rax
40114d: 8b 44 84 30 mov 0x30(%rsp,%rax,4),%eax
401151: 39 45 00 cmp %eax,0x0(%rbp)
401154: 75 05 jne 40115b <phase_6+0x58>
401156: e8 6e 04 00 00 callq 4015c9 <explode_bomb>
40115b: 83 c3 01 add $0x1,%ebx
40115e: 83 fb 05 cmp $0x5,%ebx
401161: 7e e7 jle 40114a <phase_6+0x47>//小循环,判断数组元素是否相等
401163: 49 83 c5 04 add $0x4,%r13
401167: eb b9 jmp 401122 <phase_6+0x1f>//大循环,每次将%r13加4,之后回到401122,%r13赋给了%eax
这部分伪代码:两层循环,说明输入的每个数字要求不大于6,且互不相同
401169: 48 8b 52 08 mov 0x8(%rdx),%rdx
40116d: 83 c0 01 add $0x1,%eax
401170: 39 c8 cmp %ecx,%eax
401172: 75 f5 jne 401169 <phase_6+0x66>
401174: eb 05 jmp 40117b <phase_6+0x78>
401176: ba f0 42 60 00 mov $0x6042f0,%edx
40117b: 48 89 14 74 mov %rdx,(%rsp,%rsi,2)
40117f: 48 83 c6 04 add $0x4,%rsi
401183: 48 83 fe 18 cmp $0x18,%rsi
401187: 74 15 je 40119e <phase_6+0x9b>
401189: 8b 4c 34 30 mov 0x30(%rsp,%rsi,1),%ecx
40118d: 83 f9 01 cmp $0x1,%ecx
401190: 7e e4 jle 401176 <phase_6+0x73>
401192: b8 01 00 00 00 mov $0x1,%eax
401197: ba f0 42 60 00 mov $0x6042f0,%edx
40119c: eb cb jmp 401169 <phase_6+0x66>
40119e: 48 8b 1c 24 mov (%rsp),%rbx
4011a2: 48 8d 44 24 08 lea 0x8(%rsp),%rax
4011a7: 48 8d 74 24 30 lea 0x30(%rsp),%rsi
4011ac: 48 89 d9 mov %rbx,%rcx
4011af: 48 8b 10 mov (%rax),%rdx
4011b2: 48 89 51 08 mov %rdx,0x8(%rcx)
4011b6: 48 83 c0 08 add $0x8,%rax
4011ba: 48 39 f0 cmp %rsi,%rax
4011bd: 74 05 je 4011c4 <phase_6+0xc1>
这部分伪代码:
如果把链表中编号为id的节点地址叫做L[id],那么这一部分干的事情就是P[i] = L[num[i]]
4011bf: 48 89 d1 mov %rdx,%rcx
4011c2: eb eb jmp 4011af <phase_6+0xac>
4011c4: 48 c7 42 08 00 00 00 movq $0x0,0x8(%rdx)
4011cb: 00
4011cc: bd 05 00 00 00 mov $0x5,%ebp
4011d1: 48 8b 43 08 mov 0x8(%rbx),%rax
4011d5: 8b 00 mov (%rax),%eax
4011d7: 39 03 cmp %eax,(%rbx)
4011d9: 7e 05 jle 4011e0 <phase_6+0xdd>
4011db: e8 e9 03 00 00 callq 4015c9 <explode_bomb>
4011e0: 48 8b 5b 08 mov 0x8(%rbx),%rbx
4011e4: 83 ed 01 sub $0x1,%ebp
4011e7: 75 e8 jne 4011d1 <phase_6+0xce>
4011e9: 48 83 c4 58 add $0x58,%rsp
4011ed: 5b pop %rbx
4011ee: 5d pop %rbp
4011ef: 41 5c pop %r12
4011f1: 41 5d pop %r13
4011f3: c3 retq
这部分伪代码:是按照L[num[0]]->L[num[1]]->…->L[num[5]]的顺序重排链表
发现它访问了node前4字节的值,就叫它val
这一部分,所做的事情是检查是否满足P[0]->val > … > P[5]->val
分析结构体:
偏移4字节为结点编号,偏移8字节为下一个结构体地址,所以是链表。
struct node{
int value;
int number;
node* next;
}
最后打印各个结点的val值,排序
因此得出序列:1 5 6 4 3 2
接下来,去设置断点去检测这个答案是否正确,我们kill当前程序的调试。再在explode_bomb处设置断点break explode_bomb,然后run开始运行,按照提示输入1 5 6 4 3 2。
第六关解除。
secret_phase
入口寻找
在第六关汇编代码下面还有一个fun7函数以及secret_phase部分,这很明显就说明了有隐藏关卡。我们观察给出的c语言代码可以发现每个关卡函数里面都有一个函数调用 phase_defused()。
找到该函数汇编代码:
分析可知当输入两个数字和一个字符串的组合时就调用了隐藏关卡,第四关是输入两个数字的,所以我们需要在后面再输入某个字符串,隐藏关卡是在结束第六关才会出现的,所以字符的密码就隐藏在第六关,设置断点,查看需要传入的字符串,是 DrEvil,在第四关输入两个数字之后输入DrEvil,结束第六关之后,成功跳出隐藏关卡。
分析汇编代码:
输入并读取一行字符,将字符转换为长整型,并规定了长整型的大小。
调用fun7函数,并且返回值必须是5.
这是一个递归调用函数,其结构类似于二叉树,其中首次传进去的这一个地址0x 604110就是根节点地址。
查看树结点,完整二叉树:
要得到fun7的返回值为5,需要右左右。(((20+1)2)*2)+1=5
所以是47。
接下来,去设置断点去检测这个答案是否正确,我们kill当前程序的调试。再在explode_bomb处设置断点break explode_bomb,然后run开始运行,按照提示输入47。
隐藏关解除。