Skip to content

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]