12

我试图在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.

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

4

3 回答 3

6

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

几年前,我在编写微线程库(http://www.eternallines.com/microthreads)时发现/阅读了这个。

于 2011-08-09T16:14:21.980 回答
4

不能使用test过程作为异常回调函数,因为异常回调函数有不同的原型。阅读Matt Pietrek 文章,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.
---------------------------

但不是由您的异常处理程序。可能操作系统会忽略不基于堆栈的异常注册记录(操作系统可以轻松做到这一点,因为它知道最小和最大堆栈值)

于 2011-08-09T10:07:21.017 回答
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中拦截异常,看看这篇文章。事实上,Delphi 在 Windows 上的 SEH 上添加了一些自定义层。

还要注意Win64 模式下的异常处理变化。升级到 Delphi XE2 时值得一读。

于 2011-08-09T11:01:53.773 回答