Memory Dumps from DOS DEBUG

As I’ve mentioned before, I like to use the DOS DEBUG command to investigate older 16-bit programs. Today, I present a brief guide to using that tool to take snapshots of running programs.

Motivation

The DOS DEBUG command provides only primitive facilities for the inspection of the contents of memory. When analyzing a program, it’s sometimes helpful to deploy more powerful tools – such as custom code – to manipulate a memory image. A first step to using external tools is extracting the memory image, and writing it to a file.

Procedure

  1. You’ll need to select a breakpoint at which to dump memory. The selection is left as an excercise for the reader, but I’ll assume you’ve got one picked out in SEG:OFF format.
  2. Invoke DEBUG, specifying the .exe from which you wish to dump memory as an argument, e.g. “debug neuro.exe“.
  3. Run to the breakpoint you’ve selected, e.g. “g 0b27:dd68“.
  4. Name the dump file, e.g. “n neuro.bin“. (Note that DEBUG won’t write files with a .hex or .exe suffix – pick another one.)
  5. Note the values of AX, BX and CX.
  6. Store the number of bytes to dump in BX:CX. (Note that this is *not* a SEG:OFF value, this is a 32-bit value spread across two registers.)
  7. Dump memory, specifying the starting address, e.g. “w 5113:0000“.
  8. Restore the original values of AX, BX and CX.

About that Breakpoint …

Picking a breakpoint at which to stop a new, unfamiliar program is not trivial. In general, you want to stop somewhere “interesting” – a point at which memory has been populated with meaningful data. Until disassembly and analysis reveals the location of significant function calls in the program, interrupts are your best bet for finding interesting inflection points in the code.

For instance, the INT 10h interrupt is used for a large number of important (but relatively rare) video operations: setting the video mode, manipulating the palette, etc. Breakpoints set on INT 10h opcodes will tend to be hit at interesting points in the code.

Example

Consider the game “Neuromancer”, from 1989. For this example, I’ll be working with the PC version of the game, which includes a NEURO.EXE file with a md5 hash of:

0xa583c5d83269cedf37fd2743971940ad

In order to take a meaningful memory dump, we need to find a good place to break program execution (i.e. after the program has had a chance to store data to memory). If we search the executable for INT 10h opcodes (two adjacent bytes equal to 0xcd 0x10) we find two, at file offsets 0xde68 and 0xdedc. Using a technique discussed previously, we know that these opcodes will be found at memory addresses 2000:1a18 and 2000:1a8c. (Those are the addresses on my machine, at any rate: Yours may vary.)

When we run neuro.exe through the debugger, we find that neither breakpoint is hit until we exit the program, and the video mode is restored to 80×25 text. This is fine for our purposes, however, as memory has certainly been initialized by the time the program gets around to quitting.

Once we’re at our breakpoint, we need to note the values of AX, BX, and CX so that we can restore them later. On my machine, their values are 0x0003, 0x0000, and 0x0013. Next, following the procedure above, we name the dump file, set BX:CX to 0001:0000 (to dump an entire 64K segment) and issue the “w DS:0” command to dump the data segment. Finally, we restore the AX, BX, and CX registers, and let the program run to completion with the “g” command. Here’s a list of the commands issued to DEBUG during this process:

g 2000:1a18 2000:1a8c
::Play game a bit, then exit::
n neuro.bin
r bx
1
r cx
0
w ds:0
r ax
3
r bx
0
r cx
13
g

Notes

You can dump the entire 1M address space by setting BX:CX to 10:0000 in step (6), and specifying an address of 0:0 in step (7).

Share and Enjoy:
  • Twitter
  • Facebook
  • Digg
  • Reddit
  • HackerNews
  • del.icio.us
  • Google Bookmarks
  • Slashdot
This entry was posted in Reverse Engineering. Bookmark the permalink.

Comments are closed.