| Autor |
Beitrag |
baka0815
      
Beiträge: 489
Erhaltene Danke: 14
Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
|
Verfasst: Di 05.08.08 15:55
Hallo zusammen,
ich versuche gerade ein bisschen mit Pointern auf Methoden herumzuspielen.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46:
| program Test;
{$APPTYPE CONSOLE}
uses SysUtils;
type TTest = class published procedure DoTest; procedure DoTest2; end; Proc = procedure of object;
var Test: TTest; PMethod: ^Proc;
procedure TTest.DoTest; begin WriteLn(self.ClassName + '.DoTest'); end;
procedure TTest.DoTest2; begin WriteLn(self.ClassName + '.DoTest2'); end;
begin try Test := TTest.Create; Test.DoTest; PMethod := Test.MethodAddress('DoTest'); TMethod(PMethod^).Code := Test.MethodAddress('DoTest2'); TMethod(PMethod^).Data := Test; WriteLn('Name: ' + Test.MethodName(PMethod)); Test.DoTest; except on E:Exception do WriteLn(E.ClassName, ': ', E.Message); end; ReadLn; end. |
Was ich vorhabe kann, glaube ich, jeder erkennen. Ich möchte, dass beim Aufruf von Test.DoTest nicht mehr DoTest, sondern DoTest2 aufgerufen wird.
Ich habe es auch schon mit type Proc = procedure; und (unten mit) PMethod^ = Test.MethodAddress('DoTest2'); versucht, aber alles bisher ohne Erfolg.
Das Programm lässt sich kompilieren und starten aber bei der Zuweisung TMethod(PMethod^).Code := Test.MethodAddress('DoTest2'); hängt er sich auf.
Darf/kann ich das so gar nicht machen? Gibt es überhaupt eine Möglichkeit für sowas?
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Di 05.08.08 17:30
Wenn ich mich grad nicht vertue:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| Test := TTest.Create; asm MOV EAX, Test MOV EAX, DWORD PTR [EAX] MOV EDX, VMTOFFSET [TTest.DoTest] MOV ECX, VMTOFFSET [TTest.DoTest2] PUSH DWORD PTR [EAX+EDX] POP DWORD PTR [EAX+ECX] end; Test.DoTest; |
Das was Du gemacht hast, ist deinen Methoden-Zeiger zu verbiegen, nicht jedoch den Zeiger in der Methoden-Tabelle.
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
baka0815 
      
Beiträge: 489
Erhaltene Danke: 14
Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
|
Verfasst: Di 05.08.08 23:27
Witzig ist nur, dass er an der Stelle einfach so hängt. Gehe ich im Debugger auf Pause erscheint auch keine CPU-Ansicht in Assembler o.ä.
Ich hatte diesn Artikel vom Delphi-Treff so verstanden, dass es so funktionieren müsste. Schließlich hole ich mir doch die Adresse der Methode und sage ihm, dass er dorthin, wo jetzt der Zeiger auf Methode1 zeigt, nun die Adresse von Methode2 rein soll oder habe ich da einen Denkfehler?
Würde das ganze, wenn möglich, gerne ohne ASM lösen, da ich das ganze zwar einigermaßen lesen kann, aber nicht in Gänze verstehe.
Aber so wie ich es sehe, kommt das Objekt "Test" (also der Zeiger auf Test) hier in die Speicheradresse EAX geschrieben (ist hier sichergestellt, dass ich nichts überschreibe?). Danach wird EAX als Pointer dereferenziert und der Wert in EAX gespeichert?
Die Adresse von DoTest kommt dann in EDX, die von DoTest2 in ECX und die werden durch PUSH und POP entsprechend gesetzt?
Auf jeden Fall schon mal Danke für die Info!
|
|
baka0815 
      
