BombLab for 5.20

2017-05-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且显然%sDrEvil,绞尽脑汁想了想前两个输入,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

均满足条件