Sulaiman Chang Personal Website
Windows PE File Format Walkthrough I

Sulaiman Chang
September 9, 2004


Presented is a step by step approach to understand the windows PE file format. First, we will build a simple windows .exe executable and we will then go to analyze it byte by byte.
format PE GUI 4.0
entry start

include '%fasminc%\win32a.inc'

section '.data' data readable writeable
       msgText       db     'Message Text',0
       msgCaption    db     'Message Caption',0

section '.code' code readable executable
       start:
              invoke MessageBox,HWND_DESKTOP,msgText,msgCaption,MB_OK + MB_ICONINFORMATION
              invoke ExitProcess,0

section '.idata' import data readable
       library       KERNEL32,     'KERNEL32.DLL',\
                     USER32,       'USER32.DLL'
       
       import KERNEL32,\
              ExitProcess,         'ExitProcess'
       
       import USER32,\
              MessageBox,          'MessageBoxA'
this is the what you should see if you have assembled and executed it.



now, i dump the file in hex and construct it using DB only so we could examine it step by step. Of course, you could try copy the below hex code and assemble it using your favourite assembler.

db 0x4D,0x5A,0x80,0x00,0x01,0x00,0x00,0x00,0x04,0x00,0x10,0x00,0xFF,0xFF,0x00,0x00,\
0x40,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,\
0x0E,0x1F,0xBA,0x0E,0x00,0xB4,0x09,0xCD,0x21,0xB8,0x01,0x4C,0xCD,0x21,0x54,0x68,\
0x69,0x73,0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x20,0x63,0x61,0x6E,0x6E,0x6F,\
0x74,0x20,0x62,0x65,0x20,0x72,0x75,0x6E,0x20,0x69,0x6E,0x20,0x44,0x4F,0x53,0x20,\
0x6D,0x6F,0x64,0x65,0x2E,0x0D,0x0A,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x50,0x45,0x00,0x00,0x4C,0x01,0x03,0x00,0x9E,0xA2,0x46,0x41,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0xE0,0x00,0x8F,0x81,0x0B,0x01,0x01,0x37,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x10,0x00,0x00,0x00,0x02,0x00,0x00,\
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x40,0x00,0x00,0x00,0x02,0x00,0x00,0x46,0xA7,0x00,0x00,0x02,0x00,0x00,0x00,\
0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x30,0x00,0x00,0x90,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2E,0x64,0x61,0x74,0x61,0x00,0x00,0x00,\
0x1D,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0xC0,\
0x2E,0x63,0x6F,0x64,0x65,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x00,0x20,0x00,0x00,\
0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x60,0x2E,0x69,0x64,0x61,0x74,0x61,0x00,0x00,\
0x90,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x40,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x4D,0x65,0x73,0x73,0x61,0x67,0x65,0x20,0x54,0x65,0x78,0x74,0x00,0x4D,0x65,0x73,\
0x73,0x61,0x67,0x65,0x20,0x43,0x61,0x70,0x74,0x69,0x6F,0x6E,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x6A,0x40,0x68,0x0D,0x10,0x40,0x00,0x68,0x00,0x10,0x40,0x00,0x6A,0x00,0xFF,0x15,\
0x7A,0x30,0x40,0x00,0x6A,0x00,0xFF,0x15,0x5C,0x30,0x40,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x54,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x30,0x00,0x00,\
0x5C,0x30,0x00,0x00,0x72,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x49,0x30,0x00,0x00,0x7A,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x4B,0x45,0x52,0x4E,\
0x45,0x4C,0x33,0x32,0x2E,0x44,0x4C,0x4C,0x00,0x55,0x53,0x45,0x52,0x33,0x32,0x2E,\
0x44,0x4C,0x4C,0x00,0x64,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x30,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x45,0x78,0x69,0x74,0x50,0x72,0x6F,0x63,0x65,0x73,\
0x73,0x00,0x82,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x82,0x30,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x4D,0x65,0x73,0x73,0x61,0x67,0x65,0x42,0x6F,0x78,0x41,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
if you have copied the above code and assembled it, you would get exactly the same as what we got at first.

The first 64 bytes (0 to 63 bytes) of our PE file is occupied by a structure named "IMAGE_DOS_HEADER". Below is the "IMAGE_DOS_HEADER" structure that was declared inside "WINNT.H" file.

typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
    WORD   e_magic;                     // Magic number
    WORD   e_cblp;                      // Bytes on last page of file
    WORD   e_cp;                        // Pages in file
    WORD   e_crlc;                      // Relocations
    WORD   e_cparhdr;                   // Size of header in paragraphs
    WORD   e_minalloc;                  // Minimum extra paragraphs needed
    WORD   e_maxalloc;                  // Maximum extra paragraphs needed
    WORD   e_ss;                        // Initial (relative) SS value
    WORD   e_sp;                        // Initial SP value
    WORD   e_csum;                      // Checksum
    WORD   e_ip;                        // Initial IP value
    WORD   e_cs;                        // Initial (relative) CS value
    WORD   e_lfarlc;                    // File address of relocation table
    WORD   e_ovno;                      // Overlay number
    WORD   e_res[4];                    // Reserved words
    WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
    WORD   e_oeminfo;                   // OEM information; e_oemid specific
    WORD   e_res2[10];                  // Reserved words
    LONG   e_lfanew;                    // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
so, now we got some idea, what our first 64 bytes actually was, so we construct it again then link the rest of our code with the above "db" hex values.
IMAGE_DOS_HEADER:                         ;start : 00 (0) to 3F (63)
       .e_magic      dw     0x5A4D        ;00 01
       .e_cblp       dw     0x0080        ;02 03
       .e_cp         dw     0x0001        ;04 05
       .e_crlc       dw     0x0000        ;06 07
       .e_cparhdr    dw     0x0004        ;08 09
       .e_minalloc   dw     0x0010        ;10 11
       .e_maxalloc   dw     0xFFFF        ;12 13
       .e_ss         dw     0x0000        ;14 15
       .e_sp         dw     0x0140        ;16 17
       .e_csum       dw     0x0000        ;18 19
       .e_ip         dw     0x0000        ;20 21
       .e_cs         dw     0x0000        ;22 23
       .e_lfarlc     dw     0x0040        ;24 25
       .e_ovno       dw     0x0000        ;26 27
       .e_res        rw     4             ;28 29 | 30 31 | 32 33 | 34 35
       .e_oemid      dw     0x0000        ;36 37
       .e_oeminfo    dw     0x0000        ;38 39
       .e_res2       rw     10            ;40 41 | 42 43 | 44 45 | 46 47 | 48 49 | 50 51
       .e_lfanew     dd     0x00000080    ;52 53 | 54 55 | 56 57 | 58 59
                                          ;60 61 62 63
       
db 0x0E,0x1F,0xBA,0x0E,0x00,0xB4,0x09,0xCD,0x21,0xB8,0x01,0x4C,0xCD,0x21,0x54,0x68,\
.......... ...... ..... .... ... .. .(until the end)
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
According to most of the tutorial i read on this subject, we only need to concern on 2 members of this IMAGE_DOS_HEADER structure which are, e_magic and e_lfanew. e_magic value "4D5A" is equal to "MZ" if we translated that value into ASCII while e_lfanew value "00 00 00 80" (we need to reverse read it in order to get the value) is the pointer or address to our IMAGE_NT_HEADERS structure. Before we could move our way to IMAGE_NT_HEADERS, there exists a DOS 2.0 Stub Program which lays between them. This stub program occupies the address from 0x40 to 0x77 generally then we add 8 bytes so that we could start our IMAGE_NT_HEADERS in paragraph boundary in 0x80.

The DOS 2.0 Stub Program is a 16-bit instruction. so, we simply just use our windows provided debug program to see what are the instructions contained inside.
Microsoft Windows 2000 [Version 4.00.0000]
(C) Copyright 1985-1999 Microsoft Corp.

H:\tutorial>debug pe3.exe
-u
1597:0000 0E            PUSH    CS
1597:0001 1F            POP     DS
1597:0002 BA0E00        MOV     DX,000E
1597:0005 B409          MOV     AH,09
1597:0007 CD21          INT     21
1597:0009 B8014C        MOV     AX,4C01
1597:000C CD21          INT     21

                        OUR DATA SECTION STARTS FROM HERE
                        =================================