Beiträge: 489
Erhaltene Danke: 14
Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
|
Verfasst: Mi 06.08.08 09:20
Nachtrag:
Hab's mit dem ASM-Code versucht, der verursacht jedoch einen Compile-Fehler wenn die Methoden nicht als virtual gekennzeichnet sind.
Aber selbst dann funktioniert es nicht, er bleibt auf dem POP stehen.
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Mi 06.08.08 10:59
baka0815 hat folgendes geschrieben: | Witzig ist nur, dass er an der Stelle einfach so hängt. Gehe ich im Debugger auf Pause erscheint auch keine CPU-Ansicht in Assembler o.ä.
Ich hatte diesn Artikel vom Delphi-Treff so verstanden, dass es so funktionieren müsste. Schließlich hole ich mir doch die Adresse der Methode und sage ihm, dass er dorthin, wo jetzt der Zeiger auf Methode1 zeigt, nun die Adresse von Methode2 rein soll oder habe ich da einen Denkfehler? |
Der Artikel im Delphi-Treff zeigt allgemein, wie man eine beliebige Methode aufruft. Wenn man die vom Compiler aufgerufene Methode ändern will, muss man dort seine Methode eintragen, wo der Compiler nachschlägt. Also in der VMT (Virtual Method Table) oder in der DMT (Dynamic Method Table). Diese bleiben bei Dir aber unangetastet. Lediglich dein Methodenzeiger in den du reinschreibst wird verändert. Wenn Du den aufrufen würdest, würde er deine geänderte Methode ausführen.
baka0815 hat folgendes geschrieben: | Würde das ganze, wenn möglich, gerne ohne ASM lösen, da ich das ganze zwar einigermaßen lesen kann, aber nicht in Gänze verstehe.
Aber so wie ich es sehe, kommt das Objekt "Test" (also der Zeiger auf Test) hier in die Speicheradresse EAX geschrieben (ist hier sichergestellt, dass ich nichts überschreibe?). Danach wird EAX als Pointer dereferenziert und der Wert in EAX gespeichert?
Die Adresse von DoTest kommt dann in EDX, die von DoTest2 in ECX und die werden durch PUSH und POP entsprechend gesetzt?
Auf jeden Fall schon mal Danke für die Info! |
EAX, EDX und ECX sind Allzweck-Register, die frei verwendet werden können bei ASM. Ferner steht direkt wenn man dem Zeiger auf ein Objekt folgt (dieses Dereferenziert) ein Zeiger auf die VMT. Diesen les ich einfach au und frag dann den Compiler nach dem Offset der beiden gewünschten Methoden in dieser Tabelle. Danach noch den Speicherinhalt des einen Methoden-Eintrags in den anderen kopieren (mir fehlte ein Register, daher über den Stack gelöst).
Warum er aber hängt ... k.A. ... Klappt es, wenn Du vorher ins CPU-Fenster wechselst und dann im Einzelschritt durchgehst?
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
GTA-Place
      

Beiträge: 5248
Erhaltene Danke: 2
WIN XP, IE 7, FF 2.0
Delphi 7, Lazarus
|
Verfasst: Mi 06.08.08 11:30
@baka0815:
Ich hab mal deinen Source kopiert und auch mal ausgeführt. Bei mir hängt sich das Programm nicht auf, ich bekomme aber eine Zugriffsverletzung bei Adresse 00408785 beim Schreiben von Adresse 00408588. Screenshot des CPU-Fensters im Anhang. Vielleicht hilft das, weil du ja nichtmal das CPU-Fenster sehen kannst.
PS: Heißt dein Programm wirklich Test? Das macht mein D7 gar nicht mit, weil Test redefiniert ist. Also da aufpassen!
EDIT: www.delphipraxis.net/post169771.html
Ich glaub das ist das, was du grad versuchst.
Einloggen, um Attachments anzusehen!
_________________ "Wer Ego-Shooter Killerspiele nennt, muss konsequenterweise jeden Horrorstreifen als Killerfilm bezeichnen." (Zeit.de)
Zuletzt bearbeitet von GTA-Place am Mi 06.08.08 12:01, insgesamt 3-mal bearbeitet
|
|
baka0815 
      
