Home of Gamehacking - Archiv
[C#] Reading/Writing Memory - Druckversion

+- Home of Gamehacking - Archiv (http://archiv-homeofgamehacking.de)
+-- Forum: Coding (http://archiv-homeofgamehacking.de/forumdisplay.php?fid=15)
+--- Forum: C, C#, C++, Visual C++ (http://archiv-homeofgamehacking.de/forumdisplay.php?fid=18)
+--- Thema: [C#] Reading/Writing Memory (/showthread.php?tid=146)



[C#] Reading/Writing Memory - cpt.Blubber - 22.11.2010

Hallo Allerseits.

Da Die C Ecke noch ein wenig leer ist, Dachte ich mir schreibe ich Kurz ein kleines Tutorial/Codesnippet wie man In C# Speicher Schreibt/Ausliest.

Um Dieses Tutorial zu verstehen, solltet ihr bereits eine Erfahrung in C# mitbringen, da ich nur auf die Prozedur an sich eingehen werde.

Zu allererst müssen wir ein Paar Funktionen aus der Kernel32.dll holen: OpenProcess, CloseHandle, ReadProcessMemory und WriteProcessMemory. Um die nötigen Parameter und den Rückgabe wert heruaszufinden, müssen wir einfach die funktionen auf MSDN Nachschlagen:

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
	   HANDLE OpenProcess(
					DWORD dwDesiredAccess,  // access flag
					BOOL bInheritHandle,    // handle inheritance option
					DWORD dwProcessId       // process identifier
					);

		BOOL CloseHandle(
					HANDLE hObject   // handle to object
					);
		BOOL ReadProcessMemory(
		        		HANDLE hProcess,              // handle to the process
					LPCVOID lpBaseAddress,        // base of memory area
					LPVOID lpBuffer,              // data buffer
					SIZE_T nSize,                 // number of bytes to read
					SIZE_T * lpNumberOfBytesRead  // number of bytes read
					);
		BOOL WriteProcessMemory(
					HANDLE hProcess,                // handle to process
					LPVOID lpBaseAddress,           // base of memory area
					LPCVOID lpBuffer,               // data buffer
					SIZE_T nSize,                   // count of bytes to write
					SIZE_T * lpNumberOfBytesWritten // count of bytes written
					);


Um Jetzt Das Ganze in C# auch zu Importieren, Benötigen Wir einen DLLImport für jede der Funktionen.

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
		[DllImport("kernel32.dll")]
		public static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, Int32 bInheritHandle, UInt32 dwProcessId);

		
		[DllImport("kernel32.dll")]
		public static extern Int32 CloseHandle(IntPtr hObject);


		[DllImport("kernel32.dll")]
		public static extern Int32 ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress,[In, Out] byte[] buffer, UInt32 size, out IntPtr lpNumberOfBytesRead);
	
		
		[DllImport("kernel32.dll")]
		public static extern Int32 WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress,[In, Out] byte[] buffer, UInt32 size, out IntPtr lpNumberOfBytesWritten);


Auserdem Benötigen wir noch die Flags als Enummeration:

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[Flags]
		public enum ProcessAccessType
		{
			PROCESS_TERMINATE			= (0x0001),
			PROCESS_CREATE_THREAD		= (0x0002), 
			PROCESS_SET_SESSIONID		= (0x0004), 
			PROCESS_VM_OPERATION		= (0x0008), 
			PROCESS_VM_READ				= (0x0010), 
			PROCESS_VM_WRITE			= (0x0020), 
			PROCESS_DUP_HANDLE			= (0x0040), 
			PROCESS_CREATE_PROCESS		= (0x0080), 
			PROCESS_SET_QUOTA			= (0x0100), 
			PROCESS_SET_INFORMATION		= (0x0200), 
			PROCESS_QUERY_INFORMATION	= (0x0400) 
		}


Um jetzt in unserem Program am Speicher zu spielen, müssen wir zuerst den Prozess des gewünschten Programmes in C# öffnen. Dazu müssen wir auch gleichzeitig Angeben, was wir Mit diesem Prozess Anstellen wollen, also ob wir nur lesenden Zugriff, schreibenden etc. möchten.

Wir erstellen uns zuerst eine Variable mit allen Programm Instanzen meines Programmes. Diese ist ein Array vom Typ Process, da ja mehrere Instanzen eures Programmes Theoretisch laufen können. Da ich in meinem Fall davon ausgehe, das nur eine Instanz des Programmes läuft, arbeite ich dementsprechend auch mti dem ersten Prozess des Arrays.

Diesen Prozess müssen wir nun unserer gerade Importierten Funktion OpenProcess zusammen mit den Entprechenden Flags übergeben.

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//Festlegen aller benötigten Variablen
System.Diagnostics.Process[] myProgrammInstances; //Array mit allen Prozessen des gewünschten Programmes
ProcessAccessType myAccessFlags; //Die Zugrifssflags als Enummeration
IntPtr handleProcess; //Hier wird der Handle welcher Als Rückgabewert der Funktion OpenProcess kommt gespeichert.

//Initialisiere handleProcess auf einen Nullpointer
handleProcess = IntPtr.Zero;

//Fülle Das Array mit allen Verfügbaren Prozessen von Programmname
myProgrammInstances = System.Diagnostics.Process.GetProcessesByName("NameDesProgrammes");

//Setze Die Flags für lese/Schreibzugriff auf den Speicher
myAccessFlags= ProcessAccessType.PROCESS_VM_READ 
				| ProcessAccessType.PROCESS_VM_WRITE 
				| ProcessAccessType.PROCESS_VM_OPERATION;

//Hier wird letztendlich der Prozess geöffnet und als Handle Gespeichert
handleProcess = OpenProcess((uint)myAccessFlags, 1, (uint)myProgrammInstances[0].Id); 


Jetzt haben wir einen geöffneten Handle, und es ist sehr wichtig Geöffnete Handles auch wieder zu schliesen, wenn Sie nicht mehr benötigt werden. Dies geht wie folgt:

Code:
CloseHandle(handleProcess);


Der Rückgabe Wert der Funktion CloseHandle ist ein Integer. Dieser ist dann 0, wenn Das Schliesen des Handles Fehlschlägt. Wir können also noch zusätzlich eine Überprüfung einbauen, ob das ganze so funktioniert wie wir uns das Ganze vorgestellt haben:

Code:
int closeHandleReturn;
closeHandleReturn = CloseHandle(handleProcess);
if(closeHandleReturn == 0) {
  //Code Zur Fehler Bearbeitung
}


Ich würde euch Empfehlen das Ganze Als Klasse anzulegen, selbiges habe auch ich getan.

Nun Kommen wir aber endlich zum eigentlichen Teil des ganzen Tutorials. Dem Lesen/Schreiben. Dies Geschieht über die Importierten Funktionen ReadProcessMemory/WriteProcessMemory.

Ich gehe Davon Aus ihr habt das ganze als Klasse nun angelegt. WIr Erweitern nun die Klasse um 2 funktionen, zum Lesen/Schreiben.

Zuerst die Lesefunktion:
Sie gibt ein Byte Array zurück, und benötigt als Parameter den Pointer(bzw die Statische Memory Adresse), Die Anzahl der Bytes die Gelesen werden und eine Variable in der Gespeichert wird wieveile Bytes Tatsächlich Gelesen wurden.

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
		public byte[] ReadMemoryAtAdress(IntPtr MemoryAddress, uint bytesToRead, out int bytesRead)
		{
                        //Initialisierung des Arrays In welchem Inser Ausgelesener Wert Landet
			byte[] buffer = new byte[bytesToRead];
			
			//In Dieser Variable Speichert die Funktion die Anzahl der Gelesenen Bytes
			IntPtr ptrBytesRead;

			//Diese Funktion benötigt Das Zuvor geöffnete Handle, Die Adresse Von wo aus gelesen werden soll, Die Variable in welche der Wert Gespeichert werden soll, und eine Variable die Zählt wieviel wirklich gelesen Wurde.
			ReadProcessMemory(handleProcess ,MemoryAddress,buffer ,bytesToRead,out ptrBytesRead);
			

			bytesRead = ptrBytesRead.ToInt32();

			return buffer;
		}


Hier Noch ein Anwendungs Beispiel der Lese Funktion:

Code:
1
2
3
4
5
6
7
8
9
10
int bytesread;
int memoryaddress = 0x006BA008;
int memoryaddressvalue;
byte[] memoryvalue;

//Holt das Byte Array von Adresse 0x006BA008
memoryvalue = ReadMemoryAtAdress((IntPtr)memoryaddress, 4, out bytesread);

//Hier Wird Das Byte Array zu seinem Eigentlichen Wert für uns lesbar zusammengesetzt:
memoryaddressvalue = BitConverter.ToInt32(memoryvalue, 0);


In meinem Beispiel lese Ich einen 4 Byte Integer. Wollt Ihr stattdessen einen Anderen Typen Lesen, Müsst ihr memoryadressvalue In dem Gewünschten Werten Deklarieren, und auch die Funktion BitConverter.ToInt32 mit BitConverter.ToGewünschterTyp ersetzen, also Beispielsweise BitConverter.ToDouble für einen Double Wert.

Nun kommen Wir zur Schreib Funktion, der letzten Funktion. Diese Funktioniert wie die lese Funktion, mit dem Einzigen UNterschied das sie kein byte Array Zurückgibt, und eine Variable Braucht in welcher Sie Speichern kann wiviele Bytes Sie geschrieben hat.

Code:
1
2
3
4
5
6
7
		public int WriteMemoryAtAdress(IntPtr MemoryAddress, byte[] bytesToWrite)
		{
			IntPtr ptrBytesWritten;
			WriteProcessMemory(handleProcess,MemoryAddress,bytesToWrite,(uint)bytesToWrite.Length,out ptrBytesWritten);
			
			return ptrBytesWritten.ToInt32();
		}

Wir Müssen nun Lediglich eine Adresse und Unseren gewünschten Wert als Byte array Übergeben. Um den Gewünschten Wert von Integer (oder einem Andren Typ) in ByteArray zu bekommen, könnt ihr euch der Funktion BitConverter.ToTyp bedienen.

Ein Anwendungsbeispiel der Funktion zum Schreiben:

Code:
1
2
3
4
5
6
7
8
9
10
11
int byteswritten;
int memoryaddress = 0x006BA008;
int wantedValue;
byte[] memoryvalue;

//Initialisierte Gewünschten Wert und Wandle ihn in Byte Array um
wantedValue = 99;

memoryvalue = BitConverter.GetBytes(wantedValue);

byteswritten = WriteMemoryAtAdress((IntPtr)memoryaddress, memoryvalue);


Das Wars eigentlich Schon. Falls Pointer Im einsatz sind, funktioniert das natürlich Genauso gut. Um ein pointer Offset Anzugeben, einfach den wert bei der memoryaddress hinzuaddieren.

Anwendungsbeispiel für ein Pointer zu Pointer, bei welchem wir den Wert Multiplizieren wollen:

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
				int bytesread;
                int byteswritten;
                int memoryaddress = 0x006BA008;
                int pointerbase;
                int memoryaddressvalue;
                byte[] memoryvalue;

                //Pointer with offset 0x82C to pointer with offset 0x28

                memoryvalue = ReadMemoryAtAdress((IntPtr)memoryaddress, 4, out bytesread);
                pointerbase = BitConverter.ToInt32(memoryvalue, 0);
                pointerbase += 0x82C;

                memoryvalue = ReadMemoryAtAdress((IntPtr)pointerbase, 4, out bytesread);
                pointerbase = BitConverter.ToInt32(memoryvalue, 0);
                pointerbase += 0x28;

                memoryvalue = ReadMemoryAtAdress((IntPtr)pointerbase, 4, out bytesread);
                memoryaddressvalue = BitConverter.ToInt32(memoryvalue, 0);
                
                memoryaddressvalue = memoryaddressvalue * 2;
                
                memoryvalue = BitConverter.GetBytes(memoryaddressvalue);
                byteswritten = WriteMemoryAtAdress((IntPtr)pointerbase, memoryvalue);

Zuerst Wird aus dem Ersten Pointer der Zweite Pointer Ausgelesen. Danach wird über den Zweiten Pointer unsere Adresse Bestimmt, ihr Wert Gelesen, Multipliziert und letztendlich Geschrieben.[/code]

Ich hoffe das Tutorial war Einigermasen Verständlich geschrieben, und hat etwas geholfen. Auf Feedback würde ich mich natürlich freuen.

Viele Grüße
Cpt.Blubber

P.S.: Bitte Kopiert dieses Tutorial nicht ohne meine Zustimmung.


RE: [C#] Reading/Writing Memory - SourceCoder - 24.02.2012

Mit VB geht es viel einfacher. Wenn ich den VB Code in C# Convertiere funktioniert das dann^^?

Gleich mal probieren^^.