一道简单的RE迷宫题

admin 2025-12-14 23:00:48 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 这篇文章详细分析了一个简单的RE逆向工程迷宫题,通过IDA静态分析Linux程序,理解其输入验证机制和迷宫移动逻辑。文章展示了如何识别文件类型、分析主函数逻辑、理解输入格式要求、还原迷宫布局,并解释了四种字符对应不同方向移动的原理。这是一篇实用的逆向工程教程,适合CTF初学者学习二进制安全分析技巧。 综合评分: 88 文章分类: 逆向分析,CTF,二进制安全


cover_image

一道简单的RE迷宫题

Calparrot

看雪学苑

2025年10月19日 18:00 上海

首先使用工具查看文件信息,是Linux系统下的一个文件,而且没有打包,很正常的程序。

不想动态分析啊,动态分析很依赖操作系统的嘛,用IDA查看文件。函数也很简单,一个初始化跳转到main函数,然后就可以分析函数内容了。其实说是逆向还是在这里主要看代码。这里为了方便逐一分析代码就直接粘贴过来了。

__int64 __fastcall main(__int64 a1, char **a2, char **a3){  const char *v3; // rsi  signed __int64 v4; // rbx  signed int v5; // eax  char v6; // bp  char v7; // al  const char *v8; // rdi  __int64 v10; // [rsp+0h] [rbp-28h]  v10 = 0LL;  puts("Input flag:");  scanf("%s", &s1, 0LL);

这里可以看到要求用户输入,存储在s1里面,0LL是一个多余的参数,可以不用管。

  if ( strlen(&s1) != 24 || (v3 = "nctf{", strncmp(&s1, "nctf{", 5uLL)) || *(&byte_6010BF + 24) != 125 )                这里处理用户输入,主要的判断条件有三个:                1.判断输入长度是否为24,                2.将v3赋值为”nctf{“,把s1的前五个字符与”nctf{“进行比较,其中5uLL是一个参数,表示前5,无符号,LongLong型,                3.&byte_6010BF + 24地址处的内容是否为”}“,也就是ASCII码125。  {LABEL_22:    puts("Wrong flag!");                前面条件不符就直接失败了啊。    exit(-1);  }  v4 = 5LL;                v4被赋值为5,LL表示5是一个LongLong型。  if ( strlen(&s1) - 1 > 5 )  {    while ( 1 )    {      v5 = *(&s1 + v4);                这里就比较好懂了,接下来要从用户输入的第6个字符开始分析。      v6 = 0;      if ( v5 > 78 )                      {        v5 = (unsigned __int8)v5;         为什么v5要做这么一个操作呢,v5在这里要确保字符在这里被解析成一个无符号字节,如果是负的可能有意外结果。        if ( (unsigned __int8)v5 == 79 )                一、O        {          v7 = sub_400650((char *)&v10 + 4, v3);          goto LABEL_14;        }        if ( v5 == 111 )                二、o        {          v7 = sub_400660((char *)&v10 + 4, v3);          goto LABEL_14;        }      }      else      {        v5 = (unsigned __int8)v5;        if ( (unsigned __int8)v5 == 46 )                三、.        {          v7 = sub_400670(&v10, v3);          goto LABEL_14;        }        if ( v5 == 48 )                四、0        {          v7 = sub_400680((int *)&v10);LABEL_14:          v6 = v7;          goto LABEL_15;        }         }

上面标注序号的地方就是逐个处理用户输入的字符的判断条件,对应了四种字符。每个条件后跟随的语句都对v7进行赋值,然后跳转至LABLE_14,先看v7是什么。

v7的赋值都调用了一个sub_什么什么的函数,随便点一个进去看看。

bool __fastcall sub_400650(_DWORD *a1){  int v1; // eax  v1 = (*a1)--;  return v1 > 0;}

这里还不知道是干什么的,但是大概可以看出来这里的返回值是一个布尔值,先往下走查看v7发挥了什么作用,也就是每一次调用的那个LABLE_14。在LABLE_14中,v7的值被给了v6,随后跳转至LABLE_15。这里可以看到有一个跳转是返回了之前报错失败的地方,大概能猜到这里是一个错误处理。

LABEL_15:      v3 = (const char *)HIDWORD(v10);      if ( !(unsigned __int8)sub_400690(asc_601060, HIDWORD(v10), (unsigned int)v10) )        goto LABEL_22;

接下来就是一个用户输入的移动,因为v4就相当于一个指针,先查看有没有到输入的末尾,然后再根据v6(也就是上面的v7)来再次做一个判断,如果上面的v7是TRUE就能接着执行,否则就输出”Wrong flag!“。

      if ( ++v4 >= strlen(&s1) - 1 )      {        if ( v6 )          break;LABEL_20:        v8 = "Wrong flag!";        goto LABEL_21;      }    }  }  if ( asc_601060[8 * (signed int)v10 + SHIDWORD(v10)] != 35 )    goto LABEL_20;

这个if条件很关键,它告诉了v10的一些信息,这里取了v10的低32位((signed int)v10强制转换为32位有符号整数,也就是低32位)和v10的高32位(SHIDWORD(v10)取有符号v10的高32位),而这整个表达式8 * (signed int)v10 + SHIDWORD(v10)就是一个二维数组的索引计算,这就能猜到了,asc_601060其实就是储存着迷宫信息的二维数组,而在这里知道了v10(前面sub_什么什么的参数)也就能猜出v7储存的就是移动操作的返回值,上面的每一个if下控制的就是不同按键的移动方式。

  v8 = "Congratulations!";LABEL_21:  puts(v8);  return 0LL;}

现在大概能知道整个程序是在干嘛了,查看asc_601060信息,因为8 * (signed int)v10 + SHIDWORD(v10)式子,也能知道每8个字符一行。

这个就是查看的信息,还原迷宫就是:

  ******

*   *  *

*** * **

**  * **

*  *#  *

** *** *

**     *

********

格式问题,有点不好看啊,把这个迷宫走出来就能得到flag了,不过至于上面每个字符对应的是往哪里走,可以结合v10的意义来判断,有两个参数是有v10+4的,大概可以判断操作的是y轴方向,所以对应的是上下移动,另外两个就是左右移动了,具体的还需要进入函数内部分析。就比如sub_400650,里面有一个–操作,y轴数值减小向上移动,也就是上移动函数,接下来几个也是同理。

#

看雪ID:Calparrot

https://bbs.kanxue.com/user-home-1032907.htm

*本文为看雪论坛优秀文章,由 Calparrot 原创,转载请注明来自看雪社区

报名中!看雪·第九届安全开发者峰会(SDC 2025)

往期推荐

无”痕”加载驱动模块之傀儡驱动 (上)

为 CobaltStrike 增加 SMTP Beacon

隐蔽通讯常见种类介绍

buuctf-re之CTF分析

物理读写/无附加读写实验

球分享

球点赞

球在看

点击阅读原文查看更多


评论:0   参与:  4