Drive Activities
Displaying the Hard drive's DOS Boot Record on screen
Now let’s ask debug to read the boot sector of the hard drive into RAM and then display it. Because a sector is 512 bytes in size, when it is displayed on screen in the DEBUG format, it would scroll up out of view so it must be displayed in halves. First issue the "L" command to pick up the sector and then issue the "D" commands as shown here to display first the top half then the bottom half of the sector (256 bytes at a time):
-L 100 2 0 1
-d 100 1ff
1068:0100 EB 58 90 4D 53 57 49 4E-34 2E 31 00 02 08 20 00 .X.MSWIN4.1... .
1068:0110 02 00 00 00 00 F8 00 00-3F 00 80 00 3F 00 00 00 ........?...?...
1068:0120 41 1D 20 00 06 08 00 00-00 00 00 00 02 00 00 00 A. .............
1068:0130 01 00 06 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1068:0140 80 00 29 F8 09 5E 33 4E-4F 20 4E 41 4D 45 20 20 ..)..^3NO NAME
1068:0150 20 20 46 41 54 33 32 20-20 20 FA 33 C9 8E D1 BC FAT32 .3....
1068:0160 F8 7B 8E C1 BD 78 00 C5-76 00 1E 56 16 55 BF 22 .{...x..v..V.U."
1068:0170 05 89 7E 00 89 4E 02 B1-0B FC F3 A4 8E D9 BD 00 ..~..N..........
1068:0180 7C C6 45 FE 0F 8B 46 18-88 45 F9 38 4E 40 7D 25 |.E...F..E.8N@}%
1068:0190 8B C1 99 BB 00 07 E8 97-00 72 1A 83 EB 3A 66 A1 .........r...:f.
1068:01A0 1C 7C 66 3B 07 8A 57 FC-75 06 80 CA 02 88 56 02 .|f;..W.u.....V.
1068:01B0 80 C3 10 73 ED BF 02 00-83 7E 16 00 75 45 8B 46 ...s.....~..uE.F
1068:01C0 1C 8B 56 1E B9 03 00 49-40 75 01 42 BB 00 7E E8 ..V....I@u.B..~.
1068:01D0 5F 00 73 26 B0 F8 4F 74-1D 8B 46 32 33 D2 B9 03 _.s&..Ot..F23...
1068:01E0 00 3B C8 77 1E 8B 76 0E-3B CE 73 17 2B F1 03 46 .;.w..v.;.s.+..F
1068:01F0 1C 13 56 1E EB D1 73 0B-EB 27 83 7E 2A 00 77 03 ..V...s..'.~*.w.
-d 200 2ff
1068:0200 E9 FD 02 BE 7E 7D AC 98-03 F0 AC 84 C0 74 17 3C ....~}.......t.<
1068:0210 FF 74 09 B4 0E BB 07 00-CD 10 EB EE BE 81 7D EB .t............}.
1068:0220 E5 BE 7F 7D EB E0 98 CD-16 5E 1F 66 8F 04 CD 19 ...}.....^.f....
1068:0230 41 56 66 6A 00 52 50 06-53 6A 01 6A 10 8B F4 60 AVfj.RP.Sj.j...`
1068:0240 80 7E 02 0E 75 04 B4 42-EB 1D 91 92 33 D2 F7 76 .~..u..B....3..v
1068:0250 18 91 F7 76 18 42 87 CA-F7 76 1A 8A F2 8A E8 C0 ...v.B...v......
1068:0260 CC 02 0A CC B8 01 02 8A-56 40 CD 13 61 8D 64 10 ........V@..a.d.
1068:0270 5E 72 0A 40 75 01 42 03-5E 0B 49 75 B4 C3 03 18 ^r.@u.B.^.Iu....
1068:0280 01 27 0D 0A 49 6E 76 61-6C 69 64 20 73 79 73 74 .'..Invalid syst
1068:0290 65 6D 20 64 69 73 6B FF-0D 0A 44 69 73 6B 20 49 em disk...Disk I
1068:02A0 2F 4F 20 65 72 72 6F 72-FF 0D 0A 52 65 70 6C 61 /O error...Repla
1068:02B0 63 65 20 74 68 65 20 64-69 73 6B 2C 20 61 6E 64 ce the disk, and
1068:02C0 20 74 68 65 6E 20 70 72-65 73 73 20 61 6E 79 20 then press any
1068:02D0 6B 65 79 0D 0A 00 00 00-49 4F 20 20 20 20 20 20 key.....IO
1068:02E0 53 59 53 4D 53 44 4F 53-20 20 20 53 59 53 7E 01 SYSMSDOS SYS~.
1068:02F0 00 57 49 4E 42 4F 4F 54-20 53 59 53 00 00 55 AA .WINBOOT SYS..U.
-_
The first command is letter L for load from disk, space, 100 for place it at offset 100 hex, space, 2 (for the C: drive while 0 = A: and 1 = B:), 0 for the first sector of the drive, and 1 for load one sector into RAM. Then the next command is letter D for dump (to screen), space, 100 for the RAM location we want to display, space, 1ff for the final RAM address to be displayed. Then do it again for RAM addresses 200 to 2ff.
The first three bytes starting with EBh are a short jump instruction to bypass the information that follows and get to the OS loader code that is buried further back. The next eight bytes contain the ASCII text characters for the file system version. As you can see in my example by looking to the right that these eight characters spell out “MSWIN4.1” for MicroSoft Windows version 4.1 which is Windows 98. This is the file system version that was introduced for that OS which means that it natively supports FAT32 as well as FAT16. There are some encoded binary values that follow but further down you will find the Volume Label. If someone formats and hits [ENTER] (for none) then the format command will give the disk the volume label of “NO NAME”. This can be seen on the fifth line of output on the right side. After the volume label you can see the file system in use which is FAT32. After this there is more encoded information and the OS loader code itself. At the bottom we see “Invalid system disk or disk error, replace and press any key” This is the error that the OS loader code above it will display on screen if it cannot find the file IO.SYS. The very last two bytes of the sector are 55h AAh. This is the boot signature that the BIOS boot strap loader code looks for when it loads this sector into RAM.
This sector came from the hard drive and is called the DOS Boot Record. The information at the front of it is collectively referred to as the Media Descriptor. The information just above the error message (that starts with “Invalid...”) is the OS boot strap loader code. And the last two bytes are the boot signature. These are the three significant components of the DBR. The DBR has no partition tables these are stored in the Master Boot Record of the HDD which is the first physical sector located at Cylinder = 0, Head = 0, sector = 1. This sector cannot be addressed by the L command of DEBUG.
Displaying the Hard drive's Master Boot Record on screen
Playing with the MBR is a good way to end up needing a boot diskette with CD-ROM support and the OEM Windows CD-ROM in order to perform a Full installation of Windows 98 again. Bear it in mind...
DEBUG does not have a native command that will read the MBR for you. So you must write a small assembly language program to read it into RAM, then you can view it:
-a 300
1AD9:0300 mov ax, 201
1AD9:0303 mov bx, 100
1AD9:0306 mov cx, 1
1AD9:0309 mov dx, 80
1AD9:030C int 13
1AD9:030E int 3
1AD9:030F (hit [Enter] to quit the assembler)
-g=300
AX=0201 BX=0100 CX=0001 DX=0080 SP=FFEE BP=0000 SI=0000 DI=0000
DS=136D ES=136D SS=136D CS=136D IP=030E NV UP EI PL NZ NA PO NC<=must be NC!
136D:030E CC INT 3
-_
At this point the MBR is in RAM. Display the top half with the "d 100 1ff" You should recognize at least the "Missing operating system" error message which gets issued when the MBR cannot locate the DBR. Display the bottom half of the MBR with the command "d 200 2ff" You should recognize the boot signature in the last two bytes at the bottom "55 AA"
Clearing the Hard drive's Master Boot Record
Obviously this will destroy all partitions and leave all data on the drive impossible to recover without a full data recovery operation. This is not as complete as ZAP but it is usually effective at removing Linux partitions which tend to confuse FDISK and prevent it from repartitioning the HDD.
Here is the debug operation to completely zero the MBR:
-f 100 2ff 0
-a 300
1AD9:0300 mov ax, 301
1AD9:0303 mov bx, 100
1AD9:0306 mov cx, 1
1AD9:0309 mov dx, 80
1AD9:030C int 13
1AD9:030E int 3
1AD9:030F (hit [Enter] to quit the assembler)
-g=300
AX=0201 BX=0100 CX=0001 DX=0080 SP=FFEE BP=0000 SI=0000 DI=0000
DS=136D ES=136D SS=136D CS=136D IP=030E NV UP EI PL NZ NA PO NC<=must be NC!
136D:030E CC INT 3
-_
At this point the MBR has been overwritten with zeros and you can immediately run FDISK and set up new partitions (no reboot required until after FDISK).
Here is a variant that will overwrite the first cylinder's sectors with zeros. This requires a larger program that will loop through all of the sectors of each head, and loop through all of the heads and all of the cylinders 0 to 255.
Since the geometry values of a drive can vary, the values that must match the drive you are working with will be pointed out within the code. Also, for those interested, it will be demonstrated how to make the program a standalone executable, not just a DEBUG script.
First here is the raw script itself:
f 130 330 0
a 100
mov ch, 0
mov dh, 0
mov cl, 1
mov dl, 80
mov ax, 301
mov bx, 130
int 13
inc cl
cmp cl, 40
jb 108
inc dh
cmp dh, 40
jb 104
inc ch
cmp ch, 0
jne 102
int 3
g=100
q
In the code above find the line that reads "cmp cl, 40" this controls the number of sectors/track of the zeroing operation. The compare to 40h (64 decimal) and the "jb 108" (jump if less than 64 to offset 108) instructions cap the highest sector number at 63. Just after this the instruction "cmp dh, 40" and the jump if less than that follows cap the highest head number at 63 as well. If the geometry of the drive is for example Cyls=785, Heads=128, Sect=63, then that line should be changed to read "cmp dh, 80" If the geometry of the drive is: Cyls=1023, Heads=255, Sect=63 then the line should read: "cmp dh, FF"
The "cmp ch,0" is what caps the zeroing at cylinder 255. To go higher requires some bit shifting instructions that get rather complex, but at the end of this exercise an easier method of zeroing all cylinders will be explored.
Save the above script to a file named "z255cyls.scr" so the action of the script will not be forgotten (Zero 255 Cylinders) To get the script to actually wipe the first 255 cylinders with zeros execute "debug < z255cyls.scr" Or this line can be embedded in a batch file with a " > nul" to hide the output to the screen of debug.
To make this into an executable (remember that the geometry will be fixed so it will only work with the drive it is made for) change the script to create an executable rather than to execute the script:
f 130 330 0
a 100
mov ch, 0
mov dh, 0
mov cl, 1
mov dl, 80
mov ax, 301
mov bx, 130
int 13
inc cl
cmp cl, 40
jb 108
inc dh
cmp dh, 40
jb 104
mov ah, 2
mov dl, 2e
int 21
inc ch
cmp ch, 0
jne 102
mov ax, 4c00
int 21
r bx
0
r cx
230
n z255cyls.com
w
q
The last line "int 3" has been replaced by "mov ax,4c00" then "int 21h" This places the standard function number "4C" into the top byte of the AX register and then calls the DOS interface at INT 21h. Function 4C means "I'm done and I am returning control to COMMAND.COM" so it will end the program and COMMAND.COM will go back to the DOS prompt. The R BX command sets the value of that register to 0 and the R CX command sets the value of that register to 230h (which is the size of the program in bytes). The N command names the file that will be created and the W command without parameters tells debug: "Use the number that spans the BX and CX registers as the total number of bytes to save, start the save from offset 100h in RAM, and name the save what was named with the preceding N command." And of course Q will quit back to the prompt. Save the above script as "makez255.scr" and execute this script in the same way as all of them: "debug < makez255.scr" When done check the current directory and a file named z255cyls.com. By the way, the other three lines inserted between the jb 104 and the "inc ch" (for counting the cylinder number) Set the function number in the top byte of the AX register (a.k.a. the AH register) to 2, place the ASCII code 2Eh = "." in the bottom byte of the DX (a.k.a. the DL) and call DOS (INT 21h) which will display the period on screen at the current location of the cursor. This will be done at the end of each cylinder. This means a series of dots will be displayed that will span a little more than three rows on screen and then the program will end, sort of a primitive progress indicator.
Forging on. This can be tweaked into a general purpose wipe program that will wipe the entire drive (using CHS so it has a maximum theoretical capacity of 8.4GB). The main problems are getting the geometry into the program without having to write a HUGE program to do it. A batch file can solve the problem (see Batch Files - Master Class for more details on batch files).
@echo off
rem This batch file will accept the drive's geometry and create the
rem zeroing debug script then execute it.
rem %1 = Cyls/256 integer no remainder so 100Cyls /256 = 0, 300Cyls/256 = 1
rem %2 = Cyls % 256 (modulus = remainder) so 100Cyls%256 = 100, 300Cyls%256 = 44
rem %3 = # of Heads
rem %4 = # of Sectors/Track
if %1z==z goto help
if %2z==z goto help
if %3z==z goto help
if %4z==z goto help
echo f 1b1 3b1 0 > wipehdd.scr
echo a 100 >> wipehdd.scr
echo mov ch, 0 >> wipehdd.scr
echo mov dh, 0 >> wipehdd.scr
echo mov cl, 1 >> wipehdd.scr
echo mov dl, 80 >> wipehdd.scr
echo mov ax, 301 >> wipehdd.scr
echo mov bx, 1b1 >> wipehdd.scr
echo int 13 >> wipehdd.scr
echo inc cl >> wipehdd.scr
echo cmp cl, %4 >> wipehdd.scr
echo jb 108 >> wipehdd.scr
echo inc dh >> wipehdd.scr
echo cmp dh, %3 >> wipehdd.scr
echo jb 104 >> wipehdd.scr
echo mov ah, 2 >> wipehdd.scr
echo mov dl, 2e >> wipehdd.scr
echo int 21 >> wipehdd.scr
echo inc ch >> wipehdd.scr
if %1==0 goto fixcyls0
echo cmp ch, 0 >> wipehdd.scr
goto cont0
:fixcyls0
echo cmp ch, %2 >> wipehdd.scr
:cont0
echo jnz 102 >> wipehdd.scr
if not %1==0 goto prog0
echo mov ax, 4c00 >> wipehdd.scr
echo int 21 >> wipehdd.scr
echo. >> wipehdd.scr
goto compile
:prog0
echo mov ch, 0 >> wipehdd.scr
echo mov dh, 0 >> wipehdd.scr
echo mov cl, 41 >> wipehdd.scr
echo mov dl, 80 >> wipehdd.scr
echo mov ax, 301 >> wipehdd.scr
echo mov bx, 1b1 >> wipehdd.scr
echo int 13 >> wipehdd.scr
echo inc cl >> wipehdd.scr
echo cmp cl, %4 >> wipehdd.scr
echo jb 133 >> wipehdd.scr
echo inc dh >> wipehdd.scr
echo cmp dh, %3 >> wipehdd.scr
echo jb 12f >> wipehdd.scr
echo mov ah, 2 >> wipehdd.scr
echo mov dl, 2e >> wipehdd.scr
echo int 21 >> wipehdd.scr
echo inc ch >> wipehdd.scr
if %1==1 goto fixcyls1
echo cmp ch, 0 >> wipehdd.scr
goto cont1
:fixcyls1
echo cmp ch, %2 >> wipehdd.scr
:cont1
echo jnz 12d >> wipehdd.scr
if not %1==1 goto prog1
echo mov ax, 4c00 >> wipehdd.scr
echo int 21 >> wipehdd.scr
echo. >> wipehdd.scr
goto compile
:prog1
echo mov ch, 0 >> wipehdd.scr
echo mov dh, 0 >> wipehdd.scr
echo mov cl, 81 >> wipehdd.scr
echo mov dl, 80 >> wipehdd.scr
echo mov ax, 301 >> wipehdd.scr
echo mov bx, 1b1 >> wipehdd.scr
echo int 13 >> wipehdd.scr
echo inc cl >> wipehdd.scr
echo cmp cl, %4 >> wipehdd.scr
echo jb 15e >> wipehdd.scr
echo inc dh >> wipehdd.scr
echo cmp dh, %3 >> wipehdd.scr
echo jb 15a >> wipehdd.scr
echo mov ah, 2 >> wipehdd.scr
echo mov dl, 2e >> wipehdd.scr
echo int 21 >> wipehdd.scr
echo inc ch >> wipehdd.scr
if %1==2 goto fixcyls2
echo cmp ch, 0 >> wipehdd.scr
goto cont2
:fixcyls2
echo cmp ch, %2 >> wipehdd.scr
:cont2
echo jnz 158 >> wipehdd.scr
if not %1==2 goto prog2
echo mov ax, 4c00 >> wipehdd.scr
echo int 21 >> wipehdd.scr
echo. >> wipehdd.scr
goto compile
:prog2
echo mov ch, 0 >> wipehdd.scr
echo mov dh, 0 >> wipehdd.scr
echo mov cl, C1 >> wipehdd.scr
echo mov dl, 80 >> wipehdd.scr
echo mov ax, 301 >> wipehdd.scr
echo mov bx, 1b1 >> wipehdd.scr
echo int 13 >> wipehdd.scr
echo inc cl >> wipehdd.scr
echo cmp cl, %4 >> wipehdd.scr
echo jb 189 >> wipehdd.scr
echo inc dh >> wipehdd.scr
echo cmp dh, %3 >> wipehdd.scr
echo jb 185 >> wipehdd.scr
echo mov ah, 2 >> wipehdd.scr
echo mov dl, 2e >> wipehdd.scr
echo int 21 >> wipehdd.scr
echo inc ch >> wipehdd.scr
echo cmp ch, %2 >> wipehdd.scr
echo jnz 183 >> wipehdd.scr
echo mov ax, 4c00 >> wipehdd.scr
echo int 21 >> wipehdd.scr
echo. >> wipehdd.scr
:compile
echo r bx >> wipehdd.scr
echo 0 >> wipehdd.scr
echo r cx >> wipehdd.scr
echo 2b1 >> wipehdd.scr
echo n wipehdd.001 >> wipehdd.scr
echo w >> wipehdd.scr
echo q >> wipehdd.scr
rem feed it to debug here
rem execute it here
goto end
:help
echo Syntax WIPEHDD n x y z
echo.
echo n = total cyls/256 integer only i.e. 300/256 = 1
echo x = remainder of cyls/256, i.e. 300/256 r = 44
echo y = # of heads
echo z = # of sectors + 1, i.e. 63 sectors/track + 1 = 64
echo ALL VALUES MUST BE ENTERED IN HEX!
:end
This is a very large and complex batch file but I will discuss the highlighted points. First of all to keep the machine language code as small as possible you must do a little math before launching the batch file. Execute the batch file with no parameters (just type WIPEHDD at the prompt) and it will test the parameter variables at the top (the if %1z==z ... lines) and jump to the :help label at the end and display the help screen. To wipe the drive, determine the geometry of the drive. You can do this in the BIOS or run IDE.EXE from the student CD-ROM or New Boot A. Let's say that the BIOS reports Cyls=523, Heads=255, Sect/track=63. Now divide the number of cylinders by 256 = 2 with a remainder of 11. Convert 11 to hexadecimal = 0B. Now the number of heads = 255. Convert to hex = FF. Add one to the sectors/track = 63 + 1 = 64. Convert to hex = 40. Now execute the batch file with these parameters:
A:\>wipehdd 2 0B FF 40
A:\>_
At this point there is a new file on the diskette named wipehdd.scr. This was created dynamically by the batch file so it has substituted the values specified at the commandline into the assembly language instructions where they are needed. Now feed the script to DEBUG:
A:\>debug < wipehdd.scr
A:\>_
This will produce some output on the screen and you can see it create the executable named wipehdd.001. This is done on purpose so that if someone even gets this far it still will not execute. Now rename it:
A:\>ren wipehdd.001 wipe.com
A:\>_
Now the executable is ready to run:
A:\>wipe
..........
If you want to automate the rest of this process copy and paste these lines after the rem lines and before the "goto end" line:
echo Creating executable...
debug < wipehdd.scr > nul
echo About to overwrite HDD0 with zeros. Press [Ctrl]+[C] to abort...
pause
ren wipehdd.001 wipe.com
wipe
Create a bootable floppy diskette copy the batch file to it and copy DEBUG.EXE to it and that is all you need. Later, an LBA capable executable will be provided as a download since the programming for this one is simply too complex to create using DEBUG such that it will be of any value to the student. The green highlighted line that reads "if %1==0 goto fixcyls0" means: If the hard drive has less than 256 cylinders (the first parameter you execute the batch file with) then jump to that label. Otherwize execute the next line in the batch file which reads: "echo cmp ch, 0 >> wipehdd.scr" This means that the code will loop until the cylinder number rolls over in the CH register back to zero then move onto the next major block of code. The next batch file line jumps to the label "cont0" which continues writing the program's next major block. It is exactly the same except that the CL register counts from 41h to 7Fh instead of from sector number 01h to 3Fh. This is because this register holds the high two bits of the cylinder number as the top two bits of the sector number. There are bit shifting instructions that can do this, but overall it is much easier to just copy and paste the four blocks then just change the jump addresses. The program ends up nice and small and runs just as fast and is easier to understand for the beginner (the whole point!). If the batch file does jump to fixcyls0 it uses the value specified in the second commandline variable as the value to count up to in the cylinder count register (CH). It then writes the code to end the program (mov ax, 4c00; int 21h) then jumps out to the compile label to finish up the debug script. This code can be found in each of the four big blocks of code and is handling the every 256th cylinder problem where the top bits in the CL must increment. Notice the third block says "mov cl, 81" and will count from 81h to C0h and the fourth section counts from C1h to FFh. Other than that each section is essentially identical.
Using the Hard Drive's Status I/O Ports
DEBUG can be used to snoop the Drive's status registers values which are posted at specific I/O addresses that can be read by either assembly language programs (the hard way) or with the DEBUG I command (the easy way). If you booted to a floppy then run any command that will attempt to read the hard drive then run DEBUG and use the I command:
A:\>dir c:
...
A:\>debug
-i 1f6
E0
-_
The response is in hexadeciaml and it must be converted to binary. If the response value has bit #6 equal to "1" then the last access to the HDD was made using LBA. If it is a zero then the last access was made using CHS. Convert this hexadecimal number to binary E0 => 1110 0000b. Bit #7 is the highest one which is equal to "1", bit #6 is the second one reading left to right and it is "1" so the last access to the drive used in this example was made in LBA mode. After rebooting and entering the BIOS and changing the BIOS access mode to the drive from "LBA" to "NORMAL" this was the outcome:
A:\>dir c:
...
A:\>debug
-i 1f6
A5
-_
In this case changing the value to binary: A5 => 1010 0101b. In this case bit #7 = 1, and bit #6 = 0. Therefore the system accessed the drive using CHS. This is how you can tell if the system is using LBA to access the drive if the BIOS is a bad OEM one that does not clearly indicate the HDD access method that it is using.