HAKATEMIA
13Hands-on

Funktiot ja kutsupinot

Keskitaso35MIN

Funktiot ja kutsupinot

Tähän asti ohjelmamme ovat olleet lineaarisia rivilistauksia. Oikeat ohjelmat jakavat työn funktioihin (aliohjelmiin), joita kutsutaan eri puolilta ohjelmaa. Assemblyssa funktiokutsut rakentuvat kahdesta peruskomponentista: CALL/RET-parista ja kutsupinosta (call stack), jolle laitetaan parametrit, paluuosoite (return address) ja paikallismuuttujat.

Kutsupinon kertaus

Pino kasvaa muistissa ylhäältä alaspäin: PUSH vie ESP-rekisteriä pienempään osoitteeseen ja POP päinvastoin. ESP osoittaa aina viimeksi pinotun arvon paikkaan.

X86
1PUSH 100        ; ESP -= 4, talleta 100 muistiin osoitteeseen ESP
2PUSH 200        ; ESP -= 4, talleta 200
3POP EBX         ; EBX = 200, ESP += 4
4POP EAX         ; EAX = 100, ESP += 4

CALL ja RET

CALL label tekee kaksi asiaa: pinoaa paluuosoitteen (sen rivin osoitteen, joka tulee CALLin jälkeen) ja sitten hyppää labeliin. Kun funktio ajaa RET:n, paluuosoite poppataan pinosta ja suoritus jatkuu siitä.

Koodi
Konsoli
Rekisterit
EAX0
EBX0
ECX0
EDX0
EIP0
ESP256
ESI0
EDI0
EFLAGS
ZF0
SF0
OF0
CF0
DF0
PF0
Muisti
0000
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
0010
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
0020
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
0030
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
0040
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
0050
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
0060
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
0070
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
0080
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
0090
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00a0
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00b0
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00c0
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00d0
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00e0
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00f0
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00

Yllä oleva ohjelma asettaa EAX=1234 funktiossa, palaa kutsujalle, tulostaa luvun ja lopettaa HLT:llä. Tärkeä huomio: ilman HLT:tä suoritus jatkuisi pääohjelman jälkeen funktion määrittelyyn, mikä ei ole tarkoitus.

Parametrien välitys pinon kautta

Funktiolle annetaan parametrit pinoamalla ne ennen CALLia, oikealta vasemmalle (eli viimeisin parametri pinotaan ensin). Tätä noudattaa cdecl -kutsutapaa, joka on yleinen 32-bittisessä x86-koodissa. Paluuarvo jätetään yleensä EAX-rekisteriin.

Stack frame ja EBP

Funktion sisällä on tapana lukita viittauspiste pinoon EBP-rekisterillä: paikallismuuttujat ja parametrit luetaan suhteessa siihen, ei jatkuvasti liikkuvaan ESP:hen.

Prologue ja epilogue ovat kahden käskyn rituaaleja: Pinokehys prologuen jälkeen: paikallismuuttujat, vanha EBP, paluuosoite ja parametrit osoitteissa [EBP+8] ja siitä eteenpäin.

  • Prologue (funktion alku):
    X86
    1PUSH EBP            ; tallenna kutsujan EBP pinoon
    2MOV EBP, ESP        ; ankkuroi EBP nykyiseen pinon huippuun
  • Epilogue (funktion loppu):
    X86
    1MOV ESP, EBP        ; palauta pinon huippu prologuessa olleeseen tilaan
    2POP EBP             ; palauta kutsujan EBP

Epiloguen sijaan disassemblyssä näkee usein yksinkertaisen LEAVE-käskyn: se on tasan MOV ESP, EBP + POP EBP. Prologue puolestaan kirjoitetaan aina PUSH EBP; MOV EBP, ESP eksplisiittisesti. (x86:lla on myös yhdistetty ENTER imm16, imm8 -käsky, joka pinoaa kehyksen ja varaa imm16 tavua paikallisille. Se on kuitenkin mikrokoodattu ja hidas, joten modernit kääntäjät eivät emittoi sitä.)

Esimerkki: kahden luvun yhteenlasku-funktio

Koodi
Konsoli
Rekisterit
EAX0
EBX0
ECX0
EDX0
EIP0
ESP256
ESI0
EDI0
EFLAGS
ZF0
SF0
OF0
CF0
DF0
PF0
Muisti
0000
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
0010
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
0020
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
0030
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
0040
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
0050
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
0060
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
0070
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
0080
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
0090
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00a0
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00b0
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00c0
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00d0
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00e0
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00f0
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00
00

Aja ohjelma rivi kerrallaan emulaattorissa ja seuraa, miten ESP ja EBP liikkuvat. Pinon arvot ovat luettavissa muistinäkymässä; huomaat itse, miten paluuosoite ja parametrit asettuvat prologuen (PUSH EBP; MOV EBP, ESP) jälkeen.

Miksi siivota parametrit?

cdecl:ssä kutsuja vastaa parametrien siivoamisesta pinosta CALLin jälkeen. ADD ESP, 8 kahdelle 32-bittiselle parametrille. Ilman siivoamista pino kasvaisi joka kutsulla ja luultavasti vuotaisi yli kohtuullisen pian.

Reverse engineeringissä törmäät myös stdcall:iin (käytetty Win32 API:ssa), jossa funktio itse siivoaa parametrit RET N -muodolla, sekä modernimpiin rekistereihin perustuviin kutsutapoihin.

1 / 3
Hakatemia Pro

Hakkeroinnin oppiminen alkaa tästä

Sadat interaktiiviset kurssit, virtuaalilabrat ja CTF-haasteet selaimessasi. Aloita ilmainen kokeilu ilman korttitietoja.