
一、笔记说
前两天在逆向一个小程序时看到一个顶层异常处理函数的用法很有意思,涉及的知识点也很多,对于逆向入门的人来说还是有很多启发的。我之前在学习异常与调试时也没有学习顶层异常处理函数的相关内容,所以想借助这个CM小程序加深对顶层异常处理函数的理解和使用。
我们知道当一个异常发生时,操作系统将向发生异常的线程的栈中压入三个结构体:
EXCEPTION_RECORD
CONTEXT
EXCEPTION_POINTERS
EXCEPTION_RECORD结构包含了关于抛出异常的信息,CONTEXT保存了寄存器上下文,EXCEPTION_POINTERS结构包含两个数据成员,它们分别为压入栈中的EXCEPTION_RECORD结构指针和CONTEXT结构指针。
其次,如果异常过滤程序返回EXCEPTION_CONTINUE_SEARCH时,系统会继续在调用树中的上层寻找异常处理程序,如果进程未注册VEH、SEH、VCH、UEF等异常处理程序,又会发生什么呢?
在这种情况下,我们会遇到所谓的异常未处理函数SetUnhandledExceptionFilter,这个函数也给了我们最后处理异常的机会。
二、平台环境
系统:Window10
分析工具:IDA(32位)
程序:CrackMe.exe
三、实例笔记
(一)本次逆向目标:
如下图所示,此程序为MFC窗口程序,要我们分析反汇编代码找到密码字符串,点击check按钮,验证输入的字符是否正确。

(二)分析思路:
动态分析:
在OD中打开CM.exe,运行起来随便输入字串,点击check,看看能找到什么。
很遗憾,一无所获,还触发了异常,不明所以。
如下图所示:

点击关闭程序,OD会停留在中断发生的位置,如下图所示:

那为什么会断在这?有经验的人会知道这是触发了访问异常,对于我们初学者来说不知道发生了什么那我们就用IDA来详细分析下它的反汇编代码,看看发生了什么?
静态分析:
1)在IDA中打开程序后如下图所示,你会发现代码段内容很少,点击DialogFunc进入窗口回调函数。

2)添加vc32rtf签名。
3)修改变量名称、消息类型名称。
方法:选中变量值单击右键选择——Use standard symbolic constant(快捷键M)打开符号列表。


修改后如下图所示,可以看出在调用GetDlgItemTextA函数之后,触发了访问异常,双击401113这个地址,会发现这个地址在代码段。

4)所以得出一个结论:无论输入什么字符串都会触发异常,并调用异常处理函数:
异常处理函数调用顺序:
是否有内核调试器——是否有用户调试器——是否注册异常处理函数。
如果VEH、SEH、VCH、UEF都没有注册,则检查顶层异常处理函数。
5)跟踪代码发现在初始化对话框消息中注册了顶层异常处理函数,如下图所示:

6)双击TopLevelExceptionFilter进入顶层异常处理定义函数,如下图所示。这部分数据是定义在.data段,并未识别为代码,需要我们手动将其转换为代码,并封装为函数(先按C、再按P)。


7)解析顶层异常调用函数:
先熟悉以下异常结构体字段。

分析顶层异常函数代码实现,如下图示:

对于修改CONTEXT字段中,寄存器偏移量可采用以下办法得出:用寄存器字段地址 - 结构体地址。

在看懂了以上代码以后,你会发现,异常处理函数是修改CONTEXT结构体中的EDI、ESI、EIP字段,并恢复EXCEPTION_CONTINUE_EXECUTION,继续执行。

至此,我们找到了密码字串,也明白了利用异常处理函数修改线程上下文原理。
四、学习总结
这个小程序其实代码量非常小,但挖了很多坑,如果对基础知识掌握不好的话,还是不容易分析的。
主要涉及的知识点有:
了解异常处理机制;
了解顶层异常处理函数;
恢复数据段中的代码;
异常结构体;
准确分析汇编代码意义。异常处理机制对提高程序的稳定性和对抗反调试手段都很有帮助,更多异常相关知识请参阅《Windows核心编程》《加密解密》。
附录SetUnhandledExceptionFilter函数注解,点击文末“原文链接”即可获取。
原文作者:MoonU
原文链接:https://bbs.pediy.com/thread-247623.htm
更多阅读:
1、[原创]代替创建用户线程使用ShellCode注入DLL的小技巧
网友评论