Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Exception - Register + DataStack (https://www.delphipraxis.net/210611-exception-register-datastack.html)

Gruber_Hans_12345 17. Mai 2022 16:09

Exception - Register + DataStack
 
Hallo ne Frage, es gibt ja Tools, die können bei einer Exception die CPU Register + DatenStack mit ausgeben.

Sowas wäre ja prinzipiell nicht schlecht, zB.: Könnte man da bei sowas
Delphi-Quellcode:
for i:=0 to Count-1 do Item[i].Free;
vermutlich dann anhander der Register automatisch sehen auf was der Zähler i gerade steht (zumindest wenn man sich dann den Assembler Code ansieht dann ja ganz sicher)

wie kommt man auf diese Register? bzw wie nennt sich das System mit dem man sowas auslesen kann?

Für mich ist es leider ja unlogisch, da ja die exception Routine das alles ja total verändert - daher verstehe ich das nicht wirklich ;)

Der schöne Günther 17. Mai 2022 16:47

AW: Exception - Register + DataStack
 
Verstanden habe ich das auch nie ganz, aber wenn du das schon im Delphi-Debugger nicht mehr sehen kannst, wie soll das dann noch ein externes Tool sehen können? Die Info scheint dann echt zu sein.

TurboMagic 17. Mai 2022 18:37

AW: Exception - Register + DataStack
 
Meinst du sowas wie MadExcept, Eurekalog oder das, was in der Jedi JCL enthalten ist?

Gruber_Hans_12345 18. Mai 2022 06:51

AW: Exception - Register + DataStack
 
ja genau madExcept zb kann eben die CPU Register anzeigen, und vorallem wenn mal ne exception in einer Schleife passiert wäre der CX oder was auch immer doch sehr interessant, oder eben generell der Daten-Stack.

Wenn man sich dann den Assemblercode ansieht dann kann man ja rausfinden wo die Schleifenvariable steht, und wüsste bei welchen Eintrag die Exception aufgetreten ist - wenn ich den Bug vom Kunden bekomme.

himitsu 18. Mai 2022 13:21

AW: Exception - Register + DataStack
 
Erstmal müsste diese Variable dann auch in den Registern liegen, was sie hier bestimmt nicht tut, da dieses Register über den Free/Destroy-Aufruf (und wer weiß was sonst noch alles darin passiert) ungültig/überschrieben würde.

Proderuren/Funktionen sind nicht verpflichtet Register sicherzustellen. (abgesehn von ein/zwei bestimmten Registern, wie EBX, in welchem diese Variable aber bestimmt nicht liegen wird)

In diesem Fall wird die Variable auf dem Stack liegen.




Wie: MSDN-Library durchsuchenGetThreadContext

Stevie 19. Mai 2022 00:15

AW: Exception - Register + DataStack
 
Zitat:

Zitat von himitsu (Beitrag 1506014)
Proderuren/Funktionen sind nicht verpflichtet Register sicherzustellen. (abgesehn von ein/zwei bestimmten Registern, wie EBX, in welchem diese Variable aber bestimmt nicht liegen wird)

Sehr komische und reichlich unfundierte Schlussfolgerung - gerade Zählvariablen sind prädestiniert dafür, in den nicht volatilen Registern zu liegen.

Gruber_Hans_12345 19. Mai 2022 07:39

AW: Exception - Register + DataStack
 
Das was da vermutlich einen strich durch die Rechnung machen wird, ist das eventuell dort wo die exception dann passiert, die CPU Register schon wieder für was anderes verwendet wurden.

Aber wenn man CPU + Stack von dem Zeitpunkt hat wann die Exception aufgetreten ist, dann könnte man es theoretisch ja schön rekonstruieren.

himitsu 19. Mai 2022 12:01

AW: Exception - Register + DataStack
 
Zitat:

Zitat von Stevie (Beitrag 1506057)
gerade Zählvariablen sind prädestiniert dafür, in den nicht volatilen Registern zu liegen.

Ja, das wären sie, wenn zwischendurch nicht anderer Code ausgeführt würde, welcher die Register potentiell überschreiben täte.

Eine kurze schleife, ohne Funktionsaufrufe (CALL), da darfst du das gerne machen, also wenn dein Codestrück die ganze Zeit über die volle Kontrolle über die Register besitzt.


Man kann sich bei sich selbst als Debugger registrieren.
Da kann dann bei einer Exception ein anderer Thread diese Register auslesen, ohne beim Lesen (CodeAusführen) "diese" Register zu beeinflussen, genauso wie es der Debugger von Delphi macht.

Stevie 23. Mai 2022 15:21

AW: Exception - Register + DataStack
 