1597:000E 54            PUSH    SP               -> T
1597:000F 68            DB      68               -> h
1597:0010 69            DB      69               -> i
1597:0011 7320          JNB     0033             -> s (space bar)
1597:0013 7072          JO      0087             -> p r
1597:0015 6F            DB      6F               -> o
1597:0016 67            DB      67               -> g
1597:0017 7261          JB      007A             -> r a
1597:0019 6D            DB      6D               -> m
1597:001A 206361        AND     [BP+DI+61],AH    -> (space bar) c a
1597:001D 6E            DB      6E               -> n
1597:001E 6E            DB      6E               -> n
1597:001F 6F            DB      6F               -> o
159A:0020 7420          JZ      0042             -> t (space bar)
159A:0022 62            DB      62               -> b
159A:0023 65            DB      65               -> e
159A:0024 207275        AND     [BP+SI+75],DH    -> (space bar) r u
159A:0027 6E            DB      6E               -> n
159A:0028 20696E        AND     [BX+DI+6E],CH    -> (space bar) i n
159A:002B 20444F        AND     [SI+4F],AL       -> (space bar) D O
159A:002E 53            PUSH    BX               -> S
159A:002F 206D6F        AND     [DI+6F],CH       -> (space bar) m o
159A:0032 64            DB      64               -> d
159A:0033 65            DB      65               -> e
159A:0034 2E            CS:                      -> .
159A:0035 0D0A24        OR      AX,240A          -> (carriage return) (line feed) $
159A:0038 0000          ADD     [BX+SI],AL       -> 00 00
159A:003A 0000          ADD     [BX+SI],AL       -> 00 00
159A:003C 0000          ADD     [BX+SI],AL       -> 00 00
159A:003E 0000          ADD     [BX+SI],AL       -> 00 00
-
this is what we get if we unassemble our created PE file using the DEBUG program. The debug program won't unassemble our IMAGE_DOS_HEADER because it is already been replaced with PSP (Program Segment Prefix). One thing we need to concern here is our initial or start-up register values.
-r
AX=0000  BX=0000  CX=0600  DX=0000  SP=0140  BP=0000  SI=0000  DI=0000
DS=158A  ES=158A  SS=159A  CS=159A  IP=0000   NV UP EI PL NZ NA PO NC
159A:0000 0E            PUSH    CS
The loader sets our DS and ES to the address of PSP and sets the CS, IP, SS and SP to values from our IMAGE_DOS_HEADER structure. The reason we need to PUSH CS and POP DS is to initialize our DS so that it is the same as CS so that nothing wrong when we use the instruction MOV DX,000E. i guess now, we got the information to code our dos-stub program.
DOS_STUB:
       org 0                ;because our initial IP was 0
       use16                ;DOS-STUB is a 16-bit program
       push   cs
       pop    ds            ;our DS is less 100h from CS, DS received PSP address
       mov    dx,errMsg
       mov    ah,0x9
       int    0x21
       mov    ax,0x4C01
       int    0x21
       
       errMsg db     'This program cannot be run in DOS mode.',13,10,'$'
       
       org    $ + DOS_STUB  ;is equal to 0x38 + 0x40 = 0x78
       rb     0x80 - $      ;0x80 - 0x78 = rb 0x8
you might not prefer the idea to have ORG and ORG again, so how could we do it without the ORG. To code without using the ORG, we just use the address directly, example like below.
DOS_STUB:
       use16                ;DOS-STUB is a 16-bit program
       push   cs
       pop    ds            ;our DS is less 100h from CS, DS received PSP address
       mov    dx,0x0E       ;is 0x0E because our data start at 1597:000E in below unassemble
       mov    ah,0x9
       int    0x21
       mov    ax,0x4C01
       int    0x21
       
       db     'This program cannot be run in DOS mode.',13,10,'$'

       rb     0x80 - $      ;0x80 - 0x78 = rb 0x8
...
1597:000E 54            PUSH    SP              --> This program cannot be run in DOS mode.
...
i am the minimalist type, so, i choose to modify it not to initialize the DS because i use the DS for one time only in my DOS Stub Program.
DOS_STUB:                          ;start : 40 (64) to 7F (127)
       use16                       ;DOS-STUB is a 16-bit program
                                   ;push  cs <- we save 1 byte here
                                   ;pop   ds <- we save another 1 byte here
                                   ;our DS is less 100h from CS, DS received PSP address
       mov    dx,0x100 + 0x0B      ;our db message starts at 0x0B because we save 3 bytes already
       mov    ah,0x9
       int    0x21
       mov    ah,0x4C              ;save 1 byte here because we need to use AH only for function
       int    0x21
       
       db     'This program cannot be run in DOS mode.',13,10,'$'

       rb     0x80 - $             ;0x80 - 0x75 = rb 0xB
so far, we already cover the IMAGE_DOS_HEADER and DOS 2.0 STUB PROGRAM, we will continue to IMAGE_NT_HEADERS in our next walkthrough.
PE File Format (so far)
=======================
0x00 .... 0x3F ------------------------------- IMAGE_DOS_HEADER
0x40 .... 0x7F ------------------------------- DOS 2.0 Stub Program
0x80 .... ?    ------------------------------- IMAGE_NT_HEADERS
Continue to Windows PE File Format Walkthrough II
Copyright © 2004 Sulaiman Chang. All Rights Reserved.