Kategóriák
Munkával kapcsolatos észrevételek, kérések, kérdések, egyelőre egy halomba, minden ami idefér

A Z80 processzor diszkrét bája

Őskövület a feledés szemétdombjáról – mondhatnánk azonban több szempontból is rendkívüli figyelmet érdemlő mikroprocesszort, valamint a benne rejlő lehetőségek alkalmazását és ennek programvédelmi megfontolásait szeretném ebben a posztban bemutatni.

Ki ne szeretett volna már olyan hardveres programvédelmet készíteni, ami egyrészről hatósági eljárás céljából a programot rögzíti kiolvasható módon (eprom) ugyanakkor a konkurencia reverse-enginering próbálkozásai ellen megvédi azt?!

Erre kínál egy kiváló lehetőséget ez a processzor, és ennek használati technikáját szeretném röviden bemutatni itt. Ahhoz, hogy megértsük miként működik ez a védelmi megoldás, tekintsük át a processzor és a perifériák közti kommunikációt memória írás és olvasás tekintetében.

Processzor - busz kapcsolat
A processzor memória kapcsolat időbeni felosztása.

Az ábrán látható a Z80 processzor memória hozzáférés időzítése. Vegyük észre, hogy a processzor megkülönbözteti az utasítás lehívási ciklust a többi memória hozzáféréstől, ami lehetőséget biztosít az adat és programkód memória elválasztására. Ezzel a lehetőséggel azonban védelmi szempontból is tudunk élni, mégpedig pont úgy, hogy az adat és kód hozzáféréseket egy memóriában tároljuk lehetőleg jó alaposan összekeverve. Az utasítások és adatok alkalmasan összekevert mintája a program egyedi “ujjlenyomata” is lehet, és mivel a tárgykód változás esetén relokálódik, mert a fordításkor az utasítások számára foglalt hely változik, nagyon egyedi is lehet. Alkalmas fordítóprogrammal és makrók használatával a kódba ékelt adatok mennyisége a kód méretének néhány százalékos mértékét meghaladó méretben a visszafejtést nagyon megnehezíti.

Utasítás beolvasási ciklus
A processzor utasítás beolvasási ciklusa (M1)

A fenti ábrán a processzor utasítás lehívási ciklusa van ábrázolva, és pirossal kiemelve amikor a buszról utasítás byte-ot olvas a processzor. Ha egy utasítás több byte-ból áll, akkor a T1-T4 ciklusok ismétlődnek addig , míg a teljes utasítás beolvasásra nem kerül.

Amikor a processzor az utasítást végrehajtva memória műveletet végez – olvasási vagy írási esetleg R-M-W ciklust, akkor az alábbi diagram szerint történik a memória hozzáférés:

Processzor memória olvasási és írási ciklusa
A processzor – memória kapcsolat időbeni megvalósulása.

Ugyan a diagramon az M1 vonal nincs ábrázolva, de ezen ciklusok alatt logikai “H” szinten áll, azaz inaktív.

A programunk védelmére ez biztosítja a gyakorlatilag csak nagyon nehezen megfejthető megoldást.

Tekintsük az alábbi kódszakaszt és annak memóriába fordított gépi kódját!

példa a kódba ágyazott adathozzáférésre
kódba ágyazott adat

Vegyük észre, hogy a befordított tárgykódban a szubrutin hívó utasítás utáni két byte-ot a szubrutin ADATKÉNT olvassa fel a memóriából, a környező utasítás byte-okat viszont KÓDKÉNT! A különbséget a hardveres működés során a fentiekben ismertetett M1 vonal aktivitása jelzi.

A példában a veremtárban a stackpointer mögött egy lokális változót hozok létre, hivatkozás az (IX -2) mutatóval történik azt kinullázom, majd ötig elszámolok vele a példa kedvéért. A rutin végrehajtása után felszabadítom a veremtárban foglalt helyet. A foglalt hely mérete 2 byte, ennek komplemensét adom az SP regiszter aktuális értékéhez, ami tulajdonképpen a kivonást jelenti.

A programvédelem hardver alapja, hogy az adatbusz tartalmát a címbusztól függően összekeverem, ha kellően bonyolult a függvény, akkor ez már jól védi a memória tartalmát visszafejtés ellen, de azért néhány kreatív ötlettel visszafejthető. (lásd xx posztot, ezt még nem írtam meg, de tervben van) A hab a tortán csak ezután jön! A kódoló függvénybe vegyük bele az M1 vonal értékét például egy XOR kapun keresztül!

Amennyiben saját fordítóprogramunk van (én ezt használok) és azt úgy írtuk meg, hogy a program által generált objektum kód tartalmazza, hogy mely byte-ok utasítások és melyek adatok, akkor a tárgykód készítésekor (eprom tartalom előállítása) ezeket különféleképpen kódolva jutunk el a csaknem visszafejthetetlen kódig. A példában a kódoláson felül az adatként értelmezett byte-okat negálni kell.

Ezt azzal lehet elérni, hogy a példában bemutatott módon kreatív makrók alkalmazásával például egy érték paraméter szerinti rutin felhívást (c# switch – case struktúra) valósítunk meg:

switch-case példa

A példában a .db és .dw utasításokkal definiált byte-ok adatként kerülnek tárolásra, (még a cimkék is!) míg a többi kódként.

Ez a visszafejtésnél azért okoz nehézséget, mert a kódot soronként kell elemezni, hogy egy szubrutinhívás után adat vagy programkód következik, esetleg indirekt szubrutinhívással még jobban nehézzé tehető a helyzet. Ekkor annak, aki a kódot meg akarja fejteni a visszafordítás során el kell dönteni egy eprom adatról, hogy az adatként vagy kódként kerül feldolgozásra, mert ettől függ a processzor – memória közé beékelt kódoló logika működése. További nehézséget jelent a bemutatott példában az adatstruktúra értelmezése, ugyanis byte-os konstansok, word-os konstansok, és word hosszúságú szimbólumok értéke is kerülnek egymás után tárolásra. Ennek a struktúrának az értelmezése a felhívott szubrutin dolga, amit itt terjedelmi okok miatt nem mutatok be, de a működése könnyen érthető: az első .w konstans megmutatja hány ága van a switch szerkezetnek, utána byte-os konstansokkal felsorolásra kerülnek a különböző esetek, majd az illető esetekre és az ELSE ág lefordított tárgykódjára mutató szimbolikus címek értékei következnek.

Ez a processzor számára “in-situ” körülmények közt természetesen megy, az eredmény az M1 vonalon, de ez a kódolt eprom vizsgálatakor byte-onként nem áll rendelkezésre a kíváncsi tekintetek számára! 🙂

Nos, dióhéjban ennyit szeretnék elmondani erről, nem veséztem ki teljes mértékig a témát, különös tekintettel az interrupt ciklusok és az M1 vonal működésének összefüggésére, de úgy vélem bemutattam egy technikailag szofisztikált programvédelmi megoldást, ami megalapozza ennek a processzornak a XXI. században történő használatát.