如何知道 PE Header 导入函数是否由 Ordinal 而不是按名称导入

逆向工程 视窗 聚乙烯
2021-06-12 09:47:44

我想知道如何确定 PE 标头中的导入函数是否是按顺序而不是按名称导入的,因为我遇到了执行此操作的可执行文件。这是按顺序导入所有函数的 DLL,除了一个(来自WS2_32.dll):

程序截图 ExeinfoPE 在此处输入图片说明

这就是我为导入所做的工作:

  1. 读取 PE 标头。
  2. 遍历数据目录并找到IMAGE_DIRECTORY_ENTRY_IMPORT.
  3. 一旦IMAGE_DIRECTORY_ENTRY_IMPORT找到,循环IMAGE_IMPORT_DESCRIPTORS

  4. 在每个 上IMAGE_IMPORT_DESCRIPTORS,提取所有函数。

这是我提取函数的方法(仅适用于按名称导入的函数):

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.]
    }
}

我注意到ExeinfoPE按名称导入的每个函数 Hint/Ordinal都是0. 但是,在我的代码functionImport->Hint中始终设置为某些内容,无论函数是按名称还是顺序导入。

IMAGE_IMPORT_DESCRIPTOR无法对里面是否该功能的信息IMAGE_IMPORT_DESCRIPTOR是按序号或名称导入,因为功能之一是通过名称进口,所有的人都按顺序导入。所以,我在这里完全没有想法。

以下是我正在使用的数据结构供您参考,因此无需谷歌:

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个回答

摘自http://www.pelib.com/

首先,可以设置数组中的位IMAGE_ORDINAL_FLAG(即:MSBIMAGE_THUNK_DATA,在这种情况下,列表中没有符号名称信息,并且符号纯粹是按序导入的。您可以通过检查 的较低单词来获得序数 IMAGE_THUNK_DATA

IMAGE_THUNK_DATA-大批; 沿着这个数组走下去(它被 0终止),每个成员都将是 a 的 RVA IMAGE_IMPORT_BY_NAME(除非设置了高位,在这种情况下你没有名字但只剩下一个序数)。

波动率插件中提取的更详细的解释(第 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