2011-08-09 16 views
12

Próbuję ustawić SEH bez użycia try except
(Jest to dla własnej osobistej wiedzy, aby uzyskać lepsze wyobrażenie o tym, jak SEH robót)Structured Exception Handler i Delphi

Poniższy kod nie działa

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. 

Ale to robi

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. 

Co robię źle? Jaka jest różnica między pierwszym i drugim kodem?

+6

+1 za próbowanie czegoś dziwnego. –

+1

+1 także za próbowanie czegoś dziwnego w Delphi i asmem – EMBarbosa

Odpowiedz

6

System Windows wymaga, aby wszystkie klatki stosu znajdowały się wewnątrz stosu przydzielonego przez system. Wymaga również, aby klatki stosu były w kolejności na stosie. Ponadto w przypadku obsługi wyjątków wymaga, aby wszystkie "rekordy wyjątków" znajdowały się na stosie, a także aby łańcuchy były kolejno porządkowane w pamięci stosu.

Wymyśliłem to/przeczytałem to gdzieś lata temu, pisząc bibliotekę mikropątków (http://www.eternallines.com/microthreads).

+0

Dziękuję za odpowiedź, która rzuca światło na to, co przeszkadzało. Dziękuję bardzo – opc0de

4

Nie można użyć procedury test jako funkcji wywołania zwrotnego wyjątku, ponieważ funkcja wywołania wyjątku wyjątku ma inny prototyp. Czytaj Matt Pietrek article, IMO najlepszym źródłem informacji o Win32 SEH.


Aktualizacja

Dla dalszych badań Polecam następujące zmiany w kodzie, aby ten problem nieco bardziej czysty:

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

(bo wyjątek zwrotna powinna powrócić całkowitą wartość w EAX)

I dla pierwszego fragmentu kodu

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. 

Teraz widać, że wyjątek jest obsługiwany prawidłowo jako:

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

ale nie przez procedury obsługi wyjątków. Prawdopodobnie system operacyjny ignoruje rekordy rejestracji wyjątków, które nie są oparte na stosach (system operacyjny może to łatwo zrobić, ponieważ zna minimalne i maksymalne wartości stosu)

+0

Wiem, że czytałem, ale co mnie martwi, to dlaczego działa drugi fragment kodu? – opc0de

+0

funkcja wyjątku jest stdcall zdefiniowana, więc będzie działać z procedurą zdefiniowaną bez żadnych parametrów, np. Test –

+0

@ opc0de Ciekawe, że pierwszy fragment kodu nie obsługuje wyjątku. Wygląda na to, że system operacyjny nie lubi rekordu rejestracji wyjątku, który nie jest oparty na stosie. – kludg

2

Dla pierwszego kodu, TSeh znajduje się w sekcji globalnej DATA pliku wykonywalnego, podczas gdy Drugi kod zapisuje go na stosie.

To jest IMHO, gdzie różnica jest. Struktura _EXCEPTION_REGISTRATION_RECORD powinna prawdopodobnie znajdować się na stosie. Nie wiem dlaczego, szczerze mówiąc (jakaś sztuczka rejestru SS niskiego poziomu?).

Aby podnieść wyjątek, należy lepiej spróbować czegoś jak podział na zero lub dostępu do zera absolutnego adres:

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

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

o tym, jak przechwytywać wyjątków w Delphi, spójrz at this article. W rzeczywistości Delphi dodaje niestandardową warstwę przez SEH w systemie Windows.

Należy również pamiętać, że exception handling changes in Win64 mode. Warto przeczytać, kiedy przenosimy się na szczyt Delphi XE2.

+0

Ret powoduje wyjątek, ponieważ nie widzę go w debugerze;) Próbowałem wyzwolić wyjątek w taki sposób, w jaki powiedziałeś, że nadal otrzymuję ten sam wynik ... Więc to nie jest problem, dzięki za twoją odpowiedź. – opc0de

+0

Czy wypróbować swój kod w ramach pod-procedury? Instancja TSeh byłaby na stosie, podobnie jak dla drugiego kodu. AFAIK jedyną różnicą jest to, że _EXCEPTION_REGISTRATION_RECORD znajduje się w bloku danych programu dla pierwszego kodu, podczas gdy jest na stosie dla drugiego ... –