广西CTF省赛之labyrinth
本帖最后由 yechen123 于 2018-11-10 13:24 编辑这应该是在比赛中分值最高的一道逆向题了
先上传题目
链接: https://pan.baidu.com/s/1dMbescIDsS8x9UPVhXfijA 提取码: x47w
当时是在比赛中下午看的题目 脑子很乱 英语也不好 看到labyrinth第一个反应是出生或者出生日期
然后整个思路就乱了在回来之后也简单看了下 发现还是很乱就放弃了 果然还是自己太菜了
昨晚无聊查了下labyrinth发现时迷宫的意思就想着今天下午再解一下
当知道是迷宫的意思后再看代码有种豁然开朗的感觉
果然逆向一开始还是要把握一个整体啊
写个文章记录一下自己学习的过程可能会有错误 希望各位师傅不要嫌弃
开始
查壳 无壳
ida打开
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) ) // 输入参数
{
sub_401005((int)argv);
return 0;
}
printf("You get lost!");
}
return 0;
}
发现是命令行下带参数输入不然会自动关闭软件
memset(byte_42AE58, -1, 0x1E4u);
sub_40100A();
sub_40100F();
这三个主要是生成一个迷宫
主要代码在sub_401019((char *)argv)中
再看看sub_401005((int)argv);
int __cdecl sub_401650(int a1)
{
char v2; //
char v3; //
int i; //
for ( i = 0; i < 48; ++i )
v2 = byte_427A98 ^ *(_BYTE *)(a1 + i % 16);// 16个字节
v3 = 0;
return printf("%s\n", v2);
}
主要是根据输入的值与内存的值异或输出flag
(a1 + i % 16)这里可以猜测输入是16位的字符
最后看最主要的代码sub_401019((char *)argv)
BOOL __cdecl sub_4013A0(char *input)
{
char *input_strpy; //
int lie; //
signed int v4; //
int input_two_ascii; //
int v6; //
int local.6; //
signed int v8; //
int hang; //
signed int input_one_ascii; //
int v11; //
int local.1; //
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 - 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;
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; // 9
break;
default:
break;
}
byte_42AE58 = 0;
local.1 = (v4 == v8) + (v6 ^ 1) + local.6;
input_strpy = input;
LABEL_19:
input_one_ascii = input_strpy;
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不能等于v8v6必须为1
达到这样的条件 才能得到flag
好了 现在说主要代码
走迷宫的话 得控制方向吧可能是awds 或者是abcd这里是abcd
v4 = v8;
switch ( input_one_ascii )
{
case 97:
v8 = 0;
hang = (hang - input_two_ascii) % 22;
v6 = byte_42AE58; // 里面都是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; // 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也就是迷宫的出口 迷宫的入口在最左上角的地方
其实这个迷宫有些值是随机的 但是如果能解题 有一些值肯定是固定的!
写个脚本把每次生成的迷宫对比一下
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 == twos and ones!='00' and ones!='00\n':
#print ones
print str(count)+':'+str(i)
ones = f.readline()
twos = g.readline()
count += 1
标红的地方 就是固定的01的值
可以推测步数
ok得到key为dpbucmandtbidhbo
解题结束
后记
其实从后边的输出函数逆推可能会得到flag
上次以为开头的是flag{格式的 尝试逆推推出Congratulations!you_are_a 等字符 后来感觉有点乱放弃了有兴趣的师傅可以尝试一下
随机迷宫 有点强! 师傅牛批{:1_893:} 给楼主点赞 lihaohua 发表于 2018-11-9 18:21
师傅牛批
师傅比较厉害 表示从头看不懂,大神给点入门的方法。比如要买啥书之类的?
给楼主点赞 师傅比较厉害 请收下我的膝盖 有点意思啊