VOGONS


Handling of zero-code EXE files

Topic actions

First post, by Noctumus

User metadata
Rank Newbie
Rank
Newbie

I don't know if this issue has been posted on the forum before, if it's even a bug, or if it's something you actually care about, but I thought I would at least make you aware of it.

If you create a DOS EXE file containing only the MZ header (that is, a file without any executable code) like the following (which, at least in my own opinion is in fact a valid EXE file), DOSBox crashes.

4D 5A 40 00 01 00 00 00 04 00 00 00 FF FF 00 00 MZ@.............
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 ................
e_magic     : 0x4D5A     // file id ("MZ")
e_cblp : 0x0040 // bytes on the last page in the file
e_cp : 0x0001 // total number of pages in the file
e_crlc : 0x0000 // number of relocations
e_cparhdr : 0x0004 // size of header, in paragraphs
e_minalloc : 0x0000 // minimum number of extra paragraphs needed
e_maxalloc : 0xFFFF // maximum number of extra paragraphs needed
e_ss : 0x0000 // initial value of stack segment register (SS)
e_sp : 0x0000 // initial value of stack pointer register (SP)
e_csum : 0x0000 // checksum
e_ip : 0x0000 // initial value of instruction pointer register (IP)
e_cs : 0x0000 // initial value of code segment register (CS)
e_lfarlc : 0x0000 // file address of relocation table
e_ovno : 0x0000 // overlay number (0x0000 = root)
e_res1_01 : 0x0000 // reserved
e_res1_02 : 0x0000 // reserved
e_res1_03 : 0x0000 // reserved
e_res1_04 : 0x0000 // reserved
e_oemid : 0x0000 // oem id
e_oeminfo : 0x0000 // oem info
e_res2_01 : 0x0000 // reserved
e_res2_02 : 0x0000 // reserved
e_res2_03 : 0x0000 // reserved
e_res2_04 : 0x0000 // reserved
e_res2_05 : 0x0000 // reserved
e_res2_06 : 0x0000 // reserved
e_res2_07 : 0x0000 // reserved
e_res2_08 : 0x0000 // reserved
e_res2_09 : 0x0000 // reserved
e_res2_10 : 0x0000 // reserved
e_lfanew : 0x00000040 // file offset to PE header

I've experimented with different values in the header fields, but it seems to crash no matter what when the total file size (in this case simply the value of 'e_cblp' since there's only one page in the file) is the same as the size of the header (e_cparhdr*16).

Reply 3 of 26, by Scali

User metadata
Rank l33t
Rank
l33t

Yup, when the EXE starts, it just jumps to the entrypoint address that is set to the MZ header.
In this case you set it to 0000:0000.
Does that work on a real DOS machine? I don't think so.
So unless you verified it to work on real hardware/real DOS, then I don't see what DOSBox would be doing wrong.

http://scalibq.wordpress.com/just-keeping-it- … ro-programming/

Reply 6 of 26, by Noctumus

User metadata
Rank Newbie
Rank
Newbie
Scali wrote:
Yup, when the EXE starts, it just jumps to the entrypoint address that is set to the MZ header. In this case you set it to 0000: […]
Show full quote

Yup, when the EXE starts, it just jumps to the entrypoint address that is set to the MZ header.
In this case you set it to 0000:0000.
Does that work on a real DOS machine? I don't think so.
So unless you verified it to work on real hardware/real DOS, then I don't see what DOSBox would be doing wrong.

