haker.info | Etyczny hacking |

 Bajt zabójca (ang. rogue byte) — metoda przeciw deasemblacji

30 lipca 2019 godz. 23:17    Dawid Farbaniec    650 słów

1. Słowem wstępu

arzędzia typu deasembler (ang. disassembler) to jedne z podstawowych programów, które są używane w inżynierii odwrotnej kodu (RCE). Pozwalają one uzyskać kod w języku Asembler z analizowanego pliku wykonywalnego *.exe. Dzięki tego typu narzędziom nie ma potrzeby pracy z kodem maszynowym, który jest praktycznie nieczytelny dla człowieka. W tym wpisie zaprezentowano prostą metodę, która pozwala oszukać deasemblery, a w połączeniu z dodatkowymi technikami (Kod samo-modyfikujący się i inne) może nieco utrudnić pracę crackerom łamiącym zabezpieczenia oprogramowania. Nawet jeśli nie zabezpieczasz software'u, to zapraszam do lektury, gdyż kto wie czy kiedyś pod debuggerem nie spotkasz pliku zawierającego tę metodę.

2. Teoria

Programując aplikacje np. w Visual C++ pisze się kod wyższego poziomu abstrakcji. Następnie kod źródłowy jest tłumaczony na język Asembler i dalej na kod maszynowy przez kompilator, czyli program budujący (rysunek 2.1). W prostych słowach: Plik wykonywalny (PE/PE32+) oprócz dodatkowych struktur (nagłówka itp.) zawiera kod maszynowy aplikacji.

Jeśli jesteś zainteresowany sposobem kodowania instrukcji (zamianą kodu Asemblera na kod maszynowy) dla architektury x64 odsyłam do poniższych dokumentów:

Dla procesorów Intel 64:
» Intel ® 64 and IA-32 Architectures Software Developer's Manual
   » Volume 2: Instruction Set Reference, A-Z
     » Chapter 2: Instruction Format

Dla procesorów AMD64:
» AMD64 Architecture Programmer's Manual
   » Volume 3: General-Purpose and System Instructions
     » Chapter 1: Instruction Encoding

Proces kompilacji (budowania) programu
Rysunek 2.1. Proces kompilacji (budowania) programu — schemat ogólny

Natomiast zadaniem deasemblera (ang. disassembler) jest proces odwrotny. Narzędzia tego typu starają się odtworzyć kod w języku Asembler z podanego im pliku wykonywalnego (zawierającego kod maszynowy). Spotkałem się także z dekompilatorami, które z kodu maszynowego odtwarzały pseudokod podobny do języka C (np. REC Decompiler). Celowo tutaj został użyty termin dekompilator, gdyż tworzy on kod o wyższym poziomie abstrakcji niż deasembler.

3. Prezentacja techniki Rogue Byte (z ang. bajt zabójca)

Technika Rogue byte korzysta z możliwości umieszczania bajtów o określonej wartości w kodzie programu. Przykład będzie w Asemblerze MASM64 (ML64.EXE), który jest dołączony do środowiska Microsoft Visual Studio.

Aby zrozumieć działanie tej metody należy pomyśleć jak działa deasembler. W uproszczeniu pobiera on kolejne bajty kodu maszynowego i zgodnie z regułami kodowania instrukcji procesora zamienia je na określone rozkazy.

Jeśli wewnątrz ciągu instrukcji programu wstawi się bajt o określonej wartości, to większość (a na pewno wiele) deasemblerów potraktuje go jako część kodu maszynowego i błędnie zinterpretuje rozkazy w pobliżu tego bajtu.

Spójrzmy na poniższy kod aplikacji w Asemblerze MASM64:

Listing 3.1. Przykład metody Rogue byte przeciw deasemblerom (MASM64)
extrn ExitProcess : proc extrn MessageBoxA : proc .data szText db "http://haker.info/", 0 .code Main proc jmp @f db 0E8h ;bajt zabójca     @@: sub rsp, 28h xor r9, r9 lea r8, szText lea rdx, szText xor rcx, rcx jmp @f db 0E9h ;bajt zabójca     @@: call MessageBoxA add rsp, 28h sub rsp, 8h xor rcx, rcx call ExitProcess Main endp end

Na listingu 3.1 znajduje się program, który wyświetla okno informacyjne (komunikat) za pomocą funkcji MessageBox, a następnie kończy swoje działanie (funkcja ExitProcess).

