; 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 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