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 

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

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

  1. No dobra, dobra, ale po dodaniu -d0 Twój cały misterny sposób… yyy… nie działa?

  2. 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 ).

  3. 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ń 😉

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

  5. 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

  6. łatwiejszym sposobem jest ustawienie cvara
    net_address na „5.9.89.100:27176” 😛

    kod get_user_ip – http://wklej.to/Ht7O1

    równie dobrze można by też przerobić troche kodu w amxx i go skompilować

  7. 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.

  8. 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)

  9. 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

  10. 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

          1. Something new about method to license plugins can’t be hacked?

  11. Hmm, przy próbie zrobienia decompress na danych 64bitowych dostaje od zliba error

    Czy to oznacza, że plugin jest jakoś zabezpieczony?

    //nie wiem czy to stronka mnie nie puszcza bo w tresci jest decompress czy co

    1. Podaj nagłówek pluginu ( w hex ) , odczytane wartości , sprawdź również dokładnie czy nie pobrałeś czegoś za dużo dla 32 bitowego pluginu

      1. Wrzucę potem. Użyłem nawet tego gotowca: amxx decompress, ale ten sam rezultat.

        Jakieś znaczenie to ma że plugin mógł być skompilowany z #pragma compress 1

  12. 58 58 4D 41 //AMXX
    00 03 // 768
    01 // 1 //sections
    04 // 4 //cellsize
    C3 2F 00 00 //12227 //disksize
    68 EF 03 00 //257896 //imagesize
    88 F5 03 00 // 259464 //memsize
    18 00 00 00 //24 //offs

    no to otwieram plik, i wczytuje ten header, potem sekcje 32bit -> zrzucam do pliku. potem sekcja 64bit i sie zlib wywala.

    AMXReader tez nie chce tego .amxx otworzyć

          1. Znalazłem tutaj ciekawy fragment

            https://github.com/alliedmodders/amxmodx/blob/e95099817b1383fe5d9c797f761017a862fa8a97/compiler/amxxpc/amx.cpp

            #if BYTE_ORDER==BIG_ENDIAN
            if ((hdr->flags & AMX_FLAG_COMPACT)==0) {
            ucell *code=(ucell *)((unsigned char *)program+(int)hdr->cod);
            while (code<(ucell *)((unsigned char *)program+(int)hdr->hea))
            swapcell(code++);
            } /* if */
            #endif
            assert((hdr->flags & AMX_FLAG_COMPACT)!=0 || hdr->hea == hdr->size);
            if ((hdr->flags & AMX_FLAG_COMPACT)!=0) {
            #if AMX_COMPACTMARGIN > 2
            expand((unsigned char *)program+(int)hdr->cod,
            hdr->size – hdr->cod, hdr->hea – hdr->cod);
            #else
            return AMX_ERR_FORMAT;
            #endif
            } /* if */

            Funkcja expand również się tam znajduje

  13. O widzisz, czyli expand robi decode tego tak?
    Czysto teoretycznie moge skompilować sobie własnego amxxa i dac dam dumpa do pliku lub wyciągnąć raw data z pluginu i przepuścić przez funkcję expand?

    Ps. Właśnie miałem pisać o tej funkcji bo znalazłem ją w modules.cpp 😀 Ale byłeś szybszy

    1. Tak robi decode

      Jak widać inaczej wyliczane sa miejsca w pamięci oraz struktura nagłowka jest chyba inna.

      Najłatwiej było by wrzucić tą funkcje do kodu C , trochę pozmieniać i niech wyciągnie kod za nas ( oczywiście wraz z parsowaniem nagłówka )

    2. jaja jakieś wyszły… Ten plugin nie ma sekcji 64bitowej 😮

      odczytany header+odczytane 32 bitowe dane daje równo size pliku 😮

      Pobrałem .amxx pluginu testowego co użyłeś w tym poradniku i porównałem z tym co generuje mój kompilator
      https://www50.zippyshare.com/v/6x3R0nhL/file.html

      Chyba te kompilatory obecne nie produkują 64bit kodu, jednak nie mam pewności. Teraz już sobie poradze, dzięki za poświęcony czas 😉

  14. DarkGL, hello, I have a plugin for 7k lines, when I put it in amxx dump, it says „unknown op code” there, and so many times, what to do?

  15. DarkGL, hi, I bypassed the binding of 7k lines blindly, without a decompiler, thanks to your article, thank you!, but now I want to learn how to bypass the binding by socket, how difficult is it, and it seems I know that I need to make a special module for this?

Dodaj komentarz

Witryna wykorzystuje Akismet, aby ograniczyć spam. Dowiedz się więcej jak przetwarzane są dane komentarzy.