Teoria technik używanych przez wirusy

asmie

Teoria technik wykorzystywanych przez wirusy

Opracował: Asmie (-=Nevillon=- Member)
Data: 20.VII.2003
E-mail: asmie@poczta.onet.pl

1 Wstęp
2 Instalacja w pamięci operacyjnej
3 Metody ukrywania sie w systemie
     3.1 Semi-stealth
     3.2 Full-stealth
     3.3 Sector level stealth
     3.4 Modyfikacja CMOS
     3.5 VolumeID
4 Szyfrowanie kodu
5 Inne techniki
6 Jak sie bronic przed antywirusami
7 Zakonczenie

Wstęp

Witam wszystkich czytających ten tekst. Dany artykuł traktuje o metodach
używanych przez wirusy do instalacji sie w pamięci, ukrycia swojego kodu itp.
Tekst ten nie jest przeznaczony dla początkujących twórców wirusów, gdyż nie
omawia on problematyki od podstaw, a przedstawia wybrane zagadnienia z dziedziny
tworzenia tejże gałęzi "oprogramowania" :). toteż jeżeli chcesz sie nauczyć
pisać wirusy to ten tekst nie jest dla Ciebie, natomiast jeżeli masz w planach
rozwijać swoje zdolności to dobrze trafiłeś.

Instalacja w pamięci operacyjnej

Zaczniemy od rzeczy najprostszych metod instalacji czyli od tablicy wektorów
przerwan. Jest to dosc stary sposob i w nowszych systemach operacyjnych juz
raczej nie przejdzie, gdyz IVT (Interrupt Vector Table) jest chroniona i dostep
do niej wywoluje ogolny wyjatek ochrony. Na uzytek przerwan sa 1024 bajty,
z ktorych wykorzystywanych jest najczesciej tylko polowa, poniewaz wiekszosc
programow nie korzysta z przerwan powyzej 80H, totez mamy wtedy 512 bajtow na
wrzucenie naszego wirusa. Oznacza to iz nasz mikrob musi byc krotki, nawet
baardzo krotki. Kopiujemy go wtedy pod adres 0000:0200, gdzie rozpoczyna sie
druga polowa IVT. Tak jak pisalem, ten sposob w nowych OS-ach zawodzi...

Jezeli nasz wirus ma 256 bajtow (ciekawe ktory :) ) to mozna go zaladowac pod
adres 0000:0600 gdzie znajduja sie zmienne DOS-u potrzebne do inicjalizacji
systemu. Po inicjalizacji dane sa juz nieuzywane wiec mozna je spokojnie
zamazac.

Instalacja w pamieci ponizej 640 kB i w blokach UMB zostala dosc dokladnie
omowiona w innych tekstach wiec nie bede jej poswiecal duzo czasu. Nalezy
pamietac ze kazdy blok, z ktorych sklada sie pamiec w DOS-ie rozpoczyna sie
blokiem MCB, gdzie pod przesunieciem 01h-02h mamy adres bloku PSP wlasciciela
danego MCB, a gdy w nie wpiszemy 0008 to segment zniknie i bedzie traktowany
jako integralna czesc systemu DOS, co w praktyce oznacza, ze bedzie niewidoczny
dla programow przegladajacych pamiec (a scislej bedzie widoczny ale jako czesc
systemu).

Ciekawym pomyslem jest instalacja wirusa w pamieci HMA (High Memory Area).
HMA jest to pamiec znajdujaca sie powyzej 1 MB a mozliwa do zaadresowania w
trybie rzeczywistym procesora. Taka pamiec mozna sobie przydzielic poprzez
funkcje DOS-u... oto przyklad:

mov ax, 4A01h
int 2f
cmp bx, DLUGOSC
jb BRAK_HMA
mov ax, 4A02h
mov bx, DLUGOSC
int 2f

