[net.sources] Two MS-DOS Programs Using Undocumented Features

roy@gitpyr.UUCP (Roy Mongiovi) (10/13/85)

The following are two programs that use undocumented features of DOS
to display interesting information about the PC environment.  I believe
that these programs run under PC DOS 2.0, 2.1, 3.0, and 3.1 (as well as
the corresponding MS versions), but as with all undocumented features:
caveat programmer.

The first program, LDEVS, opens an FCB to the NUL: device and uses an
undocumented field of the FCB (which is different for DOS 2 and 3) to
find the start of the device chain.  It then traces the chain displaying
the name and type of all devices.

The second program, MSCAN, uses an undocumented DOS function (52H - Get
In Vars) to find the first memory block, and then prints out information
about all memory blocks.  I'm not sure what "Get In Vars" does, but it
seems to return a pointer into the middle of a DOS data block.

;----------------- Start of LDEVS.ASM ------------------
	PAGE	64,132
	NAME	LDEVS
	TITLE	LDEVS - LIST INSTALLED DEVICE DRIVERS.

CR	EQU	0DH			;ASCII CARRIAGE RETURN
LF	EQU	0AH			;ASCII LINE FEED

CODE	SEGMENT PARA PUBLIC 'CODE'
	ORG	100H
	ASSUME	CS:CODE,DS:CODE,ES:CODE,SS:CODE
LDEVS	PROC	FAR
	JMP	LDEVS1

DOSV	DB	?			;DOS VERSION NUMBER

NFCB	DB	0, 'NUL        ', 25 DUP (0)  ;FCB FOR /DEV/NULL

HEAD	DB	' ADDRESS  STRATEGY  INTERRUPT   TYPE/COUNT  NAME      ATTRIBUTES', CR, LF, LF, '$'
INFO	DB	'XXXX:XXXX   XXXX      XXXX     XXXXX  XX    XXXXXXXX ', 0
EOLN	DB	CR, LF, '$'

DIGITS	DB	'0123456789ABCDEF'      ;HEXADECIMAL DIGITS

LDEVS1: CLD				;CLEAR DIRECTION

	MOV	AH,30H			;GET DOS VERSION NUMBER
	INT	21H
	CMP	AL,2			;ENSURE DEVICE DRIVERS EXIST
	JAE	LDEVS2			;IF DOS 2.0 OR GREATER
	MOV	DX,OFFSET LDEVSA	;GET OFFSET OF ERROR MESSAGE
	MOV	AH,9H			;PRINT A STRING
	INT	21H
	INT	20H			;TERMINATE

LDEVS2: MOV	DOSV,AL 		;SAVE DOS VERSION NUMBER

	MOV	DX,OFFSET NFCB		;GET OFFSET OF NULL FCB
	MOV	AH,0FH			;ATTEMPT TO OPEN /DEV/NULL
	INT	21H
	TEST	AL,AL			;CHECK RETURN CODE
	JZ	LDEVS3			;IF ABLE TO OPEN NULL DEVICE
	MOV	DX,OFFSET LDEVSB	;GET OFFSET OF ERROR MESSAGE
	MOV	AH,9H			;PRINT A STRING
	INT	21H
	MOV	AX,4C01H		;TERMINATE WITH ERROR 1
	INT	21H

LDEVS3: CMP	DOSV,2			;CHECK CURRENT DOS VERSION
	JNE	LDEVS4			;IF NOT DOS 2.X
	LES	BX,DWORD PTR NFCB[25]
	JMP	LDEVS5

LDEVS4: LES	BX,DWORD PTR NFCB[26]	;DOS 3.0 OR GREATER

	ASSUME	ES:NOTHING
LDEVS5: MOV	DX,OFFSET HEAD		;GET OFFSET OF DEVICE HEADER
	MOV	AH,9			;PRINT A STRING
	INT	21H

LDEVS6: CALL	PDI			;PRINT DEVICE DRIVER INFORMATION
	LES	BX,ES:DWORD PTR [BX]	;LOAD ADDRESS OF NEXT IN CHAIN
	CMP	BX,0FFFFH		;CHECK OFFSET OF NEXT DRIVER
	JNE	LDEVS6			;IF STILL MORE TO BE DISPLAYED

	MOV	AX,4C00H		;TERMINATE NORMALLY
	INT	21H
