本帖最后由 yechen123 于 2018-11-10 13:24 编辑
这应该是在比赛中分值最高的一道逆向题了
先上传题目
labyrinth.rar
(31.14 KB, 下载次数: 65)
链接: https://pan.baidu.com/s/1dMbescIDsS8x9UPVhXfijA 提取码: x47w
当时是在比赛中下午看的题目 脑子很乱 英语也不好 看到labyrinth第一个反应是出生或者出生日期
然后整个思路就乱了 在回来之后也简单看了下 发现还是很乱就放弃了 果然还是自己太菜了
昨晚无聊查了下labyrinth发现时迷宫的意思 就想着今天下午再解一下
当知道是迷宫的意思后再看代码有种豁然开朗的感觉
果然逆向一开始还是要把握一个整体啊
写个文章记录一下自己学习的过程 可能会有错误 希望各位师傅不要嫌弃
开始
查壳 无壳
IDA打开
[Asm] 纯文本查看 复制代码 int __cdecl main_0(int argc, const char **argv)
{
if ( argc == 2 )
{
memset(byte_42AE58, -1, 0x1E4u);
sub_40100A();
sub_40100F();
if ( sub_401019((char *)argv[1]) ) // 输入参数
{
sub_401005((int)argv[1]);
return 0;
}
printf("You get lost!");
}
return 0;
}
发现是命令行下带参数输入 不然会自动关闭软件
[Asm] 纯文本查看 复制代码 memset(byte_42AE58, -1, 0x1E4u);
sub_40100A();
sub_40100F();
这三个主要是生成一个迷宫
主要代码在sub_401019((char *)argv[1])中
再看看sub_401005((int)argv[1]);
[Asm] 纯文本查看 复制代码 int __cdecl sub_401650(int a1)
{
char v2[48]; // [esp+4Ch] [ebp-38h]
char v3; // [esp+7Ch] [ebp-8h]
int i; // [esp+80h] [ebp-4h]
for ( i = 0; i < 48; ++i )
v2[i] = byte_427A98[i] ^ *(_BYTE *)(a1 + i % 16);// 16个字节
v3 = 0;
return printf("%s\n", v2);
}
主要是根据输入的值与内存的值异或输出flag
(a1 + i % 16)这里可以猜测输入是16位的字符
最后看最主要的代码sub_401019((char *)argv[1])
[Asm] 纯文本查看 复制代码 BOOL __cdecl sub_4013A0(char *input)
{
char *input_strpy; // [esp+50h] [ebp-30h]
int lie; // [esp+58h] [ebp-28h]
signed int v4; // [esp+5Ch] [ebp-24h]
int input_two_ascii; // [esp+60h] [ebp-20h]
int v6; // [esp+64h] [ebp-1Ch]
int local.6; // [esp+68h] [ebp-18h]
signed int v8; // [esp+6Ch] [ebp-14h]
int hang; // [esp+70h] [ebp-10h]
signed int input_one_ascii; // [esp+74h] [ebp-Ch]
int v11; // [esp+78h] [ebp-8h]
int local.1; // [esp+7Ch] [ebp-4h]
input_strpy = input;
if ( strlen(input) & 1 ) // 必须为偶数
return 0;
input_one_ascii = *input; // 第一个字符
local.1 = 0;
v11 = 0;
hang = 0;
v8 = -1;
local.6 = 0;
if ( !*input )
goto LABEL_22;
v6 = 0;
do
{
if ( input_one_ascii < 97 || input_one_ascii > 100 )// 如果不是abcd
goto LABEL_10;
input_two_ascii = input_strpy[1] - 101;
if ( input_two_ascii > 21 ) //
{
input_strpy = input;
LABEL_10:
++local.1;
goto LABEL_19;
}
v4 = v8;
switch ( input_one_ascii )
{
case 97:
v8 = 0;
hang = (hang - input_two_ascii) % 22;
v6 = byte_42AE58[22 * v11 + hang];
break;
case 98:
v8 = 0;
hang = (hang + input_two_ascii) % 22;
goto LABEL_17;
case 99:
lie = v11 - input_two_ascii;
goto LABEL_16;
case 100:
lie = v11 + input_two_ascii; // 2
LABEL_16:
v8 = 1;
v11 = lie % 22;
LABEL_17:
v6 = byte_42AE58[22 * v11 + hang]; // 9
break;
default:
break;
}
byte_42AE58[22 * v11 + hang] = 0;
local.1 = (v4 == v8) + (v6 ^ 1) + local.6;
input_strpy = input;
LABEL_19:
input_one_ascii = input_strpy[2];
input_strpy += 2;
local.6 = local.1;
input = input_strpy;
}
while ( input_one_ascii );
if ( v11 != 21 || hang != 21 )
LABEL_22:
++local.1;
return local.1 <= 0;
}
首先这个函数的最下面的是return local.1 <= 0;
如果想返回一个1 那么local.1必须小于等于0
再看看让local.1值变动的语句
LABEL_22:
++local.1;
LABEL_10:
++local.1;
local.1 = (v4 == v8) + (v6 ^ 1) + local.6;
local.6 = local.1;
这就代表不能执行label 22 和 10
也就是输入的字符在偶数位的字符串不能大于z
还有 这个条件不能成立
还有
local.1 = (v4==v8) + (v6^1) + local.6;
local.6 = local.1;
每次循环v4不能等于v8 v6必须为1
达到这样的条件 才能得到flag
好了 现在说主要代码
走迷宫的话 得控制方向吧 可能是awds 或者是abcd 这里是abcd
[Asm] 纯文本查看 复制代码 v4 = v8;
switch ( input_one_ascii )
{
case 97:
v8 = 0;
hang = (hang - input_two_ascii) % 22;
v6 = byte_42AE58[22 * v11 + hang]; // 里面都是1
break;
case 98:
v8 = 0;
hang = (hang + input_two_ascii) % 22;
goto LABEL_17;
case 99:
lie = v11 - input_two_ascii;
goto LABEL_16;
case 100:
lie = v11 + input_two_ascii; // 2
LABEL_16:
v8 = 1;
v11 = lie % 22;
LABEL_17:
v6 = byte_42AE58[22 * v11 + hang]; // 9
break;
default:
break;
}
这个思路是 每次从输入字符串中取两个字符
input_one_ascii为第一个 必须为abcd 控制方向
input_two_ascii为第二个 控制往该方向行走的路程
从代码看出 a是往左边 b是右边 c是上边 d是下边
还可以发现不管是上下左右 都有%22的代码 所以这是一个22*22的迷宫
还发现有v8=1 或者v8=0的代码 刚刚说了 v4!=v8 所以左右或者上下不能重复
比如说这次往左边走了 下次就只能往上下走了 以此类推
解析结束
看一下迷宫
每一次走到的位置必须是01 还有最后一步必须到达右下角的01 也就是迷宫的出口 迷宫的入口在最左上角的地方
其实这个迷宫有些值是随机的 但是如果能解题 有一些值肯定是固定的!
写个脚本把每次生成的迷宫对比一下
[Asm] 纯文本查看 复制代码 f = open('1.txt', 'r')
g = open('2.txt', 'r')
one = []
ones = ''
two = []
twos = ''
ones = f.readline()
twos = g.readline()
count = 0
while count<22:
ones = ones.split(' ')
twos = twos.split(' ')
for i in range(22):
if ones[i] == twos[i] and ones[i]!='00' and ones[i]!='00\n':
#print ones[i]
print str(count)+':'+str(i)
ones = f.readline()
twos = g.readline()
count += 1
标红的地方 就是固定的01的值
可以推测步数
ok 得到key为dpbucmandtbidhbo
解题结束
后记
其实从后边的输出函数逆推可能会得到flag
上次以为开头的是flag{格式的 尝试逆推 推出Congratulations! you_are_a 等字符 后来感觉有点乱放弃了 有兴趣的师傅可以尝试一下
|