ICS lab2: Defusing a Binary Bomb
lab2的要求
lab2的要求是拆除“炸弹”,也就是通过objdump 和 gdb 或者其他工具对可执行文件 bomb 进行分析,从而给出“炸弹”不会引爆的输入来“拆除炸弹”。(就是通过gdb等工具对程序进行分析从而给出不会发生爆炸的输入)
工具准备:GDB,objdump
GDB:Gnu DeBugger
GDB的作用:
- 开始和中止程序
- 在特定条件或者指定地址暂停程序
- 测试发生了什么,查看指定地址或寄存器中的值
- 改变程序中的东西(寄存器中的值等)来测试程序
命令:
- gdb <filename> 开始用gdb调试程序
- break FUNC | *ADDR 在FUNC或ADDR地址处设置断点
- run 运行程序
- print</?> $REG | ADDR 打印寄存器的值或者指定地址的值
- continue | stepi | nexti 继续运行到下一个断点或者程序结束|下一步(不进入函数)| 下一步(进入程序)
- quit 退出调试
objdump:OBJect-file DUMP
objdump用来显示来自对象文件的信息
命令:
objdump –d | -D <object-file> > <destination-file>
举例:
objdump可以将二进制文件转换成以下形式
工具准备好了,就可以开始做lab啦!
开始做lab
得到 lab 文件之后,打开文件夹发现文件夹内有三个文件:
- README:判断bomb是不是你的
- bomb:可执行的bomb二进制文件
- bomb.c:源文件与炸弹的主程序(但是没有关键代码T^T)
第一步阅读README文档,检验这个bomb真的是自己的。
第二步阅读bomb.c(如下,可跳过(但是写的很有意思))
/***************************************************************************
* Dr. Evil's Insidious Bomb, Version 1.0
* Copyright 2002, Dr. Evil Incorporated. All rights reserved.
*
* LICENSE:
*
* Dr. Evil Incorporated (the PERPETRATOR) hereby grants you (the
* VICTIM) explicit permission to use this bomb (the BOMB). This is a
* time limited license, which expires on the death of the VICTIM.
* The PERPETRATOR takes no responsibility for damage, frustration,
* insanity, bug-eyes, carpal-tunnel syndrome, loss of sleep, or other
* harm to the VICTIM. Unless the PERPETRATOR wants to take credit,
* that is. The VICTIM may not distribute this bomb source code to
* any enemies of the PERPETRATOR. No VICTIM may debug,
* reverse-engineer, run "strings" on, decompile, decrypt, or use any
* other technique to gain knowledge of and defuse the BOMB. BOMB
* proof clothing may not be worn when handling this program. The
* PERPETRATOR will not apologize for the PERPETRATOR's poor sense of
* humor. This license is null and void where the BOMB is prohibited
* by law.
***************************************************************************/
#include <stdio.h>
#include "support.h"
#include "phases.h"
/*
* Note to self: Remember to erase this file so my victims will have no
* idea what is going on, and so they will all blow up in a
* spectaculary fiendish explosion. -- Dr. Evil
*/
FILE *infile;
int main(int argc, char *argv[])
{
char *input;
/* Note to self: remember to port this bomb to Windows and put a
* fantastic GUI on it. */
/* When run with no arguments, the bomb reads its input lines
* from standard input. */
if (argc == 1) {
infile = stdin;
}
/* When run with one argument <file>, the bomb reads from <file>
* until EOF, and then switches to standard input. Thus, as you
* defuse each phase, you can add its defusing string to <file> and
* avoid having to retype it. */
else if (argc == 2) {
if (!(infile = fopen(argv[1], "r"))) {
printf("%s: Error: Couldn't open %s\n", argv[0], argv[1]);
exit(8);
}
}
/* You can't call the bomb with more than 1 command line argument. */
else {
printf("Usage: %s [<input_file>]\n", argv[0]);
exit(8);
}
/* Do all sorts of secret stuff that makes the bomb harder to defuse. */
initialize_bomb();
printf("Welcome to my fiendish little bomb. You have 6 phases with\n");
printf("which to blow yourself up. Have a nice day!\n");
/* Hmm... Six phases must be more secure than one phase! */
input = read_line(); /* Get input */
phase_1(input); /* Run the phase */
phase_defused(); /* Drat! They figured it out!
* Let me know how they did it. */
printf("Phase 1 defused. How about the next one?\n");
/* The second phase is harder. No one will ever figure out
* how to defuse this... */
input = read_line();
phase_2(input);
phase_defused();
printf("That's number 2. Keep going!\n");
/* I guess this is too easy so far. Some more complex code will
* confuse people. */
input = read_line();
phase_3(input);
phase_defused();
printf("Halfway there!\n");
/* Oh yeah? Well, how good is your math? Try on this saucy problem! */
input = read_line();
phase_4(input);
phase_defused();
printf("So you got that one. Try this one.\n");
/* Round and 'round in memory we go, where we stop, the bomb blows! */
input = read_line();
phase_5(input);
phase_defused();
printf("Good work! On to the next...\n");
/* This phase will never be used, since no one will get past the
* earlier ones. But just in case, make this one extra hard. */
input = read_line();
phase_6(input);
phase_defused();
/* Wow, they got it! But isn't something... missing? Perhaps
* something they overlooked? Mua ha ha ha ha! */
return 0;
}
看完bomb.c我们对bomb有了初步的了解,就是:读入一个文件,如果没有文件,就读入输入,然后根据输入一步一步地运行phase,如果正确,返回正确的信息并向服务器发送正确报告,如果错误返回错误信息并向服务器发送错误报告。
接下来通过
objdump -d bomb > asm
把二进制可执行文件bomb反汇编到asm里(我随便取的名字),这样我们就会得到一个包含汇编指令的文档。接下来就可以求解phase啦!
求解phase
运行bomb之前
在运行bomb之前一定要先设置断点,不然gdb就认为你直接run这个程序,那,,就炸了(在发现忘记输断点就run之后,不要万念俱焚,断网!这样服务器端检测不到错误,本地会因为网络连接失败而返回错误信息,重新开始,这次不要再忘记设断点了)
首先在main函数处设置断点,然后run检查main的地址和asm文件里地址是否一致,不一致的话后面asm文件里的地址都要保持一样的差值就好了。
break main
为了使程序在爆炸前及时停止而不是向服务器发送错误信息(会扣分),我们采取在错误信息函数之前加断点的方式(嘻嘻)(引爆炸弹函数名可能会不同)。
break explode_bomb
这样我们就可以放心大胆地开始我们的 defuse bomb 之旅啦!
phase1
首先
break phase_1
break phase_2
然后跑到asm文件里面看到phase1是这个样子的:
0000000000001350 <phase_1>:
1350: 48 83 ec 08 sub $0x8,%rsp
1354: 48 8d 35 95 10 00 00 lea 0x1095(%rip),%rsi # 23f0 <_IO_stdin_used+0x150>
135b: e8 90 04 00 00 callq 17f0 <strings_not_equal>
1360: 85 c0 test %eax,%eax
1362: 75 05 jne 1369 <phase_1+0x19>
1364: 48 83 c4 08 add $0x8,%rsp
1368: c3 retq
1369: e8 92 0c 00 00 callq 2000 <explode_bomb>
136e: eb f4 jmp 1364 <phase_1+0x14>
就是简单的比较了一下输入的字符串(作为函数的参数通过%rdi传入函数)和标准字符串(0x1095(%rip))如果equal就跳出,如果not equal就爆炸。
所以只需要输出标准字符串就可以了
可以使用以下指令输出标准字符串(当然也可以一个一个char的蹦(嘻))
print (char *)($<寄存器> + <对应立即数>)
得到结果如下:
所以 Why make trillions when we could make… billions? 就是我 phase1的答案啦!kill掉验证一下,对啦!开始做phase_2。
phase2
照例break phase_3
然后跑到asm里看到phase2是这个样子的:
0000000000001370 <phase_2>:
1370: 55 push %rbp
1371: 53 push %rbx
1372: 48 83 ec 28 sub $0x28,%rsp
1376: 48 89 e5 mov %rsp,%rbp
1379: 48 89 e6 mov %rsp,%rsi
137c: e8 bb 0c 00 00 callq 203c <read_six_numbers>
1381: 48 89 e3 mov %rsp,%rbx
1384: 48 83 c5 14 add $0x14,%rbp
1388: eb 09 jmp 1393 <phase_2+0x23>
138a: 48 83 c3 04 add $0x4,%rbx
138e: 48 39 eb cmp %rbp,%rbx
1391: 74 11 je 13a4 <phase_2+0x34>
1393: 8b 03 mov (%rbx),%eax
1395: 83 c0 05 add $0x5,%eax
1398: 39 43 04 cmp %eax,0x4(%rbx)
139b: 74 ed je 138a <phase_2+0x1a>
139d: e8 5e 0c 00 00 callq 2000 <explode_bomb>
13a2: eb e6 jmp 138a <phase_2+0x1a>
13a4: 48 83 c4 28 add $0x28,%rsp
13a8: 5b pop %rbx
13a9: 5d pop %rbp
13aa: c3 retq
可以看到phase调用了另外一个函数read_six_numbers,所以找来这个函数看一看:
000000000000203c <read_six_numbers>:
203c: 48 83 ec 08 sub $0x8,%rsp
2040: 48 89 f2 mov %rsi,%rdx
2043: 48 8d 4e 04 lea 0x4(%rsi),%rcx
2047: 48 8d 46 14 lea 0x14(%rsi),%rax
204b: 50 push %rax
204c: 48 8d 46 10 lea 0x10(%rsi),%rax
2050: 50 push %rax
2051: 4c 8d 4e 0c lea 0xc(%rsi),%r9
2055: 4c 8d 46 08 lea 0x8(%rsi),%r8
2059: 48 8d 35 47 07 00 00 lea 0x747(%rip),%rsi # 27a7 <array.3089+0x327>
2060: b8 00 00 00 00 mov $0x0,%eax
2065: e8 96 ef ff ff callq 1000 <__isoc99_sscanf@plt>
206a