WebElektronika

Buffer Overflow alapjai a mikrovezérlőknél

person access_time 2018.08.07.
Visszatérünk a WebElektronika eredeti cékitűzéséhez, a beágyazott rendszerek minél szélesebb megismeréséhez. De az IT biztonsággal foglalkozó sorozatunk is fut, ezért most ezt a két területet összekapcsoljuk. Megnézzük, mit is jelent a Buffer Overflow, és ezt előállítjuk a PIC32MX mikrovezérlőnél. Erre a hibára később még visszatérünk, amikor ezt a hibát egy operációs rendszernél használjuk ki.


A Buffer Overflow (bof) egy szoftverhiba, egy biztonsági rés, amelyet egy támadó távolról ki tud használni. Ennek a hibának az alapja az, hogy nem történik ellenőrzés akkor, amikor a fix méretű tömbbe adatot írunk.  Ezért, ha nagyobb adatot kívánunk eltárolni egy kisebb méretű tömbbe, akkor a "maradék" adatot  a szomszédos memóriaterületre írjuk, az ott lévő adatot írjuk felül. Mostani cikkünkben ennek a hibának az előállítását nézzük meg, és használjuk fel, továbbá megismerjük az MPLABX szimulációs lehetőségét is.

Hozzunk létre egy új projektet az MPLABX fejlesztőkörnyezet segítségével, az alkalmazni kívánt mikrovezérlő legyen PIC32MX795F512L. A projekt létrehozása után adjunk hozzá egy új forrásfile-t, amelynek a tartalma legyen a következő kód:

#include <p32xxxx.h>
#include <string.h>

void fgv(char* parameter);

main()
{
    char tomb[10] = {'a','b','c','d','e',
    'f','g','h','i','j'};
    while(1)
        fgv(tomb);
}

void fgv(char* parameter)
{
    char t[5];
    strcpy(t,parameter);
}

 

A fgv() függvény segítségével létrehozunk egy ötelemű karaktertömböt (string), és a függvény meghívásakor átadott paraméter (amely tízelemű (!)) tartalmát  bemásoljuk a "t" tömbbe, amely csak öt elemből áll.
A main() függvényben létrehozunk egy tízelemű tömböt ("tomb"), amelyet fel is töltünk az ABC betűivel. Ezután a while(1) végtelen ciklusban a fgv() függvényt hívjuk meg.

Nézzük most meg a fejlesztőszoftver segítségével, hogy a programunk hogyan viselkedik. Ehhez debug módot kell alkalmaznunk.
Először álljunk rá egérrel a projekt nevére és a jobb egérgombbal kattintsunk a "Properties"-re, és ezután állítsuk be a "Simulator"-t (1. ábra). (Természetesen erre a műveletre csak akkor van szükség, ha a projekt létrehozásakor ezt nem tettük meg.)

kep
1. ábra   Simulator engedélyezése
 

Ezután a "Tools/Options" menüpontra kattintsunk, és az "Options" ablakban az "Embedded" fül "Generic Settings"-nél a "Debug startup"-ot állítsuk be "Halt at Main"-re (2. ábra).

kep
2. ábra   "Halt at Main" beállítása
 

A "Windows/Debugging" menüpontnál különböző tulajdonságokat, regiszterértékeket, stb tudunk megfigyelni a szimuláció során (3. ábra), mi a Buffer Overflow demonstrálásánál csak a "Variables" ablak tartalmát fogjuk nyomon követni.

kep
3. ábra   Különböző szimulációs lehetőségek
 

A beállításokkal készen is vagyunk, fordítsuk le a projektet (4. ábra). Kattintsunk a kalapács ikonra, vagy nyomjuk meg az F11-et.

kep
4. ábra   A projekt lefordítása
 

Ezután indítsuk el a Debug módot, kattintsunk a "Debug Main Project" ikonra (5. ábra).

kep
5. ábra   A Debug mód elindítása
 

A zöld sáv jelzi azt, hogy a Debug mód melyik programsortól fog elindulni. Lépésről lépésre fogjuk megnézni (és elemezni) a szimulációs eredményeket, ezért a "Step Into" ikonra kell majd kattintanunk (vagy megnyomni az F7-et), ha lépni szeretnénk a szimuláció során.
Először létrehozzuk a "tomb" nevű tízelemű tömböt, és ezt fel is töltjük az ABC különböző betűivel (6. ábra). Látható, hogy a tömb kezdeti értékei 0x0, majd megtörténik az értékadás is.

kep
6. ábra   A tízelemű tömb létrehozása
 

A következő lépés az, hogy a végtelen ciklusban meghívjuk a fgv() függvényt, amelynek a feladata az, hogy az átadott paramétert bemásolja az ötelemű tömbbe. Látjuk a 7. ábrán, hogy a "tomb" nevű tömb már tartalmazza az ABC különböző karaktereit.

kep
7. ábra   A fgv() függvény meghívása, a tomb nevű tömb paraméterként történő átadása
 

Látjuk a 8. ábrán lévő szimulációs eredményből, hogy a "t" nevű ötelemű tömbbe belekerült a paraméterként átadott tömb első öt eleme (a, b, c, d, e). A "Variables" ablakban látjuk a memória címeit is, ahol ezek az értékek elhelyezésre kerültek.

kep
8. ábra    A "t" tömb elemei
 

Látszólag a programunk jól működik (pontosabban fogalmazva, nem látjuk a "Variables" ablakban a maradék öt karaktert, amelyet a "t" tömb a kisebb mérete miatt nem is tartalmazhat). De ismételjük meg a korábbi szimulációs lépéseket újra!
A 9. ábrán látjuk a szimulációs eredményből, hogy amikor a függvényünket újra meghívjuk, akkor a "tomb" nevű tömbünk 0. elemét már nem adjuk át, helyette egy "0x0" byte kerül ebbe a memóriarekeszbe.

kep
9. ábra   Rossz érték kerül abba a memóriarekeszbe, ahol korábban az "a" karakter volt
 

Látható volt, hogy ez a nagyon egyszerű hiba milyen problémát tud okozni, amelyet nehéz megtalálni a hibakeresés során (és a memóriatúllépést nem is említettük). Ez a hiba nem is jelentkezik mindig, csak akkor, amikor nagyobb tömböt kívánunk átadni az fgv() függvény meghívásakor.

Érdemes kipróbálni ezt a programot úgy is, hogy nem egy 10 elemű tömböt hozunk létre a main() függvényben, hanem "csak" 7 eleműt. Ekkor nem tapasztaljuk a 9. ábrán látható eredményt.

Később érinteni fogjuk újra a Buffer Overflow-ot, de akkor egy operációs rendszert fogunk tesztelni.