CS:APP bomblab 记录

任务就是把bomb这个可执行文件里的炸弹拆掉,他有6组数据,你需要输入6次来拆除这个炸弹。

 

简化流程

你可以新建一个文件叫psol.txt,然后通过下面的执行命令

./bomb psol.txt

即可把psol.txt里的数字自动输入到bomb里。理论上直接重定向输入流<也是可以的吧。

 

Hint

1.学会用gdb。

2.objdump -t可以打印可执行文件的符号表。

3.objdump -d反编译可执行文件,列出所有指令。

4.strings 显示可打印的字符串。

 

Phase_1 

首先我们来反编译一下看看汇编。

我们可以观察可以到main部分有相似的函数调用。

 

这里看到有里那个个4个call,phase_1,phase_defused,phase_2,phase_defused。所以我们可以猜到每颗炸弹都是传参到一个函数去处理的,我们来看我们的第一颗炸弹处理函数phase_1。

前面四句基本上是函数调用都会有的指令了,就是分配栈帧,第5句我没理解有什么用。。。然后再后面三句就是调用string_not_equal函数,光看名字我都知道这个就是判字符串是否不等了。。。后面是判定返回值,zf=0则调用explode_bomb,否则就返回了。。。看到这里基本上就可以确定这题了,只要查看0x80497c0的内存是什么我们就能知道答案了。。下面来gdb调试一下。

首先我们打断点,然后单步执行到调用strings_not_equal前,然后把调用strings_not_equal的两个参数打出来,一个是xxx(就是我刚才随便输的一串),另一个是Public speaking is very easy.(这个就是答案).为什么我要把两个都打出来呢?因为我调用这个phase_1传过来的参数不是我原始的输入参数,可能main里面做了手脚,结果打印出来是没有的,那么这道题就解决了。

 

phase_2

同样先看看phase_2函数的指令。

前面几句不用分析了吧。后面我们先看第一句关键指令call read_six_numbers,从这个名字我们可以看出他调用了一个函数,这个函数可以读出6个数字,再看前面两个push,这个函数两个参数一个edx,一个是eax,eax是前面一条指令赋值过来的,是ebp-0x18,这个是phase_2分配的一个局部变量,edx是ebp+8,这个参数是phase_2的参数,也就是我们的输入。我们打印一下这两个数据。

然后我们再看看read_six_number。

可以看到调用了一个sscanf,然后这个sscanf压了8个参数,sscanf的第一个参数是ebp+8,也就是传过来的第一个参数也是我们的输入,第二个参数在0x8049b1b,我们可以猜到这个参数是"%d %d %d %d %d %d",等会会打印一下,然后剩下6个参数都是基于edx算的地址,edx是ebp+c,这是传过来的第二个参数,那么基本已经明确了,传过来一个是我们的输入一个是转换成6个整数后的数组,这里我们可以看到他还判定了一下sscanf的返回值是不是<5,也就是不满足赋值6个数就会bomb,我们可以打印一下0x8049b1b。

好,我们再返回看phase_2。我们可以看到他对第一个数字作了判断是否=1,不等于就bomb,那么我们可以确定输入的第一个数字是1,后面的指令解说太复杂了,经过我化简成c++,代码大概如下。

for(int ebx = 1; ebx<=5; ++ebx){
	int eax = ebx + 1;
	eax = eax * arr[ebx - 1];
	if (arr[ebx] != eax)
		explode_bomb();
}

有了这个就很好推输入了,答案就是1 2 6 24 120 720。

 

phase_3

同样看一下汇编代码。这个稍微长点,我们分步解析。

首先是一个sscanf,函数,看参数数量可以知道是将输入串转为3个参数,我们打印一下在地址0x80497de的格式串。

可以看到是输入是三个参数。

后面这一串是先判定sscanf返回值是否>2,不>2证明赋值不够3个那么就bomb,再后面判定第一个参数要小于7,再后面就到了无条件jmp语句了。。。这里应该是个switch语句,但是我没有时间去把switch语句全解析出来了,过于复杂,直接第一个参数往1套,然后最终先到达的第一个分支点是这里。

ebp-4这个位置是我们的第三个参数,如果不等于0xd6就会bomb,那么我们的第三个参数就出来了,然后又跳到下一个分支点。

这里如果第二个参数不等于bl寄存器的值那么就bomb,我们来看看bl寄存器的值。

那么答案就全部出来了,就是1 b 214。

 

phase_4

同样,来看看phase_4函数。

惯例的sscanf,这次只有三个参数,盲猜0x8049808是"%d",查看一下。

确定了,输入就一个整数,然后后面熟悉的判定sscanf返回值,不说了。

再后面的其实也很好理解说实话,毕竟前面都搞了三个了,这个有前三个的基础直接看都能看懂了,我来给大家转换成c语言吧,大致就是这样。

if(x <= 0)
    explode_bomb();
int eax = func4(x);
if(eax  != 0x37)
    explode_bomb();

int func4(int x){
    if(x <= 1)
        return 1;
    int esi = func4(x - 1);
    int eax = func4(x - 2);
    eax += esi;
    return eax;
}

看不懂的小伙伴根据我的c代码来对照一下吧,语句也不复杂的,转换成这样之后就很好办了,很显然func4是个用递归实现的斐波那契数列,然后需要输入的是斐波那契在第几层==0x37,那么答案就出来了,就是9。

 

phase_5

同样看一下汇编代码。

做了前面这么多,这一段不用我多说了吧,就是判一下输入是不是6位的字符串,不是就bomb。

再后面我们看到有几个常量,首先我们需要知道他是什么,下面打印一下。

第一个:

第二个:

接下来就好办了,又到了我最会的转换成c语言环节,上面这一大段用c语言表示就是这样。

char *input;//我们的输入
char *ebx = input;
int eax = 6;
char ecx[8];
char *esi = "isrveawhobpnutfg\260\001"
for(int edx = 0; edx <= 5; ++edx){
    char al = ebx[edx]
    al &= 0xf;
    eax = al;
    al = esi[eax]
    ecx[edx] = al;
}
ecx[6] = '\0';
if(strings_not_equal(ecx, "giants"))
    explode_bomb();

然后根据这一段代码反推这个字符串中的每个字符的末尾四个bit是15 0 5 11 13 1。

然后我通过将这些数字+32后对照ascill码表得到其中一组答案:/ %+-!。

 

phase_6

这题有毒啊,跟前面完全不是一个难度,我下班抽空搞搞了几天才弄出来。直接给翻译后的代码和注释,还有我画的内存图,然后对着看吧,不想过多解释了。。。。。

首先是内存图。。。。画得有点丑,大家能看懂就行,我把里面定义的变量自己画了出来用了别名来代替,不然完全没法看懂。

然后是转换后的c代码。read_six_number那里我就不写了吧。


struct Node{
    int x, y;
    Node *next;
};
int a[6];
Node *b = 0x804b26c;
int c;
int edi;
for(edi = 0; edi <= 5; ++edi){
    int eax = a[edi];
    eax--
    if(eax > 5)
        explode_bome();
    for(int ebx = edi + 1; ebx <= 5; ++ebx){
        int *esi = a;
        if (esi[edi] == esi[ebx])
            explode_bome();
    }
}
/*
 * 上面是判断输入的数两两之间不相等。
 */
edi = 0;
Node *d[6];
int *ecx = a;
Node **e = d;
for(edi = 0; edi <= 5; ++edi)
    Node *esi = b;
    int ebx = 1;
    for(int ebx = 1; ebx < ecx[edi]; ++ebx)
        esi = esi->next;
    Node **edx = e;
    edx[edi] = esi;
}
/*
 * 简单说明一下,b是链表的头节点(包含数据的,并非无数据的头)
 * 举例当edi为1时,a[edi]=2时,会把链表的第二个节点放入d[1]中
 */
Node *esi = d[0]
b = esi;
Node **edx = d;
for(edi = 1; edi <= 5; ++edi){
    Node *eax = edx[edi]
    esi->next = eax;
    esi = eax;
}
esi->next = NULL;
//这个好理解了吧,就是把数组d给串起来,构造链表
esi = b;
for(int edi = 0; edi <= 4; ++edi){
    Node *edx = esi->next;
    if(esi->x < edx->x)
        explode_bomb();
    esi = esi->next;
}
//这个也很好理解,新的链表必须前面比后面大,也就是降序

这样一看就清晰了吧,结果就是把链表重新降序排序一遍,我们输入的是他原来的index。最后再把链表打出来看看。

显然,答案就是4 2 6 3 1 5。最后全部通过。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值