0%

Boomlab

Boomlab

(感觉这个实验也可以直接丢进IDA当逆向做,相对简单;本次以汇编语言下手;所用工具为VS2022社区版)

先在Linux里用如下命令把文件反汇编出来。

1
objdump -d bomb > bomb.asm

查找到main函数方式多样,此处直接搜索main即可找到位置。

image-20220228164722822

大致浏览一遍main函数,有几个phase,就是需要拆除的炸弹了。

本文着重描述过程,运算只涉及最后的结果。

本文着重描述过程,运算只涉及最后的结果。

本文着重描述过程,运算只涉及最后的结果。(水平有限)

Phase_1

根据函数对应的地址跳转

image-20220228165317069

可以看到该函数中,先将一段地址赋给esi,然后调用strings_not_equal;然后测试eax值,为0就跳过了引爆炸弹的一步。由此可见关键在于strings_not_equal这个函数。

image-20220228170012668

可以看到还是比较长的,不过容易读懂。首先是传参,将rbx,rbp作为string_length的参数,然后调用。可以根据最开始的两个mov猜测,rdi,rsi就是string_not_equal的两个参数。

再看本函数结尾,会有一个给eax赋值的操作,然后返回。这时候就需要知道edx被赋了什么值。

浏览代码可以看出大致逻辑(也可以根据函数名直接猜)两参数的字符串相等则返回0,不相等则返回1

查看esi里的地址所对应的字符串如下:(用gdb调起来才能看到,不熟悉gdb也可以拖进IDA,看汇编还是伪代码就自己选择了)

image-20220228174801745

“Border relations with Canada have never been better.”

Phase_2

首先是代码

image-20220228171547222

可以看到调用read_six_numbers函数

image-20220228172152067

代码大致逻辑为读取6个数,没有六个就引爆炸弹。

接着看phase_2代码,将rsp的值与1作比较,不相等就爆;然后将rsp+0x4和rsp+0x18的值给了rbx,rbp然后是把rbx-0x4地址处的内容给了eax,然后eax+eax,再做比较,相等就不爆;再继续,将rbx值加4字节(一个int的大小),到了下一个数,再进行比较。依照逻辑,输入的六个数分别为:1 2 4 8 16 32.即可拆弹

Phase_3

代码

image-20220228173849867

先查看一下0x4025cf处是啥,结合下面的输入函数,这是两个输入的参数。这里可以假设一下rcx和rdx就是这两个参数。

image-20220228181405909

将0给eax,然后调用参数后将eax的值与1作比较,大于就不爆。然后将rsp+0x8的值,也就是rdx的值与7作比较,大于就跳转,小于就爆,回到上一步向下看,会将rdx的值给eax,然后跳到402470这个地址

image-20220228182641784

不知道这是个啥

image-20220228182734910

用IDA发现是到了switch语句。(其实是汇编语言的switch实现)

看后面的代码,都会跳到400fbe的位置,将eax的值与rsp+0xc的值作比较,相等则不爆。

这题答案不固定,根据第一个数不同,第二个数也会不同随机选取一组符合条件的即可。

Phase_4

代码

image-20220228183854762

前面几行跟上一个阶段一样,就是输入两个值作为参数。

再将eax的值与2作比较,不相等就爆。然后将0xe和rsp+0x8的值作比较,大于0xe就不跳,故此处需小于。

然后是三个赋值操作,再接着调用func4函数,然后验证eax的值,不为0就爆,接着rsp+0xc的值等于0,炸弹拆除。

func4:

image-20220228200230163

由上面的分析可知,此处要让eax的值为0。这玩意儿还挺复杂,自己调用自己,递归了。

大致逻辑:上面phase_4的edx,esi,edi作为func4的三个参数。将edx的值给eax,用eax的值减esi,然后保存在ecx,再将ecx的值逻辑右移31位(0x1f),再将eax的值与ecx的值相加,保存在eax,然后算数右移1位(需要注意与逻辑右移的区别。)然后是ecx=rax+rsi*1,然后是ecx和edi比较,ecx较大就跳。400ff2处,edi的值小于ecx则跳转并结束该函数调用,否则将rcx+0x1的值给esi。从400fe6继续看,将rcx-0x1的值给edx,然后调用func4,然后eax+eax,然后结束调用。

上代码,帮助理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
int fun(int a1, int a2, int a3){
int b = (a1 - a2) >> 31;
int result = ((a1-a2) + b) >> 1;
b = result + a2;
if(b == x) return 0;
if(b < x) {
result = fun(a1, b + 1, x);
return result * 2 + 1;
}else{
result = fun(b - 1, a2, x);
return result * 2;
}
}

此处a1,a2,a3分别为0xe,0x0,rsp+0x8(输入的第一个值)

最后,什么时候返回0。我们将值代入发现,7是能返回0的一个界限,那么7,0可以作为一种答案。

看大佬们的解法才恍然大悟。

1
2
3
4
5
6
7
8
9
int main(void){
for(int i = 0; i <= 0xe; i++){
if(fun(0xe,0,i) == 0){
printf("%d\n",i) ;
return 0;
}
}
return 0;
}

允许的答案由0 1 3 7,只需要简单调用一下函数就行,没想到。

Phase_5

image-20220228210929709

这1-7行刚开始是没看懂,后来看伪代码是v4 = __readfsqword(0x28u),这玩意儿。

eax存储了字符串长度,必须为6,否则就炸。然后再把0给eax,将输入保存在ecx,因为rax刚开始为0.

将cl的值(ecx的一个子寄存器)给rdx,然后获取edx的低四位。然后所得到的edx的值作为指针访问0x4024b0地址下的某一位(有点绕),一直到4010ac处的指令,可用代码表示为:

1
2
for ( i = 0LL; i != 6; ++i )
arry[i] = array_0x4024b0[*(a1 + i) & 0xF];//a1是eax的值,即字符串长度,6

0x4024b0处

image-20220228220413822

将rsp+0x16的值化为0,也就是代表该字符串的末尾;0x40245e处:

image-20220228215537273

然后传入两个参数esi,rdi到strings_not_equal函数不相等就炸。

所以根据后续代码逻辑,我们需要在0x4024b0处的字符串中找到0x40245e处的下标。

分别是9 15 14 5 6 7,但是,这些都是答案的低四位

因为有取低四位这个步骤,这里给出C语言代码

1
array[arry_4024b0[i]&0xf] = arry_0x40245e[i];

借用一张图片,可以对着查询,任意选一组即可。

63d9f2d3572c11dff05943a1652762d0f603c2ac

Phase_6

代码有点长,我们分部来看。

首先是将rsi作为参数传入read_six_numbers函数,然后又赋值,将eax的值减1后,与5作比较,小于等于5则不爆。然后将r12d的值加一(刚开始为0),与6比较,等于才跳,接着又有赋值操作,然后将eax与rbp+0作比较,相等就炸。

image-20220301113532571

接着来到401145处,ebx加一,再与5作比较,大于5就不跳。很明显一个循环。我们向下继续。

将r13的值加4,跳到刚刚的401114处,如此看来,r12d是循环次数,6次。从开始截至401151,是一个二重循环。

1
2
3
4
5
6
for (int i=0; i<6; i++){
if (arr[i] - 1 > 5) bomb()
for (int j=i+1; j<=5; j++) {
if(arr[j] == arr[i]) bomb()
}
}

我们从401153处继续,向下还是一堆赋值操作,然后比较,rax和rsi不等就跳,向下看,0x0给esi,然后跳到401197.

image-20220301120225795

来到401197处

image-20220301120702509

首先是赋值,然后与1作比较,小于等于跳到401183否则跳到401176进行循环,然后通过401176的循环再步入401188的循环。

我们从4011ab向下看

image-20220301121122327

依然是一堆赋值,然后接上比较,相等就跳转。将rbx给rcx后,跳到4011bd处,进行循环,要循环8次,然后到4011d2处,接着又是一堆赋值,需要eax小于rbx的值,才跳转,否则炸。最后还有一个从4011df开始的循环(ebp-1不为0则跳转。)。其实是一个值的置换操作,不过是执行6次,按降序排列

需要注意一下4011a4处的命令

image-20220301125119167

看样子是个结构体(用IDA看就很明确)

image-20220301124131818

image-20220301124149305

image-20220301124211115

image-20220301124227196

image-20220301124245570

image-20220301124303447

所以以上逻辑大致为我们输入的序列被7减去后得到的序列,是一个向量,向量每个数字是node的序号,向量的顺序是node的链接顺序,也就是说向量的第一个序号对应的node会变成头节点

最后放上IDA的代码

image-20220301122304972

image-20220301122335913

image-20220301122359866

故,依照上述代码逻辑将6个node的值按降序排列,所得序号为3 4 5 6 1 2,但因为在这之前涉及x=7-x这一操作

故正确答案应为4 3 2 1 6 5,至此扫雷。

secret_phase

image-20220301125404871

image-20220301130109136

wairi,受不了了,直接放反编译出的代码

image-20220301125918989

func7

image-20220301125942372

和汇编代码对照看,第二个if是右移,第三个是左移

发现0x6030f0处的往后是个二叉树,

image-20220301130237470

再往后就是node了。

└─ 36
├─ 8
│ ├─ 6
│ │ ├─ left: 1
│ │ └─ right: 7
│ └─ 22
│ ├─ left: 20
│ └─ right: 35
└─ 50
├─ 45
│ ├─ left: 40
│ └─ right: 47
└─ 107
├─ left: 99
└─ right: 1001

我们需要找的数,是最后返回2的,那么,最后一次就返回0,再上一次返回2 * rax + 1,第一次返回2 * rax

那么两个值可以,20或22.

至此,扫雷完毕。

不算小结的小结

汇编看起来确实麻烦,做最后两个的时候都是结合C语言代码来搞得,对汇编还不是很熟悉。

这个实验如果用IDA当逆向题做的话,难度会降低很多。