D:\Source\HelloWorld\CommandLine>PrintBinaryFile.exe HelloWorld_CSC_2.0.exe
00000000 4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00 MZ?·········ÿÿ··
00000010 B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ,·······@·······
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ················
00000030 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 ············?···
00000040 0E 1F BA 0E 00 B4 09 CD 21 B8 01 4C CD 21 54 68 ··º··'·I!,·LI!Th
00000050 69 73 20 70 72 6F 67 72 61 6D 20 63 61 6E 6E 6F is program canno
00000060 74 20 62 65 20 72 75 6E 20 69 6E 20 44 4F 53 20 t be run in DOS
00000070 6D 6F 64 65 2E 0D 0D 0A 24 00 00 00 00 00 00 00 mode.···$·······
00000080 50 45 00 00 4C 01 03 00 58 5B 6C 58 00 00 00 00 PE··L···X[lX····
00000090 00 00 00 00 E0 00 02 01 0B 01 08 00 00 04 00 00 ····à···········
000000A0 00 06 00 00 00 00 00 00 AE 23 00 00 00 20 00 00 ········r#··· ··
000000B0 00 40 00 00 00 00 40 00 00 20 00 00 00 02 00 00 ·@····@·· ······
000000C0 04 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 ················
000000D0 00 80 00 00 00 02 00 00 00 00 00 00 03 00 40 85 ·?············@?
000000E0 00 00 10 00 00 10 00 00 00 00 10 00 00 10 00 00 ················
000000F0 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 ················
So, what is all this mess? Well, looking in the Microsoft Portable Executable and Common ObjectFile Format Specification: Revision 10 (also known as the MS PE COFF spec) I see that the file is laid out starting with an MS-DOS 2.0 Compatible EXE Header as shown below in Figure 1 (from the doc):
MS-DOS 2.0 Compatible
EXE Header
|
Base of Image Header
|
|
unused
|
||
OEM Identifier
OEM Information
Offset to
PE Header |
MS‑DOS 2.0 Section
(for MS‑DOS compatibility only) |
|
MS‑DOS 2.0 Stub Program
and
Relocation Table
|
||
unused
|
||
PE Header
(aligned on 8-byte
boundary)
|
||
Section Headers
|
||
Image Pages:
import info
export info
base relocations
resource info
|
Figure 1. Typical Portable EXE File Layout
So this must be the header. What does it do? Again, from the document, the MS-DOS stub is just there to detect if it is running in DOS mode and print out "This program cannot be run in DOS mode" if it is. It ends at 0x3b (that is the HEX address for the 59th byte) and at 0x3C the offset to the PE signature (the 0x80 on the 4th line).
Skipping down to the offset for the PE signature (the 9th line, starting with 00000080) we find the PE (0x50 0x45 0x00 0x00) that the definition tells us to expect. Examining all three images, we are 100% identical to here. Not surprising, as it is all defined for us in the MS PE COFF spec. After that, we begin to diverge.
The next portion is the COFF File Header. Let's lay all three out so that we can compare them and then I will disect the lines:
MS Command Line, .NET 2.0:
00000080 50 45 00 00 4C 01 03 00 58 5B 6C 58 00 00 00 00 PE··L···X[lX····
VS 2015, .NET 2.0
00000080 50 45 00 00 4C 01 03 00 43 5B 6C 58 00 00 00 00 PE··L···C[lX····
Xamarin Studio, .NET 2.0
00000080 50 45 00 00 4C 01 03 00 00 00 00 00 00 00 00 00 PE··L···········
Ok, the COFF file header is defined as:
Offset
|
Size
|
Field
|
Description
|
0
|
2
|
Machine
|
The
number that identifies the type of target machine. For more information, see section
3.3.1, “Machine Types.”
|
2
|
2
|
NumberOfSections
|
The
number of sections. This indicates the size of the section table, which immediately
follows the headers.
|
4
|
4
|
TimeDateStamp
|
The
low 32 bits of the number of seconds since 00:00 January 1, 1970 (a C run-time
time_t value), that indicates when the file was created.
|
8
|
4
|
PointerToSymbolTable
|
The
file offset of the COFF symbol table, or zero if no COFF symbol table is
present. This value should be zero for an image because COFF debugging
information is deprecated.
|
12
|
4
|
NumberOfSymbols
|
The
number of entries in the symbol table. This data can be used to locate the
string table, which immediately follows the symbol table. This value should
be zero for an image because COFF debugging information is deprecated.
|
16
|
2
|
SizeOfOptionalHeader
|
The
size of the optional header, which is required for executable files but not
for object files. This value should be zero for an object file. For a
description of the header format, see section 3.4, “Optional Header (Image
Only).”
|
18
|
2
|
Characteristics
|
The
flags that indicate the attributes of the file. For specific flag values, see
section 3.3.2, “Characteristics.”
|
and starts just after the PE signature. All three have 0x4C 0x01 which is 0x014C when we see that it is a two byte halfword and represents the Machine type. If we look in the "Machine Types" table we find:
IMAGE_FILE_MACHINE_I386
|
0x14c
|
Intel
386 or later processors and compatible processors
|
which makes sense.
The next halfword is 0x03 0x00 or 0x0003 which is the number of sections the file has. The specification limits us to 96 (or 0x60) sections, so we are only using a fraction of what is available.
The next part is the TimeDateStamp which is the time that the file was created. Not a shocker that the first two are different, but it is somewhat odd that the Xamarin file leaves this section blank.
The last part of this line is the PointerToSymbolTable which is an offset to the COFF symbol table or zero (as it is in all three) if there is no symbol table present. The spec mentions that this should always be zero because COFF debugging is deprecated.
Since I am sure that you are on the edge of your seat excited to finish off the COFF header, I will put the next lines:
MS Command Line, .NET 2.0:
00000090 00 00 00 00 E0 00 02 01 0B 01 08 00 00 04 00 00 ····à···········
VS 2015, .NET 2.0
00000090 00 00 00 00 E0 00 22 00 0B 01 30 00 00 08 00 00 ····à·"···0·····
Xamarin Studio, .NET 2.0
00000090 00 00 00 00 E0 00 02 01 0B 01 08 00 00 06 00 00 ····à···········
We are only going to be looking at the first half, which begins with the NumberOfSymbols in the deprecated symbol table, so again, all 0s.
The next part is 0xE0 0x00 or 0x00E0 which is the SizeOfOptionalHeader. All three have 244 bytes.
The last part we will look at is the Characteristics which is a flag lookup to another table. The Command Line and Xamarin versions both have 0x02 0x01 or 0x102 which gives us:
IMAGE_FILE_32BIT_MACHINE
|
0x0100
|
Machine
is based on a 32-bit-word architecture.
|
IMAGE_FILE_EXECUTABLE_IMAGE
|
0x0002
|
Image
only. This indicates that the image file is valid and can be run. If this
flag is not set, it indicates a linker error.
|
Not a surprise. Let's see what we get when we lookup the 0x22 0x00 or 0x0022 for the Visual Studio 2015 version:
IMAGE_FILE_LARGE_ADDRESS_
AWARE
|
0x0020
|
Application
can handle > 2‑GB addresses.
|
IMAGE_FILE_EXECUTABLE_IMAGE
|
0x0002
|
Image
only. This indicates that the image file is valid and can be run. If this
flag is not set, it indicates a linker error.
|
Note that it doesn't say that it is for a 32 bit machine? Maybe it is because the VS 2015 version is Any CPU?
![]() |
JustDecompile Assembly Information for the HelloWorld_VS_2.0 file |
No, the Windows Command line compiled image is also listed as Any CPU:
At this point I don't know why they are different. I will look into it, unless someone can clue me in in the comments section.
Thanks for reading! We will dig deeper into the files and see what else we can find next time.