旧调重弹-SetUnhandledExceptionFilter的使用问题
kongfoo/2005.12.19

. Delphi中使用SetUnhandledExceptionFilter的问题

  这几天在写一个测试程序,包括使用SetUnhandledExceptionFilter来
反OD调试,关于SetUnhandledExceptionFilter的反调试原理之前(一年前了哈)
simonzh2000等人都有详细讲述,这里就不多讲了,在Delphi里面要用这个
函数却有点问题,先看看代码:

program SetUnhandledExceptionFilterTest;
uses Windows;

procedure Handler;
begin
  MessageBox(Hwnd(0),'Final SEH','.',0);
  asm
    mov eax,1
    retn
  end;
end;

begin
  SetUnhandledExceptionFilter(@Handler);
  asm
    xor edx,edx
    idiv edx
  end;
end.

  很简单的一个程序,但得到的效果却不是预期之中的,在弹出'Final SEH'
之后又弹出一个Runtime error。如果在窗体之中的按钮事件里面写这样的测试
代码的话,效果更加是“离奇”,弹出一个“除0错”之后就没事发生了,根本
没有去Handler里面。
  究其原因,Delphi编译出来的程序有自己的一套用SEH建立起来的异常处理
机构,是自动的,在程序启动之初就建立起来了。就连上面这样简单的程序也一
样,在SetUnhandledExceptionFilter之前就建立了一个SEH结构。如果要达到预
期效果:发生异常时去到我们的Handler里面,然后就完事了,我们就要先清掉
Delphi建立的SEH。再来看看代码:

program SetUnhandledExceptionFilterTest;
uses Windows;

procedure Handler;
begin
  MessageBox(Hwnd(0),'Final SEH','.',0);
  asm
    mov eax,1
    retn
  end;
end;

begin
  //首先要清掉SEH
  asm
   @Loop:
    mov eax,fs:[0]
    cmp dword ptr [eax],$FFFFFFFF
    je @SEHCleared
    mov eax,[eax]
    mov fs:[0],eax
    jmp @Loop
   @SEHCleared:
  end;
  SetUnhandledExceptionFilter(@Handler);
  asm
    xor edx,edx
    idiv edx
  end;
end.

  现在程序按照我们的想法运行了。在窗体的按钮OnClick事件中使用也正常。
SetUnhandledExceptionFilter在高级语言中应用时应该要注意一下这个问题了。
特别是用高级语言写壳用到这个反调试技巧时。另外说一说在异常处理函数返回
值为-1时的问题。hume在《SEH in ASM研究》中的资料说到:

EXCEPTION_EXECUTE_HANDLER equ 1 表示我已经处理了异常,可以优雅地结束了 
EXCEPTION_CONTINUE_SEARCH equ 0 表示我不处理,其他人来吧,于是windows调用默认的处理程序显示一个错误框,并结束 
EXCEPTION_CONTINUE_EXECUTION equ -1 表示错误已经被修复,请从异常发生处继续执行 

  网上搜到一些关于SetUnhandledExceptionFilter的文章演示的都是返回值
为1的情况,而在反调试当中,这种情况是没用的,因为返回到系统之后程序就
结束了,有用的是返回为-1的情况。具体设置和我们平时的SEH异常处理函数类
似,在到达SetUnhandledExceptionFilter指定的异常处理函数之后,[[esp+4]+4]
处就是CONTEXT STRUCT。看代码可以更清晰(测试环境:winxp sp2):

//这里是用SetUnhandledExceptionFilter设置的异常处理函数
@Handler:
  mov eax,[esp+4]
  mov eax,[eax+4]
  add dword ptr [eax+0B8],2 //异常指令2个字节长,加2指向下一条指令。eax指向CONTEXT STRUCT。
  mov eax,-1
  retn

//设置代码:
  push offset @Handler
  call SetUnhandledExceptionFilter
  xor edx,edx
  idiv edx
  nop //可以在这里下断测试(当然,OD要patch UnhandledExceptionFilter)。

  OK,收工。