Bomb 3
Bomb 3相关字符串的获取也是需要通过对于里面重要的phase_3(input)
进行反汇编才能分析出来的。
下面代码段是phase_3函数的反汇编的前几行,可以分析得到,字符串的输入格式为%d %d
,且输入个数大于等于1。
%d %d
格式的获取需要通过p/s (char*)0x4025cf
命令读取相应内存地址的内容获取得到的。
输入大于1个数字的信息的是通过400f60
处的cmp $0x1,%eax
代码来判断。%eax是sscanf函数的返回值,代表读入的个数。
0000000000400f43 <phase_3>:
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
下面代码块紧接着上面这段。可以在40f6a
附近看到程序在判断第一个输入数字是否大于7,如果大于7则会跳转到400fad
处触发炸弹。所以得到第一个输入数字<=7。
通过观察400f75
到400fb9
的代码可以看到,全部是执行num赋值给%eax
,再跳转到400fbe
处,相当于一个switch函数。在400fbe
会比较第二个输入数字与%eax的值是否相等,不相等则爆炸。
400f6a: 83 7c 24 08 07 cmpl $0x7,0x8(%rsp)
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) //存储目的地址的内存地址0x402470+8*i (i<=7)
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
根据上面的分析我们得到400f75:
处跳转的结果决定执行哪个case,决定%eax的值。而第一个数字决定了400f75:
处代码跳转到哪里。所以第一数字的取值会直接影响到第二个数字的值。
根据对于400f75:
出代码中0x402470+8*第一参数
打印内存信息,可以看到相应的跳转地址,已经对应的eax值应该是多少,也就是第二数字的应该的取值。打印内存信息的命令行为x/x (int *)内存地址
根据分析可以得到以下对应输入均为正确答案。
0 207 ===》x/x (int *)0x402470 打印得到地址400f7c
1 311 ===》x/x (int *)0x402478 打印得到地址400fb9
2 707 ===》x/x (int *)0x402480 打印得到地址400f83
3 256 ===》x/x (int *)0x402488 打印得到地址400f8a
4 389 ===》x/x (int *)0x402490 打印得到地址400f91
5 206 ===》x/x (int *)0x402498 打印得到地址400f98
6 682 ===》x/x (int *)0x4024a0 打印得到地址400f9f
7 327 ===》x/x (int *)0x4024a8 打印得到地址400fa6
Bomb 4
Bomb 4的开头代码和Bomb3 相类似。首先和Bomb 3类似读取$0x4025cf
内存地址中存储的字符串可以得到输入格式为%d %d
。
根据401029: cmp $0x2,%eax
代码及下面一行可以指导需要输入2个数字。
根据40102e
处代码,可以分析得到第一个数字<=14。如果
40100c: 48 83 ec 18 sub $0x18,%rsp
401010: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx
401015: 48 8d 54 24 08 lea 0x8(%rsp),%rdx
40101a: be cf 25 40 00 mov $0x4025cf,%esi //==>相关输入格式%d %d
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> //==> 第一个参数《=14
401035: e8 00 04 00 00 callq 40143a <explode_bomb>
接下去的代码分别是为调用<fun4>
函数做准备,传参数。
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 400fce <func4>
先跳过<fun4>
,分析返回之后怎么执行,相关代码见下面代码块。用test
命令来测试返回值(test的效果是两个操作数做&操作;jne指令测试ZF位是否为0)。
根据代码可以知道,返回值为0时,炸弹才不会爆炸。而且第二个输入数字可以通过这行代码401051、401056
行代码确定,应该为0.
40104d: 85 c0 test %eax,%eax
40104f: 75 07 jne 401058 <phase_4+0x4c> //~zf
401051: 83 7c 24 0c 00 cmpl $0x0,0xc(%rsp)
401056: 74 05 je 40105d <phase_4+0x51> //zf
401058: e8 dd 03 00 00 callq 40143a <explode_bomb>
40105d: 48 83 c4 18 add $0x18,%rsp
401061: c3 retq
现在分析<fun4>
函数。根据代码中注释,我们可以还原出递归函数的原始模样
0000000000400fce <func4>:
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 //eax = ((edx-esi)>>5+(edx-esi))>>1 = (edx-esi)/2
400fdf: 8d 0c 30 lea (%rax,%rsi,1),%ecx //ecx = 0.5*(edx-esi)+rsi
400fe2: 39 f9 cmp %edi,%ecx //第一个输入数字与%ecx比较
400fe4: 7e 0c jle 400ff2 <func4+0x24>
400fe6: 8d 51 ff lea -0x1(%rcx),%edx //如果输入第一个参数小于ecx,递归调用edx = rcx -1
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 //eax = 2 * rax + 1
401007: 48 83 c4 08 add $0x8,%rsp
40100b: c3 retq
========================================================================
还原得到的fun4
int fun4(int a , int b,int c){
int ret = a/2;
int tmp = a/2 + b;
if(c < tmp){
ret = 2* fun4(a-1,b,c);
return ret;
}
ret = 0;
if(c > 0){
return 2*fun4(a,b+1,c)+1;
}
return ret;
}
递归函数fun4的C语言版本如上代码块。其中初始调用a=14 , b = 0 , c = 输入的第一个数字。
需要递归调用结果为0,c一定不能大于0,c大于0 返回值至少为1(第二个判断处)。递归结束时c >= tmp是成立的,而tmp递归到最后一定0(因为之前推测c<=0)。所以得到如下0<=c<=0表达式。从这里可以推测得到第一个输入数字参数为0;
所以这个炸弹的密码为
0 0