Es geht doch nicht um published Properties, sondern um published Methods
Ganz ohne Zusatzinfos nicht ganz einfach (wie Hagen schon sagte). Ich habe noch eine "unsichere" Möglichkeit (auch gleich getestet). Im Prinzip bekommst du aus einer
Exception als wichtige Infos die Addresse des Auftretens der
Exception und den Zustand aller Register zu diesem Zeitpunkt. Dummerweise funkt dir als erstes schonmal Delphi dazwischen der bei "Except" gleich einen Handler aufruft, welcher den Stack durcheinanderwirbelt ($38 Bytes untendran hängt).
Man könnte jetzt über die Adresse versuchen die
RTTI zu durchlaufen um eine published Methode zu finden. Ich bin allerdings noch zusätzlich davon ausgegangen, dass Delphi den self-Pointer meist in ebx legt. Einfacher wärs, wenn man die Methoden mit stdcall aufrufen würde, dann könnte man sich relativ zu ebp bewegen.
Also: Ich versuche nach der
Exception das Register ebx zu "retten" und mir die Adresse des Auftretens der
Exception zu holen. Das sieht bei mir so aus:
Delphi-Quellcode:
function getMethod:TMethod;
const cDelphiException = $0EEDFADE;
//from Unit System
asm
//clear
xor ecx,ecx
xor edx,edx
//push result
push eax
//try (Exception-Handling of getMethod)
push ebp
push offset @Exc
push fs:[edx]
mov fs:[edx],esp
//get pointer to Exception-Context-Structure
//[esp + $c + $38]
//$38 because of system._HandleAnyException
mov edx,[esp+$44]
//old ebx is in Context.structure + $A4
mov eax,[edx+$A4]
//get pointer to Exception-Structure
//[esp + $4 + $38]
//$38 because of system._HandleAnyException
mov edx,[esp+$3C]
//is Delphi Exception (=Exception created by "Raise Exception.create...")
cmp [edx],cDelphiException
je @@1
//non Delphi Exception
mov ecx,[edx+$0C]
//get Exception-Address from Exception-Structure
jmp @@2
@@1:
//Delphi Exception
mov ecx,[edx+$14]
//get Exception-Adress from data of Exception-Structure
@@2:
//test if eax is self-pointer
mov edx,[eax]
cmp edx,[edx+vmtSelfPtr]
jz @mbclass
xor eax,eax
//if not then clear eax
@mbclass:
//clear own Exception-Handling
xor edx,edx
pop fs:[edx]
pop edx
pop edx
jmp @fend
@Exc:
//handle own Exception (+ clear eax)
mov esp,[esp+8]
pop edx
pop edx
pop ebp
xor eax,eax
@fend:
pop edx
//pop result
mov [edx],ecx
//TMethod.code <-- ecx
mov [edx+4],eax
//TMethod.data <-- eax
//call getMethod (if TMethod.data<>nil)
test eax,eax
mov eax,edx
jz @
end
call getMethodCode
@
end:
end;
Die Funktion speichert (bis zum aufruf von getMethodCode) die oben genannten Werte in TMethod. Der self-Pointer, der mutmaßlich in ebx lag, wird in TMethod.Data gelegt und die Adresse des Auftretens des Fehler in TMethod.Code. Letzteres ist allerdings noch nicht die Anfangsadresse der Methode. Dazu gehe ich in die
RTTI und such nach dem Einsprungpunkt einer published Method, welcher vor Method.code liegt:
Delphi-Quellcode:
procedure getMethodCode(
var Method:TMethod);
var mt:pointer;
size:Pword;
count:Pword;
i,j,p:integer;
adr:cardinal;
adrlist:
array of cardinal;
found:boolean;
begin
if cardinal(Method.code)>=cardinal(getprocaddress(getmodulehandle('
kernel32'),'
RaiseException'))
then exit;
mt:=ppointer(integer(method.data^)+vmtMethodtable)^;
if mt<>
nil then
begin
count:=mt;
mt:=pointer(cardinal(mt)+2);
setlength(adrlist,count^);
for i:=0
to count^-1
do adrlist[i]:=0;
for i:=1
to count^
do
begin
size:=mt;
adr:=pcardinal(cardinal(mt)+2)^;
j:=count^-1;
while adrlist[j] > adr
do dec(j);
if j>=0
then for p:=0
to j
do adrlist[p]:=adrlist[p+1];
adrlist[j]:=adr;
mt:=pointer(cardinal(mt)+size^);
end;
if cardinal(method.Code)>adrlist[0]
then
begin
found:=false;
for i:=1
to count^-1
do
begin
if cardinal(method.Code)<adrlist[i]
then
begin
method.Code:=pointer(adrlist[i-1]);
found:=true;
break;
end;
end;
if not found
then method.Code:=pointer(adrlist[count^-1]);
end
else
method.Code:=nil;
end;
end;
Soweit funktioniert es bei mir. Dies alles unter den Vorraussetzungen, dass:
1. getMethod direkt nach "Except" aufgerufen wird
2. HandleAnyException nicht noch mehr "Unfug" macht (
Exception-Klasse suchen, etc.), oder anderen als bei mir
3. Der self-Pointer zum Zeitpunkt der Excepton in ebx lag
Punkt 1 kann man ja recht gut sicherstellen. Für Punkt 2 hilft ein eigener Exceptionhandler (in
ASM) oder die Hoffnung das HandleAnyException nicht anderes reagiert und zu 3. hilft wahrscheinlich stdcall bei den Methoden, da dann der self-Pointer auf dem Stack übergeben wird.
Delphi-Quellcode:
var Method:TMethod
obj:Tobject;
begin
try
//...
except
Method:=getMethod;
obj:=TObject(Method.Data);
if obj is TObject then
begin
edit1.text:=obj.ClassName+' '+ obj.MethodName(method.Code);
end
else
edit1.Text:='Kenne ich nicht';
end;
end;
Edit: Bei stdcall für deine Methoden kannst du die Zeile
Delphi-Quellcode:
//old ebx is in Context.structure + $A4
mov eax,[edx+$A4]
durch das hier ersetzen:
Delphi-Quellcode:
//old ebp is in Context.structure + $B4
mov eax,[edx+$B4]
mov eax,[eax+$08] //=self
.
Das dürfte etwas "sicherer" sein, als sich auf ebx zu verlassen.
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.