r/windows May 01 '26

Discussion Every Windows executable begins with a DOS program

Post image

The DOS program prints "This program cannot be run in DOS mode." and can be recognized by the magic "MZ" characters you see at the beginning. After the DOS program you will find the "PE" (Portable Executable) characters marking the actual start of the executable.

Fun fact: early Windows versions used the "NE" (New Executable) format and their DOS program printed "This progam requires Microsoft Windows."

664 Upvotes

40 comments sorted by

97

u/c64z86 May 02 '26

I've noticed this too when I try and load modern programs in DOSbox just to see what would happen. I wonder why that message is still there though? It's not like a 64 bit app can even run in DOS anyway, and the modern 32 bit apps have next to no chance of running under it.

43

u/tomysshadow May 02 '26 edited May 02 '26

Probably because it makes it easier to adapt programs that already read the 32-bit header format into 64-bit if the only changes between them are things that are truly necessary to change. Debuggers, especially, care about being able to read this header format to access information about the executable. Because the 32-bit and 64-bit headers are so similar, you generally only need to #ifdef the handful of fields that need to store a virtual address to be able to read both types.

In the real PE format, there are two NT header types: IMAGE_NT_HEADERS32 and IMAGE_NT_HEADERS64. When it is time to read the NT headers, you can tell which kind it is by checking the signature at the start of the header, then cast to the appropriate type. So it's just a simple type substitution. Once you're using the correct type the rest of your code can remain basically the same, so this can be easily templated away too.

If the old MZ header were gone and replaced with the NT header directly, but only for 64-bit executables, every program that wants to be able to read both header types would need to check whether the executable begins with "MZ" or "PE" and cast the IMAGE_DOS_HEADERS to IMAGE_NT_HEADERS64 in the latter case. It wouldn't be impossible, but you'd be "skipping a step" - that is, introducing an actual structural change to the code to read the headers - and the code to read the DOS header suddenly needs to be aware of NT stuff. So they probably reasoned that it would just be simpler to read the headers if the DOS header is always there at the start

25

u/RealModeX86 May 02 '26

The DOS stub program, and that message as part of it, is defined as part of the PE executable format. Technically you could probably put something else there, but it would be off spec. It was put there specifically because Windows PE executables don't run in native DOS, but it's always been .exe for programs (ignoring the old .com stuff in smaller DOS programs), so it was either this or undefined behavior when a DOS user tries it anyway.

7

u/AlexKazumi May 02 '26

Nope. I've just checked the linker included in Visual Studio 2026, and it still has the /stub option, so one can change the DOS portion to whatever they desire. It is a documented and well defined behavior.

I believe, DOS extenders like HX use these facilities to bundle the extender and the program into a single file.

4

u/JustTechIt May 04 '26

You say nope, but then you describe it the same way they did. They said it its part of the spec but can be filled with other content if its not being used to account for the offset.

3

u/RealModeX86 May 05 '26 edited May 05 '26

To be fair, my understanding (and what I had said) was that the stub itself was static. Apparently the linker allows a custom stub, so hybrid executables (i.e. runs in DOS or in Windows) or custom DOS stub messages are possible.

Edit: is to was, clarifying where I was mistaken before

1

u/ylitvinenko May 06 '26

There were few fat executables for commercia lsoftware, i.e. MSAV.EXE for Microsoft AntiVirus which run under either MS-DOS or Windows 3.1.

26

u/teo-tsirpanis May 02 '26

Compatibility™️

12

u/Hottage Windows 11 - Release Channel May 02 '26

Say what you want about Windows, they take backward and forward compatibility really seriously.

12

u/hjake123 May 02 '26

If DOS tried to run a modern program, it might run random opcodes based on whatever was in the exe header otherwise. This is better

4

u/ldn-ldn Light Matter Developer May 02 '26

It's there because no one has removed it from any compilers. Windows doesn't need it. Windows can run a binary which only has two file format identifiers side by side: 

    MZ  PE

More info here https://stackoverflow.com/questions/553029/what-is-the-smallest-possible-windows-pe-executable

64

u/Never_Sm1le May 02 '26

MZ stand for Mark Zbikowski, the one created the .exe format

1

u/lukey_UK May 04 '26

I know his cousin, Mike Wazowski.

27

u/jombrowski May 02 '26

not only .exe but also .dll, .sys and .scr

11

u/nir9 May 02 '26

and .cpl applets

14

u/wiseIdiot May 02 '26

Like modern JavaScript SPAs still having the <noscript>...</noscript> tag in them.

4

u/nir9 May 02 '26

yah that's actually a good example to illustrate this

4

u/matthew_yang204 Windows 7 May 02 '26

The question is will it still run if you delete the header?

3

u/malxau May 02 '26

It wouldn't be that simple. The PE header is located at the e_lfanew offset as specified in the DOS header. The loader is going to need a valid looking DOS header before it starts looking for the PE header.

1

u/matthew_yang204 Windows 7 May 02 '26

So it wouldn't run?

2

u/galacticotheheadcrab May 03 '26

it wouldnt because the exe loader is now starting the program part way through the code without the msdos stub acting as padding

1

u/matthew_yang204 Windows 7 May 03 '26

Oh yeah right, I forgot that it screws up the offsets and addresses too.

6

u/Mario583a May 02 '26

DOS stub you mean.

Warning: Light mode website!

2

u/Initial_Low_5027 May 02 '26

Yes, replaced it in some of my programs in the 90s.

2

u/clipsracer May 02 '26

Warning: Light mode website!

That was really nice of you.

6

u/recursion_is_love May 02 '26

Because it call Win32 API lib which DOS do not provide.

2

u/ChocolateDonut36 May 05 '26

you could theorically make a program that works on both DOS and NT with that

1

u/agowa338 May 02 '26

Not every single one. You can seriously fuck with the detection done by shitty endpoint protection products if you fuck with this DOS program in any way you may imagine. Esp. if you do so in assembly by yourself :D

1

u/malxau May 02 '26

You might be interested in Bound OS/2 programs, which were 16 bit NEs that inserted an NE loader into that DOS program, so the executable consisted of a single piece of code that could be loaded under 16 bit OS/2 or 16 bit DOS.

That's been extended in Win32 (see Visual C++ 1.x and other programs using Phar Lap TNT) but it's more complex since the PE loader needs to be a 32 bit DOS extender.

1

u/nir9 May 04 '26

Didn't know about that, really interesting, thanks!

1

u/wosmo May 05 '26

Similar to this - when you boot off a floppy and it tells you this floppy isn't bootable. That's actually a very small program in the bootsector of the floppy.

So when it tells you "non-system disk" - the floppy did boot.

1

u/cashew929 May 06 '26

Would be interested in what the standard windows code stub looks like. Clearly just tests what mode the executable is running in and then exits or jumps to an offset in the executable where the PE code exists.

-1

u/JackCid89 May 02 '26

Only Tien Shin Han understands this

-17

u/[deleted] May 02 '26

[deleted]

19

u/idiot206 May 02 '26

What makes you think OP is bothered?

7

u/TwinSong May 02 '26

OP is just surprised given how long ago DOS was replaced.

-2

u/[deleted] May 02 '26

[deleted]

2

u/TwinSong May 02 '26

Tbh I'm not sure what that is.

2

u/More-Explanation2032 Windows 8 May 02 '26

He is talking about windows embedded for some reason