Samomodifikující se kód

Samomodifikující se kód (anglicky Self-modifying code) je v informatice takový zdrojový kód, který při svém provádění mění vlastní instrukce. Obvykle je to kvůli zkrácení výkonného kódu programu, zvýšení výpočetního výkonu, případně kvůli usnadnění údržby zdrojového kódu (který by jinak byl příliš dlouhý). Termín je používán pro kód, který sám sebe mění záměrně, nikoliv v důsledku počítačového útoku na přetečení zásobníku a podobně.

Charakteristika

[editovat | editovat zdroj]

Změna instrukcí programu může být provedena dvěma způsoby:

  • pouze při spuštění –na základě vstupních parametrů.
  • během vykonávání programu („on-the-fly“) –na základě určitých stavů, do kterých se program dostane.

Kvůli počítačové bezpečnosti je v současných systémech obvykle zakázáno používat samomodifikující kód, aby útočník nemohl podvrhnout svůj kód, který bude následně spuštěn (bez toho, aby tento kód v programu pocházel od jeho původního autora). Proto je operační paměť, ve které se nachází kód programu, označena NX bitem nebo jinak chráněna proti přepsání.

Aplikování na programovací jazyky

[editovat | editovat zdroj]

Modifikace kódu může být dosaženo různými způsoby v závislosti na použitém programovacím jazyku a překladači.

  • překrytím stávajících instrukcí programu nebo jejich částí,
  • přímým vytvořením kompletních instrukcí daného programu nebo sekvencí instrukcí v paměti,
  • vytvářením nebo úpravou příkazů ve zdrojovém kódu a následnou kompilací nebo dynamickým překladem nebo
  • vytvořením celého programu dynamicky

Jazyk symbolických adres

[editovat | editovat zdroj]

Samomodifikující se kód je celkem jednoduché implementovat pokud je použit jazyk symbolických adres. Strojové instrukce mohou být v paměti vytvořeny dynamicky (nebo mohou překrývat stávající kód v nechráněném programovém úložišti), v sekvenci ekvivalentní k těm, které standardní kompilátor může generovat jako objektový kód. S moderními procesory může dojít k nežádoucím vedlejším účinkům ve vyrovnávací paměti, což je třeba brát v úvahu. Tato metoda byla hojně využívána pro testování „first-time“ podmínek. Používá se překrytí instrukcí programu, aby bylo možné omezit množství instrukcí o (n×1)-1, kde n je počet záznamů v souboru (-1 ... zdroje, které jsou potřeba k provedení překrytí).

SUBRTN NOP OPENED      FIRST TIME HERE?
* The NOP is x'4700'<Address_of_opened>
       OI     SUBRTN+1,X'F0'  YES, CHANGE NOP TO UNCONDITIONAL BRANCH (47F0...)
       OPEN   INPUT           AND  OPEN THE INPUT FILE SINCE IT'S THE FIRST TIME THRU
OPENED GET    INPUT           NORMAL PROCESSING RESUMES HERE
      ...

Počítač IBM SSEC, představený v červenci roku 1948, měl schopnost měnit své instrukce a zacházet s nimi jako s daty. Nicméně, tato schopnost byla v praxi použita jen zřídka. V době začátků počítačů byl samomodifikující se kód často používán, aby redukoval využití vyhrazené paměti, zvyšoval výkon nebo obojí. Někdy byl použit také pro volání podprogramu nebo vracení výsledku, pokud sada instrukcí poskytovala pouze jednoduché větvení, nebo pro přeskočení některých instrukcí. Tento postup je stále využitelný v určitých RISC architekturách, alespoň z teoretického hlediska. Fiktivní počítač MIX od Donalda Knutha také využívá pro implementaci podprogramů samomodifikující se kód.

Samomodifikující se kód bývá použit z různých důvodů:

  • poloautomatická optimalizace smyčky závislé na stavu
  • generování kódu za běhu programu nebo specializace algoritmů při běhu nebo během spouštění (což je velmi populární, například v oblasti real-time grafiky) jako hlavní řadící nástroj – připravuje kód, aby mohl porovnat klíče popsané při specifickém spuštění
  • změna inlined stavu objektu nebo simulace vysokoúrovňové konstrukce closures
  • oprava adres volání podprogramů, obvykle prováděna během načítání dynamických knihoven nebo při každém spuštění
  • evoluční výpočetní systémy jako například genetické programování
  • schování kódu aby mohlo být zabráněno reverznímu inženýrství nebo detekci antivirovým nebo antispywarovým programem
  • využití 100% paměti (v některých architekturách) vzorem opakujících se opcodes, pro smazání všech programů a dat nebo při testování hardware
  • komprese kódu, který je následně dekomprimován a vykonán při spuštění (například pokud je omezená paměť nebo volné místo na disku)
  • některé velmi omezené sady instrukcí nemají jinou možnost než používat samomodifikující se kód, aby bylo možné provádět určité funkce, například počítač OISC
  • změna instrukcí kvůli toleranci chyb

Jádro Linuxu používá modifikaci vlastního strojového kódu při startu (pro korekce pro vlastnosti použitého procesoru – například různé implementace virtualizace pro procesory Intel a AMD) nebo při instrumentaci (vkládání úseků kódu za běhu kvůli ladění nebo monitorování systému). Cílem je vyhnout se použití podmíněných skoků, které by zbytečně zdržovaly.