Debug.exe Tutorial

I started this tutorial on 01/01/2010, then forgot about it, then finished it Feb 08.

Debug.exe Tutorial

by Jakash3
New Years! 01/01/2010

0×00 What is debug?
0×01 Hex Math
0×02 Data reading
0×03 Hex Editing
0×04 Debugging

0×00 What is debug?

Debug is a hex editor, assembler, disassembler, and debugger. It is a console program and is included with every Windows OS (In path C:\Windows\System32). In more plainer terms, this program allows the manipulation of file and disk contents byte by byte.

0×01 Hex Math

First open up debug.exe. Since the directory (C:\Windows\System32) it is in should already be a part of your %PATH% environment var, you can just go to Run and enter ‘debug.exe’ or do it from the command prompt.

Now to begin with the simple H command of debug. H stands for hex and this command is used to calculate the sum and difference of two hexadecimal numbers.

The syntax: h value1 value2

where both value1 and value2 are hex numbers with a digit range of 1 to 4.

On return you should have the first number as the sum of the two specified numbers and the second number is the result of value2 subtracted from value1. Example:

-h 010E 0100
020E  000E
-h 8 7
000F 0001

In the first occurance of the H command it calculated 010E+0100 or just 10e+100 and the result was 20E, the difference: 10E-100 is E.

Next I found the sum of 8 and 7 and the difference. The first number it shows in the result is the sum, in this case 000F (the 0×8+0×7), and the difference which is 0×8-0×7=0001.

This command proves very useful when calculating offset address range(s).

Although, if you wish for a GUI and more functionality you can use MS Calculator (C:\Windows\System32\calc.exe) which also capable of multi., div., long expressions, and bitwise comparisons.

There are also other calculator software out there or you can program your own!

0×02 Data Reading

