Zabezpieczanie pluginu na ip ? Nic prostszego do złamania

Ostatnio na forum pojawił się taki o to wątek http://amxx.pl/topic/101210-licenja/

podany tam kod

#include <amxmodx>

new const serverIP[] = "5.9.89.100:27176";

public plugin_init() {
	
	register_plugin("nazwa", "1.0", "autor");
	
	new szIp[ 33 ];
	get_user_ip( 0, szIp, charsmax( szIp ) );
	
	if( !equal( szIp , serverIP ) ){
		server_print("IP nieprawidlowe");
		set_fail_state("Plugin nie dziala na tym serwerze.");
	}
	
	server_print("IP prawidlowe");
}

ma zabezpieczać plugin przed uruchomieniem go na serwerze z innymi ip niż podaliśmy niestety takie zabezpieczenie jest dość prymitywne i da się łatwo ominąć.

Potrzebne nam będzie

  1. Jakiś hex editor np. http://mh-nexus.de/en/hxd
  2. Znajomość jakiegoś języka programowania ( ja używam pythona w tym poradniku )
  3. AMXReader/Disassembler Download tutaj http://forums.alliedmods.net/showpost.php?p=1053057&postcount=4
  4. Znajomość asemblera nie jest wymagana ale przydaje się

Kod pluginu na którym będziemy działać to kod podany na górze postu.
Download pliku amxx po kompilacji testIP Download

Uruchomienie pluginu na moim serwerze obecnie daje taki efekt

L 12/28/2012 - 23:52:32: Start of error session.
L 12/28/2012 - 23:52:32: Info (map "dark_test") (file "addons/amxmodx/logs/error_20121228.log")
L 12/28/2012 - 23:52:32: [AMXX] Plugin ("testIP.amxx") is setting itself as failed.
L 12/28/2012 - 23:52:32: [AMXX] Plugin says: Plugin nie dziala na tym serwerze.
L 12/28/2012 - 23:52:32: [AMXX] Run time error 1 (plugin "testIP.amxx") - forced exit

oraz w konsoli serwera

IP nieprawidlowe

Przejdziemy teraz do analizy pliku amxx wiemy że zawiera on nagłówek oraz dwa razy kod pluginu oddzielnie dla systemów 32 bitowych i 64 bitowych oba są spakowane za pomocą gzip.

Osoby wygodne mogą przeskoczyć ten fragment ( do „Tak jak na początku wspominałem będzie nam potrzebny” ) i użyć tego http://darkgl.pl/index.php/2013/05/03/amxx-decompress/ 😉

Najpierw odczytujemy header pamiętając o http://pl.wikipedia.org/wiki/Kolejno%C5%9B%C4%87_bajt%C3%B3w

Struktura nagłówka

magic //4 bajty
version //2 bajty
sections //1 bajt
cellsize //1 bajt
disksize //4 bajty
imagesize //4 bajty
memsize //4 bajty
offs// 4 bajty

