观前提示:本文并非速通指南,偏向于如何一步步思考与解题,也许会分析得有些拖沓。且本文为系列文章,每一篇会分析一关的破解思路,前文提到的知识点后面不会赘述,分析过的类似的汇编代码段也不会再分析,除非是没见过的结构,所以越往后分析得越少。
本文共九百余字(汇编代码占大部分,实际没有多少字)。
前言
如果这些解析对你有帮助,那么我会非常荣幸和开心!
如果我的解析存在错误,非常抱歉误导了你!请在评论区提出!我也是一个正在不断学习的学生。
谢谢你!
正式破解
概览全局
这一关并没有什么新东西,就是条件跳转构成的循环。所以我不打算一句句分析了。这关只是看着代码很长而已,难点可能是分清循环的并列关系和嵌套关系和数据结构判断。
关于控制结构
只分析一个片段:
感觉好多都是读到最后才知道是循环体的范围。
关于数据结构
跟踪堆栈内存:
分析汇编代码时划分出的循环体
#第一块(有嵌套)
0x08048d82 <+39>: mov 0xc(%esp,%esi,4),%eax
0x08048d86 <+43>: sub $0x1,%eax
0x08048d89 <+46>: cmp $0x5,%eax
0x08048d8c <+49>: jbe 0x8048d93 <phase_6+56>
0x08048d8e <+51>: call 0x804907d <explode_bomb>
0x08048d93 <+56>: add $0x1,%esi
0x08048d96 <+59>: cmp $0x6,%esi
0x08048d99 <+62>: je 0x8048dce <phase_6+115>
0x08048d9b <+64>: mov %esi,%ebx
0x08048d9d <+66>: mov 0xc(%esp,%ebx,4),%eax
0x08048da1 <+70>: cmp %eax,0x8(%esp,%esi,4)
0x08048da5 <+74>: jne 0x8048dac <phase_6+81>
0x08048da7 <+76>: call 0x804907d <explode_bomb>
0x08048dac <+81>: add $0x1,%ebx
0x08048daf <+84>: cmp $0x5,%ebx
0x08048db2 <+87>: jle 0x8048d9d <phase_6+66>
0x08048db4 <+89>: jmp 0x8048d82 <phase_6+39>
#第二块(有嵌套)
0x08048db6 <+91>: mov 0x8(%edx),%edx
0x08048db9 <+94>: add $0x1,%eax
0x08048dbc <+97>: cmp %ecx,%eax
0x08048dbe <+99>: jne 0x8048db6 <phase_6+91>
0x08048dc0 <+101>: mov %edx,0x24(%esp,%esi,4)
0x08048dc4 <+105>: add $0x1,%ebx
0x08048dc7 <+108>: cmp $0x6,%ebx
0x08048dca <+111>: jne 0x8048dd3 <phase_6+120>
0x08048dcc <+113>: jmp 0x8048dea <phase_6+143>
0x08048dce <+115>: mov $0x0,%ebx
0x08048dd3 <+120>: mov %ebx,%esi
0x08048dd5 <+122>: mov 0xc(%esp,%ebx,4),%ecx
0x08048dd9 <+126>: mov $0x1,%eax
0x08048dde <+131>: mov $0x804c13c,%edx
0x08048de3 <+136>: cmp $0x1,%ecx
0x08048de6 <+139>: jg 0x8048db6 <phase_6+91>
0x08048de8 <+141>: jmp 0x8048dc0 <phase_6+101>
#第三块(无嵌套)
0x08048dea <+143>: mov 0x24(%esp),%ebx
0x08048dee <+147>: lea 0x24(%esp),%eax
0x08048df2 <+151>: lea 0x38(%esp),%esi
0x08048df6 <+155>: mov %ebx,%ecx
0x08048df8 <+157>: mov 0x4(%eax),%edx
0x08048dfb <+160>: mov %edx,0x8(%ecx)
0x08048dfe <+163>: add $0x4,%eax
0x08048e01 <+166>: mov %edx,%ecx
0x08048e03 <+168>: cmp %esi,%eax
0x08048e05 <+170>: jne 0x8048df8 <phase_6+157>
#第四块(无嵌套)
0x08048e07 <+172>: movl $0x0,0x8(%edx)
0x08048e0e <+179>: mov $0x5,%esi
0x08048e13 <+184>: mov 0x8(%ebx),%eax
0x08048e16 <+187>: mov (%eax),%eax
0x08048e18 <+189>: cmp %eax,(%ebx)
0x08048e1a <+191>: jge 0x8048e21 <phase_6+198>
0x08048e1c <+193>: call 0x804907d <explode_bomb>
0x08048e21 <+198>: mov 0x8(%ebx),%ebx
0x08048e24 <+201>: sub $0x1,%esi
0x08048e27 <+204>: jne 0x8048e13 <phase_6+184>
还原成C代码
里面的注释是对注释上面的代码作用说明。
typedef struct NODE{
int val;
int num;
struct NODE *next;
}node;
void phase_6(char* input , node *head){
int numbers[6]; //开辟空间
read_six_numbers(input,numbers);
int i=0;
node * sorted[7];
while(1){
unsigned int number = numbers[i];
number--;
//巧妙利用无符号负数比正数大的方法排除负数,如果是0,减去1变成负数,能排除零
if(number>5){
explode_bomb();
}
//限制输入的范围在1~5 为了下面的数组遍历操作
i++;
if(i == 6){break;}
for(int j=i;j<=5;j++){
if(numbers[j] == numbers[j-1]){
explode_bomb();
}
}
//确保输入的6个数字不重复
}
for(int j=0; j!=6 ;j++){
int number = numbers[j];
int k=1;
node *p = head->next;
if(number > 1){
do{
p=p->next;
k++;
}while(number != k);
}
sorted[number] = p;
}
//按我们的输入把对应位置的链表节点重新排序 存放在数组里
head->next = NULL;
node *q=sorted;
node *p = head;
node *temp;
do{
temp = ++q;
p->next = temp;
p = temp;
}while(p->next != NULL);
//按数组构建新链表
i=5;
q=head->next;
p=q->next;
while(i--){
if(p->val >= q->val){
q=p;
p=p->next;
}else{
explode_bomb();
}
}
//遍历链表 如果链表节点不是递增的 会爆炸
}
后记
感谢你看到这里!