NE-Executable | Importing modules and Procedures

This note is a next part of notes-cycle about Microsoft NE format for Windows 1.x/3.x and OS/2 1.x programs. This part contains more information about importing procedures, and modules in NE Windows executable format.

I’ve specially decided to write this article strongly after all experiments. I could have done it six months ago if I didn’t respect you. Because all my words must have proofs. And after all experiments with Microsoft LINK.EXE and Open Watcom 1.8, I’m ready to declare and determine all interesting things what I’ve found.

Remember the base

The segmented New Executable format is a first Microsoft solution which allows you to run programs in Intel 80286+ Protected Mode. Starts from Protected Mode the memory organization was the different.

All DOS executables (MZ Executables) works in Real Mode and have special memory characteristics.

For now (since 1985 year, as you know) the Microsoft Windows 1.01 was released. And the Microsoft Windows 1.01 was the second well-known source which active uses the New Executable segmented modules. (First source was a multitasking MS-DOS 4.0).

Modern terminology like an “Import” or “Export” in binary structures didn’t exist. Exporting procedures was read from .DEF file and rewrote as binary records inside the module by the linker. Exporting procedures has own name in “Resident” or “Not resident names” table, and the raw address has been in EntryTable.

This idea was fully described in previous article. In this article I want describe little more the process of 16-bit import.

Import Modules Table | Overview

The Importing Modules table have a e_imptab value in the NE header. This is a relative offset starting from NE Header.

let real_imptab: u16 = e_lfanew + e_imptab;

The Importing Modules are the strings in Resident Names table by the @0 ordinal. Name of dynamically linked library in file system may be different.

dosca11s.dll may contain DOSCALLS string by @0 and exactly this name is a module name.

The “Importing Modules Table” is a sequence of ASCII (not terminated) starting with special BYTE which tells count of bytes in the following C string.

Let’s look inside the Microsoft documentation:

The imported-name table follows the module-reference table. This table 
contains the names of modules and procedures that are imported by the 
executable file. Each entry is composed of a 1-byte field that 
contains the length of the string, followed by any number of 
characters. The strings are not null-terminated and are case 
sensitive.

The importing names table has a following format

BYTE     Length of the name string that follows. 
BYTE     ASCII text of the name string.

And let’s retranslate this structure to Rust pseudo code.

struct ImportRecord {
    pub e_cbname: u8,
    pub e_name: Vec::<u8>, // size = e_cbname
}

Great! You may think about “you can read imports now!”. But get it slow… really. Unfortunately, it’s not that simple, which is why I’m going to explain the unsaid words from the documentation.

Importing Modules Table | LINK.EXE and the Patience

I’ve specially decided to not start with Module References table just because I really want to demonstrate all ambigous Microsoft LINK.EXE behavior. If I were based on Microsoft documentation, I would never be able to read the imports correctly in my life.

First canonical thing, which I’ve found is a naming corruption. The previous article about exports tells more about this phemonemon. I’ll just tell it again. All resolved imports in the next will be UPPERCASE. And this is sad (but true). I suppose, this trouble concrete bound with exporting process. All procedures, what the application (in example CLOCK.EXE) requires, are the parts of Windows API, which store in KERNEL, GDI, USER executable modules.

As far as I know, toolkit for them was the Microsoft Macro Assembler (shorten MASM) and the Microsoft Linker. This will come back to us when we’re analyzing the binary content.

Story with the IBM OS/2 1.0+ unfortunately the same. Toolkit for Win-OS/2 middleware and the Win16 applications was made by Microsoft. (evil LINK.EXE is knocking again)

Second thing which I want to declare is an ambiugous filling of table. If you carefully read the Microsoft docs about it, you will define that names in the table follows one by one.
This is not true again!

Microsoft linker bases on another logic and this idea on the practice (if you will try to read imports) become a reason of strings tuncation or an incorrect unsafe strings (data may contains unreadable ASCII codes).