The DOS loader will [in this case, since there's only one page] calculate the size of the code as "e_cblp - (e_cparhdr * 16)" = "40 - 40" = "0". Since there is no code in the file to execute the value of CS:IP is irrelevant. However, these values [e_cs:0, e_ip:0] are the same values used by the Microsoft Visual C++ linker, and they simply tell the loader to add 0 to the memory address that will be stored/assigned in/to the CS register, and set IP to point at the first instruction in that segment. What you wrote suggesting that it will start execution at 0000:0000 is wrong. I haven't tried it in real DOS, though, but I'm fairly sure it will handle an EXE file with no code correctly and simply exit with an appropriate exit-code generated by the loader.

Reply 7 of 26, by Scali

User metadata
Rank l33t
Rank
l33t
Noctumus wrote:

I haven't tried it in real DOS, though, but I'm fairly sure it will handle an EXE file with no code correctly and simply exit with an appropriate exit-code generated by the loader.

Well, try it first, because assumption... well, you know 😀

I think it's pretty far-fetched to assume that the DOS loader is actually robust enough to handle zero-code files, to be honest. I mean, it's 1981. Why waste any resources on a case that makes no sense whatsoever?

Last edited by Scali on 2017-05-22, 13:46. Edited 1 time in total.

http://scalibq.wordpress.com/just-keeping-it- … ro-programming/

Reply 8 of 26, by Noctumus

User metadata
Rank Newbie
Rank
Newbie
spiroyster wrote:

Not sure if a valid header means a valid executable file? Something needs to executed, null ain't going to do it o.0

Who or what documentation says that "something needs to execute"?

spiroyster wrote:

Do you not need a stack frame setup too?

Obviously not, since no code/interrupts are being executed.

Reply 9 of 26, by spiroyster

User metadata
Rank Oldbie
Rank
Oldbie
Noctumus wrote:

Microsoft Visual C++ linker

I think this will produce a different header (not DOS). "Visual" == 32-bit.

Noctumus wrote:
spiroyster wrote:

Not sure if a valid header means a valid executable file? Something needs to executed, null ain't going to do it o.0

Who or what documentation says that "something needs to execute"?

The fact that you are telling the OS that it is an executable?

Reply 10 of 26, by Noctumus

User metadata
Rank Newbie
Rank
Newbie
Scali wrote:
Noctumus wrote:

I haven't tried it in real DOS, though, but I'm fairly sure it will handle an EXE file with no code correctly and simply exit with an appropriate exit-code generated by the loader.

Well, try it first, because assumption... well, you know 😀

I think it's pretty far-fetched to assume that the DOS loader is actually robust enough to handle zero-code files, to be honest. I mean, it's 1981. Why waste any resources on a case that makes no sense whatsoever?

I agree, but living in 2017 I'm fresh out of machines running native DOS to test it on 😉

The only reason this is of interest to me is because I want my [Win32/Win64] code generator to create "correct" MZ stubs, while at the same time make them as small as possible, since they most likely will never actually run. Making a zero-code stub seems like the ideal/most elegant solution.

Reply 11 of 26, by mrau

User metadata
Rank Oldbie
Rank
Oldbie
Noctumus wrote:
Who or what documentation says that "something needs to execute"? […]
Show full quote
spiroyster wrote:

Not sure if a valid header means a valid executable file? Something needs to executed, null ain't going to do it o.0

Who or what documentation says that "something needs to execute"?

spiroyster wrote:

Do you not need a stack frame setup too?

Obviously not, since no code/interrupts are being executed.

1. already gave you that information - dos api
2. absolutely wrong logic, but the result is luckily the same

Reply 12 of 26, by Noctumus

User metadata
Rank Newbie
Rank
Newbie
spiroyster wrote:
I think this will produce a different header (not DOS). "Visual" == 32-bit. […]
Show full quote
Noctumus wrote:

Microsoft Visual C++ linker

I think this will produce a different header (not DOS). "Visual" == 32-bit.

Noctumus wrote:
spiroyster wrote:

Not sure if a valid header means a valid executable file? Something needs to executed, null ain't going to do it o.0

Who or what documentation says that "something needs to execute"?

The fact that you are telling the OS that it is an executable?

Dude ... if you don't even know that all Win32/Win64 executables contain a small DOS program at the very beginning of the file (called a DOS or MZ stub) before the actual PE/COFF header, I think you should just keep your "advice" to yourself.

Reply 13 of 26, by mrau

User metadata
Rank Oldbie
Rank
Oldbie
Noctumus wrote:

Dude ... if you don't even know that all Win32/Win64 executables contain a small DOS program at the very beginning of the file (called a DOS or MZ stub) before the actual PE/COFF header, I think you should just keep your "advice" to yourself.

its funny how you point out all the important points, but you yourself don't get them; those stubs are a good example of what works, your idea does not produce a working exe, so who is right?

Reply 14 of 26, by Scali

User metadata
Rank l33t
Rank
l33t
Noctumus wrote:

The only reason this is of interest to me is because I want my [Win32/Win64] code generator to create "correct" MZ stubs, while at the same time make them as small as possible, since they most likely will never actually run. Making a zero-code stub seems like the ideal/most elegant solution.

Does it matter, though?
Back when I did that myself, I could easily fit in some code to print a string and do a clean exit as well.
Because of the PE header, the MZ part always needs to be at least 60 bytes.
You can create a valid MZ where the code itself is before those 60 bytes (with a relocation table of size 0, and then placing the code at the relocation table's position in the MZ header).
See the article here: http://mattst88.com/programming/AssemblyProgr … ournal/issue/6/

http://scalibq.wordpress.com/just-keeping-it- … ro-programming/

Reply 15 of 26, by spiroyster

User metadata
Rank Oldbie
Rank
Oldbie
Noctumus wrote:

Dude ... if you don't even know that all Win32/Win64 executables contain a small DOS program at the very beginning of the file (called a DOS or MZ stub) before the actual PE/COFF header, I think you should just keep your "advice" to yourself.

😲 My my so they do....

My point is, you say your comparing to Visual C++ output, which does not produce valid DOS stubs by default. You can ask the linker to do this for you https://msdn.microsoft.com/en-us/library/7z0585h5.aspx, in which case it would probably create a valid header (containing valid information on an entry point), with entry point (ironically saying This program cannot be run in MS-DOS mode). Maybe you are comparing to the output of non stubbed, Win32 program and expect it to work in DOS?

I think you should just keep your "advice" to yourself.

Charming, duly noted... good luck. o.0

[EDIT:] Yes, I did not understand entirely the Stub generation in detail. I read the link I posted. o.0

Last edited by spiroyster on 2017-05-22, 15:39. Edited 1 time in total.

Reply 16 of 26, by Noctumus

User metadata
Rank Newbie
Rank
Newbie
mrau wrote:
Noctumus wrote:

Dude ... if you don't even know that all Win32/Win64 executables contain a small DOS program at the very beginning of the file (called a DOS or MZ stub) before the actual PE/COFF header, I think you should just keep your "advice" to yourself.

its funny how you point out all the important points, but you yourself don't get them; those stubs are a good example of what works, your idea does not produce a working exe, so who is right?

I'm listening ... what is it I don't get? If you can provide a link to the DOS API that specifically states that an EXE must contain some code after the header, that's all I need to know.

Scali wrote:
Does it matter, though? Back when I did that myself, I could easily fit in some code to print a string and do a clean exit as we […]
Show full quote
Noctumus wrote:

The only reason this is of interest to me is because I want my [Win32/Win64] code generator to create "correct" MZ stubs, while at the same time make them as small as possible, since they most likely will never actually run. Making a zero-code stub seems like the ideal/most elegant solution.

Does it matter, though?
Back when I did that myself, I could easily fit in some code to print a string and do a clean exit as well.
Because of the PE header, the MZ part always needs to be at least 60 bytes.
You can create a valid MZ where the code itself is before those 60 bytes (with a relocation table of size 0, and then placing the code at the relocation table's position in the MZ header).
See the article here: http://mattst88.com/programming/AssemblyProgr … ournal/issue/6/

In "winnt.h", an official header from Microsoft, they actually define the MZ header as all the first 64 bytes in the file, although much of the data after the overlay number is just unused/"reserved". Obviously the original DOS header did not contain an addres/offset to a PE-header, but I thought it would be most "correct" to allocate space for the entire header, as specified by Microsoft. However, I did actually try inserting a small program that simply exits with code 0 before the PE offset. I even wrote a "Hello, World!" program that fits exactly in the space between the original DOS header (the first 32 bytes) and the "modern" DOS header (the first 64 bytes), but eventually decided that I should probably not overwrite these "reserved" bytes with my program, just for good measure.

spiroyster wrote:
Noctumus wrote:

Dude ... if you don't even know that all Win32/Win64 executables contain a small DOS program at the very beginning of the file (called a DOS or MZ stub) before the actual PE/COFF header, I think you should just keep your "advice" to yourself.

My my so they do....My point is, you say your comparing to Visual C++ output, which does not produce valid DOS stubs by default. You can ask the linker to do this for you https://msdn.microsoft.com/en-us/library/7z0585h5.aspx, in which case it would probably create a valid header (containing valid information on an entry point), with entry point (ironically saying This program cannot be run in MS-DOS mode). Maybe you are comparing to the output of non stubbed, Win32 program and expect it to work in DOS?

The linker does actually create a valid DOS stub by default (that prints "This program cannot be run in MS-DOS mode." in the console with an AH:09/INT 21 instruction). Copy any EXE file that is compiled with VC++ (like "notepad.exe", for example) into a mounted DOSBox folder and run it, and you can see for yourself.

I think you should just keep your "advice" to yourself.

Charming, duly noted... good luck. o.0

Thanks 😀

Reply 17 of 26, by mrau

User metadata
Rank Oldbie
Rank
Oldbie
Noctumus wrote:

I'm listening ... what is it I don't get? If you can provide a link to the DOS API that specifically states that an EXE must contain some code after the header, that's all I need to know.

already told you, read the conversation again

Reply 18 of 26, by Noctumus

User metadata
Rank Newbie
Rank
Newbie
mrau wrote:
Noctumus wrote:

I'm listening ... what is it I don't get? If you can provide a link to the DOS API that specifically states that an EXE must contain some code after the header, that's all I need to know.

already told you, read the conversation again

  • seems rather correct, you did not signal end of the program to dos;
  • i think that you are not getting the whole point
  • 1. already gave you that information - dos api
    2. absolutely wrong logic, but the result is luckily the same
  • its funny how you point out all the important points, but you yourself don't get them; those stubs are a good example of what works, your idea does not produce a working exe, so who is right?

This is what you have written so far. I don't see a link anywhere? As a matter of fact, reading your replies again, I'm pretty sure you don't have the faintest idea what the heck you're talking about ...

Reply 19 of 26, by mrau

User metadata
Rank Oldbie
Rank
Oldbie
Noctumus wrote:
This is what you have written so far. I don't see a link anywhere? As a matter of fact, reading your replies again, I'm pretty s […]
Show full quote
mrau wrote:
Noctumus wrote:

I'm listening ... what is it I don't get? If you can provide a link to the DOS API that specifically states that an EXE must contain some code after the header, that's all I need to know.

already told you, read the conversation again

  • seems rather correct, you did not signal end of the program to dos;
  • i think that you are not getting the whole point
  • 1. already gave you that information - dos api
    2. absolutely wrong logic, but the result is luckily the same
  • its funny how you point out all the important points, but you yourself don't get them; those stubs are a good example of what works, your idea does not produce a working exe, so who is right?

This is what you have written so far. I don't see a link anywhere? As a matter of fact, reading your replies again, I'm pretty sure you don't have the faintest idea what the heck you're talking about ...

why would i give you a link? i gave you the info;
provocation will not work, read the conversation again;