Beiträge: 489
Erhaltene Danke: 14
Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
|
Verfasst: Mi 06.08.08 11:47
Mein Projekt heißt (mittlerweile) TestX.
Ich bin nun auch mal vorher in die CPU-Ansicht gewechselt und er bleibt bei mir an der selben stelle hängen wie bei dir GTA-Place (siehe TestX.jpg), eine Meldung bekomme ich trotzdem nicht. Vermutlich hab' ich irgendwo was eingestellt - wobei keine einzige Exception ignoriert wird...
Mit dem ASM-Code von BenBE hängt er beim POP (siehe TestX_ASM.jpg).
Ich vermute mal, dass beides eine Zugriffsverletzung auslöst. Liegt das an Windows? Muss ich irgendwas einstellen? Beides ist doch Speicher meiner Applikation, da darf ich doch (so dachte ich zumindest) machen was ich will oder?
[edit]Danke für den Link zur Delphipraxis, sieht so aus als wäre es das, was ich suche. Werd' ich mal testen![/edit]
Einloggen, um Attachments anzusehen!
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Mi 06.08.08 11:51
baka0815 hat folgendes geschrieben: | | Ich vermute mal, dass beides eine Zugriffsverletzung auslöst. Liegt das an Windows? Muss ich irgendwas einstellen? Beides ist doch Speicher meiner Applikation, da darf ich doch (so dachte ich zumindest) machen was ich will oder? |
Nein, nicht ganz. Code-Bereiche und darum handelt es sich bei den VMTs und anderen Dingen, sind von Haus aus Schreibgeschützt und dürfen nur gelesen und ausgeführt werden (Ja, das sind verschiedene Berechtigungen, auch wenn Execute ohne Read auch zu nem Fehler führt).
Um also Code-Bereiche ändern zu können, muss man zuerst bei Windows beantragen, dass der Schreibschutz aufgehoben wird. Erst dann darf man dort schreiben. Anschließend sollte man den Schreibschutz wieder aktivierenn, da man sonst eine der Sicherheitsmaßnahmen unter Windows aushebelt.
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
GTA-Place
      

Beiträge: 5248
Erhaltene Danke: 2
WIN XP, IE 7, FF 2.0
Delphi 7, Lazarus
|
Verfasst: Mi 06.08.08 12:02
Mit Hilfe des Thread aus der DP, haben BenBE und ich grad seinen Source zum Laufen bekommen:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:
| var Tmp: DWORD; MBI: TMemoryBasicInformation; begin VirtualQuery(@TTest.DoTest, MBI, SizeOf(MBI)); Virtualprotect(MBI.BaseAddress, MBI.RegionSize, Page_Execute_ReadWrite, @Tmp);
Test.DoTest;
asm MOV EAX, Test MOV EAX, DWORD PTR [EAX] MOV EDX, VMTOFFSET [TTest.DoTest] MOV ECX, VMTOFFSET [TTest.DoTest2] PUSH DWORD PTR [EAX+ECX] POP DWORD PTR [EAX+EDX] end; Test.DoTest;
FlushInstructionCache(GetCurrentProcess, @TTest.DoTest, 4); VirtualProtect(MBI.BaseAddress, MBI.RegionSize, Tmp, Tmp); |
Unit Windows nötig.
_________________ "Wer Ego-Shooter Killerspiele nennt, muss konsequenterweise jeden Horrorstreifen als Killerfilm bezeichnen." (Zeit.de)
|
|
baka0815 
      
Beiträge: 489
Erhaltene Danke: 14
Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
|
Verfasst: Mi 06.08.08 12:06
Japp, genau das war's.
Durch die Verwendung von VirtualProtect() ( unit Windows) kann ich den benötigten Speicher vom Schreibschutz entfernen und dann kann ich da auch ändern. Der Code aus der Delphipraxis funktioniert bestens, aber dein Beispiel BenBE klappt nicht.
Du hast zum Schluss ECX und EDX vertauscht sehe ich gerade. Du holst per PUSH den Pointer auf EAX+EDX (also DoTest) und setzt den in EAX+ECX ein (DoTest2), also genau anders herum.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| asm MOV EAX, Test MOV EAX, DWORD PTR [EAX] MOV EDX, VMTOFFSET [TTest.DoTest] MOV ECX, VMTOFFSET [TTest.DoTest2] PUSH DWORD PTR [EAX+ECX] POP DWORD PTR [EAX+EDX] end; |
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| asm push ebx mov eax, oldfunc mov ebx, newfunc mov byte ptr [eax], $E9 sub ebx, eax sub ebx, 5 mov dword ptr [eax + 1], ebx pop ebx end; |
Die beiden ASM-Blöcke machen im Endeffekt das gleiche, bei der Delphipraxis wird jedoch vorher noch die Sprunggröße bestimmt und hier ein JMP eingebaut. Ich vermute das ist das gleiche wie VMTOFFSET?
Ist eine der beiden Routinen zu bevorzugen?
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Mi 06.08.08 12:20
baka0815 hat folgendes geschrieben: | Japp, genau das war's.
Durch die Verwendung von VirtualProtect() (unit Windows) kann ich den benötigten Speicher vom Schreibschutz entfernen und dann kann ich da auch ändern. Der Code aus der Delphipraxis funktioniert bestens, aber dein Beispiel BenBE klappt nicht. |
Das Beispiel aus der DP ist ein Code Hook, meinst ist ein VMT-Hook. Code-Hooks sind Virenscannern immer sehr suspekt. Und dass ich die Geschichte mit dem VirtualProtect vergessen hab, hat GTA ja schon geschrieben (hab ich mit ihm zusammen geklärt).
baka0815 hat folgendes geschrieben: | Du hast zum Schluss ECX und EDX vertauscht sehe ich gerade. Du holst per PUSH den Pointer auf EAX+EDX (also DoTest) und setzt den in EAX+ECX ein (DoTest2), also genau anders herum.  |
Passiert im Gedränge
baka0815 hat folgendes geschrieben: | Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| asm MOV EAX, Test MOV EAX, DWORD PTR [EAX] MOV EDX, VMTOFFSET [TTest.DoTest] MOV ECX, VMTOFFSET [TTest.DoTest2] PUSH DWORD PTR [EAX+ECX] POP DWORD PTR [EAX+EDX] end; | |
Ist wenn nur so ausgeführt unvollständig, genauso wie:
baka0815 hat folgendes geschrieben: | Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| asm push ebx mov eax, oldfunc mov ebx, newfunc mov byte ptr [eax], $E9 sub ebx, eax sub ebx, 5 mov dword ptr [eax + 1], ebx pop ebx end; |
Die beiden ASM-Blöcke machen im Endeffekt das gleiche, bei der Delphipraxis wird jedoch vorher noch die Sprunggröße bestimmt und hier ein JMP eingebaut. Ich vermute das ist das gleiche wie VMTOFFSET? |
Beide Codes machen was komplett anderes ... Der Source aus der DP verbiegt den ASM-Source der auszuführenden (alten) Routine und überschreibt den Anfang mit einem Long-Jump auf die Neue Methode.
Mein Source patcht die VMT, um beim Suchen der Methode durch Delphi eine korrekt Sprungaddresse auf die neue Methode zu liefern. Meine Variante ist zur Laufzeit somit schneller
baka0815 hat folgendes geschrieben: | | Ist eine der beiden Routinen zu bevorzugen? |
Jup, meine, weil schneller und flexibler. Funktioniert genauso auch mit dynamischen Methoden, dann muss man nur statt VMTOFFSET DMTINDEX schreiben.
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
baka0815 
      
Beiträge: 489
Erhaltene Danke: 14
Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
|
Verfasst: Mi 06.08.08 12:25
BenBE hat folgendes geschrieben: | baka0815 hat folgendes geschrieben: | Japp, genau das war's.
Durch die Verwendung von VirtualProtect() (unit Windows) kann ich den benötigten Speicher vom Schreibschutz entfernen und dann kann ich da auch ändern. Der Code aus der Delphipraxis funktioniert bestens, aber dein Beispiel BenBE klappt nicht. |
Das Beispiel aus der DP ist ein Code Hook, meinst ist ein VMT-Hook. Code-Hooks sind Virenscannern immer sehr suspekt. Und dass ich die Geschichte mit dem VirtualProtect vergessen hab, hat GTA ja schon geschrieben (hab ich mit ihm zusammen geklärt). |
Und das "funktioniert nicht" bezog sich auf die vertauschten Variable und nicht mehr auf die Speichergeschichte. Hatte vergessen den Satz zu entfernen.
BenBE hat folgendes geschrieben: | Beide Codes machen was komplett anderes ... Der Source aus der DP verbiegt den ASM-Source der auszuführenden (alten) Routine und überschreibt den Anfang mit einem Long-Jump auf die Neue Methode.
Mein Source patcht die VMT, um beim Suchen der Methode durch Delphi eine korrekt Sprungaddresse auf die neue Methode zu liefern. Meine Variante ist zur Laufzeit somit schneller
baka0815 hat folgendes geschrieben: | | Ist eine der beiden Routinen zu bevorzugen? |
Jup, meine, weil schneller und flexibler. Funktioniert genauso auch mit dynamischen Methoden, dann muss man nur statt VMTOFFSET DMTINDEX schreiben. |
Ah, hatte nicht gesehen, dass der JMP eingefügt wird, ich dachte der JMP würde ausgeführt.
Das Patchen der VMT (statt der Methode) ist auch deswegen zu bevorzugen, da man zu einem späteren Zeitpunkt das Original wieder herstellen kann. Bei der anderen Möglichkeit müsste ich mir merken, was dort stand bevor ich den Code mit dem JMP überschrieben habe, per VMT kann ich einfach den Zeiger wieder zurückbiegen.
Danke euch!
|
|
Motzi
      
