姚小宝 发表于 2018-11-9 17:59

广西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 等字符 后来感觉有点乱放弃了有兴趣的师傅可以尝试一下


jimo 发表于 2018-11-9 18:08

随机迷宫 有点强!

lihaohua 发表于 2018-11-9 18:21

师傅牛批{:1_893:}

wangqiustc 发表于 2018-11-9 18:24

给楼主点赞

姚小宝 发表于 2018-11-9 18:55

lihaohua 发表于 2018-11-9 18:21
师傅牛批

师傅比较厉害

小宾是老子 发表于 2018-11-9 19:43

表示从头看不懂,大神给点入门的方法。比如要买啥书之类的?

回归自然 发表于 2018-11-9 21:07


给楼主点赞

yssun 发表于 2018-11-9 22:05

师傅比较厉害

单程线 发表于 2018-11-9 23:48

请收下我的膝盖

hshtzlove 发表于 2018-11-9 23:56

有点意思啊
页: [1] 2 3 4 5 6 7
查看完整版本: 广西CTF省赛之labyrinth