Replace

发布日期:2019年03月13日 类别:reversing 题目来源:reversing.kr 题目链接:http://reversing.kr/challenge.php

打开程序后弹出了一个对话框,文本框中似乎只能输入数字,输入错误的话程序会直接退出,有时还会崩溃:

dialog

点击此处显示 Writeup
点击此处隐藏 Writeup

用 cutter 打开,查看一下字符串,发现一个“Correct!”。再查看一下交叉引用,能够看出这个函数应该是对话框的消息处理回调函数:

我们感兴趣的地方从 0x0040105a 这里开始。程序首先调用了 GetDlgItemInt 将文本框中的文本按照整数读出,将其存入 0x004084d0中;而后又调用了一个函数,紧接着是两个 jmp。此时如果搜索下面压入“Correct”字符串处的地址 0x00401073,会发现并没有地方直接引用它,很奇怪。不管它了,还是先看一下 fcn.0040466f 处的函数吧:

首先这个函数第一句调用了 0x0040467a。从旁边的一列地址中我们可以发现,这个地址并不在 cutter 显示出来的这些地址里。这是由于 x86 是变长指令集所导致的——任意地址都可以认为是一条指令的开始。也就是说,如果我们认为 0x00404674 地址为一条指令的开始,那么它就是 cutter 显示出来的 add dword [0x4084d0], 0x601605c7 这一句,这条指令的长度为 8 个字节;可是我们同样也可以认为 0x0040467a 是一条指令的开始,CPU 同样可以从这里继续执行,只不过此时 cutter 没有正确地把指令显示出来而已。

我们可以在 0x00404674 上右键选择“Set to Data”,选择 “…”:

set_to_data

然后在弹出的对话框中填入 6(即 0x0040467a - 0x00404674),让 r2 将 0x00404674 处的字节解释为长度为 6 字节的数据,从而使得它能够从 0x0040467a 处开始继续反汇编指令。这样这个函数就变成了:

0x0040467a 是一个 mov,这个地址似乎没什么用,这条指令也不影响程序的逻辑,可以忽略。接下来需要注意的是,call fcn.00404689 直接 call 了下一行指令 0x00404689,这里即将输入的数字加一;ret 返回后再次回到了 0x00404689,又执行了一次加一操作,而后函数才返回。此时我们已经将输入加 2 了。

返回后就返回到了 0x0040466f 的下一个字节处,即此时 0x00404674 就是新一条指令的开始了。我们需要在 cutter 里面把刚才的“Set to Data”取消掉——在 0x00404674 上右键,选择“Set as Code”即可:

这样继续执行的话,0x00404674 处将我们的输入加上了 0x601605c7。随后是几条不影响程序逻辑的指令,然后又到了 0x00404684 这里。刚才我们已经分析过了,这几行代码会将我们的输入加 2。

因此总结一下,fcn.0040466f 这个函数会把我们输入的数字加上 2 + 0x601605c7 + 2

回到刚才的消息处理函数中,调用了这个函数后,清空 eax,然后又 jmp 到了 loc.00404690处:

首先第一行是把我们的数据读入至 eax。第二行压入了一个立即数,通过后续分析发现倒数第三行 pop eax 将这个数拿回了 eax,但是随后重新设置了 eax 的值,因此这个 push 也没什么用。第三行调用了 fcn.00404689 将我们输入的数字加一,但要注意我们刚才已经将这个数字读出至 eax,因此这一次调用不会影响到 eax 的值。

第四行 mov dword [fcn.0040466f], 0xc39000c6,这条指令将 0040466f 处的数据做了修改,这不就是我们刚才看的函数吗?也就是说,这一句代码在运行时改变了 0x0040466f 处的汇编指令。首先看看没改动之前的情况:

before

我们把从 0x0040466f 开始的四个字节修改为 0xc6 0x00 0x90 0xc3,然后重新用 cutter 打开查看反汇编:

这个函数的作用就变为了向 eax 处写入 nop

继续向下看到第五行:

调用了 fcn.0040466f,随后 eax 加一,再调用一次 fcn.0040466f。也就是说,我们连续把从 eax 开始的两个字节都写成了 nop。要记得,eax 中就是根据我们输入的数字计算而得的。

再往下就是把 fcn.0040466f 恢复原状,eax 置 -1 后,跳转到了 fcn.00401071

我们看一下 fcn.00401071

刚好在我们的 Correct 上方,一个 jmp 就跳走了。联系刚才那段逻辑可以想到,我们完全可以将 0x00401071 处的两个字节修改为 nop,这样就能够输出 Correct 了。因此我们输入的数字应为 0x00401071 - (2 + 0x601605c7 + 2) = 0xa02a0aa6

这里还需要注意的是,由于 GetDlgItemInt 的返回类型是 UINT,因此应该以无符号整数解释 0xa02a0aa6。所以只需要在对话框中填入2687109798,程序就能够显示“Correct”了,这串数字即为 flag。

dontpan1c 的 CTF 笔记
南阳一出即相,淮阴一出即将。

知识共享许可协议

本站所有作品均采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

本站不包含明令禁止公开解题过程的题目。

本站由 Jekyll 强力驱动。