工具:IDA、pwndbg
看到maze就知道又要走迷宫
静态分析
附件拖进IDA中分析主函数
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rax
int v5[52]; // [rsp+0h] [rbp-270h] BYREF
int v6[52]; // [rsp+D0h] [rbp-1A0h] BYREF
int v7[7]; // [rsp+1A0h] [rbp-D0h] BYREF
int v8; // [rsp+1BCh] [rbp-B4h]
int v9; // [rsp+1C0h] [rbp-B0h]
int v10; // [rsp+1C4h] [rbp-ACh]
int v11; // [rsp+1C8h] [rbp-A8h]
int v12; // [rsp+1CCh] [rbp-A4h]
int v13; // [rsp+1D0h] [rbp-A0h]
int v14; // [rsp+1D4h] [rbp-9Ch]
int v15; // [rsp+1D8h] [rbp-98h]
int v16; // [rsp+1DCh] [rbp-94h]
int v17; // [rsp+1E0h] [rbp-90h]
int v18; // [rsp+1E4h] [rbp-8Ch]
int v19; // [rsp+1E8h] [rbp-88h]
int v20; // [rsp+1ECh] [rbp-84h]
int v21; // [rsp+1F0h] [rbp-80h]
int v22; // [rsp+1F4h] [rbp-7Ch]
int v23; // [rsp+1F8h] [rbp-78h]
int v24; // [rsp+1FCh] [rbp-74h]
int v25; // [rsp+200h] [rbp-70h]
int v26; // [rsp+204h] [rbp-6Ch]
int v27; // [rsp+208h] [rbp-68h]
int v28; // [rsp+20Ch] [rbp-64h]
int v29; // [rsp+210h] [rbp-60h]
int v30; // [rsp+214h] [rbp-5Ch]
int v31; // [rsp+218h] [rbp-58h]
int v32; // [rsp+21Ch] [rbp-54h]
int v33; // [rsp+220h] [rbp-50h]
int v34; // [rsp+224h] [rbp-4Ch]
int v35; // [rsp+228h] [rbp-48h]
int v36; // [rsp+22Ch] [rbp-44h]
int v37; // [rsp+230h] [rbp-40h]
int v38; // [rsp+234h] [rbp-3Ch]
int v39; // [rsp+238h] [rbp-38h]
int v40; // [rsp+23Ch] [rbp-34h]
int v41; // [rsp+240h] [rbp-30h]
int v42; // [rsp+244h] [rbp-2Ch]
int v43; // [rsp+248h] [rbp-28h]
int v44; // [rsp+24Ch] [rbp-24h]
int v45; // [rsp+250h] [rbp-20h]
int v46; // [rsp+254h] [rbp-1Ch]
int v47; // [rsp+258h] [rbp-18h]
int v48; // [rsp+25Ch] [rbp-14h]
int v49; // [rsp+260h] [rbp-10h]
v7[0] = 1;
v7[1] = 1;
v7[2] = -1;
v7[3] = 1;
v7[4] = -1;
v7[5] = 1;
v7[6] = -1;
v8 = 0;
v9 = 0;
v10 = 0;
v11 = 0;
v12 = 1;
v13 = -1;
v14 = 0;
v15 = 0;
v16 = 1;
v17 = 0;
v18 = 0;
v19 = 1;
v20 = 0;
v21 = -1;
v22 = -1;
v23 = 0;
v24 = 1;
v25 = 0;
v26 = 1;
v27 = -1;
v28 = 0;
v29 = -1;
v30 = 0;
v31 = 0;
v32 = 0;
v33 = 0;
v34 = 0;
v35 = 1;
v36 = -1;
v37 = -1;
v38 = 1;
v39 = -1;
v40 = 0;
v41 = -1;
v42 = 2;
v43 = 1;
v44 = -1;
v45 = 0;
v46 = 0;
v47 = -1;
v48 = 1;
v49 = 0;
memset(v6, 0, 0xC0uLL);
v6[48] = 0;
memset(v5, 0, 0xC0uLL);
v5[48] = 0;
Step_0((int (*)[7])v7, 7, (int (*)[7])v6);
Step_1((int (*)[7])v6, 7, (int (*)[7])v5);
v3 = std::operator<<<std::char_traits<char>>(&_bss_start, "Please help me out!");
std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
Step_2((int (*)[7])v5);
system("pause");
return 0;
}
前面定义了一大堆变量,直接看后面,有三个关键函数Step_0、Step_1、Step_2。
__int64 __fastcall Step_0(int (*a1)[7], int a2, int (*a3)[7])
{
__int64 result; // rax
int j; // [rsp+20h] [rbp-8h]
unsigned int i; // [rsp+24h] [rbp-4h]
for ( i = 0; ; ++i )
{
result = i;
if ( (int)i >= a2 )
break;
for ( j = 0; j < a2; ++j )
(*a3)[7 * i + j] = (*a1)[7 * j + a2 - i - 1];
}
return result;
}
函数Step_0是将数组v7做变换后的结果放在v6中
__int64 __fastcall Step_1(int (*a1)[7], int a2, int (*a3)[7])
{
int v5[7]; // [rsp+20h] [rbp-D0h] BYREF
int v6; // [rsp+E4h] [rbp-Ch]
int j; // [rsp+E8h] [rbp-8h]
int i; // [rsp+ECh] [rbp-4h]
v6 = getA(a1, a2);
if ( !v6 )
return 0LL;
getAStart(a1, a2, (int (*)[7])v5);
for ( i = 0; i < a2; ++i )
{
for ( j = 0; j < a2; ++j )
(*a3)[7 * i + j] = v5[7 * i + j] / v6;
}
return 1LL;
}
函数Step_1是将数组v7做变换后的结果放在v5中,
__int64 __fastcall Step_2(int (*a1)[7])
{
int v1; // eax
__int64 v2; // rax
__int64 v3; // rax
__int64 result; // rax
__int64 v5; // rax
char v6[35]; // [rsp+10h] [rbp-30h] BYREF
char v7; // [rsp+33h] [rbp-Dh] BYREF
int v8; // [rsp+34h] [rbp-Ch]
int v9; // [rsp+38h] [rbp-8h]
int v10; // [rsp+3Ch] [rbp-4h]
v10 = 0;
v9 = 0;
v8 = 0;
while ( v8 <= 29 && (*a1)[7 * v10 + v9] == 1 )
{
std::operator>><char,std::char_traits<char>>(&std::cin, &v7);
v1 = v8++;
v6[v1] = v7;
if ( v7 == 100 ) // 右 'd'
{
++v9;
}
else if ( v7 > 100 )
{
if ( v7 == 115 ) // 下 's'
{
++v10;
}
else // 上 'w'
{
if ( v7 != 119 )
goto LABEL_14;
--v10;
}
}
else if ( v7 == 97 ) // 左 'a'
{
--v9;
}
else
{
LABEL_14:
v2 = std::operator<<<std::char_traits<char>>(&_bss_start, "include illegal words.");
std::ostream::operator<<(v2, &std::endl<char,std::char_traits<char>>);
}
}
if ( v10 == 6 && v9 == 6 )
{
v3 = std::operator<<<std::char_traits<char>>(&_bss_start, "Congratulations!");
std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
output(v6, v8);
result = 1LL;
}
else
{
v5 = std::operator<<<std::char_traits<char>>(&_bss_start, "Oh no!,Please try again~~");
std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>);
result = 0LL;
}
return result;
}
函数Step_2是游戏的过程,每输入一个字符,就要判断一次。根据传进来的参数是v5和7可知,迷宫的起点在v5[0]。
根据while循环体判断条件
v8 <= 29 && (*a1)[7 * v10 + v9] == 1
可知最多走30步,迷宫大小是7x7且v10控制着列,v9控制着行,只能走1的位置。
根据while循环体中的几个判断语句
if ( v7 == 100 ) // 右 'd'
{
++v9;
}
else if ( v7 > 100 )
{
if ( v7 == 115 ) // 下 's'
{
++v10;
}
else // 上 'w'
{
if ( v7 != 119 )
goto LABEL_14;
--v10;
}
}
else if ( v7 == 97 ) // 左 'a'
{
--v9;
}
输入的字符只能是‘wsad’并且分别表示上下左右。
根据
if ( v10 == 6 && v9 == 6 )
{
v3 = std::operator<<<std::char_traits<char>>(&_bss_start, "Congratulations!");
std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
output(v6, v8);
result = 1LL;
}
可知迷宫终点在v5[6*7+6=48]处。
动态调试
因为v5是迷宫路径,而生成v5的函数Step_0和Step_1太复杂,所以直接看函数的运行结果。
由main函数中v5的定义
int v5[52]; // [rsp+0h] [rbp-270h] BYREF
可知v5存放在函数栈中,rsp指向的位置。
在pwndbg中调试此文件
$ gdb easy_Maze
.......
在Step_1函数处打上断点
pwndbg> b Step_1
Breakpoint 1 at 0x1590
运行文件
pwndbg> r
.......
返回主函数
pwndbg> finish
.......
此时Step_1函数已经执行完成,查看v5的内容
- x:查看内存
- 49:49个单元
- d:按照10进制查看
- w:四个字节为一个单元(int)
- $rsp:内存地址在rsp中
复制下来并以7*7的样式展示
maze = [
1, 0, 0, 1, 1, 1, 1,
1, 0, 1, 1, 0, 0, 1,
1, 1, 1, 0, 1, 1, 1,
0, 0, 0, 1, 1, 0, 0,
1, 1, 1, 1, 0, 0, 0,
1, 0, 0, 0, 1, 1, 1,
1, 1, 1, 1, 1, 0, 1]
根据规则 只走1,一步一步走
得到ssddwdwdddssaasasaaassddddwdds
然后运行原来的文件走就能得到flag。
也可以查看函数,然后找到成功标志"Congratulations!"的位置
发现了UNCTF{}
所以最后的flag为UNCTF{ssddwdwdddssaasasaaassddddwdds}