Az informatika területén az önmódosító kód (self-modifying code) olyan programkód, ami programfutás közben megváltoztatja saját utasításait – általában az utasítás-úthosszúság csökkentése és a teljesítmény növelése céljából, vagy egyszerűen a különben ismétlődő jellegű kód rövidítése, így a karbantartás egyszerűsítése érdekében. Az önmódosítás a „jelzőbit-beállítást” követő feltételes elágazás alternatívája, általában abból a célból, hogy a feltételek tesztelésének számát csökkentsék. Az „önmódosító kód” kifejezést csak olyan programkódra használják, ami szándékoltan módosítja önmagát, a programozó szándékával ellentétesen (pl. puffertúlcsordulás miatt) módosuló kódra nem.

A módszert gyakran használják teszt/debug célú kódrészletek az I/O ciklusokra eső többletszámítások (overhead) nélküli, feltételes előhívására.

A módosítások történhetnek: kizárólag inicializálás közben – a bemeneti paraméterek alapján (ilyenkor a folyamat leírható úgy is, mint a szoftver konfigurálása és hardveres terminológiában nagyjából megfeleltethető a nyomtatott áramköri kártyákon a jumperek beállításának). A program belépési pontjaira mutató pointerek megváltoztatása az önmódosítással egyenértékű módszer, de szükségessé teszi a programkódban egy vagy több alternatív utasítás-útvonal létezését, növelve a bináris fájl méretét. futásidő alatt ('on-the-fly') – a futás során bekövetkező programállapotoktól függően

A két eset bármelyikében a módosítások maguknak a gépi kódú utasításoknak a felülírásával végezhetők, például egy összehasonlítás és feltételes elágazás lecserélésével feltétel nélküli vezérlésátadásra vagy NOP utasításra. Az IBM/360 és a Z/Architecture utasításkészletében egy EXECUTE (EX) utasítás üzemszerűen felülírja a célutasítás második bájtját az 1. általános célú regiszter alsó 8 bitjével, a platformon ez az (időleges) utasításmódosítás standard és legitim módja.

Alkalmazása alacsony és magas szintű nyelvekben Az önmódosítás megvalósítása az egyes programozási nyelvekben különböző lehet, attól függően, hogy interpretált nyelv-e, támogatja-e a pointereket, hozzáférést a különböző dinamikus fordító vagy értelmező „motorokhoz” stb. létező utasítások felülírása (vagy utasításrészek, mint opkód, regiszter, jelzőbit vagy memóriacím), teljes utasítások közvetlen létrehozása a memóriában – vagy akár teljes utasításszekvenciáké forráskód-sorok létrehozása vagy módosítása, amit egy „mini-fordítási” vagy dinamikus interpretációs lépés követ (lásd az eval kifejezést) teljes program dinamikus létrehozása, majd lefuttatása

Assembly nyelv Assembly nyelv használatát feltételezve az önmódosító kód létrehozása nagyon egyszerű. Ugyanazokat az utasításokat létre lehet hozni a memóriában (vagy felülírni vele nem védett programtárban létező kódot), mint amit egy szabványos fordítóprogram is generálna tárgykódként (/bináris fájlként). Modern processzorok esetében a CPU-gyorsítótár működését is figyelembe kell venni, különben váratlan mellékhatások léphetnek fel. Ezt a módszert gyakran alkalmazták a futásidő elején történő feltételvizsgálatra („first time” conditions), ahogy az az alábbi, magyarázattal bőségesen ellátott IBM/360 assembler példában is olvasható. Az utasítás-felülírás módszerével (N×1)-edrészére csökkenti az utasítás-útvonalhosszúságot, ahol N a fájlban lévő rekordok száma (a pedig a felülírás miatt fellépő többletszámítási költség).

SUBRTN CLI SUBRTN,X'95' #<small>ELŐSZÖR JÁRUNK ITT? (ez az utasítás az első futás után azonnal felülírásra kerül)</small> BNE OPENED #<small>(ÁTUGORJUK, HA A CLI OPCODE, = x'95' MÉG NEM LETT MEGVÁLTOZTATVA)</small> MVC SUBRTN(4),JUMP #<small>IGEN, ÍRJUK FELÜL A TESZTELÉST EGY FELTÉTEL NÉLKÜLI UGRÓUTASÍTÁSSAL (ugyanolyan hosszú gépi kód)</small> OPEN INPUT #<small> és NYISSUK MEG A BEMENETI FÁJLT, hiszen az első alkalommal járunk itt</small> JUMP B OPENED #<small>EZ A 4 BÁJTOS FELTÉTEL NÉLKÜLI UGRÓUTASÍTÁS ÍRJA FELÜL A 4 BÁJTOS UTASÍTÁST A ‘TEST’ CÍMKÉNÉL<!-- Milyen TEST címke???--></small> OPENED GET INPUT #<small>ITT FOLYTATÓDIK A NORMÁL FELDOLGOZÁS</small> #...