Register Command {

One of debug’s debugging features is the ability to read or write to a loaded program’s registers.

The syntax: r register

where register can be the name of one of these 16 bit registers: AX, BX, CX, DX, SP, BP, SI, DI, DS, ES, SS, CS, IP.

On return with a register as an argument it will display the value of that register and prompt you to enter a new value. If omitted the value is unchanged:

-r cx      ;Command to view CX
CX 000A    ;Returns value of CX, which in this case is 000A
:0012      ;I enter a new value, 0012 for CX. (optional)

If you use the R command with no arguments it will display the values of all those registers and the flags, including the the instruction that IP is pointing to.

One important thing to remember is if debug has a file loaded, then the value CX is the length of the file. If the file is bigger than 64k then the value will be split into two registers where BX contains the high order of the length of the file and CX contains the low.

So if the file was 1b bytes long, CX = 001b and BX=0000. But if the file was 00310f52 bytes then, CX = 0f52 and BX = 0031.

Dump Command {

The dump command is used to display the bytes of a loaded file in hex and in their ascii representation.

The syntax: d range

Where range is could be just one address to start reading from or two addresses to start reading and end reading from. Examples:

-d 00f9
1879:00F0                             00 00 00 00 00 00 00            .......
1879:0100  EB 0D 68 65 6C 6C 6F 20-77 6F 72 6C 64 21 24 B4   ..hello world!$.
1879:0110  09 BA 02 01 CD 21 CD 20-00 00 00 00 00 00 00 00   .....!. ........
1879:0120  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1879:0130  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1879:0140  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1879:0150  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1879:0160  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
1879:0170  00 00 00 00 00 00 00 00-00                        .........
-d 010f 0118
1879:0100                                               B4                  .
1879:0110  09 BA 02 01 CD 21 CD 20-00                        .....!. .

The output from the dump command has three parts: on the left we have the segment and offset addresses which are displayed every 16 bytes of the file or disk. Then in the middle it displays the value of every byte (the dashes in the middle are there to help you keep track of counting). And finally we have the ascii representations (characters) of the bytes, only the standard console characters are shown otherwise it is replaced by a dot/period.

When specifying a range it cannot go outside of a segment. And the range cannot exceed 64k.

Compare Command {

Think of this as a stripped down dump command with double the power. What it does is display the bytes of two addresses you specify side by side.

The syntax: c range address

Where range is the starting and ending address of the first block you wish to compare, and address is starting address of the second block you wish to compare. Example:

-c 0100 0109 010f
1879:0100  EB  B4  1879:010F
1879:0101  0D  09  1879:0110
1879:0102  68  BA  1879:0111
1879:0103  65  02  1879:0112
1879:0104  6C  01  1879:0113
1879:0105  6C  CD  1879:0114
1879:0106  6F  21  1879:0115
1879:0107  20  CD  1879:0116
1879:0108  77  20  1879:0117
1879:0109  6F  00  1879:0118

As seen here I have bytes from 0100 to 0109 displayed on the left side compared with the same number of bytes displayed on the right side starting at address 010f.

But keep in mind this: When any of the two compared bytes are the same, then debug skips the display of those bytes. (so if, according to the previous example, bytes at 0101 and 0110 matched then debug would display bytes at 0100 + 010f then skip to 0102 + 0111)

Also if both blocks of data are exactly the same then there would be no display

Search Command {

Search searches for the specified byte(s) (or ‘character arrays’) within a segment, then it displays a list of addresses containing those bytes.

The syntax: s range list

Where of course range is the starting and ending offset address to search in, and list is the bytes to search for. You may also specify strings too, provided that they are in quotes. Examples:

-s 0100 0120 b4 09 ba 02 01 cd 21
-s 0100 0150 "Hello World!" 24

See, 1st parameter = starting address to search in, 2nd parameter = ending address to search in, * parameter = bytes to search for

The returns are the addresses that those bytes were found at.

Be aware that it is simply search through loaded bytes and it does not recognize them as instructions, just a bunch of data.

Unassemble Command {

One of the more common commands, this one takes the read bytes from a specified range within the loaded sector and displays those bytes as human readable assembly instructions.

The syntax: u range

range can be either one address to start unsassembling from or it can be two addresses to the start and end of the unassembly reading.

By default, the offset address to start reading at is pointed to by the IP register in debug which is 0100 and depending on your machine, the segment is set by the CS register. After that, it will remember where it disassembled at and then disassemble from where it last stopped at the next time you use the U command during the current debug.exe process.

In addition to the default, if you do not specify the range, it will disassemble the next 31 bytes from the IP pointer. Example:

-u 10f 118
0AE0:010F B409          MOV     AH,09
0AE0:0111 BA0201        MOV     DX,0102
0AE0:0114 CD21          INT     21
0AE0:0116 B400          MOV     AH,00
0AE0:0118 CD21          INT     21
|_______||____|       |_______________|
    |       |                 |
    |       |                 |
    |       +---------+       +------------+
    |                 |                    |
Segment:Offset   Byte Values    Converted ASM Instructions

The output is the converted assembly instructions on each line that follows their equivalent byte values on the same line that is labeled with their address.

One disadvantage is that debug is not very modern, in other words: debug.exe cannot tell the difference between 8086 Instructions, data, or new x86 Instructions for the bytes is disassembles. So just manage. Here’s an example of this ‘flaw’ shown here where debug treats the data “hello world!” as instructions:

0AE0:0102 68            DB      68
0AE0:0103 65            DB      65
0AE0:0104 6C            DB      6C
0AE0:0105 6C            DB      6C
0AE0:0106 6F            DB      6F
0AE0:0107 20776F        AND     [BX+6F],DH
0AE0:010A 726C          JB      0178
0AE0:010C 64            DB      64
0AE0:010D 2124          AND     [SI],SP

Which is generally the same as this instruction:

db "hello world!",24

But debug.exe treats everything as old 16 bit instructions. One way for you to manually know that it is data is that if you look at the byte values, you will notice that their ascii character representations (for hex) outputs real human readable characters.

In this case, as seen above; 68,65,6C,6C,6F,20,77,6F,72,6C,64,and 21 output “hello world!” if displayed as characters. Check an ascii chart to see.

0×03 Hex Editing

Now let’s get into the hex editing part of debug, where you can modify the bytes of a file you have loaded, or just modify bytes a file you are about to create.

Enter Command {

The single letter command known as enter is denoted by the E command. The E command is used to insert bytes of data starting at an address you specify.

The syntax: e address list

Where address is the address of where to insert the data at and list is where you specify all the bytes you want to be placed there. If you only specify the address, then you will be prompted to enter the bytes. There are types of data you can insert: byte data and string data, which will be converted to bytes. Examples:

-e 100 b4 09 ba 0b 01 cd 21 b4 00 cd 21
-e 7ea 24
-e 100
1868:0100  00.b4   00.09   00.0b   00.01   00.21   00.b4
1868:0108  00.00   00.21
-e 200 'hello world!$' 00

The first command, I placed the bytes b4 09 … into memory starting at address 100. Then I placed the single 24 byte into 7ea, then I was prompted to enter the bytes from address 100 since I didn’t provide the bytes as an argument. Then Finally I entered a string instead of typing their byte equivalents for the enter command.

Fill Command {

The fill command is denoted by F. It is used to replace or fill the data of a range of addresses with the same padded data you specify.

The syntax: f range list


-f 100 12f 'BUFFER'
-d 100 12f
xxxx:0100  42 55 46 46 45 52 42 55-46 46 45 52 42 55 46 46 BUFFERBUFFERBUFF
xxxx:0110  45 52 42 55 46 46 45 52-42 55 46 46 45 52 42 55 ERBUFFERBUFFERBU
xxxx:0120  46 46 45 52 42 55 46 46-45 52 42 55 46 46 45 52 FFERBUFFERBUFFER
-f 100 ffff 0

See how I filled the range from 100 to 120 with the same data? ‘BUFFER’?. Also the next trick was to erase all the data from 100 to ffff by filling it all with null bytes or 00.

Move Command {

It should’ve have been called the copy command, but the C sub-command has already been taken by the Compare command. So that’s why I’m guessing they called this one move. Anyways, this command copies a range of data to another address.

The syntax: m range address

Example: m 7c00 7dff 600

Will copy the data from between the addresses 7c00 and 7dff to the destination address 600.

Assemble Command {

This is the command in debug that allows you to use it as a limited 16-bit assembler. The syntax is relatively easy: a address Where you specify the address you wish to start writing assembly code at, but you’re
on you’re own after that. Watch me create a hello world program with the assemble command:

-a 100
xxxx:0100 jmp 010f
xxxx:0102 db "hello world!",24
xxxx:010f mov ah,09
xxxx:0111 mov dx,0102
xxxx:0114 int 21
xxxx:0116 mov ax,4c00
xxxx:0119 int 21

You’re going to have to learn a little bit of assembly language to use this command. But I will gladly explain the code above. First command is like the GOTO command in batch except you referencing an address to jump to, here I declared to jump to the address 010f. The next command at 0102 declares bytes or data that are equivalent to the string literal “hello world!$”. 010f: move a byte with the value of 9 into the8-bit register named AH. 0111: move two bytes 01 and 02 into the 16-bit DX register, 0102 is the address of where my hello world string lies. 0114: Interrupt call to the
operating system. The interrupt call 21 function deals with DOS, in this case when you call this interrupt with AH=09 then you are referencing for a string to be printed
which the pointer to the string that ends with $ is placed in DX. Next we move 4c00 into AX, when calling int 21 with AH=4c we are telling the program to exit an arg
to this function is the value of the return exit code is placed in AL, which I have equal to zero. 0119: int 21, exit with return errorlevel 0, due to the previous

To find a list of interrupts and their functions, please look up Ralf Brown’s Interrupt list.

Now when in assemble mode, you are going to have to know some math to calculate the addresses you want to reference. Otherwise just put in any random 4 digit address then correct your work with the E sub-command or entering into assemble mode at the address you messed up at then correcting the intstruction.

Now in order to actually build this program we just assembled, we need to use three more commands.

Let’s start with the H sub-command. Use h to calculate the length of byte you assembled. In this case I will use h to find the difference from my ending address, 011b, with the address I started assembling at 0100.

-h 011b 0100
021B  001B

Now I have the length of my code: 001B We will have to set this number to the CX register within debug (I’ll tell you why later). I will do that with the R command:

-r cx
CX 0000

Now I will specify a name for the output file of this program to be called. The name cannot have drives or directories in it, just a single name with the .com extension:


Finally, the Write sub-command will write the the changed data into the loaded file or sector, but since we specified a different file name that doesn’t exist, then it will write this data to that file. The amount of data to write is referenced in the CX register. And you can pick an address to start writing from, by default it is 0100.

-w 0100
Writing 001B Bytes

Now if you look in the current directory you will see the file we created. Run it from command prompt and see the magic happen.

0×04 Debugging

This section will cover the debugging features of debug.exe where you may run certain addresses in a loaded program.

Go Command {

This command is used to run a program and set breakpoints or addresses to stop executing at in a program’s code.

Syntax: g =address breakpoints

Where address is the address to start executing the loaded code at and breakpoints are a list of addresses to stop at when the debugger reaches those addresses.

Proceed and Trace Commands {

The two commands are relatively the same except the proceed command can in addition execute a loop, repeated string instruction, software interrupt, or subroutine. Trace doesn’t do that except runs the instructions while stepping over the loops and interrupts.

Their syntax is the same: p/t =address number

Where address is the address to start running at and number is the number of instructions to execute. After each processed instruction debug displays the flags, registers, and the next instruction to be executed.

If you give it a change, debug can be the most useful tool. As like I said, is a built-in assembler, debugger, and hex editor for windows. What makes it even more powerful and faster is that it runs with cmd! Well that’s my opinion, I love CLI as much or even more than GUI.

4 thoughts on “Debug.exe Tutorial

  1. Pingback: Rebooting DOS « cavok's R&I

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: