Minnislíkan forrits
Þegar forrit er keyrt, fær það úthlutað minni frá stýrikerfinu. Þetta minni er skipt í ákveðna svæði (e. segments/regions).
Heildarmynd
Hærra vistfang (0x7FFFFFFFFFFF)
┌────────────────────────────────┐
│ STACK │ ← Staðværar breytur, return addresses
│ (vex niður á við) │
├────────────────────────────────┤
│ │
│ (óúthlutað svæði) │
│ │
├────────────────────────────────┤
│ HEAP │ ← malloc/calloc/realloc gögn
│ (vex upp á við) │
├────────────────────────────────┤
│ .bss │ ← Ófrumstilltar altækar breytur
├────────────────────────────────┤
│ .data │ ← Frumstilltar altækar breytur
├────────────────────────────────┤
│ .text │ ← Keyranlegur kóði
├────────────────────────────────┤
│ (libc og önnur [bókasöfn](../glossary.md#bokasafn)) │
└────────────────────────────────┘
Lægra vistfang (0x000000000000)
Stafli
Staflinn geymir: - Staðværar breytur (e. local variables): breytur sem skilgreindar eru innan falls - Return address: hvert á að fara þegar fallinu lýkur - Saved registers: gistigildi sem fall vistar áður en það notar þau
Stack frame
Þegar fall er kallað, er nýtt stack frame búið til:
... (fyrra fall)
┌──────────────┐
│ return addr │ ← ÞETTA viljum við yfirskrifa í pwn!
├──────────────┤
│ saved rbp │
├──────────────┤
│ local_var1 │
├──────────────┤ ← rbp - 0x10
│ buffer[64] │ ← Hér byrjar [biðminnið](../glossary.md#bidminni)
│ │
├──────────────┤ ← rbp - 0x50 = rsp
Return address
Return addressinn er geymdur á stafla. Ef við getum skrifað yfir hann (með buffer overflow), getum við fengið forritið til að hoppa hvert sem við viljum!
rsp og rbp
rsp(Stack Pointer): bendir alltaf á efsta hluta stafla (lægsta vistfangið)rbp(Base Pointer): bendir á botn núverandi stack frame
; Dæmigerð byrjun falls (e. function prologue):
push rbp ; Vista gamla rbp
mov rbp, rsp ; rbp = rsp (botn nýja ramma)
sub rsp, 0x50 ; Gera pláss fyrir staðværar breytur
; Dæmigerð lok falls (e. function epilogue):
leave ; mov rsp, rbp; pop rbp
ret ; pop rip (hoppar í return address)
Hrúga
Hrúgan er notuð þegar forrit þarf kvikt minni, þ.e. minni sem úthlutað er á keyrslutíma.
void *p = malloc(100); // Biður um 100 bæti á hrúgu
free(p); // Losar minni þegar búið
Hrúgan vex upp á við (öfugt við stafla).
malloc gögn (e. chunk)
Þegar kallað er í malloc(n) gefur libc þér bendi á minnisblokk (e. chunk):
┌──────────────┐
│ prev_size │ (8 bæti)
├──────────────┤
│ size | flags│ (8 bæti) ← Stærð blokkarinnar + flaggar
├──────────────┤
│ │
│ Notendagögn │ ← malloc() skilar bendi hingað
│ │
└──────────────┘
.data og .bss: Altækar breytur
int initialized = 42; // Fer í .data
int uninitialized; // Fer í .bss (sjálfkrafa 0)
const char *msg = "hi"; // Strengurinn fer í .rodata, bendirinn í .data
Þessir hlutar hafa fast vistfang ef PIE er ekki virkt, gagnlegt í pwn.
Að skoða minnislíkanið í GDB
# Í pwndbg:
vmmap # Sýnir alla minnissvæðið (e. memory mappings)
heap # Sýnir hrúguupplýsingar
stack 20 # Sýnir efstu 20 gildi á stafla
Dæmi um vmmap úttak:
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x400000 0x401000 r--p 1000 0 /challenge
0x401000 0x402000 r-xp 1000 1000 /challenge ← .text
0x402000 0x403000 r--p 1000 2000 /challenge ← .rodata
0x403000 0x404000 rw-p 1000 3000 /challenge ← .data/.bss
0x404000 0x425000 rw-p 21000 0 [heap]
0x7ffff7d00000 0x7ffff7f28000 r--p 228000 0 libc.so.6
...
0x7ffffffde000 0x7ffffffff000 rw-p 21000 0 [stack]