(A cserekód, azaz a feltétel nélküli ugrás némileg gyorsabb is az összehasonlító utasításnál, ráadásul csökkenti az utasítás-útvonalhosszat; ezek a különbségek N-szeresen jutnak érvényre. A ‘jump’ utasítás megtartja a helyhez kötöttséget, mivel közel helyezkedik el a felülírt utasításhoz, ami megfelelő kárpótlás az OPEN utáni szükségtelen utasítás beiktatásáért). Modernebb operációs rendszereken, ahol a programok védett memóriaterületeken helyezkednek el, a fenti technika nem alkalmazható, ehelyett a szubrutinra mutató pointert kell megváltoztatni. A pointer dinamikus tárterületen foglal helyet, így tetszőlegesen módosítható az első menet után, hogy átugorja az OPEN szakaszt (az, hogy a közvetlen ugró utasítás és a szubrutinra való hivatkozás helyett először egy pointert be kell tölteni, N utasítással megnöveli az útvonalhosszt, viszont a feltétel nélküli ugrás szükségtelenné válása miatt N utasítással csökkenni is fog).

Magas szintű nyelvek Egyes lefordított nyelvek expliciten engedélyezik az önmódosító kódot. Például a COBOL-ban ismert ALTER parancsszó lehetővé teszi a programok számára, hogy módosítsák magukat. A batchprogramozásban is gyakran alkalmazott technika az önmódosítás.Self-modifying Batch File by Lars Fosdal A Clipper és a SPITBOL (egy SNOBOL-fordító) szintén segítséget nyújt önmódosító kód írásában. A B6700 rendszer Algol-fordítója az operációs rendszer felé olyan interfészt nyújtott, melyen keresztül a futó kód képes volt az Algol-fordítónak átadni egy karakterláncot vagy egy fájlnevet, majd elindítani a procedúra új verzióját.

Interpretált nyelvek esetében a „gépi kód” megegyezik a forrásszöveggel, így egyszerűbben megoldható a futásidejű szerkesztése: SNOBOL-ban a futó program forrásállításai egy szöveges tömb elemeit alkotják. Más nyelvek, mint a Perl, a Python és a JavaScript lehetővé teszik futásidőben új kód létrehozását és lefuttatását az eval függvénnyel, de nem engedik meg a már létező kód módosítását. A módosítás illúzióját (bár ténylegesen semmilyen kód nem íródik felül) a függvénypointerek módosítása adja, ahogy az alábbi JavaScript-példa mutatja: var f = function (x) {return x + 1};

// új definíciót adunk f-nek: f = new Function('x', 'return x + 2'); A Lisp makrói szintén lehetővé teszik a futásidejű kódgenerálást anélkül, hogy programkódot tartalmazó karakterláncokkal kellene műveletet végezni.

A Push programozási nyelv genetikus programozási rendszer, amit kimondottan önmódosító programok írására alkottak meg. Nem magas szintű nyelv, de az assembly fölött helyezkedik el.Evolutionary Computing with Push

Az újabb keretű Java nyelvi környezet is lehetőséget ad önmódosító kód írására a bájtkód elemző library-k használatával, majd a megváltozott osztályok példányosításával a Reflection (betekintés) API hatékony segítségével. Objektumorientált környezetben a generikus programozás segítségével lehetőség van osztály típussal ill. osztállyal való paraméterezésére, pl. C++-ban a Standard Template Library ill. Java-ban a generics. E tárgykörbe tartozik még az osztályok generálása különböző makró nyelvek pl. Java környezetben leggyakoribb az Velocity ill. Freemarker makrók használata.

Összetett módosítás Az ablakozó rendszerek térhódítása előtt a parancssori felületeken elterjedt az olyan menürendszerek használata, ami egy futó parancssori szkript módosítását is magában foglalta. Tegyük fel, hogy egy DOS szkript (avagy batch file), nevezzük Menu.bat-nak, a következőket tartalmazza: :StartAfresh ← a kettősponttal kezdődő sor címkét jelöl ShowMenu.exe A Menu.bat parancssori indítása után a ShowMenu megjelentet egy menüt a képernyőn, valószínűleg használati információkkal, útmutatókkal stb. Miután a felhasználó kiválaszt valamit a menüből, ami a „valami” parancs végrehajtását foglalja magában, a ShowMenu kilép, de előtte újraírja a Menu.bat-ot a következőképpen: :StartAfresh ShowMenu.exe CALL C:\Commands\valami.bat GOTO StartAfresh Mivel a DOS parancsértelmező nem fordítja le a batch file-t futtatáskor, továbbá nem olvassa be az egészet a memóriába a futtatás előtt vagy puffereli bármilyen módon, ezért a ShowMenu kilépése után a parancsértelmező talál egy új, végrehajtandó parancsot (ami a valami nevű fájl végrehajtásáról szól, a ShowMenu által alapul vett könyvtárban és az általa ismert protokollt követve), majd a parancs végrehajtása után visszatér a batch file elejére, újraindítja a ShowMenu programot, ami vár a következő választásra. Ha a felhasználó a kilépést választja, a batch file visszaíródna eredeti állapotába. Bár a kiindulási állapotban nem lenne szükség a címkére, az önmódosítás miatt fontos, hogy ott legyen a címke (vagy vele azonos hosszúságú szöveg), mivel a DOS parancsértelmező a következő parancs bájtpozícióját jegyzi meg, ezért az újraírt parancsfájlban a következő parancsnak ugyanott kell kezdődnie, mint az újraírás előtt.

A menürendszer kényelmessége (és egyéb hasznos funkciói) mellett, ennek a megoldásnak az is jellegzetessége, hogy a ShowMenu.exe-nek nem kell a memóriában lennie a kiválasztott parancs aktiválásakor, ami korlátozott memória esetén komoly előny lehet.

Vezérlő táblák A vezérlőtábla-alapú (control table) értelmezők tekinthetők bizonyos értelemben önmódosítónak, mivel a táblabejegyzésekből kinyert értékeket használják (és nem közvetlenül a kódban található feltételes utasítások szerepelnek, "IF inputx = 'yyy'" formában).

Története Az 1948 januárjában bemutatott elektromechanikus számítógép, az IBM SSEC képes volt az utasításainak módosítására, illetve adatként való kezelésére. Ezt azonban ritkán alkalmazták a gyakorlatban. A számítástechnika korai napjaiban gyakran alkalmaztak önmódosító kódokat vagy az igen korlátozott méretű memória jobb kihasználására, vagy a teljesítmény javítására, gyakran mindkét célból. Szubrutinok hívására és az azokból való visszatérésre is használták olyan architektúrákon, melyek utasításkészlete csak egyszerű elágazó vagy kihagyó utasításokkal tudta a vezérlést módosítani. Ez a használata még mindig elterjedt egyes ultra-RISC architektúrákon, legalábbis elméletben; mint például a one instruction set computer esetén. Donald Knuth MIX architektúrája is önmódosító kóddal valósította meg a szubrutinhívásokat.

Felhasználása

Önmódosító kódot különböző célokra alkalmaznak, köztük:

Állapotfüggő ciklus félautomata optimalizálására. Futásidejű kódgenerálásra, illetve algoritmus futásidejű vagy betöltési időben történő egyénítésére (utóbbi főleg a valós idejű grafika területén népszerű) Objektumok beágyazott (inline) állapotának megváltoztatása, vagy a magas szintű closure konstrukció szimulációja Szubrutinok híváscímére mutató pointer patchelése, általában a dinamikus programkönyvtár betöltési/inicializálási idejében, vagy minden meghívásakor, a szubrutin belső hivatkozásait úgy módosítva a paramétereknek megfelelően, hogy azok tényleges címét használja (indirekt önmódosítás). Evolúciós számítástechnikai rendszerek, mint pl. genetikus programozás Kódrejtés vagy -összezavarás a reverse engineering (kódvisszafejtés disassembler vagy debugger használatával) megakadályozására, víruskeresés elkerülésére vagy hasonló okból. A memória 100%-ának (egyes architektúrákon) ismétlődő opkódokkal való feltöltése minden program és adat törlése céljából, vagy a hardver beégetése érdekében. A kód tömörítése és későbbi futásidejű kicsomagolása (EXE-tömörítés), ha a memória vagy a lemezterület korlátozott. Egyes nagyon korlátozott utasításkészletekben az önmódosító kód az egyetlen lehetőség bizonyos feladatok elvégzésére. Például a one instruction set computer (OISC) gép, aminek az egyetlen utasítása a kivonás-és-elágazás-ha-negatív (subtract-and-branch-if-negative) nem képes indirekt másolás (kb. a C nyelvű „*a = **b”) elvégzésére önmódosító kód nélkül. Utasítások módosítása hibatűrés céljából.

Állapotfüggő ciklus optimalizálása Pszeudokódú példa:

repeat N times { if STATE is 1 increase A by one