MBR Utility
This is a huge batch file project that will be developed in sections. The first section which will be used to demonstrate the core concepts is a small section of the overall project that can display the contents of the MBR to screen.
Check out the DEBUG Home page to see how to read the MBR with DEBUG. Record each actual line of commands that have to be made to accomplish this within DEBUG on paper. Once this is done record these into a text file:
a 300
mov ax,201
mov bx,100
mov cx,1
mov dx,80
int 13
int 3
(hit [Enter] and leave a blank line here!)
g=300
d 100 1ff
q
Normally one would put in the d 200 2ff command to see the bottom half but this would scroll the top half off of the screen so the automated script must end here and then run again to display the bottom half of the MBR. Save this file as MBRTOP.SCR. Copy it to another file named MBRBOT.SCR. In MBRBOT.SCR change the line that reads d 100 1ff to read d 200 2ff instead and save it.
Now a batch file can run DEBUG and redirect all of the input to DEBUG from this text file having the effect that DEBUG will create the small assembly language program and then run it and then display the top of the MBR to screen and then quit back to the prompt. Here is the batch file:
@echo off
debug < mbrtop.scr
Save this as SHOWMBR.BAT in the same directory where the script file MBRTOP.SCR is located. Run the batch file by typing its name at the DOS prompt and the top half of the hard drive's MBR will appear on screen.
The output is kind of polluted by the assembly instructions and the debug registers display and so on. It would be nice if the output could be limited to only the actual lines of hexadecimal and ASCII codes that represent the top half of the MBR. It would be really nice if the batch file could determine if the INT 13 read request of the HDD succeeded and then inform the user if it failed or display the top half of the MBR if it succeeded.
First, the output has to be searched for the test strings that indicate that the read request failed. This is indicated by the last flag in the registers display after the little program executes. If the last flag is "NC" then all went well. If the last flag is "CY" then all did not go well: an error occured. The flag in front of it is the parity flag which can be set to either PE (parity even) or PO (parity odd). This one is unpredictable in the event of an error condition so we will have to test for both strings: "PE CY" and "PO CY" both of which would be a fairly rare random occurence within the output strings on the ASCII side of the screen within an MBR. To perform this check and then filter the rest of the output it is best to redirect all of the output from the screen into an output file. Call it MBRTOP.OUT. Open SHOWMBR.BAT and make this change:
@echo off
debug < mbrtop.scr > mbrtop.out
Now that the output is captured into a file it can be searched to see if the error occured and it can be filtered and then be displayed on screen. The FIND command will attempt to match a string within a text file. The /c switch tells it to display only the number of lines within the file where a match occured. So the output of "find /c "PE CY" mbrtop.out" should be simply "0" on screen and that would mean that the error condition did not occur. If the output to the screen were a "1" then the error did occur and the MBR was not read from the hard drive.
Instead of displaying the number on screen, it can be "piped" from the output of the FIND command into the input of the CHOICE command. CHOICE accepts a single keystroke input from the user and then sets an "ERRORLEVEL" that DOS will hold onto until another command is executed that sets its output errorlevel to something other than zero. This value can be acted upon by the batch file processing command "IF ERRORLEVEL" So if we executed the CHOICE command within the batch file like this: choice /c:01 /n then a blinking cursor would appear waiting for input. If we pressed the "0" key, that would appear on screen and the next line of the batch file would execute. The errorlevel that the IF ERRORLEVEL command would find would be 1. If the second possible key of "1" were pressed instead, then the errorlevel would be 2. Armed with this information lets set up the output of the find command which is going to be either 0 or 1 to go to this choice command and redirect the output (to the screen) of it to nul so that the number (whichever comes out of find and into choice) will not be displayed:
@echo off
debug < mbrtop.scr > mbrtop.out
find /c "PE CY" mbrtop.out | choice /c:01 /n > nul
Now we can set up the "IF ERRORLEVEL" commands to detect the level set by CHOICE and jump to a section of code in the batch file to deal with each possibility:
@echo off
debug < mbrtop.scr > mbrtop.out
find /c "PE CY" mbrtop.out | choice /c:01 /n > nul
if errorlevel 2 goto readfail
if errorlevel 1 goto nextchk
...
We would fall to the location of the ellipsis (...) if the error condition is not met so we do not have to put the IF ERRORLEVEL 1 line at all and we can put the next check code right there:
@echo off
debug < mbrtop.scr > mbrtop.out
find /c "PE CY" mbrtop.out | choice /c:01 /n > nul
if errorlevel 2 goto readfail
find /c "PO CY" mbrtop.out | choice /c:01 /n > nul
if errorlevel 2 goto readfail
...
If the batch file reaches this ellipsis then the read was successful and so the output can be displayed on screen. But again, we would like to filter it so that only the actual output hexadecimal and ASCII lines get displayed and the other lines creating and running the little program are suppressed. Notice that in the output lines each begins with some unpredictable four digit hex value then a colon and the offsets starting with 0100 then 0110 then 0120... So each output line must contain the string ":01" FIND can do that:
@echo off
debug < mbrtop.scr > mbrtop.out
find /c "PE CY" mbrtop.out | choice /c:01 /n > nul
if errorlevel 2 goto readfail
find /c "PO CY" mbrtop.out | choice /c:01 /n > nul
if errorlevel 2 goto readfail
find ":01" mbrtop.out
...
That will display exactly what we want to see and none of the other garbage. Now we still have to embed the readfail section and then end the little program we are writing here:
@echo off
debug < mbrtop.scr > mbrtop.out
find /c "PE CY" mbrtop.out | choice /c:01 /n > nul
if errorlevel 2 goto readfail
find /c "PO CY" mbrtop.out | choice /c:01 /n > nul
if errorlevel 2 goto readfail
find ":01" mbrtop.out
goto end
:readfail
echo Error in INT 13h attempt to read the MBR
:end
del mbrtop.out
That's it. You can see that every "goto labelname" must have a corresponding ":labelname" where it can literally go to. While we are at it we will have the batch file clean up the temporary file it creates right at the end. Save it and run it and see how things look. Notice that nasty output at the beginning coming from FIND that reads: "--------mbrtop.out" We can't have that. FIND accepts piped data and of course the pipe does not have a file name and so it won't pollute the output like this. Here's the change:
@echo off
debug < mbrtop.scr > mbrtop.out
find /c "PE CY" mbrtop.out | choice /c:01 /n > nul
if errorlevel 2 goto readfail
find /c "PO CY" mbrtop.out | choice /c:01 /n > nul
if errorlevel 2 goto readfail
type mbrtop.out | find ":01"
goto end
:readfail
echo Error in INT 13h attempt to read the MBR
:end
del mbrtop.out
Try this. The output should be very clean. Now add the second half of the MBR. This means that the "end" of the program must be changed to the middle since it must now go and run the second debug script, test it, then output it to the screen. A pause will also be needed while the user inspects the top half of the MBR and then when he decides to go on he can press a key and then have the bottom half displayed on screen and then return to the prompt. First change all mentions of end to middle and put in the pause command:
@echo off
debug < mbrtop.scr > mbrtop.out
find /c "PE CY" mbrtop.out | choice /c:01 /n > nul
if errorlevel 2 goto readfail
find /c "PO CY" mbrtop.out | choice /c:01 /n > nul
if errorlevel 2 goto readfail
type mbrtop.out | find ":01"
goto middle
:readfail
echo Error in INT 13h attempt to read the MBR
:middle
pause
...
del mbrtop.out
We will save the clean up to the end. Now put in the line to run the debug script to generate the bottom half of the MBR:
@echo off
debug < mbrtop.scr > mbrtop.out
find /c "PE CY" mbrtop.out | choice /c:01 /n > nul
if errorlevel 2 goto readfail
find /c "PO CY" mbrtop.out | choice /c:01 /n > nul
if errorlevel 2 goto readfail
type mbrtop.out | find ":01"
goto middle
:readfail
echo Error in INT 13h attempt to read the MBR
:middle
pause
debug < mbrbot.scr > mbrbot.out
...
del mbrtop.out
OK now here's where the famous program bugs come from...programmer's laziness. Should we test the INT13h read success of the MBR again? After all if it read it the first time, then it should be able to read it the second time right? We can leave the success checks of the second read attempt out. But that error message up above used to run right into the end of the program, but it does not do this anymore because we have moved the end of the program further down. It should be moved next to the end of the program again, AND we will finish up the display code too:
@echo off
debug < mbrtop.scr > mbrtop.out
find /c "PE CY" mbrtop.out | choice /c:01 /n > nul
if errorlevel 2 goto readfail
find /c "PO CY" mbrtop.out | choice /c:01 /n > nul
if errorlevel 2 goto readfail
type mbrtop.out | find ":01"
goto middle
:middle
pause
debug < mbrbot.scr > mbrbot.out
type mbrbot.out | find ":02" <=you need a 2 here!
goto end
:readfail <=moved to here
echo Error in INT 13h attempt to read the MBR <=moved to here
:end
del mbrtop.out
del mbrbot.out
Notice that by moving the readfail section there is a goto middle followed by the line :middle. We don't need to jump there, that is where the batch file will fall to anyway. Delete both lines:
@echo off
debug < mbrtop.scr > mbrtop.out
find /c "PE CY" mbrtop.out | choice /c:01 /n > nul
if errorlevel 2 goto readfail
find /c "PO CY" mbrtop.out | choice /c:01 /n > nul
if errorlevel 2 goto readfail
type mbrtop.out | find ":01"
pause <=deleted unecessary jump here
debug < mbrbot.scr > mbrbot.out
type mbrbot.out | find ":02"
goto end
:readfail
echo Error in INT 13h attempt to read the MBR
:end
del mbrtop.out
del mbrbot.out
At this point the batch file is fully functional. All that is left is some nice descriptive output:
@echo off
debug < mbrtop.scr > mbrtop.out
find /c "PE CY" mbrtop.out | choice /c:01 /n > nul
if errorlevel 2 goto readfail
find /c "PO CY" mbrtop.out | choice /c:01 /n > nul
if errorlevel 2 goto readfail
echo Top Half of the MBR:
type mbrtop.out | find ":01"
pause
debug < mbrbot.scr > mbrbot.out
echo Bottom Half of the MBR:
type mbrbot.out | find ":02"
goto end
:readfail
echo Error in INT 13h attempt to read the MBR
:end
ctty nul
del mbrtop.out
del mbrbot.out
ctty con
The CTTY NUL command switches the system console to the NUL device which means that the PC will no longer accept any input from the keyboard (although {Ctrl]+[Alt]+[Del] will still work) and no output will reach the screen. This is an old DOS batch file master's trick for arresting all possible output to the screen. You see although it is difficult for you to test the batch file on a system that will fail to read the MBR, I do have such a problem child HDD. And when that happens the program jumps to display the error message and never creates the *.OUT files. Then the DEL commands complain with "File not found" errors. Very ugly but they don't hurt anything so the CTTY NUL and CTTY CON (which returns control to the keyboard which I think you would appreciate) effectively sweep these messy error messages right under the rug.
If all went well, you have a batch file with two scripts that requires DEBUG.EXE, FIND.EXE and CHOICE.COM. Copy these to a bootable diskette and you have an a nice MBR viewer. It creates temporary files so it cannot be write protected. An issue that will be dealt with in the Technician's Tips and Tricks page.