K
Und hier der 2. nie veröffnetlichte Teil:
Speichertutorial Teil 2 by Thomas Nitschke
www.codecreator.net
master@codecreator.net
Copyright Thomas Nitschke
Dies ist eine Fortsetzung des ersten "Wie lese ich
Speicher von fremden Programmen"-Tutorial.
Viele Leute haben mich gefragt wie man denn den
Speicherbereich eines Programmes heraus finden
oder zumindest eingrenzen kann.
Ich werde hier eine (und mir einzig bekannte)
Methode vorstellen.
Als Basis dient dieser etwas abgeänderte
Code aus dem letzten Tutorial:
#include <windows.h>
#include <iostream.h>
typedef unsigned int uint;
HANDLE hproc;
DWORD procid;
int main(void)
{
HWND hWnd;
hWnd = FindWindow(0,"Opfer");
if(!hWnd)
return 0;
GetWindowThreadProcessId(hWnd, &procid);
hproc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procid);
CloseHandle(hproc);//<-- Wichtig!
return 0;
}
Außerdem benötigen wir wieder unser Opfer-Prog.
Da ich das Ermitteln des Speicherbereiches nicht auch
noch in die main() reinquetschen will, schreiben wir
die Funktion GetMemMinMax():
#include <windows.h>
#include <iostream.h>
typedef unsigned int uint;
HANDLE hproc;
DWORD procid;
void GetMemMinMax(void);
int main(void)
{
HWND hWnd;
hWnd = FindWindow(0,"Opfer");
if(!hWnd)
return 0;
GetWindowThreadProcessId(hWnd, &procid);
hproc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procid);
GetMemMinMax();
CloseHandle(hproc);//<-- Wichtig!
return 0;
}
void GetMemMinMax(void)
{
//hier speicherbereich bestimmen
}
Infos über den Speicherbereich erhalten wir mit der
Funktion VirtualQueryEx(). Sie braucht als Parameter
das Processhandle, die Adresse,
einen Pointer auf die MEMORY_BASIC_INFORMATION-Struktur,
und die Größe dieser Struktur.
Die MEMORY_BASIC_INFORMATION-Struktur sieht so aus:
struct _MEMORY_BASIC_INFORMATION {
PVOID BaseAddress;
PVOID AllocationBase;
DWORD AllocationProtect;
DWORD RegionSize;
DWORD State;
DWORD Protect;
DWORD Type;
} MEMORY_BASIC_INFORMATION;
BaseAdress speichert die Anfangsadresse der
Speicherregion während RegionSize die Größe dieser
Speicherregion speichert. Der Bereich geht also
von BaseAdress bis BaseAdress+RegionSize.
Alles was wir jetzt machen ist in einer
Schleife immer mit VirtualQueryEx() uns
a) die Größe des Speicherbereichs zuholen
b) Prüfen ob er zu dem Programm gehört
c) Beim nächtsten Bereich weitermachen
Als Code sähe das ungefährt so aus:
MEMORY_BASIC_INFORMATION mbi;
unsigned int adress = 0x400000;
do
{
VirtualQueryEx(hproc,(void*)adress,
&mbi,sizeof(MEMORY_BASIC_INFORMATION));
adress += mbi.RegionSize;
} while(adress < 0x80000000);
Jetzt müssen wir nur noch überprüfen ob der
Speicherbereich zu unserem Programm gehört und ob er
mit Dingen "gefüllt" ist die wir brauchen.
Es kann nämlich auch sein, das wir auf den
Speicher stoßen in dem der Programmcode unseres
Zielprogs liegt! Für Virencoder sicherlich
interessant, aber wir begnügen uns jetzt mal
mit dem Verändern der Werte.
Um zu überprüfen ob das "unser" Speicher ist,
müssen wir uns die State-Variable der
MEMORY_BASIC_INFORMATION-Struktur anschauen.
Ist sie == MEM_COMMIT, gehört der Speicher uns.
Danach müssen wir noch darauf achten das
wir nicht Programmcode oder andere Sachen
gefunden haben. Das kann man mit der
Protect-Variable überprüfen. Normale Variablen
kann man ja sowohl auslesen als auch schreiben,
also ist der gesuchte Wert: PAGE_READWRITE
Als zusätzliche Sicherheit überprüfe ich
noch die Type-Variable, die sollte immer == MEM_PRIVATE
sein. Hier ist der erweiterte Code:
void GetMemMinMax(void)
{
MEMORY_BASIC_INFORMATION mbi;
unsigned int adress = 0x400000;
do
{
VirtualQueryEx(hproc,(void*)adress,
&mbi,sizeof(MEMORY_BASIC_INFORMATION));
if((mbi.State == MEM_COMMIT)&&
(mbi.Protect == PAGE_READWRITE)&&
(mbi.Type == MEM_PRIVATE))
{
uint start = (uint)mbi.BaseAddress;
uint end = (uint)mbi.BaseAddress+mbi.RegionSize;
cout << "Bereich: " << hex << start << " - "
<< hex << end;
}
adress += mbi.RegionSize;
} while(adress < 0x80000000);
}
In der start und end Variable wird jeweils die
Anfangs und Endadresse des Bereich gespeichert.
So, jetzt können wir also nach unseren
Speicherbereichen suchen! Ob der gesuchte Wert
darin enthalten ist, müssen wir noch prüfen.
Dazu schreibe ich wieder eine neue Funktion mit
Namen ScanMem():
void ScanMem(DWORD start, DWORD end)
{
cout << "Bereich wird gescannt...\n";
DWORD read = 0;
uint buffer = 0;
for(start;start<end;start++)
{
ReadProcessMemory(hproc,(void*)start,&buffer,
sizeof(uint),&read);
if(buffer == 15)
{
cout << "Wert an " << hex << start << " gefunden!";
char choice;
cout << "Abbrechen? [j,n]";
cin >> choice;
if(choice == 'j')
return;
}
}
return;//nichts gefunden
}
Wenn das Programm den Wert gefunden hat,
wird der User gefragt ob weiter gesucht werden
soll. Man kann nämlich immernoch nicht sicher
sein ob dieser Wert DIE gesuchte Variable ist.
Hier ist ScanMem() bereits in die Schleife eingebaut:
do
{
VirtualQueryEx(hproc,(void*)adress,
&mbi,sizeof(MEMORY_BASIC_INFORMATION));
if((mbi.State == MEM_COMMIT)&&
(mbi.Protect == PAGE_READWRITE)&&
(mbi.Type == MEM_PRIVATE))
{
uint start = (uint)mbi.BaseAddress;
uint end = (uint)mbi.BaseAddress+mbi.RegionSize;
cout << "Bereich: " << hex << start << " - "
<< hex << end;
ScanMem(start,end);
}
adress += mbi.RegionSize;
} while(adress < 0x80000000);
Hier vollständigkeitshalber
nochmal der gesamte Programmcode:
#include <windows.h>
#include <iostream.h>
typedef unsigned int uint;
void GetMemMinMax(void);
void ScanMem(DWORD start, DWORD end);
HANDLE hproc;
DWORD procid;
int main(void)
{
HWND hWnd;
hWnd = FindWindow(0,"Opfer");
if(!hWnd)
return 0;
GetWindowThreadProcessId(hWnd, &procid);
hproc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procid);
GetMemMinMax();
CloseHandle(hproc);//<-- Wichtig!
return 0;
}
void GetMemMinMax(void)
{
MEMORY_BASIC_INFORMATION mbi;
unsigned int adress = 0x400000;
do
{
VirtualQueryEx(hproc,(void*)adress,
&mbi,sizeof(MEMORY_BASIC_INFORMATION));
if((mbi.State == MEM_COMMIT)&&
(mbi.Protect == PAGE_READWRITE)&&
(mbi.Type == MEM_PRIVATE))
{
uint start = (uint)mbi.BaseAddress;
uint end = (uint)mbi.BaseAddress+mbi.RegionSize;
cout << "Bereich: " << hex << start << " - "
<< hex << end;
ScanMem(start,end);
}
adress += mbi.RegionSize;
} while(adress < 0x80000000);
}
void ScanMem(DWORD start, DWORD end)
{
cout << " Bereich wird gescannt...\n";
DWORD read = 0;
int buffer = 0;
for(start;start<end;start++)
{
ReadProcessMemory(hproc,(void*)start,&buffer,
sizeof(int),&read);
if(buffer == 15)
{
cout << "Wert an " << hex << start << " gefunden!";
char choice;
cout << "Abbrechen? [j,n]";
cin >> choice;
if(choice == 'j')
return;
}
}
return;//nichts gefunden
}
Machen wir einen Testlauf! Wenn alles klappt, steht in
der Konsole:
Bereich: 408000 - 40d000 Bereich wird gescannt...
Wert an 409040 gefunden! Abbrechen? [j,n]
Und *täterätä* die Adresse stimmt!!!
Ich hoffe, das damit die meisten Fragen was
Speicherbereiche angeht beantwortet sind.
Falls ihr Kritik, Anregungen oder Fragen habt,
schickt eine Mail an master@codecreator.net!
Viel Spaß!
PS:
Bitte glaubt nicht das das Tut. meinen jetzigen Wissenstand wiederspiegelt.
Das habe ich anno dazumal geschrieben
[EDIT]Codetags vergessen