Skip to content
KitploitKITPLOIT
StrumentiBlog
Log in
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

FeedContattoPrivacy© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
page_table_walk — Percorri le tabelle delle pagine x86-64 a mano in qemu e gdb. Scomponi un indirizzo virtuale, segui cr3 attraverso tutti i livelli della memoria fisica ed estrai una flag da byte grezzi. | Kitploit
Strumenti/GitHubGitHub/jazho76/page_table_walk
Memory ForensicsReverse EngineeringDebuggerCTFAnalisi di BinariApprendimento e FormazioneLab e Pratica
GitHubjazho76/page_table_walk

page_table_walk

Percorri le tabelle delle pagine x86-64 a mano in qemu e gdb. Scomponi un indirizzo virtuale, segui cr3 attraverso tutti i livelli della memoria fisica ed estrai una flag da byte grezzi.

Vedi Repository
281136 mesi faRevisionato da Kitploit

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

Capture the Flag nella Memoria Fisica

Hai letto del paging. I diagrammi hanno senso. Quattro livelli, 9 bit ciascuno, page frame, offset. Certo. Ma poi ti imbatti in una sfida che richiede di camminare effettivamente nelle tabelle delle pagine, e ti rendi conto che non lo sai. Sai a grandi linee. Grande differenza.

Ciò che ha funzionato per me è stato sedermi davanti a QEMU e gdb e fare la camminata da solo: calcolare ogni indice, leggere ogni entry dalla memoria fisica, seguire ogni puntatore a mano. Un pomeriggio di questo può insegnare più di ore di lezioni.

Questa è una raccolta dei miei appunti da quel processo. Se ti manca ancora il lato concettuale, guarda per prima la lezione di Zardus sulla gestione della memoria del kernel. Quella è la teoria. Questo è il laboratorio.

L'obiettivo: prendere un indirizzo virtuale e inseguirlo attraverso la memoria fisica grezza fino a trovare i dati. Nessun helper del kernel. Nessuna astrazione. Solo una VM QEMU, gdb e memoria fisica grezza.

Alla fine, il paging non sarà qualcosa che hai letto, sarà qualcosa che conosci perché l'hai fatto a mano.


Allestimento del laboratorio

Un kernel precompilato e initramfs sono inclusi. L'ho eseguito in Fedora, ma qualsiasi sistema operativo che esegue QEMU e gdb dovrebbe funzionare. Installali con il tuo gestore di pacchetti:```

Ubuntu/Debian

sudo apt install qemu-system-x86 gdb

Fedora/RHEL

sudo dnf install qemu-system-x86 gdb

macOS

brew install qemu gdb

### Il binario della sfida

Il target è un semplice programma C che memorizza una flag in memoria e stampa il suo indirizzo virtuale:```c
#include <stdio.h>
#include <unistd.h>

int main(void)
{
    char secret[] = "FLAG{p4g3_t4bl3_w4lk3r}";

    printf("secret @ %p\n", (void *)secret);
    printf("pid = %d\n", getpid());
    printf("Spinning. Walk the page tables to find the flag.\n");

    while (1)
    {
    }
}

The busy loop is intentional. I originally used pause(), but that puts the process to sleep in a syscall: when gdb halts the VM, the CPU is likely running the idle task with a different CR3. A spinning loop keeps the process on-CPU, so halting guarantees you're in its context with the right page tables.

Il ciclo occupato è intenzionale. Inizialmente ho usato pause(), ma questo mette il processo in pausa in una syscall: quando gdb ferma la VM, la CPU sta probabilmente eseguendo il task idle con un CR3 diverso. Un ciclo di spinning mantiene il processo sulla CPU, quindi l'arresto garantisce di trovarsi nel suo contesto con le tabelle delle pagine corrette.

A pre-built initramfs with this binary is already included in initramfs.cpio.gz. If you need to rebuild it (Linux only, requires busybox and glibc-static), run make in this directory.

Un initramfs pre-costruito con questo binario è già incluso in initramfs.cpio.gz. Se è necessario ricostruirlo (solo Linux, richiede busybox e glibc-static), esegui make in questa directory.

Launching QEMU

Avviare QEMU```

./start.sh

Lo script avvia il kernel e l'initramfs inclusi sotto QEMU con `-s` (server gdb su `localhost:1234`) e `nokaslr` in modo che gli indirizzi del kernel rimangano fissi tra le esecuzioni.

La VM si avvia immediatamente e il binario della challenge viene eseguito. Vedrai l'indirizzo virtuale della flag stampato sulla console.```
secret @ 0x7ffe08985c90
pid = 1
Spinning. Walk the page tables to find the flag.

Annota quell'indirizzo virtuale. Questo è il tuo obiettivo.

