本文主要是介绍【从零开始的新手之旅】:2023西工大计算机系统基础实验之Lab2,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
实验要求
通过程序反汇编和逆向工程拆除所给的目标代码文中的二进制炸弹。⼀个“Binary Bombs”(二进制炸弹)是⼀个Linux可执行C程序,包含phase1~phase6共6个阶段,即6个炸弹。炸弹运⾏各阶段要求输⼊⼀个字符串,若输⼊符合程序预期,该阶段炸弹被“拆除”,否则“爆炸”。
每个阶段考察机器级语言程序的不同方面,难度递增
实验所用
*对程序机器级表示的基本理解(比如大端排序与小端排序;数组等数据在十六进制数表示的二进制文本中是如何存储的,还有栈的生长是从低地址开始还是从高地址开始的等等)
*出色的AI大模型(gdb调试器和objdump工具的指令不会用时可以问,类似高级语言的伪代码看不懂也可以问,用十六进制数表示的二进制文本看不懂还是可以问)
*IDA(是一款业界领先的专业级静态反编译和逆向工程工具,以其卓越的功能和强大的分析能力闻名于网络安全、恶意软件分析、二进制逆向工程等领域)
对于某些架构(如x86),IDA Pro包含了高度优化的反编译器,可以生成类似高级语言的伪代码,极大地简化了逆向工程的过程。这样实验某种程度上就变成了C语言程序阅读题。
虽然实验目的是希望能够让学生熟悉汇编语言,并学会使用gdb调试器和objdump工具(偶尔还是会用一下的......)。但奈何本人水平有限,不得不出此下策。
由于各种原因,在此本人就不放置下载链接了,各位自行上网搜索即可。
IDA超基础用法
我下载的IDA是运行在Windows系统上的。用IDA打开原本需要反汇编的Linux系统可执行文件(我
的文件名叫“bomb”),一路点击蓝色边框的选项就弹出了最下方的界面。
左边的“函数名称”那栏可以双击选择想要查看的函数 ,右键选择文本视图可以以文本形式而不是图表形式查看反汇编代码。
点击“Pseudocode-A”可以查看选定函数对应的类似高级语言的伪代码。标为黄色的函数名或变量名可以双击进入来查看所对应的函数的伪代码,或是全局变量的内容。
点击“Hex-Vew-1”可以查看用十六进制数表示的可执行文件的文本内容。
退出时点击“确定”保存并退出,会生成一个IDB 文件 。需要时双击打开就可以继续查看了。
更为详尽且专业的应用方法,敬请参考互联网上的其他资源,例如CSDN博客平台上众多博主发布的相关文章。
实验思考与结果
Phase1
用IDA将汇编语言转为伪代码,可以看出它负责判断输入的字符串与“Houses will begat jobs, jobs will begat houses.”是否一致,若不一致则炸弹爆炸。因此“Houses will begat jobs, jobs will begat houses.”即为答案。
1. int __cdecl phase_1(int a1)
2. {
3. int result; // eax
4.
5. result = strings_not_equal(a1, "Houses will begat jobs, jobs will begat houses.");
6. if ( result )
7. explode_bomb();
8. return result;
9. }
Phase2
int __cdecl phase_2(int a1)
{int result; // eaxint v2[6]; // [esp+4h] [ebp-24h] BYREFint i; // [esp+1Ch] [ebp-Ch]read_six_numbers(a1, v2);result = v2[0];if ( v2[0] != 1 )explode_bomb();for ( i = 1; i <= 5; ++i ){result = v2[i];if ( result != 2 * v2[i - 1] )explode_bomb();}return result;
}
用IDA将汇编语言转为伪代码,可以看出这次需要输入六个数字存入数组V2[6]中,且根据read_six_number函数的伪代码可知,六个数字之间要使用空格分开。
根据“if ( v2[0] != 1 )explode_bomb();”可知第一个数字为1,又根据“if ( result != 2 * v2[i - 1] )explode_bomb();”可知从第二个数字开始每一个数字都是前一个数字的2倍,因此答案为“1 2 4 8 16 32”。
int __cdecl read_six_numbers(int a1, int a2)
{int result; // eaxresult = __isoc99_sscanf(a1, "%d %d %d %d %d %d", a2, a2 + 4, a2 + 8, a2 + 12, a2 + 16, a2 + 20);if ( result <= 5 )explode_bomb();return result;
}
Phase3
用IDA将汇编语言转为伪代码,根据“v5 = __isoc99_sscanf(a1, "%d %c %d", &v4, &v2, &v3);”可知这次需要输入一个数字,空格,一个字符,空格和又一个数字。根据“switch(V4)”可知程序会根据V4的值给V6赋上对应的值,并且V3的值也要等于该条件下某个特定值才不会让炸弹爆炸。又根据“if ( v6 != v2 ) explode_bomb();”可知我们输入的V2要等于对应的V4得到的V6。因为有多个出口,因此理论上答案不止一个,为了方便起见,取V4=0,则V6=111,V3=584,由ASCII码对照表可知,V2=‘o’,所以答案可以是“0 o 584”。
int __cdecl phase_3(int a1)
{int result; // eaxunsigned __int8 v2; // [esp+Fh] [ebp-19h] BYREFint v3; // [esp+10h] [ebp-18h] BYREFint v4; // [esp+14h] [ebp-14h] BYREFint v5; // [esp+18h] [ebp-10h]char v6; // [esp+1Fh] [ebp-9h]v5 = 0;v5 = __isoc99_sscanf(a1, "%d %c %d", &v4, &v2, &v3);if ( v5 <= 2 )explode_bomb();switch ( v4 ){case 0:v6 = 111;if ( v3 != 584 )explode_bomb();return result;case 1:v6 = 104;if ( v3 != 677 )explode_bomb();return result;case 2:v6 = 101;if ( v3 != 288 )explode_bomb();return result;case 3:v6 = 120;if ( v3 != 433 )explode_bomb();return result;case 4:v6 = 105;if ( v3 != 636 )explode_bomb();return result;case 5:v6 = 109;if ( v3 != 959 )explode_bomb();return result;case 6:v6 = 114;if ( v3 != 665 )explode_bomb();return result;case 7:v6 = 98;if ( v3 != 508 )explode_bomb();return result;default:v6 = 101;explode_bomb();}result = v2;if ( v6 != v2 )explode_bomb();return result;
}
Phase4
int __cdecl phase_5(int a1)
{int result; // eaxint v2; // [esp+8h] [ebp-20h] BYREFint v3; // [esp+Ch] [ebp-1Ch] BYREFint v4; // [esp+10h] [ebp-18h]int v5; // [esp+14h] [ebp-14h]int v6; // [esp+18h] [ebp-10h]int v7; // [esp+1Ch] [ebp-Ch]v5 = __isoc99_sscanf(a1, "%d %d", &v3, &v2);if ( v5 <= 1 )explode_bomb();v3 &= 0xFu;v4 = v3;v7 = 0;v6 = 0;while ( v3 != 15 ){++v7;v3 = array_2707[v3];v6 += v3;}if ( v7 != 15 || (result = v2, v6 != v2) )explode_bomb();return result;
}
用IDA将汇编语言转为伪代码,由“v6 = __isoc99_sscanf(a1, "%d %d", &v3, &v2);if ( v6 != 2 || v3 >= 0xF ) explode_bomb();”可知,要输入的是两个用空格隔开的数字,且前一个数字不大于15;又由“v5 = 6;v4 = func4(v3, 0, 14);if ( v4 != v5 || (result = v2, v2 != v5) ) explode_bomb();”可知调用func4函数后返回的结果等于6,且V2的值即输入的后一个数字是6 。
int __cdecl func4(int a1, int a2, int a3)
{int v4; // [esp+Ch] [ebp-Ch]v4 = (a3 - a2) / 2 + a2;if ( v4 > a1 )return 2 * func4(a1, a2, v4 - 1);if ( v4 >= a1 )return 0;return 2 * func4(a1, v4 + 1, a3) + 1;
}
现在研究func4函数来得到前一个数字的值。根据“v4 = func4(v3, 0, 14);”、“v4 = (a3 - a2) / 2 + a2;”、“ if ( v4 > a1 ) return 2 * func4(a1, a2, v4 - 1);”等可知,该函数的功能是取传入的后两个数据的平均值与第一个数据的值进行比较,再根据结果进行递归。由“if ( v4 >= a1 ) return 0;”可知,最后一次调用函数时一定得到了0的结果,否则无法结束递归;又因为结果为6,所以过程是“一共四层递归,最后一次调用得0,返回上一层进行2*0+1=1的运算,再返回上一层进行2*1+1=3的运算,最后返回最开始进行3*2=6的运算得到结果”。根据思路逆推可列出下表:
要求 | a1 | a2 | a3 | v4 |
v4>a1 | v3 | 0 | 14 | 7 |
v4<a1 | v3 | 0 | 6 | 3 |
v4<a1 | v3 | 4 | 6 | 5 |
v4=a1 | v3 | 6 | 6 | 6 |
则“v3<7&&v3>3&&v3>5&&v3=6”,故前一个数字的值也为6。因此答案为“6 6”。
Phase5
int __cdecl phase_5(int a1)
{int result; // eaxint v2; // [esp+8h] [ebp-20h] BYREFint v3; // [esp+Ch] [ebp-1Ch] BYREFint v4; // [esp+10h] [ebp-18h]int v5; // [esp+14h] [ebp-14h]int v6; // [esp+18h] [ebp-10h]int v7; // [esp+1Ch] [ebp-Ch]v5 = __isoc99_sscanf(a1, "%d %d", &v3, &v2);if ( v5 <= 1 )explode_bomb();v3 &= 0xFu;v4 = v3;v7 = 0;v6 = 0;while ( v3 != 15 ){++v7;v3 = array_2707[v3];v6 += v3;}if ( v7 != 15 || (result = v2, v6 != v2) )explode_bomb();return result;
}
用IDA将汇编语言转为伪代码,根据“v5 = __isoc99_sscanf(a1, "%d %d", &v3, &v2);”可知字符串的格式为“数字 数字”。“v3&=0XFu”的意思是只保留v3二进制格式的后四位,因为“0XFu”等价于“00001111”且只有1&1=1。故将v3视作一个不大于15的自然数。汇编代码中出现了“array_2707”,而根据“int array_2707[16]”可知这是一个有16个int类型元素的全局数组。
又由“while ( v3 != 15 ){++v7;v3=array_2707[v3]; v6 += v3; }”和“if ( v7 != 15 || (result = v2, v6 != v2) ) explode_bomb();”可知,执行完while循环语句后v3和v7的值都变为15且v2要等于v6。我们还能知道v7相当于计数器,则循环总共执行了15次;在循环中v3每次会取数组array_2707[16]的一个元素,则循环结束后v6表示v3取到的所有元素之和。因此为了得到答案,我们需要知道数组array _2707[16]的结构。
因为一个地址对应一个字节,汇编代码是以十六进制的形式来表示字节的,int类型整数是四个字节长的,且字节的排列方式为小端序,数组元素是按下标从小到大从低地址向高地址排序的,所以array_2707[16]的结构为:
array_2707[0]=10 | array_2707[1]=2 | array_2707[2]=14 | array_2707[3]=7 |
array_2707[4]=8 | array_2707[5]=12 | array_2707[6]=15 | array_2707[7]=11 |
array_2707[8]=0 | array_2707[9]=4 | array_2707[10]=1 | array_2707[11]=13 |
array_2707[12]=3 | array_2707[13]=9 | array_2707[14]=6 | array_2707[15]=5 |
.data:0804D5E0 ; int array_2707[16]
.data:0804D5E0 0A 00 00 00 array_2707 dd 0Ah ; DATA XREF: phase_5+52↑r
.data:0804D5E4 02 db 2
.data:0804D5E5 00 db 0
.data:0804D5E6 00 db 0
.data:0804D5E7 00 db 0
.data:0804D5E8 0E db 0Eh
.data:0804D5E9 00 db 0
.data:0804D5EA 00 db 0
.data:0804D5EB 00 db 0
.data:0804D5EC 07 db 7
.data:0804D5ED 00 db 0
.data:0804D5EE 00 db 0
.data:0804D5EF 00 db 0
.data:0804D5F0 08 db 8
.data:0804D5F1 00 db 0
.data:0804D5F2 00 db 0
.data:0804D5F3 00 db 0
.data:0804D5F4 0C db 0Ch
.data:0804D5F5 00 db 0
.data:0804D5F6 00 db 0
.data:0804D5F7 00 db 0
.data:0804D5F8 0F db 0Fh
.data:0804D5F9 00 db 0
.data:0804D5FA 00 db 0
.data:0804D5FB 00 db 0
.data:0804D5FC 0B db 0Bh
.data:0804D5FD 00 db 0
.data:0804D5FE 00 db 0
.data:0804D5FF 00 db 0
.data:0804D600 00 db 0
.data:0804D601 00 db 0
.data:0804D602 00 db 0
.data:0804D603 00 db 0
.data:0804D604 04 db 4
.data:0804D605 00 db 0
.data:0804D606 00 db 0
.data:0804D607 00 db 0
.data:0804D608 01 db 1
.data:0804D609 00 db 0
.data:0804D60A 00 db 0
.data:0804D60B 00 db 0
.data:0804D60C 0D db 0Dh
.data:0804D60D 00 db 0
.data:0804D60E 00 db 0
.data:0804D60F 00 db 0
.data:0804D610 03 db 3
.data:0804D611 00 db 0
.data:0804D612 00 db 0
.data:0804D613 00 db 0
.data:0804D614 09 db 9
.data:0804D615 00 db 0
.data:0804D616 00 db 0
.data:0804D617 00 db 0
.data:0804D618 06 db 6
.data:0804D619 00 db 0
.data:0804D61A 00 db 0
.data:0804D61B 00 db 0
.data:0804D61C 05 db 5
.data:0804D61D 00 db 0
.data:0804D61E 00 db 0
.data:0804D61F 00 db 0
那么由结果逆推可列出下表表示循环过程:
v7 | v3 | v6 |
0 | 5 | 0 |
1 | 12 | 12 |
2 | 3 | 15 |
3 | 7 | 22 |
4 | 11 | 33 |
5 | 13 | 46 |
6 | 9 | 55 |
7 | 4 | 59 |
8 | 8 | 67 |
9 | 0 | 67 |
10 | 10 | 77 |
11 | 1 | 78 |
12 | 2 | 80 |
13 | 14 | 94 |
14 | 6 | 100 |
15 | 15 | 115 |
所以“v3&=0XFu=5,v2=v6=115”,则答案为“5 115”。
Phase6
_DWORD *__cdecl phase_6(int a1)
{_DWORD *result; // eaxint v2[6]; // [esp+0h] [ebp-48h]int v3[6]; // [esp+18h] [ebp-30h] BYREF_DWORD *v4; // [esp+30h] [ebp-18h]int j; // [esp+34h] [ebp-14h]int i; // [esp+38h] [ebp-10h]_DWORD *v7; // [esp+3Ch] [ebp-Ch]v4 = &node1;read_six_numbers(a1, v3);for ( i = 0; i <= 5; ++i ){if ( v3[i] <= 0 || v3[i] > 6 )explode_bomb();for ( j = i + 1; j <= 5; ++j ){if ( v3[i] == v3[j] )explode_bomb();}}for ( i = 0; i <= 5; ++i ){v7 = v4;for ( j = 1; v3[i] > j; ++j )v7 = (_DWORD *)v7[2];v2[i] = (int)v7;}v4 = (_DWORD *)v2[0];v7 = (_DWORD *)v2[0];for ( i = 1; i <= 5; ++i ){v7[2] = v2[i];v7 = (_DWORD *)v7[2];}v7[2] = 0;result = v4;v7 = v4;for ( i = 0; i <= 4; ++i ){if ( *v7 > *(_DWORD *)v7[2] )explode_bomb();result = (_DWORD *)v7[2];v7 = result;}return result;
}
用IDA将汇编语言转为伪代码。由“read_six_numbers(a1, (int)v3); for ( i = 0; i <= 5; ++i ) { if ( v3[i] <= 0 ||v3[i] > 6 )explode_bomb();for ( j = i +1;j<=5;++j ){if( v3[i]==v3[j] )explode_bomb();}}”可知,输入的字符串一定“1 2 3 4 5 6”这六个数字的某种排列组合,因为“read_six_numbers(a1, (int)v3);”说明输入的字符串的格式是六个以空格间隔的整数;“if( v3[i]<=0||v3[i] >6 )explode_bomb();”说明这六个数字都是不大于六的自然数;“for ( j = i +1;j<=5;++j ){if( v3[i]==v3[j] )explode_bomb();}}”说明这六个数字互不相等。
.data:0804D4E0 public node6
.data:0804D4E0 37 node6 db 37h ; 7
.data:0804D4E1 02 db 2
.data:0804D4E2 00 db 0
.data:0804D4E3 00 db 0
.data:0804D4E4 06 db 6
.data:0804D4E5 00 db 0
.data:0804D4E6 00 db 0
.data:0804D4E7 00 db 0
.data:0804D4E8 00 db 0
.data:0804D4E9 00 db 0
.data:0804D4EA 00 db 0
.data:0804D4EB 00 db 0
.data:0804D4EC public node5
.data:0804D4EC 81 node5 db 81h
.data:0804D4ED 01 db 1
.data:0804D4EE 00 db 0
.data:0804D4EF 00 db 0
.data:0804D4F0 05 db 5
.data:0804D4F1 00 db 0
.data:0804D4F2 00 db 0
.data:0804D4F3 00 db 0
.data:0804D4F4 E0 db 0E0h
.data:0804D4F5 D4 db 0D4h
.data:0804D4F6 04 db 4
.data:0804D4F7 08 db 8
.data:0804D4F8 public node4
.data:0804D4F8 D2 node4 db 0D2h
.data:0804D4F9 02 db 2
.data:0804D4FA 00 db 0
.data:0804D4FB 00 db 0
.data:0804D4FC 04 db 4
.data:0804D4FD 00 db 0
.data:0804D4FE 00 db 0
.data:0804D4FF 00 db 0
.data:0804D500 EC db 0ECh
.data:0804D501 D4 db 0D4h
.data:0804D502 04 db 4
.data:0804D503 08 db 8
.data:0804D504 public node3
.data:0804D504 0D node3 db 0Dh
.data:0804D505 03 db 3
.data:0804D506 00 db 0
.data:0804D507 00 db 0
.data:0804D508 03 db 3
.data:0804D509 00 db 0
.data:0804D50A 00 db 0
.data:0804D50B 00 db 0
.data:0804D50C F8 db 0F8h
.data:0804D50D D4 db 0D4h
.data:0804D50E 04 db 4
.data:0804D50F 08 db 8
.data:0804D510 public node2
.data:0804D510 13 node2 db 13h
.data:0804D511 03 db 3
.data:0804D512 00 db 0
.data:0804D513 00 db 0
.data:0804D514 02 db 2
.data:0804D515 00 db 0
.data:0804D516 00 db 0
.data:0804D517 00 db 0
.data:0804D518 04 db 4
.data:0804D519 D5 db 0D5h
.data:0804D51A 04 db 4
.data:0804D51B 08 db 8
.data:0804D51C public node1
.data:0804D51C 8C node1 db 8Ch ; DATA XREF: phase_6+6↑o
.data:0804D51D 02 db 2
.data:0804D51E 00 db 0
.data:0804D51F 00 db 0
.data:0804D520 01 db 1
.data:0804D521 00 db 0
.data:0804D522 00 db 0
.data:0804D523 00 db 0
.data:0804D524 10 db 10h
.data:0804D525 D5 db 0D5h
.data:0804D526 04 db 4
.data:0804D527 08 db 8
因为“v4 = &node1;”且“v7 = v4;”,所以我们必须弄明白“public”类型变量node1的结构。又因为v4,v7全都是“_DWORD *”类型的指针,长度为四个字节,而“public”类型的变量是一个长度为十二个字节的结构体,因此我们可以将变量node1按每四个字节为一组划分为三段,这样“_DWORD *”类型的指针每次只指向其中的一段。又因为之后的代码中出现了“v7 = (_DWORD *)v7[2];”这样的语句,它的意思是记v7中存储的地址对应的“四个字节”为第一段“四个字节”(即v[0])。然后开始向高地址移动两次,取第三段的“四个字节”存入v7之中。值得注意的是,node1的第三段恰好是“public”类型变量node2的地址,且它们的名字惊人地相似,而这样的变量还有四个。这绝对不是巧合,因此我们可以先将变量node(1~6)从低地址到高地址排序备用:
node6 | 0237h | 0 | 0 |
node5 | 0181h | 5 | & node6 |
node4 | 02D2h | 4 | & node5 |
node3 | 030Dh | 3 | & node4 |
node2 | 0313h | 2 | & node3 |
node1 | 028Ch | 1 | & node2 |
只有进行了前面分析,我们才有能力来解读“for ( i = 0; i <= 5; ++i ){ v7 = v4;for ( j = 1; v3[i] > j; ++j )v7 = (_DWORD *)v7[2];v2[i] = (int)v7;}”这段代码。外层for循环的“i = 0; i <= 5; ++i”以及内部出现的“v3[i] > j”和“v2[i] = (int)v7;”表明内部的操作与数组v2[6]和v3[6]的所有元素都相关,且两个数组中相同下标的元素间存在某种关联;而内层for循环的“j = 1; v3[i] > j; ++j”表明v3[i]比1大多少,该循环就进行多少次。据此我们列出表格,来记录v3[i]与v7的关系:
v3[i] | v7 |
1 | &node1 |
2 | &node2 |
3 | &node3 |
4 | &node4 |
5 | &node5 |
6 | &node6 |
所以v3[i]与v2[i]之间的关系具体为v3[i]=k,则v2[i]=&node(k)(k=1~6)。
接着分析“v7 = (_DWORD *)v2[0];for ( i = 1; i <= 5; ++i ) { v7[2] = v2[i];v7 = (_DWORD *)v7[2] ;}v7[2] = 0;”这段代码。该循环执行5次,第一遍循环时“v7[2] = v2[i];”的意思是将变量v2[0]对应的node(x)的第三段的“四个字节”换为v2[1]中存储的&node(y);“v7 = (_DWORD *)v7[2];”的意思则是将v7中存储的值更改为&node(y)。也就是说如果:
v2[0] | &node(x) |
v2[1] | &node(y) |
v2[2] | &node(z) |
v2[3] | &node(u) |
v2[4] | &node(v) |
v2[5] | &node(w) |
那么(该表不表示node变量之间的顺序,且变量的前两段没有改动):
node(x) | X | ? | & node(y) |
node(y) | Y | ? | & node(z) |
node(z) | Z | ? | & node(u) |
node(u) | U | ? | & node(v) |
node(v) | V | ? | & node(w) |
node(w) | W | ? | 0 |
当然,仅仅是这样的话,得到的信息必然是不足以让我们得出答案的,我们还需要分析剩下的最后那段代码。
现在我们来分析结尾的“v7=v4;for ( i = 0; i<=4;++i ){if( *v7 >*(_DWORD*)v7[2] ) explode_bomb(); result = (_DWORD *)v7[2]; v7 = result; }”。根据上文的“v4 = (_DWORD *)v2[0];”,那么“v7=v4;”是令指针v7重新指向node(x)。接下来是一个执行次数为五的循环,第一遍循环时“*v7 >*(_DWORD*) v7[2] )”的意思是要求node(x)的前四个字节代表的整数一定不大于node(y) 的前四个字节代表的整数;而“result = (_DWORD *)v7[2]; v7 = result;”等价于“v7 = (_DWORD *)v7[2];” 意思是令指针v7指向node(y)。这样的话就要求:“X≤Y≤Z≤U≤V≤W”,又因为“0181h<0237h<028Ch<02D2h <030Dh <0313h”,根据之前的表格可知:
v2[0] | &node5 |
v2[1] | &node6 |
v2[2] | &node1 |
v2[3] | &node4 |
v2[4] | &node3 |
v2[5] | &node2 |
所以
v3[0] | 5 |
v3[1] | 6 |
v3[2] | 1 |
v3[3] | 4 |
v3[4] | 3 |
v3[5] | 2 |
输入的字符串为“5 6 1 4 3 2”。
Secret Phase
int secret_phase()
{int v1; // [esp+8h] [ebp-10h]char *nptr; // [esp+Ch] [ebp-Ch]nptr = (char *)read_line();v1 = atoi(nptr);if ( v1 <= 0 || v1 > 1001 )explode_bomb();if ( fun7(&n1, v1) != 2 )explode_bomb();puts("Wow! You've defused the secret stage!");return phase_defused();
}
先用IDA将汇编语言转为伪代码。对于“nptr = (char *)read_line(); v1 = atoi(nptr);”这段代码,我的理解是从终端中读入输入的字符串并用atoi函数将其转换为int类型再赋给v1。而“if ( v1 <= 0 || v1 > 1001 )explode_bomb();”要求 v1是一个不大于1001的自然数。“if ( fun7(n1, v1) != 2 )explode_bomb();”则要求调用fun7函数并且返回的值要为2,因此接下来我们需要来分析fun7函数。
int __cdecl fun7(_DWORD *a1, int a2)
{if ( !a1 )return -1;if ( *a1 > a2 )return 2 * fun7(a1[1], a2);if ( *a1 == a2 )return 0;return 2 * fun7(a1[2], a2) + 1;
}
fun7函数与func4函数在名称和结构上都非常相似,且“fun7(n1, v1)”中涉及到了全局变量n1。依据“_DWORD n1[4]”,我们可以知道n1是一个有四个元素的数组,每个元素都是一个长度为四个字节的“_DWORD *”类型的指针。我们还可以发现在比n1的地址更低的地方,还有“n21、n22、n31……n48”这些与n1只是下标不同的“public”类型的变量,我们可以称其为“n族变量”。某些n族变量也像变量node(1~6)一样储存了同族其他变量的地址。基于这些现象,我们可以断定这道题目启示我们要参考解决Phase4和Phase6的经验,这可以为我们打开解题的思路。
.data:0804D528 public n48
.data:0804D528 E9 n48 db 0E9h
.data:0804D529 03 db 3
.data:0804D52A 00 db 0
.data:0804D52B 00 db 0
.data:0804D52C 00 db 0
.data:0804D52D 00 db 0
.data:0804D52E 00 db 0
.data:0804D52F 00 db 0
.data:0804D530 00 db 0
.data:0804D531 00 db 0
.data:0804D532 00 db 0
.data:0804D533 00 db 0
.data:0804D534 public n46
.data:0804D534 2F n46 db 2Fh ; /
.data:0804D535 00 db 0
.data:0804D536 00 db 0
.data:0804D537 00 db 0
.data:0804D538 00 db 0
.data:0804D539 00 db 0
.data:0804D53A 00 db 0
.data:0804D53B 00 db 0
.data:0804D53C 00 db 0
.data:0804D53D 00 db 0
.data:0804D53E 00 db 0
.data:0804D53F 00 db 0
.data:0804D540 public n43
.data:0804D540 14 n43 db 14h
.data:0804D541 00 db 0
.data:0804D542 00 db 0
.data:0804D543 00 db 0
.data:0804D544 00 db 0
.data:0804D545 00 db 0
.data:0804D546 00 db 0
.data:0804D547 00 db 0
.data:0804D548 00 db 0
.data:0804D549 00 db 0
.data:0804D54A 00 db 0
.data:0804D54B 00 db 0
.data:0804D54C public n42
.data:0804D54C 07 n42 db 7
.data:0804D54D 00 db 0
.data:0804D54E 00 db 0
.data:0804D54F 00 db 0
.data:0804D550 00 db 0
.data:0804D551 00 db 0
.data:0804D552 00 db 0
.data:0804D553 00 db 0
.data:0804D554 00 db 0
.data:0804D555 00 db 0
.data:0804D556 00 db 0
.data:0804D557 00 db 0
.data:0804D558 public n44
.data:0804D558 23 n44 db 23h ; #
.data:0804D559 00 db 0
.data:0804D55A 00 db 0
.data:0804D55B 00 db 0
.data:0804D55C 00 db 0
.data:0804D55D 00 db 0
.data:0804D55E 00 db 0
.data:0804D55F 00 db 0
.data:0804D560 00 db 0
.data:0804D561 00 db 0
.data:0804D562 00 db 0
.data:0804D563 00 db 0
.data:0804D564 public n47
.data:0804D564 63 n47 db 63h ; c
.data:0804D565 00 db 0
.data:0804D566 00 db 0
.data:0804D567 00 db 0
.data:0804D568 00 db 0
.data:0804D569 00 db 0
.data:0804D56A 00 db 0
.data:0804D56B 00 db 0
.data:0804D56C 00 db 0
.data:0804D56D 00 db 0
.data:0804D56E 00 db 0
.data:0804D56F 00 db 0
.data:0804D570 public n41
.data:0804D570 01 n41 db 1
.data:0804D571 00 db 0
.data:0804D572 00 db 0
.data:0804D573 00 db 0
.data:0804D574 00 db 0
.data:0804D575 00 db 0
.data:0804D576 00 db 0
.data:0804D577 00 db 0
.data:0804D578 00 db 0
.data:0804D579 00 db 0
.data:0804D57A 00 db 0
.data:0804D57B 00 db 0
.data:0804D57C public n45
.data:0804D57C 28 n45 db 28h ; (
.data:0804D57D 00 db 0
.data:0804D57E 00 db 0
.data:0804D57F 00 db 0
.data:0804D580 00 db 0
.data:0804D581 00 db 0
.data:0804D582 00 db 0
.data:0804D583 00 db 0
.data:0804D584 00 db 0
.data:0804D585 00 db 0
.data:0804D586 00 db 0
.data:0804D587 00 db 0
.data:0804D588 public n34
.data:0804D588 6B n34 db 6Bh ; k
.data:0804D589 00 db 0
.data:0804D58A 00 db 0
.data:0804D58B 00 db 0
.data:0804D58C 64 db 64h ; d
.data:0804D58D D5 db 0D5h
.data:0804D58E 04 db 4
.data:0804D58F 08 db 8
.data:0804D590 28 db 28h ; (
.data:0804D591 D5 db 0D5h
.data:0804D592 04 db 4
.data:0804D593 08 db 8
.data:0804D594 public n31
.data:0804D594 06 n31 db 6
.data:0804D595 00 db 0
.data:0804D596 00 db 0
.data:0804D597 00 db 0
.data:0804D598 70 db 70h ; p
.data:0804D599 D5 db 0D5h
.data:0804D59A 04 db 4
.data:0804D59B 08 db 8
.data:0804D59C 4C db 4Ch ; L
.data:0804D59D D5 db 0D5h
.data:0804D59E 04 db 4
.data:0804D59F 08 db 8
.data:0804D5A0 public n33
.data:0804D5A0 2D n33 db 2Dh ; -
.data:0804D5A1 00 db 0
.data:0804D5A2 00 db 0
.data:0804D5A3 00 db 0
.data:0804D5A4 7C db 7Ch ; |
.data:0804D5A5 D5 db 0D5h
.data:0804D5A6 04 db 4
.data:0804D5A7 08 db 8
.data:0804D5A8 34 db 34h ; 4
.data:0804D5A9 D5 db 0D5h
.data:0804D5AA 04 db 4
.data:0804D5AB 08 db 8
.data:0804D5AC public n32
.data:0804D5AC 16 n32 db 16h
.data:0804D5AD 00 db 0
.data:0804D5AE 00 db 0
.data:0804D5AF 00 db 0
.data:0804D5B0 40 db 40h ; @
.data:0804D5B1 D5 db 0D5h
.data:0804D5B2 04 db 4
.data:0804D5B3 08 db 8
.data:0804D5B4 58 db 58h ; X
.data:0804D5B5 D5 db 0D5h
.data:0804D5B6 04 db 4
.data:0804D5B7 08 db 8
.data:0804D5B8 public n22
.data:0804D5B8 32 n22 db 32h ; 2
.data:0804D5B9 00 db 0
.data:0804D5BA 00 db 0
.data:0804D5BB 00 db 0
.data:0804D5BC A0 db 0A0h
.data:0804D5BD D5 db 0D5h
.data:0804D5BE 04 db 4
.data:0804D5BF 08 db 8
.data:0804D5C0 88 db 88h
.data:0804D5C1 D5 db 0D5h
.data:0804D5C2 04 db 4
.data:0804D5C3 08 db 8
.data:0804D5C4 public n21
.data:0804D5C4 08 n21 db 8
.data:0804D5C5 00 db 0
.data:0804D5C6 00 db 0
.data:0804D5C7 00 db 0
.data:0804D5C8 94 db 94h
.data:0804D5C9 D5 db 0D5h
.data:0804D5CA 04 db 4
.data:0804D5CB 08 db 8
.data:0804D5CC AC db 0ACh
.data:0804D5CD D5 db 0D5h
.data:0804D5CE 04 db 4
.data:0804D5CF 08 db 8
.data:0804D5D0 public n1
.data:0804D5D0 24 n1 db 24h ; $ ; DATA XREF: secret_phase+39↑o
.data:0804D5D1 00 db 0
.data:0804D5D2 00 db 0
.data:0804D5D3 00 db 0
.data:0804D5D4 C4 db 0C4h
.data:0804D5D5 D5 db 0D5h
.data:0804D5D6 04 db 4
.data:0804D5D7 08 db 8
.data:0804D5D8 B8 db 0B8h
.data:0804D5D9 D5 db 0D5h
.data:0804D5DA 04 db 4
.data:0804D5DB 08 db 8
.data:0804D5DC 00 db 0
.data:0804D5DD 00 db 0
.data:0804D5DE 00 db 0
.data:0804D5DF 00 db 0
先将n族变量整理备用(n族变量是按地址从高到低排序,但各变量内部是按地址从低到高排序):
*n1[4] | n1[0]=36 | n1[1]=&n21 | n1[2]=&n22 | n1[3]=0 | |||
n21 | 8 | &n31 | &n32 | ||||
n22 | 50 | &n33 | &n34 | ||||
n31 | 6 | &n41 | &n42 | ||||
n32 | 22 | &n43 | &n44 | ||||
n33 | 45 | &n45 | &n46 | ||||
n34 | 107 | &n47 | &n48 | ||||
n41 | 1 | 0 | 0 | ||||
n42 | 7 | 0 | 0 | ||||
n43 | 20 | 0 | 0 | ||||
n44 | 35 | 0 | 0 | ||||
n45 | 40 | 0 | 0 | ||||
n46 | 47 | 0 | 0 | ||||
n47 | 99 | 0 | 0 | ||||
n48 | 1001 | 0 | 0 |
fun7函数中“if ( !a1 )return -1;”是检查第一个传入的变量的值是否为零,如果为零返回-1;如果不为零,则根据a1中的值解引用后得到的值与a2的大小关系进行递归。根据之前的经验,如果能跳出递归,那么最后要么执行“if ( !a1 )return -1;”要么执行“if ( *a1 == a2 )return 0;”。因为我们想要的返回值为2,那不妨设计如下的步骤:“一共三层递归,最后一次调用得0,返回上一层进行2*0+1=1的运算,最后进行1*2=2的运算得到返回值”。
将思路用表格的形式呈现:
a1 | *a1 | a2 | |
*a1 > a2 | n1 | 36 | v1 |
*a1 < a2 | &n21 | 8 | v1 |
*a1 = a2 | &n32 | 22 | v1 |
所以“36>v1&&v1>8&&v1=22”解得v1=22,则答案为“22”。
温馨提示
*善于运用人工智能技术,遇到不解之处可随时向AI寻求解答。
*尽管题目整体框架是一致的,但具体细节却存在差异性,因此本人的答案可能并不通用,仅供参考。
*本人的论述极度缺乏专业性,对此,建议各位参阅其他博主撰写的深度文章以获取更为准确的知识。同时,建议各位最好理解并掌握相关原理,因为难度较高的理论课考试可能会涉及到。
*完全依赖个人的独立摸索并非总是最优策略,如果你能得到来自同学伙伴或学长学姐的指导与帮助,那么恭喜你。
这篇关于【从零开始的新手之旅】:2023西工大计算机系统基础实验之Lab2的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!