IDA反汇编的结果:
看上去确实挺草的,一堆goto和奇怪的函数,再加上题目名为迷宫,感觉很害怕
不过其实goto的结构并不混乱,函数也很简单
四个函数总共有两个功能:++x<8和x-->0
四个goto其实是每次执行完函数之后就直接跳到指定位置,把函数返回值保存下来
那么相应的,可以把v6和v9改个名,改成mark和mark2,方便观察
(善用ida的改名和注释功能啊,不要偷懒,不改完全没法看,但是改着改着就懂了)
分支结构清楚了之后,可以看一下循环结构,其实就是从第5个字符开始,一直到倒数第二个字符
相当于把'nctf{'和'}'拿掉,只考虑里边包着的部分,串长24,还剩18个未知字符
那么v4可以改成i,v5=*(&s1 + v4)可以改成ci
接下来仔细看循环代码的细节
可以看到,四个分治结构分别在ci等于四个值的时候进入,把4个值转成char可以发现是四个非常有趣的字符
'o', 'O', '0', '.'
针不辍……
当进入字符对应的操作后,会让v10或者v10的下一个字节+1或-1(不知道为啥,点进函数前是v10,但是如果进去看一眼就变成v9了,第二个参数v3也无了==)
那么&v10和&v10+1相当于两个计数器,把它们分别叫做cnt和cnt2
循环代码的最后出现了一个奇怪的字符串,只由'*', ' ', '#'组成,把它称为wbwb
那么如果wbwb[cnt+8*cnt2]=='*',就会跳转到Wrong flag,我们可以把这里的label重命名为gg
此外,当所有内容串遍历完了之后,如果mark==0(cnt和cnt2不在0-7的范围内)或者wbwb[cnt+8*cnt2]!='#'也会gg
那至此这个程序的意图就很明显了,通过一个长度为18的'oO0.'串,操控一个指针在字符串上移动,使得避开'*'的情况下到达'#'
整理完后的反汇编代码是这样的:
(这里的HIDWORD和SHIDWORD是取cnt这个64位int的高32位,其实和(int *)&cnt + 1是一样的)
状态数看上去不是很多,可以写个搜索爆破
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 char s[110]; 6 int m=0; 7 int q[110]; 8 int n=18; 9 void dfs(int x,int y,int z,int w){ 10 if(w<0 || w>=m) return ; 11 if(s[w]=='*') return ; 12 if(x==n+1){ 13 if(s[w]!='#') return ; 14 for(int i=1;i<=n;++i){ 15 char tmp=' '; 16 if(q[i]==1) tmp='o'; 17 else if(q[i]==2) tmp='O'; 18 else if(q[i]==3) tmp='0'; 19 else tmp='.'; 20 printf("%c",tmp); 21 } 22 printf(" "); 23 return ; 24 } 25 q[x]=1; 26 dfs(x+1,y+1,z,w+1); 27 q[x]=2; 28 dfs(x+1,y-1,z,w-1); 29 q[x]=3; 30 dfs(x+1,y,z+1,w+8); 31 q[x]=4; 32 dfs(x+1,y,z-1,w-8); 33 } 34 int main(){ 35 char ch=getchar(); 36 while(ch!=' '){ 37 s[m++]=ch; 38 ch=getchar(); 39 } 40 dfs(1,0,0,0); 41 return 0; 42 }
跑得很快,只有一解,这就是flag
做完这题之后看了一下攻防世界的WP,里边说这其实就是个8*8的迷宫,cnt+-1是往上下走,cnt2+-1是往左右走,'*'不能走,'#'是目标
现在我终于知道题面里的迷宫是指啥了
原来这题这么简单的吗囧