Структурированный обработчик исключений и Delphi - PullRequest
12 голосов
/ 09 августа 2011

Я пытаюсь установить 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 ]

6 голосов
/ 09 августа 2011

Windows требует, чтобы все кадры стека находились внутри стека, выделенного системой.Это также требует, чтобы кадры стека были в последовательном порядке в стеке.Кроме того, для обработки исключений требуется, чтобы все «записи исключений» находились в стеке, и чтобы они последовательно соединялись через стековую память.

Я понял это / прочитал это где-то несколько лет назад, когда писалбиблиотека микропотоков (http://www.eternallines.com/microthreads).

4 голосов
/ 09 августа 2011

Вы не можете использовать test процедуру в качестве функции обратного вызова исключения, потому что функция обратного вызова исключения имеет другой прототип. Читайте статью Мэтта Пьетрека , 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 голосов
/ 09 августа 2011

Для первого кода 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 добавляет некоторый пользовательский слой поверх SEH поверх Windows.

И обратите внимание также, что обработка исключений изменяется в режиме Win64 .Стоит прочитать при переходе на Delphi XE2.

...