「不一样的 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」 题解