Importing Modules Table | How to see it?

Let’s look inside the binary for an answer. I represent you a specially “corrupted” variant of “Import Modules Table”.

00xx:0000 00 00 04 4D 41 53 4D 03   4E 45 53[00 00 00 00 00    | ...MASM.NLS.....
00xx:0000 00  |  | ..           |     ]06 4D  | 52 4E 45 4C    | ..........KERNEL
              |  |              |       |     |
              +-------> Head of import table. |
                 |      The sequence of names starts strongly from zero.
                 |      This is a good flag to determine start. 
                 |              |       |     |
                 |              |       |     |
                 +----> Count of chars in the Pascal string.
                                              |
                                              +--> Padding?!  

This is a working example of importing modules table for real. But unknown padding which nobody wants to see is reason of data truncation.

My assumptions about this padding are probably wrong, but they are there, and they will help you read the table correctly.

My ideas about this padding are spinning around the per-segment relocations
just because names offsets in the case of "Import by Name" fixups don't actually
known at the "one" linkage step. Linker makes the reserved space with unknown
for me data (in example count of ordinals per the segment)

Module References Table | Overview

The main tool in your hands is a module references table. It locates at the e_modtab value and relative too. Following this annoying logic here is a NEAR pointer

let real_modtab: u16 = e_lfanew + e_modtab;

Count of records in the module references table describes by the e_cbmod in NE header. Be carefully: This is exactly count of WORD records. I don’t know WHY? But for this you better know the b means not BYTEs and not bundles and not an actually size of table at all. This is a count of the offsets in the table

That’s all. Module References Table is a list of an offsets which enumerates from one.

struct ModuleReferenceRecord {
    pub e_modoff: u16;
}

Here is an example taken from Sunflower

| Index:2     | Offset:2    |
|-------------|-------------|
| 1           | 0x0001      |
| 2           | 0x0006      |
| 3           | 0x0013      |

Offsets in the module reference table are relative too. Starts from beginning of imptab.

let real_mod_by_index: u16 = e_lfanew + e_imptab + modtab[index - 1];

Then, you firstly must know the module references instead of importing names. It helps you avoid unexpected paddings.

Where is Importing Procedures hidden? | Microsoft LINK.EXE knows more than me…?

This part of an article the most interesting, I suppose.

You can send the Microsoft PDFs to the DeepSeek or another LLM with deep analysis by the source. Unfortunately for you, all what it suggest will be wrong. And all those suggestions will be wrong only by the one Microsofts logic issue.

For real there are two ways to resolve importing procedures exactly.

  1. Static Imports resolving due 0x80 high byte;
  2. Per-segment Relocations filtering.

First way I prefer to completely avoid just because results of this experiment always different and unsafe. (Any OS/2 binary could crash the editor after call). But Win16 applications resolves successfully.

I’ve found this way very hard and advise you to follow by the second way.

Second way strongly requires by you the understanding of per-segment relocations. Please open the previous articles about it, because the next information strictly depends on it.

Per-Segment relocations and Imports | My helping hand

As you remember, there are 2 huge sources of information:

  • EntryTable (with Non/Resident Names);
  • Segments and Per-segment relocations.

Let’s look deeper inside the per-segment relocations. I’m going to call Sunflower for this

Segments Table

The segment table contains an entry for each segment in the executable file.

### Segments

The number of segment table entries are defined in the segmented EXE header.
The first entry in the segment table is segment number 1. The following is the structure of a segment table entry. 

| Type:s   | #Segment:4   | Offset:2   | Length:2   | Flags:2   | Minimum Allocation:2   | Characteristics:s   |
|----------|--------------|------------|------------|-----------|------------------------|---------------------|
| .CODE    | 0x1          | 0x1        | 0x5BCA     | 0xD00     | 0x5BCA                 |                     |
| .CODE    | 0x2          | 0x30       | 0x6388     | 0xD00     | 0x6388                 |                     |
| .CODE    | 0x3          | 0x63       | 0x41A4     | 0xD00     | 0x41A4                 |                     |
| .CODE    | 0x4          | 0x85       | 0x1FB9     | 0xD00     | 0x1FB9                 |                     |
| .CODE    | 0x5          | 0x96       | 0x1CBF     | 0xD00     | 0x1CBF                 |                     |
| .DATA    | 0x6          | 0xA5       | 0x1191     | 0xD41     | 0x3430                 | HAS_MASK PRELOAD    |

### Relocations Table for Segment #1 (.CODE)

The location and size of the per-segment data is defined in the segment table entry for the segment. If the segment has relocation fixups, as defined in the segment table entry flags, they directly follow the segment data in the file.
| ATP:1   | RTP:1   | RTP:s        | IsAdditive:f   | OffsetInSeg:2   | SegType:2   | Target:2   | TargetType:s   | Mod#:2   | Name:2   | Ordinal:2   | Fixup:s   |
|---------|---------|--------------|----------------|-----------------|-------------|------------|----------------|----------|----------|-----------|-----------|
| 0x2     | 0x0     | [Internal]   | [False]        | 0x598A          | 1           | 0x0        | [MOVABLE]      | 0x0      | @0       | 0x0         |           |
| 0x2     | 0x0     | [Internal]   | [False]        | 0x5B13          | 2           | 0x0        | [MOVABLE]      | 0x0      | @0       | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x57AB          | 0           | 0x0        | []             | 0x2      | @120     | 0x0         |           |
| 0x2     | 0x0     | [Internal]   | [False]        | 0x24C5          | 3           | 0x0        | [MOVABLE]      | 0x0      | @0       | 0x0         |           |
| 0x2     | 0x0     | [Internal]   | [False]        | 0x5AB5          | 4           | 0x0        | [MOVABLE]      | 0x0      | @0       | 0x0         |           |
| 0x3     | 0x2     | [Import]     | [False]        | 0x2F62          | 0           | 0x0        | []             | 0x1      | @0       | 0x8         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x4967          | 0           | 0x0        | []             | 0x1      | @8       | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x4DB1          | 0           | 0x0        | []             | 0x2      | @130     | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x49A9          | 0           | 0x0        | []             | 0x2      | @2       | 0x0         |           |
| 0x2     | 0x0     | [Internal]   | [False]        | 0x4E11          | 5           | 0x0        | [MOVABLE]      | 0x0      | @0       | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x3164          | 0           | 0x0        | []             | 0x1      | @14      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x56FA          | 0           | 0x0        | []             | 0x2      | @137     | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x309A          | 0           | 0x0        | []             | 0x1      | @17      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x493C          | 0           | 0x0        | []             | 0x2      | @10      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x57EE          | 0           | 0x0        | []             | 0x2      | @138     | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x2E63          | 0           | 0x0        | []             | 0x2      | @144     | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x2098          | 0           | 0x0        | []             | 0x3      | @9       | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x450B          | 0           | 0x0        | []             | 0x4      | @1       | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x48EC          | 0           | 0x0        | []             | 0x3      | @10      | 0x0         |           |
| 0x5     | 0x0     | [Internal]   | [False]        | 0x126E          | 1           | 0x8300     | [MOVABLE]      | 0x0      | @0       | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x4917          | 0           | 0x0        | []             | 0x3      | @11      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x534           | 0           | 0x0        | []             | 0x2      | @151     | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x4AF3          | 0           | 0x0        | []             | 0x6      | @1       | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x523D          | 0           | 0x0        | []             | 0x2      | @34      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x392A          | 0           | 0x0        | []             | 0x2      | @163     | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x4E2A          | 0           | 0x0        | []             | 0x2      | @164     | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x52C6          | 0           | 0x0        | []             | 0x2      | @39      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x2F8C          | 0           | 0x0        | []             | 0x6      | @8       | 0x0         |           |
| 0x5     | 0x0     | [Internal]   | [False]        | 0x12A6          | 1           | 0x9C00     | [MOVABLE]      | 0x0      | @0       | 0x0         |           |
| 0x5     | 0x0     | [Internal]   | [False]        | 0x200           | 2           | 0xFD00     | [MOVABLE]      | 0x0      | @0       | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x25E9          | 0           | 0x0        | []             | 0x7      | @7       | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x2557          | 0           | 0x0        | []             | 0x7      | @9       | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x5787          | 0           | 0x0        | []             | 0x2      | @53      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x265D          | 0           | 0x0        | []             | 0x7      | @15      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x5575          | 0           | 0x0        | []             | 0x2      | @57      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x5791          | 0           | 0x0        | []             | 0x2      | @59      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x58D7          | 0           | 0x0        | []             | 0x7      | @21      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x5909          | 0           | 0x0        | []             | 0x7      | @22      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x4FE3          | 0           | 0x0        | []             | 0x2      | @63      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x23D0          | 0           | 0x0        | []             | 0x7      | @27      | 0x0         |           |
| 0x5     | 0x0     | [Internal]   | [False]        | 0x12BF          | 1           | 0xB500     | [MOVABLE]      | 0x0      | @0       | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x576B          | 0           | 0x0        | []             | 0x2      | @70      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x5538          | 0           | 0x0        | []             | 0x2      | @71      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x2408          | 0           | 0x0        | []             | 0x7      | @32      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x54EA          | 0           | 0x0        | []             | 0x2      | @72      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x43D0          | 0           | 0x0        | []             | 0x2      | @73      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x52AC          | 0           | 0x0        | []             | 0x2      | @74      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x42ED          | 0           | 0x0        | []             | 0x2      | @75      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x5495          | 0           | 0x0        | []             | 0x2      | @77      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x4DCD          | 0           | 0x0        | []             | 0x7      | @40      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x43A4          | 0           | 0x0        | []             | 0x2      | @81      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x43ED          | 0           | 0x0        | []             | 0x2      | @82      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x256E          | 0           | 0x0        | []             | 0x7      | @46      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x2620          | 0           | 0x0        | []             | 0x7      | @48      | 0x0         |           |
| 0x5     | 0x0     | [Internal]   | [False]        | 0x12B9          | 1           | 0xCE00     | [MOVABLE]      | 0x0      | @0       | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x5081          | 0           | 0x0        | []             | 0x2      | @95      | 0x0         |           |
| 0x3     | 0x1     | [Import]     | [False]        | 0x546D          | 0           | 0x0        | []             | 0x2      | @98      | 0x0         |           |
| 0x5     | 0x0     | [Internal]   | [False]        | 0x1274          | 1           | 0x5800     | [MOVABLE]      | 0x0      | @0       | 0x0         |           |

You see many records registered and interpret by Sunflower as [Import] Look at the table and find [Import] records with zero-ordinal (@0). Those records will be imports by name. Other imports having non-zero ordinal are imports by ordinal.

Main idea of this algorithm bases on the fact that “Ordinals in Relocations table already set”. Troubles what you may have to implement it -

  1. Position of Module Name;
  2. Position of Procedure Name.

Don’t think that table of relocations looks clearly like Microsoft wants. The RTP:s field is custom (my solution) and it retranslate the RTP:1 field from BYTE to expected name of relocation type.

So, select all imports which marked as IMPORT_BY_NAME or IMPORT_ORDINAL (check out RTP:1).

To find module name you must:

  1. Calculate the offset e_lfanew + e_modtab + 2 * (relocation.ModuleIndex - 1) and seek to it.
  2. Read u16 module Name Offset. dll_name_offset
  3. Calculate the offset e_lfanew + e_imptab + dll_name_offset and seek to it.
  4. Read the Pascal string (one u8 of length, that many bytes of character data) at that location. (Make sure to add the NUL terminator at the end of the string if you are using Pascal strings with C string functions)
  5. Seek to the location remembered in the first step.

To find the procedure name (import by name): To find the procedure name for an IMPORT_NAME relocation, do the following:

  1. Calculate the offset e_lfanew + e_imptab + relocation.NameOffset and seek to it. Read the Pascal string (one u8 of length, that many bytes of character data) at that location. (Make sure to add the NUL terminator at the end of the string if you are using Pascal strings with C string functions!) Seek to the location remembered in the first step.

You can imagine it like this:

Looking for an import                Calculate offset to the mod_name
+----------------+    +----------------+  modtab[i] +----------------+
| Relocation     | -> | ModuleIndex (i)| ---------->| Import Table   |
| ModuleIndex=_  |    | Offset=0x0006  |            | "DOSCALLS"     |
| Record         |    |                |            | Name Entry     |
| NameOffset=0x__|    +----------------+            +----------------+
+----------------+             |
                               v Calculate offset to the imp_proc_name
                      +----------------+
                      | Procedure Name |
                      | "DosWrite"     |
                      +----------------+

If relocation type equal the IMPORT_ORDINAL, it looks little simplier:

                                Calculate the offset mod_name
+----------------+    +----------------+    +----------------+
| Relocation     | -> | Module Table   | -> | Import Table   |
| ModuleIndex=2  |    | Offset=0x0006  |    | "DOSCALLS"     |
| Record         |    |                |    | Name Entry     |
| ImpOdrinal=0x10|    +----------------+    +----------------+
+----------------+    

Importing module: read uppercase PascalString by mod_name offset
Importing procedure: ImpOrdinal or @16 (this means 0x10)  

As you see, this idea requires filled correctly per-segment relocations already.

I suppose the Ghidra bases on the same algorithm but also holds special libraries or data files with symbols. That may be a reason of more informative output (in example you will see the USER!FatalExit / @1 instead of (USER!@1))

In the 2.0.0.0 build of Sunflower the “Sunflower.Ne” plugin has an error of bytes reinterpretation for relocations. In 2.1.0.0 I’ve fixed it and completely see the imports.

For example I’ve took the OS/2 1.1 installation program (SYSINST2.EXE). Let’s see the part of results of this procedure

### Resolved Imports of `SESMGR`

16-bit Imports processor bases at per-segment relocations. If segment has special bit in the byte-mask, next services will iterate preprocessed relocations records
| Name               | Ordinal   |
|--------------------|-----------|
| `@14`              | @14       |
| `@17`              | @17       |
| `@8`               | @8        |
| `DOSSMPMPRESENT`   | @0        | <-- DosSmPmPresent 
| `DOSSMSETTITLE`    | @0        | <-- DosSmSetTitle (I suppose SM means Session Manager)

### Resolved Imports of `KBDCALLS`

16-bit Imports processor bases at per-segment relocations. If segment has special bit in the byte-mask, next services will iterate preprocessed relocations records
| Name    | Ordinal   |
|---------|-----------|
| `@10`   | @10       |
| `@11`   | @11       |
| `@13`   | @13       |
| `@4`    | @4        |
| `@5`    | @5        |
| `@9`    | @9        |

### Resolved Imports of `MSG`

16-bit Imports processor bases at per-segment relocations. If segment has special bit in the byte-mask, next services will iterate preprocessed relocations records
| Name   | Ordinal   |
|--------|-----------|
| `@1`   | @1        |
| `@2`   | @2        |

### Resolved Imports of `QUECALLS`

16-bit Imports processor bases at per-segment relocations. If segment has special bit in the byte-mask, next services will iterate preprocessed relocations records
| Name   | Ordinal   |
|--------|-----------|
| `@1`   | @1        |
| `@8`   | @8        |

I advise you to memorize those 2 strings from SESMRG imports table. They are coming back later in the next region.

You can’t differ module names and procedure names | evil trick by LINK.EXE

If you seek for symbols in hex-view of NE segmented program You can find interesting phemonenon: I also demonstrate hex table of OS/2 1.x SYSINST2.EXE just because this file demonstrates it very colourful.

00 06 53 45 53 4D 47 52 0D 44 4F 53 53 4D 53 45 | _.SESMGR.DOSSMSE
54 54 49 54 4C 45 08 44 4F 53 43 41 4C 4C 53 08 | TTITLE.DOSCALLS.
4B 42 44 43 41 4C 4C 53 08 56 49 4F 43 41 4C 4C | KBDCALLS.VIOCALL
53 03 4E 4C 53 03 4D 53 47 00                   | S.NLS.MSG_


'_' is not ASCII code, this is a start and the end of Import module names

But if you look at this better, you will see very strange things. For a first: DOSSMSETTITLE is a procedure name instead of module name. But this is not differs explicit like in Portable Executable format programs. The DosSMSetTitle is a DOS Session Manager SetTitle function which will be defined incorrect (like DLL).

Remember previous tables with processed by relocation records are imports. Do you see the DosSMPMPresent in the dump? Yes, You don’t see it! But relocations caught this symbols correctly (without non-ASCII garbage). So this tells once for me:

Without expected training or knowledge of OS/2 and Win16
environment architecture you can't define symbols corrrectly!

And the Microsoft LINK.EXE not strongly differs symbols by table. It fills tables following the prepared relocation records too.

Therefore, if you don’t know about PC/MS-DOS Session Manager, you can make a mistake when define SESMGR like an function instead of DLL.

I’ll say it again: “This observations you can do without decompiling and disassembling procedures”. And I’ve done it avoiding disassembler. Sunflower plugin just reads and reinterprets binary data of segmented executable and translate it to markdown.

Beyond the Dynamic Linking | Runtime insertions

Remember that project linked by any format may have an imports and the extra objects data insertions. It strongly depends on

  • Dynamic Linking (make a FAR call to something “into the runtime”);
  • Static Linking (move functions from another classes or headers “into the compile-time”).

Symbols of statically linked procedures also exists, and if you link the binary which uses format output (e.g printf from (c)stdio.h) the signs about it also will be. But those static calls already stores inside the segments and never checks Sunflower plugin because it requires disassembling.

How the Operating System knows for who you make FAR call?

And IBM OS/2 and Microsoft Windows uses special abstractions for a userland for applications. If you want to call another library at the runtime stage, operating system represents you special functions which tries to find position of your function in memory. Pointer what you give by this call is not just FAR pointer.

Those objects in the meaning of Windows API calls the “Handles”. Hungarian notation for C/++ programmers unofficially holds special prefix for Windows handles.

auto hDevides = QueryDosDevices(...); // naming for example

And the hDevices marked as h because type of it will become a Win32 or Win16 HANDLE.

If you see the winevdm documentation by otya128, you might catch the pointers to procedures in relocation tables stores as HANDLE16. This is a special pointer types which WineVDM uses to make a real retranslated 16 -> 32 -> 64-bit call to existing dynamic (shared) object or a program.

In the End

Many sources which not copies the Microsoft documentation may help you to resolve unexpected problems. You can lookup the Sunflower sources and make sure that all what I described here fully repeats the Sunflower codebase.

Those materials what I’ve done for it bases strongly on the Sunflower reports and Hexadecimal view of programs. Detail analysis of raw data bases on the different materials (not Microsoft docs at all) but for a many people I specially try to open them eyes, what “official sources may be written badly.” I never used decompilers and disassemblers for Microsoft Windows and IBM OS/2 modules because they are secured under the law.


Author | Alexey Tolstopyatov (21 y.0.)

Desktop developer and System developer enthusiast. Currently a student in Tomsk State University in the area of software for microprocessor systems.