Speicherbereich auf bestimmte Daten absuchen



  • Hallo C++-Community 🙂

    Ich komme mit einem Anliegen, welches ich schon bei mehreren Leuten geäußert, aber keine Antwort bekommen konnte.
    Ich habe andere Foren durchsucht, doch auch in diesen konnte ich keine (zufriedenstellende) Antwort finden, meist waren die Beiträge zu meinem Thema stark über die einzelnen Subforen fragmentiert, eine Gesamtlösung so nicht eruierbar.

    Nun ich möchte ein C++-Programm schreiben, welches den benutzen Speicher eines bestimmten Prozesses nach bestimmten Daten - in diesem speziellen Fall nach Strings - durchsucht und die Adresse zurückgibt (hört sich an wie ein Spiel-Trainer, könnte es vielleicht sogar mal werden...)
    Doch um den (virtuellen) Speicher durchsuchen zu können, bräuchte ich ja eine Start- und eine ungefähre Endadresse des Speicherbereichs, welches für den Prozess reserviert wurde.

    Ich weiß (ungefähr, näheres kann ich mir wieder einlesen) wie die MMU funktioniert, dass die Speicheradressen, auf die man zugreift, nicht die physischen sind. Zudem kenn ich die Problematik, dass man nicht einfach auf den Speicherbereich eines fremden Prozesses zugreifen kann, ohne etwas mit Windows-APIs zu spielen (jedenfalls habe ich es so gelesen).

    Daher konkretisiere ich meine Anfrage:
    - Wie kann ich den Speicherbereich eines Prozesses ermitteln?
    - Ist obiges überhaupt möglich? Ich kann mir vorstellen das man den Anfang des Speicherbereichs ermitteln kann, aber das Ende...?
    - Wenn ich den Speicherbereich eines Prozesses herausgefunden habe, wie kann ich diesen Speicher Adresse für Adresse nach einem String durchsuchen? (Wie ein String aufgebaut ist und wie ich genau einen String finde weiß ich, mir geht es eher darum wie ich den Inhalt dieser Adresse einlesen kann)
    - Nachdem ich die gesuchte Adresse gefunden habe, wie könnte ich diese mit Werten, die ich bestimme, überschreiben?

    Ich hoffe die kompetenten, erleuchteten C++-Programmierer können mir in meinem Problem weiterhelfen, ich würde mich über jegliche Hilfe freuen.
    Es müssen keine Codeschnippsel oder dergleichen sein, mir wäre es genug wenn ich erklärt bekomme, wie das funktioniert (oder könnte) und Ratschläge bekomme, in welcher Weise ich das umsetzen kann - den Rest würde ich selbstverständlich nachlesen und selbst coden.

    Mit freundlichen Grüßen,

    faker der lol



  • Ich habe so etwas einmal gemacht, ist aber schon einige Zeit her. Vom Prinzip war es so etwas wie Gamewiz, also Anwendung wählen, Wert suchen, Wert verändern, erneut suchen etc.
    Den kompletten Code kann ich hier nicht posten, grundsätzlich läufts aber folgendermaßen ab:
    GetSystemInfo(...) aufrufen
    Adresse von lpMinimumApplicationAddress bis lpMaximumApplicationAddress in einer Schleife durchlaufen,
    Aufruf von VirtualQueryEx(...), gefolgt von ReadProcessMemory(...).
    Sollte der passende Wert gefunden werden, wird diese Speicherstelle in einem dynamischen Array (wenn du C++ programmierst, am Besten std::deque) gespeichert. Bei der weiteren Suche kann nun dieser Container jeweils durchsucht werden, was kein Problem darstellen sollte.
    Ich hoffe, es ist jetzt nicht unklarer als vorher?



  • Falls du zu irgend einem Punkt noch Fragen hast, beantworte ich sie gerne.
    Kann ein Moderator diesen Thread ins Forum WinApi verschieben?



  • VF schrieb:

    Falls du zu irgend einem Punkt noch Fragen hast, beantworte ich sie gerne.
    Kann ein Moderator diesen Thread ins Forum WinApi verschieben?

    Hallo VF, danke für die Hilfe! Nun weiß ich jedenfalls das es möglich ist 😃
    So wie ich das verstanden habe kann man aber nicht prozessspezifisch suchen sondern muss den ganzen Speicher abklappern, ist das richtig?

    Nun habe ich mir die einzelnen Methoden angeschaut die du aufgeschrieben hattest, wobei ich kein gutes Beispiel für VirtualQueryEx finden konnte bzw garnicht weiß, wofür ich das überhaupt brauche.

    Nehmen wir mal an ich möchte mich an den Prozess "test.exe" hängen und die Adressen durchsuchen, also gehe ich in einer Schleife von lpMinimumApplicationAddress bis lpMaximumApplicationAddress alle Adressen durch.
    Um sich jedoch an diesen Prozess "dranhängen" zu können, braucht man hierfür das VirtualQueryEx?

    Ich bin mir leider nicht im Klaren welche Schritte ich gehen muss, da ich dies zuvor nie gemacht habe :S

    Und nochmals danke für die tatkräftige Hilfe 😃



  • Dieser Thread wurde von Moderator/in volkard aus dem Forum C++ in das Forum WinAPI verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.



  • Nein, es ist prozess-spezifisch. Funktionen wie VirtualQueryEx, ReadProcessMemory, WriteProcessMemory etc. erwarten alle einen Handle auf einen Prozess als Parameter. Es kann auch gar nicht anders sein. Auf User-Ebene ist es nicht feststellbar, ob Daten im physischen Ram liegen oder durch den Memory-Manager von Windows auf Disk ausgelagert wurden. Einen Handle auf einen Prozess zu erhalten, ist aber auch nicht wirklich schwer.
    Entweder du füllst bspw. eine Liste mit den HWNDs bzw. den Titelleistentexten der momentan geöffneten Fenster und läßt den Benutzer entscheiden, wessen Speicher durchsucht werden soll, oder du möchtest den Speicher eines bestimmten Prozesses durchsuchen und kennst den Namen.
    Stichwörter:
    zu Ersterem:
    EnumWindows (http://msdn.microsoft.com/en-us/library/ms633497%28VS.85%29.aspx)
    GetWindowThreadProcessId (google msdn)
    OpenProcess (google msdn)
    zu Zweiterem
    EnumProcesses
    oder CreateToolhelp32Snapshot
    So, jetzt hast du einen Handle auf einen Prozess.
    Wenn du soweit bist, melde dich noch mal. In der Forumssuche solltest du über sowas Tonnen von Material finden (is' nen Standardthema).
    Anschließend musst du überprüfen, wie groß der mögliche virtuelle Speicher des Prozesses ist. Dazu gibt es die Funktion GetSystemInfo(...) http://msdn.microsoft.com/en-us/library/ms724381%28VS.85%29.aspx.
    Die Hauptschleife sieht folgendermaßen aus:

    while(address <(reinterpret_cast<DWORD_PTR>( sys_info.lpMaximumApplicationAddress))
    	{
    		VirtualQueryEx(m_hCurProcess,
    						  reinterpret_cast<void*>(address), 
    						  &mbi, 
    						  sizeof(MEMORY_BASIC_INFORMATION));
    
    		DWORD_PTR start = reinterpret_cast<DWORD_PTR>(mbi.BaseAddress);
    		DWORD_PTR end	= reinterpret_cast<DWORD_PTR>(mbi.BaseAddress) + mbi.RegionSize;
    
    		if(mbi.State == MEM_COMMIT)
    			ScanMemory(hwnd,start, end);
    
    		address += mbi.RegionSize;
    	}
    

    Es wird also beim ersten Mal der komplette (virtuelle) Speicher durchsucht. ScanMemory kann folgendermaßen aussehen:

    const int iStep = 4;	  // 32 Bit-Werte, muss geändert werden
    	const int iArrSize = 128; // gleich mehrere Werte lesen
    
    	int read_buffer[iArrSize];
    	for(DWORD_PTR i = start; i < end; i+=(iStep*iArrSize))
    	{
    		memset(&read_buffer,0,sizeof(unsigned int)*iArrSize);
    		ReadProcessMemory(m_hCurProcess, reinterpret_cast<void*>(i), &read_buffer, sizeof(unsigned int)*iArrSize, 0);
    
    		for(int ii=0;ii<iArrSize;++ii)
    		{
    			if(read_buffer[ii] == m_dwSearchVal)
    			{
    				m_vAddr.push_back(i+ii*4);
    			}
    		}
    	}
    

    ScanMemory durchsucht nur 32 Bit-Werte, dies kann aber sehr leicht geändert werden.
    m_hCurProcess ist das Hanlde auf den jeweils ausgewählten Prozess, die Adressen werden in einer deque gespeichert.
    Im nächsten Schritt, d.h. Werte wurden gefunden, muss nur noch diese deque durchsucht werden.
    Wenn Werte verändert werden sollen, folgt ein Aufruf von WriteProcessMemory http://msdn.microsoft.com/en-us/library/ms681674%28VS.85%29.aspx.
    Die Adresse ist in der deque gespeichert, der Wert muss nur noch gesetzt werden.
    Alle Trainer arbeiten auf diese Weise, ziehen jedoch häufig Vorteile daraus, dass die Adresse fest ist und nicht gesucht werden muss.



  • Wow das is doch schon einmal was!
    Mit so viel Information kann ich doch bestimmt was anfangen 🙂

    Ich werde mich daran setzen und sobald ich Fragen habe oder es fertiggestellt habe, melde ich mich wieder!

    👍 👍 👍



  • CheatEngine ist ein MemoryScanner und OpenSource. Allerdings weiß ich nicht, ob es dir etwas bringt, wenn du dir den genauer anschaust, weil er vielleicht etwas overpowered ist.

    greetz KN4CK3R



  • KN4CK3R schrieb:

    CheatEngine ist ein MemoryScanner und OpenSource. Allerdings weiß ich nicht, ob es dir etwas bringt, wenn du dir den genauer anschaust, weil er vielleicht etwas overpowered ist.

    greetz KN4CK3R

    Das hatte ich einmal versucht aber naja... ich kann von mir zwar behaupten das ich das Eine oder Andere verstehe, doch war das definitiv... wie hast du es beschrieben? Overpowered. Das trifft zu 😃

    Nun ich habe mir dank der Hilfe hier schon ein wenig Code zusammengestellt, sieht noch alles etwas unstrukturiert aus aber ums Design mache ich mir wann anders Sorgen.

    Was mir jedoch welche macht ist die Geschwindigkeit der Suche: Sie lahmt extremst, vergleicht man diese mit der von CheatEngine.
    Natürlich braucht es etwas Zeit, um so viele Speicherstellen abzusuchen und immer wieder Schleifen zu durchgehen, um die Speicherinhalte mit dem gesuchten Wert zu vergleichen - doch gibt es da nichts schnelleres?

    Zumal ich, wenn ich erstmal die gesuchten Adressen gefunden habe, diese dann "zurückverfolgen" möchte, um den Variablenwert zu ermitteln. Also muss ich die gefundene Adresse als Wert wieder suchen, sprich wieder den ganzen Speicher (frisch) absuchen. Habe ich die 2., gesuchte Adresse gefunden, muss ich diese wieder als Wert nehmen und nochmal einen Suchdurchlauf starten, bis ich endlich den gesuchten Wert zum String gefunden habe ...
    Und wenn ich nun für jeden String 3 volle Suchdurchläufe brauche, kann es sich um mehrere Stunden handeln...

    Was mache ich also falsch? Gibt es eine bessere, schnellere Möglichkeit?
    Hier der genaue Programmablauf:

    • Finde im Speicherbereich von Prozess X alle Adressen, die ein String Y beinhalten, und speichere die Adressen in einem Container.

    • Für jede somit gefundene Adresse durchsuche den Speicher erneut, indem die Adresse als Suchwert dient

    • Für die so gefundene Adresse das letzte Byte abändern und wieder als Suchwert nehmen, um den Speicher zu durchsuchen

    • Für die so gefundene Adresse wieder das letzte Byte abändern und wieder als Suchwert nehmen - Speicher durchsuchen...

    • Letztendlich diese Adresse als Pointer mit einem bestimmten Offset benutzen, um an den gesuchten Wert zu kommen

    Und dies nunmal für ALLE Adressen, die anfangs beim Suchen der Strings gefunden werden - durchschnittlich 50 ~ 80 Adressen * 4 Suchdurchläufe durch den ganzen Speicher, da die Adressen nie wirklich in unmittelbarer Nähe liegen.

    Wie könnte ich dieses Performance-Problem lösen? Wie löst es z.B. CheatEngine? Suche ich dort nach einem 'Array of Bytes', dauert es keine Sekunde bis das Ergebnis erscheint - tue ich das, dauert es genau so lange wie alle anderen Suchdurchgänge...



  • Ich habe mich jetzt auch einmal registriert.
    Zeig doch mal den entsprechenden Code. Wenn ich einen Wert suche, dann habe ich auch nach ganz kurzer Zeit die Adressen; ich glaube, irgendetwas machst du falsch.
    Und wenn du den Wert an einer bereits gefundenen Speicherstelle auslesen möchtest, dann musst du natürlich nicht den kompletten Suchvorgang wiederholen. Dafür hast du ja die Adressen in einem Container gespeichert. Du kannst jetzt also direkt ReadProcessMemory mit der Adresse aufrufen.



  • Vicious Falcon schrieb:

    Ich habe mich jetzt auch einmal registriert.
    Zeig doch mal den entsprechenden Code. Wenn ich einen Wert suche, dann habe ich auch nach ganz kurzer Zeit die Adressen; ich glaube, irgendetwas machst du falsch.
    Und wenn du den Wert an einer bereits gefundenen Speicherstelle auslesen möchtest, dann musst du natürlich nicht den kompletten Suchvorgang wiederholen. Dafür hast du ja die Adressen in einem Container gespeichert. Du kannst jetzt also direkt ReadProcessMemory mit der Adresse aufrufen.

    Hihi das war auch mein erster Gedanke, ABER ich kenne ja nicht die Adresse, sondern habe nur die vorgänger-Adresse, die der Wert ist 😃
    D.h. ist habe zwar eine Adresse, aber die ist nur ein Wert, dessen Adresse ich wieder suche.

    Wie gesagt der Code ist nicht der Beste und ich musste vieles Ändern, da es sonst Fehlermeldungen in meinem System gab 😃

    #include <iostream>
    #include <iomanip>
    #include <windows.h>
    #include <Tlhelp32.h>
    #include <string>
    using namespace std;
    
    int main()
    {
        HANDLE hproc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, getPID("plugin-container.exe"));
    
        MEMORY_BASIC_INFORMATION mbi;
    
        SYSTEM_INFO sinfo;
        memset(&sinfo, 0, sizeof(sinfo));
        GetSystemInfo(&sinfo);
    
        unsigned int adresse = (unsigned int) sinfo.lpMinimumApplicationAddress;
        unsigned int start = NULL, end = NULL;
        char buffer[9];
        char suchWort[9];
        suchWort[0] = '_'; suchWort[4] = 'e';
        suchWort[1] = 'l'; suchWort[5] = 'l';
        suchWort[2] = 'e'; suchWort[6] = '0';
        suchWort[3] = 'v'; suchWort[7] = '/';
        suchWort[8] = ':';
    
        DWORD read = 0;
        bool found = true;
    
        do
        {
            VirtualQueryEx(hproc, (LPCVOID) adresse, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
    
            if (mbi.State == MEM_COMMIT && mbi.Protect == PAGE_READWRITE && mbi.Type == MEM_PRIVATE)
            {
                start = (unsigned int) mbi.BaseAddress;
                end = (unsigned int) mbi.BaseAddress + mbi.RegionSize;
                cout << hex << start << " - " << end << endl;
    
                for (start; start < end; start++)
                { 
                    ReadProcessMemory(hproc, (LPCVOID) start, &buffer, sizeof(buffer), &read);
    
                    found = true;
                    for (size_t j = 0; j < sizeof(suchWort); j++)
                    {
                        if (buffer[j] != suchWort[j])
                        {
                            found = false;
                            break;
                        }
                    }
    
                    if (found)
                    {
                        char buchstabe = 'a';
                        string variablenName = "";
                        unsigned int adresse = start + 9;
                        while (buchstabe)
                        {
                            ReadProcessMemory(hproc, (LPCVOID) adresse, &buchstabe, sizeof(buchstabe), &read);
                            adresse++;
                            if (buchstabe) variablenName += buchstabe;
                        }
                        cout << "\tVariable gefunden: " << variablenName << endl;
                    }
                }
            }
    
            adresse += mbi.RegionSize;
        }
        while (adresse < (unsigned int)sinfo.lpMaximumApplicationAddress);
    
        CloseHandle(hproc);
    }
    

    Wenn ich das Programm ausführe, crashed es am Ende (wenn ein sehr hoher Speicherbereich erreicht wurde)

    Frag dich nicht warum ich bei suchWort extra so primitiv vorgegangen bin, ein Vergleich war sonst nicht möglich, strcmp lieferte mir obgleich 2 identischer char-arrays nie eine 0 ...

    Der obige Code sucht nur die String-Adressen die ich benötige, doch für die nächsten Suchanfragen müsste ich im Grunde das Gleiche machen, nur mit anderen Suchwerten.


Anmelden zum Antworten