DEBUG will display a list of its valid commands on screen:
C:\>debug -? assemble A [address] compare C range address dump D [range] enter E address [list] fill F range list go G [=address] [addresses] hex H value1 value2 input I port load L [address] [drive] [firstsector] [number] move M range address name N [pathname] [arglist] output O port byte proceed P [=address] [number] quit Q register R [register] search S range list trace T [=address] [value] unassemble U [range] write W [address] [drive] [firstsector] [number] allocate expanded memory XA [#pages] deallocate expanded memory XD [handle] map expanded memory pages XM [Lpage] [Ppage] [handle] display expanded memory status XS -_
This will do little more than jog your memory concerning these commands and is of little value in learning how to use them.
Here is the list and what each one does:
A - Assemble
This command invokes the assembly language compiler built into DEBUG. This converts standard syntax assembly language instructions into their actual binary machine language opcodes. MOV AX, 1 is an example assembly language instruction that means "Move the value 1 into the AX register" essentially assigning it this value and destroying the previous contents of the register. (See Register in the Hardware A-to-Z pages for a map of the Intel 8088 processor's registers). However, an assembly language compiler must convert this ASCII text into the actual binary machine language code which it turns out is these three bytes: B8 01 00. When the assemble command is issued an optional memory offset may be supplied as a parameter indicating to DEBUG where to begin placing the machine language instructions, the default is offset 100h which is the lowest address that can be used inside of DEBUG (the offsets from 0 to FFh are part of DEBUG itself and you can crash it if you modify their values!). To get out of the assembler mode hit [Enter] on a blank line:
-a 100 136D:0100 mov ax, 1 136D:0103 (Hit [Enter] to exit the assembler) -_
C - Compare
This command allows two ranges of RAM to be compared for differences. First a starting and ending RAM address range must be provided followed by the starting address of the RAM memory range to be compared. For example, start DEBUG and at the prompt clear the RAM from 100 to 2FF (See the F - Fill command below). Then place the word THIS at offset 100 and the word THAT at offset 200 (See the E - Enter command below). Now compare the contents of the block of RAM starting at offset 100 and ending at offset 1FF with the block of RAM starting at offset 200 like this:
-C 100 1FF 200
136D:0102 49 41 136D:0202
136D:0103 53 54 136D:0203
-_
This output indicates that the byte at offset :0102 is 49, but it is 41 at the comparable offset :0202. It indicates that the byte at offset :0103 is 53 but it is 54 at the corresponding offset :0203.
In other words the C command is a test of two blocks of information that should be the same, and it will display the differences if they exist.
D - Dump
E - Enter
F - Fill
G - Go
H - Hex
I - Input
L - Load
M - Move
N - Name
O - Output
Q - Quit
P - Proceed
R - Register
S - Search
T - Trace
U - Unassemble
W - Write
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.
Change the Name of the Initial Boot File (IO.SYS)
Pending...
Change the "Starting MS-DOS . . ." Text
Pending...
Display the BIOS Manufacture Date
The BIOS chip has information stored behind the backwards jump instruction located at memory address F000:FFF0h. It holds the date of manufacture of the BIOS code (not the EPPROM chip itself which could have sat on a shelf for years). Boot to the DOS prompt and launch debug and enter this command to read the BIOS manufacture date by displaying the memory location of the processor’s entry point into the EEPROM on screen:
C:\>debug
-d f000:fff0 L 10
F000:FFF0 EA 5B E0 00 F0 31 30 2F-33 31 2F 30 30 00 FC CB .[...10/31/00...
-_
The top line represents the locations where we are looking. By giving the "dump" command the "L 10" part we are saying only List 10 (hexadecimal! - which is 16!) bytes. Any address beyond 16 bytes would be a wrap around the top of RAM and would actually reside at some location lower in RAM. The first EAh is the jump instruction code followed by 5Bh E0h 00h F0h which is the location to jump to. The Intel processors are called “Little Endians” meaning that the little part of a number is stored at the lowest address. So each 16-bit (2 byte value must be turned around: E0h 5Bh then F0h 00h. But this is a complete four byte number that specifies the CS:IP. So each of these 16-bit values (called words) must also be turned around: F0h 00h then E0h 5Bh. Which we would express in the CS:IP notation as F000:E05Bh. This address also resides on the EEPROM lower down and is the start of the POST code. Behind this is the BIOS date stored in ASCII characters. The bytes are displayed first in hexadecimal, then in ASCII to the right. Over there you can see the date is: 10/31/00. This BIOS manufacture date stamp can be used to look up the BIOS upgrades for the system on line. If there is a BIOS upgrade of a later date than what you find here, then the BIOS upgrade may be worth the effort. If the dates on line are the same then you know that this motherboard has the latest version of the BIOS available and "flashing" it would be a dangerous waste of time.
Clear the CMOS Password (Kill all CMOS settings)
This one you obviously use at your own risk...
C:\>debug -0 70 17 -0 71 17 -_
An alternate method that circulates and appears to perform well is:
C:\>debug -0 70 2e -0 71 ff -_
All done, reboot and you will get a CMOS checksum failure and have to enter the BIOS Setup utility and if it had a password it will not have one anymore. See CMOS offset map to see what each offset means within the CMOS and an activity further down which demonstrates how to use this map.
Backing up your CMOS (And restoring it)
It should be noted that this operation will only backup and restore the AT CMOS RAM settings and it will NOT backup the PCI CMOS settings also refered to as the DMI pool, and the PCI also has ESCD settings (along with the AT portion). These two PCI bus CMOS RAM areas can be huge and they differ from motherboard to motherboard as to the valid values they hold and even which I/O addresses they reside at. So this operation will save the boot sequence and floppy drive A: type but it will not save any of the information about the PCI bus devices and peripherals. If that is lost the potential exists that when the system redetects all devices on the bus that their resources could get "shuffled" ruining their driver settings and this could end up requiring reinstalling device drivers or worse the whole OS. This operation can only be done from real mode DOS. It should not be done from a Windows 9x DOS box and Windows NT will refuse to allow it and shutdown the DOS window.
This operation is far easier to perform with a script. Copy and paste the script below into notepad and save it as cmosback.scr:
f 100 2ff 0 a 200 mov cx, 80 mov ax, cx mov bx, cx out 70, al in al, 71 mov [bx + 100],al loop 203 int 3 g=200 r bx 0 r cx 80 n cmosram.bak w q
Execute the script like this: debug < cmosback.scr. This script will save your CMOS RAM settings into a 128 byte file (that's is how big the AT CMOS RAM is including the AT ESCD segment) named CMOSRAM.BAK. Whenever the settings are lost perform the restore with this script. Copy and paste it into notepad and save it as cmosrest.scr:
a 200 mov cx, 80 mov ax, cx mov bx, cx out 70, al mov al, [bx + 100] out 71, al loop 203 int 3 g=200 q
Execute the restore like this: debug cmosram.bak < cmosrest.scr This will read in the CMOSRAM.BAK file and execute the instructions to place the values within the file back into the CMOS RAM locations from which they were originally saved.
Verifying your CMOS
Once you perform the above exercise it is possible to detect changes to the CMOS also. This can be done in the AUTOEXEC.BAT prior to the launch of Windows. If the CMOSBACK.SCR is performed in the root of the C: drive, then the actual backup data file CMOSRAM.BAK will be created in the root. Then debug can run a script at startup and compare the values found in the CMOS with the ones that were in the CMOS when the first backup was made. First rename the original CMOSRAM.BAK to CMOSRAM.1ST Then be sure that the backup script is in the root of the C: drive and add the following lines to the end of the AUTOEXEC.BAT:
debug < cmosback.scr fc /b cmosram.bak cmosram.1st > fc.txt type fc.txt | find /v "00:" | find /v "01:" | find /v "02:" | find /v "03:" > fc.txt type fc.txt | find /v "04:" | find /v "05:" | find /v "06:" | find /v "07:" > fc.txt type fc.txt | find /v "08:" | find /v "09:" | find /v "0A:" | find /v "0B:" > fc.txt type fc.txt | find /v "0C:" | find /v "0D:" | find /v "0E:" | find /v "0F:" > fc.txt type fc.txt | find /c ":" > choice /n /c:01 if errorlevel 2 goto diff echo No CMOS Setting changes detected. goto end :diff echo Differences found in the CMOS RAM settings! pause :end del cmosram.bak
The output of the binary comparison (the fc command) is saved as a text file that is then filtered through the find commands which eliminate all found differences at offsets 00h to 0Fh in the CMOS settings. These are the time/date which of course will change with every boot up and they are also status registers that can reflect momentary status states which will not always affect the constant settings. If differences are found the system will pause so that this message can be seen by the user and if the CMOS should not be different then the cmosram.* files can be compared by opening each in a DOS box window in DEBUG and then displaying the contents in side by side windows. The offset that has been changed can then be checked at sites where a full CMOS RAM map is shown. (See Links Page)
Copyright©2000-2004 Brian Robinson ALL RIGHTS RESERVED