异常处理流程是学习逆向中至关重要的一环,我们试图揭开异常处理流程的神秘面纱。
调试器:OllyDbg
环境:WIN XP SP3 虚拟机
异常处理的本质是一个外部中断。中断是计算机硬件的一个重要概念,这里分享一个百度百科的解释给不清楚的朋友参考。
https://baike.baidu.com/item/%E4%B8%AD%E6%96%AD/3933007?fr=aladdin
具体讲,就是在遇到异常时,程序会跳出正常的程序执行流程,执行一段远在程序代码之外的代码,这段代码叫SEH(
Structured Exception Handling
)结构化异常处理函数。顾名思义是处理异常的专用函数。很多壳会利用系统的这一特性实现反调试,可谓是别出心裁了。只要有想法,任何一个不起眼的函数或者机制都可以摇身一变成为反调试的利器。本例就是如此
初次配置反调试插件时,首先忽略所有异常,用插件把FindWindow和IsDebuggerPresent过掉,保存,重启,让插件生效。
载入OD,发现运行以后停下来,显示无法处理的异常,很奇怪,明明忽略所有的异常,为什么还会因为异常被断下?
遇到这情况,需要明白异常区前都执行了什么函数
载入程序,首先来了解一些,SetUnhandleExeptionFilter函数的作用
翻译第二段:当这个函数调用完成后,如果一个未调试器调试的进程有异常发生后,异常会让这个进程运行结构化异常处理函数(SEH),这个函数(原文是filter,不是function。所以这里是意译)会调用由
lpTopLevelExceptionFilter所指定的异常处理函数。
所以这个函数很关键很关键很关键。
这个函数怎么用呢?我们看一下。
#include <windows.h>
long __stdcall callback(_EXCEPTION_POINTERS* excp)
{
MessageBox(0,"Error","error",MB_OK);
printf("Error address %x/n",excp->ExceptionRecord->ExceptionAddress);
printf("CPU register:/n");
printf("eax %x ebx %x ecx %x edx %x/n",excp->ContextRecord->Eax,
excp->ContextRecord->Ebx,excp->ContextRecord->Ecx,
excp->ContextRecord->Edx);
return EXCEPTION_EXECUTE_HANDLER;
}
int main(int argc,char* argv[])
{
SetUnhandledExceptionFilter(callback);
_asm int 3 //只是为了让程序崩溃
return 0;
}
由此可见,只要调用过一次这个函数,以后再出现异常时,就调用这个函数设置好的异常处理函数。
查看函数列表,有这个函数,下断(请思考,如果没有该怎么办呢?)
该函数的参数很重要,它要做的事很重要
断下,看堆栈:
Cal到SetUnhandledExceptionFilter来自00401005
Ptoplevelfilter=00401108
返回到7c81776f
给00401108设置断点bp 401108,这个参数就是
lpTopLevelExceptionFilter 。它是一个专门处理异常的函数的首地址,有异常就执行这个函数。
然而这里401108不是处理异常的,一点异常处理的功能都没有,由于无法处理,系统就自动退出了,实现了反调试。作者很有想法,他的思路值得我们借鉴。
SetUnhandledExceptionFilter
这个函数的返回值有三种情况:
EXCEPTION_EXECUTE_HANDLER equ 0x1,翻译:“
UnhandledExceptionFilter 函数执行完毕后得到这一返回值,并且执行事先关联好的异常句柄。这通常(usually)会导致程序终止”
EXCEPTION_CONTINUE_EXECUTION
equ -1 “
UnhandledExceptionFilter 函数执行完毕后得到这一返回值 ,并且从异常的代码那里继续执行。”
EXCEPTION_CONTINUE_SEARCH
equ 0 执行
UnhandledExceptionFilter 规定的一般异常处理流程。也就是执行默认异常流程。
这个call不轻易调用,必须得有异常,而且没人管,而且没有进行调试,就会调用SetUnhandledExceptionFilter,没调试时出现异常,没人管时,调用异常处理函数。
接下来,我们给UnhandledExceptionFilter设置断点。每次设置API断点都记住要注释,然后再去查询一下这个函数啥作用
给zwqueryinformationprocess
设置断点
。这三个函数就是函数处理的流程。
SetUnhandledExceptionFilter会调用UnhandledExceptionFilter,UnhandledExceptionFilter调用zwqueryinformationprocess。zwqueryinformationprocess用来检测程序是否被调试,重点是第三个,决定了程序运行还是不运行 。通过故意制造异常,可以调用SetUnhandledExceptionFilter。
首先,我们来看看
zwqueryinformationprocess函数是什么
其中
Infoclass参数的作用如图
具体到本例中
Hprocess=FFFFFFFF 可无视
Infoclass=7 表示函数的功能是:检测程序是否在被调试。关于这个参数上图的说明是:
“抽取一个双字长度的数值。这个数值是调试该进程的端口数。一个非零值表明这个进程正在被一个三环的调试器调试中。 想实现这一功能最好使用 CheckRemoteDebuggerPresent或IsDebuggerPresentfunction.”
Buffer=0012f6a0 这里存放了结果,在数据窗口跟随,取一个dword,一开始里面的数是0,当执行到段位后变为FFFFFFFF,只要不是0就表示在被调试。
Bufsize=4 取四个字节
Preqsize=null 可无视
这里我们补充一点。第三函数可以不依赖前两函数时被单独调用,可以检测是否在调试程序哦
如何过检测呢?我们需要借助插件。网络上有很多其他的优秀插件,所以其他的插件也可以。
怎么配置呢?先来到hidedebugger,给unhandle exception tricks打上勾(屏蔽zwqueryinformationprocess)给terminate process也打上勾
再打开hideod-option,在这里执行两个操作
第一除了unhandleexceptonfilter不勾其他的都勾上,第二zwqueryinformationprocess要选择method1它可让那个buffer里的数永远保持00 00 00 00.也就是说,本来有调试器那里是ff ff 但是这次永远是0。但注意
zwqueryinformationprocess还有很多其他作用,不要因为使用插件而导致其他功能无法使用。
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界
最后于 2018-5-11 12:29
被r0Cat编辑
,原因: