Looking at what is in conventional memory and the master environment
Download the source code from here (4k), and unzip it (See my links page for PKUNZIP) in a directory of your choice, then view MCB.ASM for details.
(To download, I suggest right clicking the link and saving to disk)
- To use MCB just type MCB at the DOS prompt.
MCB will show you what each Memory Control Block has and the source explains in more detail how it is done.
Before we get on to the code, there is an undocumented feature in DOS 2.x and up that allows us to send a command to the COMMAND.COM interpreter as if we typed this on the command line.
This feature is INT 2Eh. We point ds:si to a small table holding our command line and call INT 2Eh. Please note that this interrupt destroys all registers except cs:ip.
The format of the table is:
byte size of command line not counting CR
bytes command line
byte CR (ascii 13)
This allows the master environment to be modified by issuing a "SET" command. However, changes in the master environment will not become effective until all programs descended from the primary COMMAND.COM terminate.
Also, your program must ensure that sufficient memory to load the transient portion can be allocated by DOS if necessary.
Here is an example:
;
; This routine simply sets an ENVIRONMENT VARIABLE to
; the current date in YYYMMDD form using COMMAND.COM and SET.
;
; assembled with NBASM
;
.model tiny
.code
.186
.start ; free unused part of memory block
mov ah,2Ah ; DOS get date function
int 21h ; cx=year, dh=month, dl=day
push cs ; make sure es=cs
pop es ;
cld ; make sure going forward
mov di,offset our_strng ;
mov ax,cx ; year
call prtdec ;
xor ah,ah ;
mov al,dh ; month
call prtdec ;
mov al,dl ; day
call prtdec ;
mov si,offset commnd_str ; call command.com to run it
int 2Eh ;
.exit
; Prints a number to ES:DI in decimal asciiz representation
; if the number is a single digit, left pads with '0'
; on entry:
; ax = number
; es:di -> position to write to
PrtDec proc near uses ax cx dx
cmp ax,09 ; if 0 - 9, left pad it
ja short prtdecnp ;
mov byte es:[di],'0' ; left pad a '0'
inc di ;
prtdecnp: mov cx,0FFFFh ; Ending flag
push cx
mov cx,10
PD1: xor dx,dx
div cx ; Divide by 10
add dl,30h ; Convert to ASCII
push dx ; Store remainder
or ax,ax ; Are we done?
jnz short PD1 ; No, so continue
PD2: pop ax ; Character is now in AL
cmp ax,0FFFFh ; Is it the ending flag?
je short PD3 ; Yes, so continue
stosb ;
jmp short PD2 ; Keep doing it
PD3: ret
PrtDec endp
commnd_str db 21
db 'set ourdate='
our_strng db 'yyyymmdd'
db 13
.end
- To use MSTRENV just type MSTRENV at the DOS prompt.
MSTRENV shows what is in the master environment and the source explains how to get it. Once you have
the address and size of the master environment, then you can modify it with out error as long as you don't exceed the size.
comment %
Demo program assembled with the NBASM
to locate and print the master environment.
22 Oct 2000 %
.186 ; allow 186 intructions
.model tiny ; create COM file
.code ; start of code segment
mov ah,52h ; (undocumented) get 1st MCB
int 21h ; .
mov ax,es:[bx-2] ; .
mov es,ax ; .
add ax,es:[0003h] ; add length of 1st block
inc ax ; (paragraphs)
inc ax ; +2 to get command psp
mov es,ax ; and put in es
mov ax,es:[002Ch] ; 2Ch = offset in PSP to environment
or ax,ax ; if = 0,
jnz short validoff ; then must be DOS 3.x or below
mov ax,es ; dec es
dec ax ;
mov es,ax ;
add ax,es:[0003h] ; get address
inc ax ;
validoff: dec ax ; mov one paragraph back
mov es,ax ; and put in es
mov cx,es:[0003h] ; cx = size of environment
shl cx,04 ; (in bytes)
add ax,04 ; for Windows9x DOS session
;add ax,02 ; for True DOS 7.x
;inc ax ; for DOS 4.01 and less
mov ds,ax ; make ds:si point to first
xor si,si ; envirnment var
PrtLoop: mov al,0Dh ; print CRLF
int 29h ;
mov al,0Ah ;
int 29h ;
mov al,ds:[si] ; if null then we're done
or al,al ;
jz short Done ;
PrtLoop1: lodsb ; get the char to print
or al,al ; if null then end of string
jz short PrtLoop ;
int 29h ; else print it
loop PrtLoop1 ; loop to next one
; if for some reason we don't find a double null string
; we will only go cx times with the loop instruction above
Done: ret
.end
SETPATH is also included to show how to added to the PATH= string variable.
comment %
Demo program assembled with the NBASM
22 Oct 2000
It is quite simple really. All we have to do is the following items:
1. Find the master environment segment.
2. Syphon out the PATH=... variable string
3. Put the PATH= at the end of the rest of the variable strings
4. Add the command line paramter to the end of the PATH
(adding a ';' if needed)
5. Resize the environment block to allow our new path to fit
6. rep movsb it
7. done
%
.186 ; allow 186 intructions
.model tiny ; create COM file
.code ; start of code segment
.start ; tell NBASM to free unused mem
; so we can resize the env block
mov ah,52h ; (undocumented) get 1st MCB
int 21h ; .
mov ax,es:[bx-2] ; .
mov es,ax ; .
add ax,es:[0003h] ; add length of 1st block
inc ax ; (paragraphs)
inc ax ; +2 to get command psp
mov es,ax ; and put in es
mov ax,es:[002Ch] ; 2Ch = offset in PSP to environment
or ax,ax ; if = 0,
jnz short validoff ; then must be DOS 3.x or below
mov ax,es ; dec es
dec ax ;
mov es,ax ;
add ax,es:[0003h] ; get address
inc ax ;
validoff: dec ax ; mov one paragraph back
mov es,ax ; and put in es
mov cx,es:[0003h] ; cx = size of environment
shl cx,04 ; (in bytes)
add ax,04 ; for Windows9x DOS session
;add ax,02 ; for True DOS 7.x
;inc ax ; for DOS 4.01 and less
mov cs:EnvStrSeg,ax ; save envirnment seg
mov ds,ax ; make ds:si point to first
xor si,si ; envirnment var
push cs ; make sure es = cs
pop es
mov di,offset cs:Buffer1
PrtLoop: mov al,ds:[si] ; if null then we're done
or al,al ;
jz short DoneG ;
PrtLoop1: push cx ; see if it is PATH=
push si ;
push di ;
mov di,offset cs:PathStr ;
mov cx,5 ;
repe ;
cmpsb ;
pop di ;
pop si ;
pop cx ;
jne short NotPath ;
push di ; if it is PATH= then mov to buffer2
mov di,offset cs:Buffer2 ; (this will put all of the env in
IsPath: lodsb ; buffer1 and the PATH in buffer2)
stosb ;
dec cx ; one less byte to get
or al,al ;
jnz short IsPath ;
pop di ;
jmp short PrtLoop ;
NotPath: lodsb ; get the char
stosb ; put in buffer1
or al,al ; if null then end of string
jz short PrtLoop ;
loop PrtLoop1 ; loop to next one
; if for some reason we don't find a double null string
; we will only go cx times with the loop instruction above
DoneG: push cs ; point ds to cs (PSP)
pop ds
mov si,81h ; point si to command line
SkpSpcs: lodsb ; get parameters
cmp al,20h ; skip leading spaces
je short SkpSpcs ;
cmp al,'+' ; if '+' then go to added to path
je short addtopath ;
cmp al,'v' ; if 'v' then just print current path
je short printpath ;
mov si,offset ErrCmdL ; else print error with command com
call prtstring ;
jmp short ExitIt ;
printpath: mov si,offset Buffer2 ; print PATH=........
call prtstring ;
jmp short ExitIt ;
addtopath: push cs ; add to path
push cs ; make es and ds = cs
pop es ;
pop ds ;
mov di,offset Buffer1 ; find end of env
xor ax,ax ;
mov cx,2000 ; up to 2000 bytes
repne ;
scasw ;
dec di ; go back
dec di ;
push si ; save pos of addstr
mov si,offset Buffer2 ; put PATH=..... at end of env
AdditL: lodsb ;
stosb ;
or al,al ;
jnz short AdditL ;
dec di ; go back
cmp byte [di-1],';' ; if PATH doesnt end with ';' then
je short IsSc ; add one
mov byte [di-1],';' ;
IsSc: pop si ; restore pos of addstr
IsScL: lodsb ; now add parameter string to PATH
cmp al,0Dh ;
je short AddItD ;
cmp al,20h ;
je short AddItD ;
stosb ;
jmp short IsScL ;
AddItD: xor ax,ax ; put the double null
stosw ;
mov bx,di ; length of full environment needed
sub bx,offset Buffer1 ;
mov ESize,bx ; save size
shr bx,4 ; resize the environment block
inc bx ; to allow our addition
mov es,cs:EnvStrSeg ;
mov ah,4Ah ; resize mem block
int 21h ;
jnc short ResizeOK ; if error then say so and
mov si,offset RszMemErr ; leave env untouched
call prtstring ;
jmp short ExitIt ;
ResizeOK: push cs ; make sure ds=cs
pop ds ;
mov cx,ESize ; cx = length of new env
mov ax,EnvStrSeg ;
mov es,ax ; point es to mstr env block
xor di,di ; start at first
mov si,offset Buffer1 ; our new env
rep ; *change it*
movsb ;
ExitIt: mov ah,4Ch ; exit to DOS
int 21h ;
prtstring proc near uses ax dx
mov ah,02 ; DOS print char service
Ps1: lodsb ; Get character & point to next one
or al,al ; End of string?
jz short ps2 ; Yes, so exit
mov dl,al ;
int 21h ; Output a character
jmp short Ps1 ; Keep doing it
Ps2: ret
endp
EnvStrSeg dw 00h
ESize dw 00h
ErrCmdL db 'Error with command line. Environment unchanged.',13,10,0
RszMemErr db 'Error resizing Environment space.',13,10,0
PathStr db 'PATH=',0
Buffer1 dup 2000,? ; holds original environment
Buffer2 dup 2000,? ; holds original path
.end