QEMU terminal after boot, showing the challenge binary's output with the flag address and PID

Il tasto di escape predefinito di QEMU è Ctrl-a, ma questo collide con il mio prefisso tmux quindi lo script usa -echr 0x11 per rimapparlo a Ctrl-q. Se usi Ctrl-q per qualcos'altro, cambia il valore esadecimale in start.sh per adattarlo alla tua configurazione.

Collegamento di gdb

In un secondo terminale:``` gdb -ex "target remote :1234"

![gdb terminal after attaching, halted and ready](https://assets.kitploit.com/production/public/readmes/12435/9fb13e082596378677ea0d42cf7da3a86706df44127860fc724b2bad0a9f138c.png)

---

## Decomposizione dell'indirizzo virtuale

Hai un indirizzo virtuale. Ma dove sono i dati, _davvero_?

Gli indirizzi virtuali sono la finzione educata del sistema operativo. Ogni processo pensa di avere la propria memoria privata che inizia da zero. In realtà, i dati si trovano in una posizione completamente diversa nella RAM fisica. La tabella delle pagine è la mappa tra i due: una struttura ad albero che la CPU percorre ad ogni accesso alla memoria (o cerca nella sua cache TLB).

Quindi facciamo ciò che fa la CPU. Manualmente. Per tradurre quell'indirizzo, dobbiamo scomporlo negli indici che la CPU usa ad ogni livello.

Un indirizzo virtuale x86-64 è largo 48 bit. Quei 48 bit sono suddivisi in cinque campi:```
 63    48 47    39 38    30 29    21 20    12 11       0
┌────────┬────────┬────────┬────────┬────────┬──────────┐
│ sign   │  PGD   │  PUD   │  PMD   │   PT   │  Offset  │
│ extend │ index  │ index  │ index  │ index  │          │
│ (16b)  │ (9b)   │ (9b)   │ (9b)   │ (9b)   │  (12b)   │
└────────┴────────┴────────┴────────┴────────┴──────────┘

Ciascun indice a 9 bit seleziona una delle 512 voci in una tabella delle pagine a quel livello. L'offset a 12 bit seleziona un byte all'interno della pagina finale di 4 KB (0x1000). Per estrarre gli indici, esegui shift e maschera:``` PGD index = (VA >> 39) & 0x1FF PUD index = (VA >> 30) & 0x1FF PMD index = (VA >> 21) & 0x1FF PT index = (VA >> 12) & 0x1FF Offset = VA & 0xFFF

In gdb, puoi calcolare questi direttamente:```
(gdb) p/x (0x7ffe08985c90 >> 39) & 0x1ff
$1 = 0xff
(gdb) p/x (0x7ffe08985c90 >> 30) & 0x1ff
$2 = 0x1f8
(gdb) p/x (0x7ffe08985c90 >> 21) & 0x1ff
$3 = 0x44
(gdb) p/x (0x7ffe08985c90 >> 12) & 0x1ff
$4 = 0x185
(gdb) p/x 0x7ffe08985c90 & 0xfff
$5 = 0xc90

Prendi nota di questi. Li userai a ciascun livello corrispondente.

I tuoi valori saranno diversi. L'indirizzo 0x7ffe08985c90 è solo un esempio. Usa l'indirizzo stampato dal tuo binario della challenge.

Una nota sul paging a 5 livelli. CPU e kernel recenti supportano LA57, che aggiunge un quinto livello (PML5) sopra il PGD ed estende gli indirizzi virtuali a 57 bit. Il walk segue lo stesso schema: un indice di 9 bit in più, una ricerca in più nella tabella. La maggior parte dei sistemi utilizza ancora il paging a 4 livelli. Puoi verificare il tuo: cat /proc/cpuinfo | grep la57. Tutto in questo articolo assume il paging a 4 livelli.


Trovare CR3: la radice dell'albero

Ogni albero ha una radice. Per le tabelle delle pagine, quella radice è nel registro CR3: contiene l'indirizzo fisico della tabella di livello superiore, il PGD. Ogni processo ha il proprio valore CR3; il kernel lo scambia durante il context switch.

Questo è il nostro punto di ingresso nel walk. Leggilo da gdb:``` (gdb) info registers cr3 cr3 0x66c7000 [ PDBR=26311 PCID=0 ]

La base della tabella delle pagine è `0x66c7000`. I 12 bit bassi sono PCID/flag (zero qui), quindi l'indirizzo base è il valore così com'è.

Qui inizia la camminata.

---

## La camminata

Ecco il trucco: ogni livello segue lo stesso schema. I flag variano leggermente tra i livelli, ma il processo no. Lo schema:
Scarica lo strumento