Wie würde man vorgehen, um Speicher aus einem Prozess zu lesen? Ist es anders nach Betriebssystem?

11

Als erfahrener Webentwickler, aber ein unerfahrener "Low-Level" -Programmierer, ist dieses Zeug für mich immer noch eine Art Voodoo.

Ich bin gespannt, wie man überhaupt anfangen würde, einen Speicherblock zu finden und ihn dann zu lesen (zum Beispiel den Timer in einem Minenspiel zu lesen). Unterscheidet sich dies je nach OS / OS-Version?

Eva
quelle

Antworten:

14

Dies ist eine etwas knifflige und sogar komplexe Frage, in dem Sinne, dass Sie damit ziemlich tief gehen können. Aber um die Dinge ein bisschen zu vereinfachen:

Betrachten Sie Ihr Beispiel für " Lesen des Timers in einem Minenspiel ":

Der erste Schritt besteht darin , die Speicheradresse zu finden . Dies erfolgt normalerweise mit einem Tool namens Speichereditor (in einigen Fällen auch mit einem Debugger), das mithilfe verschiedener Methoden den Speicherort einer Variablen im Prozessspeicher ermitteln kann . Es ist üblich, im Speicher des Zielprozesses nach einem bestimmten Wert (z. B. dem Wert des Timers) zu suchen, dann den Zielwert zu ändern (z. B. den Timer vorzuschieben) und erneut nach Übereinstimmungen zu suchen. Dieser Prozess fixiert die Kandidaten bei jeder Iteration, bis nur noch die genaue Variable übrig ist. Das Abrufen der Adresse dieser Variablen im Speicher des Prozesses ist nur ein Mausklick mit einem Speichereditor.

Der zweite Schritt besteht darin, die Daten in dieser bestimmten Adresse zu ändern . Wie dies geschieht, hängt vom Betriebssystem ab. Unter Windows gibt es einen WinAPI-Aufruf mit dem Namen, mit WriteProcessMemorydem vordefinierte Daten an eine bestimmte Adresse im Speicher des Zielprozesses geschrieben werden können. In unserem Beispiel würden Sie diese Funktion verwenden, um die Timer-Variable im Zielprozess mit Ihrem eigenen gewünschten Wert zu überschreiben und den Timer im Spiel effektiv zu ändern.

In der Praxis müssten Sie die Prozess- ID des Zielprozesses ermitteln und dann Ihren Rogue-Prozess an den Zielprozess anhängen , um dessen Speicherplatz ändern zu können. Dies ist eine ziemlich triviale Aufgabe, trägt aber nicht zur Beantwortung der Frage bei, so dass ich sie dem Leser als Übung ausgelassen habe. ;)

zxcdw
quelle
Wird der Speicherort der Variablen bei jedem Ausführen des Prozesses gleich sein?
Brunoqc
1
@brunoqc Nein, fast sicher nicht.
Dan
2

Es ist völlig anders von OS. Einige erlauben es Ihnen überhaupt nicht, und Sie müssen wissen, wo sich die gewünschten Daten im Speicher des Ziels befinden.

So geht's unter Linux: /unix/6301/how-do-i-read-from-proc-pid-mem-under-linux

pjc50
quelle
Welches Betriebssystem würde es nicht zulassen? Es scheint, dass Sie es zumindest unter Linux und Windows tun können
Eva
@Evan Jedes Betriebssystem muss (aus Sicherheitsgründen eingeschränkt) Mittel zum Ändern des Remote-Prozessspeichers für Debugging-Zwecke zulassen. Beachten Sie jedoch, dass beispielsweise aus Sicherheitsgründen nicht übereinstimmende Berechtigungen dazu führen können, dass der Prozessspeicher eines Prozesses mit höheren Berechtigungen nicht gelesen / geschrieben werden kann. Ein gutes Beispiel hierfür wäre, einem Prozess mit Gastbenutzerrechten nicht zu erlauben, Speicher eines Prozesses mit Administratorrechten zu lesen / schreiben, da dies möglicherweise das gesamte System gefährden und ein direkter Weg für einen Gastbenutzer sein könnte, Administratorrechte zu erlangen das System.
zxcdw
1

Eine Anwendung erhält vom Betriebssystem einen Speicherbereich. Im Allgemeinen muss die Anwendung den Speicher anfordern, aber diese Funktionalität kann für den Programmierer aufgrund der Sprache verdeckt sein.

Sprachen wie C ermöglichen Blockanforderungen für bestimmte Größen, während andere Sprachen wie C ++, C # und Java Anforderungen mithilfe von Schlüsselwörtern wie ermöglichen new. Jede Sprache verfügt über verschiedene Möglichkeiten, Speicher zuzuweisen. Dies ist also nur eine kurze Übersicht. Die Freigabe des Speichers an das Betriebssystem kann entweder explizit oder über einen Garbage Collector erfolgen.

Der Zugriff auf den Speicher in der Anwendung hängt davon ab, wie er zugewiesen wurde. C und C ++ sind am bekanntesten für die Verwendung des Konzepts von Zeigern, um anzuzeigen / zu verfolgen, wo sich der Speicher befindet. Andernfalls wird der Speicherzugriff über die erstellte Klasse oder Variable abgewickelt.

Meistens müssen Sie sich nicht um einen bestimmten Speicherzugriff in Ihrem Programm kümmern. Die Sprachkonstrukte und das Betriebssystem verschleiern diese Bedenken für Sie effektiv.

Ihr Beispiel für einen Timer in einem Spiel ist ein großartiges Beispiel dafür, wo Sie sich keine Gedanken über die zugrunde liegende Speicherzuordnung machen müssen. Sie haben eine Variable, die den Timer darstellt, und Sie lesen nur aus der Variablen.

Meine Antwort ist relevant für das Schreiben der Anwendung, während die Antwort von zxcdw für den Zugriff auf Speicher relevant ist, der zu einer anderen Anwendung gehört. Ihre LMGTFY-Begriffe wären "Debugging" und "Reverse Engineering", um sich weiter mit diesem Thema zu befassen.

Einige zusätzliche Lektüre:

  • Der Wikipedia-Artikel über Computerspeicher gibt Ihnen einen anständigen Überblick über die Dinge.
  • Schauen Sie sich dann den Wikipedia-Artikel über speicherabgebildete E / A an, um ein tieferes Verständnis der laufenden Machenschaften zu erhalten.
  • Lesen Sie abschließend den Artikel über virtuellen Speicher , um eine bessere Antwort darauf zu erhalten, wie jedes Betriebssystem die Speicherzuordnung ein wenig anders als die anderen behandelt.

quelle
Hervorragende Antwort, obwohl ja, in diesem Fall geht es mir mehr darum, Speicher aus einer Anwendung zu lesen, die ich nicht selbst geschrieben habe. Ich werde upvoten, sobald ich 15 Wiederholungen habe;)
Eva
@Evan - dann lesen Sie auf jeden Fall den Artikel über virtuellen Speicher. Dadurch erhalten Sie ein besseres Verständnis für die Zuordnung. Sie müssen die Zuordnung verstehen, um eine App auswählen zu können. Bei den meisten Debuggern können Sie eine Verbindung zu einem laufenden Prozess herstellen, um die Anweisungen und den Speicher der Anwendung anzuzeigen.