Entwickler-Ecke

Windows API - Variablen einer Programmes aus dem Arbeitsspeicher auslesen


Makenshi - Mi 30.05.07 23:54
Titel: Variablen einer Programmes aus dem Arbeitsspeicher auslesen
Aloha liebes Delphiforum,

ich wende mich hier mit einem Problem an euch das sicherlich in jedem Forum das sich mit einer Programmiersprache beschäftigt ungern gesehen ist. Es ist die Frage nach einem fertigen Code der auch noch erklärt wird. Ich bin selbst Moderator in einem thematisch ähnlichem Forum und kenne diese Anfragen zur Genüge. Ich mag sie eigentlich auch nicht.
In diesem Fall jedoch, bitte ich eine Ausnahme zu machen und zuzulassen das ich eine solche Anfrage stelle.

An sich ist die Problemstellung folgende:

Wir haben ein Windowsanwendung. Ein selbsterstelltes Spiel um genau zu sein. Dieses Spiel benutzt 4 Byte große Integervariablen die es arrayartig anordnet im Speicher.
Wenn also z.B. sich die erste angelegte Variable dieses Spiels bei 19FFF8C befindet, dann wäre die nächste bei 19FFF90.
Halt 4 Byte draufaddiert. Soweit ganz simpel.

Nur stellt sich jetzt das Problem das die Speicheradresse an dem diese erste Variable vom Spiel abgelegt wird ja immer unterschiedlich ist. Dynamische Speicherverwaltung sei dank *grummel*. Ich müsste nun jedoch eben an die Adresse dieser ersten Spielevariable herankommen und ihren Wert auslesen können. Ich bräuchte also genauer gesagt ein Programm welches die Adresse der ersten Spielvariablen "herausfindet", dann entsprechend 16 Bytes draufaddiert und dann den Wert DIESER Variable in eine Textdatei schreibt.

Nun ist jedoch das Problem das ich anfangs ansprach, allein bin ich nicht dazu in der Lage dieses Programm zu schreiben. Ich habe bereits im Forum herumgesucht und bin dabei auf entsprechende Funktionen wie READPROCESSMEMORY und ähnliches getroffen. Ebenfalls habe ich versucht mir anhand euer Codeschnippsel und den gewonnen Programmiererfahrungen aus Java was zusammenzureimen. Leider ist mir dies nicht geglückt.
Darum nun die Frage: Kann mir jemand ein entsprechendes Programm schreiben und dann hier als Quellcode posten? Was genau bräuchtet ihr noch dafür als Angaben? Den Window Title? Oder anderes?

Ich hoffe das sich in dieser Hinsicht jemand erbarmt und mir antwortet. Allerdings bitte nicht mit einem simplen "Nutz die Suchfunktion und lern Delphi."


MFG Makenshi


Moderiert von user profile iconChristian S.: Topic aus Sonstiges (Delphi) verschoben am Do 31.05.2007 um 00:02
Moderiert von user profile iconUGrohne: Topic aus Job Börse verschoben am Fr 01.06.2007 um 10:02


Jann1k - Do 31.05.07 00:03

also quelltext kann ich dir nciht schreiben, aber haste dieses tutorial gefunden?

http://www.delphi-library.de/viewtopic.php?t=34077&highlight=spieletrainer

sieht für mich ganz nach dem aus was du brauchst.


Anonymous - Do 31.05.07 21:03

user profile iconJann1k hat folgendes geschrieben:
also quelltext kann ich dir nciht schreiben, aber haste dieses tutorial gefunden?

http://www.delphi-library.de/viewtopic.php?t=34077&highlight=spieletrainer

sieht für mich ganz nach dem aus was du brauchst.


Das sieht durchaus interessant aus.
Eventuell hilft mir das schon weiter. Danke für den Hinweis !
Ich werd mal schauen ob ich es damit hinbekomme.


Makenshi - Do 31.05.07 22:53


Delphi-Quelltext
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:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
unit maker;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  WindowName  :  integer;
  ProcessId  :  integer;
  ThreadId  :  integer;
  buf  :  PChar;
  HandleWindow  :  Integer;
  write  :  cardinal;

Const  WindowTitle  =  'Untitled';  //hier einfach den namen von unserm fenster eingeben! einfach spiel starten,
          //dann alt+tab drücken und in der taskleiste einfach nachschaun welche caption
                                        //unser programm hat und diese einfach einfügen.

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);

