「不一样的 flag」 题解
不一样的flag
info
题目来源
不一样的flag.
解题过程
首先查找一下,没发现程序有壳,那便直接使用反编译工具直接获得程序的代码.
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
注意到第 13 行 qmemcpy(&v3, _data_start__, 0x19u); 不必多想,直接得出结论,程序将 _data_start__ 复制到了 v3 的位置.查看 _data_start__ 存放的内容为:*11110100001010000101111# 含有 0x19 个字符(不计算 \0).
不必担心 char v3; 无法存储 _data_start__ 导致程序错误的问题,_data_start__
可以看到 v3 与 v4 间留有巨大的空隙,从 v3 到 v4 的空间恰好能存储整个字符串(除了 \0).
剩下的一切就不是那么的困难了,仔细看代码会发现输入的值(即v6)只能为 1、2、3、4,其余的值均会导致程序执行 exit(1); (即 exit(EXIT_FAILURE);).继续看代码可发现一个对应关系
| 方向 | 输入的值 | 变量变化 |
|---|---|---|
| Up | 1 | --v4 |
| Down | 2 | ++v4 |
| Left | 3 | --v5 |
| Right | 4 | ++v5 |
可发现,这正对应着一个「以竖直向下为 x 轴正方向、以水平向右为 y 轴正方向」的平面直角坐标系,即有 :「v4 代表 x 轴坐标 ,v5 代表 y 轴坐标」.
考察第 51 行、第 53 行代码,会发现 49=='1' 并且 35=='#' .然后分析看 *((_BYTE *)&v8 + 5 * v4 + v5 - 41) ,_BYTE 表示 1 byte 也就是 char 类型,那也就是 *((char *)&v8 + 5 * v4 + v5 - 41) ,看 v8 的地址是 esp+40h,那么 (char *)&v8 - 41 就是 esp + 40h - 41 == esp + 17h,也就是 &v3.那么就得到了最终的结论,*((_BYTE *)&v8 + 5 * v4 + v5 - 41) 就是 v3[5 * v4 + v5].
刚才说过,「v4 代表 x 轴坐标 ,v5 代表 y 轴坐标」.还记得 *11110100001010000101111# 含有 0x19 个字符(不计算 \0)吗?
| 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|
| 5 | 6 | 7 | 8 | 9 |
| 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 |
以 0 作为坐标原点建立「以竖直向下为 x 轴正方向、以水平向右为 y 轴正方向」,「v4 代表 x 轴坐标 ,v5 代表 y 轴坐标」就会发现 5 * v4 + v5 的值正是上标中格子的序号.而 5 * v4 + v5 正是 v3 的索引值.
这说明 v3 存储的实际上是一个被压扁地图,也就是下表中的模样.
| * | 1 | 1 | 1 | 1 |
|---|---|---|---|---|
| 0 | 1 | 0 | 0 | 0 |
| 0 | 1 | 0 | 1 | 0 |
| 0 | 0 | 0 | 1 | 0 |
| 1 | 1 | 1 | 1 | # |
分析第 53 行代码,if ( *((_BYTE *)&v8 + 5 * v4 + v5 - 41) == 35 )在刚才的分析中已经明白其等价于 if (v3[5 * v4 + v5] == '#') ,只有满足该条件才会执行 exit(0);(即 exit(EXIT_SUCCESS);).那么本题的分析就全部结束了,只需要寻找按照全为 0 的路径移动即可到达 # 的位置.那么就可以轻松的得到 flag 为 222441144222.当然也还需要用 flag{} 包上,得到最终答案 flag{222441144222}.
「不一样的 flag」 题解