.286
decoder segment
  assume cs:decoder, ds:decoder, es:decoder
 
 jumps

cMAX_FILE   equ 05
cNAME_LEN   equ 12
cPASS_LEN   equ 10
cBUF_SIZE   equ 512

start:
 
  push ds                           ;save PSP address
; Check command line
  cmp byte ptr ds:[80h], 0
  je install

  cmp word ptr ds:[82h], 'u/'
  je remove

install: 
  push cs cs
  pop ds es
 
  mov ax,0DEADh                     ;our decoder residency check
  int 21h                           ;
  cmp bx,0CAFEh                     ;>CAFE :o)
  je already_resident               ;its already resident

;----------------
; Read files&passwords
  mov ax, 3d00h
  mov dx, offset fname
  int 21h
  jc exit_prog

  xchg bx, ax

  mov ah, 3fh
  mov cx, cMAX_FILE*cNAME_LEN
  mov dx, offset files
  int 21h

  mov ah, 3eh
  int 21h
  
  mov ax, 3d00h
  mov dx, offset pname
  int 21h
  jc exit_prog

  xchg bx, ax

  mov ah, 3fh
  mov cx, cMAX_FILE*cPASS_LEN
  mov dx, offset passwords
  int 21h

  mov ah, 3eh
  int 21h
  
  pop ds                            
  push ds                           ;PSP address back into DS

  ;--------------------------------------------------
  mov ax,ds                         ;MCB residency
  dec ax
  mov ds,ax

  mov cx, the_end - RES_BEG         ; Size of interrupt handlers
  shr cx, 10
  inc cx

  sub word ptr ds:[413h], cx        ; Decrease BIOS data's free space

  shl cx, 6
 
  sub word ptr ds:[3], cx           ; Decrease the size of Memory Block
  sub word ptr ds:[12h], cx         ; Same, but in PSP
 
  xor ax,ax
  mov ds,ax
 
  mov ax,word ptr ds:[413h]         ; Calculate address of free space
  shl ax,6
 
  mov es,ax
 
  push cs
  pop ds
 
  lea si, [RES_BEG]                   ; Copy our prog there
  xor di,di
  mov cx, the_end - RES_BEG              ; Size of interrupt handlers
  rep movsb
  ;--------------------------------------------------
  xor ax,ax                                 ;Setting of interrupts
  mov ds,ax                                 
                                            
  mov ax,es                                 
  mov bx,0                                  ; Offset to interrupt in new address

  cli
  xchg bx,word ptr ds:[21h*4]
  xchg ax,word ptr ds:[21h*4+2]
  mov word ptr es:[old_int21h-RES_BEG],bx
  mov word ptr es:[old_int21h+2-RES_BEG],ax
  sti
  ;--------------------------------------------------
  push cs cs
  pop ds es
 
  mov ah,9                                  ; Installed OK message
  lea dx,[message]
  int 21h

 exit_prog:

  pop ds                                    ;Restore PSP in DS
  mov ax,4c00h                              ;Request terminate program
  int 21h
;-----------------------------------------------------------------
; Resident begins here
RES_BEG:
 
 new_int21h:                                ;our int 21h handler
  pushf                                     ;push the flags
  cmp ax,0DEADh                             ;residency check
  jne no_install_check
  mov bx,0CAFEh                             ;already resident
  popf                                      ;restore all flags
  iret                                      ;return
 no_install_check:
  cmp ax, 0DEDDh
  je uninstall
  cmp ah, 4bh                               ;check if execute
  je infect
  cmp ah, 4ch                               ;check if terminate
  je terminate

 return:
  popf                                      ;restore all flags
 db 0eah                                    ;jmp to orig int 21h
 old_int21h dd ?
;--------------------------------
uninstall:
  pusha                                     ;only 286, saves all gen reg
  push ds
  push es

  push cs
  pop es

  xor ax,ax
  mov ds,ax
 
  cli
  xchg bx,word ptr es:[old_int21h-RES_BEG]
  xchg ax,word ptr es:[old_int21h+2-RES_BEG]
  
  mov word ptr ds:[21h*4],bx
  mov word ptr ds:[21h*4+2],ax
  sti

  mov cx, the_end - RES_BEG         ; Size of interrupt handlers
  shr cx, 10
  inc cx

  add word ptr ds:[413h], cx        ;Increase BIOS data's free space by 1Kb
  pop es
  pop ds
  popa

  popf                                      ;restore all flags
  iret                                      ;return