obok macie podane ile poszczególna pola zajmują w pamięci
otwieramy plik amxx w hex edytorze i odczytujemy
pierwsze 4 bajty to 58 58 4D 41
w odwrotnej kolejności to będzie 41 4D 58 58 co po zamienieniu na znaki ascii da nam ciąg „AMXX” i tak zaczynają się wszystkie poprawne pliki amxx
dalej odczytujemy 2 bajty 00 03 odwrotnie to 03 00 co daje nam w systemie dziesiętnym 768 ( do zamieniania hex -> dec użyjcie jakiegoś kalkulatora np. http://www.mathsisfun.com/binary-decimal-hexadecimal-converter.html )
i dalej postępujemy podobnie aż odczytamy wszystkie pola co da nam taki wynik

magic // AMXX
version // 768
sections // 2
cellsize // 4
disksize // 593
imagesize // 1389
memsize // 17324
offs // 41

będą nas interesować tylko pola disksize i offs

pole offs mówi gdzie w pliku zaczyna się kod pluginu
pole disksize mówi ile kod 32 bitowy zajmuje bajtów w pliku

teraz potrzebujemy trochę po programować musimy

1. odczytać wszystkie dane które są w pliku amxx przed kodem
2. odczytać kod 32 bitowy i zdekompresować go za pomocą zlib
3. odczytać kod 64 bitowy i zdekompresować go za pomocą zlib

zmiany będzie wprowadzać w obu plikach odzielnnie dla kodu 32 bitowego i 64 bitwego

1. Odczytanie danych

Liczba którą podałem w read to własnie wartość pola offs.

2. Odczytanie kodu 32 bitowego i dekompresja

wartość w read to wartość pola disksize

3. Odczytanie kodu 64 bitowe i dekompresja

read w takim zastosowaniu odczyta wszystkie bajty aż do końca pliku. Kod 64 bitowy to bajty od końca kodu 32 bitowego do końca pliku.

4. Zapisujemy zdekompresowany kod 32 bitowy i 64 bitowy do oddzielnych plików będziemy je potem edytować.

Tak jak na początku wspominałem będzie nam potrzebny AMXReader otwieramy w nim plik amxx ( to jaką wersje wybierzemy 32/64 bitową nie ma znaczenia )

Musimy teraz zlokalizować kod instrukcji warunkowej nie jest to trudne w tak krótkim pluginie w dłuższym musieli byśmy poświęcić na to więcej czasu.

Kod którego szukamy to

Trochę teorii o budowie pluginów
Kompilator tłumaczy kod pluginu na ciąg opcodów są to zdefinowane wartości wykonujące dane czynności na wirtualnej maszynie. Opcode w kodzie 32 bitowym zajmuje 4 bajty w 64 bitowym 8 bajtów jeśli opcode wymaga dodatkowego parametru to zajmuje on kolejne 4/8 bajtów.

Opis poszczególnych opcode znajdziemy pod tym linkiem
http://www.compuphase.com/pawn/Pawn_Implementer_Guide.pdf strona 99

equal zwraca wartość do rejestru PRI
Co robi NOT i JZER ?
NOT powoduje że PRI = !PRI
JZER powoduje że jeżeli PRI jest równe 0 to plugin skoczy do danego adresu

połączenie tych dwóch funkcji powoduje że jeżeli equal zwróci 0 ( czyli ip jest inne ) to zostanie ono zamienione na 1 ( NOT )
JZER nie skoczy do danego adresu bo PRI jest różne od 0 i wykona się kod

server_print("IP nieprawidlowe");
set_fail_state("Plugin nie dziala na tym serwerze.");

numer NOT to 0x54 a JZER 0x35
szukając ciągu 54 00 00 00 35 00 00 00 w pliku z kodem 32 bitowym łatwo znajdziemy miejsce gdzie występuje interesujący nas kod ( w 64 bitowym szukamy 54 00 00 00 00 00 00 00 35 00 00 00 00 00 00 00 )

Trzeba teraz podmienić NOT i JZER na takie opcody aby program ominął warunek i skoczył do miejsca gdzie by skoczył gdyby warunek był fałszywy ( ip było by prawidłowe )

Można by usunąć NOT jednak to spodowało by zmiany w nagłówku pliku amxx a my nie chcemy sobie dokładać pracy.
Mogli byśmy zamienić skok warunkowy na bez warunkowy JUMP ( wartość 0x33 ) i to by zadziałało jednak
ja zamienie JZER na JNZ ( 0x36 ). JZER skakało do podanego adresu gdy PRI było 0 JNZ skacze do danego adresu gdy PRI jest różne od 0
połączenie
NOT + JNZ daje nam tak naprawdę samo JZER czyli gdy equal zwróci 0 funkcja skoczy do danego adresu omijając kod ustawiający set_plugin_failed 🙂

teraz wystarczy że wyszukamy w plikach z kodami dane ciągi i podmienimy 35 na 36
ciągi ( 54 00 00 00 35 00 00 00 i 54 00 00 00 00 00 00 00 35 00 00 00 00 00 00 00 )

( podmieniamy zaznaczając blok i po prostu pisząc )

to samo robimy z drugim plikiem ( kodem 64 bitowym )

Gotowe wystarczy teraz połączyć header z kodem 32 bitowym i 64 bitowym ( nie zapominając że trzeba oba pliki skompresować gzipem )

Po sprawdzeniu długości skompresowanego 32 kodu okazało się że ma on 593 znaki zamiast 594 więc musimy to uwzględnić w nagłówku 51 02 00 00 na 50 02 00 00.

Możemy sprawdzić teraz otrzymany plik amxx

Dzięki temu że zmienialiśmy tylko jedną linijkę uniknęliśmy wielu nieprzyjemnych edycji w nagłówku niestety przy większych próbach łamania pluginu nie unikniemy tego ( i pewnie jeszcze wielu innych nieprzyjemnych sytuacji )

Przyznam że nie jestem do końca pewnie czy wersja 64 bitowa zadziała poprawnie po prostu nie miałem gdzie tego przetestować.

Download pliku new.amxx abyście sami mogli się przekonać na swoich serwerach że plugin działa 🙂

new.amxx Download

24 komentarzy o “Zabezpieczanie pluginu na ip ? Nic prostszego do złamania

  1. A nie lepiej zamienić całą instrukcje warunkową wraz zawartością na NO(-O)P’y ( 0x00 ) zamiast tworzyć trampoline xD ?

    Warto też wspomnieć że sam format pliku *.amxx jest tylko kontenerem dla zachowania kompatybilności a wypakowane raw’y to w rzeczywistości pliki *.amx które spokojnie można odpalić ( z włączonym trybem kompatybilności wstecznej oczywiście ).

  2. Tak też miałem taki pomysł ale lubię eleganckie rozwiązania 🙂 Ten poradnik nie ma pokazywać jednej poprawnej drogi zachęcam do eksperemyntowania i znajdowania własnych rozwiązań 😉

  3. A tak ogólnie to mam jeszcze takie pytanie – można odczytać IP na jakim plugin ma działać? Po kompilacji z -d0?

  4. AMXReader ( download jest w poście ) pokazuje używane globalne zmienne oraz ich wartości ( no i oczywiście jeszcze te hardcoded ) znajdziesz tam poprawne ip

  5. Cypis czy ja gdzieś mówiłem że jest to jedyne rozwiązanie ? Po za tym wybierając taki temat chciałem poruszyć właśnie temat dekompilacji.

  6. Tak czysto teoretycznie, jeżeliby tak pobrać ip serwera i porównać ją z czymś z poza pluginu, znaczy się nie pobierać jakie ma być tylko np. wysłać zapytanie sql z warunkiem i porównać pobrane ip serwera to wartości komórki w sql? Przy tym działaniu plugin raczej nie musi znać do jakiego ip chcemy przyrównać ip serwera, na którym znajduje się plugin.
    Chyba, że się myle? (SQL na poziomie podstawowym xd)

  7. Wszystko da się złamać w takim przypadku musisz i tak przesłać odpowiedź zwrotną czy plugin ma dalej działać czy nie , a to już pozwala na wpłynięcie na to i zmuszenie pluginu do dalszego działania

  8. NIE lepiej zrobić kod w pluginie który jeżeli ip nie jest prawidłowe crashuje plugin ale nie set_fail_state, ale coś normalnie crashujące/blokujace plugin.

    1. You can repeat licence code multiple times with small changes or add code which does nothing just take space it is harder to find licence code in big blocks of assembler code

Dodaj komentarz