Подтвердить что ты не робот

Обработчик структурированных исключений и Delphi

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

Что я делаю неправильно? В чем разница между первым кодом и вторым?

4b9b3361

Ответ 1

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

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

Ответ 2

Вы не можете использовать процедуру test в качестве функции обратного вызова исключения, поскольку функция обратного вызова исключения имеет другой прототип. Прочтите статью Мэтта Пьетрека, ИМО - лучший источник информации о Win32 SEH.


Обновление

Для дальнейших исследований я бы рекомендовал следующие изменения в коде, чтобы сделать проблему более чистой:

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

(поскольку обратный вызов exception должен возвращать целочисленное значение в 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 игнорирует записи регистрации исключений, которые не основаны на стеках (ОС легко справится, поскольку она знает минимальные и максимальные значения стека)

Ответ 3

Для первого кода TSeh находится в глобальной секции DATA исполняемого файла, тогда как второй код хранит его в стеке.

Это ИМХО, где разница. Структура _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 добавляет некоторый пользовательский слой поверх SEH поверх Windows.

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