Hoppa yfir í efnið

Forritunarmálið C

Dennis Ritchie þróaði forritunarmálið C á áttunda áratugnum til að endurskrifa Unix-stýrikerfið. C er þýtt yfir í smalamál, sem gerir málið hentugt til að kanna hvað gerist í vélarkóða.

Mörg bakhönnunarverkefni eru skrifuð í C og síðan þýdd yfir í vélarkóða sem er vistaður í tvíundaskrá. Til að lesa úttak bakþýðenda þurfum við því að þekkja grunnatriði C.

Minnisstjórnun

Í C sér forritarinn sjálfur um að úthluta (e. allocate) og losa (e. free) minni. Þetta er ólíkt Python og Java, þar sem þetta gerist sjálfkrafa.

int x = 5;           // staðvær breyta, á stafla
int *p = malloc(4);  // kvikt minni (e. dynamic memory), á hrúgu

Strengir

Strengir í C eru í raun fylki af bætum sem enda á \0, svokölluðu \0-bæti. Þetta kallast núllendaður strengur.

char name[] = "hello";
// Í minni: [ 'h', 'e', 'l', 'l', 'o', '\0' ]
//           0x68  0x65  0x6c  0x6c  0x6f  0x00

Ef strengur er geymdur án \0 geta föll á borð við printf og strlen ekki greint hvar hann endar. Þau lesa þá áfram í minni þar til þau finna næsta \0-bæti. Þetta getur leitt til upplýsingaleka.

Bendlar

Bendill er breyta sem geymir vistfang í minni, ekki gildið sjálft.

int x = 42;
int *p = &x;       // p geymir vistfang x (& = "vistfang x")
printf("%d", *p);  // * = aftilvísun: sækir gildið sem p bendir á → 42

Í smalamáli eru bendlar mikið notaðir. Þegar baksmalinn sýnir [rax] merkir það „gildið á vistfanginu sem rax geymir“ (sjá aftilvísun).

Gagnatög og algengar stærðir

Taflan sýnir algengar stærðir í 64-bita Linux-kerfi sem notar LP64-líkanið. Stærðir geta verið aðrar á öðrum kerfum.

Gerð Stærð Bilið
signed char 1 bæti -128 til 127
unsigned char 1 bæti 0 til 255
int 4 bæti -2^31 til 2^31-1
long / long long 8 bæti -2^63 til 2^63-1
bendill 8 bæti (64 bitar)

Heiltöluyfirflæði

Heiltöluyfirflæði (e. integer overflow) verður þegar niðurstaða rúmast ekki í gagnataginu. Yfirflæði formerkjalausra heiltalna vefst samkvæmt reglum C, en yfirflæði formerktra heiltalna er óskilgreind hegðun nema sérstakar þýðandastillingar, til dæmis -fwrapv, skilgreini annað. Slíkar stillingar koma stundum fyrir í CTF-verkefnum.


Óörugg föll sem við leitum að

Þegar við opnum forrit í bakþýðanda leitum við að þessum föllum:

Fall Vandinn
gets(buf) Les ótakmarkað magn gagna og er því alltaf óöruggt
strcpy(dst, src) Afritar streng án þess að athuga lengd
scanf("%s", buf) Getur lesið of langan streng ef hámarkslengd er ekki tilgreind
sprintf(buf, fmt, ...) Getur skrifað út fyrir mörk buf
printf(buf) Getur valdið sniðstrengsveikleika ef buf kemur frá notanda

Þegar við sjáum eitthvert þessara falla í bakþýðanda könnum við hvort inntak notandans geti borist til þess. Ef svo er kann að vera hægt að nýta veikleika þar.


Að lesa úttak bakþýðanda

Bakþýðandi, til dæmis Ghidra, IDA eða Binary Ninja, þýðir vélarkóðann yfir í C-líkan kóða. Úttakið er ekki nákvæmur C-kóði heldur mat tólsins. Staðværar breytur liggja yfirleitt í vakningarfærslu (e. stack frame) fallsins. Hér eru algeng fyrirbæri:

// Tólin gefa ónefndum breytum óskiljanleg nöfn eins og:
undefined8 local_48;   // Átta bæta stór breyta (gæti verið char[8] eða long)
long local_10;         // Breyta sem liggur í vakningarfærslunni

// Þetta þýðir venjulega char[64] eða svipað:
char local_48 [64];

// Tímabundnar breytur fá oft nöfn eins og:
int iVar1;   // Tímabundin breyta af gagnataginu int (Ghidra)
int v1;      // Sama breyta í IDA

Endurnefnum föll og breytur

Þegar við skiljum hlutverk falls eða breytu endurnefnum við það (e. rename). Það gerir kóðann mun auðveldari að lesa. Við getum til dæmis breytt local_48 í user_input. Flýtilykillinn er L í Ghidra og N í IDA og Binary Ninja.


Æfingar

Verkefni til að æfa okkur í að lesa C-líkan kóða og fylgja því hvernig forrit vinna með gögn:

  • Decoder Ring: Við skoðum einfalda umbreytingu og endurheimtum inntakið sem forritið samþykkir.
  • Conveyor Belt: Við rekjum lykkju sem vinnur með hvert bæti á mismunandi hátt eftir staðsetningu þess.
  • High Score: Við greinum forrit þar sem niðurstaðan er ekki eins augljós og hún virðist í fyrstu.
  • Treasure Map: Við fylgjum tengingum milli gagna í minni og finnum rétta lestrarröð þeirra.