Register auslesen ist relativ einfach (ich skizzier das mal für Windows).
Du ruftst MSDN-Library durchsuchenGetThreadContext auf und bekommst in der contextvariable die entsprechenden Registerwerte.

Allerdings, wie schon zuvor erwähnt, sind das dann die Werte zu diesem Zeitpunkt - also wenn das derselbe Thread ist wie der gerade diesen Code ausführende, liegt die Wahrscheinlichkeit hoch dass das nicht mehr der Wert ist, der im Code vor der Exception da drin stand - um das rekonstruieren müsste man rückwärts den Code disassembeln und das macht meines Wissens nach keins der Tools wie madExcept.

Beispiel: Ich hab den Code hier in nen ButtonClick geschrieben:

Delphi-Quellcode:
procedure ShowMsg(i: Integer);
begin
  ShowMessage(i.ToString);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
begin
  for i := 1 to 10 do
  begin
    RaiseLastOSError;
    ShowMsg(i);
  end;
end;
Der assembler code unter Win32 sieht so aus (mit
Delphi-Quellcode:
$O+W-
)

Code:
Unit1.pas.36: begin
0063A974 53               push ebx
Unit1.pas.37: for i := 1 to 10 do
0063A975 BB01000000       mov ebx,$00000001
Unit1.pas.39: RaiseLastOSError;
0063A97A E8F9CDE1FF      call RaiseLastOSError
Unit1.pas.40: ShowMsg(i);
0063A97F 8BC3             mov eax,ebx
0063A981 E89EFFFFFF      call ShowMsg
Unit1.pas.41: end;
0063A986 43               inc ebx
Unit1.pas.37: for i := 1 to 10 do
0063A987 83FB0B          cmp ebx,$0b
0063A98A 75EE            jnz $0063a97a
Unit1.pas.42: end;
0063A98C 5B              pop ebx
0063A98D C3               ret
Wenn der call auf RaiseLastOSError passiert ist ebx also 1, aber im angezeigten madExcept bugreport Fenster steht dort bei mir im "cpu regs" Tab 00000000 drin, da bis dahin ebx wiederverwendet wurde

himitsu 23. Mai 2022 18:00

AW: Exception - Register + DataStack
 
Das stimmt so weit auch, denn nicht "RaiseLastOSError" löst die Exception aus, sondern das passiert ja eh erst später, weiter drinnen.

Delphi-Quellcode:
procedure RaiseLastOSError;
begin
  RaiseLastOSError(GetLastError);
end;

procedure RaiseLastOSError(LastError: Integer);
var
  Error: EOSError;
begin
  if LastError <> 0 then
    Error := EOSError.CreateResFmt(@SOSError, [LastError,
      SysErrorMessage(LastError)])
  else
    Error := EOSError.CreateRes(@SUnkOSError);
  Error.ErrorCode := LastError;
  raise Error; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
end;
Mit DebugDCUs bis zum Raise, schauen was die CPU-Ansicht im Delphi sagt und das dann mit madExcept vergleichen.

Stevie 23. Mai 2022 22:25

AW: Exception - Register + DataStack
 
Zitat:

Zitat von himitsu (Beitrag 1506272)
Das stimmt so weit auch, denn nicht "RaiseLastOSError" löst die Exception aus, sondern das passiert ja eh erst später, weiter drinnen.

Ja, genau wie beim Code im Eingangspost! :roll:

himitsu 24. Mai 2022 07:45

AW: Exception - Register + DataStack
 
Wenn man mit richtigen Delphi-Exceptions arbeitet, dann erstellt man ja oft auch erst kurz vorher noch das Exception-Object
und beim Erstellen eines Objektes und dem zusammenbauen von Strings, läuft genug ab, um Alles zu verändern.

Ausnahme, man macht es wie beim EOutOfMemory, wo das Objekt bereits vorher erstellt wurde (klar, weil ja praktisch der RAM schon voll ist und kein Speicher mehr zum Erstellen frei sein könnte)
und braucht dann "nur" noch das
Delphi-Quellcode:
Raise
, womit aber dennoch mindestens ein EAX verändert wird, um das Objekt zu übergeben.



Bleiben also nur noch die OS-Exceptions, wie AccessViolation oder DivisionByZero, was man nahezu direkt auslösen könnte.
Beim Letztem wird zwar oft auch mit mindestens einem Wert in den Registern gerechnet, aber da könnte man "irgendwas" nehmen, war grade in den Registern steht und die NULL kommt vom Heap/Stack.

Aber auch hier wieder das Problem, dass im ExceptionRecord von Windows keine Register drin sind und man somit auch erst wieder Code in der Fehlerbehandlung braucht, um die Register auszulesen,
was nur ginge, wenn das in einem anderen Thread passiert.

Delphi-Quellcode:
  EExternal = class(Exception)
  public
    {$IFDEF MSWINDOWS}
    ExceptionRecord: PExceptionRecord platform; <<<<<<<<<<<<<<<<<<<<<<<<<<<<
    {$ENDIF}
    {$IF defined(LINUX) or defined(MACOS) or defined(ANDROID)}
    ExceptionAddress: LongWord platform;
    AccessAddress: LongWord platform;
    SignalNumber: Integer platform;
    {$ENDIF}
  end;

  EExternalException = class(EExternal);

  EIntError = class(EExternal);
  EDivByZero = class(EIntError);
  ERangeError = class(EIntError);
  EIntOverflow = class(EIntError);

  EMathError = class(EExternal);
  EInvalidOp = class(EMathError);
  EZeroDivide = class(EMathError);
  EOverflow = class(EMathError);
  EUnderflow = class(EMathError);

  EAccessViolation = class(EExternal); <<<<<<<<<<<<<
  EPrivilege = class(EExternal);
  EStackOverflow = class(EExternal) end deprecated;
  EControlC = class(EExternal);
Delphi-Quellcode:
  try
    ...
    Pointer(nil) := 666;
  except
    on E: Exception do begin
      // für GetThreadContext wäre es hier schon zu spät
      // aber E.ExceptionRecord wäre aber noch verfügbar
    end;
  end;
Noch etwas früher kommt man in die Fehlerbehandlung rein, wenn man die Registrierungsstellen in Exception/SysUtils/System benutzt,
aber auch da wird vorher immer noch etwas Code ausgeführt.

Sinspin 24. Mai 2022 10:31

AW: Exception - Register + DataStack
 
Ich verweise mal ganz dezent auf INT 3, der als Debugger Interrupt bekannt ist. Damit bekommt man eigentlich am sichersten die Werte in den Registern. Die Frage ist nur wie man ihn auslößt wenn man einen Fehler erkennt und wie dann ein Debugger reagiert um die infos anzuzeigen. In meinem Fall war immer Delphi offen und stoppte unerwünschterweise in einer Win DLL.

himitsu 24. Mai 2022 10:44

AW: Exception - Register + DataStack
 
Delphi-Quellcode:
asm int 3 end;
oder MSDN-Library durchsuchenDebugBreak; als Befehl. Ersteres geht nur in Win32, da der Win64-Compiler kein InlineAssembler mehr unterstützt. :cry:
Es muß dann aber ein Debugger am Programm hängen. (ein Programm kann sich aber auch selbst bei sich selbst als Debugger registrieren/anhängen).

Hängt kein Debugger am Programm, dann raucht INT 3 ab und Windows beendet das Programm.

Gruber_Hans_12345 25. Mai 2022 06:56

AW: Exception - Register + DataStack
 
Ok mal danke für die Infos.

dh. wenn du würde es nur gehen, wenn eine zeite EXE (der Debugger) oder der Debugger im eigenen Programm in nem Thread aktiv ist.

Hat schon mal irgendwer einen primitiven Debugger in delphi gemacht?
dh. Es würde dann dann auch automatisch die Exceptions im Debugger ankommen, zu einem Zeitpunkt wo noch kein weiterer Code ausgeführt wurde, und alle Register und Stack sachen sauber sind?

Was wäre da der Grund warum das nicht jede ExceptionHandling Routine macht? - wäre doch dann viel klüger?

himitsu 25. Mai 2022 08:19

AW: Exception - Register + DataStack
 
INT 3 triggert den angehängten Debugger (hängt kein Debugger dran, raut der Interuppt ab und das Programm wird beendet).
Der Debugger oder jeder Andere kann von einem anderem Thread/Prozess aus problemlos die Register auslesen, da der dort ablaufende Code keinen Einfluß auf die eigentlichen Register hat (der hat ja seine eigenen Register/ThreadContext, in dem er arbeitet)

Bei einer Fehlerbehandlung im eigenen Thread, wird dort Code ausgeführt, was die Register unweigerlich verändert.
Die einzige Lösung wäre gewesen, wenn Windows bei der Behandlung die Register in den ExceptionRecord kopieren täte, dann hätte man in der eigenen Fehlerbehandlung später Zugriff auf diese Kopie gehabt.
Wenn man also z.B. von in einem anderen Thread seine Fehlerbehandlung einbaut, sich dann von da als Debugger bei seinem eigenem Prozess dranhängt, dann hat han die Fehlerbehandlung in einem anderen Thread, von wo aus man drüben die Register auslesen kann, bevor dort weitergearbeitet wird.
Im Gegensatz zu den normalen Debuggern darf man natürlich nicht die anderen Threads anhalten (wenn man auf die Exception reagiert), bzw. zumindestens nicht sich selbst.


Alle Zeitangaben in WEZ +1. Es ist jetzt 00:41 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz