BombLab for 5.20
今天是神奇的5月20号,风和日丽万里无云。
我本不想掀起一场腥风血雨,但是为了免受几万点暴击伤害我选择了与bomblab作伴。
bomblab原是CMU计算机组成原理(#15213)课程的实验,任务是拆掉7个二进制炸弹(phase1~6,secret_phase)
由于当了半个学期咸鱼干,汇编什么的基本不记得了,投机一下用了神器ida。
bomblab由两部分组成,一部分用于记录成绩,一部分是炸弹。
附带的bomb.c如下
#include
#include
#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 , the bomb reads from
* until EOF, and then switches to standard input. Thus, as you
* defuse each phase, you can add its defusing string to 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 []\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;
}
grade
在phase1()
执行之前会调用initialize_bomb()
,.data
节中有host_table
aGreatwhite_ics [] = "greatwhite.ics.cs.cmu.edu";
aAngelshark_ics [] = "angelshark.ics.cs.cmu.edu";
aMakoshark_ics [] = "makoshark.ics.cs.cmu.edu";
然后在submitr
中看到有:
GET /%s/submitr.pl/?userid=%s&lab=%s&result=%s&submit=submit
,由此推测扫雷过程被炸次数等指标会被记录并上传到服务器,万幸的是这明显就不是我们自己学校的评分服务器,所以就别管了,反正对应的网址打不开,有无数条命就对了。
bomb
phase_1()
foxwest@My-Mint-OS ~/Documents/Course $ ./bomb
Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
一堆提示之后进入phase1_()
signed int (_BYTE *a1)
{
signed int result;
result = strings_not_equal(a1, "He is evil and fits easily into most overhead storage bins.");
if ( result )
explode_bomb();
return result;
}
答案很明显就是He is evil and fits easily into most overhead storage bins.
phase_2()
int __cdecl phase_2(int a1)
{
int result;
signed int v2;
int v3;
int v4[9];
read_six_numbers(a1, (int)v4);
if ( v4[0] < 0 )
explode_bomb();
v2 = 1;
do
{
result = *(&v3 + v2) + v2;
if ( v4[v2] != result )
explode_bomb();
++v2;
}
while ( v2 != 6 );
return result;
}
2333ida出来的变量名就是那么风骚,懒得改了。抽出重点:
#include
using namespace std;
int main()
{
// int a=0;
int result=0;
for (int i = 0; i < 6; ++i)
{
/* code */
result=result+i;
cout<
跑一下就是答案了0 1 3 6 10 15
phase_3()
int __cdecl phase_3(int a1)
{
signed int v1; // eax@4
int v2; // eax@6
int v3; // eax@8
int v4; // eax@10
int v5; // eax@12
int v6; // eax@14
int v7; // eax@16
int result; // eax@18
int v9; // [sp+8h] [bp-14h]@1
int v10; // [sp+Ch] [bp-10h]@1
if ( __isoc99_sscanf(a1, "%d %d", &v10, &v9) <= 1 )
explode_bomb();
switch ( v10 )
{
case 0:
v1 = 298;
goto LABEL_6;
case 1:
v1 = 0;
LABEL_6:
v2 = v1 - 961;
goto LABEL_8;
case 2:
v2 = 0;
LABEL_8:
v3 = v2 + 236;
goto LABEL_10;
case 3:
v3 = 0;
LABEL_10:
v4 = v3 - 351;
goto LABEL_12;
case 4:
v4 = 0;
LABEL_12:
v5 = v4 + 351;
goto LABEL_14;
case 5:
v5 = 0;
LABEL_14:
v6 = v5 - 351;
goto LABEL_16;
case 6:
v6 = 0;
LABEL_16:
v7 = v6 + 351;
break;
case 7:
v7 = 0;
break;
default:
explode_bomb();
return result;
}
result = v7 - 351;
if ( v10 > 5 || result != v9 )
explode_bomb();
return result;
}
一堆跳转,只要最后v10<6&&result==v9
即可,那就选case 3:
好了
得到答案3 -351
phase_4()
里有个func4()
就是个不知道干什么的递归,扫了一下第二个输入参数明显是37
,懒得想了,整理成如下代码直接跑:
#include
using namespace std;
int func4(int a1, int a2, int a3)
{
int v3; // eax@1
int v4; // ebx@1
int result; // eax@2
v3 = (a3 - a2) / 2;
v4 = v3 + a2;
if ( v3 + a2 <= a1 )
{
result = v3 + a2;
if ( v4 < a1 )
result = v4 + func4(a1, v4 + 1, a3);
}
else
{
result = v4 + func4(a1, a2, v4 - 1);
}
return result;
}
int main()
{
int v3;
for (v3 = 0; v3 < 15; ++v3)
{
if(func4(v3,0,14)==37)
{
cout<
phase_5()
发现有个数组array_2850()
,输入两个数对数组运算,稍加整理穷举一下:
#include
using namespace std;
int main()
{
int result;
int v2;
int v3;
int v5;
int array[]={0xa,2,0xe,7,8,0xc,0xf,0xb,0,4,1,0xd,3,9,6,5};
for (v5 = 0; v5 < 16; ++v5)
{
v2=0;
v3=0;
result = v5 & 0xF;
if(result==15)
continue;
v5 = result;
do
{
++v3;
result = array[result];
v2 += result;
}
while ( result != 15 );
if(v3==15)
{
cout<
运行得答案5 115
phase_6()
~@!$#$^此处一堆粗话,怎么那么多循环,一开始就发现有node1~6
的东西:
可以看出是结点结构如下的链表
typedef struct node
{
int data;
int number;
node *next;
}node;
node1->data=0x76
node2->data=0x35f
node3->data=0x24e
node4->data=0x39
node5->data=0xfa
node6->data=0x19d
这里搞了半天...那些循环就是对结点降序排列得到2 3 6 5 1 4
,注意到有个和7的异或操作,得到输入应该为5 4 1 2 6 3
secret_phase()
发现函数phase_defused()
中有:
if ( num_input_strings == 6 )
{
if ( __isoc99_sscanf(&unk_804B5F0, "%d %d %s", &v1, &v0, &v2) == 3 && !strings_not_equal(&v2, "DrEvil") )
{
puts("Curses, you've found the secret phase!");
puts("But finding it and solving it are quite different...");
secret_phase();
}
puts("Congratulations! You've defused the bomb!");
}
要求输入三个东西,格式为%d %d %s
且显然%s
为DrEvil
,绞尽脑汁想了想前两个输入,n次尝试失败,发现每一个phase_*()
之后都之后都会接上phase_defused()
,就索性全部数字输入后都加上DrEvil
。
但是到最后也没弄明白为什么前面有readline()
之后有scanf()
的情况下两个后面的函数仍然能正常工作。(此处做个小标记)
然后就无敌了...
int secret_phase()
{
const char *v0; // eax@1
int v1; // ecx@1
__int32 v2; // ebx@1
int v3; // ecx@3
v0 = (const char *)read_line();
v2 = strtol(v0, 0, 10);
if ( (unsigned int)(v2 - 1) > 0x3E8 )
explode_bomb(v1);
if ( fun7((int)&n1, v2) != 2 )
explode_bomb(v3);
puts("Wow! You've defused the secret stage!");
return phase_defused();
}
int fun7(int a1, int a2)
{
int result; // eax@3
if ( a1 )
{
if ( *(_DWORD *)a1 <= a2 )
{
result = 0;
if ( *(_DWORD *)a1 != a2 )
result = 2 * fun7(*(_DWORD *)(a1 + 8), a2) + 1;
//right
}
else
{
result = 2 * fun7(*(_DWORD *)(a1 + 4), a2);
//left
}
}
else
{
result = -1;
}
return result;
}
发现其中的n1
是一颗存为链表的树,fun7()
是对树中结点的简单运算,把n1
存到堆里,根据要求第二个输入参数应该满足v2<1001
,所以又修改fun7()
整理成如下代码
#include
#include
using namespace std;
int tree[]={15,0x24,0x8,0x32,0x16,0x16,0x2d,0x6b,0x1,0x7,0x14,0x23,0x28,0x2f,0x63,0xe9};
int fun7(int tree[], int a2,int i)
{
int result; // eax@3
if ( tree[i] )
{
if (tree[i] <= a2 )
{
result = 0;
if (tree[i] != a2 )
result = 2 * fun7(tree, a2,i*2+1) + 1;
//right
}
else
{
result = 2 * fun7(tree, a2,i*2);
//left
}
}
else
{
result = -1;
}
return result;
}
int main()
{
int a2;
for (a2 = 0; a2 < 1001; ++a2)
{
if (fun7(tree,a2,1)==2)
{
cout<<"result:"<
第二个输入从0到1001穷举,得到满足条件的结果有
result:20
result:22
均满足条件