ethical.blue Magazine

// Cybersecurity clarified.

Kod samo-modyfikujący się dla Windows x64

29.05.2022   Dawid Farbaniec
...
Program, który sam modyfikuje swój kod, gdy jest już uruchomiony na pewno wzbudzi ciekawość podczas analizy w debuggerze. Technika ta polega na zamienianiu przez program swoich instrukcji podczas wykonywania.

Na początek trochę potrzebnej teorii. Kompilator lub jeśli programujemy w Asemblerze to bezpośrednio asembler (pisane z małej litery to narzędzie, z dużej to język) buduje kod źródłowy do pliku wykonywalnego (np. *.exe). Instrukcje procesora w pliku *.exe są reprezentowane poprzez kod maszynowy. Rozkazy i ich operandy są zakodowane jako ciąg bajtów (w prostych słowach: liczby).

Sytuacja początkowa przedstawia się tak, że po uruchomieniu programu mamy w pamięci rozkazy procesora jako kod maszynowy (bajty). Sposób tłumaczenia instrukcji w formie mnemoników (np. ADD, MOV czy XOR) na ich kody operacyjne (opkody) jest opisany dokładnie w dokumentacji procesorów x86-64. Modyfikacja kodu aplikacji w pamięci polega w skrócie na zamianie opkodów.

Na przykład instrukcja xor rcx, rcx (zerowanie rejestru licznika RCX) jest zakodowana jako ciąg bajtów 48 33 C9. Chcąc zmodyfikować kod w pamięci należy podmienić te bajty. Np. ustawienie im wartości 90 90 90 spowoduje, że otrzymanymi instrukcjami będą trzy rozkazy NOP.

Na tym etapie próba nadpisania instrukcji zakończy się błędem naruszenia ochrony pamięci (Exception Access Violation). Już tłumaczę dlaczego tak jest. Określone bloki pamięci mają ustawione odpowiednie atrybuty. I tak np. sekcji z danymi nie można domyślnie wykonywać (ang. execute). A do sekcji kodu nie można domyślnie pisać (ang. write).

Zatem kolejnym krokiem będzie poznanie funkcji, która pozwoli zmieniać atrybuty stron pamięci w uruchomionej aplikacji. Jest to funkcja z Windows API o nazwie VirtualProtect. Jej składnia przedstawia się następująco:
BOOL VirtualProtect(

    LPVOID lpAddress,
    SIZE_T dwSize,
    DWORD flNewProtect,
    PDWORD lpflOldProtect);

Kod samo-modyfikujący się w MASM x64

Prostym przykładem dla lepszego zrozumienia może być próba nadpisania przez program własnych instrukcji. Działanie tego kodu pod debuggerem można zobaczyć na materiale wideo poniżej.


Instrukcja jak zbudować przykładowy program:
Projekt w języku Asembler x64 dla Visual Studio 2022
extrn ExitProcess : proc

extrn VirtualProtect : proc
.const
PAGE_EXECUTE_READWRITE equ 040h
.data
oldProtect dd 0
.code
Main proc
;zmiana atrybutów bloku pamięci na:
;wykonanie, odczyt i zapis
sub rsp, 28h
mov r9, offset oldProtect
mov r8, PAGE_EXECUTE_READWRITE
mov rdx, 15h
mov rcx, offset _mutable
call VirtualProtect
add rsp, 28h
_mutable:
;wpisanie do rejestru RBX adresu etykiety _mutable
mov rbx, offset _mutable
;nadpisanie wskazywanych instrukcji czterema rozkazami NOP
mov dword ptr [rbx+011h], 090909090h
;poniżej instrukcje do nadpisania
xor rcx, rcx
ret
_exit:
sub rsp, 28h  
xor rcx, rcx    
call ExitProcess  
Main endp
end

Nieszkodliwy wirus EICAR też modyfikuje swój kod

Plik Eicar to nieszkodliwy program typu .com dla systemu MS-DOS. Służy do testów działania oprogramowania antywirusowego. Skrót EICAR oznacza European Institute for Computer Anti-Virus Research. Instytut ten to organizacja pracująca nad rozwojem technik wykrywania i zwalczania nie tylko wirusów komputerowych, ale ogólnie pojętego złośliwego oprogramowania. To właśnie w tym instytucie zrodził się pomysł stworzenia pliku, który będzie rozpoznawany jako wirus komputerowy, ale nie będzie wykonywał żadnych szkodliwych działań. Uznano, że plik tego rodzaju będzie bardzo pomocny przy testowaniu oprogramowania antywirusowego. Za pomocą tego małego pliku każdy użytkownik może sprawdzić działanie zainstalowanego antywirusa. Metody przeprowadzenia testu mogą być różne. Można spróbować pobrać ten plik z internetu, aby sprawdzić czy gdzieś na łączu jest oprogramowanie antywirusowe. Pobieranie można spróbować wykonać standardowo oraz przez ruch szyfrowany (np. HTTPS). Warto też sprawdzić czy antywirus wykrywa plik w czasie rzeczywistym (próbie dostępu). Kolejny pomysł to weryfikacja czy plik testowy Eicar spakowany w archiwum ZIP również zostanie wykryty czy też nie. Plik Eicar jest programem typu .com dla systemu MS-DOS. Jego rozmiar to 68 bajtów. W celu zmniejszenia możliwości użycia tego pliku w złośliwym celu wprowadzono limit ograniczający ilość bajtów do maksymalnie 128. Jego działanie to wyświetlenie napisu na ekranie o treści: EICAR-STANDARD-ANTIVIRUS-TEST-FILE! Zawartość pliku Eicar została opracowana tak, aby zawierała jedynie drukowalne znaki ASCII bez spacji i innych nietypowych znaków.

Eicar pod deasemblerem prezentuje następujący kod.
;this is eicar.com file disassembly

pop ax
xor ax, 214Fh
push ax
and ax, 4140h
push ax
pop bx
xor al, 5Ch
push ax
pop dx
pop ax
xor ax, 2834h
push ax
pop si
sub [bx], si
inc bx
inc bx
sub [bx], si
jnl 0140h
db "$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$"
int 21h
int 20h

Z ciekawości polecam prześledzić wykonanie tego programu pod debuggerem, ale uwaga: to program dla MS-DOS.

Wykaz literatury

Advanced Micro Devices Inc., 2017 – AMD64 Architecture Programmer's Manual
Intel Corporation, 2019 – Intel 64 and IA-32 Architectures Software Developer's Manual
https://docs.microsoft.com/en-us/cpp/assembler/masm/masm-for-x64-ml64-exe [access: 2020-07-28]