; 24 April 1995
; Follower.asm - Andrew Kohlsmith
; Follows MCBs, DOS Device Drivers, DPBs, DOS Buffers, etc.
; based on MCB.ASM, also by Andrew Kohlsmith
; 24 Apr 1995: Interesting... If you get to the last MCB link and follow it
; as if it were a normal link, you get into the HMA MCB's! DOS must change
; the 'Z' to an 'M' if you LH the program! Good for anti-detection? Who knows?
; 24 Apr: Just found out: If a DOS block (PID=0008) has a filename of "SD",
; then a driver MCB array like that found in the first MCB is embedded.
; So far, These filenames in a DOS block are special:
; SC = ? device driver headers? VDISK/XMMXXXX/etc are found 2 paras below.
; SC also marks the unusable area at a000:0 - afff:ffff...
; SD = DOS Device Driver MCB array
; UMB = found (on my machine) at B000:0, 1 para before himem MCB.
jumps
model small
include macros.inc ; standard macros
;.stack ; standard stack
;==============================================================================
.data
;==============================================================================
INVARS dw ?, ?
in_special_mcb db ? ; nonzero if special DOS mcb
;next_mcb dw ? ; seg of MCB after special one
init_msg db 0dh, 0ah, "FOLLOWER V0.1 By TZ 04/24/95", 0dh, 0ah, '$'
invars_at db 0dh, 0ah, "INVARS at $"
;--- MCB-related messages -----------------------------------------------------
; What an entry looks like:
;P_ID Para Reserved ------------ Program ---------- (MCB Link @ xxxx:xxxx)
;0008 0018 00 00 00 00 00 00 00 00 00 00 00 ABCDEFG (DOS MCB)
mcb_1st_at db 0dh, 0ah, "First MCB Link at $"
mcb_SC_break db 0dh, 0ah, '"SC"-Type DOS MCB Link Breakdown (MCB @ $'
mcb_SD_break db 0dh, 0ah, '"SD"-Type DOS MCB Link Breakdown (MCB @ $'
mcb_UMB_break db 0dh, 0ah, '"UMB"-Type DOS MCB Link Breakdown (MCB @ $'
mcb_break2 db '):', 0dh, 0ah, 79 dup ('='), '$'
mcb_S_end db 0dh, 0ah, "=== Regular MCBs follow========================="
db "==============================", 0dh, 0ah, '$'
mcb_line db 0dh, 0ah, "P_ID Para Reserved ------------ Program ---------"
db "-- $"
mcb_at db " MCB @ $"
mcb_followhi db 0dh, 0ah, 0ah, "MCB table ends in low memory."
db 0dh, 0ah, "Attempting to follow link into upper memory..."
db 0dh, 0ah, "(may abort if no upper memory)...", 0dh, 0ah, '$'
bad_mcb_msg db 0dh, 0ah, 0ah, "*** BAD MCB, Backtracking!", 0dh, 0ah, '$'
mcb_corrupt db 0dh, 0ah, 0ah, 07h, "MCB Table Corrupt!$"
; MCB flags (special PID numbers)
mcb_free db " (free MCB)$" ; PID = 0000
mcb_dos db " (DOS MCB)$" ; PID = 0008
; Following is a list of all MCB types. Only "M" and "Z" are valid outside of
; an special DOS MCB. Each entry is 27 characters long.
; " 1234567890123456789012345$"
mcb_types label byte
mcb_A db " $"
mcb_B db "BUFFERS= storage $"
mcb_C db "BUFFERS= EMS workspace $"
mcb_D db "Device Driver $"
mcb_E db "Device Driver Appendage $"
mcb_F db "FILES= control block $"
mcb_G db " $"
mcb_H db " $"
mcb_I db "File System Driver $"
mcb_J db " $"
mcb_K db " $"
mcb_L db "LASTDRIVE= dir struc array$"
mcb_M db "MCB link $"
mcb_N db " $"
mcb_O db " $"
mcb_P db " $"
mcb_Q db " $"
mcb_R db " $"
mcb_S db "STACKS= code/data area $"
mcb_T db " $"
mcb_U db " $"
mcb_V db " $"
mcb_W db " $"
mcb_X db "FCBS= control block $"
mcb_Y db " $"
mcb_Z db "Final MCB $"
;==============================================================================
.code
org 100
;==============================================================================
main proc near
mov ax, @data ; init DS, ES
mov ds, ax
mov es, ax
mov ah, 9 ; print header
mov dx, o init_msg
int 21h
mov ah, 52h ; Get INVARS ptr
int 21h ; INVARS @ ES:BX
mov [INVARS], bx ; save INVARS for later
push es
pop [INVARS+2]
mov ax, o invars_at ; DS:AX = message
call Print_Pointer_Msg ; display INVARS ptr
call Follow_MCB ; follow MCBs
mov ax, 4c00h
int 21h
main endp
;----------
; Follow_MCB
; Does what it says. Follows the MCB list until the end and returns.
; Bombs if MCB table corrupt.
;----------
Follow_MCB proc
mov [in_special_mcb], 0 ; clear flag
les bx, dword ptr [INVARS] ; ES:BX = INVARS
sub bx, 2 ; ES:BX is now ptr to 1st mcb
mov dx, es:[bx] ; DX = seg of 1st mcb
mov es, dx ; ES = seg of 1st mcb
xor bx, bx ; ES:BX = 1st mcb
mov ax, o mcb_1st_at ; message
call Print_Pointer_Msg ; print "1st MCB at xxxx:xxxx"
mov ah, 2
mov dl, 0dh
int 21h
mov dl, 0ah
int 21h ; print CR/LF
display_mcb_loop:
call Print_Mcb ; display MCB information
jnc mcb_ok
; MCB was bad, if [in_special_mcb], grab the address of the next mcb and
; continue there.
mov ah, 9
mov dx, o bad_mcb_msg
int 21h
pop es
; mov dx, [next_mcb] ; seg of next mcb
; mov es, dx
dec [in_special_mcb]
jmp display_mcb_loop
mcb_ok:
; if the MCB was ok, check for special DOS MCB types:
cmp word ptr es:[1], 0008h ; is it a DOS block?
jnz normal_mcb ; jmp if not
cmp word ptr es:[8], "DS" ; is it an SD-type block?
jnz normal_mcb ; jmp if not
; Put this back in when I want to work on SC and UMB jumps...
; jnz check_sc ; jmp if not
mov ax, o mcb_SD_break
jmp short do_block
check_sc:
cmp word ptr es:[8], "CS" ; is it an SC-type block?
jnz check_umb ; jmp if not
mov ax, o mcb_SC_break
jmp short do_block
check_umb:
cmp word ptr es:[8], "MU" ; first 2 letters of "UMB"?
jnz normal_mcb ; jmp if not
mov ax, o mcb_UMB_break
do_block:
xor bx, bx
call Print_Pointer_Msg
mov ah, 9
mov dx, o mcb_break2
int 21h ; print msg
inc [in_special_mcb] ; flag special mcb breakdown
mov dx, es ; DX = MCB segment
add dx, 1 ; drop into 1st MCB (+1 para)
mov ax, dx ; AX = MCB+1
; we already have MCB+1, so just add on the length of this MCB to find out
; where the second one lies. We need to know so we can drop out of
; [in_special_mcb].
add ax, es:[3] ; add on length of MCB
push ax
; mov [next_mcb], ax ; save seg of next MCB
mov es, dx ; ES = special MCB (internal)
jmp short display_mcb_loop ; jmp back and show internal MCB
normal_mcb:
cmp byte ptr [in_special_mcb], 0 ; are we doing special mcb?
jnz skip_bad_mcb_check ; jmp if so
cmp byte ptr es:[0],'M' ; is there another?
jz skip_bad_mcb_check ; jmp if so
cmp byte ptr es:[0],'Z' ; The last one?
jnz bad_mcb ; jmp if not
; If the last link was found to be in low memory, try to follow link up into
; high memory. Just treat the link as if it were an 'M' link. LoadHigh must
; change the last MCB to 'M' before executing the program. Possible cool
; utility? Perhaps...
; Note that I don't try to follow a "Z" link in high memory. I don't know
; if it would serve any purpose of if a "Z" link in high memory is really a
; "Z" link. Maybe SuperLoadHigh changes it to an "M"... :-)
mov dx, es ; get seg of MCB in DX
cmp dx, 0a000h ; was it in low memory?
jae follow_mcb_done ; jmp if not
mov ah, 9
mov dx, o mcb_followhi
int 21h ; print message
jmp short skip_bad_mcb_check ; follow MCB like usual
; Not M or Z, bad entry... just bomb, let DOS lock up.
bad_mcb:
mov dx, o mcb_corrupt ; if we end up here, there is a
mov ah, 9 ; corrupt MCB table <bad thing>
int 21h
mov ax, 4cffh
int 21h
skip_bad_mcb_check:
mov dx, es ; DX:0 = MCB
add dx, es:[3] ; add on # of paras used
inc dx ; include mcb (+1 para)
mov es, dx ; ES:0 = next MCB
cmp byte ptr [in_special_mcb], 0 ; are we tracing special MCB?
jz display_mcb_loop ; jmp if not
mov bp, sp
cmp dx, [bp] ; are we at next mcb already?
; cmp dx, [next_mcb] ; are we at next mcb already?
jnz display_mcb_loop ; jmp if not
dec [in_special_mcb] ; unflag special mcb breakdown
pop es
mov ah, 9
mov dx, o mcb_S_end
int 21h ; display ending string
jmp display_mcb_loop ; keep on going...
follow_mcb_done:
ret
Follow_MCB endp
;----------
; Print_MCB
; Just prints out the information about an MCB, including special PID codes.
; Prints out special link codes as well (@ mcbseg:[0]) for DOS-related links.
; returns with carry set on error
;----------
Print_MCB proc
mov ah, 9
mov dx, o mcb_line
int 21h ; print top line
mov dx, o mcb_types ; start of link descriptions
mov al, es:[0] ; type of MCB link
sub al, 'A' ; convert to number 0-25
jge pmcb_ok_type ; if > 0, type is ok
; we underflowed the table, type was < "A", just return. We don't check for
; overflowing, but fuck it. Well, at least in V0.1... :-)
stc ; set carry
ret
pmcb_ok_type:
mov ah, 27 ; 27 chars per description
mul ah ; AX has offset into table
add dx, ax ; DX has offset to entry
mov ah, 9
int 21h ; print MCB link type
mov ah, 2
mov dl, 0dh
int 21h
mov dl, 0ah
int 21h ; print CR/LF
mov dx, es:[1] ; DX = Process ID
call printdx_spc
mov dx, es:[3] ; DX = size in paras
call printdx_spc
mov si, 5 ; ES:SI = reserved byte
mov cx, 11 ; 11 bytes to go
pmcb_resloop:
mov al, es:[si]
inc si
mov dl, al
call printdl_spc ; print it
loopx cx, pmcb_resloop
mov cx, 8 ; CX = 8 chars
mov si, cx ; ES:SI = program name
pmcb_prog_charloop:
mov ah, 2 ; DOS print char
mov al, es:[si]
inc si
cmp al, 20h ; >= space?
jb pmcb_pdot ; jmp if not
cmp al, 80h ; < 128?
jb pmcb_pchar ; jmp if so
pmcb_pdot:
mov al, '.' ; default to '.'
pmcb_pchar:
mov dl, al
int 21h
loopx cx, pmcb_prog_charloop ; print all reserved bytes
mov ax, o mcb_at
xor bx, bx
call Print_Pointer_Msg ; print "MCB @ xxxx:yyyy"
mov dx, es:[1] ; DX = PID
cmp dx, 8 ; PID = 8 (DOS) ?
jnz pmcb_notdos ; jmp if not
mov dx, o mcb_dos ; set up DX
jmp s pmcb_printspecial
pmcb_notdos:
cmp dx, 0 ; PID = 0 (free) ?
jnz pmcb_not_free ; jmp if not
mov dx, o mcb_free
pmcb_printspecial:
mov ah, 9
int 21h ; print special PID
pmcb_not_free:
mov ah, 2
mov dl, 0dh
int 21h
mov dl, 0ah
int 21h ; print CR/LF
clc
ret
Print_MCB endp
;----------
; Print_Pointer_Msg
; Prints the message @ DS:AX, followed by "xxxx:yyyy",
; with xxxx = ES and yyyy = BX
;----------
Print_Pointer_Msg proc
mov dx, ax ; DS:DX = message
mov ah, 9
int 21h ; print it
mov dx, es
call printdx
mov ah, 2
mov dl, ':'
int 21h
mov dx, bx
call printdx
ret
Print_Pointer_Msg endp
;----------
; Just dumb little function-macros so I don't waste lots of code space.
;----------
printdx proc
printh16 dx ; 0-pad hex dump of DX
ret
printdx endp
printdx_spc proc
printh16 dx ; 0-pad hex dump of DX
mov ah, 2
mov dl, 20h
int 21h ; and a space
ret
printdx_spc endp
printdl_spc proc
printh8 dl ; 0-pad hex dump of DL
mov ah, 2
mov dl, 20h
int 21h ; and a space
ret
printdl_spc endp
end main