C++ Zeiger in Adressen überschreiben

TheLastSamurai

Lieutenant
Registriert
Feb. 2009
Beiträge
620
Hey Leute,

Ich hab ein kleines Problem: Ich hab zwei Zeiger, die auf strings zeigen
Code:
	std::string* pText1 = &text1;
	std::string* pText2 = &text2;

Mit diesen beiden Zeigern will ich in Addressen gespeicherte Zeiger auf andere strings überschreiben, sodass die in den Adressen gespeicherten Zeiger auf meine strings text1 und text2 zeigen.

Aber wie kann ich einen Zeiger auf string, über die Adresse in der er gespeichert ist, mit einem andern zeiger auf string 'überschreiben'?

danke im Vorraus.
 
Ich hoffe mal, dass ich nicht der Einzige bin, aber ich habe absolut keine Ahnung was du machen moechtest. Versuch dich mal verstaendlich auszudruecken oder versuch es mal mit einem Beispiel. Vielleicht versteht es ja aber auch der ein -oder andere und es liegt an mir. Ich kapiers nich :)
 
Mal schauen, ob ich es verstanden habe: Du hast also noch irgendwo zwei Variablen, sagen wir "einZeiger1" und "einZeiger2", die jeweils Adressen von Zeigern - also Zeiger auf Zeiger - auf Strings enthalten? Und du willst, daß diese Zeiger zukünftig auf die Zeiger pText1 und pText2 zeigen?

Das ginge dann genauso wie bei den Zeigern, die du schon hast, nur daß es diesmal Zeiger auf Zeiger sind statt Zeigern auf Strings:
Code:
einZeiger1 = &pText1;
einZeiger2 = &pText2;

Natürlich nur, falls ich dich nicht mißverstanden habe.
 
Schätze er meint sowas hier (:

Code:
	int i;
	int *ip; // zeiger auf int
	int **ipp; // zeiger auf (zeiger auf int)

	ipp = &ip; // ipp zeigt auf ip
	
	*ipp = &i; // ip zeigt auf i
	
	**ipp = 4; // i = 4
	
	*ip = 3; // i = 3
 
Also:

Im globalen Speicher ist in den Adressen 0x619608 und 0x7463C3 ein Zeiger auf einen Text gespeichert. Ich will jetzt diesen gespeicherten zeiger mit meinem überschreiben, sodass die Adressen jetzt einen Zeiger vom gleichen Typ enthalten, der aber auf einen anderen string zeigt.

Hinzu kommt, dass ich irgendwie VirtualProtect einbauen muss, damit ich die Adressen auch überschreiben kann.

Die Frage ist nur: Was tun? die Funktion VirtualProtect() kann zwar überschreiben, ich hab nur keinen Plan ob die auch das gewünschte umsetzt.
 
Ein wenig komplizierter als simple Dereferentierung ist es schon :D

Also es geht darum, dass ein Spiel einen Zeiger auf einen String in einer Adresse speichert. Diese Adresse will ich so überschreiben, dass das Spiel meinen String verwendet. Ich mogel also in die Adresse einen Zeiger auf meinen Text ein und das Spiel wird dann meinen text verwenden, ohne, dass ich den Text zu ersetzen brauche, da ich ihn später nochmal benötige. Deshalb muss ich das ganze über die Addressen abwickeln, da man nicht so einfach auf die Zeiger einer andern Anwendung zugreifen kann.


Schema:
Code:
std::string originalerText = "Ich bin der originale text";
std::string* zeigerAufOriginalenText = &originatlertext; //  dieser Zeiger wird in der Addresse 0x619608 gespeichert und auch vom Spiel verwendet

--> Addresse 0x619608 verweißt auf originalerText weil sie den zeiger zeigerAufOrignalerText speichert

std::string meinString = "ich bin nicht der originale Text!";
std::string* zeigerAufMeinString = &meinString;

//irgendwie wird jetzt 0x619608 überschrieben

--> jetzt verweist 0x619608 auf meinString weil zeigerAufMeinString gespeichert wurde.

Das Spiel soll weiterhin die gleichen Addressen verwenden, um die Zeiger abzulegen, nur muss ich dem Spiel einen andern zeiger unterjubeln.

Das ganze bewegt sich im Rahmen des legalen Spielraums von Modifikationen, da es nur darum geht, im Rahmen einer Modifikation es dem Spieler möglich zumachen, die Savegames für die Mod in einem Anderen Ordner speichern zu können, also nicht seine alten überschreiben zu müssen. Die Oben gennanten Addressen enthalten Zeiger auf den Ordnernamen, der geöffnet werden soll um die saves im Menü anzuzeigen.
 
Zuletzt bearbeitet:
Hast du den Quellcode der Anwendung, die du da auf so brisante Weise modifizieren willst? Wie gedenkst du, deinen Code mit dieser Anwendung zu verheiraten? Ersetzt du eine von dem Programm eingezogene DLL mit einer selbst geschriebenen DLL? Wir brauchen mehr Details. ;)


__Falls__ ich deine Problematik richtig verstanden habe, hier mal eine Riesenschweinerei, mit der man so etwas rein THEORETISCH bewerkstelligen könnte (allerdings wird es in der Realität höchstwahrscheinlich nicht funktionieren).

Code:
// Du weißt, daß an Adresse 0x619608 ein Pointer liegt, der die Adresse des Strings enthält. Also ...
std::string** const ptrToPtrToString = reinterpret_cast < std::string** > ( 0x619608 );
std::string meinErsatzString( "bla bla bla" );
*ptrToPtrToString = &meinErsatzString;

Aber wie gesagt, wird wahrscheinlich nicht funktionieren, denn woher weißt du eigentlich, daß an Adresse 0x619608 überhaupt SCHREIBBARE Daten liegen (könnte ja im Code-Segment oder wo anders liegen)?

Und bist du sicher, daß die Adresse bei jedem Programmstart die gleiche ist?

Die Probleme gehen sogar noch weiter. Die im Originalcode verwendete std::string-Implementierung muß exakt die gleiche wie die in deinem Code verwendete sein (also auch die C++-Runtime), denn wenn das Programm nun deinen String verwendet und irgend was mit ihm tut, das etwas mit Speicherallokierung-oder Freigabe zu tun hat, müssen die exakt gleichen Allokierungs/Freigabe-Routinen verwendet werden, mit denen das Originalprogramm übersetzt wurde ... ansonsten begibst du dich geradewegs ins Land des "undefinierten Verhaltens".
 
Zuletzt bearbeitet:
Der Code wird als dll in den Spielprozess geladen. Der ASI-loader lädt alle *.asi datein, also umbenannte dlls.

Code:
#include "windows.h"
#include <stdlib.h>
#include <stdio.h>
//#include <malloc.h>
#include <string>

//using namespace std;

std::string  text1 = "GTA: The '95 Story";
std::string text2 = "GTA The '95 Story User Files";

BOOL APIENTRY DllMain(HMODULE, DWORD, LPVOID);

void DllInit();


BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved)
{
	switch (reason)
	{
	case DLL_PROCESS_ATTACH:
		DllInit();
		break;
	case DLL_THREAD_ATTACH:
		break;
	case DLL_THREAD_DETACH:
		break;
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

void DllInit() // baustelle...
{
	//	defining new texts
	std::string  text1 = "GTA: The '95 Story";
	std::string text2 = "GTA The '95 Story User Files";
	//	defining pointers to new strings
	std::string* pText1 = &text1;
	std::string* pText2 = &text2;
              
              //überschreiben



}
 
So was ähnliches dachte ich mir fast. Also ich befürchte, das geht weniger in Richtung Programmierung und mehr in Richtung Hacken. ;)

So fern die Autoren des Originalprogramms keine von außen abgreifbare Schnittstelle speziell für Leute wie dich bereitsgestellt haben, bleibt dir meines Erachtens nur dieser dreckige, schmutzige kleine Hack, den ich dir oben angeboten habe - und das nur mit äußerst zweifelhaften Erfolgsaussichten.
 
darf ich mal ganz blöd fragen was dir die zeiger hier an der stelle eigentlich bringen? willste hier n spiel hacken oder was?
dann biste mit std::string schonma auf dem völlig falschen dampfer. dabei handelt es sich nämlich _nicht_ um einen string im sinne von C, sondern um ein _objekt_, das einen string verkapselt!
 
Naja, wenn an 0x619608 ein Zeiger zum String der verändert werden soll liegt gibt es zwei Möglichkeiten. Man könnte den String, ich vermute mal dass ein char Array gemeint ist, am besten erst versuchen direkt zu überschreiben oder sollte der neue String zu lang sein eben etwas Speicher mit VirtualAlloc allokieren, den neuen String dort ablegen und den Zeiger dann entsprechend abändern.
 
Moi moin,

'tschuldigung, dass ich diesen thread hier nochmal reanimieren muss aber ich hab mittlerweile ein paar Sachen in Erfahrung bringen können:

Es handelt sich wirklich um einen string*, nicht um einen char Array und ich bin nach einer nahezu endlosen Sucherei in einem russichen Forum auf das hier gestoßen:
Code:
string gta_user_dir_path;


*(_DWORD *)&gta_user_dir_path = *(_DWORD *)"data";
Scheint mit einer IDA software in einen C ähnlichen pseudocode übersetzt worden sein. Wie dem auch sei, endlich zu meiner eigentlichen Frage:

WriteProcessMemory() würde ich gerne erst ein mal außen vor lassen und ich bin mir nicht sicher, ob ich VirtualProtect() verwenden kann.

Also: Kann ich VirtualProtect() verwenden um einen Zeiger auf String im Speicher zu überschreiben? Wenn ja wie genau? :S

danke fürs Zeit nehmen zum lesen :)
 
Wenn du dir einmal den Artikel über VirtualProtect() im MSDN durchlesen würdest, dann würdest du erkennen, dass du mit dieser Funktion nicht weit kommen wirst, da sie etwas anderes macht.
Das was du vor hast ist auf keinen Fall einfach und ich würde sagen, dass es auch keine schöne C++ Lösung dafür gibt. Wenn man jedoch annimmt, dass die Adresse des Pointers wirklich konstant ist, dann sollte es eine Lösung geben.
Wenn deine DLL gelanden wird, dann solltest du Zugriff auf die Variablen des Prozesses haben, sofern du deren Adresse kennst.
Wenn es sich also um einen std::string* handelt, von dem du die Adresse hast, dann ist der Rest nicht mehr sooo schwer.
Code:
#include <windows.h>
#include <string>
using namespace std;

#define DLL extern "C" __declspec(dllexport)

// Has to be static so that the value will not disappear when the function has been called
static string g_strNewText = "New String";

DLL int DLLMain()
{
	// Change Pointer
	string** ppstrText = (string**)(0x01039E80); // Insert address here ;-)

	*ppstrText = &g_strNewText;

	return 0;
}

Mich hat das ganze interessiert und ich habe mir ein kleines Beispiel geschrieben, welches genau dein Problem behandelt. Dabei bin ich darauf gestoßen, dass std::string, als Debug bzw. Release kompiliert unterschiedlich ist.
Heißt also, dass du dein Programm im gleichen Modus wie deine Zieldatei kompilieren musst, sonst funktioniert das Ganze nicht und dein String unterscheidet sich vom Ziel-String! ( Hat einige Zeit an Trial & Error gebraucht, bis ich darauf gekommen bin )

Falls du mein Beispielprogramm willst, habe ich es angehängt.
Ich habe es nicht geschafft den VC++ Compiler zu überreden meinen Variablen eine konstante Adresse zu geben, deshalb musste ich einen kleinen Trick anwenden: Wenn du mehrere Instanzen des Programms startest, dann haben die immer ( jedenfalls bei mir :) ) die gleiche Adresse. Also habe ich immer eine Instanz gestartet, die Adresse kopiert und dann meine DLL mit dieser Adresse kompiliert und eine weitere Instanz mit dieser DLL gestartet. Ist alles etwas mühselig, aber das was du dir da vorgenommen hast ist auch wirklich nicht so einfach.

Gruß
BlackMark
 

Anhänge

Hey,

tut mir leid, dass ich diesen Thread hier nochmal ausgraben muss.

ich hab mitlerweile mehrere Sachen ausprobiert, so zum Beispiel diese Funktion:
(Die Funktion wurde von einem anderen modder erstellt, der damit, auf die gleiche Weise numerische daten gepatcht hat)
Code:
void _patch(void* pAddress, void* pData, int iSize)
{
	unsigned long dwProtect[2];
	VirtualProtect(pAddress, iSize, PAGE_EXECUTE_READWRITE, &dwProtect[0]);
	memcpy(pAddress, pData, iSize);
	VirtualProtect(pAddress, iSize, dwProtect[0], &dwProtect[1]);
}

//    Aufruf:

_patch((void*)0x619608, (void*)&ptr_appNameText, sizeof(ptr_appNameText));

// und natürlich alles was mit appNameText zu tun hat

std::string appNameText = "mod name";   // mod name is only a dummy string
std::string* ptr_appNameText = &appNameText;

Außerdem hab ich etwas noch etwas umständlicheres versucht, nähmlich WriteProcessMemory(), obwohl ich über die DLL ja direkt auf die Variabeln des Spiels zugreifen kann:
Code:
#include <Windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <malloc.h>

BOOL APIENTRY DllMain(HMODULE, DWORD, LPVOID);
void DLLInit();


BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason_for_call, LPVOID lpReserved)
{
	switch (reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		DLLInit();
		break;
	case DLL_THREAD_ATTACH:
		DLLInit();
		break;
	case DLL_THREAD_DETACH:
		break;
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

void DLLInit()
{
	//
	HWND WindowHandle = FindWindow(NULL, L"GTA: San Andreas");
	if (WindowHandle == NULL)
	{
		return;
	}
	else
	{
		DWORD WindowProcessID;
		GetWindowThreadProcessId(WindowHandle, &WindowProcessID);	//	get the game's process handle
		HANDLE process = OpenProcess(STANDARD_RIGHTS_REQUIRED | PROCESS_ALL_ACCESS | SYNCHRONIZE | 0xFFF, FALSE, WindowProcessID);

		//defining strings
		std::string appNameText = "dummy string";
		std::string folderNameText = "dummy string";	//	std::string folderNameText = "\\GTA The '95 Story User Files";
		std::string saveFileNameText = "dummy string";
		std::string setFileNameText = "dummy string";

		//defining text pointers
		std::string* ptr_appNameText = &appNameText;
		std::string* ptr_folderNameText = &folderNameText;

		WriteProcessMemory(process, (LPVOID)0x619608, &ptr_appNameText, sizeof(ptr_appNameText), 0);
		WriteProcessMemory(process, (LPVOID)0x7463C3, &ptr_appNameText, sizeof(ptr_appNameText), 0);

		WriteProcessMemory(process, (LPVOID)0x74503F, &ptr_folderNameText, sizeof(ptr_folderNameText), 0);

		WriteProcessMemory(process, (LPVOID)0x865674, &setFileNameText, sizeof(setFileNameText), 0);
		WriteProcessMemory(process, (LPVOID)0x86D2EC, &saveFileNameText, sizeof(saveFileNameText), 0);
	}
}

Hat leider auch nicht funktioniert, ebensowie das hier:

Code:
	std::string** ptrAppNameText = (std::string**)(0x619608);
	*ptrAppNameText = &appNameText;
	//
	std::string** ptrAppName2 = (std::string**)(0x7463C3);
	*ptrAppName2 = &appNameText;
	//
	std::string** ptrFolderNameText = (std::string**)(0x74503F);
	*ptrFolderNameText = &folderName;
	//
	std::string** ptrNineFive = (std::string**)(0x865674);
	*ptrNineFive = &nineFive;
	//
	std::string** ptrSaveName = (std::string**)(0x86D2EC);
	*ptrSaveName = &saveName;

Ich hoffe ihr könnt mir helfen

Gruß,
TheLastSamurai
 
Sorry dass ich das jetzt so sagen muss. Aber du solltest es wirklich bleiben lassen. Du kennst dich so wenig mit der Materie aus, dass es einfach keinen Sinn hat.
 
Zurück
Oben