const
v1 = $90;
begin
 WindowName  :=  FindWindow(nil,WindowTitle);
          If  WindowName  =  0  then MessageDlg('...erst spiel starten...dann trainer starten =)',  mtwarning,[mbOK],0);
    ThreadId  :=  GetWindowThreadProcessId(WindowName,@ProcessId);
    HandleWindow  :=  OpenProcess(PROCESS_ALL_ACCESS,False,ProcessId);
    GetMem(buf,1);
    buf^  :=  Chr(v1);
//----------------------------------------
//Hier kommen die Speicheroperationen rein
//----------------------------------------

//----------------------------------------
    FreeMem(buf);
    closehandle(HandleWindow);
end;

end.



Soweit habe ich es nun geschafft mit dem Thread. Anscheinend wird auch schon das richtige Fenster gefunden. Der Button ist also wie vorgesehen nur verfügbar wenn das Programm auch läuft. Passt soweit.

Nun habe ich leider noch zwei Probleme:

Ich habe mit Tsearch eine entsprechende Adresse per Autohack rausgefunden. Wie genau muss ich diese jetzt in Verbindung mit der READPROCESSMEMORY Funktion nutzen? Der Befehl ist mir noch nicht ganz klar. Das Trainerbeispiel setzt sich ja mit dem Schreiben auseinander. Das bräuchte ich nicht einmal.

Zum zweiten soll die Anwendung am besten im Hintergrund starten, kurz durchlaufen und dann auch wieder beendet werden.
Sie soll nicht erst eine Usereingabe entgegennehmen. Dafür muss ich dann denke ich einfach das Buttonelement weglassen?

Moderiert von user profile iconUGrohne: Code- durch Delphi-Tags ersetzt


Makenshi - Fr 01.06.07 17:10

Man möge mir bitte den Push und den Doppelpost verzeihen, nur habe ich das ganze mittlerweile zu einer Konsolenanwendung umgemünzt. Und entsprechend versucht das ganze selbst weiterzuschreiben.
Dabei kommt folgendes heraus:


Delphi-Quelltext
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:
program menuetausch;

{$APPTYPE CONSOLE}

uses
  SysUtils;

var
TimerID : Word;
WindowName : Integer;
ProcessId : Integer;
ThreadId : Integer;
HandleWindow : Integer;
Buffer : Integer;
BytesRead : DWORD;

const
WindowTitle = 'Untitled';
AddressHealth = $48639B;

procedure Health(wTimerID,wMsg : Word; dwUser,dw1,dw2 : DWord); pascal;
begin
  WindowName := FindWindow(nil,WindowTitle);
  ThreadId := GetWindowThreadProcessId(WindowName,@ProcessId);
  HandleWindow := OpenProcess(PROCESS_ALL_ACCESS,False,ProcessId);
  ReadProcessMemory(HandleWindow,pointer(AddressHealth),@Buffer,SizeOf(Buffer),BytesRead);
  writeln(IntToStr(Buffer));
end;


Das gibt aber wiederum folgende Fehlermeldungen vom Compiler:

Quelltext
1:
2:
3:
4:
5:
6:
7:
  [Error] menuetausch.dpr(15): Undeclared identifier: 'DWORD'
  [Error] menuetausch.dpr(23): Undeclared identifier: 'FindWindow'
  [Error] menuetausch.dpr(24): Undeclared identifier: 'GetWindowThreadProcessId'
  [Error] menuetausch.dpr(25): Undeclared identifier: 'OpenProcess'
  [Error] menuetausch.dpr(25): Undeclared identifier: 'PROCESS_ALL_ACCESS'
  [Error] menuetausch.dpr(26): Undeclared identifier: 'ReadProcessMemory'
  [Error] menuetausch.dpr(31): Declaration expected but end of file found



Hoffe auf Hilfe in dieser Sache. :)


jakobwenzel - Fr 01.06.07 17:18

Da fehlt einfach nur ne Unit, nämlich Windows.


Makenshi - Fr 01.06.07 17:21

Stimmt. Da fehlte wohl einiges an Biblotheken.
So hat der Compiler es zumindestens gefressen:


Delphi-Quelltext
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:
program menuetausch;

{$APPTYPE CONSOLE}

uses
    Windows, SysUtils;

