Sie können eine Gerätedatei mit mmap(2)
einem Benutzerprozessspeicher zuordnen Systemaufruf. Normalerweise sind Gerätedateien Abbildungen des physischen Speichers auf das Dateisystem. Andernfalls müssen Sie ein Kernelmodul schreiben, das eine solche Datei erstellt oder eine Möglichkeit bietet, den benötigten Speicher einem Benutzerprozess zuzuordnen.
Eine andere Möglichkeit besteht darin, Teile von /dev/mem einem Benutzerspeicher zuzuordnen.
Bearbeiten:Beispiel für mmaping /dev/mem (dieses Programm muss Zugriff auf /dev/mem haben, z. B. Root-Rechte haben):
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
if (argc < 3) {
printf("Usage: %s <phys_addr> <offset>\n", argv[0]);
return 0;
}
off_t offset = strtoul(argv[1], NULL, 0);
size_t len = strtoul(argv[2], NULL, 0);
// Truncate offset to a multiple of the page size, or mmap will fail.
size_t pagesize = sysconf(_SC_PAGE_SIZE);
off_t page_base = (offset / pagesize) * pagesize;
off_t page_offset = offset - page_base;
int fd = open("/dev/mem", O_SYNC);
unsigned char *mem = mmap(NULL, page_offset + len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, page_base);
if (mem == MAP_FAILED) {
perror("Can't map memory");
return -1;
}
size_t i;
for (i = 0; i < len; ++i)
printf("%02x ", (int)mem[page_offset + i]);
return 0;
}
busybox devmem
busybox devmem
ist ein winziges CLI-Dienstprogramm, das /dev/mem
mmmaps .
Sie können es in Ubuntu erhalten mit:sudo apt-get install busybox
Verwendung:4 Bytes von der physikalischen Adresse 0x12345678
lesen :
sudo busybox devmem 0x12345678
Schreiben Sie 0x9abcdef0
an diese Adresse:
sudo busybox devmem 0x12345678 w 0x9abcdef0
Quelle:https://github.com/mirror/busybox/blob/1_27_2/miscutils/devmem.c#L85
mmap MAP_SHARED
Beim Mapping /dev/mem
, möchten Sie wahrscheinlich verwenden:
open("/dev/mem", O_RDWR | O_SYNC);
mmap(..., PROT_READ | PROT_WRITE, MAP_SHARED, ...)
MAP_SHARED
bewirkt, dass Schreibvorgänge sofort in den physischen Speicher gehen, was die Beobachtung erleichtert und für Hardware-Registerschreibvorgänge sinnvoller ist.
CONFIG_STRICT_DEVMEM
und nopat
Um /dev/mem
zu verwenden Um den regulären RAM auf Kernel v4.9 anzuzeigen und zu ändern, müssen Sie zuerst:
- deaktiviere
CONFIG_STRICT_DEVMEM
(standardmäßig auf Ubuntu 17.04 eingestellt) - übergeben Sie den
nopat
Kernel-Befehlszeilenoption für x86
IO-Ports funktionieren auch ohne diese.
Siehe auch:mmap von /dev/mem schlägt mit ungültigem Argument für virt_to_phys-Adresse fehl, aber Adresse ist seitenausgerichtet
Cache leeren
Wenn Sie versuchen, in den RAM statt in ein Register zu schreiben, wird der Speicher möglicherweise von der CPU zwischengespeichert:Wie wird der CPU-Cache für einen Bereich des Adressraums in Linux geleert? und ich sehe keinen sehr tragbaren/einfachen Weg, um es zu leeren oder die Region als nicht zwischenspeicherbar zu markieren:
- Wie schreibt man Kernel Space Memory (physische Adresse) mit O_DIRECT in eine Datei?
- Wie wird der CPU-Cache für eine Region des Adressraums in Linux geleert?
- Ist es möglich, im Benutzerbereich einen nicht zwischenspeicherbaren Speicherblock unter Linux zuzuweisen?
Also vielleicht /dev/mem
kann nicht zuverlässig verwendet werden, um Speicherpuffer an Geräte zu übergeben?
Dies ist in QEMU leider nicht zu beobachten, da QEMU keine Caches simuliert.
So testen Sie es
Nun zum lustigen Teil. Hier sind ein paar coole Setups:
- Userland-Speicher
- weisen Sie
volatile
zu Variable in einem Userland-Prozess - erhalten Sie die physische Adresse mit
/proc/<pid>/maps
+/proc/<pid>/pagemap
- Ändern Sie den Wert an der physikalischen Adresse mit
devmem
, und beobachten Sie, wie der Userland-Prozess reagiert
- weisen Sie
- Kernelland-Speicher
- Kernelspeicher mit
kmalloc
zuweisen - erhalten Sie die physische Adresse mit
virt_to_phys
und an Userland zurückgeben - ändern Sie die physikalische Adresse mit
devmem
- den Wert vom Kernelmodul abfragen
- Kernelspeicher mit
- IO mem und virtuelles QEMU-Plattformgerät
- Erstellen Sie ein Plattformgerät mit bekannten physischen Registeradressen
- benutze
devmem
in das Register schreiben - schau dir
printf
an s kommen vom virtuellen Gerät als Antwort
Bonus:Ermitteln Sie die physische Adresse für eine virtuelle Adresse
Gibt es eine API zum Ermitteln der physischen Adresse aus der virtuellen Adresse in Linux?