When we open native or .NET windows executables in a hex-editor
we can notice that almost all of them contains strange
“This program cannot be run in DOS mode” text at the beginning of the file.
The original purpose on this text and surrounding it small
MS-DOS program, called MS-DOS stub is to print message to
the user and then exit if the
.exe file is run from under MS-DOS.
I in this blog post I will explain how it works and since
currently I have only GNU/Linux boxes in my flat I will
investigate using only Linux.
First we must obtain some
.exe files. We can create .NET executable
using Mono Project:
Native executable can be created using MinGW cross-compiler:
Now when we have a .NET and a 64-bit
.exe files we may look at
them using hex-editor
(I will use Bless here):
MS-DOS Stubs in both files looks very similar. This is quite unexpected
because we used two completely different compilers to create them.
Let’s confirm our assumptions first by extracting MS-DOS Stubs and then
by comparing them.
In case of both files MS-DOS Stubs end on 0x80 offset.
At that offset we can see “PE” letters
that mark start of PE headers
(“PE” means Portable Executable, this is
the name of
.exe file format used by Windows).
These letters are often called a Magical Number or a file signature.
This is similar to MZ letters that are always present at the
beginning of MS-DOS executables.
To extract stubs we must copy first 0x80 bytes (0x80 = 8*16 = 128)
.exe files. Then we may compare them:
So indeed MS-DOS Stubs are identical.
Now just for the sake of doing it I will grab DOSBox and check if these subs are working:
After DOSBox starts we must mount our directory with
We may use
CLS command to clear the screen and attempt to
.exe files. For those that never used MS-DOS, this
system supports only short files names (eight letters for file name plus
three letters for extension, so called
That is why our
HelloWorld.exe is displayed as
Everything works as expected. But that is not all of it.
Instead of our
.exe files we may run our extracted MS-DOS Stubs and
they also work without any problems:
This suggest that MS-DOS Stubs are just tiny MS-DOS EXE programs
(in MZ format) embedded in PE files.
To confirm that assumption we must look under the cover. Let’s start by dumping values of MS-DOS COM header:
To extract these values I wrote a small Java program. Source code is available as a GitHub Gist here.
All values in both MS-DOS file header and in PE headers are stored
using little-endian convention. This means that a four byte integer
0x11223344 will be represented on disk by bytes
0x44 0x33 0x22 0x11
(least significant byte first).
Of course this applies only to multi-byte types supported by CPU
Also because characters in ASCII strings are represented by single bytes
they are not affected by endianness.
For example string “foo” is represented on disk as
0x66 (f) 0x6f (o) 0x6f (o)
in both little-endian and bit-endian conventions.
I must admit that most of values in MS-DOS header seem magical to me. The most important thing that I learn by looking at the header was its size: 64 bytes. So after first 64 bytes of MS-DOS Stub I expect to find some MS-DOS code.
Before we attempt to disassemble code we must learn how MS-DOS programs are loaded in memory. I didn’t make a deep research but I have found a few valuable informations:
- MS-DOS divides memory in 64k segments. Programs refer to
a specific address in memory using
segmentvalue is set by MS-DOS when program is loaded and is stored in special CPU registers like
CS(code segment) or
e_ipvalue of MS-DOS header points to the first instruction of the program (to be more precise
Let’s try to use NASM to disassemble our code:
While reading resulting assembly code we must remember that our tiny program
stores both “This program cannot be run in DOS mode”
message data and code in the same segment.
Since the message starts at offset
0x0E we may assume that
is the last instruction of the program.
Now we should try to analyze this assembly code, fortunately for me I have found this beautifully commend piece of code here:
Since we go that far why don’t we change default stub to something more interesting e.g. printing “Nyan” 3 times? First we must create a valid assembly program:
The main idea here is that we keep loop counter on the top of the
stack and we load counter into
bx register only to decrement it
or to compere it with zero. I keep the counter value on the stack because
I don’t know if contents of
bx register is changed by
interrupt (yeah I am too lazy too check).
If you have some spare time you may play with other MS-DOS and BIOS interrupts to print colorful messages or to beep at the user.
Let’s compile our assembly code and patch one of our
And when we start it using DOSBox: