Orpheu, jest to moduł autorstwa joaquimandrade. Jest możliwy do pobrania stąd: Module: Orpheu2.3a - AlliedModders
No to lecimy !
Pierwsza rzecz, jaka jest potrzebna, to dekompilator Windowsowy/Linuxowy. Osobiście posiadam IDA Pro Advanced + Hex Rays Decompiler.
Wszystkie poniższe operacje będę przeprowadzał właśnie na tym programie.
Od czego zacząć?
Pierwszą operacje, jaką przeprowadzimy będzie dekompilacji biblioteki. Mamy 2 rodzaje bibliotek:
- engine - silnik gry (nie mylić z biblioteką HL!). Jest to podstawa każdego modu. Nazwa pliku: swds.dll(Windows)/hlds_engine*(Linux).
- mod - silnik moda np. Counter-Strike, Half-Life, Team Fortress etc. Nazwa pliku:(dla Cs'a) mp.dll(Windows)/cs_i386.so(Linux). Oba te pliki znajdują się w cstrike/dlls.
Po otwarciu IDA ukaże się takie o to okienko:
Wybieramy "New".
I tutaj mamy wybór. Możemy dekompilować biblioteke Windowsową albo Linuxową. Wybieramy Linuxową, dlaczego ? Ponieważ zdekompilowana biblioteka Windowsa jest nieczytelna
Klikamy to co zaznaczyłem na screenie wyżej i szukamy odpowiedniej biblioteki.
Aby otworzyć odpowiednią zakładkę, klikamy w Views>Open Subviews>Names.
Jak zapewne niektórzy zauważyli, są tu podane funkcje występujące w danej bibliotece.
Dobra mamy podstawę. Jak mam napisać z tego plugin !?
Aby móc korzystać z tych funkcji, potrzebna jest tzw. sygnatura.
Są 2 rodzaje sygnatur:
- do zwykłych funkcji, znacznie trudniej zrobić do nich sygaturę
- do funkcji wirtualnych, o wiele łatwiejsze do wykonania
Zwykłe funkcje
Każda funkcja posiada swój własny offset w bibliotece. W systemie Linux ten offset bardzo łatwo znaleść - jest to jego nazwa. W systemie Windows sprawa nie wygląda już tak pięknie i łatwo. Tutaj aby znaleść odpowiedni offset, trzeba mieć przedewszystkim cierpliwość.
No dobra, mamy funkcję InstallGameRules, o to jej 'pseudocode' z biblioteki Linuxowej:
int __cdecl InstallGameRules() { int result; // eax@2 int v1; // eax@2 int v2; // eax@3 (*(void (__cdecl **)(_DWORD))&g_engfuncs[156])("exec game.cfg\n"); (*(void (**)(void))&g_engfuncs[160])(); if ( *(float *)(gpGlobals + 20) == 0.0 ) { v2 = __builtin_new(0x2C4u); result = __18CHalfLifeMultiplay(v2); } else { v1 = __builtin_new(0x2D8u); result = __17CHalfLifeTraining(v1); } return result; }
Tutaj bardzo łatwo można zobaczyć szczególną cechę tej funkcji, string'a exec game.cfg\n. Skoro chcemy znaleść jej offset w Windowsie, dekompilujemy binarkę Windowsa i szukamy tego stringa w kodzie. Powinnyśmy znaleść coś w tym stylu:
//----- (10088530) -------------------------------------------------------- int __cdecl sub_10088530() { long double v0; // fst7@1 int v1; // eax@2 int v3; // eax@4 dword_101623DC("exec game.cfg\n"); dword_101623E0(); v0 = *(float *)(LODWORD(dword_101625B8) + 20); if ( v0 == 0.0 ) { v1 = (int)operator new(0x2E8u); if ( v1 ) return sub_100C5EF0(v1, v0); } else { v3 = (int)operator new(0x2D0u); if ( v3 ) return sub_10093D80(v3, v0); } return 0; }Jak zapewne widać, oba kody różnią się od siebie. Naszym offsetem w Windowsie jest sub_10088530. Ponieważ jest wartość wyrażona w systemie szesnastwkowym, prawidłowy zapis wygląda w ten sposób: 0x88530.
Teraz możemy już napisać naszą sygnaturę, dla tej funkcji będzie ona wyglądać następująco:
{ "name" : "InstallGameRules", "library" : "mod", "return" : { "type" : "CHalfLifeMultiplay *", "info" : "Ten parametr jest opcjonalny. Jeśli chcemy przechwycić returna tej funkcji, to musimy uwzględnić tą zakladke u siebie" }, "identifiers": [ { "os" : "windows", "mod" : "cstrike", "value" : 0x88530 }, { "os" : "linux", "mod" : "cstrike", "value" : "InstallGameRules" } ] }
Sygnaturę wrzucamy do configs/orpheu/functions.
W przypadku gdy funkcja należy do jakiejś klasy tworzymy dodatkowo folder z nazwą danej klasy. Np. dla funkcji CBaseEntity::Spawn ścieżka będzie następująca: configs/orpheu/functions/CBaseEntity.
Przykładowy kod z wykorzystaniem tej funkcji:
#include <amxmodx>
#include <orpheu>
#include <orpheu_stocks>
new g_pGameRules
public plugin_precache()
{
OrpheuRegisterHook(OrpheuGetFunction("InstallGameRules"),"OnInstallGameRules",OrpheuHookPost) // pobieramy uchwyt i rejestrujemy hooka
}
public OnInstallGameRules()
{
g_pGameRules = OrpheuGetReturn() // pobieramy wartość która jest zwracana przez daną funkcję
}
Funkcje wirtualne
Dla tych, którzy nie wiedzą co to są funckje wirtualne - krótkie wyjaśnienie.
Jak zapewne widzicie mamy klase CBaseEntity oraz CAWP. Jak zapewne wiadomo, każda broń jest bytem, ale nie każdy byt jest bronią. Klasa CAWP jest klasą pochodną od klasy CBaseEntity. Ponieważ klasa CBaseEntity posiada funkcje Spawn(id), klasa CAWP również posiada funkcję Spawn(id). I tutaj jest istota działania funkcji wirtualnych. Wystarczy że zrobimy sygnaturę dla CBaseEntity::Spawn i możemy przechwytywać tą funkcję niemal że dla każdego bytu !
Teraz możesz jeszcze nie rozumieć zabardzo tego, jednakże spójrz na ten przykład: chce przechwycić Spawn dla AWP. więc przy użyciu sygnatury dla CBaseEntity::Spawn jest to możliwe. Wystarczy skonkretyzować dla jakiego obiektu ma ta funkcja zostać przechwycona. W naszym przypadku dla CAWP.
Dobra, zajmijmy się tworzeniem sygnatur.
Otwieramy IDA, wybieramy bibliotekę Linuxa i szukamy.
Funkcje wirtualne są zawarte w <nazwa_klasy>::'vtbl'.
Np. CBaseEntity::'vtbl'.
W IDA wygląda to następująco:
Zapewne niektórzy się domyślają, jest to spis funkcji wirtualnych w danej klasie. Tutaj zamiast offsetów mamy id. ID Spawn w Windowsie wynosi 0, a w Linuxie 2. (zawsze +2)
Jeśli chcemy poznać id danej funkcji możemy liczyć, bądź zajrzeć do pliku addons/amxmodx/cofigs/hamdata.ini. W tym pliku mamy dokładnie podane id wszystkich (prawie) funkcji wirtualnych do wszystkich modów opartych na silniku HL. Zarówno na Linuxie jak i Windowsie.
Tutaj sygnatura za mocno się nie zmienia. Jedyna różnica jest taka, że zamiast "indentifiers" mamy "indexes".
Sygnatura funkcji wirtualnej Spawn będzie wyglądać następująco:
{ "name" : "Spawn", "class" : "CBaseEntity", "library" : "mod", "indexes": [ { "os" : "windows", "mod" : "cstrike", "value" : 0 }, { "os" : "linux", "mod" : "cstrike", "value" : 2 } ] }
Sygnaturę wrzucamy do folderu virtualFunctions zamiast do folderu functions. Reszta jest tak samo.
Kod będzie również wyglądać inaczej. zamiast użycia funkcji OrpheuGetFunction, używamy funkcji OrpheuGetFunctionFromClass. Dlaczego ? Jak już wcześniej powiedziałem, musimy "powiedzieć" kompilatorowi dla jakiej klasy ta funkcja ma zostać wykonana.
#include <amxmodx>
#include <orpheu>
new OrpheuFunction:g_pSpawn;
public plugin_init()
{
register_plugin("Orpheu Tutorial", "1.0.0", "Owner dla AMXX.PL");
g_pSpawn = OrpheuGetFunctionFromClass("weapon_awp", "Spawn", "CBaseEntity"); // pobieramy uchwyt dla funkcji CBaseEntity::Spawn
OrpheuRegisterHook(g_pSpawn, "on_AwpSpawn"); // rejetrujemy hooka
}
public OrpheuHookReturn:on_AwpSpawn(ent)
return OrpheuSupercede; // blokujemy respawn broni
No cóż, to chyba wszystko jeśli chodzi o podstawy. W przyszłości postaram się napisać coś więcej o Orpheu np. zmiana stringów, bądź edycja wybranych bajtów w biblioteki.
Zapraszam do komentowania oraz krytykowania. Każdy post mile widziany
Użytkownik Owner123 edytował ten post 02.08.2011 12:52