V rámci mnoha bezesných koronocí se najdou ty nejlepší okamžiky na procházení občas i docela podivných vzpomínek, možná techno-vzpomínek, nebo někdy taky omg-párty vzpomínek. Z těch vyloženě techno jsou jedněmi z nejlepších (čti nejhorších) asi ty, co zahrnují peklo na USTAFu. Rčení peklo na USTAFu je sice anagram, ale má i svůj reálný základ – od podivně černých tranzistorů po zážitky ze strachu z halonových rozvodů v šatništi, kde si Bio vylámal zuby. Už to bude pár let, co to naštěstí všechno skončilo, ale asi tím, že tam krajovníci pořádají školeníčka, tak je to pořád aktuální. Nuže, podělím se tedy dnes o jednu historku z té neuvěřitelně temné doby.
Divoké hodiny měření bývaly v závislosti na místnosti a jejím facilitátorovi, ty lajdáku, jedněmi z nejvíce záhadných. Už si naštěstí nedovedu vybavit konkrétní číslo dveří, ale tipuji si, že to byla ta kulatá místnost ve druhém patře, kde se s pravidelnou občasností odehrávalo cosi vskutku magického. Psal se zrovna nějaký rok a do spotřební třídy teprve dost pomalu začínaly pronikat 64bitové technologie, tedy x86_64 jak ho známe dneska, kdy je vlastně téměř na svém konci. Všude Intel s EMT64, AMD na veletrzích a do toho si minji od nich pořídil svůj první naleštěnej Opteron 144 a všem nám ukázal, jak mrtě mrzký výpočetní výkon máme. No a v tu chvíli se většinou ozvalo něco jako Skupina šest půjde na Repexy, pronto! A bylo to, fail dne dorazil. Přiznám se, že nemůžu nikde najít význam nebo původ slova Repexy, ale očividně musí vycházet z nějaké reálné historie určitých strojů, na kterých se tyhle zlé věci dříve prováděly. Ve významu, kdy nám byly poprvé představeny, jimi byly v temné místnosti velmi překvapivě stará bezdisková ATčka, nebo možná ještě spíše XTčka z úsvitu 80. let, a to pěkně s vychytanými monochromatickými oranžovými monitory. Mohlo jich být okolo šesti nebo osmi, všechny byly připojené BNC-téčkama na koaxiálech, zkrátka na nějaké 10Base2 síti, prostě hotová oživlá historie na talíři. Kromě toho, že to byly zcela jistě 16bitové stroje, se ze vzpomínky nedá o moc více rekonstruovat, to byste to zkrátka museli vidět.
Vždycky při příchodu do místnosti na nich už zlověstně běžel připravený program Repexy, který – viz výše – maximálně sliboval příjemnou zábavu. Ta spočívala ve vyplnění jména na staré klávesnici a zadání názvu konkrétní lekce, který vždycky včas určil vyučující, pan M, se svým smyslem pro mnoho překvapení na této škole. V tu chvíli se už pak nedalo nic moc dalšího dělat, na obrazovku se vykreslilo většinou nějaké divoké schéma se záludnou otázkou a výčtem číslem označených odpovědí, obyčejně tak čtyř možných. Žádní poníci se nekonali. Vpravo nahoře neúprosně běžel čas a bylo jasné, že příjemná zábava je zde pouze eufenismem pro jednu z mnoha forem pekla, ale na to jsme si už stejně zvykli. Po odeslání všech odpovědí to celé píplo a program provedl vyhodnocení, tedy udělil známku ze zkoušení na základě počtu správně zodpovězených otázek. Kdo měl hotovo, s pětkou se zaradoval, přestal moc čumět a odpotácel se zpět do neméně zábavného dne. Servis.
Mno, takhle nějak to vypadalo z té čistě uživatelské strany. Dneska se už po letech asi konečně může mluvit i o straně druhé, protože nedávné vedení stejně s celým USTAFem provedlo tak radikální změny, že stejně z těch komponent nezůstalo asi vůbec nic v původním tvaru. Celá tehdejší síť byla poháněná Novellem, jak už bývalo tenkrát zvykem, na běžných stanicích běžely NT4 a možná, možná později i 2000 Professional ve vyšších patrech. Ale co nám je po tom, zajímavé bylo především propojení té osmdesátkové technologie do tehdejší moderní sítě. Asi by nás příliš netrápilo přemýšlet nad tím, jak tyhle věci fungují, kdybychom jednou při laxním procházení novellovských stromů neobjevili celou jednotku s do očí bijícím názvem REPEXY… To nabízelo samozřejmě spoustu úvah, v první řadě ale bylo už ze seznamu MAC patrné, že ty bezdiskové mašiny startují z nějakého BOOTP, tedy že existuje doslova jednotné prostředí pro všechny. No a kdo ví o starých protokolech, tak tohle se nahazuje přes TFTP, čili kromě kontroly MAC, která jde triviálně obejít, vlastně neexistuje účinný zabezpečovací mechanismus, který by znemožnil zmocnit se sdíleného obsahu. Co na to říct, k dispozici tam bylo mnohé, kromě samotného operačního systému taky všechny testové otázky v otevřené podobě, samotný binární program a jako třešnička na dortu ještě jeho pascalovský zdrojový kód. A protože otázky bylo zřejmě nezbytné i po letech doplňovat a upravovat, získaná data rozhodně nebyla read-only. No samozřejmě není asi nutné napovídat, k čemu toto celé spělo.
Protože mi tedy po nějaké době na oči přišel celý archiv se získanými daty, je možná čas na detailnější analýzu. Co je jeho obsahem? Chtělo by se říct, že všechno, co bylo k dispozici na tehdy nabořeném sdíleném prostředku, ale po ohledání mám za to, že přesto něco chybí. Obsah čítá samozřejmě hlavní binární program repexy.exe (pro 16b DOS ve starém stylu, 32 kB), přidružený nepořádek z použitých borlandovských grafických knihoven (režimy egavga.bgi, herc.bgi. vesa16.bgi a písmo litt.chr), prázdný adresář TEMP, adresář REP s jednotlivými otázkami (*.rep), jejich grafickou přílohou (*.bmp), návod k použití (repexy.txt) a původní zdrojový kód (repexy.pas), pak ještě pár zajímavých programů řídících uživatelský vstup na úrovni OS, jmenovitě choice.com a dále dva 11B lock.com a unlock.com. Poslední v řadě je samotný command.com s MS-DOS 6.22 z 31. května 1994. Datům, zdá se, se dá asi docela dobře věřit – program Repexy samotný bude mít původ někde v prosinci 1994 s poslední úpravou a překladem z března 1995, použitý pro něj byl nejspíše Borland Turbo Pascal 7 z roku 1992. Všechny texty jsou psané česky v kamenickém kódování a použité písmo jej v grafickém režimu podporující nese úctyhodnou časovou značku 9. prosince 1989. Bohužel ale ani z toho se nedá zpětně vyzjistit, zda počítače disponovaly alespoň 80286, nebo jakou vůbec měly paměť – všechno z toho by hypoteticky mohlo běžet na původním 8086 a nikdo by si ničeho ani nevšiml.
Je hrozně super nahodit si to teď v roce 2021 na 4K UHD displeji pod x64 UNIXovým prostředím v DOSBoxu 0.74, který má jako výchozí grafický režim zřejmě svga_s3, a zavzpomínat na ty monochrómy ve chvíli, kdy se inicializuje nějaký grafický režim. Ale to není pořád moc příjemná zábava. Příjemná zábava bude si to zpětně po letech rozebrat na atomy a provést něžný zásah, který by zvýšil funkcionalitu celého programu. Samozřejmě mít po ruce pascalovský kód z roku 1994 a koukat, jak je to napsané víceméně objektově, je docela legrace, ale prakticky není možné do něj zasahovat, jelikož by se stejně nepovedl zpětný binární překlad pro DOS – tehdejší originální Turbo Pascal nemám a s fpc to stejně nedovedu crossnout bez zbytečného rozčilování. Budiž tedy tento kód pouze vodítkem a skutečná práce bude provedena přímo v assembleru. Repexy, přivítejte příjemnou zábavu v prostředí Ghidra.
Možná by bylo dobré na úvod shrnout, oč vlastně půjde. Proces crackování je v dárk brožurkách popsán jako způsob eliminace omezení, které původní program má. Tím je často myšlena jeho 30tidenní triálnost, nag screeny, vyžadování přítomnosti CDčka a podobně. Samozřejmě Repexy v tomhle ohledu taky nefungují, protože pokud je cílem bezproblémové práce v nich získat výslednou známku 1 z každého testu, je potřeba odpovědět správně na všechny otázky. Ty se dají buď naučit, nebo by se dal vytvořit řekněme keygen, který poskytl všechny odpovědi. Ale nejlepší bude udělat tam klasická zadní vrátka přímo ve způsobu zadávání odpovědi tak, aby pak byla odpověď na každou otázku správná. Tehdy bylo naivní přemýšlet nad kódem tak, že bude akceptovat číslici 9 jako vždycky správnou, protože prakticky nikdy neproběhl ostrý test s více než čtyřmi otázkami – pravda, dokonce úloha test, která asi slouží jako jakýsi tutoriál, má nanejvýše osm možných odpovědí u jedné otázky, ale brzy se po detailní analýze ukáže, že uvažovat devítkou je vlastně poněkud krátkozraké a určitě to půjde i lépe. Následující diagram zachycuje fungování programu v kostce a byl vytvořen na základě prvotní analýzy v disassembleru a až posléze v pascalovém kódu.
Důvodem je hlavně to, že Ghidra má poněkud potíže tento typ softwaru nějak automagicky analyzovat tak, aby všechno běželo hned na první dobrou. Jednak je samozřejmě složité zdlouhavě prohledávat borlandovskou omáčku a mnohdy i dost divoké funkce, které vlastně nepotřebujeme, ale hlavně automatická detekce Old-Style MZ v reálném režimu rozdělí soubor na segmenty tak nešťastně, že z dohledu zmizí několik souvislostí s globálními proměnnými. Takže je tedy lepší si to otevřít jako raw binárku a začít prostě chrlit. Taky je hlavně hrozné, že v Pascalu se psalo dříve všelijak, literály lítaly vzduchem, číselné hodnoty jednotlivých bajtů byly bez ohledu na kontext zapsané dekadicky a hlavně je tady jedna spekulativní "zajímavost" ohledně správy paměti, které se, jak jsme díky minjimu zjistili, říká Arena-Based Allocation. Prakticky spočívá ve statickém vyhrazení části paměti, v tomto případě několika polí znaků, která se recykluje a používá pořád dokola v závislosti na konkrétní operaci. Příkladem budiž sestavení lekce, ke které dochází po zadání jejího názvu na uvítací obrazovce.
Název lekce musí přesně odpovídat názvu souboru .rep v adresáři REP. Tato (relativní) cesta a přípona je v kódu zapsána natvrdo, takže to tak prostě musí být. Soubory jednotlivých lekcí jsou zdánlivě strukturované obyčejné texty – všechno je psáno krásně česky… v kódování Kamenických. To se dá od softwaru z 80. a 90. let ostatně docela očekávat, bohužel smutné překvapení nastane, když oblíbený macovský editor ze současného desetiletí jaksi podporu pro tuhle sadu už nemá, ale ještě smutnější je zjistit, že CP859 jaksi postrádá i jakákoliv standardní instalace iconv. No ale zpátky k té zdánlivé struktuře. Tu na začátku tvoří povinná klíčová slova v pořadí OTAZEK, ZNAMKY a CAS. Po jejich detekci na samostatném řádku dojde k načtení odpovídajících proměnných na řádku následujícím, v případě známek pak tabulky od 1 do 5 s odstupňovaným počtem správně zodpovězených otázek. Jednotlivé otázky jsou pak jako odřádkovaný text umístěny v pseudobloku označeném složenými závorkami (přičemž jeden řádek může mít maximálně 101 znaků, přestože formálně se jich na obrazovku vejde pouze 80), kterým může předcházet křížkem označený název bitmapového souboru s doprovodným obrázkem, naopak za uzavírací závorkou je pak na samostatném řádku v hranatých závorkách číslo správné odpovědi. Pokud bylo načteno více otázek, než kolik bylo uvedeno ve specifikaci za slovem OTAZEK, počet otázek se zredukuje :). Každá otázka je reprezentována samostatnou instancí nesoucí své číslo, správnou odpověď, obrázkovou přílohu a text; otázky jsou pak vyloženě v kolekci, která je pak součástí instance celé lekce.
A sranda je právě ta kontrola správných odpovědí, resp. počítání správných odpovědí. Po spuštění totiž existuje jediná globální proměnná nesoucí počet správně zodpovězených otázek, která je na začátku logicky nulová. Ověřování se provádí průběžně. Každá otázka číhá na vstup z klávesnice ukončený stiskem klávesy return (ano, čeká se na znak CR = 0x0a), vstupem musí být platné 16bitové dekadické číslo se znaménkem. To v praxi znamená, že správně odpovědi mohou být kdekoliv v intervalu od -32768 do 32767. Pokud je zadaný vstup platným číslem, provede se jeho porovnání proti správné odpovědi uvedené u dané otázky. Když souhlasí, proměnná nesoucí počet všech správně zodpovězených otázek je inkrementována. Už jen z toho je vidět, že to je celé docela nesmysl. Jakmile test skončí, program pípne posloupností zhruba h-gis, iterativně se projede pole známek od 1 do 5 a hledá se hodnota, která je menší nebo rovna počtu správně zodpovězených otázek. Takto získaná známka je pak zobrazena na obrazovce. Zde byl objeven ještě jeden prima perk – pokud na začátku uvedené jméno začínalo mezerou a bylo celkově delší než tři znaky, povede se ještě tisk do zařízení na portu LPT1. Poté program vypíše čekání na stisk kláves Alt-X pro ukončení.
Čekání na tuto kombinaci nejprv nedávalo smysl, ale kdo si to v DOSu zkusí, zjistí, že Alt-X generuje pomlčku, která je v ASCII na 0x2d (45 dekadicky). ASCII taky nerozlišuje mezi pomlčkou a znaménkem minus. Program tedy ve smyčce čeká na stisk kombinace, resp. na odpovídající scan code. Tady je důležité vědět, že běžící smyčka při čekání využívá nečinnosti, tedy neustále kontroluje nulu, až pak údaj 0x2d. Taky se tu recykluje proměnná, do které je načítáno, což značně usnadňuje práci.
No a teď to pojednání, proč není dobrý nápad použít jako univerzálně správnou odpověď devítku, ale naprosto skvěle by se hodila -1. Otázek, jak bylo řečeno, může být totiž mnoho, takže možná by se devítka někde mohla překrýt a vzbudilo by to podezření. Dále jsou tu dosud nezmíněné další programy choice.com, lock.com a unlock.com a já mám za to, že i když nám tu chybí něco jako autoexec.bat, skutečná flow ze sítě mohla mít co do činění s postupným během těchto programů. Začněme těmi jednoduššími, z pozdějšího data.
; lock.com - uzamčení klávesnice 0000: e4 21 | IN AL, 0x21 ; IMR, čtení dat z PIC1 0002: 0c 02 | OR AL, 0x2 ; zamaskování IRQ 1 (IMR | 0000 0010) 0004: e6 21 | OUT 0x21, AL ; změna IMR, zápis do dat PIC1 0006: b8 00 4c | MOV AX, 0x4c00 ; AL = 0, AH = 0x4c - konec programu s návratovým kódem 0 0009: cd 21 | INT 0x21 ; přerušení 21h, předání řízení zpět DOSu
Když je to zobrazené takhle, asi to máloco přesně řekne. IMR je interrupt mask register, tedy registr masek přerušení. Pomocí nastavené masky je možné některé IRQ zakázat, nebo jejím odstraněním zase povolit. Původní IBM PCčka měly na procesorech sériově zavěšené dva obvody 8259A, kterým se říkalo PIC. tedy programmable interrupt controller. Oba PIC měly svoje příkazové (0x20, 0xa0) a datové porty (0x21, 0xa1) a umožňovaly posílat do CPU až 15 různých přerušení – každý osm mínus jedna linka pro jejich sériové propojení. Tady je zajímavé IRQ 1 a v kontextu programu možná ještě IRQ 5. IRQ 1 je totiž vyhrazené pro klávesnici. To, co tedy program lock.com dělá je to, že si nejprve načte současný stav IMR a druhý bit určený pro IRQ 1 nastaví na jedničku, tedy nastaví masku pro přerušení jdoucí z klávesnice. Tento nový stav IMR zase zapíše do PIC a vrátí řízení operačnímu systému. Bum ho, jsme v tu chvíli bez klávesnice, na počítači nelze matlat – tohle může být úžasné vhodné pro zákaz jakékoliv další manipulace s programem Repexy, pokud někdo dostal v prvním pokusu pětku. Ne, nezamrzlo to náhodou… :-5.
; unlock.com - odemčení klávesnice 0000: e4 21 | IN AL, 0x21 ; IMR, čtení dat z PIC1 0002: 0c 02 | AND AL, 0xfd ; odmaskování IRQ 1 (IMR & 1111 1101) 0004: e6 21 | OUT 0x21, AL ; změna IMR, zápis do dat PIC1 0006: b8 00 4c | MOV AX, 0x4c00 ; AL = 0, AH = 0x4c - konec programu s návratovým kódem 0 0009: cd 21 | INT 0x21 ; přerušení 21h, předání řízení zpět DOSu
Analogicky funguje unlock.com, který udělá přesný opak – masku IRQ 1 vrátí zpátky na nulu a povolí klávesnici. Dovedu si představit, že skutečné použití by zahrnovalo nějaký sleep po uzamčení a pak by se spustilo zase odemčení. Je to možné? Asi ano, ale naživo to nikdo stejně neviděl.
Naproti tomu první jmenovaný, choice.com, musel být jistou variantou sofistikovanějšího přístupu, ale možná to nefungovalo dle plánu. Na DOSBoxu to nejede a když si to rozebereme na atomy, pak je tam nějaký problém s absolutními adresami, které nesedí o 0x100, ale třeba se jen špatně dívám. Program je určen pro DOS 4.0 a novější, měl by selektivně povolovat pouze určitý výběr kláves, ze kterých v defaultním stavu nabízí Y a N. Je tedy velmi pravděpodobné, že by pro samotné testování byly povoleny číslice 0-9 a dost možná právě pomlčka/mínusítko pro konec. Tahle teorie má ale díru v tom, že na začátku zkoušení musí být zadáno alfanumerické jméno a název lekce. Je možné, že třeba kvůli tomu se od toho upustilo.
A konečně samotné prolamování Repexů v prostředí Ghidra. Identifikovat, kde dochází k ověření odpovědi, bylo poměrně složité, takže bylo chytřejší jít na to od konce a najít část programu, kde se provádí kontrola něčeho proti hodnotě 0x2d, což je již zmíněná pomlčka, resp. scan kód Alt-X.
...
1e27: e8 7b f2 | CALL fun_Screen.OutLN ; vypíše na obrazovku hlášení o čekání na stisk Alt-X
čekat na klávesu:
1e2a: 9a 1a 03 8d 05 | CALLF _fun_ReadInt ; vstup z klávesnice uložený do AX
1e2f: 30 e4 | XOR AH,AH ; horní bajt se nuluje
1e31: a3 ca 09 | MOV [0x9ca], AX ; přesun načtených dat do proměnné
1e34: 83 3e ca 09 00 | CMP word ptr [0x9ca], 0 ; porovnání proměnné s nulou
1e39: 75 0a | JNZ hledat alt-x ; pokud zadaná data nejsou nulová, bude se porovnávat s očekávaným vstupem
1e3b: 9a 1a 03 8d 05 | CALLF _fun_ReadInt ; pokud byla nulová, zkusí se to znovu
1e40: 30 e4 | XOR AH, AH ; smazání horního bajtu
1e42: a3 ca 09 | MOV [0x9ca], AX ; uložení vstupu do proměnné
hledat alt-x:
1e45: 83 3e ca 09 2d | CMP word ptr [0x9ca], 0x2d ; porovnání prověnné s hodnotou chr('-') = 0x2d
1e4a: 75 de | JNZ čekat na klávesu ; bylo cokoliv jiného, čekání se opakuje
...
Na první pohled je zde vidět, že se pracuje s jakousi proměnnou na adrese 0x09ca, což je pascalovský 16b integer a existují na něj reference i z jiných funkcí, tedy tato proměnná je zřejmě globální, nebo prostě recyklovaná v rámci nějaké procedury. Směrem nahoru v toku programu postupně bude vidět tisk, kontrola prvního znaku a celkové délky jména, funkce použité pro pípnutí až najednou, hurá, výše zmíněná adresa jedné z proměnných poblíž blízkých adres podobných proměnných a především porovnávání hodnot obsahu dvou adres, z nichž jedna (0x09cc) je získána z funkce pro čtení čísla z klávesnice.
...
1b23: 9a df 12 ef 05 | CALLF _fun_input ; provede se načtení odpovědi do AX
1b28: a3 cc 09 | MOV [0x9cc], AX ; načtená odpověď se uloží do proměnné
1b2b: 83 3e ce 09 00 | CMP word ptr [0x9ce], 0 ; pokud nebyl zadaný bogus (nečíslo), je tu nula
1b30: 74 0e | JZ kontrola odpovědi ; a proběhne kontrola; jinak je tam skok zpět na nový vstup
...
kontrola odpovědi:
1b40: ff 36 ca 09 | PUSH word ptr [0x9ca] ; odložení proměnné s indexem otázky na stack
1b44: ca4 3e ba 05 | LES DI, [0x5ba] ; ->
1b48: 06 | PUSH ES ; ->
1b49: 57 | PUSH DI ; -> parametry funkce pro čtení v kolekci
1b4a: 9a 3f 01 36 05 | CALLF _poss_fun_RepCol.At(i) ; -> získání správné odpovědi u i-té otázky
1b4f: 89 c7 | MOV DI, AX
1b51: 8e c2 | MOV ES, DX ;
1b53: 26 8b 45 04 | MOV AX, word ptr ES:[DI + 0x4] ; správná odpověď dané otázky na indexu i do AX
1b57: 3b 06 cc 09 | CMP AX, word ptr [0x9cc] ; porovnání správné odpovědi se zadanou hodnotou v proměnné j
; - na tom je hezky vidět, že odpověď může být 16b číslo
1b5b: 75 04 | JNZ chybná odpověď ; odpověď byla špatná
správná odpověď:
1b5d: ff 06 d2 09 | INC word ptr [0x9d2] ; inkrementace celkového počtu správných odpovědí
chybná odpověď:
1b61: 31 c0 | XOR AX, AX ; příprava nuly pro další funkci (kresba čáry)
...
Pokud porovnání obou hodnot selže, přeskočí se kritická inkrementace proměnné na adrese 0x09d2 a pokračuje se dál, což jsem tu označil jako chybná odpověď, ale hej, je to jen návěstí, nebo v tomhle případě spíš komentář simulující návěstí. Později je pak možné potvrdit, že 0x09d2 je místo pro globální proměnnou nesoucí počet správně zodpovězených otázek. Takže co s tím? V plánu by bylo přidat ještě jedno porovnání zadané hodnoty s 0xffff (-1 v 16b) a pokud souhlasí, také provést inkrementaci. Bohužel takovou změnu není možné provést přímo v tomto toku, neboť tu není místo na tolik instrukcí.
Obyčejně se v programu hledá nějaké hluché místo, kde nebude vadit přidat několik instrukcí navíc. Já jsem si tady vybral nějakou vzdálenou adresu ke konci, kde je hodně nul, možná víc než kolik by jich bylo vyloženě potřeba. V praxi tohle funguje hlavně při buffer-overflow exploitování, kde do nějakého paměťového regionu uloží spustitelný kód a pomocí přetečení zásobníku se do něj řízení prostě dostane a spustí ho. To není tenhle případ, tady prostě názorně vznikne pár nových instrukcí pro původní a pro nové ověřování.
...
1b23: 9a df 12 ef 05 | CALLF _fun_input ; provede se načtení odpovědi do AX
1b28: a3 cc 09 | MOV [0x9cc], AX ; načtená odpověď se uloží do proměnné
1b2b: 83 3e ce 09 00 | CMP word ptr [0x9ce], 0 ; pokud nebyl zadaný bogus (nečíslo), je tu nula
1b30: 74 0e | JZ kontrola odpovědi ; a proběhne kontrola; jinak je tam skok zpět na nový vstup
...
kontrola odpovědi:
1b40: ff 36 ca 09 | PUSH word ptr [0x9ca] ; odložení proměnné s indexem otázky na stack
1b44: ca4 3e ba 05 | LES DI, [0x5ba] ; ->
1b48: 06 | PUSH ES ; ->
1b49: 57 | PUSH DI ; -> parametry funkce pro čtení v kolekci
1b4a: 9a 3f 01 36 05 | CALLF _poss_fun_RepCol.At(i) ; -> získání správné odpovědi u i-té otázky
1b4f: 89 c7 | MOV DI, AX
1b51: 8e c2 | MOV ES, DX ;
1b53: 26 8b 45 04 | MOV AX, word ptr ES:[DI + 0x4] ; správná odpověď dané otázky na indexu i do AX
1b57: e9 a6 61 | JMP hack ; skok někam do pryč, kde se provede nové vyhodnocení
1b5a: 90 | NOP ; tady neoptimálně zbyl jeden bajt, vynechá se operace
1b5b: 75 04 | JNZ chybná odpověď ; odpověď byla špatná
správná odpověď:
1b5d: ff 06 d2 09 | INC word ptr [0x9d2] ; inkrementace celkového počtu správných odpovědí
chybná odpověď:
1b61: 31 c0 | XOR AX, AX ; příprava nuly pro další funkci (kresba čáry)
...
hack:
7d00: 3b 06 cc 09 | CMP AX, word ptr [0x9cc] ; původní porovnání vstupu se správnou odpovědí
7d04: 0f 84 55 9e | JZ správná odpověď ; pokud sedí, skočí se zpět na správnou odpověď
7d08: b8 ff ff | MOV AX, 0xffff ; uložení 16b hodnoty -1 do AX
7d0b: 3b 06 cc 09 | CMP AX, word ptr [0x9cc] ; kontrola, zda ve vstupu byla hodnota -1
7d0f: 0f 84 4a 9e | JZ správná odpověď ; pokud byla, skočí se na správnou odpověď
7d13: e9 4b 9e | JMP chybná odpověď ; skok zpět, žádná odpověď nebyla správná
...
Původní kontrola bude nahrazená skokem, zbude jeden bajt, to se nedá nic dělat, dostane NOP a nějak to zpoždění překousneme. V novém bloku pak proběhne původní kontrola a pokud je odpověď správná, skočí se zpátky na správná odpověď a jako by se nic nestalo. Pokud je chybná, pokračuje v porovnávání se dál – v pomocném registru bude nachystaná -1, která se v 16 bitech zapíše jako 0xffff a provede se kontrola proti tomu, co je na adrese 0x09cc, kam se prve uložil vstup z klávesnice. Když je to správně, skočíme zase na správná odpověď. Když ne, asi někdo prostě neví o tomto skvělém vylepšení a program pokračuje tradiční cestou na chybná odpověď. V praxi to vypadá nějak následovně.
Mám trochu radost, že tomuto peklu již bylo učiněno za dost. Teď už to jen poslat strojem času do minulosti. Je nesmírně vtipné probírat se starými soubory, vzpomínkami, bolestí z pekla a zároveň si říkat, že tohle nám už naštěstí nehrozí, protože jsme zvítězili. Na nějakou dobu můžeme x86 zase opustit a věnovat se tomu, co je skutečně důležité – tedy současný arm64 a riscv64. I když… pořád tu jsou ještě srandy, jako byl NTShutdownController, nebo nesmírně populární AlíkHack.