3

I want to know how to find out if an imported function in a PE header is being imported by ordinal rather than by name because I came across an executable that does that. Here is the DLL that imports all functions by ordinal except for one (from WS2_32.dll):

Screenshot from program called ExeinfoPE enter image description here

This is what I'm doing to get to the imports:

  1. Read the PE header.
  2. Loop over data directories and find IMAGE_DIRECTORY_ENTRY_IMPORT.
  3. Once IMAGE_DIRECTORY_ENTRY_IMPORT is found, loop over IMAGE_IMPORT_DESCRIPTORS.

  4. On each IMAGE_IMPORT_DESCRIPTORS, extract all the functions.

Here is how I extract the functions (only works for functions imported by name):

void extractFunctions(IMAGE_IMPORT_DESCRIPTOR dll, uintptr_t sectionStartAddrRaw) {
    uintptr_t selectedFunctionImport = dll.Characteristics + sectionStartAddrRaw;
    uintptr_t selectedFunctionImportIAT = dll.FirstThunk + sectionStartAddrRaw;

    while (true) {
        IMAGE_THUNK_DATA thunkPtrToImportByName = *(IMAGE_THUNK_DATA*)selectedFunctionImport;
        selectedFunctionImport += sizeof(IMAGE_THUNK_DATA); //Next loop we'll loop over to the next IMAGE_THUNK_DATA.
        if (thunkPtrToImportByName.u1.Function == NULL) { //Check if we need to exit the looping since there are no more functions to import.
            break;
        }

        IMAGE_IMPORT_BY_NAME* functionImport = (IMAGE_IMPORT_BY_NAME*)(thunkPtrToImportByName.u1.Function + sectionStartAddrRaw);

        Function function;
        function.name = std::string(functionImport->Name); //Access violation here if the function needs to be imported by ordinal, instead of by name.
        function.locationInIAT = selectedFunctionImportIAT;
        function.locationInOriginalIAT = selectedFunctionImportIAT - sectionStartAddrRaw + header.OptionalHeader.ImageBase;

        selectedFunctionImportIAT += sizeof(IMAGE_THUNK_DATA);
        dlls.back().functions.push_back(function); //We assume that IMAGE_IMPORT_DESCRIPTOR dll is the last one in the dlls vector.]
    }
}

I noticed that every function Hint/Ordinal inside ExeinfoPE that is imported by name is 0. However, in my code functionImport->Hint is always set to something, regardless if the function is supposed to be imported by name or ordinal.

The IMAGE_IMPORT_DESCRIPTOR cannot have information on whether the functions inside that IMAGE_IMPORT_DESCRIPTOR are imported by ordinal or name, since one of the functions is imported by name and all of the others are imported by ordinal. So, I'm all out of ideas here.

Here are the data structures I'm using for your reference so no need to Google:

typedef struct _IMAGE_THUNK_DATA {
    union {
        uint32_t* Function;             // address of imported function
        uint32_t  Ordinal;              // ordinal value of function
        PIMAGE_IMPORT_BY_NAME AddressOfData;        // RVA of imported name
        DWORD ForwarderStringl              // RVA to forwarder string
    } u1;
} IMAGE_THUNK_DATA, *PIMAGE_THUNK_DATA;

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics; /* 0 for terminating null import descriptor  */
        DWORD   OriginalFirstThunk; /* RVA to original unbound IAT */
    } DUMMYUNIONNAME;

    DWORD   TimeDateStamp;  /* 0 if not bound,
    * -1 if bound, and real date\time stamp
    *    in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT
    * (new BIND)
    * otherwise date/time stamp of DLL bound to
    * (Old BIND)
    */
    DWORD   ForwarderChain; /* -1 if no forwarders */
    DWORD   Name;
    /* RVA to IAT (if bound this IAT has actual addresses) */
    DWORD   FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR;

typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD    Hint;
    BYTE    Name[1];
} IMAGE_IMPORT_BY_NAME,*PIMAGE_IMPORT_BY_NAME;
1
  • I found something relevant on page 51 of Windows' pe header documentation under the topic "Import Lookup Table", gyazo.com/0fde0a89b514b3e40a163e9934391e4f Looks like the first bit of the IMAGE_THUNK_DATA->Ordinal is a flag, and if it is set then the function should be imported by ordinal. It's late right now, I will write the answer tomorrow or someone else write it.
    – Majiick
    Commented Aug 28, 2016 at 6:13

1 Answer 1

4

Extracted from http://www.pelib.com/:

First, the bit IMAGE_ORDINAL_FLAG (that is: the MSB) of the IMAGE_THUNK_DATA in the arrays can be set, in which case there is no symbol-name-information in the list and the symbol is imported purely by ordinal. You get the ordinal by inspecting the lower word of the IMAGE_THUNK_DATA.

IMAGE_THUNK_DATA-array; walk down this array (it is be 0-terminated), and each member will be the RVA of a IMAGE_IMPORT_BY_NAME (unless the hi-bit is set in which case you don't have a name but are left with a mere ordinal).

A more detailed explanation extracted from a volatility plugin (lines 363-367):

350  while 1:
351      thunk = obj.Object('_IMAGE_THUNK_DATA',
352                 offset = self.obj_parent.DllBase + self.OriginalFirstThunk +
353                 i * self.obj_vm.profile.get_obj_size('_IMAGE_THUNK_DATA'),
354                 vm = self.obj_native_vm)

355      # We've reached the end when the element is zero 
357      if thunk == None or thunk.AddressOfData == 0:
358          break
359      o = obj.NoneObject("Ordinal not accessible?")
361      n = obj.NoneObject("Imported by ordinal?")
362      f = obj.NoneObject("FirstThunk not accessible")

363      # If the highest bit (32 for x86 and 64 for x64) is set, the function is 
365      # imported by ordinal and the lowest 16-bits contain the ordinal value. 
366      # Otherwise, the lowest bits (0-31 for x86 and 0-63 for x64) contain an 
367      # RVA to an _IMAGE_IMPORT_BY_NAME struct. 
368      if thunk.OrdinalBit == 1:
369          o = thunk.Ordinal & 0xFFFF
370      else:
371          iibn = obj.Object("_IMAGE_IMPORT_BY_NAME",
372                            offset = self.obj_parent.DllBase +
373                            thunk.AddressOfData,
374                            vm = self.obj_native_vm)
375          o = iibn.Hint
376          n = iibn.Name
377      # See if the import is bound (i.e. resolved)
379      first_thunk = obj.Object('_IMAGE_THUNK_DATA',
380                      offset = self.obj_parent.DllBase + self.FirstThunk +
381                      i * self.obj_vm.profile.get_obj_size('_IMAGE_THUNK_DATA'),
382                      vm = self.obj_native_vm)
383      if first_thunk:
384          f = first_thunk.Function.v()
385      yield o, f, str(n or '')
387      i += 1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.