Skip to content
FreeDOSDeep Dive Published Updated 8 min readViews unavailable

Using FreeDOS DEBUG for Read-Only Binary Inspection

Inspect DOS program memory and bytes with DEBUG's dump, register, and disassembly commands while avoiding execution and raw-sector writes.

FreeDOS DEBUG is a command-driven x86 debugger and binary inspection utility. It can display memory, show registers, disassemble bytes, compare ranges, and, in other commands, edit or write data. Those capabilities make it useful for diagnosing a small DOS executable, but they also make it possible to alter memory, execute a program, access I/O ports, or write disk sectors. This guide deliberately stays on the read-only path: load a copy of a file, inspect it with dump and disassembly commands, and quit without using execution or write operations.

Confirm which DEBUG you have

FreeDOS documents DEBUG as a clone of the MS-DOS DEBUG command, while noting that it has additional commands and some differences from the original. That matters because command names that look familiar may not be implemented identically across clones. Start with DEBUG and type ? at its prompt to see the commands available in the installed build. Record the executable path and version/help output if results must be reproducible.

DEBUG is an external program, so normal command search rules apply. A different directory earlier on PATH can select a different DEBUG than the one installed with FreeDOS. For a controlled lab, invoke the known executable with a fully qualified path. Work on a read-only copy or a disposable VM snapshot if the file matters; loading and inspecting a file should not change that file, but DEBUG also exposes modifying commands that should be kept out of an unattended procedure.

What a DEBUG session represents

At startup, DEBUG provides a small command language that addresses registers, memory, ports, and files. Its commands are terse because the original environment was resource constrained. Typical commands include D to dump bytes, U to unassemble instructions, R to display or change registers, C to compare ranges, S to search memory, Q to quit, and W to write. The same one-letter interface includes dangerous commands such as E (enter/edit memory), G (go/execute), port input/output commands, and a sector-write form of W.

The prompt does not provide a transaction boundary. If a command edits a buffer or writes a file, quitting afterward does not automatically undo it. A useful safety rule is to restrict a read-only session to ?, D, U, R without a value argument, and Q; do not type commands you have not checked against the local help.

Inspect a copied COM program

A COM file is loaded by DOS at offset 0100h within its program segment. For a known COM file, this gives a useful starting point for inspection. In a disposable working directory, open the file in DEBUG and display the entry bytes:

C:\>DEBUG C:\LAB\SAMPLE.COM
-R
-D 100 11F
-U 100 12F
-Q

The D command displays bytes and printable character equivalents for the selected range. U interprets memory as 8086-family instructions and prints an assembly-like listing. The exact formatting depends on the DEBUG implementation and its selected processor mode. The addresses are offsets in the current debug segment; they are not file offsets in every executable format.

For a COM image loaded by the standard DEBUG path, file offset zero corresponds to offset 0100h in the load image because the first 256 bytes are the Program Segment Prefix. This relationship is useful for examining a small program, but do not extrapolate it to EXE files, overlays, packed programs, or a program whose load path differs. An EXE has relocation and entry-point data that determine where code begins. Read the header with a format-aware tool before interpreting arbitrary offsets.

Use registers and ranges carefully

R with no argument displays the current general registers, segment registers, flags, and instruction pointer. It is a read-only observation when no register value is supplied. A command such as R AX may accept a new value and therefore changes debugger state; it does not merely print. Check the prompt and syntax before using a register subcommand.

Memory ranges are expressed in hexadecimal. A small bounded range such as D 100 11F is easier to review than an open-ended dump that fills the screen and hides the relevant context. Use U 100 12F only when interpreting the bytes as instructions makes sense. Data tables, strings, compressed content, or random file bytes can disassemble into plausible-looking but meaningless opcodes. A disassembly is an interpretation, not proof that the file is valid code.

When comparing two in-memory ranges, the DEBUG C command can report differing bytes. It is not a substitute for FC /B when comparing separate files, because that comparison depends on the ranges and segments actually loaded into the current DEBUG session. Preserve the original artifacts and use a second trusted tool for checks where exact file equality matters.

Loading a program is not the same as running it

DEBUG can accept an executable name as an argument and load it for inspection. The G command transfers control to program code; avoid it in a read-only analysis session. Loading untrusted machine code may not execute it immediately, but mistakes with the command interface can. Use a disposable VM without access to important host files or devices for unknown binaries. Do not test a suspected executable on the only system that contains valuable data.

For a COM file, the conventional entry point is offset 0100h. An EXE’s entry point is described by its header and may be relocated. Executing at the wrong address can cause an immediate fault or unexpected writes through DOS calls, BIOS services, or direct port I/O. DEBUG is not a sandbox and does not restrict what a program can access after execution begins.

The boundary between inspection and modification

The command list makes the risks explicit. E enters bytes into memory; F fills a range; M moves a memory block; A assembles instructions into memory; G executes; O writes to an I/O port; and W writes program data. DEBUG also has a W address drive sector count form for writing sectors. These are not diagnostic read commands. Never explore them against an active disk merely to see what happens.

DEBUG also documents L address drive sector count for reading sectors. Even reads can be risky if the drive selection or addressing is misunderstood, and a sector dump bypasses filesystem safety checks. Use a cloned image and a documented read-only imaging utility for disk analysis instead. If you must inspect raw sectors, first take a full image and verify that the tool is pointed at the image rather than a physical device.

Do not treat DEBUG output as a forensic acquisition log. It does not automatically record hashes, timestamps, device identity, or a chain of custody. For an auditable analysis, preserve the original file, calculate a trusted checksum before and after inspection, capture the commands and output, and record the tool version and environment. If the hashes differ, stop and investigate before drawing conclusions.

Common interpretation traps

An all-zero dump may mean the wrong segment or range, not an empty file. A readable string in a dump does not prove that the bytes belong to a valid resource or are actually used. A disassembly that looks sensible can be a coincidence, particularly in data sections. DOS programs may use self-modifying code, overlays, interrupt handlers, or memory beyond their nominal image; one static range is only a view of one state.

Address units also matter. DEBUG prompts commonly show hexadecimal values without a 0x prefix. Typing decimal-looking numbers can therefore select a different address than intended. Ranges may include both starting and ending offsets, and the tool’s current segment context affects memory commands. Begin with ?, read the exact local help, and keep test ranges small.

A repeatable inspection record

For a small COM-file inspection, record the exact file path, file size and trusted checksum, DEBUG executable path, and command sequence. Start DEBUG with the copied file, use register display and bounded dump/disassembly commands, capture output, and exit with Q. Recompute the file checksum afterward. If the checksum changed, discard the copy and investigate whether an editing or write command was entered; never alter the original to “fix” the difference.

For an EXE, use a format-aware header parser first, identify the declared entry point and relocation model, and only then decide whether DEBUG’s memory view can answer the question. For a DOS crash, capture the register state at the failure from the program or debugger before changing registers. Avoid trying random G breakpoints on a production system; execution changes the machine state and may make the original failure impossible to reproduce.

Acceptance criteria

An inspection is complete when the selected file identity is recorded, the commands were limited to read-only operations, each address range was bounded and interpreted with the correct COM/EXE model, and the original artifact’s checksum still matches. Any command that writes memory, ports, files, or sectors moves the session outside this guide’s safe scope. A careful DEBUG session should answer a narrow question, such as what bytes or registers are present, not become an uncontrolled experiment.

Related:

Sources:

Comments