;;
 ; @(#) kill.asm - Task killer resident utility.
 ; (c) 1997 Ivan Maidanski <ivmai@chat.ru> http://ivmai.chat.ru
 ; Freeware program source. All rights reserved.
 ;;
 ; Language: ASM8086 (for IBM PC/AT, MS-DOS v3.0+, COM file)
 ; Tested with: Turbo Assembler v3.1, Turbo Link v5.1
 ; Last modified: 1997-10-11 14:50:00 GMT+04:00
 ;;
NAME KillTask

 ; tasm kill.asm
 ; tlink /t kill.obj

 ; Comment: When installed You can abort any program by pressing Ctrl+Alt+Esc

.MODEL SMALL
.CODE
.8086

Null LABEL NEAR

 ; Program global constants
TSR@ActCnt= 3h ; number of sequental hot key pressing to ignore DOS state
TSR@SavedIntsCnt= 100h ; number of saved/restored interrupt vectors

 ; PSP segment
 ORG 2Ch
Main@EnvSeg DW ? ; environment segment

 ; Resident-part non-initialized valiables
 ORG 10h
Int2Fh@SwStRec DW 9h DUP(?)
Int2Fh@InstRec DW 5h DUP(?)

 ; Resident-part valiables with initialization
 ORG 6Ch
TSR@Data LABEL BYTE
TSR@LastIVT DW ? ; last control task segment (with IVTable)
TSR@Activated DB ? ; activation required flag
TSR@Busy DB ? ; system busy flag (counter)
TSR@DataLen= $-TSR@Data

 ; Resident-part shifted code beginning
TSR@Start LABEL NEAR

 ; Program start point
 ORG 100h
Start:
 cmp sp,OFFSET StackTop ; check available memory
 jb Start@Error
 jmp Main
Start@Error:
 xor ax,ax ; abort program if no memory
 push es
 push ax
 retf

 ; RAM-resident part code
ASSUME ds:NOTHING,es:NOTHING
 EVEN
TSR@Begin LABEL NEAR
TSR@Shift= TSR@Begin-TSR@Start

 EVEN
Int08h PROC FAR
 ; Comment: Timer interrupt handler
 ; Expects: -
 ; Returns: -
 ; Uses: -
 pushf
 DB 9Ah ; call far
Int08h@Old DD ?
 cmp cs:[TSR@Activated],0 ; activated ?
 jne Int08h@Try
 iret
Int08h@Try:
 cmp cs:[TSR@Activated],TSR@ActCnt ; ignore DOS status ?
 jae Int08h@Abort
 push ds ; check DOS status
 push ax
 mov ax,0 ; load DS with DOS segment
 ORG $-2h
Int08h@InDOSSeg DW ?
 mov ds,ax
 pop ax
 cmp byte ptr ds:[0],0 ; DOS busy ?
 ORG $-3h
Int08h@InDOSOfs DW ?
 DB 0
 pop ds
 jne Int08h@Ret
Int08h@Abort:
 call Abort
Int08h@Ret:
 iret
Int08h ENDP

 EVEN
Int09h PROC FAR
 ; Comment: Keyboard interrupt handler
 ; Expects: -
 ; Returns: -
 ; Uses: -
 inc cs:[TSR@Busy] ; close activation gate
 push ax
 in al,60h ; get key scan code
 cmp al,1h ; [Esc] key ?
 je Int09h@Check
Int09h@FarCall:
 pop ax
 pushf
 DB 9Ah ; call far
Int09h@Old DD ?
 dec cs:[TSR@Busy] ; open activation gate
 iret
Int09h@Check:
 push ds
 xor ax,ax
 mov ds,ax
 mov al,byte ptr ds:[417h] ; get keyboard flags
 pop ds
 and al,0Fh ; Ctrl+Alt ?
 cmp al,0Ch
 jne Int09h@FarCall
 in al,61h ; reset keyboard controller
 or al,80h
 out 61h,al
 and al,7Fh
 out 61h,al
 mov al,20h ; reset interrupt controller
 out 20h,al
 pop ax
 inc cs:[TSR@Activated] ; set activation flag
 dec cs:[TSR@Busy] ; open activation gate
 iret
Int09h ENDP

 EVEN
Int10h PROC FAR
 ; Comment: BIOS Video service handler
 ; Expects: - (registers passed to int 10h)
 ; Returns: - (registers returned by int 10h)
 ; Uses: -
 inc cs:[TSR@Busy] ; close activation gate
 pushf
 DB 9Ah ; call far
Int10h@Old DD ?
 dec cs:[TSR@Busy] ; open activation gate
 sti ; simulate IRET without popping flags
 ret 2h
Int10h ENDP

 EVEN
Int13h PROC FAR
 ; Comment: BIOS disk I/O service handler
 ; Expects: - (registers passed to int 13h)
 ; Returns: - (registers returned by int 13h)
 ; Uses: -
 inc cs:[TSR@Busy] ; close activation gate
 pushf
 DB 9Ah ; call far
Int13h@Old DD ?
 dec cs:[TSR@Busy] ; open activation gate
 sti ; simulate IRET without popping flags
 ret 2h
Int13h ENDP

 EVEN
Int21h PROC FAR
 ; Comment: DOS multifunctional handler
 ; Expects: ax=4B00h, ds:[dx] - ASCIIZ file name, es:[bx] - ExecParamRec
 ; Returns: ax - error code,...
 ; Uses: - (ax)/(all)
 pushf
 cmp ax,4B00h ; execute new process ?
 je Int21h@Execute
; cmp ax,4B05h ; set execution state ?
; je Int21h@SetState
 popf
 DB 0EAh ; jmp far
Int21h@Old DD ?
Int21h@Execute:
 popf
 inc cs:[TSR@Busy] ; close activation gate
 sti
 push ax
 push bx
 mov ah,48h ; allocate memory for IVT (and chain pointers)
 mov bx,((6h+TSR@SavedIntsCnt*4h)+0Fh)/10h
 pushf
 call cs:[Int21h@Old-TSR@Shift]
 pop bx
 jc Int21h@ErrRet
 push ds ; chain current table
 mov ds,ax
 xchg ax,cs:[TSR@LastIVT]
 mov word ptr ds:[0],ax
 pop ds
 pop ax
 cld ; save address of the call-point
 push es
 push si
 push di
 mov es,cs:[TSR@LastIVT]
 mov di,2h
 mov si,sp
 add si,6h
 movs word ptr es:[di],word ptr ss:[si]
 movs word ptr es:[di],word ptr ss:[si]
 push ds ; make copy of the current IV table
 push cx
 xor si,si
 mov ds,si
 mov cx,TSR@SavedIntsCnt*2h
 rep movs word ptr es:[di],word ptr ds:[si]
 pop cx
 pop ds
 pop di
 pop si
 pop es
 cli
 dec cs:[TSR@Busy] ; open activation gate
 pushf ; execute process
 call cs:[Int21h@Old-TSR@Shift]
 inc cs:[TSR@Busy] ; close activation gate
 jnc Int21h@Restore
 push ax
 push es
 mov es,cs:[TSR@LastIVT] ; unchain current copy
 mov ax,word ptr es:[0]
 mov cs:[TSR@LastIVT],ax
 sti
 mov ah,49h ; free current IV table copy
 pushf
 call cs:[Int21h@Old-TSR@Shift]
 pop es
Int21h@ErrRet:
 pop ax
 dec cs:[TSR@Busy] ; open activation gate
 ret 2h ; simulate IRET without popping flags
Int21h@Restore:
 cli
 cld ; restore address of the call-point
 xor si,si
 mov ds,cs:[TSR@LastIVT]
 lods word ptr ds:[si]
 push word ptr ds:[si+2h]
 push word ptr ds:[si]
 cmp cs:[TSR@Activated],0 ; TSR activated ?
 je Int21h@Skip
 mov cs:[TSR@Activated],0 ; reset activation flag
 add si,4h ; restore IV table
 xor di,di
 mov es,di
 mov cx,TSR@SavedIntsCnt*2h
 rep movs word ptr es:[di],word ptr ds:[si]
Int21h@Skip:
 mov cs:[TSR@LastIVT],ax ; unchain current copy
 sti
 mov ah,49h ; free current IV table copy
 push ds
 pop es
 pushf
 call cs:[Int21h@Old-TSR@Shift]
 clc ; clear error flag
 dec cs:[TSR@Busy] ; open activation gate
 ret ; simulate JMP to previous process
Int21h ENDP

 EVEN
Int28h PROC FAR
 ; Comment: DOS Idle handler
 ; Expects: -
 ; Returns: -
 ; Uses: -
 cmp cs:[TSR@Activated],0 ; activated ?
 jne Int28h@Act
Int28h@FarJump:
 DB 0EAh ; jmp far
Int28h@Old DD ?
Int28h@Act:
 call Abort
 jmp short Int28h@FarJump
Int28h ENDP

 EVEN
Int2Fh PROC FAR
 ; Comment: Multiplex interrupt handler
 ; Expects: ah - ID number, al=0 - installation check,
 ; ax=1605h - identify instance data (Windows 386 API),...,
 ; ax=4B05h - identify instance data (Task Switcher API),...,
 ; es=0 & bx=0 (identify)
 ; Returns: ah - ID number (check), al=1h - installed (check),
 ; es:[di] - ASCIIZ ID string (check), es:[bx] - SwStRec (identify),...
 ; Uses: - (al,di,es)/(bx,cx,si,ds,es)
 cmp ax,1605h ; starting Windows 386 ?
 je Int2Fh@MultiEnv
 cmp ax,4B05h ; starting DOS Task Switcher ?
 je Int2Fh@MultiEnv
 cmp ah,0
 ORG $-1h
Int2Fh@IDNumber DB ?
 je Int2Fh@Check
 DB 0EAh ; jmp far
Int2Fh@Old DD ?
Int2Fh@MultiEnv:
 pushf
 call cs:[Int2Fh@Old-TSR@Shift]
 mov cs:[Int2Fh@SwStRec+2h],bx
 mov bx,OFFSET Int2Fh@InstRec
 mov word ptr cs:[bx+2h],cs ; make copy of TSR initialized variables
 mov word ptr cs:[bx],OFFSET TSR@Data
 mov word ptr cs:[bx+4h],TSR@DataLen
 mov word ptr cs:[bx+6h],0 ; end of areas definition
 mov word ptr cs:[bx+8h],0
 mov bx,OFFSET Int2Fh@SwStRec
 mov word ptr cs:[bx+4h],es ; chain to previous list
 mov word ptr cs:[bx+0Eh],OFFSET Int2Fh@InstRec
 mov word ptr cs:[bx+10h],cs
 mov word ptr cs:[bx],3h ; set target Windows version
 mov word ptr cs:[bx+6h],0
 mov word ptr cs:[bx+8h],0
 mov word ptr cs:[bx+0Ah],0
 mov word ptr cs:[bx+0Ch],0
 jmp short Int2Fh@SetSeg
Int2Fh@Check:
 cmp al,0
 jne Int2Fh@Ret
 inc ax ; indicate that ID number taken
 mov di,OFFSET Int2Fh@IDStr-TSR@Shift
Int2Fh@SetSeg:
 push cs
 pop es
Int2Fh@Ret:
 iret
Int2Fh@IDStr DB 'TaskKill v1.1 (c) 1997',0
Int2Fh@IDStrLen= $-Int2Fh@IDStr
Int2Fh ENDP

 EVEN
Abort PROC NEAR
 ; Comment: Abort current process if possible
 ; Expects: -
 ; Returns: - (not applicable if possible)
 ; Uses: -
 cmp cs:[TSR@Busy],0 ; system busy ?
 jne Abort@Ret
 cmp cs:[TSR@LastIVT],0 ; no executed processes ?
 push ax
 je Abort@Reset
 mov al,0Bh ; interrupt controller busy ?
 out 20h,al
 jmp short $+2h
 in al,20h
 test al,al
 jnz Abort@Restore
 inc cs:[TSR@Busy] ; close activation gate
 mov ax,3h ; clear screen by setting 80*25 text mode
 int 10h
 mov ax,4CFFh ; terminate current process
 pushf
 dec cs:[TSR@Busy] ; open activation gate
 call cs:[Int21h@Old-TSR@Shift]
Abort@Reset:
 mov cs:[TSR@Activated],0 ; reset activation flag
Abort@Restore:
 pop ax
Abort@Ret:
 ret
Abort ENDP

TSR@CodeLen= $-TSR@Begin
TSR@Length= $-TSR@Shift-Null

 ; Non-resident part code
ASSUME ds:@CODE,es:NOTHING

Main PROC NEAR
 ; Comment: -
 ; Expects: ds=es=cs
 ; Returns: - (not applicable)
 ; Uses: - (all)
 mov sp,OFFSET StackTop
 mov ax,3000h ; get DOS version
 int 21h
 cmp al,3h ; version 3.00 or later ?
 jae Main@Check
Main@Exit:
 int 20h
Main@Check:
 mov ah,9h ; print program ID information
 mov dx,OFFSET Main@Msg
 int 21h
 call CheckInst ; already installed ?
 test bh,bh
 jnz Main@Exit
 xor ax,ax ; check for presence of PSP
 xchg ax,ds:[Main@EnvSeg]
 test ax,ax
 jz Main@Set
 mov es,ax ; free PSP
 mov ah,49h
 int 21h
Main@Set:
 call SetUpTSR
 mov ax,3100h ; terminate but stay resident
 mov dx,(TSR@Length+0Fh)/10h
 int 21h
Main ENDP

CheckInst PROC NEAR
 ; Comment: Check whether TSR already installed
 ; Expects: ds=cs
 ; Returns: bh - ID number (or bh=0 if TSR not installed),
 ; es - TSR segment (if bh>0)
 ; Uses: ax,bx,cx,dx,si,di,es
 mov bx,0C001h
CheckInst@Loop:
 xor ax,ax ; check current ID number
 mov es,ax
 mov ah,bh
 push bx
 int 2Fh
 pop bx
 cmp ax,bx ; TSR found ?
 jne CheckInst@Cont
 mov ax,es ; No segment ?
 test ax,ax
 jz CheckInst@Cont
 cld
 mov si,OFFSET Int2Fh@IDStr
 mov cx,Int2Fh@IDStrLen
 repe cmps byte ptr ds:[si],byte ptr es:[di]
 je CheckInst@Ret
CheckInst@Cont:
 inc bh
 jnz CheckInst@Loop
CheckInst@Ret:
 ret
CheckInst ENDP

SetUpTSR PROC NEAR
 ; Comment: initialize all TSR variables and set up new interrupt handlers
 ; Expects: ds=cs
 ; Returns: -
 ; Uses: ax,bx,cx,dx,si,di,es
 mov bx,0C000h ; get free ID number
SetUpTSR@Loop:
 mov ax,bx ; check current ID number
 push bx
 int 2Fh
 pop bx
 cmp ax,bx ; ID number reserved ?
 je SetUpTSR@Init
 inc bh
 jnz SetUpTSR@Loop
SetUpTSR@Init:
 mov ds:[Int2Fh@IDNumber],bh
 mov ds:[TSR@Activated],0 ; reset activation flag
 mov ds:[TSR@Busy],0 ; reset system busy flag
 mov ds:[TSR@LastIVT],0 ; set no last task control segment
 mov ah,34h ; get InDOS flag address
 int 21h
 mov ds:[Int08h@InDOSSeg],es
 mov ds:[Int08h@InDOSOfs],bx
 mov ax,3509h ; store old int 9h handler address
 int 21h
 mov word ptr ds:[Int09h@Old],bx
 mov word ptr ds:[Int09h@Old+2h],es
 mov ax,3510h ; store old int 10h handler address
 int 21h
 mov word ptr ds:[Int10h@Old],bx
 mov word ptr ds:[Int10h@Old+2h],es
 mov ax,3513h ; store old int 13h handler address
 int 21h
 mov word ptr ds:[Int13h@Old],bx
 mov word ptr ds:[Int13h@Old+2h],es
 mov ax,352Fh ; store old int 2Fh handler address
 int 21h
 mov word ptr ds:[Int2Fh@Old],bx
 mov word ptr ds:[Int2Fh@Old+2h],es
 mov ax,3521h ; store old int 21h handler address
 int 21h
 mov word ptr ds:[Int21h@Old],bx
 mov word ptr ds:[Int21h@Old+2h],es
 mov ax,3528h ; store old int 28h handler address
 int 21h
 mov word ptr ds:[Int28h@Old],bx
 mov word ptr ds:[Int28h@Old+2h],es
 mov ax,3508h ; store old int 8h handler address
 int 21h
 mov word ptr ds:[Int08h@Old],bx
 mov word ptr ds:[Int08h@Old+2h],es
 cld ; shift resident-part code
 push ds
 pop es
 mov si,OFFSET TSR@Begin
 mov di,OFFSET TSR@Start
 mov cx,(TSR@CodeLen+1h)/2h
 rep movs word ptr es:[di],word ptr ds:[si]
 mov ax,2509h ; set up new int 09h handler
 mov dx,OFFSET Int09h-TSR@Shift
 int 21h
 mov ax,2510h ; set up new int 10h handler
 mov dx,OFFSET Int10h-TSR@Shift
 int 21h
 mov ax,2513h ; set up new int 13h handler
 mov dx,OFFSET Int13h-TSR@Shift
 int 21h
 mov ax,252Fh ; set up new int 2Fh handler
 mov dx,OFFSET Int2Fh-TSR@Shift
 int 21h
 mov ax,2521h ; set up new int 21h handler
 mov dx,OFFSET Int21h-TSR@Shift
 int 21h
 mov ax,2528h ; set up new int 28h handler
 mov dx,OFFSET Int28h-TSR@Shift
 int 21h
 mov ax,2508h ; set up new int 08h handler
 mov dx,OFFSET Int08h-TSR@Shift
 int 21h
 ret
SetUpTSR ENDP

 ; Program messages
Main@Msg DB 'Task Killer Resident Utility v1.1',0Dh,0Ah
         DB 'Use Ctrl+Alt+Esc to abort any program.',0Dh,0Ah,'$'

 ; Program stack
 EVEN
 DW 80h DUP(?)
StackTop LABEL WORD

 ; End of program
END Start
