Analysiert man den Arbeitsspeicher, so muss man früher oder später virtuelle in physische Adressen umrechnen. Beim ersten Mal kann das durchaus Schwierigkeiten bereiten. Dieser Artikel erklärt das Vorgehen anhand eines Beispiels.
Zur Einführung in die Thematik und Erläuterung der technischen Hintergründe empfehle ich die Lektüre von
- Undocumented Windows 2000, Kapitel 4, von Sven B. Schreiber
- Windows Internals (4. Aufl.), Kapitel 7, von Mark Russinovich und David Salomon.
Außerdem sollten Sie sich unbedingt die kostenlose Dokumentation für den Pentium-Prozessor von Intel besorgen. Kapitel 3.7 im Band 3A erklärt die Funktionsweise der 32-Bit Adressierung. Warum man die überhaupt verstehen muß? Diese Frage beantwortet der folgende Ausschnitt aus einer Sitzung mit dem Debugger:
kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
PROCESS 829516a0 SessionId: 0 Cid: 0008 Peb: 00000000 ParentCid: 0000
DirBase: 00030000 ObjectTable: 82976108 TableSize: 235.
Image: System
PROCESS 8246c6a0 SessionId: 0 Cid: 00a4 Peb: 7ffdf000 ParentCid: 0008
DirBase: 04e4b000 ObjectTable: 8246cd88 TableSize: 33.
Image: SMSS.EXE
PROCESS 82442020 SessionId: 0 Cid: 00bc Peb: 7ffdf000 ParentCid: 00a4
DirBase: 07409000 ObjectTable: 824ec9a8 TableSize: 399.
Image: CSRSS.EXE
PROCESS 824038c0 SessionId: 0 Cid: 00d0 Peb: 7ffdf000 ParentCid: 00a4
DirBase: 07dce000 ObjectTable: 82404ae8 TableSize: 422.
Image: WINLOGON.EXE
Ist es nicht seltsam, dass alle Prozesse den selben Process Environment Block (Peb) zu verwenden scheinen? Der Peb ist eine große Struktur, die unter anderem auch auf das Abbild der in den Arbeitsspeicher geladenen Programmdatei verweist. Offensichtlich können deshalb nicht alle Prozesse den selben Peb verwenden! Die Lösung dieses Problems ist überraschend einfach: bei der Adresse 0x7ffdf000 des Peb handelt es sich um eine virtuelle Adresse. Sie ist deshalb nur im Kontext des jeweiligen Prozesses gültig. Das hilft Ihnen aber nicht viel, wenn Ihnen für Ihre Untersuchung lediglich ein Abbild des Arbeitsspeichers zur Verfügung steht, denn nun müssen Sie die virtuelle Adresse in eine physische Umrechnen (und diese dann nochmals in einen Offset innerhalb des Abbildes, aber das ist eine andere Geschichte).
Für das folgende Beispiel verwende ich das erste Abbild aus der DFRWS Memory Analysis Challenge. Zum einen ist es frei erhältlich. Zum anderen wurde es mit dd erstellt. Physische Adresse und Dateioffset sind deshalb identisch.
Betrachten Sie bitte den Peb des Prozesses UMGR32.EXE. Wie Sie aus der Dokumentation von Intel ersehen können, ist der Ausgangspunkt für die Umrechnung von virtuellen in physische Adressen das Control Register CR3 der CPU. Sein Wert wird sich für jeden Prozess unterscheiden, denn jeder Prozess verfügt über seinen eigenen individuellen virtuellen Adressraum. Der jeweilige Wert von CR3 ist in der Variablen DirectoryTableBase der Struktur EPROCESS gespeichert. Ich habe PTfinder leicht verändert, so dass es diesen Wert in der Spalte "PDB" ausgibt.
> ptfinder.pl --nothreads dfrws2005-physical-memory1.dmpNo. Type PID TID Time created Offset PDB Remarks ---- ---- ------ ------ ------------------- ---------- ---------- ---------------- 4 Proc 668 2005-06-05 00:55:08 0x0095f020 0x075a7000 UMGR32.EXE
Und damit kann es jetzt endlich losgehen. Zunächst gilt es die (virtuelle) Adresse des Peb herauszufinden. Öffnen Sie dazu das Speicherabbild in einem Hexeditor und betrachten Sie den Offset 0x0095f020. Tragen Sie ab hier den Offset zum Peb ab (0x1b0 für Windows 2000). Auf diese Weise finden Sie die Bytefolge 0x00 0xf0 0xfd 0x7f. Die Adresse des Peb lautet also 0x7ffdf000.
Schreiben Sie diese Adresse als Binärzahl auf. Die 10 höherwertigsten Bits geben Ihnen den Index in das Page Directory an. In diesem Beispiel lautet der Index 0x1ff.
| Page Directory Index | Rest | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 31 - 22 | 21-0 | ||||||||||||||||||||||||||||||
| 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 0x1ff (511) | |||||||||||||||||||||||||||||||
Wie oben gezeigt beginnt das Page Directory an der physischen Adresse 0x075a7000. Jeder Page Directory Entry (PDE) ist 4 Bytes groß. Die Adresse für den Eintrag Nr. 0x1ff ist also 0x075a7000+0x1ff*4 = 0x075a77fc. Der PDE enthält den Wert 0x05cee067.
| Page Table Base Address | unused | G | PS | 0 | A | CD | WT | U/S | R/W | P | |||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 31 - 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |||||||||||||||||||
| 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 1 |
| 0x5cee | |||||||||||||||||||||||||||||||
Als erstes sollten Sie die Page Size (PS) überprüfen. Wenn diesesFlag gesetzt ist, liegt die Adresse innerhalb einer Speicherseite von 4 MB Umfang. Windows verwendet diese großen Seiten nur, um den Code des Kernels zu speichern. Es ist deshalb nicht überraschend, dass die Adresse des Peb in einer kleinen (4 kB) Speicherseite liegt. Das Flag Present (P) zeigt an, dass diese Seite tatsächlich im physischen Speicher gehalten wird und nicht etwa ausgelagert wurde. Damit kann es weitergehen.
Da die Adresse innerhalb einer kleinen Speicherseite liegt, geben die nächsten 10 Bit (21 bis 12) den Index in der sog. Page Table an. Die restlichen 12 Bit enthalten den Offset innerhalb der Seite.
| Page Directory Index | Page Table Index | Offset in der Speicherseite | |||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 31 - 22 | 21 - 12 | 11 - 0 | |||||||||||||||||||||||||||||
| 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 0x1ff (511) | 0x3df (991) | 0 | |||||||||||||||||||||||||||||
Die Page Table beginnt an der Adresse 0x5cee * 0x1000. Jeder Page Table Entry (PDE) ist 4 Bytes lang. Dies führt auf den Eintrag Nr. 0x3df an der physischen Adresse 0x5cee * 0x1000 + 0x3df * 4 = 0x5ceef7c. Er enthält den Wert 0x0532f047.
| Page Base Address | unused | G | PAT | D | A | CD | WT | U/S | R/W | P | |||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 31 - 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |||||||||||||||||||
| 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 1 |
| 0x0532f | |||||||||||||||||||||||||||||||
Das ergibt nun endlich die Page Base Address von 0x0532f * 0x1000. Der Offset innerhalb dieser Seite ist 0. Für die virtuelle Adresse 0x7ffdf000 lautet die physische Adresse damit 0x0532f * 0x1000 + 0 = 0x532f000.
