HTML

rm -r

Biztonságtechnika a weben és SAMP alatt. PAWN & AMX. Elméleti és gyakorlati informatika, algoritmusok, számítástudomány. LSRP, PNP/CRPG, személyes kalandok, fordítások, és minden egyéb, időhatárok nélkül. Néha angolul.

Friss topikok

Címkék

amx (1) deamx (1) pwn (1) samp (1) Címkefelhő

Archívum

DeAMX és működése

2013.02.10. 18:47 Pritchard

BEVEZETÉS
Mostanában a SAMP fórumon nagyon fellendült a gépi kód alacsony szintű programozása. Mivel egy ideje én is foglalkozok ezzel, és a DeAMX működése a kezdő scriptereknek rejtély, ezért írtam meg a tutorialt.

Remélem, aki PAWN-ban scriptel, az tisztába van azzal, hogy a PAWN, mint olyan, egy programozási nyelv. Az általunk kifejezett utasításokat gépi kódokká alakítja. Míg az emberek a szöveges nyelvet egyszerűbben, és hatékonyabban tudják kezelni, a gépek a számokat preferálják. Ezt nevezzük gépi kódnak.

Ellentétben a közhiedelemmel, a gépi kód nem teljesen értelmetlen zagyvaság, hiszen akkor a számítógép se értené meg. Ellenben igen nehéz olvasnia egy ismeretekkel nem rendelkező felhasználónak. Ahhoz, hogy megértsük a problémát, tudnunk kell, mit nevezünk magas és alacsony szintű programnyelveknek. A magas szintű programnyelvek az emberek számára érthetőbbek, jobban programozhatóak, viszont a fordításuk erőforrásigényesebb, a program futása az alsóbb rétegek miatt általában lassabb. Az alacsonyabb szintű nyelvek az ember számára kevésbbé érthetőbbek, viszont gépi kóddá előbb fordítódnak (hiszen közelebb állnak hozzájuk), és gyorsabbak.

A PAWN, mint olyan, egy magas szintű nyelv, az ember számára könnyen olvasható, és megérthető.

  1.    new var = 15;
  2.    while (var > 0)
  3.    {
  4.       var = var - 1;
  5.    }
  6.  

A fenti scriptet, ha lefordítjuk, egy futtatható fájlt kapunk (AMX). Az AMX gépi kód, ezért megértése lehetetlen, ha nem ismerjük a felépítését. Ebben segít nekünk az ún. assembly nyelv. Az assembly a legalacsonyabb szintű programozási nyelv: a gépi kód felett állva az egyik legbonyorultabb, de leggyorsabban forduló és a gép számára legésszerűbb nyelv.

A PAWN fordító -a kapcsolójával az AMX helyett ASM (assembler) kódot kapunk. Ha valakit érdekel, nyisson egy parancssort rendszergazdaként, navigáljon el a pawno mappájába, majd üsse be a pawncc fájlnév -a parancsot, ahol a fájlnév értelemszerűen a kódunk neve. Ne feledjük, hogy az assembly minden architektúrán változik, az AMX-nek (ami egy absztrakt gép, tehát egy szimulált számítógép) más fajta assemblyje van, mint egy x86-os processzornak. Ha a gépi kódot szeretnétek megtekinteni változatlanul, dobjatok egy #pragma compress 0-t a módban, és a tiszta kódot kapjátok meg a hex editorban. Én is így csináltam a gépi kódokhoz a példákat.

A fenti PAWN kód így néz ki az AMX assemblyjében:

break
push.c f
break
l.0
break
load.s.pri fffffffc
move.alt
zero.pri
jsgeq 1
break
load.s.pri fffffffc
add.c -1
stor.s.pri fffffffc
jump 0
l.1

És miért mondjuk azt, hogy a gépi kódhoz a legközelebb áll? Egészen egyszerűen azért, mert minden egyes utasítás a gépi kódban megfelel egy két hexadecimális számból álló opkódnak, az utánna álló paraméterek pedig az opkód után álló hexadecimális számoknak.

89 00 00 00 |           // break

27 00 00 00 | 0F 00 00 00 // push.c f
Látszódik a fenti két kódrészletből, hogy az assemblyből gépi kódot képezni nem éppen komoly munka. Az utasítást az első négy, míg a paramétereket az utolsó négy bájtban tárolja el. Az utasítás négy bájtja és a paraméterek négy bájtja közé húztam egy vonalat, ez az eredeti gépi kódban nincs ott. Látható hogy a break utasítás opkódja 89, a többi nulla csak a kötelező 4 bájt kitöltésére van.

Ugyanígy a második példánál a 27 a push.c opkódja, majd a 4 üres bájt a kötelező formátum miatt van. A paramétere 1B, a maradék három bájt a kötelező formátum miatt itt is üres.

A VÁLTOZÓK MIZÉRIÁJA
Amennyiben valaki már próbált DeAMX-el kódot visszafejteni, rájöhetett, hogy nem éppen jönnek vissza a változónevek, helyette értelmetlen var0, var4, var16, és egyéb értékeket kap. A helyzet a következő: a számítógépek nem szeretik a szöveget: bonyorultak, sok memóriát foglalnak, lassítják a kódot, az embereknek viszont jobban megjegyezhető, mint egy rakat hexadecimális szám. Viszont a PAWN kód nem arra van, hogy visszafejtsed.

load.s.pri fffffffc    03 00 00 00 FC FF FF FF
add.c -1               57 00 00 00 FF FF FF FF
stor.s.pri fffffffc    11 00 00 00 FC FF FF FF
A fenti kód betölti az 0xfffffffc memóriacím alól a változót, csökkenti az értékét eggyel, majd eltárolja az új értéket a memóriacímben. Mellé írtam a gépi kód azon részét, ahol ez lejátszódik. Láthatjuk, hogy a load.s.pri opkódja a 03, követi három üres bájt, majd az FC FF FF FF felel meg a 0xFFFFFFFC-nek. A gépi kódoknál a hexadecimális számok bájtonkénti fordított sorrendben kerülnek be a listára (lásd Anthony kommentjét). Tehát egy ABCDEF12 hexadecimális szám mint 12 EF CD AB kerül bele a gépi kódba.

A második sorban látjuk, hogy az add.c opkódja 57, követi három üres bájt, majd a -1 signed integer hexadecimális megfelelője: 0xFFFFFFFF. Arról, hogy ez hogyan lesz végül is, ha valaki tájékozódni akar, javaslom Anthony tutorialját a bináris műveletekről.

Végül a módosult értéket ugyanabba a memóriacímbe elraktározzuk.

A lényeg, hogy a fenti kód hasonlít ehhez. Mert hogy pont ugyanaz a kettő:

var = var - 1;

És akkor mégis mi az a 0xfffffffc, és miért ez, nem "var"?

MEMÓRIACÍMEK, MEMÓRIA LEFOGLALÁSA
Arról már volt szó, hogy a számítógép nem szereti a szöveget, és a PAWN nem arra lett tervezve, hogy visszafejtsék. Ezért eldobja azt, ami nem kell. Vegyük példának az alábbi kódot:

  1. new var1;
  2. new var2;
  3. new var3;
  4. new var4;

Ezt a DeAMX a következőképpen adja vissza:

new var12;
new var8;
new var4;
new var0;
Az assembly kódba ugyan nem került bele a három változó, hiszen sosem lettek használva, de kommentekben megtalálhatjuk a nyomát:

;$lcl var1 fffffffc
;$lcl var2 fffffff8
;$lcl var3 fffffff4
;$lcl var4 fffffff0
Láthatjuk, hogy mind a három változó pontosan 4 bájtot (1 cellát) foglal el, hiszen a memóriacímek négyesével növekednek (0, 4, 8, C, 10, 14, 18, 1C, 20, ...). Ezt próbálja meg a DeAMX ábrázolni, mikor a változónknak var0, var4, var8, var12, var16... neveket ad.

Ugyanez a helyzet a függvényekkel: memóriacímek alapján azonosítják őket. Az egyetlen kivétel az eljárások esete (callback), melyeknek a helyzete előre nem tudható, ezért a függvény neve és a függvényre mutató pointer tárolódik el a scriptben.

TAGEK

Miért van az, hogy a

new Float:var = 5.7;
változó ez lesz fordítás után:

new var0 = 1085695590;
?

A megoldás itt is egyszerű: a tagek csak a fordító számára érdekesek, a gépi kódnak nem. A tagek csak a fordító számára fontos, hogy egyféle műveletet különböző adatokkal különbözőképpen használjon. Miután a fordítás megtörtént az adatok rögzítődnek az IEEE 754 szabványnak megfelelően (egy szabvány ami leírja a lebegőpontos számok bináris ábrázolását). Mikor a DeAMX visszafejti a kódot, mivel mind a lebegőpontos számok, mind az integerek ugyanúgy tárolódnak, nem tudja, hogy az adott cím alatt integer, vagy float van, ezért integerré alakítja.

A szabvány felépítését nem fogom részletezni, mivel egyrészt bonyorult, másrészt érdektelen a tutoriallal kapcsolatban. Ha valakit nagyon érdekel, keressen rá az interneten a szabványra.

ANTI-DEAMX
Végül egy gyors mondat az anti-deamx-ről: ezek mindegyike a compiler hibáját használja ki, egyik sem hivatalos, és ha a SAMP-hoz új compiler jönne ki, akkor egyik se érne sokat.

A loopos példa Y_Less tutorialjából van. Észrevételeket szívesen látok, de mivel ez a tutorial abszolút kezdőknek és ismertető jelleggel készült, nem a megfelelő szaknyelv használata volt a cél. A magyar SAMP közösségen található eredeti tutorial megtekinthető itt.

Szólj hozzá!

Címkék: samp amx pwn deamx

süti beállítások módosítása