![]() |
schreibender Zugriff auf private Variablen ?
Hallo,
ich habe ein kleines Problem. Ich müsste eine private Variable ändern (von außen) Das ist normalerweise nicht möglich und auch nicht gewollt. Kennt jemand einen dirty hack um das trotzdem zu machen? Beisp:
Delphi-Quellcode:
Ich möchte nun in der Unit2 schreibend auf sTest zugreifen.
unit Unit1;
interface uses SysUtils; type TmyTest = class(TObject) private sTest: string; public constructor Create; function getTest: string; end; implementation {$R *.dfm} { TmyTest } constructor TmyTest.Create; begin sTest := IntToStr(Random(100)); end; function TmyTest.getTest: string; begin Result := sTest; end; end. Aber ich kann Unit1 nicht ändern !!! Wäre sTest protected, wäre es kein Problem. Innerhalb der selben Unit kann ich auf sTest zugreifen, aber das bringt mir nix. Class Helper bringt mich auch nicht weiter, da er ja auch von "außen" zugreift.
Delphi-Quellcode:
Hat jemand eine Idee?
unit Unit2;
interface uses SysUtils, Unit1; type type TmyTestHelper = class helper for TmyTest procedure setTest(s: string); end; implementation {$R *.dfm} { TmyTestHelper } procedure TmyTestHelper.setTest(s: string); begin // hier gibt es leider keinen schreibenden Zugriff auf sTest end; end. (vieleicht mit Code Beisp. ;-), evtl mit RTTI?) |
Re: schreibender Zugriff auf private Variablen ?
Vielleicht geht's so: Einander zugewiesene Strings zeigen ja normalerweise solange auf denselben Speicherbereich, bis man sie ändert. Irgendwo in der Struktur TmyTest muss es einen Zeiger auf denselben String geben, wie getTest ihn zurückliefert.
(ungetestet)
Delphi-Quellcode:
type
PString = ^string; function FindStringReference(Obj: Pointer; Size: Integer; const TheString: string): PString; begin while Size >= 8 do begin Obj := Pointer(Integer(Obj) + 4); dec(Size, 4); if PInteger(Obj)^ = Integer(@TheString) then begin Result := PString(Obj); Exit; end; end; Result := nil; end; // Aufrufen mit: FindStringReference(myObj, SizeOf(myObj), myObj.getTest)^ := 'Neuer Text'; |
Re: schreibender Zugriff auf private Variablen ?
Okay, jetzt wird's dirty:
Caste eine Methode von dem Objekt, auf dem Du die Variable ändern willst, nach TMethod. TMethod hat ein Property Data und ein Property Code. Das Data Property ist ein Zeiger auf den Datenbereich des Objektes. In dem solltest Du nach dem aktuellen Wert Suchen und so die Speicherstelle herausfinden können. Es sollte gehen, das dann nach String zu casten und zu verändern. Allerdings gebe ich da keine Gewähr ;-) |
Re: schreibender Zugriff auf private Variablen ?
Zitat:
|
Re: schreibender Zugriff auf private Variablen ?
@Flocke: Gute Idee, aber ich glaube, dass da noch ein paar kleine Fehler drin sind...
Delphi-Quellcode:
function FindStringReference(Obj: Pointer; Size: Integer; const TheString: string): PString;
begin while Size >= 8 do begin Obj := Pointer(Integer(Obj) + 4); dec(Size, 4); if PInteger(Obj)^ = Integer(TheString) then //Der String selbst ist der Zeiger - wir brauchen nicht seine Adresse! begin Result := PString(Obj); Exit; end; end; Result := nil; end; FindStringReference(myObj, MyObj.InstanceSize, myObj.getTest)^ := 'Neuer Text';//SizeOf liefert für alle Klassen 4 zurück, da es Zeiger sind |
Re: schreibender Zugriff auf private Variablen ?
@Phoenix: Da steht aber nur der Zeiger. Das wird recht kompliziert.
Erweiterte Möglichkeit zu Phoenix: Variable ist "String", also dynamisch. Damit muss sie definitv in der RTTI auftauchen. Die RTTI zum Datenrecord einer Klasse findest du so
Delphi-Quellcode:
Dort (bei inittable) dürftest du erstmal ein $0E für Record finden, dann einen Shortstring der nur aus dem Längenbyte besteht (indem 0 drinn steht), und dann kommt folgender Datenkonstrukt
var inittable:^pointer;
begin inittable:=pointer(integer(self.classparent)+vmtinittable); //oder noch mehr classparent's; je nachdem wo man hinwill //oder nicht im Parent, dann pointer(integer(ppointer(self)^)+vmtinittable); //bzw. pointer(integer(mytest.classInfo)+vmtinittable); if inittable<> nil then inittable:=inittable^ else //diese Klasse hat (in der Generation) keine dynamischen Komponenten
Delphi-Quellcode:
Dich interessiert davon das Array Content[0..Propcount-1]. Und in jedem Element des Arrays hlst du dir RecordData. Das ist ein Zeiger auf einen Zeiger auf die RTTI der dynamischen Komponente aus deiner Klasse. Und wenn dort das erste Byte $0A ist, dann ist die dynamische Komponente ein String (Du kannst auch mit den Konstanten aus der TypeInfo arbeiten tkString=10).
//RTTI eines Records
type PPropContent=^TPropContent; TPropContent=record RecordData:pointer; //RTTI einer dynamischen Variablen innerhalb des Records Position:LongInt; //Position der Variablen im Record end; type PRecordData=^TRecordData; TRecordData=record ParamCount:cardinal; //Größe des Records insgesamt PropCount:cardinal; //Anzahl der dynamischen Komponenten Content:array[0..16737] of TPropContent; end; Jetzt weist du dass diese dynamische Komponente ein String ist und du nimmst dir Position, addierst den Wert zu self und hast deinen String. Und jetzt musst du den Inhalt überprüfen, wie Phoenix sagte. |
Re: schreibender Zugriff auf private Variablen ?
Eine Instanz ist eigentlich ein Pointer auf den Bereich wo die Daten für die Klasse liegen.
Wenn du also weißt an welchem Offset der String liegt brauchst du einfach nur auf die Instanzvariable den Offset addieren, die erhaltene Adresse auf einen PString casten und dann zu ändern. bei
Delphi-Quellcode:
kommst du so an "sTest":
type
TmyTest = class(TObject) private sTest: string; public constructor Create; function getTest: string; end;
Delphi-Quellcode:
[Edit]
var
lMyTest : TmyTest; begin lMyTest := TmyTest.Create(); PString(Cardinal(lMyTest) + Cardinal(TmyTest.InstanceSize) - sizeof(string))^ := 'neuer Wert'; TmyTest.InstanceSize auf Cardinal gecastet um Warnung weg zu bekommen [/Edit] |
Re: schreibender Zugriff auf private Variablen ?
Hallo MaBuSE,
unter gewissen Voraussetzungen kann eine ![]() Gruß Hawkeye |
Re: schreibender Zugriff auf private Variablen ?
Danke an Alle !!!
@sirius: ich muß mal schauen wie weit ich hier mit rtti komme -> morgen Zitat:
Das Objekt, um das es geht hat ein paar hundert private Variablen, Funktionen und Proceduren. und noch ein mal ein paar hundert Dinge in protected, public und published. Das macht das ganze etwas schwerer. @Hawkeye: Deine Lösung sieht vielversprechend aus. Ich werde das mal morgen im Büro testen ;-) Vieleicht gibt es ja noch mehr Ideen :mrgreen: |
Re: schreibender Zugriff auf private Variablen ?
Hast du den Source der Klasse ? Wenn ja kopiere deren Deklaration, nenne sie um, schmeiße alle Methoden usw. raus bis nur noch die privaten Felder in deiner neuen Deklaration drinnen stehen. Beim Zugriff auf diese Felder castest du dein Object zum neuen Klassentyp. Das sähe so aus:
Delphi-Quellcode:
Du deklarierst quasi eine 1 zu 1 Kopie deiner Klasse auf die du später illegalen Zugriff erlangen möchtest. Letzendlich geht es dabei nur darum das der Compiler die richtigen Zeigeroffsets auf die Felder des Objectes errechnen kann.
type
TXYClassCracker = class(TXYClassAnchor) private FField: ..... end; procedure Test; begin TXYClassCracker(Object).FField := ....; end; Gruß Hagen |
Re: schreibender Zugriff auf private Variablen ?
Mit einem Zeiger auf den alten Propertywert ginge es.
Delphi-Quellcode:
Edit: Wtf? Ich bin noch nicht wach... Hatte eben noch keine Antworten gesehen :lol:
type
TTest = class private fValue: String; function GetValue: String; public property Value : String read GetValue; constructor Create(const aValue : String); end; { TTest } constructor TTest.Create(const aValue: String); begin fValue := aValue; end; function TTest.GetValue: String; begin result := fValue; end; var instance : TTest; begin instance := TTest.Create('A'); try PChar(instance.Value)^ := 'B'; Writeln(instance.Value); finally instance.Free(); end; end. |
Re: schreibender Zugriff auf private Variablen ?
Das kann eigentlich nicht gehen. Die Property ist vollkommen gekapselt durch Zugriffsmethode. Dh. es muß garkein private feld geben auf das sich die Property bezieht, sondern die Zugriffmethoden könnten auch einen String dynamsich zusammenbauen erst beim Zugriff auf diese property. Das was Elvis vorschlägt kann und darf nicht funktionieren.
Besser so:
Delphi-Quellcode:
Ansonsten gehts auch noch enfacher
type
TTestCracker = class(TObject) private FValue: String; end; procedure Test; var T: TTest; begin T := TTest.Create; try TTestCracker(T).FValue := 'Test'; finally T.Free; end; end;
Delphi-Quellcode:
Gruß Hagen
procedure Test;
type PTestCracker = ^TTestCracker; TTestCracker = packed record ClassVMT: Pointer; // hier alle Felder eintragen die in allen Vorgängerklassen von TTest implementiert wurde, // oder alternativ einmalig den +Offset aus gehend von TTest.Self auf FValue per Debugger ermitteln und exakt soviele // Bytes als Dummy[] Array hier einfügen, zb. Offset->FValue ist 32, dann hier Dummy[0..32-4-1] of Byte; FValue: String; end; var T: TTest; begin T := TTest.Create; try PTestCracker(T).FValue := 'Test'; finally T.Free; end; end; |
Re: schreibender Zugriff auf private Variablen ?
Zitat:
Mein Vorschlag hat den gravierenden Nachteil, dass auch andere Strings, die diesen Wert hatten mit geändert werden (bin gerade im Prozess des Aufwachens mit der einhergehenden graduellen Erleuchtung :mrgreen: ). Wenn ich einen PChar eines String hole, wird ja nicht ein Zeiger auf den ersten Char einer Kopie des Strings zurückgegeben, sondern auf den originalen String, der durch den Getter der Eigenschaft zurückgegeben wurde. |
Re: schreibender Zugriff auf private Variablen ?
Fie Frage war wie man auf ein privates Feld eines Objectes modifizierend zugreifen kann. Mit deiner Methode veränderst du nur irgendeine Kopie eines Strings die durch den Getter der Property zurückgebene wird, es besteht keinerlei zwingender Zusammenhang zu irgendeinem privaten Feld des Objekts.
Gruß Hagen |
Re: schreibender Zugriff auf private Variablen ?
Zitat:
Btw, Sirus hatte letzlich fast die ![]() |
Re: schreibender Zugriff auf private Variablen ?
Zitat:
Ich bin heute leider nicht zum testen gekommen, das werde ich aber morgen nachholen ;-) Danke noch mal an alle. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 16:38 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz