前言
- 这是一个另类的迷宫模型。在我第一次拿到手上分析的时候,因为没有注意到程序的名字,尽管代码量很小,输入和校验都比较集中,也能看懂,但依然是不知道该怎么逆。花了一天的时间,后面搜了一下 “labyrinth” 才知道是 迷宫、曲径 的意思。再结合程序的代码,一下子就确定了算法的模型——迷宫。
- 为何说它另类,且看代码。
代码


- 在初始化函数中,随机生成了一张迷宫图。但是解是唯一的,说明迷宫图中有些地方是固定的。多运行几下,把每次生成的图做个比较,最后发现迷宫图大致如下:
{0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0}
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
{0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0}
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0}
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
{0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0}
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
{0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1}
- 据经验,走的肯定是1的位置,但问题来了,与以往不同的地方就是1不是连续成若干条路径的。
- 带着这个疑惑,再来分析程序的Check流程:
int __cdecl sub_4013A0(char *a1)
{
char v2; // [sp+Ch] [bp-74h]@1
int v3; // [sp+4Ch] [bp-34h]@11
char *input; // [sp+50h] [bp-30h]@1
int v5; // [sp+54h] [bp-2Ch]@23
int v6; // [sp+58h] [bp-28h]@14
int v7; // [sp+5Ch] [bp-24h]@11
int v8; // [sp+60h] [bp-20h]@8
int v9; // [sp+64h] [bp-1Ch]@4
int v10; // [sp+68h] [bp-18h]@3
int v11; // [sp+6Ch] [bp-14h]@3
int column; // [sp+70h] [bp-10h]@3
int ch1; // [sp+74h] [bp-Ch]@3
int row; // [sp+78h] [bp-8h]@3
int v15; // [sp+7Ch] [bp-4h]@3
memset(&v2, 0xCCu, 0x74u);
input = a1;
if ( strlen(a1) & 1 )
return 0;
ch1 = *a1;
v15 = 0;
row = 0;
column = 0;
v11 = -1;
v10 = 0;
if ( !*a1 )
goto end;
v9 = 0;
do
{
if ( ch1 < 'a' || ch1 > 'd' )
goto LABEL_10;
v8 = input[1] - 101;
if ( v8 > 21 )
{
input = a1;
LABEL_10:
++v15;
goto LABEL_19;
}
v7 = v11;
v3 = ch1 - 97;
switch ( ch1 )
{
case 'a': // 左
v11 = 0;
column = (column - v8) % 22;
v9 = *(&maze[22 * row] + column);
break;
case 'b': // 右
v11 = 0;
column = (column + v8) % 22;
goto LABEL_17;
case 'c': // 上
v6 = row - v8;
goto LABEL_16;
case 'd': // 下
v6 = row + v8;
LABEL_16:
v11 = 1;
row = v6 % 22;
LABEL_17:
v9 = *(&maze[22 * row] + column);
break;
default:
break;
}
*(&maze[22 * row] + column) = 0;
v15 = (v7 == v11) + (v9 ^ 1) + v10;
input = a1;
LABEL_19:
ch1 = input[2];
input += 2;
v10 = v15;
a1 = input;
}
while ( ch1 );
if ( row != 21 || column != 21 )
end:
++v15;
v5 = v15 <= 0;
return v15 <= 0;
}
- 根据经验,可以分辨出 abcd 控制的是什么方向,然后从解密输出flag的函数中,可以确定输入的字符串长度为 16 字节。

- 回到Check函数中,根据逻辑,可以断定每次都必须走到 1 的位置,并且每次只能走 直线,不能有 折角,最后出口在 (21,21) 处,共走 8 个点。因此,可以确定走的路径就是

- 坐标序列:{11,0},{11,16} ,{3,16},{3,7 },{18,7},{18,11},{21,11},{21,21}
脚本
#include<iostream>
using namespace std;
struct Point {
int x;
int y;
};
int main()
{
Point p[] = { {11,0},{11,16} ,{3,16},{3,7 },{18,7},{18,11},{21,11},{21,21} };
int r = 0, c = 0;
string ans="";
char ch;
for (int i = 0; i < 8; i++) {
if (p[i].x - r>0) {//下
cout << "d" << (char)(101 + p[i].x - r);
r = p[i].x;
}
else if(p[i].x - r < 0){//上
cout << "c" << (char)(101 - (p[i].x - r));
r = p[i].x;
}
else if(p[i].y - c > 0)//向右
{
cout << "b" << (char)(101 + (p[i].y - c));
c = p[i].y;
}
else if (p[i].y - c < 0)//向左
{
cout << "a" << (char)(101 - (p[i].y - c));
c = p[i].y;
}
}
cout << endl;
return 0;
}
- 得到key:dpbucmandtbidhbo

总结
- 在没有知道这个算法模型的情况下,逆了一天也逆不出来。究其原因就是思考的方向不对,凭空很难靠到正确思路上。说明思维很重要,像我智商不高的人做逆向主要是靠经验,这又是一次新的经验!
版权声明:本文为qq_41252520原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。