Pierwsze dwie instrukcje sprawdzaja ile jest dostepnych bajtow pamieci HMA i
ta ilosc zwracana jest w bx. Jezeli jest jej mniej niz dlugosc wirusa (zmienna
DLUGOSC) to oznacza ze nie mozna sie w niej zainstalowac, natomiast gdy wiecej
to nastepne dwie linijki powoduja przydzielenie odpowiedniej ilosci pamieci w
bloku HMA.

Istnieje mozliwosc instalacji sie w obszarze przeznaczonym dla buforow dyskowych
DOS. Metoda ta nie zmniejsza ilosci pamieci dla programow, a przez to ze
alokujemy sie w buforach, zostajemy uznani za czesc systemu. Jak kogos to
interesuje to niech przejrzy kod zrodlowy wirusa USSR.516.

Metody ukrywania sie w systemie

Wirusy modyfikuja pliki i sektory dyskowe. Dlatego tez programy antywirusowe
uzywajac zwyklego skaningu sa w stanie wykryc modyfikacje rzeczonych plikow lub
sektorow. Jak temu zaradzic ?? W momencie proby dostepu do zainfekowanego pliku
lub sektoru uruchamiane sa funkcje wirusa majace na celu oszukanie system.
Technika ta nosi nazwe stealth. Istnieje wiele odmian tej techniki... Postaram
sie przedstawic je po kolei:

Semi-stealth

Wirusy, ktore uzywaja tej techniki podaja rzeczywista dlugosc zainfekowanych
plikow wyswietlana na przyklad poprzez polecenie DIR. Wirus chcacy oszukac
system w ten sposob musi przejac jedna z trzech par przerwan: 11,12/21; 4E,4F/21;
714E,714F/21. Nie bede tutaj opisywal dwoch pierwszych rodzajow funkcji gdyz
odnosza sie one do starego typu przeszukiwania plikow. Uwzgledniaja one nazwy
nie dluzsze od 8 znakow, a jak wiemy poczawszy od Windows 95 mamy mozliwosc
uzywania nazw plikowych dluzszych niz te rzeczone 8 znakow. Wlasnie do obslugi
plikow z dlugimi nazwami powstaly dwie funkcje 714E oraz 714F przerwania 21.
Ogolnie wszystkie funkcje 71xx z int21h sa odpowiednikami tych samych funkcji
bez 71, tylko ze te z prefiksem obsluguja pliki o dlugich nazwach. Funkcja 714E
odpowiada funkcji FindFirstFileA, a funkcja 714F odpowiada FindNextFileA,
znanym na pewno wszystkim z WindowsAPI. Obie te funkcje korzystaja z nowej
struktury zaimplementowanej w Win95: FindData. Opis struktury:

00-03: atrybuty pliku - bity 0-6 standardowe atrybuty; bit 8 - plik tymczasowy
04-0B: czas i data utworzenia pliku
0C-13: czas i data ostatniego dostepu do pliku
14-1B: czas i data ostatniej modyfikacji pliku
1C-1F: dlugosc pliku (bardziej znaczace 32 bity)
20-23: dlugosc pliku (mniej znaczace 32 bity)
24-2B: zarezerwowane
2C-12F: 260 bajtowe pole pelnej nazwy pliku (jako ASCIIZ)
130-13D: 14 bajtowe pole krotkiej nazwy pliku (jako ASCIIZ)

Czyli zasada jest prosta - przejmujemy przerwania i podczas odwolania do
jakiegos zainfekowanego pliku podajemy jego oryginalny rozmiar, bez dlugosci
wirusa.

Full-stealth

Aby w pelni oszukac system nalezy oprocz prawdziwych dlugosci podawac takze ich
prawdziwa zawartosc. W tym celu nalezy przejac funkcje sluzace do manipulacji
zawartoscia plikow. Metoda jest prosta: w momencie odczytu pliku sprawdzic czy
jest on zainfekowany i w razie czego leczyc go w locie w pamieci. Wtedy program
odczytujacy plik widzi ze jest on zdrowy, natomiast jego obraz na dysku jest
nadal zainfekowany. Przy programowaniu takiego wirusa czesto dosc potrzebne sa
dwie funkcje DOS-a: 1216 i 1220/2F ,ktore sluza do operowania na wewnetrznych
strukturach DOS, tzw. tablicach SFT (System File Table), ktore zawieraja
wszelkie informacje o otwartym pliku. Dostep do zawartych tam informacji
otrzymuje sie na podstawie uchwytu (ang. handle) zwracanego przez funkcje 3D,
6C/21 przy jego otwieraniu. Uchwyt ten podaje sie jako parametr dla funkcji 1216,
1220/2F. Po ich uzyciu uzyskujemy adres JFT (Job File Table), opisujacej pliki
otwarte w danym procesie. Nastepnie otrzymujemy adres SFT za pomoca podanego
nizej kodu:

mov ax, 1220h		;wez adres JFT
mov bx, HANDLE		;uchwyt pliku
int 2FH			;wywolanie przerwania
mov bl, es:[di]		;wez numer tablicy SFT
mov ax, 1216h		;wez adres tablicy na podstawie numeru w BL
int 2fh			;wywolaj przerwanie; ES:DI wskazuje na tablice SFT pliku

A oto jak wyglada opis pliku zawarty w tablicy SFT:

00-01: liczba lacznikow do pliku
02-03: tryb
04   : atrybuty
05-06: informacja o pliku
07-0A: wskaznik do naglowka programu obslugi lub do bloku DPB
0B-0C: pierwsza JAP pliku
0D-0E: czas ostatniej modyfikacji
0F-11: data ostatniej modyfikacji
12-14: rozmiar pliku
15-18: aktualna pozycja odczytu/zapisu pliku
19-1A: wzgledny numer JAP
1B-1E: polozenie elementu katalogu opisujacego plik
1F-2A: nazwa i rozszerzenie pliku
2B-2E: wskaznik do poprzedniego elementu SFT
2F-30: numer komputera w sieci
31-32: adres wlasciciela pliku (PSP)
33-34: polozenie w obszarze roboczym listy zablokowanych regionow pliku
35-36: numer JAP
37-3A: wskaznik do IFS pliku lub 00000000h

Przejmujac przerwania dotyczace otwarcia i zamkniecia pliku mozemy stworzyc jego
zdrowy obraz w pamieci operacyjnej, ktory bedzie widziany dla wszystkich
programow. Przede wszystkim w tablicy SFT nalezy zmodyfikowac rozmiar pliku o
dlugosc wirusa. Do tego jedynym obszarem ktorym sie musi zajac nasz wirus jest
poczatek pliku, czyli na przyklad naglowek plikow EXE, gdzie wirus musi
przywrocic oryginalna zawartosc (czyli w wiekszosc przypadkow wystarczy
oryginalny Entry Point).

Sector level stealth

Technike stealth stosuje sie takze w odniesieniu do sektorow dyskowych. Jak to
zrobic ? Wystarczy przejac obsluge funkcji 02 przerwania 13h oraz ewentualnie
0A/13. W momencie gdy jakis program chce odczytac na przyklad zarazony MBR lub
BOOT-sektor nasz wirus podsuwa mu oryginalna zawartosc tegoz wlasnie sektora.

Modyfikacja CMOS

Kazda nowsza wersja BIOS-u zawiera mechanizmy ochrony newralgicznych punktow,
potrzebnych do rozruchu komputera. W ten sposob jest zabezpieczany MBR oraz
BOOT-sektory. Aby ominac to zabezpieczenie mozna wykorzystac pewna wlasciwosc,
a mianowicie dostepnosc programowa pamieci FLASH CMOS-u. Wymaga to znajomosci
roznych wersji BIOS-u poniewaz kazda wersja odznacza sie tym iz interesujace
nas informacje znajduja sie w roznych miejscach pamieci. Przejrzyj pliki
dotyczace organzacji pamieci CMOS w konkretnych BIOS-ach, a znajdziesz zadany
parametr. Potem wystarczy go tylko odpowiednio ustawic.

VolumeID

Wykorzystujac tablice SFT, o ktorych byla wczesniej mowa mozna w prosty sposob
ukryc swoj wirus. Plikowi zarazonemu przez naszego szkodnika nadajemy poprzez
wspomniane tablice atrybut VolumeID, czyli atrybut okreslajacy ze ten plik jest
etykieta dysku. Plik ukryty ta metoda bedzie widoczny tylko w edytorach
binarnych, natomiast zadne polecenie nie wyswietli pliku z danym atrybutem.

Szyfrowanie kodu

Szyfrowanie kodu przez wirusy to jest w sumie temat na osobny artykul. Postaram
sie tutaj przyblizyc ogolne zasady jakimi posluguja sie tworcy wirusow przy
szyfrowaniu swoich "prac" :). Do zaszyfrowania wirusa mozna zastosowac
praktycznie kazda operacje dostepna w mikorprocesorze posiadajacych swoja
odwrotnosc. Mam wiec do wyboru np:

  • ADD --> dodawanie
  • SUB --> odejmowanie
  • XOR --> suma modulo 2
  • NEG --> negacja arytmetyczna
  • NOT --> negacja logiczna
  • ROL --> przesuniecie cykliczne w lewo
  • ROR --> przesuniecie cykliczne w prawo

Procedury szyfrujace dzielimy na stale i losowe. Jak sie dosc nietrudno domyslic
stale to takie, ktore szyfruja wedlug wczesniej ustalonego algorytmu. Losowe
sa dosc zaawansowane i szyfrowanie polega na wybraniu przypadkowej ilosci
operacji szyfrujacych a nastepnie losowaniu w petli rodzaju operacji
wykonywanych na argumencie, argumentu operacji i rodzaju argumentow (BYTE, WORD,
DWORD, QWORD itp). Wybierane operacje sa zapamietywane na stosie lub w jakiejs
tablicy aby dekoder mogl je wykorzystac.

Dzialanie typowego dekodera ogranicza sie do wykonania w odwrotnej kolejnosci
operacji kodera oraz zmiane instrukcji na przeciwne tam gdzie to konieczne.
Prosty przyklad:

mov cx, DLUGOSC
mov bx, DANE
petla:
	add byte ptr [bx], 20h
	neg byte ptr [bx]
	xor byte ptr [bx], 91h
	sub byte ptr [bx], 2h
	inc bx
	loop petla

A oto procedura dekodujaca:

mov cx, DLUGOSC
mov bx, DANE
petla:
	add byte ptr [bx], 2h
	xor byte ptr [bx], 91h
	neg byte ptr [bx]
	sub byte ptr [bx], 20h
	inc bx
	loop petla

I oto caly trick z szyfratorami i deszyfratorami stalymi. Sprawa sie zupelnie
zmienia i komplikuje w przypadku wprowadzenia polimorfizmu w obieg :).
Najtrudniejszym elementem jest napisanie generatora zmiennych procedur
dekodujacych. Istnieja dwie odmiany polimorfizmu: tzw. semi-polimorfizm oraz
full-polimorfizm. W pierwszym z nich procedura dekodujaca jest w zasadzie
stala, gdyz opiera sie na stalym wzorcu i urozmaica sie ja poprzez napisaniu
kilku procedur dekodujacych i losowaniu jednej z nich podczas dzialania
wirusa. Kazda z nich dziala tak samo jednak sa zbudowane z roznych instrukcji
i rejestrow. W drugim typie polimorfizmu wirus przy kazdym uruchomieniu tworzy
calkowiecie nowa procedure dekodujaca. Dla kazdej procedury wirus losuje
kolejno:

  • indeks
  • licznik
  • kierunek zwiekszania licznika
  • kierunek dekodowania
  • rodzaj uzywanych instrukcji

Licznik jest to jakis rejestr procesora wybierany z listy np.: (e)ax, (e)bx,
(e)cx itd. Osobiscie nie polecam stosowania do tego celu rejestrow 32 bitowych
poniewaz ich zapis jest dluzszy od ich 16 bitowych odpowiednikow i otrzymujemy
dluzszy kod. No chyba ze jest to konieczne ale to juz sam musisz zadecydowac.
Indeks jest wybierany z listy mozliwych sposobow adresowania dla procesorow
80x86. Wszystkie mozliwe kombinacje pola indeks to:

  • [BX+xxxx]
  • [BP+xxxx]
  • [SI+xxxx]
  • [DI+xxxx]
  • [BP+SI+xxxx]
  • [BP+DI+xxxx]
  • [BX+SI+xxxx]
  • [BX+DI+xxxx]

Lista ta poszerza sie podczas korzystania z mozliwosci procesora 80386 i
wyzszych. Kierunek licznika okresla czy ma on byc inkrementowany od zera do
wartosci koncowej czy tez dekrementowany od wartosci koncowej do zera. Kierunek
dekodowania okresla czy dekodowanie zaczyna sie od poczatku czy tez od konca.
Aby bardziej skomplikowac dekoder pomiedzy wlasciwe instrukcje wstawiamy rozkazy
niepotrzebne z punktu widzenia dzialania wirusa ale potrzebne do zaciemnienia
calego obrazu. Instrukcje stanowiace taki wypelniacz musza spelniac kilka
warunkow:

  • nie moga zamazywac dowolnie nie uzywanej przez siebie pamieci
  • nie moga zawieszac komputera
  • nie moga generowac wyjatkow
  • nie moga niszczyc zawartosci rejestrow waznych dla dzialania programu
    (eg. CS, SS, SP itp.)

Dobrze jest umieszczac w wirusie instrukcje podobne do tych jakie uzywaja
zwykle programy. Czyli dosc wskazane jest uzywanie przerwan BIOS-u, DOS-u jak
na przyklad odczyt wersji czy tez sprawdzanie czy w buforze klawiatury jest
jakis znak.

Maly przyklad ile jest mozliwosc na zerowanie rejestru:

mov cx,0

xor cx,cx

sub cx,cx

and cx,0

zeruj:
loop zeruj

mov cx, xxx
sub cx, xxx

xor cl,cl
sub ch,ch

i jest tego jeszcze wiecej. Nalezy zaopatrzyc sie w spis instrukcji procesora
pod ktory piszemy wirusa i wybrac sobie zamienniki instrukcji uzywanych w
procedurze...

Inne techniki

Aby utrudnic zycie programistom tworzacym oprogramowanie antywirusowe nalezy
w jakis konkretny sposob zabezpieczyc nasz program przed debugowaniem i
disassemblacja. Sztuczki antydebugerowe w trybie chronionym nic nie dadza gdyz
debugger posiada wlasny IVT i nawet zmieniajac glowny IVT spod adresu 0000:0000
nic nie zrobimy. Stala sztuczka na debuggery trybu rzeczywistego bylo:

mov al, 0FFh
out 21h, al
out 0A1h, al

czyli maskowanie przerwan sprzetowych. Niestety obecnie nie jest to juz w ogole
przydatne.... Obecnie po prostu aby utrudnic disassemblacje wirusa stosuje sie
szyfrowanie opisane w poprzednim punkcie.

Wazna rzecza jest optymalizacja kodu wirusa tak aby byl on jak najmniejszy i
dzialal jak najszybciej. W tym celu stosuje sie odpowiedniki niektorych
instruckji ktore sa krotsze w zapisie ale za to tak samo funkcjonalne.
Najbardziej znana optymalizacja uzywana na duza skale jest zerowanie rejestru
poprzez xorowanie a nie poprzez mov rej, 0. Wymiana zawartosci AX z innym
rejestrem zajmuje mniej kodu gdy sie ja zapisze jako xchg ax, rej zamiast
mov... Czasem na przyklad lepiej jest napisac dwa razy inc rej , niz add rej, 2
gdyz jest to krotszy sposob.

Dobrym trickiem jest zaatakowanie programu antywirusowego. Jak to mowia
najciemniej pod latarnia i dobre monitory antywirusowe mimo ze bacznie badaja
kod innych programow do wlasnego nie przywiazuja wiekszej wagi. Dzieki
schematycznej budowie mozna takowy antywirus dosc latwo unieszkodliwic.
Najprostszym sposobem jest dodanie przez wirusa parametru uruchomieniowego
dla programu antywirusowego, ktory wylacza mu dostep do dyskow itp.

Jak sie bronic przed antywirusami

W tym punkcie postaram sie przedstawic techniki stosowane przez programy
antywirusowe:

a) skaning - najprostsza technika polegajaca na szukaniu charakterystycznego
dla danego wirusa ciagu bajtow tzw. sygnatury. Dlatego tez nalezy tworzyc w
miare mozliwosci jak najbardziej zmieniajacy sie kod aby programy antywirusowe
nie mialy okazji wylapac naszego wiruska po unikalnej dla niego sekwencji bajtow

b) heurystyka - podobnie jak skaning operuje ona na znajomosci sygnatury
wirusow. Wiekszosc nowych wirusow jest przerobkami starych totez tworcy
antyvirkow wpadli na pomysl ze przeciez sa pewne podobienstwa pomiedzy starymi,
a nowymi szkodanikami. Czesto heurystyka analizuje takze niektore rozkazy
charakterystyczne dla wirusow jak np.:

cmp ax, 4b00h   ;sprawdzenie czy jakis program jest uruchomiony

call next:
next:           ;pobranie RVA

Takich instrukcji jest wiele wiecej i nalezy na nie zwrocic uwage.

c) tryb krokowy - wykorzystywany przy wykrywaniu wirusow polimorficznych,
ktorych zwykly skaning nie da rady wykryc, poniewaz ich kod jest zmienny.

d) przynety - sa to programy dajace sie zainfekowac wirusowi. Na kod takiego
programu sklada sie kilkuset instrukcji NOP i instrukcji konca programu.

e) pobieranie wielkosci pamieci operacyjnej - no tego chiba nie musze tlumaczyc

Zakonczenie

Tekst ten tylko pobieznie prezentuje techniki wirusowe wiec nie wymagajcie od
niego za duzo :). W razie bledow e-mail me:

asmie@poczta.onet.pl
GG: 1868980

I of coz greetz :D : (jakze by inaczej :P )
moje Sloneczko :), Ekipa NvSlask, reszta Nevillonu, kanal #dabrowagornicza i #nevillon

logout

4 komentarzy

W dobie śmierci DOS'a art nieaktualny.
Ale można zrobić nowy art o współczasnych zagrożeniach.
Na stronie kaspersky bardzo fajnie opisują trojany, backdoory, robaki, malware.
A na tej stronce poziom szczegółowości opisów mógłby być trochę mniejszy niż na kaspersky a i tak by się fajnie czytało.

Hehe, wybaczcie ale nawyk... pisze juz zawsze bez ogonkow wiec baaardzo trudno jest mi sie przestawic na pisanie z ogonkami... zreszta po prostu nie chcialoby mi sie przerabiac tego tekstu bo idzie on do publikacji tez na innych portalach i tam z kolei jest raczej odwrotnie ze wola bez polskich znakow.. ale okej.. niemniej jednak o znacznikach bede pamietal :)

Polecam w celu zwiekszenia czytlnosci stosowac znaczniki <asm> oraz </asm> opatrujac nimi kod Asemblera.

Tak jak napisał Adam + polskie znaki - w artukułach tego przestrzegamy (a raczej staramy się, ale zawsze ktoś się wyłamie).

A sam artykuł dobry.