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.

私は何を間違っていますか?最初のコードと 2 番目のコードの違いは何ですか?

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.
---------------------------

ただし、例外ハンドラーによるものではありません。おそらく、OS はスタックベースではない例外登録レコードを無視します (OS はスタックの最小値と最大値を知っているため、簡単に実行できます)。

于 2011-08-09T10:07:21.017 に答える
2

最初のコードでTSehは、実行可能ファイルの DATA グローバル セクションにありますが、2 番目のコードではそれをスタックに格納します。

これは違いがある私見です。_EXCEPTION_REGISTRATION_RECORD 構造体はおそらくスタック上にあるはずです。正直なところ、理由はわかりません (低レベルの SS レジスター トリックですか?)。

例外を発生させるには、ゼロごとの除算や nil 絶対アドレスへのアクセスなどを試してください。

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 に答える