var
TimerID : Word;
WindowName : Integer;
ProcessId : Integer;
ThreadId : Integer;
HandleWindow : Integer;
Buffer : Integer;
BytesRead : DWORD;

const
WindowTitle = 'Untitled';
AddressHealth = $48639B;

procedure Health(wTimerID,wMsg : Word; dwUser,dw1,dw2 : DWord); pascal;
begin
  WindowName := FindWindow(nil,WindowTitle);
  ThreadId := GetWindowThreadProcessId(WindowName,@ProcessId);
  HandleWindow := OpenProcess(PROCESS_ALL_ACCESS,False,ProcessId);
  ReadProcessMemory(HandleWindow,pointer(AddressHealth),@Buffer,SizeOf(Buffer),BytesRead);
  writeln(IntToStr(Buffer));
end;

end.


Nur ist die Frage ob das nun so funzt. Mal fix in der Konsole starten...hoffentlich liest dann jemand das editierte.

Edit:

Das "writeln" müsste mir doch nun entsprechend etwas ausgeben? Da passiert leider nichts. :/


jakobwenzel - Fr 01.06.07 17:38

Es würde auch etwas ausgeben, wenn die Prozedur denn aufgerufen werden würde.

BTW: Wenn du am Schluss noch ReadLn reinschreibst, musste erst enter drücken, bevor das Fenster zugeht.


Makenshi - Fr 01.06.07 17:50

Öhm ja...eine Prozedur die nie aufgerufen wird funktioniert nicht. Logischerweise. X_x
Also nun:


Delphi-Quelltext
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:
program menuetausch;
{$APPTYPE CONSOLE}
uses
    Windows, SysUtils,Classes,Variants;
var
TimerID : Word;
WindowName : Integer;
ProcessId : Integer;
ThreadId : Integer;
HandleWindow : Integer;
Buffer : Integer;
BytesRead : DWORD;
const
WindowTitle = 'Untitled';
AddressHealth = $48639B;
begin
  WindowName := FindWindow(nil,WindowTitle);
  ThreadId := GetWindowThreadProcessId(WindowName,@ProcessId);
  HandleWindow := OpenProcess(PROCESS_ALL_ACCESS,False,ProcessId);
  ReadProcessMemory(HandleWindow,pointer(AddressHealth),@Buffer,SizeOf(Buffer),BytesRead);
  write('Ausgelesener Wert: ');
  writeln(IntToStr(Buffer));
  write('Bytes gelesen: ');
  writeln(IntToStr(BytesRead));
  readln;
end.


Das ist nun ok so. Nur gibt das Programm die Art und Weise den Wert "-55542647" aus. Nicht wirklich das womit ich gerechnet hatte? An sich dachte ich das $48639B die Hexadresse einer bestimmten Variable wäre. So hab ich es jedenfalls mit Tsearch verstanden. Anscheinend ist auch das falsch so?


uall@ogc - Fr 01.06.07 20:21

Ohne die kleinsten Grundlagen in Pascal, solltest du sowas erst gar net versuchen.
Zumindest solltest du verstehen war da -34234 rauskommt, denn die zahl wird als Integer (inttostr) nicht als hex ausgegeben (inttohex).
Ohne Debuggerkenntnisse wirst du auch nicht an die statische Addresse kommen, ausserdem sag mal um welches Spiel es sich handelt.


Chryzler - Fr 01.06.07 20:34

user profile iconuall@ogc hat folgendes geschrieben:
Ohne Debuggerkenntnisse wirst du auch nicht an die statische Addresse kommen, ausserdem sag mal um welches Spiel es sich handelt.

Seltsamerweise war bei mir bis auf eine Ausnahme die Adresse immer gleich geblieben.
Und hab ich das jetzt richtig verstanden, es gibt statische Adressen und dynamische Adressen, und damit das Programm selber weiß, wo die Variablen zu finden sind, legt es Pointer im statischen Bereich an, die auf die eigentlichen Variablen im dynamischen Bereich zeigen? Oder so irgendwie.. Und dann muss man praktisch den Pointer finden, der auf die gewünschte Variable zeigt. Dann kann man den auslesen, da er ja immer an der selben Stelle zu finden ist, und dann zeigt er genau auf die Stelle, wo der eigentliche Wert steht? Richtig oder komplett falsch?


uall@ogc - Fr 01.06.07 20:38

jo eignetlich richtig, kommt halt noch drauf an wo der statsiche pointer ist (eventl in einer dll) dann muss man halt noch bisl umrechnen wenn die dll wo anders geladen ist.
aber ja es gibt immer einen statsichen pointer der da hinzeigt, oder eine berechnung um an die dynamische variable zu kommen, es sei denn die liegt auf dem stack, dann wirds noch komplizierter


Makenshi - Fr 01.06.07 21:00

user profile iconuall@ogc hat folgendes geschrieben:
Ohne die kleinsten Grundlagen in Pascal, solltest du sowas erst gar net versuchen.
Zumindest solltest du verstehen war da -34234 rauskommt, denn die zahl wird als Integer (inttostr) nicht als hex ausgegeben (inttohex).
Ohne Debuggerkenntnisse wirst du auch nicht an die statische Addresse kommen, ausserdem sag mal um welches Spiel es sich handelt.


Da genau liegt an sich auch das Problem. Ich arbeite in der Regel nicht mit Delphi.
Mein Problem ist jedoch das ich nunmal eine Variable aus dem Speicher auslesen muss. Da führt im Moment kein Weg herum.
Das Spiel ist kein bekanntes komerzielles Spiel oder ähnliches, sondern ein Spiel das mit einem Editor erzeugt wird.
Dieser Editor besitzt für das Spiel lediglich Variablen vom Typ Boolean und Integer. Allerdings muss ich ein externes Programm mit einer Variable dieses Spieles füttern. Das ganze ist sozusagen als Parameter für das externe Programm gedacht.
Da dieses Editorspiel keinerlei Möglichkeiten besitzt Systemvariablen zu verändern oder sonst irgendwie vernünftig zu kommunizieren, bleibt mir nur der Weg an der Quelle direkt zu graben. Das wäre in dem Fall der Arbeitsspeicher.

Mit Tsearch habe ich ja sogar bereits die erste Int-Variable aufgespürt. Leider fehlt mir in diesem Fall einfach das nötige Wissen um das ganze zu Nutzen. Ärgerlicherweise benötigen ich dennoch den Effekt der ganzen Sache.
Der übergeben Parameter soll dazu dienen Dateien in einem Ordner des Editorspiels entsprechende Grafikdateien zu kopieren.
Damit das externe Programm weiß welche Dateien es zu verändert hat, benötigen ich unbedingt diesen Parameter.

Ich hoffe so wird näher verständlich warum ich hier etwas unbeholfen aussehe. :)
An sich mag ich es auch nicht mit Funktionen zu arbeiten bei denen ich nicht weiß wie sie arbeiten. Nur bleibt mir in diesem Fall nichts anderes übrig.


uall@ogc - Fr 01.06.07 22:03

Dann hilft wohl nur:
1) selbst nachforschungen antreten, z.b. kann man Daten inden Prozess mit WriteProcessMemory schreiben
2) das Problem in die Job Börse verschieben lassen


Makenshi - Fr 01.06.07 22:59

Dann bleibt mir wohl nichts anderes übrig als das ganze nochmals in die Jobbörse zu schieben.
Ich dachte mir lediglich ich versuche mir einmal selbst das ganze zusammenzubasteln. Was damit wohl gescheitert ist.
Wie du vielleicht bemerkt hast habe ich Nachforschungen angestellt. Nur leider reicht mein Wissensstand und die Informationen nicht um mir zu meinem Programm zu verhelfen. Dafür gibt es da für mich zuviele unklare Stellen.
Aufgrund der Nachforschung ist ja überhaupt erst der Quellcode in meinem Post entstanden.

Nunja, ich hoffe mal das die Wahrscheinlichkeit hier höher ist jemanden zu finden der so ein Programm erschaffen mag. In anderen Foren sieht das nach meiner bisherigen Erfahrung eher schlecht aus.


uall@ogc - Sa 02.06.07 14:14

Bis jetztsollte der code ja stimmen, du liest eben das aus was an besagter Stelle steht. Was willst du denn jetzt noch amchen? Mit WriteProcessMemory kannst du den Speicher ändern.

Willst du eine absolute Adresse finden, die genau auf die Adresse von dir zeigt, wirds schwierig da das wenig mit Delphi zu tun hat.
ABer es sieht so aus als wenn 0x48639B im Speicherbereich der Exe liegt, sollte also keine Probleme geben.


Makenshi - Sa 02.06.07 16:56

Öhm nunja, an sich hätte ich ganz gerne den Wert der in der Variable ist die an der Adresse 48639B steht.
Darum dachte ich anfangs es sei ok wenn die Zahl dort als Integer ausgegeben wird?

Ich bin ehrlich gesagt verwirrt. ReadProcessMemory liest doch eine bestimmte Stelle des Speichers aus? Oder habe ich das völlig falsch verstanden? Nur habe ich ja nicht den Wert herausbekommen der an dieser Stelle im Speicher steht?


Chryzler - Sa 02.06.07 17:11

Doch, stimmt schon. ReadProcessMemory liest den Wert aus, der an der Stelle steht, und WriteProcessMemory schreibt einen Wert an eine bestimmte Stelle.
Ich verstehe nur nicht, warum bei dir ein falscher Wert herauskommen sollte. Erstmal musst du dir sicher sein, dass die Adresse richtig ist. Dann musst du herausfinden, in welchem Datenformat die Zahl gespeichert ist, vielleicht ist es ja gar kein 4-Byte Integer. Und dann solltest du dir mal anzeigen lassen, wieviele Bytes denn tatsächlich ausgelesen wurden, nicht dass es beim Auslesen einen Fehler gegeben hat.


uall@ogc - Sa 02.06.07 17:29

user profile iconuall@ogc hat folgendes geschrieben:
Ohne die kleinsten Grundlagen in Pascal, solltest du sowas erst gar net versuchen.
Zumindest solltest du verstehen war da -34234 rauskommt, denn die zahl wird als Integer (inttostr) nicht als hex ausgegeben (inttohex).
Ohne Debuggerkenntnisse wirst du auch nicht an die statische Addresse kommen, ausserdem sag mal um welches Spiel es sich handelt.


Das was da bei dir rausklommt, kann durchaus stimmen, sofnern die adresse richitg ist.


Makenshi - Sa 02.06.07 17:44

Nunja, das Programm sagt das 4 Bytes ausgelesen werden. Soweit ich weiß müssten es auch eindeutig 4 Byte Integer sein.
Nur ist meine Adresse dort keine statische. An sich werden die Variablen dynamisch bei jedem Start des Programms im Speicher verteilt. Alles nichts neues.

Der ausgegebene Wert kann auch nicht negativ sein. Ich sehe ihn ja. Er müsste an sich 4 sein.

Soweit ich weiß ist $48639B eine Grundadresse. Zumindestens kam sie bei der Benutzung der Autohackfunktion von Tsearch heraus. Daneben standen auch Assemblerbefehl. Entsprechend denk ich mal das das Editorspiel von dieses Grundadresse ausgehend mit entsprechenden Assemblerbefehlen die Adressen für die eigenen Variablen ausrechnet. Keine Ahnung ob ich das nun richtig verstanden habe.
Muss ich dann im meinem Delphiprogramm nicht ebenfalls die entsprechenden Assemblerbefehle benutzen? Wenn ja, dann hört beim Assembler leider absolut das Gebiet auf bei dem ich behaupten kann das ich es verstehe. Mit Assembler hatte ich leider noch nicht allzuviel zutun.


uall@ogc - Sa 02.06.07 17:53

Screenshot von TSearch + Name vom Spiel


Chryzler - Sa 02.06.07 18:32

Versuchs mal so:
Such erstmal in TSearch die aktuelle Adresse von deinem gewünschten Wert, und dann suchst du nach dieser wiederum, sodass du praktisch zum Pointer kommst, der auf die Adresse zeigt. Ich hoffe mal, dass TSearch den findet.


Makenshi - So 03.06.07 00:17

So. Zum besseren Verständnis einmal die zwei Screenshots aus Tsearch.
Das Spiel hat wie schon erwähnt keinen Namen. Es ist ein von einem Editor erzeugtes Spiel. Es ist kein komerzielles bekanntes Spiel. Ich möchte auch keinen Spieletrainer oder ähnliches machen.

Screenshot vom "Autohack"(Debugger) von Tsearch [http://home.arcor.de/makenshi2k/thack.png]
Screenshot von der Konsole des Spiels und von Tsearch [http://home.arcor.de/makenshi2k/thack2.png]


Zitat:

Versuchs mal so:
Such erstmal in TSearch die aktuelle Adresse von deinem gewünschten Wert, und dann suchst du nach dieser wiederum, sodass du praktisch zum Pointer kommst, der auf die Adresse zeigt. Ich hoffe mal, dass TSearch den findet.


Also praktisch den Pointer vom Pointer finden?
Hm, ich weiß nicht ob das hier möglich ist. Ich schau mal ob ich da was finde. Danke für die Idee !

Edit:

Leider ist der Pointer anscheinend nirgenswo abgespeichert. Schade. :/


Makenshi - Fr 08.06.07 09:52

Ich hoffe das ein Push hier erlaubt ist.

An sich wäre ich über eine abschließende Antwort zu diesem Thema glücklich. Selbst wenn es etwas ist wie "tut uns leid, das ist nichts was man ohne Erweiterung deines eigenen Wissens fertigstellen kann."

Immerhin solle ich ja auch Screenshots posten. Hatte dies nun einen Sinn?


uall@ogc - Fr 08.06.07 18:59

Abschließend ist zu erwarten, dass neben deiner Unerfahrung mit Delphi noch die Unerfahrung mit Assembler/Debugging/Speichermanagement dazu kommt und ich einfach mal behaupte, dass DU es nicht hinbekommst. Und wenn du nach solanger Zeit immer noch nicht gesagt hast um welches Programm es geht, wird hier auch niemand zwischenzeilich mal selbst geschaut haben.


Makenshi - Fr 08.06.07 19:29

user profile iconuall@ogc hat folgendes geschrieben:
Und wenn du nach solanger Zeit immer noch nicht gesagt hast um welches Programm es geht, wird hier auch niemand zwischenzeilich mal selbst geschaut haben.


Liest du meine Posts nicht?
Ich habe solangsam das Gefühl. Ich habe doch bereits mindestens 3 mal geschrieben das:
Zitat:

Das Spiel hat wie schon erwähnt keinen Namen. Es ist ein von einem Editor erzeugtes Spiel. Es ist kein komerzielles bekanntes Spiel.


Der Editor selbst ist der RPG Maker 2003. Ich weiß zwar nicht inwiefern das helfen kann, aber nun gut.
Du wirst sicher deine Gründe haben.


Aber anscheinend ist es auch für jemanden mit den Kentnissen nicht möglich so ein Programm mal eben aus dem Hut zu zaubern.


uall@ogc - Fr 08.06.07 19:43

Korrekt, wenn man nämlich nicht den Zugriff darauf hat ist es nicht möglich. Sonst wäre das eine Sache von 5 Minuten.


Robinator - Fr 08.06.07 19:44

Nicht, wenn er die Software nicht selber hat

gruss


uall@ogc - Sa 09.06.07 12:31

Also habs mir nu angeschaut:

1) vielleicht reciht schon das Spiel mit "TestPlay HideTitle Window" zu starten (was anderes macht dr RPG maker auch nicht, dann kann man die Variablen auch im SPiel ändern) WObei das TestPlay das entscheiden ist, Window macht halt keinen Fullscreen ;P

2) Ansonsten berchnet sich die Variable nach: PPointer(PInteger(PInteger($004c5a94)^)^+$28)
Sie änder sich während des spiels öfter mal, also immer neu auslesen. Hab aber gerade keine Zeit ein PRogramm dafür zu schreiben, vielleicht später.


bensch - So 20.04.08 11:56
Titel: help
ich glaube, hier passt mein anliegen am besten rein, deswegen grabe ich nochmal den alten thread aus.

also ich würde gerne aus dem speicher den wert einer bestimmten adresse auslesen. da der wert, den ich auslesen will, von der anwendung immer unter einer anderen adresse geschrieben wird, habe ich nach dem pointer gesucht, der auf diese adresse zeigt.

der pointer befindet sich auf 9D903C. um jetzt an die richtige adresse des wertes zu kommen, müsste ich die adresse 9D903C auslesen, und den wert dieser adresse mit 60hex addieren. als ergebnis würde ich dann die speicheradresse des wertes erhalten, den ich benötige.

dabei habe ich aber bei der berechnung der richtigen speicheradresse schwierigkeiten. bisher habe ich das auslesen und addieren des ausgelesenen wertes so gemacht:


Delphi-Quelltext
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:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
uses ... TLHelp32, ...


  type
  PFindWindowStruct = ^TFindWindowStruct;
  TFindWindowStruct = record
    Caption: string;
    ClassName: String;
    WindowHandle: THandle;
  end;

var Form1: TForm1;
    hSnapshot: THandle;
    hProcess: THandle;
    ProcessEntry: TProcessEntry32;
    Schleife: BOOL;
    pID: Integer;
    wert:integer;
    BytesRead: DWORD;
    adresse:pointer;
    hexwert:string;

const ProcessName =  'Anwendung.exe';

...

procedure TForm1.Button1Click(Sender: TObject);
begin
  adresse:=ptr($9D903C);  // Adresse des Pointers

// Handle der fremden Anwendung besorgen
  hSnapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  ProcessEntry.dwSize := Sizeof(ProcessEntry);
  Schleife := Process32First(hSnapshot, ProcessEntry);
  while Schleife do
  begin
    if (CompareText(ProcessEntry.szExeFile, ProcessName) = 0then
      pID := ProcessEntry.th32ProcessID;
    Schleife := Process32Next(hSnapshot, ProcessEntry);
  end;
  CloseHandle(hSnapshot);
  hProcess := OpenProcess(PROCESS_ALL_ACCESS, FALSE, pID);


// Hier wird der Wert der Pointer-Adresse ausgelesen--> HANDLE, Adresse, Wert, Größe, gelesene Bytes
  ReadProcessMemory(hProcess,adresse,@wert,sizeof(wert), BytesRead );

  wert:=wert+96;
  hexwert:=IntToHex(wert,0);   // Bis hierher stimmt alles...
  adresse:=@hexwert;


// Hier wird der Wert aus der berechnetten Adresse ausgelesen--> HANDLE, Adresse, Wert, Größe, gelesene bytes
  ReadProcessMemory(hProcess,adresse,@wert,sizeof(wert), BytesRead );

  closehandle(hProcess);

// ausgabe des wertes der gelesenen Adresse
  Edit1.Text := inttostr(wert);
end;


aber da gibt es irgendwo ein problem im unteren teil des codes. bis zu der stelle "// Bis hierher stimmt alles..." wird alles korrekt durchgeführt. aber bei der zeile darunter, also

Delphi-Quelltext
1:
  adresse:=@hexwert;                    

wird komischerweise nicht der berechnette wert von

Delphi-Quelltext
1:
2:
  wert:=wert+96;
  hexwert:=IntToHex(wert,0);

übergeben, sondern irgend eine andere adresse. ich habe jetzt schon versucht, mit verschiedenen datentypen diese adresse zu berechnen, aber entweder es kam ein falscher wert dabei raus, oder es kam der fehler "Operation ist auf diesen Operator nicht anwendbar"...

wie könnte ich nun diese berechnung durchführen? kann mir da irgend jemand helfen? ich habe den spiele-trainer thread schon einige male durchgelesen, aber in dem thread geht es leider nur um statische speicheradressen...

hoffe ihr könnt mir helfen.

gruß bensch


Chryzler - So 20.04.08 12:03

Probiers mal so:

Delphi-Quelltext
1:
2:
3:
4:
5:
ReadProcessMemory(hProcess, adresse, @wert, SizeOf(wert), BytesRead);
adresse := Ptr(wert + $60);
ReadProcessMemory(hProcess, adresse, @wert, SizeOf(wert), BytesRead);
CloseHandle(hProcess);
Edit1.Text := IntToStr(wert);


bensch - Mo 21.04.08 22:29

hey klasse, das funktioniert!

sagenhaft, da saß ich jetz einige stunden drann, habe probiert und mindestens 10 seiten gelesen, an was es liegen könnte, und dann wird einem so eine simple lösung gezeigt, die wunderbar funktioniert. ich fass es nicht!

aber ich verstehe momentan etwas noch nicht ganz: wie kann aus einem integer-wert bei addidion mit $60 ein hex-wert werden? denn schließlich wird ein hex-wert ja zum auslesen "ReadProcessMemory(hProcess, adresse, @wert, SizeOf(wert), BytesRead);" benötigt, oder?

auf alle fälle: danke für die hilfe, hat wunderbar geklappt!


elundril - Di 22.04.08 02:49

ich glaub int und hex verwaltet delphi. das heißt du kannst glaub ich jede zahl sowohl als hex als auch als int verwenden. bitte korrigiert mich wenn ich falsch liege aber ich hab das so irgendwie im hinterkopf gehabt und wollts mal ebenso da reinschreiben (damit ich mein englisch-spezialgebiet nicht weitermachen muss^^)

lg elundril


BenBE - Di 22.04.08 08:58

Oh Mann, ob man dem Computer ne Zahl zur BAsis 2, 10, 16 oder 64 gibt, ist dem im Endeffekt so ziemlich egal, weil der intern eh nur mit Basis 2 rechnet ;-) Und wie man dem das präsentiert, ist Ansichtssache :mrgreen: Im Endeffekt sind alles nu Ganzzahlen.


