• PE文件结构(三) 输入表


    PE文件结构(三)


    參考

    书:《加密与解密》

    视频:小甲鱼 解密系列 视频



    输入表


        输入函数,表示被程序调用可是它的代码不在程序代码中的,而在dll中的函数。对于这些函数。磁盘上的可执行文件仅仅是保留相关的函数信息,如函数名,dll文件名称等。

    在程序执行前。程序是没有保存这些函数在内存中的地址。当程序执行起来时。windows载入器会把相关的dll装入内存。而且将输入函数的指令与函数真在内存中正的地址联系起来。输入表(导入表)就是用来保存这些函数的信息的。


        在   IMAGE_OPTIONAL_HEADER 中的 DataDirectory[16]  数组保存了 输入表的RVA跟大小。

    通过RVA能够在OD中载入程序通过 ImageBase+RVA 找到 输入表,或者通过RVA计算出文件偏移地址,查看磁盘中的可运行文件,通过文件偏移地址找到输入表。

       

        输入表是以一个IMAGE_IMPORT_DESCRIPTOR(IID)数组 開始的。每个被PE文件隐式的链接进来的dll都有一个IID。IID数组的最后一个单元用NULL表示。


    IMAGE_IMPORT_DESCRIPTOR 结构:

    typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    	_ANONYMOUS_UNION union {              //00h
    		DWORD Characteristics;
    		DWORD OriginalFirstThunk; 
    	} DUMMYUNIONNAME;
    	DWORD TimeDateStamp;                  //04h
    	DWORD ForwarderChain;                 //08h
    	DWORD Name;                           //0Ch
    	DWORD FirstThunk;                     //10h
    } IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR;



        当中Name是dll名字的指针。OriginalFirstThunk指向一个IMAGE_THUNK_DATA数组叫做输入名称表Import Name Table(INT),用来保存函数,FirstThunk也指向IMAGE_THUNK_DATA数组叫做输入地址表Import Address Table(IAT)。


    IMAGE_THUNK_DATA 结构:

    typedef struct _IMAGE_THUNK_DATA32 {
    	union {
    		DWORD ForwarderString;
    		DWORD Function;
    		DWORD Ordinal;
    		DWORD AddressOfData;
    	} u1;
    } IMAGE_THUNK_DATA32,*PIMAGE_THUNK_DATA32;

        当IMAGE_THUNK_DATA 的值最高位为1时,表示函数是以序号方式输入。这时低31为被当作函数序号。当最高位是0时,表示函数是以字符串类型的函数名方式输入的,这时。IMAGE_THUNK_DATA 的值为指向 IMAGE_IMPORT_BY_NAME 的结构的RVA。


    typedef struct _IMAGE_IMPORT_BY_NAME {
    	WORD Hint;
    	BYTE Name[1];
    } IMAGE_IMPORT_BY_NAME,*PIMAGE_IMPORT_BY_NAME;

    Hint 表示这个函数在其所驻留dll的输出表的序号。不是必须的。

    Name 表示 函数名,是一个ASCII字符串以0结尾,大小不固定。



        INT保存的是这个程序导入这个dll中函数信息,它是固定的不会被改动。可是IAT会在程序载入时被重写,当程序载入时,它会被PE载入器重写成 这些函数的在内存中的真正地址。

    即把它原来指向的IMAGE_IMPORT_BY_NAME 改成 函数真正的地址。



    那为什么要两个 IMAGE_THUNK_DATA 数组?

       

        当程序载入时,IAT 会被PE载入器重写。PE载入器先搜索INT,PE载入器迭代搜索INT数组中的每一个指针。找出 INT所指向的IMAGE_IMPORT_BY_NAME结构中的函数在内存中的真正的地址。并把它替代原来IAT中的值。

    当完毕后,INT就没实用了,程序仅仅须要IAT就能够正常执行了。


    看以下图,这个是可运行程序在磁盘中的时候:



    这个是当程序被载入的是后:





    实例分析:


        先找到输入表RVA,通过IMAGE_OPTIONAL_HEADER 中的最后一个项  IMAGE_DATA_DIRECTORY 能够知道 输入表相对与PE文件头的偏移量为80h能够找到输入表达RVA。


    图片1


       

        但这个是RVA不是文件偏移地址。

    通过转换能够知道。输入表的文件偏移地址为850h。



        查看850h。即IID,能够看到这个程序有两个IID。即链接了 两个dll,看到一个IID,能够知道它的OriginalFirstThunk 是 2098h  FirstThunk  为200Ch,它们转换后的文件偏移地址分别为898h 和 80Ch


    图片2  IID数组





      查看OriginalFirstThunk跟FirstThunk 。能够发现这里INT跟IAT的内容是一样的。先看看第一个函数的信息,由于第一位为1,所以这里00002122h 表示 IMAGE_IMPORT_BY_NAME 的RVA,转化为文件偏移值为922h


    图片3  INT跟IAT



     


        查看922h 能够看函数名。



    图片4:







        我们能够从上面看到程序在磁盘中时 INT 与IAT内容一样,都是指向 IMAGE_IMPORT_BY_NAME 。

    用OD载入程序,查看INT与IAT的内容


    图片5 INT



    图片6 IAT



        能够发现INT没有发生变化。IAT变成了比如77D3C702h,IAT中的RVA被改成了函数的真正的地址。

       


        查看77D3C702h,就能够看到这个函数


    图片7





  • 相关阅读:
    记一次DAC转换功能修改的解决
    图数据库|正反向边的最终一致性——TOSS 介绍
    开源分布式图数据库的思考和实践
    图数据库|基于 Nebula Graph 的 BetweennessCentrality 算法
    图数据库|如何从零到一构建一个企业股权图谱系统
    GitHub 自动合并 pr 的机器人——automergebot
    leetcode单调栈下一个最大元素
    leetcode单链表环形链表2
    Spring Boot 如何热加载jar实现动态插件?
    leetcode 单链表19
  • 原文地址:https://www.cnblogs.com/lytwajue/p/7019640.html
Copyright © 2020-2023  润新知