An NE file opens with an MS-DOS program, so that DOS can run it and say it needs Windows. Its 64-byte DOS header ends with the offset of the NE header, which describes the module and says where every table is. All numbers are little-endian. Offsets in the NE header count from the start of the NE header, except where this page says “file offset”.
The DOS header
The first 64 bytes are an MS-DOS header with the extended fields of Windows and OS/2
programs. DOS reads the first 28 bytes to run the stub; e_lfanew at the
end leads to the NE header.
| Offset | Size | Field | Meaning |
|---|---|---|---|
0x00 | 2 | e_magic | MZ. |
0x02 | 2 | e_cblp | Bytes in the last 512-byte page of the DOS program; 0 means the whole page. |
0x04 | 2 | e_cp | 512-byte pages the DOS program fills, counting the last, partial one. |
0x06 | 2 | e_crlc | Entries in the DOS relocation table; usually 0. |
0x08 | 2 | e_cparhdr | The size of the DOS header in 16-byte paragraphs; the stub starts after it. |
0x0A | 2 | e_minalloc | Extra paragraphs the stub needs. |
0x0C | 2 | e_maxalloc | Extra paragraphs it would like. |
0x0E | 2 | e_ss | The stub’s initial SS, relative to its start. |
0x10 | 2 | e_sp | The stub’s initial SP. |
0x12 | 2 | e_csum | A checksum; usually 0. |
0x14 | 2 | e_ip | The stub’s initial IP. |
0x16 | 2 | e_cs | The stub’s initial CS, relative to its start. |
0x18 | 2 | e_lfarlc | The file offset of the DOS relocation table; 0x40 or more in Windows programs. |
0x1A | 2 | e_ovno | The overlay number; 0. |
0x1C | 8 | e_res | Reserved. |
0x24 | 2 | e_oemid | An OEM identifier; usually 0. |
0x26 | 2 | e_oeminfo | OEM information; usually 0. |
0x28 | 20 | e_res2 | Reserved. |
0x3C | 4 | e_lfanew | The file offset of the NE header. |
The fields are those of an MS-DOS header,
but here they describe only the stub. Linkers often give the stub’s size as most of the
file: in TEST1.EXE, below, e_cp and e_cblp come to 14,080
bytes of a 14,336-byte file. The explorer ends the stub where the NE header begins.
e_lfanew
The 32-bit word at 0x3C is the file offset of the NE header, which comes
after the stub: 0x100 in TEST1.EXE, 0x400 in WINMINE.EXE. If the two bytes there are not
NE, or the header’s tables do not fit in the file, the file is not taken
as NE; see Telling it apart.
The DOS stub
The stub is the MS-DOS program after the DOS header. It usually prints “This program requires Microsoft Windows.” or “This program must be run under Microsoft Windows.” and exits with code 1. Stubs differ from linker to linker; this is TEST1.EXE’s:
BA 10 00 mov dx, 0x0010 ; the message, 16 bytes in
0E push cs
1F pop ds
B4 09 mov ah, 9 ; DOS: print a string ending in $
CD 21 int 21h
B8 01 4C mov ax, 0x4C01 ; DOS: exit with code 1
CD 21 int 21h
INT 21h is described under INT in the x86 instruction reference. Minesweeper’s starts with a near call instead (E8 53 00), with the message straight after it.The explorer shows the stub as a DOS program, with its message, and disassembles it in
the Code section. It names the bytes between the end of the DOS header’s fields and the
end of the header “Rest of the DOS header”, and any bytes between the end of the stub,
as its header gives its size, and the NE header “After the DOS stub”. Some stubs run on
past their stated size, so their message ends there. If e_crlc is not 0,
the stub’s relocation table is shown too.
The NE header
The NE header is 64 bytes:
| Offset | Size | Field | Meaning |
|---|---|---|---|
0x00 | 2 | ne_magic | NE. |
0x02 | 1 | ne_ver | The linker’s major version. |
0x03 | 1 | ne_rev | The linker’s minor version. |
0x04 | 2 | ne_enttab | The offset of the entry table. |
0x06 | 2 | ne_cbenttab | The size of the entry table in bytes. |
0x08 | 4 | ne_crc | A checksum of the file, or 0. |
0x0C | 2 | ne_flags | Module flags. |
0x0E | 2 | ne_autodata | The automatic data segment’s number, from 1, or 0. |
0x10 | 2 | ne_heap | The initial size of the local heap. |
0x12 | 2 | ne_stack | The initial size of the stack. |
0x14 | 4 | ne_csip | The entry point: the segment number in the high word, the offset in the low word. |
0x18 | 4 | ne_sssp | The stack: the segment number in the high word, SP in the low word. |
0x1C | 2 | ne_cseg | Entries in the segment table. |
0x1E | 2 | ne_cmod | Entries in the module reference table. |
0x20 | 2 | ne_cbnrestab | The size of the non-resident name table in bytes. |
0x22 | 2 | ne_segtab | The offset of the segment table. |
0x24 | 2 | ne_rsrctab | The offset of the resource table. |
0x26 | 2 | ne_restab | The offset of the resident name table. |
0x28 | 2 | ne_modtab | The offset of the module reference table. |
0x2A | 2 | ne_imptab | The offset of the imported names table. |
0x2C | 4 | ne_nrestab | The file offset of the non-resident name table. |
0x30 | 2 | ne_cmovent | The number of movable entry points. |
0x32 | 2 | ne_align | The alignment shift for segment offsets; 0 means 9. |
0x34 | 2 | ne_cres | The number of resource segments (OS/2). |
0x36 | 1 | ne_exetyp | The target system. |
0x37 | 1 | ne_flagsothers | More flags. |
0x38 | 2 | ne_pretthunks | OS/2: the offset of the return thunks. Windows: the start of the fast-load area. |
0x3A | 2 | ne_psegrefbytes | OS/2: the offset of the segment reference thunks. Windows: the length of the fast-load area. |
0x3C | 2 | ne_swaparea | The minimum size of the code swap area. |
0x3E | 2 | ne_expver | The Windows version the program expects. |
Signature
ne_magic is 4E 45, NE in ASCII.
Linker version
ne_ver and ne_rev are the major and minor version of the
linker that wrote the file, as two bytes: 06 01 is 6.1. Values from 5.1 to
5.60 are the most common in the files examined for this site, with 6.1 and 7.1 as well;
some files carry odd values such as 10.0 or 17.32, whose meaning is not known.
Table offsets
Six words give the offsets of the tables from the start of the NE header, and one double word gives the non-resident name table’s offset from the start of the file:
| Field | Table | Its size |
|---|---|---|
| ne_segtab | Segment table | ne_cseg × 8 bytes |
| ne_rsrctab | Resource table | up to ne_restab; equal to it when there are no resources |
| ne_restab | Resident name table | up to its terminating zero |
| ne_modtab | Module reference table | ne_cmod × 2 bytes |
| ne_imptab | Imported names table | up to ne_enttab; it has no size of its own |
| ne_enttab | Entry table | ne_cbenttab bytes |
| ne_nrestab | Non-resident name table, at a file offset | ne_cbnrestab bytes |
The tables normally follow the header in the order of this table. The explorer reads them wherever the offsets say, and notes on the header when they are in another order.
Checksum
ne_crc is a 32-bit checksum of the file. Many files have 0 here
(fileformat.info says Borland’s Turbo Pascal for Windows leaves it 0). How the value is
computed is not described here; the explorer shows it without checking it.
Module flags
ne_flags describes the module. The bits are those given by
fileformat.info; Wine names bits 0, 1 and 15 the same way.
| Bits | Meaning |
|---|---|
0x0003 | The automatic data segment: 0 none, 1 a single shared one (libraries), 2 one for each instance (programs). |
0x0004 | Global initialisation. |
0x0008 | Protected mode only. |
0x0010 | Uses 8086 instructions. |
0x0020 | Uses 80286 instructions. |
0x0040 | Uses 80386 instructions. |
0x0080 | Uses 80x87 instructions. |
0x0700 | The application type: 1 full screen (not aware of the Windows or Presentation Manager API), 2 compatible with the Windows or PM API, 3 uses the Windows or PM API. |
0x0800 | fileformat.info calls it an “OS/2 family application”; Wine calls the same bit a self-loading application. Both are given because the sources differ. |
0x2000 | Errors in the image: the linker found errors and the module cannot be loaded. |
0x4000 | A “non-conforming program”; fileformat.info gives no more. |
0x8000 | A library (a DLL or a driver). Its CS:IP points at a far initialisation routine, and SS:SP is not used. |
Automatic data segment
ne_autodata is the number, from 1, of the automatic data segment: the
segment that holds the module’s static data, its local heap and, in a program, its
stack. 0 means there is none.
Heap and stack
ne_heap and ne_stack are the initial sizes, in bytes, of the
local heap and of the stack. Both are added to the automatic data segment when it is
loaded. A library does not use ne_sssp: it runs on the stack of the
program that calls it.
Entry point and stack
ne_csip gives where the module starts: the segment number, from 1, in the
high word, and the offset in the low word. In a library it is the initialisation
routine, or 0 when there is none. ne_sssp gives the initial stack in the
same way; a segment number of 0 means the automatic data segment. Both refer to
segment numbers, not addresses, since Windows chooses where each segment goes.
Counts
ne_cseg: the entries in the segment table, resource segments included in OS/2 files;ne_cmod: the entries in the module reference table;ne_cbnrestab: the size in bytes of the non-resident name table;ne_cmovent: the number of entries in movable segments in the entry table.
Alignment shift
ne_align sets the unit, a power of two, in which the segment table gives
where each segment’s data starts: a segment at sector n is at file offset
n × 2ne_align. 0 means 9, 512-byte units. WINMINE.EXE uses 4
(16 bytes) and TEST1.EXE 8 (256 bytes). A shift above 24 cannot be right, and the explorer does
not take such a file as NE. The resource table has its own shift.
Resource segments
ne_cres is the number of resource segments. OS/2 1.x keeps each resource
as a segment, the last ne_cres of the segment table; Windows files have 0
here and a resource table instead. See
The resource table.
Target system
| ne_exetyp | System |
|---|---|
| 0 | Unknown |
| 1 | OS/2 |
| 2 | Windows |
| 3 | European MS-DOS 4.x |
| 4 | Windows 386 |
| 5 | BOSS (Borland Operating System Services) |
Files with 1, 2, 5 and 0 were among those examined for this site; most are Windows files.
Other flags
| Bit | Meaning |
|---|---|
0x01 | Supports long file names. |
0x02 | A Windows 2.x program that runs in protected mode. |
0x04 | A Windows 2.x program that uses proportional fonts. |
0x08 | Has a fast-load area; see below. |
Fast-load area and thunks
The words at 0x38 and 0x3A mean different things to the two
systems. In OS/2 files they are the offsets of the return thunks and of the segment
reference thunks. In Windows files they give the fast-load (gangload) area: a run of
the file that holds the segments and resources Windows loads at start, so that it can
read them in one go. The first word is where the area starts and the second its length,
both in sectors of 2ne_align bytes. Bit 3 of
ne_flagsothers says the area is there.
In WINMINE.EXE, Microsoft’s “Minesweeper v3.1”, the flag is set, the shift is 4, and the
two words are 0x0060 and 0x037C: the area runs from
0x600 for 0x37C0 bytes, to 0x3DC0, and holds
both of its segments. Its first resource starts at 0x3DC0.
Code swap area
ne_swaparea is the minimum size of the code swap area. It is 0 in both
examples on this page.
Expected Windows version
ne_expver is the Windows version the program was built for, the major
version in the high byte and the minor in the low one: stored 0A 03, it is
0x030A, Windows 3.10; 00 03 is 3.00.
Data after the module
Bytes after the last segment and resource are not loaded. Debuggers, installers and self-extracting archives keep data here. The explorer shows them as “Data after the module”, and names what they hold when it recognises the signature, in the same way as for an MS-DOS program’s overlay: a ZIP or CAB archive, CodeView or Turbo Debugger information, and so on. A run of zero bytes is reported as such.
Worked example
TEST1.EXE, a small Windows 3.0 program of 14,336 bytes. Its DOS header gives
e_lfanew 0x100:
00000000 4D 5A 00 01 1C 00 00 00 08 00 00 00 FF FF 08 00 MZ..............
00000010 00 01 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 00 01 00 00 ................
…
00000080 BA 10 00 0E 1F B4 09 CD 21 B8 01 4C CD 21 90 90 ........!..L.!..
00000090 54 68 69 73 20 70 72 6F 67 72 61 6D 20 72 65 71 This program req
000000A0 75 69 72 65 73 20 4D 69 63 72 6F 73 6F 66 74 20 uires Microsoft
000000B0 57 69 6E 64 6F 77 73 2E 0D 0A 24 20 20 20 20 20 Windows...$
e_cparhdr 8), e_lfanew at 0x3C, and the stub at 0x80, padded with spaces to 0x10000000100 4E 45 06 01 9F 00 11 01 00 00 00 00 02 03 04 00 NE..............
00000110 00 20 00 20 5D 03 01 00 00 00 04 00 04 00 06 00 . . ]...........
00000120 05 00 40 00 60 00 64 00 6D 00 79 00 B0 02 00 00 ..@.`.d.m.y.....
00000130 2D 00 08 00 00 00 02 00 00 00 00 00 00 00 00 03 -...............
0x100| Field | Bytes | Value |
|---|---|---|
| ne_magic | 4E 45 | NE |
| ne_ver, ne_rev | 06 01 | linker 6.1 |
| ne_enttab, ne_cbenttab | 9F 00 11 01 | entry table at +0x9F (file 0x19F), 273 bytes |
| ne_crc | 00 00 00 00 | 0 |
| ne_flags | 02 03 | 0x0302: an automatic data segment for each instance; uses the Windows API |
| ne_autodata | 04 00 | segment 4 |
| ne_heap, ne_stack | 00 20 00 20 | 8,192 bytes each |
| ne_csip | 5D 03 01 00 | segment 1, offset 0x035D |
| ne_sssp | 00 00 04 00 | segment 4, SP 0 |
| ne_cseg, ne_cmod, ne_cbnrestab | 04 00 06 00 05 00 | 4 segments, 6 module references, 5 bytes of non-resident names |
| ne_segtab … ne_imptab | 40 00 60 00 64 00 6D 00 79 00 | tables at +0x40, +0x60, +0x64, +0x6D, +0x79 |
| ne_nrestab | B0 02 00 00 | file offset 0x2B0 |
| ne_cmovent | 2D 00 | 45 movable entries |
| ne_align | 08 00 | 256-byte sectors |
| ne_cres | 00 00 | no resource segments |
| ne_exetyp, ne_flagsothers | 02 00 | Windows; no other flags |
| ne_pretthunks … ne_swaparea | 00 00 00 00 00 00 | no fast-load area, no swap area |
| ne_expver | 00 03 | Windows 3.00 |