测试文件下载:https://adworld.xctf.org.cn/media/task/attachments/fa4c78d25eea4081864918803996e615
1.准备
获得信息
- 64位文件
2.IDA打开
选择main函数,反编译为C代码。
1 __int64 __fastcall main(__int64 a1, char **a2, char **a3) 2 { 3 const char *v3; // rsi 4 signed __int64 v4; // rbx 5 signed int v5; // eax 6 char v6; // bp 7 char v7; // al 8 const char *v8; // rdi 9 __int64 v10; // [rsp+0h] [rbp-28h] 10 11 v10 = 0LL; 12 puts("Input flag:"); 13 scanf("%s", &s1, 0LL); 14 if ( strlen(&s1) != 24 || (v3 = "nctf{", strncmp(&s1, "nctf{", 5uLL)) || *(&byte_6010BF + 24) != 125 ) 15 { 16 LABEL_22: 17 puts("Wrong flag!"); 18 exit(-1); 19 } 20 v4 = 5LL; 21 if ( strlen(&s1) - 1 > 5 ) 22 { 23 while ( 1 ) 24 { 25 v5 = *(&s1 + v4); 26 v6 = 0; 27 if ( v5 > 78 ) 28 { 29 v5 = (unsigned __int8)v5; 30 if ( (unsigned __int8)v5 == 79 ) 31 { 32 v7 = sub_400650((char *)&v10 + 4, v3); 33 goto LABEL_14; 34 } 35 if ( v5 == 111 ) 36 { 37 v7 = sub_400660((char *)&v10 + 4, v3); 38 goto LABEL_14; 39 } 40 } 41 else 42 { 43 v5 = (unsigned __int8)v5; 44 if ( (unsigned __int8)v5 == 46 ) 45 { 46 v7 = sub_400670(&v10, v3); 47 goto LABEL_14; 48 } 49 if ( v5 == 48 ) 50 { 51 v7 = sub_400680(&v10, v3); 52 LABEL_14: 53 v6 = v7; 54 goto LABEL_15; 55 } 56 } 57 LABEL_15: 58 v3 = (const char *)HIDWORD(v10); 59 if ( !(unsigned __int8)sub_400690(asc_601060, HIDWORD(v10), (unsigned int)v10) ) 60 goto LABEL_22; 61 if ( ++v4 >= strlen(&s1) - 1 ) 62 { 63 if ( v6 ) 64 break; 65 LABEL_20: 66 v8 = "Wrong flag!"; 67 goto LABEL_21; 68 } 69 } 70 } 71 if ( asc_601060[8 * (signed int)v10 + SHIDWORD(v10)] != 35 ) 72 goto LABEL_20; 73 v8 = "Congratulations!"; 74 LABEL_21: 75 puts(v8); 76 return 0LL; 77 }
打开字符串的视图,我们可以看到
.data:0000000000601060 asc_601060 db ' ******* * **** * **** * *** *# *** *** *** *********',0
2.1 代码分析
1 __int64 __fastcall main(__int64 a1, char **a2, char **a3) 2 { 3 signed __int64 v4; // rbx 4 signed int v5; // eax 5 char v6; // bp 6 char v7; // al 7 const char *v8; // rdi 8 __int64 v10; // [rsp+0h] [rbp-28h] 9 10 v10 = 0LL; 11 puts("Input flag:"); 12 scanf("%s", &s1, 0LL); 13 14 15 if ( strlen(&s1) != 24 || strncmp(&s1, "nctf{", 5uLL) || *(&byte_6010BF + 24) != 125 ) //len(s1) = 24, s1开头为'nctf', 16 { 17 LABEL_22: 18 puts("Wrong flag!"); 19 exit(-1); 20 } 21 v4 = 5LL; //flag总长24,已知5个长度,v4表示输入的表示方向的字符数。 22 if ( strlen(&s1) - 1 > 5 ) 23 { 24 while ( 1 ) 25 { 26 v5 = *(&s1 + v4); //v5 = s1[5] 27 v6 = 0; 28 if ( v5 > 'N' )//v5 > 'N' 29 { 30 v5 = (unsigned __int8)v5; 31 if ( (unsigned __int8)v5 == 'O' ) //1. 当v5 == 'O'的情况,表示左移 32 { 33 v7 = sub_400650((char *)&v10 + 4, v3); //v10--,表示位置的列数-1,v7判断是否越界(越界返回:FALSE, 未越界返回:TRUE) 34 goto LABEL_14; 35 } 36 if ( v5 == 'o' ) //2. 当v5 == 'o‘的情况,表示右移 37 { 38 v7 = sub_400660((char *)&v10 + 4, v3); //v10++,表示位置的列数+1, 39 goto LABEL_14; 40 } 41 } 42 43 else//v5 < 'N' 44 { 45 v5 = (unsigned __int8)v5; 46 if ( (unsigned __int8)v5 == '.') //3. v5 == '.'的情况, 表示上移 47 { 48 v7 = sub_400670(&v10, v3); //v10--,表示位置的行数-1, 49 goto LABEL_14; 50 } 51 if ( v5 == '0' ) //4. v5=='0'的情况,表示下移 52 { 53 v7 = sub_400680(&v10, v3); //v10++, 表示位置的行数+1 54 LABEL_14: 55 v6 = v7; //赋值给v6,下面判断走迷宫时,是否越界 56 goto LABEL_15; 57 } 58 } 59 60 61 LABEL_15: 62 if ( !(unsigned __int8)sub_400690((__int64)asc_601060, SHIDWORD(v10), v10) )//判断该位置的字符是' ', '#'或者'*'中的哪种,如果是'*'则,返回再次寻路 63 goto LABEL_22; 64 if ( ++v4 >= strlen(&s1) - 1 ) //因为最后以'}'结尾,所以strlen(s1) - 1。我们需要找到中间18个字符 65 { 66 if ( v6 )//判断是否越界(未越界退出循环) 67 break; 68 LABEL_20: 69 v8 = "Wrong flag!"; 70 goto LABEL_21; 71 } 72 } 73 } 74 if ( asc_601060[8 * (signed int)v10 + SHIDWORD(v10)] != 35 )//判断是否到达'#'处,没有到达,继续返回循环,进行移动 75 goto LABEL_20; 76 v8 = "Congratulations!"; 77 LABEL_21: 78 puts(v8); 79 return 0LL; 80 }
通过代码分析,能够判断出那一串字符实际上就是一个迷宫,通过'.', '0', 'O', 'o'来控制移动,V10实际上就是一个二维数组,一维表示X轴,二维表示Y轴。
2.2 方向判断
在汇编代码中
.text:00000000004006B7 mov dword ptr [rsp+28h+var_28+4], 0 .text:00000000004006BF mov dword ptr [rsp+28h+var_28], 0
我们能够知道,v9高位表示x轴,v9低位表示y轴,
通过函数
__int64 __fastcall sub_400690(__int64 a1, int a2, int a3) { __int64 result; // rax result = *(unsigned __int8 *)(a1 + a2 + 8LL * a3);//a1是迷宫,a2是列,a3是行。通过行列计算迷宫对应位置的字符 LOBYTE(result) = (_DWORD)result == 32 || (_DWORD)result == 35;//如果是' '或者'#‘返回TRUE,是'*’返回FALSE return result; }
sub_400690函数中是a2+a3*8,即a3表示行,a2表示列。返回到调用函数处,即v10高位表示列,低位表示行。那么就可以判断出'O','o'表示左右,'.','0'表示上下
2.3 迷宫表示
通过a2+a3*8我们也能了解到这个迷宫有8行,总共64个字符,也就是8x8的迷宫,表示出来就是。
00****** *000*00* ***0*0** **00*0** *00*#00* **0***0* **00000* ********
2.4 解迷宫
起点(1,1),只能走0,要走到‘#’,找到路径右下右右下下左下下下右右右右上上左左
写出脚本
str = "右下右右下下左下下下右右右右上上左左" str = str.replace('上', '.') str = str.replace('下', '0') str = str.replace('左', 'O') str = str.replace('右', 'o') str = 'nctf{' + str + '}' print(str)
3.get flag!
nctf{oo0oo00O000oooo..OO}