maze writeup
攻防世界的一道迷宫题,第一次接触这样的题,个人感觉很有意思,收获也挺多,做一篇笔记记录一下。
程序分析
__int64 sub_4006B0() { signed __int64 v0; // rbx signed int v1; // eax bool v2; // bp bool v3; // al const char *v4; // rdi __int64 v6; // [rsp+0h] [rbp-28h] v6 = 0LL; puts("Input flag:"); scanf("%s", &s1, 0LL); if ( strlen(&s1) != 24 || strncmp(&s1, "nctf{", 5uLL) || *(&byte_6010BF + 24) != 125 )// flag长度为24字节,前五个字节为"nctf{",最后一个字节要是"}" { LABEL_22: puts("Wrong flag!"); exit(-1); } v0 = 5LL; if ( strlen(&s1) - 1 > 5 ) { while ( 1 ) { v1 = *(&s1 + v0); // 对每一位进行判断 v2 = 0; if ( v1 > 78 ) { v1 = (unsigned __int8)v1; if ( (unsigned __int8)v1 == 'O' ) // "O"表示向左移动 { v3 = sub_400650((__int64)&v6 + 4); goto LABEL_14; } if ( v1 == 'o' ) // "o"表示向右移动 { v3 = sub_400660((__int64)&v6 + 4); goto LABEL_14; } } else { v1 = (unsigned __int8)v1; if ( (unsigned __int8)v1 == '.' ) // "."表示向上移动 { v3 = sub_400670((__int64)&v6); goto LABEL_14; } if ( v1 == '0' ) // "0"表示向下移动 { v3 = sub_400680((__int64)&v6); LABEL_14: v2 = v3; goto LABEL_15; } } LABEL_15: if ( !(unsigned __int8)sub_400690((__int64)asc_601060, SHIDWORD(v6), v6) )// asc_601060是字符串首地址 goto LABEL_22; if ( ++v0 >= strlen(&s1) - 1 ) { if ( v2 ) break; LABEL_20: v4 = "Wrong flag!"; goto LABEL_21; } } } if ( asc_601060[8 * (signed int)v6 + SHIDWORD(v6)] != '#' ) goto LABEL_20; v4 = "Congratulations!"; LABEL_21: puts(v4); return 0LL; }
主函数如上所示。我开始看的时候没有看明白,后来发现给出了四种操作符,对应上下左右四种移动,给出的字符串要想象成一个二维图像,相当于要用一个二维数组表示出来。
flag的格式已经给我们规定好了:nctf{......}。
__int64 __fastcall sub_400690(__int64 a1, int a2, int a3) { __int64 result; // rax result = *(unsigned __int8 *)(a1 + a2 + 8LL * a3); LOBYTE(result) = (_DWORD)result == 32 || (_DWORD)result == 35; return result; }
if ( !(unsigned __int8)sub_400690((__int64)asc_601060, SHIDWORD(v6), v6) )// asc_601060是字符串首地址 goto LABEL_22;
sub_400690函数这里拉出来看一下,SHIDWORD这里是一个宏定义,定义如下:
#define SHIDWORD(x) (*((int32*)&(x)+1))
对字符串如何取值或者对二级指针理解还不清楚的,可以尝试敲一下下面的测试代码:
#include<stdio.h> #include<string.h> int main() { __int64 v6; v6=1LL;char* a="abcdefghjk"; printf("%p\n",a); printf("%p\n",*(&a)); printf("%p\n",&a); printf("%c\n",*a); printf("%c",*(a+1)); return 0; }
宏定义和四种操作符理解不了,尝试下面的测试代码:
#include<stdio.h> #include<string.h> #define SHIDWORD(x) (*((__int32*)&(x)+1)) int main() { __int64 v6; v6=1LL; char* a="abcdefghjk"; printf("%p\n",&v6); printf("%p\n",(__int64)&v6); printf("%p\n",&v6+4); printf("%p\n",(__int64)&v6+4); printf("%p",&(SHIDWORD(v6))); return 0; }
sub_400690中,v6是行数,即y坐标,SHIDWORD(v6)是列数,即x坐标,(__int64)asc_601060是字符串首地址。后面的判断表示走的时候,只能走“#”和空格。
我们先把字符串转换成一个二维的迷宫
maze=" ******* * **** * **** * *** *# *** *** *** *********" x="" New_maze="" for a in maze: if a==" ": New_maze+="0" elif a=="*": New_maze+="1" else: New_maze+=a x="" for i in range(len(New_maze)): x+=New_maze[i] if (i+1)%8==0: print(x) x=""
然后从头开始,“O”表示向左移动,“o”表示向右移动,“.”表示向上移动,“0”表示向下移动(上下这里要注意,容易搞错)。四种操作符看不懂,就好好看一下上面给出的测试代码。
最后得到flag:nctf{o0oo00O000oooo..OO}。