Egy ismerősömtől kaptam egy levelet, hogy segítsek egy BME-VIK gólyatábor feladat megoldásában, mivel korábban többször foglalkoztam Commodore 64 programozással, és érdekelt, hogy a VIK-es kollégák milyen programot írtak C64-re, ezért teljesen visszafejtettem a kiadott programot.
A történet szerint zombik ellen kell harcolnunk, és szükségrogünk van egy titkos kódra. A program a képernyő felső sorába egy scroller van, amiben a küldetés feladatait sorolja, alsó sorban pedig egy sprite, amin a titkos kód található, amit a keret eltakar. Ránézésre egyszerű kis programocska, de nézzünk bele, mi is van benne pontosan, és milyen megoldásokat alkalmaztak.
Forrás: http://golya.sch.bme.hu/rejtvenyek_zombifoldrol_2____commodore Már nem elérhető az eredeti helyen.
Kezdő lépések
Akkor lássunk is neki, ezúttal modern eszközök felhasználásával. A boncoláshoz a Vice monitorját használom fel, illetve az ICU64 nevű progit, ami láthatóvá teszi valós időben a gép memóriáját.
Először illene tudni, hogy a program a memória melyik területén helyezkedik majd el, a betöltést követően. Ezt nem nehéz megmondani. Minden C64 futtatható bináris első két byteja a kezdőcímet tartalmazza, ahova betölti a lemezről Kernal (nem kernel ) a kódot. Mivel programunk a RUN
parancsra le is fut, ezért ennek a címnek a basic terület elejére kell hogy mutasson, ami a $0800
= 2048 (továbbiakban $xxxx
formátumban írom le) címtől kezdődik. Ha ezen a címen $00
van, akkor a Kernal az ezt követő kódot basic kódként fogja kezelni, tehát a fileunk a $0801
címtől kell hogy betöltődjön, de azért nézzünk bele egy HEX editorral:
|
|
így is van. A fileunkból ez a két byte “kiesik” így a 2048 byteos hossz 2046-ra ($03FE
) csökken. A kezdőcímből és a hosszból tudjuk, hogy a kódunk $0801
és $0BFF
között lesz (A továbbiakban a listázást mégis $0800
-ról kezdem majd)
Minden program, amit RUN
paranccsal szeretnénk futtatni, annak a basic tárterület elejétől a basic interpreter által értelmezhető basic kódnak kell lennie. Még akkor is, ha a basicben egyetlen SYS
parancs található, amivel a megadott címtől kezdődő gépi kód hajtódik végre. Ebből következik, hogy a LIST
parancs kiadására, látni kellene valamilyen basic kódot.
Hopp, nem így történt. VIK-es kollégáink valamilyen listázás elleni védelmet alkalmaztak, vagy azért hogy jobban nézzen ki a lista, vagy azért, hogy elrejtsék a gépi kód kezdőcímét. Akkor nézzünk bele a BASIC ram területbe, mi található ott.
|
|
Nos, lássuk hogy is kell mindezt értelmezni, ezúttal $0800-tól:
|
|
Vegyük szépen sorba. A sorok egy láncolt listához hasonló (de nem teljesen az, mert köztes sor beszúrásakor átrendezi) struktúrában foglalnak helyet; illetve csak listázáskor használja az interpreter. A linket követően a sorszám, majd a sor tartalma. Minden sor végét egy nulla jelöl. Minden basic utasítást rövidít le.
Ezek alapján a basic sorunk valahogy így fest:
Itt látható már, hogy a basic kódba a megjegyzés utasítást (REM) követő három karakter felel a “védelemért”, az inverz M és Q két vezérlőkarakter (CBM karaktertábla szerint). Ezek a vezérlő karakterek ún. idézőjel-módba karakterláncként építhetők be a kódba, későbbi felhasználás (print) végett. Ha ebből a módból sikerül “megszökni”, akkor a list parancs kírás közbe “végrehajtja” ezeket. Az inverz M sortörést produkál, az inverz Q pedig egy kurzor fel, így a -BME-VIK-2011-
karaktersor felülírja listázáskor az előzőleg kiírt 0 sys2474:REM"
részt.
Ezután már a kezdőcímet követve végig kell menni egy disassemberrel a memóraképen. Vice-ban van beépített monitor, ami képes mindenféle mellékhatás nélkül a memóriába látni.
Teljes disassembly
Végül itt a teljes disassemblelt kód:
|
|
Varázslat
A feladat kiírásában szerepelt az is, hogy lehet írni egy aprócska basic programot a basic indító és a gépikód közé, amivel varázslatos dolgokat lehet művelni az alsó sorban kavaró sprite-tal:
|
|