The file nano.exe is an attempt to generate the smallest possible CLI executable using Microsoft's tools. This was done by Arch Robison, of Intel Corporation, and a member of the ECMA technical committee working on this standard. Following is the CIL assembly source: .assembly donothing {} .method static public void main() cil managed { .entrypoint .maxstack 0 ret } The resulting nano.exe file was 2,048 bytes long when assembled with the .NET 1.0 ilasm. That's not quite the smallest size allowed by the ECMA specification. Indeed, the .NET 1.1 beta version generates 1,536 bytes. The descriptions that follow are in order of ascending offset within the file. The intent is to explain the reason for every byte in the file. This exercise uncovered multiple errors in the first edition of the ECMA specification, which were then corrected. MS-DOS Header The MS-DOS header matches that in the ECMA specification, with the lfanew field set to "0x80 00 00 00". PE File Header The PE signature is at offset 0x80, with the ECMA-documented value of "PE\0\0". The PE file header is at offset 0x84, with the values shown in Table 4-1. Table 4-1. PR File HeaderFile Offset | RVA | Field | Contents |
---|
0x84 | 0x84 | Machine | 0x14C | 0x86 | 0x86 | Number of Sections | 2 | 0x88 | 0x88 | Time/Date Stamp | 0x3C18B9D8 | 0x8C | 0x8C | Pointer to Symbol Table | 0 | 0x90 | 0x90 | Number of Symbols | 0 | 0x94 | 0x94 | Optional Header Size | 0xE0 | 0x96 | 0x96 | Characteristics | 0x10E | The PE header standard fields have the values shown in Table 4-2. Table 4-2. Standard Fields in the PE HeaderFile Offset | RVA | Field | Contents |
---|
0x98 | 0x98 | Magic | 0x10B | 0x9A | 0x9A | LMajor | 6 | 0x9B | 0x9B | LMinor | 0 | 0x9C | 0x9C | Code Size | 0x400 | 0xA0 | 0xA0 | Initialized Data Size | 0x200 | 0xA4 | 0xA4 | Uninitialized Data Size | 0 | 0xA8 | 0xA8 | Entry Point RVA | 0x21FE | 0xAC | 0xAC | Base Of Code | 0x2000 | 0xB0 | 0xB0 | Base Of Data | 0x4000 | The PE header Windows NT specific fields have the values shown in Table 4-3. Table 4-3. Windows NT-Specific Fields in the PE HeaderFile Offset | RVA | Field | Contents |
---|
0xB4 | 0xB4 | Image Base | 0x400000 | 0xB8 | 0xB8 | Section Alignment | 0x2000 | 0xBC | 0xBC | File Alignment | 0x200 | 0xC0 | 0xC0 | OS Major | 4 | 0xC2 | 0xC2 | OS Minor | 0 | 0xC4 | 0xC4 | User Major | 0 | 0xC6 | 0xC6 | User Minor | 0 | 0xC8 | 0xC8 | SubSys Major | 4 | 0xCA | 0xCA | SubSys Minor | 0 | 0xCC | 0xCC | Reserved | 0 | 0xD0 | 0xD0 | Image Size | 0x6000 | 0xD4 | 0xD4 | Header Size | 0x200 | 0xD8 | 0xD8 | File Checksum | 0 | 0xDC | 0xDC | SubSystem | 0x3 (...CE_GUI) | 0xDE | 0xDE | DLL Flags | 0 | 0xE0 | 0xE0 | Stack Reserve Size | 0x100000 | 0xE4 | 0xE4 | Stack Commit Size | 0x1000 | 0xE8 | 0xE8 | Heap Reserve Size | 0x100000 | 0xEC | 0xEC | Heap Commit Size | 0x1000 | 0xF0 | 0xF0 | Loader Flags | 0 | 0xF4 | 0xF4 | Number of Data Directories | 0x10 | The PE header data directories are shown in Table 4-4. Table 4-4. PE Header Data DirectoriesFile Offset | RVA | Field | Contents |
---|
0xF8 | 0xF8 | Export Table | 0 | 0x100 | 0x100 | Import Table | RVA=0x21A8 Size=0x53 | 0x108 | 0x108 | Resource Table | 0 | 0x110 | 0x110 | Exception Table | 0 | 0x118 | 0x118 | Certificate Table | 0 | 0x120 | 0x120 | Base Relocation Table | RVA=0x4000 Size=0xC | 0x128 | 0x128 | Debug | 0 | 0x130 | 0x130 | Copyright | 0 | 0x138 | 0x138 | Global Ptr | 0 | 0x140 | 0x140 | TLS Table | 0 | 0x148 | 0x148 | Load Config Table | 0 | 0x150 | 0x150 | Bound Import | 0 | 0x158 | 0x158 | IAT | RVA=0x2000 Size=8 | 0x160 | 0x160 | Delay Import Descriptor | 0 | 0x168 | 0x168 | CLI Header | RVA=0x2008 Size=0x48 | 0x170 | 0x170 | Reserved | 0 | PE Section Headers The section table contains two sections. The first part is shown in Table 4-5; the second, in Table 4-6. Table 4-5. PE Section Headers: Part 1File Offset | RVA | Field | Contents |
---|
0x178 | 0x178 | Name | ".text" | 0x180 | 0x180 | VirtualSize | 0x204 | 0x184 | 0x184 | VirtualAddress | 0x2000 | 0x188 | 0x188 | SizeOfRawData | 0x400 | 0x18A | 0x18A | PointerToRawData | 0x200 | 0x18E | 0x18E | PointerToRelocations | 0 | 0x192 | 0x192 | PointerToLinenumbers | 0 | 0x196 | 0x196 | NumberOfRelocations | 0 | 0x198 | 0x198 | NumerOfLinenumbers | 0 | 0x19A | 0x19A | Characteristics | 0x60020 (...CODE, ...EXECUTE, ...READ) | Table 4-6. PE Section Headers: Part 2File Offset | RVA | Field | Contents |
---|
0x1A0 | 0x1A0 | Name | ".reloc" | 0x1A8 | 0x1A8 | VirtualSize | 0xC | 0x1AC | 0x1AC | VirtualAddress | 0x4000 | 0x1B0 | 0x1B0 | SizeOfRawData | 0x200 | 0x1B4 | 0x1B4 | PointerToRawData | 0x600 | 0x1B8 | 0x1B8 | PointerToRelocations | 0 | 0x1BC | 0x1BC | PointerToLinenumbers | 0 | 0x1C0 | 0x1C0 | NumberOfRelocations | 0 | 0x1C2 | 0x1C2 | NumerOfLinenumbers | 0 | 0x1C4 | 0x1C4 | Characteristics | 0x42000040 (...INITIALIZED_DATA, ?, ...READ) | The section header tables are followed by zero-fill all the way up to and including offset 0x1FF. Import Address Table From file offset 0x200 to 0x400 is the .text section. It starts with the Import Address Table (Table 4-7) at 0x200. Note that the beginning RVA for this section is 0x2000, as specified by the VirtualAddress field of the .text section (at file offset 0x184) in the the section table (see Table 4-5). Table 4-7. Import Address TableFile Offset | RVA | Field | Contents |
---|
0x200 | 0x2000 | Hint/Name Table RVA | 0x21E0 | 0x204 | 0x2004 | (Zero-fill for two bytes) | 0 | 0x206 | 0x2006 | 2 zeros for sake of alignment | 0 | CLI Header The CLI header (Table 4-8) immediately follows at 0x208 (which agrees with the corresponding entry in the data directory): Table 4-8. CLI HeaderFile Offset | RVA | Field | Contents |
---|
0x208 | 0x2008 | Cb | 0x48 | 0x20C | 0x200C | MajorRuntimeVersion | 2 | 0x20E | 0x200E | MinorRuntimeVersion | 0 | 0x210 | 0x2010 | MetaData | 0x2060 | 0x214 | 0x2014 | Size of the Metadata | 0x148 =(RVA of Import Table) (RVA of MetaData) | 0x218 | 0x2018 | Flags | 1 | 0x218 | 0x201C | EntryPointToken | 0x06000001 (Method #1 in TypeDef table) | 0x220 | 0x2020 | Resources | 0 | 0x228 | 0x2028 | StrongNameSignature | 0 | 0x230 | 0x2030 | CodeManagerTable | 0 | 0x238 | 0x2038 | VTableFixups | 0 | 0x240 | 0x2040 | ExportAddressTableJumps | 0 | 0x248 | 0x2048 | ManagedNativeHeader | 0 | Method Header and CIL Immediately following the CLI header is a fat method header (Table 4-9) and CIL instructions: Table 4-9. Method HeaderFile Offset | RVA | Field | Contents |
---|
0x250 | 0x2050 | Flags (lower 12 bits) | 0x013 ...Fat, ...InitLocals | +12 bits | | Size (upper 4 bits) | 0x3 | 0x252 | 0x2052 | MaxStack | 0 | 0x254 | 0x2054 | CodeSize | 0x1 | 0x258 | 0x2058 | LocalVarSigTok | 0 | 0x25C | 0x205C | CIL "ret" instruction | 0x2A | 0x25D | 0x205D | Zero fill for sake of alignment | 0 | Metadata Root Next comes the metadata root (Table 4-10). Table 4-10. Metadata RootFile Offset | RVA | Field | Contents |
---|
0x260 | 0x2060 | Signature | 0x424A5342 | 0x264 | 0x2064 | MajorVersion | 1 | 0x266 | 0x2066 | MinorVersion | 1 | 0x268 | 0x2068 | Reserved | 0 | 0x26C | 0x206C | Length (of version string) | 0xC | 0x270 | 0x2070 | Version | "1.0.2914" | 0x27C | 0x207C | Flags | 0 | 0x27D | 0x207D | Streams | 4 | Metadata Stream Headers The four stream headers (see Table 4-11) immediately follow. Table 4-11. Metadata Stream HeadersFile Offset | RVA | Field | Contents |
---|
0x280 | 0x2080 | Offset | 0x60 | 0x284 | 0x2084 | Size | 0x68 | 0x286 | 0x2086 | Name | "#~\0\0" | File Offset | RVA | Field | Contents |
---|
0x28C | 0x208C | Offset | 0xC8 | 0x290 | 0x2090 | Size | 0x68 | 0x294 | 0x2094 | Name | "#Strings\0\0\0\0" | File Offset | RVA | Field | Contents |
---|
0x2A0 | 0x20A0 | Offset | 0x130 | 0x2A4 | 0x20A4 | Size | 0x10 | 0x2A8 | 0x20A8 | Name | "#GUID\0\0\0" | File Offset | RVA | Field | Contents |
---|
0x2B0 | 0x20B0 | Offset | 0x140 | 0x2B4 | 0x20B4 | Size | 0x8 | 0x2B8 | 0x20B8 | Name | "#Blob" | The #~ Stream The data for the #~ stream (Table 4-12) immediately follows. Table 4-12. The #~ StreamFile Offset | RVA | Field | Contents |
---|
0x2C0 | 20C0 | Reserved | 0 | 0x2C4 | 20C4 | MajorVersion | 1 | 0x2C5 | 20C5 | MinorVersion | 0 | 0x2C6 | 20C6 | HeapSizes | 0 (all heap indices are 16 bits wide) | 0x2C7 | 20C7 | Reserved | 1 | 0x2C8 | 20C8 | Valid | 0x100000045 (Assembly, Method, TypeDef, Module) | 0x2D0 | 20D0 | Sorted | 0x2003301FA00 | 0x2D8 | 20D8 | Rows [Module] | 1 | 0x2DC | 20DC | Rows [TypeDef] | 1 | 0x2E0 | 20E0 | Rows [Method] | 1 | 0x2E4 | 20E4 | Rows [Assembly] | 1 | The Module table (Table 4-13) immediately follows. Table 4-13. Module TableFile Offset | RVA | Field | Contents |
---|
0x2E8 | 20E8 | Generation | 0 | 0x2EA | 20EA | Name | 0x4D | 0x2EC | 20EC | Mvid | 1 | 0x2EE | 20EE | EncId | 0 | 0x2F0 | 20F0 | EncBaseId | 0 | The TypeDef table (Table 4-14) immediately follows. Table 4-14. TypeDef TableFile Offset | RVA | Field | Contents |
---|
0x2F2 | 0x20F2 | Flags | 0 | 0x2F4 | 0x20F4 | Name | 0x44 | 0x2F6 | 0x20F6 | Namespace | 0 | 0x2F8 | 0x20F8 | Extends | 0 | 0x2FA | 0x20FA | FieldList | 1 | 0x2FC | 0x20FC | MethodList | 1 | The Method table (Table 4-15) immediately follows. Table 4-15. Method TableFile Offset | RVA | Field | Contents |
---|
0x300 | 0x2100 | RVA | 0x2050 | 0x304 | 0x2104 | ImpFlags | 0x44 | 0x306 | 0x2106 | Flags | 0x0016 | 0x308 | 0x2108 | Name | 0x60 | 0x30A | 0x210A | Signature | 1 | +12 | 0210C | ParamList | 1 | The Assembly table (Table 4-16) immediately follows. Table 4-16. Assembly TableFile Offset | RVA | Field | Contents |
---|
0x30E | 0x210E | HashAlgId | 0 | 0x312 | 0x2112 | MajorVersion | 0 | 0x314 | 0x2114 | MinorVersion | 0 | 0x316 | 0x2116 | BuildNumber | 0 | 0x318 | 0x2118 | RevisionNumber | 0 | 0x31A | 0x211A | Flags | 0 | 0x31E | 0x211E | PublicKey | 0 | 0x320 | 0x2120 | Name | 0x56 (index of string "donothing") | 0x322 | 0x2122 | Culture | 0 | String Heap The String heap (Table 4-17) follows. Table 4-17. String HeapFile Offset | RVA | String |
---|
0x328 | 0x2128 | "\0" | 0x329 | 0x2129 | "Version of runtime against which the binary is built : 1.0.2914.16\0" | 0x36C | 0x216C | "<Module>" | 0x375 | 0x2175 | "nano.exe" | 0x37E | 0x217E | "donothing" | 0x388 | 0x2188 | "main" | 0x38D | 0x218D | 0 fill for 3 bytes | Guid Heap Next comes the Guid heap (Table 4-18). Table 4-18. Guid HeapFile Offset | RVA | GUID |
---|
0x390 | 0x2190 | 0xE22D90FB 0x446C1C25 0x5D9D0B95 0x4D6C243E | Blob Heap Immediately afterward is the Blob heap (Table 4-19). Table 4-19. Blob HeapFile Offset | RVA | Blob |
---|
0x3A0 | 0x21A0 | {} | 0x3A1 | 0x21A1 | {0,0,1} | 0x3A5 | 0x21A5 | 0 fill for 3 bytes | At this point we are at the end of the metadata. Import Table We have reached the Import Table (Partition II, section 24.3.1) pointed to by the data directory. The Import Table's contents are as shown in Table 4-20. Table 4-20. Import TableFile Offset | RVA | Field | Contents |
---|
0x3A8 | 0x21A8 | ImportLookupTable | 0x21D0 | 0x3A8 | 0x21AC | DateTimeStamp | 0 | 0x3B0 | 0x21B0 | ForwarderChain | 0 | 0x3B4 | 0x21B4 | Name | 0x21EE | 0x3B8 | 0x21B8 | ImportAddressTable | 0x2000 | 0x3BC | 0x21BC | Filled with twenty zeros | | Immedately following is the Import Lookup Table (Table 4-21). Table 4-21. Import Lookup TableFile Offset | RVA | Field | Contents |
---|
0x3D0 | 0x21D0 | Hint/Name Table RVA | 0x21E0 | 0x3D4 | 0x21D4 | Filled with two zeros | 0 | 0x3DA | 0x21DA | Undocumented | Ten zeros not documented in II.24.3.1 | Following the Import Lookup Table is the Hint/Name table (Table 4-22) and some miscellanea. Table 4-22. Hint/Name TableFile Offset | RVA | Field | Contents |
---|
0x3E0 | 0x21E0 | Hint | 0x21E0 | 0x3E2 | 0x21E2 | Name | "_CorExeMain\0" | 0x3EE | 0x21EE | | "mscoree.dll\0" | 0x3FA | 0x21FA | 4 undocumented zero bytes | | 0x3FE | 0x21FE | Entry point per II.24.2.3.1 | 0xFF 0x25 | 0x400 | 0x2200 | RVA that must follow | 0x402000 | Relocation Section The rest of the file is zeros until we reach offset 0x600, which is the raw data for the relocation section (.reloc) (see Table 4-23). Table 4-23. Raw Data for RelocationFile Offset | RVA | Field | Contents |
---|
0x600 | 0x4000 | RVA for page | 0x2000 | 0x604 | 0x4004 | Size of this structure | 0xC | 0x608 | 0x4008 | Relocation | 0x3200 IMAGE_REL_BASED_HIGHLOW at 0x200 | 0x60A | 0x400A | Placeholder | 0 | This is a request for the loader to add a fix-up delta to the RVA following the "0xFF 0x25" stub. The rest of the file is zeros, up to the last offset of 0x7FF. |