结构化异常处理程序和Delphi

时间:2011-08-09 09:46:44

标签: delphi exception-handling assembly seh

我正在尝试设置SEH而不使用try except
(这是我个人的知识,可以更好地了解SEH的工作原理)

以下代码不起作用

type
    TSeh = packed record
    OldSeh:DWORD;
    NewSeh:DWORD;
    end;


procedure test;
begin
WriteLn('Hello from seh');
end;


var
    eu:TSeh;
    old_seh:DWORD;
begin
    asm
    mov eax,fs:[0]
    mov old_seh,eax
    end;
    eu.OldSeh := old_seh;
    eu.NewSeh := DWORD(@test);
    asm
        mov eax,offset eu
        mov fs:[0],eax
        ret //This will cause an exception because jumps on an invalid memory address
    end;
end.

但这确实

procedure test;
begin
WriteLn('Hello from seh');
end;



begin
    asm
    push offset test
    push fs:[0]
    mov fs:[0],esp
    ret //This will cause an exception because jumps on an invalid memory address
    end;
end.

我做错了什么?第一个代码和第二个代码有什么区别?

3 个答案:

答案 0 :(得分:6)

Windows要求所有堆栈帧都在系统分配的堆栈内。它还要求堆栈帧在堆栈上按顺序排列。此外,对于异常处理,它要求所有“异常记录”都在堆栈中,并且要求它们链接到 通过堆栈内存顺序排序。

几年前我在编写一个微线程库(http://www.eternallines.com/microthreads)时想到了这个/读过这个。

答案 1 :(得分:4)

您不能将test过程用作异常回调函数,因为异常回调函数具有不同的原型。阅读Matt Pietrek article,IMO是有关Win32 SEH的最佳信息来源。


<强>更新

对于进一步的调查,我建议在代码中进行以下更改,以使问题更加清晰:

function test: Integer;
begin
  WriteLn('Hello from seh');
  Result:= 0;
end;

(因为异常回调应该在EAX中返回整数值)

并为第一个代码段

begin
    asm
        mov eax,fs:[0]
        mov old_seh,eax
    end;
    eu.OldSeh := old_seh;
    eu.NewSeh := Cardinal(@test);
    asm
        lea eax, eu
        mov fs:[0],eax
        mov ds:[0],eax //This will cause an AV exception
    end;
end.

现在您看到异常正确处理为:

---------------------------
Debugger Fault Notification
---------------------------
Project C:\Users\Serg\Documents\RAD Studio\Projects\Project13.exe faulted with
message: 'access violation at 0x004050f5: write of address 0x00000000'. Process
Stopped. Use Step or Run to continue.
---------------------------

但不是您的异常处理程序。操作系统可能会忽略非基于堆栈的异常注册记录(操作系统可以很容易地执行此操作,因为它知道最小和最大堆栈值)

答案 2 :(得分:2)

对于第一个代码,TSeh位于可执行文件的DATA全局部分,而第二个代码将其存储在堆栈中。

这是恕我直言,差异在于。 _EXCEPTION_REGISTRATION_RECORD结构可能应该在堆栈上。不知道为什么,老实说(一些低级SS注册技巧?)。

要引发异常,您最好尝试类似于零除法或访问零绝对地址的内容:

PInteger(nil)^ := 0; // will always raise an exception

asm
  xor eax,eax
  mov [eax],eax // will always raise an exception
end;

关于如何拦截Delphi中的异常,请查看at this article。实际上,Delphi通过Windows在SEH上添加了一些自定义层。

还要注意exception handling changes in Win64 mode。值得一读的是Delphi XE2。