Należy zauważyć, że pomiędzy instrukcje zostały wstawione dwa fragmenty kodu:

jmp @f db 0E8h ;bajt zabójca @@:

Gdy deasembler (często wbudowany w debugger) napotka na bajt 0E8h uzna, że jest to początek rozkazu w kodzie maszynowym. Dokładnie to rozkazu CALL (wywołanie procedury). Od tego momentu kod będzie błędnie interpretowany co spowoduje jego zaciemnienie (ang. obfuscation). Bajt wstawiony w drugie miejsce ma kod operacyjny 0E9h, czyli rozkaz JMP (skok bezwarunkowy). Za tym miejscem również instrukcje zostaną błędnie rozkodowane.

Widok kodu z listingu 3.1 w debuggerze x64dbg przedstawiono na rysunku 3.1.

Metoda Rogue byte przeciw deasemblacji
Rysunek 3.1. Prezentacja działania metody Rogue byte — porównanie kodu

Jak widać na rysunku 3.1 oryginalny kod programu po deasemblacji nie jest poprawny. Szkodliwy bajt wstawiony między instrukcje częściowo zaburzył ich tłumaczenie m.in. wywołanie funkcji MessageBox całkowicie zniknęło, a dookoła pojawiło się pełno dziwnych instrukcji.

Dla całkowitej jasności wspomnę, że kod z zastosowaną metodą Rogue byte wykonuje się poprawnie.
Dzieje się tak dlatego, że bajt zabójca jest przeskakiwany instrukcją skoku JMP.

4. Zakończenie

Dziękuję za poświęcony czas na przeczytanie tego wpisu.

Dawid Farbaniec


Tagi:  reverse-engineering  masm64  security 

Komentarze czytających

majster napisał:

Ciekawy wpis. Możliwym jest zastosowanie takiego bajtu zabójcy w skompilowanym kodzie c++, deasemblując go? I na ile jest to skuteczne przeciw doświadczonemu crackerowi / hackerowi?

 ponad 2 tygodnie temu (10 sierpnia 2019 godz. 23:02)
Dawid (haker.info) napisał:

Dziękuję za komentarz i zainteresowanie wpisem 😃 Jeśli chodzi o Visual C++ z którym czasem pracuję, to kompilator dla architektury 64-bitowej (x64) nie wspiera tzw. inline assembly, czyli wstawek w języku Asembler w kodzie C++ (wprowadzili za to funkcje wewnętrzne nazywane intrinsics). Myślę, że można zastosować pseudo-instrukcję _emit, która odpowiada dyrektywie db z Asemblera MASM, czyli wstawia w określone miejsce bajt o podanej wartości.

Inny sposób połączenia Asemblera MASM z Visual C++ to utworzenie w projekcie Visual C++ procedur w języku Asembler, które można wywoływać w kodzie C++ i rogue byte można wstawić w tych procedurach.

W przypadku innych kompilatorów C++. Jeśli można zrobić wstawkę w Asemblerze (ang. inline assembly) w kodzie C++ to i raczej możliwe jest wstawienie opisywanego w artykule „bajtu zabójcy”.

Ciężko powiedzieć na ile ta metoda jest skuteczna. Na pewno nie będzie skuteczna w prostej formie i zastosowana samodzielnie bez innych zabezpieczeń. Ja traktowałbym technikę „bajtu zabójcy” jako dodatkowe zabezpieczenie.

 ponad 2 tygodnie temu (11 sierpnia 2019 godz. 03:52)


Wszystkie treści umieszczone na tej witrynie są chronione prawem autorskim. Surowo zabronione jest kopiowanie i rozpowszechnianie zawartości tej witryny bez zgody autora. Wszelkie opublikowane tutaj treści (w tym kody źródłowe i inne) służą wyłącznie celom informacyjnym oraz edukacyjnym. Właściciele tej witryny nie ponoszą odpowiedzialności za ewentualne niezgodne z prawem wykorzystanie zasobów dostępnych w witrynie. Użytkownik tej witryny oświadcza, że z zamieszczonych tutaj danych korzysta na własną odpowiedzialność. Wszelkie znaki towarowe i nazwy zastrzeżone zostały użyte jedynie w celach informacyjnych i należą wyłącznie do ich prawnych właścicieli. Korzystając z zasobów witryny haker.info oświadczasz, że akceptujesz powyższe warunki.