roger 发表于 2019-6-11 07:49:51

看雪CTFQ2晋级赛第一题神秘来信解题Wirteup

这道题目主要考察异常处理机制,题目刚发布的时候出现多解,我现在分析的是作者修复后的。1. 查壳,无壳的C++程序。2. 直接载入OD,看到了异常处理需要的两个函数,SetUnhandledExceptionFilter和UnhandledExceptionFilter。SetUnhandledExceptionFilter函数的唯一一个参数为异常处理函数指针。当程序发生异常时,且程序不处于调试模式(在VS或者其他调试器里运行)则首先调用该异常处理函数。因此,程序可以主动抛出一个异常来判断当前程序是否正在被调试3. 我们现在给SetUnhandledExceptionFilter和UnhandledExceptionFilter这两个函数设置断点,运行程序。断在了SetUnhandledExceptionFilter的入口处。我们看下堆栈的情况,正如你所看到的异常处理函数入口地址为00401C8E,我们在命令栏中输入BP 00401C8E给该函数设置断点。4. 异常处理函数前面已经设置过了,所以我们将对SetUnhandledExceptionFilter设置的断点删除掉,运行程序,找到获取输入的函数,输入序列号401353(篇幅有限,不演示踩坑,这里直接输入的是真码)。5. 输入的长度必须小于7位才可以继续运行6. 限定真码的后三位为3537. 限定前三位的ascii十六进制之和为0x95,也就是十进制的149,我一次做的时候猜的是212353和230353。8. 把输入的字符串直接转换成整数放到esi9. 在0040134E处CALL 00401354,我们知道call是一个组合的指令,call 00401354相当于push 00401353然后jmp 00401354,先压入下一行地址作为返回地址,然后再跳转。但是00401354处执行了pop eax,这样就导致返回地址丢失了,这个程序回不到00401353这个地方了。10. 输入正确这里会触发除零异常,然后走异常处理程序,这里就可以看出程序的这部分代码应该是用内联汇编写的,可见作者的良苦用心,如果错误就继续向下执行,提示error,并且程序卡死,因为最后那条jmp是跳向自己的死循环。11. 异常处理过程将会断在系统默认的异常处理函数入口处,因为程序有异常发生,并且当前程序正在被调试,所以,并不会首先调用程序之前设置的入口为00401C8E的异常处理函数,而异常转交给调试器处理了,而调试器也无法处理该异常,所以最终调用系统默认的异常处理函数UnhandledExceptionFilter来处理,这个函数里会内嵌ZwQueryInformationProcess函数来检测是否有调试器。12. 该函数通过将InfoClass参数设置为7,将可以获取到当前进程是否被调试的信息,该信息将保存在Buffer参数指向的缓冲区中。如果该缓冲区返回的是FFFFFFFF的话表示当前程序正在被调试,我们将其四个字节全修改为零就表示没有调试器了。因为我使用的原版OD,没有任何插件,所以这里要改,如果有strongOD等插件,这里插件会帮你过了反调试。这里看一下程序的整个流程注意:1. 此程序直接用IDA可能无法直接F5转换伪代码,因为堆栈不平衡,ida无法正常判断程序的流程,这里需要手动平衡堆栈(用od把pop去掉一个再用ida的F5)2. 此程序涉及到异常处理,最好用不带插件的原版OD分析,OD需要设置忽略异常。
题目下载:
页: [1]
查看完整版本: 看雪CTFQ2晋级赛第一题神秘来信解题Wirteup