Beiträge: 2931
XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
|
Verfasst: Mi 06.08.08 19:21
BenBE hat folgendes geschrieben: | baka0815 hat folgendes geschrieben: | | Ist eine der beiden Routinen zu bevorzugen? |
Jup, meine, weil schneller und flexibler. Funktioniert genauso auch mit dynamischen Methoden, dann muss man nur statt VMTOFFSET DMTINDEX schreiben. |
Das würde ich so nicht sagen, deine Methode ist zwar eleganter, funktioniert aber eben nur bei virtuellen Methoden. Und dass ein Austauschen von VMTOFFSET gegen DMTINDEX ausreicht damit das ganze auch für dynamische Methoden funktioniert halte ich für ein Gerücht! DMTINDEX gibt nicht das Offset des Eintrags in der Tabelle zurück, sondern direkt den Wert des Eintrags. Nachdem du aber mit Position + Offset arbeitest ( PUSH DWORD PTR [EAX+ECX]; POP DWORD PTR [EAX+EDX]) würde das böse enden... Vor allem müsste man bei dynamischen Methoden noch berücksichtigen in welcher Klasse diese implementiert sind, da ja die dynamic method table einer Klasse (im Gegensatz zur virtual method table) nur jene Methode enhält, die auch von der jeweiligen Klasse implementiert werden.
Gruß, Motzi
_________________ gringo pussy cats - eef i see you i will pull your tail out by eets roots!
|
|
baka0815 
      
Beiträge: 489
Erhaltene Danke: 14
Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
|
Verfasst: Mi 06.08.08 23:20
Vorteil der anderen Methode (ohne es jetzt getestet zu haben) dürfte sein, dass man damit auch Methoden überschreiben kann, die weder als virtual noch als dynamic deklariert sind. Aber für meine Tests reicht das so vollkommen aus.
Aber, wo wir schon dabei sind - wie bekomme ich heraus ob es sich um eine dynamische oder virtuelle Methode handelt? Und wie kann ich das oben genannte erreichen wenn die Methode weder dynamisch noch virtuell ist? Muss ich dann die Lösung mit dem Überschreiben der Methode durch den Sprung wählen?
|
|
baka0815 
      
Beiträge: 489
Erhaltene Danke: 14
Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
|
Verfasst: Do 07.08.08 09:46
Noch was (ich weiß, es gibt edit, aber dann sieht ja keiner, dass sich an dem Thread was getan hat):
In dem Code von BenBE sind die Methoden hart kodiert auf deren Offset zugegriffen werden soll:
Delphi-Quelltext 1:
| MOV EDX, VMTOFFSET [TTest.DoTest] |
Gibt es eine Möglichkeit, dies dynamisch zu erreichen? Statt "TTest.DoTest" möchte ich eine Variable übergeben - geht das?
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Do 07.08.08 11:02
Ja, geht, dann muss man aber entsprechend die VMT durchsuchen nach diesem Methoden-Zeiger. Da ich aber nicht aus'm Kopf weiß, wie man die Größe der VMT herausbekommt, kann ich Dir nicht sagen, wie die Abbruchbedingung heißen müsste.
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
baka0815 
      
