-
-
[看雪CTF.TSRC 2018 团队赛][分析] 第二题 半加器
-
发表于: 2018-12-3 23:30 2790
-
题目给出了一个 32 位控制台 Windows 应用程序 Exam.exe
:
看起来没有什么保护,它是一个 MSVC 2017 编译的 C/C++ 程序。
运行一下:
看起来就是一个经典的输入 flag,输出对错的挑战。
简单用 IDA Pro 分析一下,顺着 MSVCRT 的初始化逻辑脑补一下,可以找到 main 在 0x4A19B0 处。
逻辑大致如下:
仅仅检查了长度是否大于等于10,小于30,以及 0-based index 下 [7]
处是否是 'A'。
若不是的话则报错,这很奇怪,因为按照规则,若答案正确的话应该输出明确的正确提示。
尝试输入满足以上条件的串 0123456A89
,程序确实没有任何输出。
显然,程序中存在一些隐藏的检查 flag 的逻辑。观察可以发现程序没有写文件和外部环境的行为,因此可以排除规则中所允许的“重启验证”,flag 应该就在本次执行中被验证了。考虑到输入的来源就这么两处,只要死咬着输入(和被变换过的输入)不放,肯定能找到处理 flag 的地方。
在下一个教科书般的内存读取断点之前,让我们先试试查找到 g_input
和 g_p_buf
的交叉引用,结果发现在 main
和 MangleInput
以外另有两个函数 0x495810 (命名为 InitBuffer
) 和 0x49DC80 (命名为ValidateFlag
)引用到了 g_input
。
这两个函数的逻辑也很简单:
看起来,这里才是真正的检查 flag 的地方,注意到与之比较的标准答案来自于函数参数,寻找交叉引用得到该参数:
至此,已经可以计算出输入惹:
提交即可。可以看出来多出来的那个判断 [7]
的地方是为了对付规则中的 “flag只能包含字母和数字”。
然而,题目里的谜团还没有彻底解开,这个检查 flag 的函数到底是在哪里调用的呢?
继续追踪交叉引用,可以找到是在 0x4957B0 这个函数里调用 atexit()
注册成退出时执行的函数的。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!