;-------------------------------   
 infect:
  pusha                                     ;only 286, saves all gen reg
  push ds
  push es

  call tsr_delta
 tsr_delta:
  pop bp                                    ;a tsr delta offset %-)
  sub bp, offset tsr_delta

  push ds
  push dx

  ; Test, if currently opening file is in database
  mov di, dx
  call SeekZ
  push di

  std
  mov al, '\'
  repnz scasb
  cld
  jnz $+3
  inc di
  inc di

  pop cx
  sub cx, di

  push cx
  push di

  mov dx, 12

  push cs
  pop es
  lea di, es:[bp+files]

searchloop:
  pop si
  pop cx
  push cx
  push si
  push di

  mov al, es:[di]
  test al, al
  jz not_found

  repz cmpsb
  jz found

  pop di
  add di, dx
  jmp searchloop

found:
  pop di
  pop si
  pop cx

; Calculate offset to password
; Get number of file in table
  lea si, es:[bp+files]
  sub di, si
  mov ax, di
  mov bx, cNAME_LEN
  xor dx, dx
  div bx

  mov bx, cPASS_LEN
  mul bx

  lea di, es:[bp+passwords]
  add di, ax
  mov es:[bp+pass], di

  push cs
  pop ds
  mov cx, cPASS_LEN
  mov al, ' '
  repnz scasb
  jnz $+3
  inc cx
  sub cx, cPASS_LEN
  neg cx

  mov es:[bp+pass_len], cx


  jmp decode_file

not_found:
  pop di
  pop si
  pop cx

  pop dx
  pop ds
  jmp exit
;------------------------------------

decode_file: 

  pop dx
  pop ds

  mov ax,3d02h                              ;open file in DS:DX
  int 21h
  jc exit

  xchg ax,bx                                ;file handle to bx
  mov cs:[bp+handle], ax
 
  push cs cs
  pop ds es
 
;-------------------------
; Decode all the file
;------------------------- 
        ; Calculate count of encoded bytes
        mov ax, 4202h
        xor cx, cx
        xor dx, dx
        int 21h

        mov cx, dx      ; Hi part
        mov dx, ax      ; Lo part

        sub dx, 2
        sbb cx, 0

        push cx
        push dx

        mov bx, ds:[bp+handle]
        mov ax, 4200h
        xor cx, cx
        mov dx, 2
        int 21h

        pop dx
        pop cx
buf_loop:
        push cx
        push dx

        sub dx, cBUF_SIZE
        sbb cx, 0
        jl last_buf

begins:
        mov ah, 3Fh             ; Read full buffer of
        mov cx, cBUF_SIZE       ; EXECing file
        lea dx, ds:[bp+buffer]
        int 21h         

        mov cx, ax
        dec cx
        lea di, ds:[bp+buffer]
        call decode
        lea di, ds:[bp+buffer]
        call submagic

        mov ax, 4201h
        mov dx, cBUF_SIZE
        neg dx
        xor cx, cx
        not cx
        int 21h
        jc BAD

        mov ah, 40h     ; Write segment to
        mov cx, cBUF_SIZE ; the input file
        lea dx, ds:[bp+buffer]
        int 21h         ;
        jc BAD

        cmp ax, cBUF_SIZE
        jne BAD

        pop dx
        pop cx

        sub dx, cBUF_SIZE
        sbb cx, 0
        jmp buf_loop
BAD:
     mov dx, ax
     shr dx, 12
     add dl, '0'
     mov ah, 2
     int 21

     mov dx, ax
     shr dx, 8
     and dx, 0Fh
     add dl, '0'
     mov ah, 2
     int 21

     mov dx, ax
     shr dx, 4
     and dx, 0Fh
     add dl, '0'
     mov ah, 2
     int 21

     mov dx, ax
     and dx, 0Fh
     add dl, '0'
     mov ah, 2
     int 21

     mov al, 7
     int 29h
     jmp close

last_buf:
        pop cx
        pop dx

        mov ah, 3Fh     ; Read data of
        push cx
        lea dx, ds:[bp+buffer]
        int 21h         ;

        lea di, ds:[bp+buffer]
        pop cx
        push cx
        dec cx
        lea di, ds:[bp+buffer]
        call decode
        lea di, ds:[bp+buffer]
        call submagic

        mov ax, 4201h
        pop dx
        push dx
        neg dx
        xor cx, cx
        not cx
        int 21h

        mov ah, 40h     ; Write segment to
        pop cx
        lea dx, ds:[bp+buffer]
        int 21h         ;
mov al, 7
int 29h
       ;- Done
 
 close:

  mov ah,3eh                                ;close file
  int 21h
 
 exit:
  pop es
  pop ds
  popa
  jmp return

terminate:

 jmp return
 
 
 pass_len dw 0
 pass      dw 0
 handle    dw 0
 passwords db (cPASS_LEN*cMAX_FILE) dup (0)
  db 0
 files     db (cNAME_LEN*cMAX_FILE) dup (0)
  db ' '
 buffer    db cBUF_SIZE dup (?)
 test_pass db 10, 12, 15, 17, -20, -13, 05, -30, 55, -51
;-----------------------
SeekZ proc near
  mov al, 0
  mov cx, 0ffffh
  repnz scasb
  neg cx
  dec cx
  dec di
  ret
endp

;------------------
; es:di - what to decode
; cx - length
decode proc near 
        push si
        push ax
        push cx
        push dx
        mov dx, ds:[bp+pass_len]
loops2: mov si, ds:[bp+pass]
        cmp cx, dx
        jb lasts2
        push cx
        mov cx, dx
addmag2: mov al, ds:[si]
        xor es:[di], al
        inc si
        inc di
        loop addmag2
        pop cx
        sub cx, dx
        jmp loops2

lasts2: inc cx
lasts3: mov al, ds:[si]
        xor es:[di], al
        inc si
        inc di
        loop lasts3

        pop dx
        pop cx
        pop ax
        pop si
        ret
endp

submagic proc near 
        push si
        push ax
        push cx
loops:  lea si, ds:[bp+test_pass]
        cmp cx, 10
        jb lasts
        push cx
        mov cx, 10
addmag: mov al, ds:[si]
        sub es:[di], al
        inc si
        inc di
        loop addmag
        pop cx
        sub cx, 10
        jmp loops

lasts:  inc cx
lasts0: mov al, ds:[si]
        sub es:[di], al
        inc si
        inc di
        loop lasts0

        pop cx
        pop ax
        pop si
        ret
endp
;------------------
the_end:

 message db 10,13,10,13
 db '- Decoder of encrypted files successfully installed -',10,13
 db '                           - Copyright Enikeew I.R. 2084/1 -',10,13,'$'

 msg_already db 10, 13
 db '- Decoder already installed - ', 10, 13
 db '                           - Copyright Enikeew I.R. 2084/1 -',10,13,'$'

 msg_not db 10, 13
 db '- Decoder isn''t installed !- ', 10, 13
 db '                           - Copyright Enikeew I.R. 2084/1 -',10,13,'$'

 msg_un db 10,13,10,13
 db '- Decoder were successfully removed -',10,13
 db '                           - Copyright Enikeew I.R. 2084/1 -',10,13,'$'

 fname db 'files.txt',0
 pname db 'pass.txt', 0

already_resident:
  mov ah,9                                  ; Installed OK message
  lea dx,[msg_already]
  int 21h
  jmp exit_prog

not_installed:
  mov ah,9                                  ; Installed OK message
  lea dx,[msg_not]
  int 21h
  jmp exit_prog

remove:
  push cs cs
  pop ds es
 
  mov ax,0DEADh                     ;our decoder residency check
  int 21h                           ;
  cmp bx,0CAFEh                     ;>CAFE :o)
  jne not_installed                 ;it isn't resident

  mov ax, 0DEDDh
  int 21h

  mov ah,9                                  ; Installed OK message
  lea dx,[msg_un]
  int 21h
  jmp exit_prog

VERY_END:
decoder ends
end start