bensch - Di 22.04.08 16:07

ich hatte mich bloß gewundert, weil alle beispiele, die ich irgendwo im netz gefunden habe, benutzen den datentyp string für die hex-darstellung...

aber danke für die antwort :)


AHT - Mo 28.04.08 13:02

Mmmh..., interessante Sache...

OK, habe hier auch noch ein paar Fragen:

1.) Wenn ich das hier richtig sehe, ist der erste (also der fixe) Pointer, der zum Auslesen der Variable ermittelt werden muss, nichts anders als ein "Listeneintrag".
Die Liste, um die es sich da handelt, dürfte eine Liste aller mit GlobalAlloc und dem Flag GMEM_MOVEABLE zugewiesenen Speicherbereiche sein. Ist dem so oder bin ich da auf dem Holzweg?

2.) Wenn dem so ist, dürfte diese fixe Adresse, deren Wert am Anfang ausgelesen werden muss, abhängig vom jeweiligen Betriebsystem sein. Stimmt ihr mir zu?

3.) Wenn ich bislang Recht hatte, dürfte sich die fixe Adresse auf andere Betriebsysteme umrechnen lassen. Ist dem so?

4.) Wenn ich bislang Recht hatte, wo ist dann der Startpunkt dieser Liste? Irgendwie muss der Prozess ja wissen, wo er diese Liste erstellen soll, der Startpunkt dürfte sich also auf irgend eine Weise zuverlässig ermitteln lassen...

5.) Wird der Speicherbereich für die "Variablen" aus irgendeinem Grund mit GMEM_FIXED bereitgestellt, dürfte sich die Adresse nicht so einfach ermitteln lassen - wüsste im Augenblick jedenfalls keinen fixen Punkt zum Auslesen des Pointers auf den Speicherbereich der Variablen, der einfach zu ermitteln wäre...

Will hoffern, es ist verständlich, worauf ich da hinaus will. Ich bin ein ziemlich ungebildeter Mensch und mir fehlen zu vielen Sachen die Fachbegriffe.
Habe mich mit dem Thema noch nicht eingehender beschäftigt. Sind erst mal nur Vermutungen, weil mir da etwas bekannt vorkommt.


BenBE - Mo 28.04.08 13:34

1) Jein, es ist eine Liste, muss aber nicht die Liste von GlobalAlloc sein

2) Solche fixen Pointer sind eher Hinweise auf globale Variablen. während solche Variablen Adressen auf dynamisch reservierte Bereiche hindeuten

3) Umrechnen zwischen den OS brauch man nicht, da das ganze immer relativ zum Datensegment der Anwendung aus der Symbol File hervorgeht; wenn man die nicht hat, kann man das aber auch mit Adresse-StartOfDataSegment rausfinden und rechnet diese VA je nach Memory Mapping wieder um.


AHT - Mo 28.04.08 15:13

Besten Dank...

user profile iconBenBE hat folgendes geschrieben:
1) Jein, es ist eine Liste, muss aber nicht die Liste von GlobalAlloc sein


War da auf dem Holzweg - das ist definitiv eine andere Liste (die steht ganz woanders); das Verfahren zum Auslesen scheint da aber gleich zu sein, das hat mich etwas irritiert. Nette Geschichte - scheint man ja (auf den ersten Blick) recht einfach umsetzen zu können... :)


bensch - Mo 28.04.08 21:04

@ AHT: Wenn sich deine Frage auf meine Posts bezog: In meinem Fall wäre das Betriebssystem Windows gewesen, da die Anwendung, deren Speicherbereich ich auslesen wollte, eh nur für Windows geproggt wurde...