LDEVS	ENDP
LDEVSA	DB	'Incorrect DOS version', CR, LF, '$'
LDEVSB	DB	'Cannot open NUL:', CR, LF, '$'

;;;	CWH - CONVERT WORD TO HEXADECIMAL ASCII.
;
;	ENTRY	(DX) = WORD TO BE CONVERTED.
;		(DS:BX) = OFFSET OF TRANSLATION TABLE.
;		(DS:DI) = OFFSET OF LOCATION FOR ASCII.

	ASSUME	CS:CODE,DS:CODE,ES:NOTHING,SS:CODE
CWH	PROC	NEAR
	MOV	AH,DH			;GET HIGH HALF OF WORD
	MOV	AL,AH			;MAKE A COPY
	AND	AL,0FH			;EXTRACT LOWER NIBBLE
	XLAT				;CONVERT TO ASCII
	XCHG	AL,AH			;GET UPPER NIBBLE
	SHR	AL,1			;EXTRACT UPPER NIBBLE
	SHR	AL,1
	SHR	AL,1
	SHR	AL,1
	XLAT				;CONVERT TO ASCII
	MOV	[DI + 0],AX		;STORE DIGITS IN WINDOW
	MOV	AH,DL			;GET LOW HALF OF WORD
	MOV	AL,AH			;MAKE A COPY
	AND	AL,0FH			;EXTRACT LOWER NIBBLE
	XLAT				;CONVERT TO ASCII
	XCHG	AL,AH			;GET UPPER NIBBLE
	SHR	AL,1			;EXTRACT UPPER NIBBLE
	SHR	AL,1
	SHR	AL,1
	SHR	AL,1
	XLAT				;CONVERT TO ASCII
	MOV	[DI + 2],AX		;STORE DIGITS IN WINDOW
	RET
CWH	ENDP

;;;	PDI - PRINT DEVICE DRIVER INFORMATION.
;
;	ENTRY	(ES:BX) = ADDRESS OF CURRENT DEVICE DRIVER.

	ASSUME	CS:CODE,DS:CODE,ES:NOTHING,SS:CODE
PDI	PROC	NEAR			;PRINT DEVICE DRIVER INFORMATION
	MOV	SI,BX			;GET ADDRESS OF DEVICE DRIVER
	MOV	BX,OFFSET DIGITS	;GET OFFSET OF TRANSLATION TABLE
	MOV	DI,OFFSET INFO[0]	;CONVERT DRIVER SEGMENT NUMBER
	MOV	DX,ES			;GET DRIVER SEGMENT
	CALL	CWH			;CONVERT WORD TO ASCII
	MOV	DI,OFFSET INFO[5]	;CONVERT DRIVER OFFSET
	MOV	DX,SI
	CALL	CWH

	MOV	DI,OFFSET INFO[12]	;CONVERT DRIVER STRATEGY OFFSET
	MOV	DX,ES:[SI + 6]
	CALL	CWH
	MOV	DI,OFFSET INFO[22]	;CONVERT DRIVER INTERRUPT OFFSET
	MOV	DX,ES:[SI + 8]
	CALL	CWH

	TEST	ES:BYTE PTR [SI + 5],80H ;CHECK ATTRIBUTE BYTE
	JZ	PDI1			;IF BLOCK DEVICE
	MOV	INFO[31],' '            ;STORE ' CHAR' IN LINE
	MOV	WORD PTR INFO[32],'hC'
	MOV	WORD PTR INFO[34],'ra'
	MOV	WORD PTR INFO[38],'  '  ;CLEAR UNIT COUNT
	MOV	AX,ES:[SI + 10] 	;COPY THE DEVICE NAME
	MOV	WORD PTR INFO[44],AX
	MOV	AX,ES:[SI + 12]
	MOV	WORD PTR INFO[46],AX
	MOV	AX,ES:[SI + 14]
	MOV	WORD PTR INFO[48],AX
	MOV	AX,ES:[SI + 16]
	MOV	WORD PTR INFO[50],AX
	JMP	SHORT PDI3

PDI1:	MOV	INFO[31],'B'            ;STORE 'BLOCK' IN LINE
	MOV	WORD PTR INFO[32],'ol'
	MOV	WORD PTR INFO[34],'kc'
	MOV	AL,ES:[SI + 10] 	;LOAD THE UNIT COUNT
	AAM				;CONVERT TO UNPACKED BCD
	ADD	AX,'00'                 ;CONVERT TO ASCII
	CMP	AH,'0'                  ;CHECK FOR LEADING ZERO
	JNE	PDI2			;IF NONZERO LEADING DIGIT
	MOV	AH,' '                  ;REPLACE WITH LEADING BLANK
PDI2:	XCHG	AH,AL			;SWAP BYTES FOR WORD STORE
	MOV	WORD PTR INFO[38],AX	;STORE UNIT COUNT IN LINE
	MOV	WORD PTR INFO[44],'  '  ;CLEAR DEVICE NAME
	MOV	WORD PTR INFO[46],'  '
	MOV	WORD PTR INFO[48],'  '
	MOV	WORD PTR INFO[50],'  '

PDI3:	MOV	BX,OFFSET INFO		;GET OFFSET OF LINE
	MOV	AH,2H			;PRINT A CHARACTER
PDI4:	MOV	DL,[BX] 		;LOAD NEXT CHARACTER FROM LINE
	TEST	DL,DL			;CHECK CHARACTER VALUE
	JZ	PDI5			;IF END OF STRING
	INT	21H
	INC	BX			;ADVANCE TO NEXT CHARACTER
	JMP	PDI4

PDI5:	MOV	BX,ES:[SI + 4]		;LOAD DRIVER ATTRIBUTE WORD
	MOV	AH,9H			;PRESET PRINT STRING FUNCTION
	TEST	BL,01H			;CHECK STDIN BIT
	JZ	PDI6			;IF DRIVER IS NOT STDIN
	MOV	DX,OFFSET PDIA		;GET OFFSET OF ATTRIBUTE
	INT	21H
PDI6:	TEST	BL,02H			;CHECK STDOUT BIT
	JZ	PDI7			;IF DRIVER IS NOT STDOUT
	MOV	DX,OFFSET PDIB		;GET OFFSET OF ATTRIBUTE
	INT	21H
PDI7:	TEST	BL,04H			;CHECK NUL BIT
	JZ	PDI8			;IF DRIVER IS NOT NUL
	MOV	DX,OFFSET PDIC		;GET OFFSET OF ATTRIBUTE
	INT	21H
PDI8:	TEST	BL,08H			;CHECK CLOCK BIT
	JZ	PDI9			;IF DRIVER IS NOT CLOCK
	MOV	DX,OFFSET PDID		;GET OFFSET OF ATTRIBUTE
	INT	21H
PDI9:	TEST	BL,10H			;CHECK SPECIAL BIT
	JZ	PDI10			;IF DRIVER IS NOT SPECIAL
	MOV	DX,OFFSET PDIE		;GET OFFSET OF ATTRIBUTE
	INT	21H
PDI10:	TEST	BH,40H			;CHECK IOCTL BIT
	JZ	PDI11			;IF IOCTL NOT SUPPORTED
	MOV	DX,OFFSET PDIF		;GET OFFSET OF ATTRIBUTE
	INT	21H
PDI11:	TEST	BH,80H			;CHECK DEVICE TYPE
	JNZ	PDI13			;IF CHARACTER DEVICE
	TEST	BH,20H			;CHECK NON-IBM BIT
	JZ	PDI12			;IF IBM FORMAT
	MOV	DX,OFFSET PDIG		;GET OFFSET OF NEGATIVE
	INT	21H
PDI12:	MOV	DX,OFFSET PDIH		;GET OFFSET OF FORMAT
	INT	21H

PDI13:	MOV	DX,OFFSET EOLN		;PRINT END OF LINE
	MOV	AH,9H			;PRINT A STRING
	INT	21H
	MOV	BX,SI			;RESTORE ADDRESS OF DRIVER
	RET
PDI	ENDP
PDIA	DB	' stdin$'               ;POSSIBLE DEVICE ATTRIBUTES
PDIB	DB	' stdout$'
PDIC	DB	' nul$'
PDID	DB	' clock$'
PDIE	DB	' special$'
PDIF	DB	' ioctl$'
PDIG	DB	' not$'
PDIH	DB	' IBM format$'
CODE	ENDS
	END	LDEVS
;----------------- End of LDEVS.ASM ------------------

;----------------- Start of MSCAN.ASM ------------------
	PAGE	64,132
	NAME	MSCAN
	TITLE	MSCAN - DISPLAY ALL DOS MEMORY BLOCKS.

CR	EQU	0DH			;ASCII CARRIAGE RETURN
LF	EQU	0AH			;ASCII LINE FEED

ARENA	SEGMENT AT 0H
FLAG	DB	?			;'M' FOR NORMAL, 'Z' FOR FINAL BLOCK
OWNER	DW	?			;SEGMENT OF OWNER'S PSP
PSIZE	DW	?			;BLOCK SIZE IN PARAGRAPHS
ARENA	ENDS

CODE	SEGMENT PARA PUBLIC 'CODE'
	ORG	2CH
ENVP	DW	?			;SEGMENT OF ENVIRONMENT

	ORG	100H
	ASSUME	CS:CODE,DS:CODE,ES:CODE,SS:CODE
MSCAN	PROC	FAR
	JMP	MSCAN1

HEAD	DB	'BLOCK  OWNER  LENGTH', CR, LF, LF, '$'
INFO	DB	' XXXX   XXXX   XXXX ', '$'
ENVM	DB	'  Mscan', 27H, 's environment$'
OWNM	DB	'  Mscan', 27H, 's memory block$'
EOLN	DB	CR, LF, '$'

DIGITS	DB	'0123456789ABCDEF'      ;HEXADECIMAL DIGITS

MSCAN1: CLD				;CLEAR DIRECTION

	MOV	AH,30H			;GET DOS VERSION NUMBER
	INT	21H
	CMP	AL,2			;ENSURE DEVICE DRIVERS EXIST
	JAE	MSCAN2			;IF DOS 2.0 OR GREATER
	MOV	DX,OFFSET MSCANA	;GET OFFSET OF ERROR MESSAGE
	MOV	AH,9H			;PRINT A STRING
	INT	21H
	INT	20H			;TERMINATE

MSCAN2: MOV	DX,OFFSET HEAD		;GET OFFSET OF DEVICE HEADER
	MOV	AH,9			;PRINT A STRING
	INT	21H

	MOV	AH,52H			;GET IN VARS (WHATEVER THAT MEANS)
	INT	21H
	ASSUME	ES:NOTHING
	MOV	ES,ES:[BX - 2]		;LOAD SEGMENT OF FIRST MEMORY BLOCK
	ASSUME	ES:ARENA

MSCAN3: CALL	PBI			;DISPLAY INFORMATION ABOUT BLOCK
	CMP	FLAG,'Z'                ;CHECK FOR LAST MEMORY BLOCK
	JE	MSCAN4			;IF ALL BLOCKS HAVE BEEN DISPLAYED
	MOV	AX,ES			;GET SEGMENT OF CURRENT BLOCK
	INC	AX			;ADVANCE TO CONTENTS OF BLOCK
	ADD	AX,PSIZE		;ADD LENGTH OF CURRENT BLOCK
	MOV	ES,AX			;SEGMENT NUMBER OF NEXT BLOCK
	JMP	MSCAN3

MSCAN4: MOV	AX,4C00H		;TERMINATE NORMALLY
	INT	21H
MSCAN	ENDP
MSCANA	DB	'Incorrect DOS version', CR, LF, '$'

;;;	CWH - CONVERT WORD TO HEXADECIMAL ASCII.
;
;	ENTRY	(DX) = WORD TO BE CONVERTED.
;		(DS:BX) = OFFSET OF TRANSLATION TABLE.
;		(DS:DI) = OFFSET OF LOCATION FOR ASCII.

	ASSUME	CS:CODE,DS:CODE,ES:ARENA,SS:CODE
CWH	PROC	NEAR
	MOV	AH,DH			;GET HIGH HALF OF WORD
	MOV	AL,AH			;MAKE A COPY
	AND	AL,0FH			;EXTRACT LOWER NIBBLE
	XLAT				;CONVERT TO ASCII
	XCHG	AL,AH			;GET UPPER NIBBLE
	SHR	AL,1			;EXTRACT UPPER NIBBLE
	SHR	AL,1
	SHR	AL,1
	SHR	AL,1
	XLAT				;CONVERT TO ASCII
	MOV	[DI + 0],AX		;STORE DIGITS IN WINDOW
	MOV	AH,DL			;GET LOW HALF OF WORD
	MOV	AL,AH			;MAKE A COPY
	AND	AL,0FH			;EXTRACT LOWER NIBBLE
	XLAT				;CONVERT TO ASCII
	XCHG	AL,AH			;GET UPPER NIBBLE
	SHR	AL,1			;EXTRACT UPPER NIBBLE
	SHR	AL,1
	SHR	AL,1
	SHR	AL,1
	XLAT				;CONVERT TO ASCII
	MOV	[DI + 2],AX		;STORE DIGITS IN WINDOW
	RET
CWH	ENDP

;;;	PBI - PRINT MEMORY BLOCK INFORMATION.
;
;	ENTRY	(ES) = SEGMENT OF CURRENT MEMORY BLOCK.

	ASSUME	CS:CODE,DS:CODE,ES:ARENA,SS:CODE
PBI	PROC	NEAR			;PRINT MEMORY BLOCK INFORMATION
	MOV	BX,OFFSET DIGITS	;GET OFFSET OF TRANSLATION TABLE
	MOV	DI,OFFSET INFO[1]	;CONVERT MEMORY SEGMENT NUMBER
	MOV	DX,ES			;GET SEGMENT OF MEMORY BLOCK
	CALL	CWH			;CONVERT WORD TO ASCII
	MOV	DI,OFFSET INFO[8]	;CONVERT DRIVER STRATEGY OFFSET
	MOV	DX,OWNER
	CALL	CWH
	MOV	DI,OFFSET INFO[15]	;CONVERT DRIVER INTERRUPT OFFSET
	MOV	DX,PSIZE
	CALL	CWH

	MOV	DX,OFFSET INFO		;GET OFFSET OF INFORMATION LINE
	MOV	AH,9H			;PRINT A STRING
	INT	21H

	MOV	AX,ES			;LOAD SEGMENT OF CURRENT BLOCK
	INC	AX			;PARAGRAPH OF BLOCK CONTENTS
	CMP	AX,ENVP 		;CHECK FOR MSCAN'S ENVIRONMENT
	JNE	PBI1
	MOV	DX,OFFSET ENVM		;GET OFFSET OF ENVIROMENT MESSAGE
	MOV	AH,9H			;PRINT A STRING
	INT	21H
	JMP	SHORT PBI2

PBI1:	MOV	DX,DS			;GET SEGMENT OF MSCAN'S PSP
	CMP	AX,DX			;CHECK FOR MSCAN'S CODE SEGMENT
	JNE	PBI2			;IF JUST A NORMAL MEMORY BLOCK
	MOV	DX,OFFSET OWNM		;GET OFFSET OF OWNER MESSAGE
	MOV	AH,9H			;PRINT A STRING
	INT	21H

PBI2:	MOV	DX,OFFSET EOLN		;GET OFFSET OF END OF LINE
	MOV	AH,9H			;PRINT A STRING
	INT	21H
	RET
PBI	ENDP
CODE	ENDS
	END	MSCAN
;----------------- End of MSCAN.ASM ------------------
-- 
Roy J. Mongiovi.	Office of Computing Services.		User Services.
Georgia Institute of Technology.	Atlanta GA  30332.	(404) 894-6163
 ...!{akgua, allegra, amd, hplabs, ihnp4, masscomp, ut-ngp}!gatech!gitpyr!roy

		  exp(sqrt(-1.0) * 4.0 * atan(1.0)) == -1