Beiträge: 489
Erhaltene Danke: 14
Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
|
Verfasst: Do 07.08.08 11:24
Das VMTOFFSET anhand einer Variablen zu ermitteln ist also nicht möglich, statt dessen die VMT durchlaufen und jeden Pointer mit dem auf die Methode vergleichen und wenn ja hat man die richtige Stelle gefunden?
Was ich auch festgestellt habe, dass zwar ein MOV EAX, Test ausgeführt wird, was ich so verstanden habe, dass die VMT der Instanz Test geändert wird.
Die VMT scheint jedoch für die Klasse TTest zu gelten, wenn ich eine zweite Instanz der Klasse TTest hole, ist dort auch DoTest = DoTest2.
Wie müsste ich das also ändern wenn ich das Instanzweise anpassen möchte?
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Do 07.08.08 11:34
VMT kopieren und nur in der Kopie ändern, diese dann deiner Instanz zuweisen
ODER
Code-Hook auf die besagte Methode, Prüfung auf Instanz und ggf. Original-Methode oder gehookte Methode ausführen.
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
Motzi
      
Beiträge: 2931
XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
|
Verfasst: Do 07.08.08 11:46
Jede Klasse in Delphi ist implizit von TObject abgeleitet, das bedeutet also, dass jedes Objekt auch eine Instanz von TObject ist. TObject.InstanceSize = sizeof(Pointer) = 4 -> die ersten 4 Bytes jedes Objektes sind ein Zeiger auf seine Metaklasse. Die Metaklasse ist eine Datenstruktur die vom Compiler erzeugt wird - für jede Klasse gibt es auch eine Metaklasse. Diese enthalten Alle Informationen über die Klasse (Klassenname, InstanceSize, virtual method table, dynamic method table, RTTI, ...). Die ersten 4 Bytes des Objekts zeigen also auf diese Datenstruktur, genau genommen auf die virtual method table, die immer am Anfang dieser Struktur liegt. An den negativen Offsets liegen dann die Zeiger auf die anderen Tabellen der Metaklasse.
Das Ende der VMT kann man sich suchen, indem man sich die niedrigste Adresse der anderen Teile der Metaklasse sucht.
Willst du das ganze bei einer nicht virtuellen Methode machen, so bleibt dir nur der Code-Hook (das gilt im Prinzip auch für dynamische Methoden. Es gäbe hier zwar auch eine Möglichkeit ohne Code-Hook, allerdings ist etwas aufwändiger).
BenBE hat folgendes geschrieben: | | VMT kopieren und nur in der Kopie ändern, diese dann deiner Instanz zuweisen |
Das würde ich so nicht empfehlen, da neben der VMT noch etliche andere Sachen in dieser struktur stecken, die benötigt werden. Man müsste eine komplette Kopie der Metaklasse erstellen. Unter Umständen bekommt man dann aber Probleme bei der dynamischen Typprüfung.
Gruß, Motzi
_________________ gringo pussy cats - eef i see you i will pull your tail out by eets roots!
|
|
baka0815 
      
Beiträge: 489
Erhaltene Danke: 14
Win 10, Win 8, Debian GNU/Linux
Delphi 10.1 Berlin, Java, C#
|
Verfasst: Do 07.08.08 11:57
Wie kann ich denn die VMT kopieren und meiner Instanz zuweisen?
Finde bisher das editieren der VMT "schöner", also die Methode mit einem JMP zu überschreiben, weswegen ich von einem Code-Hook wie aus dem Delphi-Treff erstmal absehen würde.
[edit]Ok, da scheint es mit dem Code-Hook ja fast einfacher zu werden. Ich werd' mir das alles mal anschauen und meld mich, wenn ich nicht weiter komme. Danke auf jeden Fall schon mal![/edit]
[edit2]In der Delphipraxis habe ich noch diesen Artikel gefunden. Hier wird zwar auch der Code überschrieben, jedoch ein Code-Segment mit dem überschriebenen Code erstellt. Da ich zu einem späteren Zeitpunkt das Original wieder herstellen möchte, scheint so etwas eine gangbare Methode für mich zu sein.[/edit2]
|
|
|