zoukankan      html  css  js  c++  java
  • 干货分享丨表哥带你学习导入表及导入表注入

    今天的文章分享是 i 春秋作者flag0原创的文章,浅析导入表及导入表注入的相关内容,文章篇幅较长,阅读时长约15分钟,文章未经许可禁止转载!

    干货分享丨表哥带你学习导入表及导入表注入

     

    导入表的概述

    导入表是逆向和病毒分析中比较重要的一个表,在分析病毒时几乎第一时间都要看一下程序导入表的内容,判断程序大概用了哪些功能。

    导入表位于数据目录项中的第二项,一般会有多个,每调用一个DLL便存在一张导入表,导入表记录了导入的dll中被调用的函数地址。

    导入表结构体 IMAGE_IMPORT_DESCRIPTOR

    typedef struct _IMAGE_IMPORT_DESCRIPTOR{
        union{
            DWORD Characteristics;
            DWORD OriginalFirstThunk;
        };
        DWORD TimeDataStamp;
        DWORD ForwarderChain;
        DWORD Name;
        DWORD FirstThunk;
    } IMAGE_IMPORT_DESCRIPTOR;
    typedef IMAGE_IMPORT_DESCRIPTOR UNALGNED *PIMAGE_IMPORT_DESCRIPTOR;
    • OriginalFirstThunk 指向导入名称表INT(Import Name Table)的RVA,INT是IMAGE_THUNK_DATA结构的数组,以内容为0的IMAGE_THUNK_DATA结构结束,每个IMAGE_THUNK_DATA指向IMAGE_IMPORT_BY_NAME结构。
    • TimeDataStamp 时间戳,当其值为-1时,采用绑定导入技术进行加载。
    • Name,指向存储函数名字的RVA。
    • FirstThunk 指向导入地址表IAT(Import Address Table)的RVA,IAT与INT结构相同,也是一个IMAGE_THUNK_DATA结构的数组。

    导入绑定技术

    一般情况下,在程序加载前IAT表和INT表中的内容相同,都是程序引用的dll中的函数名或序号,加载完成后IAT表中将替换为函数的真正地址。

    但在加载前IAT表中直接写绝对地址是可以实现的,并且具有启动速度快的优点,但是如果在dll重定位时,没能占据自身ImageBase处的地址,则需要修复绝对地址,当dll被修改时,IAT表中对应的函数地址可能被改,需要修复函数地址。

    这种方式被称为绑定导入,在Windows自带的记事本中,就采用了这种方式。

    IMAGE_THUNK_DATA

    typedef struct _IMAGE_THUNK_DATA32 {                                    
        union {                                 
            PBYTE  ForwarderString;                                 
            PDWORD Function;                                    
            DWORD Ordinal;         
            PIMAGE_IMPORT_BY_NAME  AddressOfData;
        } u1;                                   
    } IMAGE_THUNK_DATA32;                                   
    typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;
    • Ordinal 被导入的API的序号值
    • AddressOfData 指向IMAGE_IMPORT_BY_NAME的RVA

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

    IMAGE_IMPORT_BY_NAME

    存储了一个输入函数的相关信息

    typedef struct _IMAGE_IMPORT_BY_NAME {
        WORD    Hint;      
        BYTE    Name[1];                           
    } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
    • Hint 记录了函数在DLL的导出表中的序号,不是必须的,可以为空。
    • Name 输入函数的函数名,是一个字符串以0结尾。

    导入地址表IAT

    IAT由PE装载器重写,PE装载器先搜索OriginalFirstThunk指向的INT表,如果找到,则遍历数组,找出每个IMAGE_IMPORT_BY_NAME结构所指向的输入函数的地址,加载器用函数真正的日寇地址代替由FirstThunk指向的IMAGE_THUNK_DATA数组里元素的值。

    在加载前,非导入绑定情况下,INT表与IAT表中的内容是一样的。

    干货分享丨表哥带你学习导入表及导入表注入

     

    PE文件加载后,IAT表被替换为函数的入口地址。

    干货分享丨表哥带你学习导入表及导入表注入

     

    在加载前,可以看到:

    FirstThunkFOA(23280)->IMAGE_THUNK_DATA32(0242BC)->IMAGE_IMPORT_BY_NAME(232B0)->Name="MessageBoxA"

    干货分享丨表哥带你学习导入表及导入表注入

     

    在加载后的内存中,可以看到:

    FirstThunkFOA(42428C)->IMAGE_THUNK_DATA32(77D5050B)函数地址

    干货分享丨表哥带你学习导入表及导入表注入

     

    打印输出导入表

     定位导入表

    获得数据目录项第二项中的VirtualAddress 将其RVA转为FOA,用以定位到导入表结构,将导入表结构中的DWORD Name打印输出,即定位导入表,打印DLL名。

    遍历INT表

    通过导入表中的OriginalFirstThunk定义到INT表,以此判断IMAGE_THUNK_DATA数组的最高位是否为1,如果为1则按到导出序号导出,如果为0则其指向IMAGE_IMPORT_BY_NAME的RVA,将其转换成FOA,定位到IMAGE_IMPORT_BY_NAME,输出函数名字,依次遍历,直到数组结尾。

    遍历IAT表

    通过导入表中的FirstThunk定义到IAT表,余下方法同上。

    实现代码

    // PrintImport.cpp : Defines the entry point for the console application.
    //

    #include "stdafx.h"
    #include "stdlib.h"
    #include "windows.h"

    size_t FileLength(FILE *fp)
    {
        size_t num;
        fseek(fp,0,SEEK_END);//将文件指针移动到文件尾部
        num = ftell(fp);//获取文件指针的当前位置
        fseek(fp,0,SEEK_SET);
        return num;
    }

    LPVOID readFile(PCHAR path)//将文件中的数据读取到内存中{
        FILE* fp;
        size_t len;
        LPVOID pFileBuffer;

        fp = fopen(path,"rb");
        if(fp == NULL)
        {
            printf("error ");
        }

        len = FileLength(fp);//获取文件大小
        pFileBuffer = (LPVOID)malloc(len);//申请动态内存

        fread(pFileBuffer,len,1,fp);
        fclose(fp);
        return pFileBuffer;
    }

    DWORD RVATOFOA(DWORD RVA,LPVOID pFileBuffer){
        DWORD FOA = NULL;

        PIMAGE_DOS_HEADER pDosHeader = NULL;
        PIMAGE_NT_HEADERS pNtHeaders = NULL;
        PIMAGE_FILE_HEADER pFileHeader = NULL;
        PIMAGE_OPTIONAL_HEADER pOptionalHeader = NULL;
        PIMAGE_SECTION_HEADER pSectionHeader = NULL;

        pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
        pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
        pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNtHeaders + 4);
        pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
        pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);

        if(RVA <= pOptionalHeader->SizeOfHeaders)
            return RVA;

        //此处不需要减去ImageBase 此处的RVA是基于FileAliment和SectionAliment来说的,并不是真正的内存中的RVA,此处的程序并没有在内存中运行

        for(;RVA > (pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize);pSectionHeader++);//定位到所在节
        FOA = RVA - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
        return FOA;

    }

    DWORD FOATORVA(DWORD FOA,LPVOID pFileBuffer){
        DWORD RVA = NULL;

        PIMAGE_DOS_HEADER pDosHeader = NULL;
        PIMAGE_NT_HEADERS pNtHeaders = NULL;
        PIMAGE_FILE_HEADER pFileHeader = NULL;
        PIMAGE_OPTIONAL_HEADER pOptionalHeader = NULL;
        PIMAGE_SECTION_HEADER pSectionHeader = NULL;

        pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
        pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
        pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNtHeaders + 4);
        pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
        pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);

        if(FOA <= pOptionalHeader->SizeOfHeaders)
            return FOA;

        //此处不需要减去ImageBase 此处的RVA是基于FileAliment和SectionAliment来说的,并不是真正的内存中的RVA,此处的程序并没有在内存中运行

        for(;FOA > (pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize);pSectionHeader++);//定位到所在节
        RVA = FOA - pSectionHeader->PointerToRawData + pSectionHeader->VirtualAddress ;
        return RVA;

    }

    VOID PrintImport(LPVOID pFileBuffer){
        PIMAGE_DOS_HEADER pDosHeader = NULL;//DOS头
        PIMAGE_NT_HEADERS pNtHeaders = NULL;//NT头
        PIMAGE_FILE_HEADER pFileHeader = NULL;//标准PE头
        PIMAGE_OPTIONAL_HEADER pOptionalHeader = NULL;//拓展PE头
        PIMAGE_SECTION_HEADER pSectionHeader = NULL;//节表
        PIMAGE_SECTION_HEADER pNewSec = NULL;//新节表结构
        PIMAGE_EXPORT_DIRECTORY pExportDirectory = NULL; //导出表结构体
        PIMAGE_IMPORT_DESCRIPTOR pImportDescriptop = NULL;//导入表结构体

        pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
        pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
        pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNtHeaders + 4);
        pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);

        //定位导入表地址
        DWORD offsetImportFoa = RVATOFOA(pOptionalHeader->DataDirectory[1].VirtualAddress,pFileBuffer);
        //printf();
        pImportDescriptop = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pFileBuffer + offsetImportFoa);

        while(pImportDescriptop->Name != NULL && pImportDescriptop->OriginalFirstThunk != NULL && pImportDescriptop->FirstThunk != NULL)
        {
            DWORD offsetDllNameFoa = RVATOFOA(pImportDescriptop->Name,pFileBuffer);
            PCHAR DllName = (PCHAR)((DWORD)pFileBuffer + offsetDllNameFoa);
            printf(" DLLName:%s ",DllName);

            printf("OriginalFirstThunk:%x ",pImportDescriptop->OriginalFirstThunk);
            DWORD offsetThunkFOA = RVATOFOA(pImportDescriptop->OriginalFirstThunk,pFileBuffer);
            PDWORD ImageThunk = (PDWORD)((DWORD)pFileBuffer + offsetThunkFOA);

            printf("======INT====== ");
            while(*ImageThunk != 0x0000)
            {
                printf("ThunkValue:%x  ",*ImageThunk);
                PIMAGE_IMPORT_BY_NAME IBName = NULL;
                try{
                    if(*ImageThunk & 0x80000000)
                    {
                        printf("按序号导出:%x ",*ImageThunk & 0x7FFFFFFF);
                    }else
                    {
                        DWORD IBNameFoa = RVATOFOA(*ImageThunk,pFileBuffer);
                        IBName = (PIMAGE_IMPORT_BY_NAME)((DWORD)pFileBuffer+IBNameFoa);
                        printf("Hit Name:%x-%s ",IBName->Hint,IBName->Name);
                    }
                    ImageThunk++;
                }catch(...)
                {
                    printf(" ");
                    ImageThunk++;
                }
            }

            printf("======IAT====== ");
            DWORD FirstThunkFOA = RVATOFOA(pImportDescriptop->FirstThunk,pFileBuffer);
            ImageThunk = (PDWORD)((DWORD)pFileBuffer + FirstThunkFOA);

            while(*ImageThunk != 0x0000)
            {
                printf("ThunkValue:%x  ",*ImageThunk);
                PIMAGE_IMPORT_BY_NAME IBName = NULL;
                try{
                    if(*ImageThunk & 0x80000000)
                    {
                        printf("按序号导出:%x ",*ImageThunk & 0x7FFFFFFF);
                    }else
                    {
                        DWORD IBNameFoa = RVATOFOA(*ImageThunk,pFileBuffer);
                        IBName = (PIMAGE_IMPORT_BY_NAME)((DWORD)pFileBuffer+IBNameFoa);
                        printf("Hit Name:%x-%s ",IBName->Hint,IBName->Name);
                    }
                    ImageThunk++;
                }catch(...)
                {
                    printf(" ");
                    ImageThunk++;
                }
            }
            pImportDescriptop++;
        }
    }

    int main(int argc, char* argv[]){
        LPVOID pFileBuffer = NULL;
        LPVOID pNewSecFoa = NULL;
        PCHAR path = "C:\飞鸽传书.exe.";

        pFileBuffer = readFile(path);
        PrintImport(pFileBuffer);

        system("pause");
        return 0;
    }

    导入表注入

    工具及运行环境:

    • 用于实验的程序:飞鸽传书
    • dll文件
    • Uedit编辑器
    • LoadPE
    • 实验环境:Windows Xp

    实验程序下载:

    链接:

    https://pan.baidu.com/s/1pWWsqkLb_hjF7ksfnMkqeQ

    提取码:x5b7

    原理

    当exe被加载时,系统会根据exe导入表信息来加载需要用到的DLL,导入表注入的原理就是修改exe导入表,将自己的DLL添加到exe的导入表中,这样exe运行时可以将自己的DLL加载到exe的进程空间。

    实现步骤

    1、新增节表用来存储移动后的导入表(这里也可以扩大节,或者是找节区的空白区);

    2、根据目录项(第二个值就是导入表)得到导入表的VirtualAddress和Size;

    3、将VirtualAddress RVA转FOA 定位到导入表;

    4、将原导入表全部Copy到空白区(只需要移动原导入表,不需要改变OriginalFirstThunk和FirstThunk);

    5、在新的导入表后面,追加一个导入表;

    6、在原导入表IID所在的内存空间,追加8个字节的INT表,8个字节的IAT表,追加一个IMAGE_IMPORT_BY_NAME 结构,前2个字节是0 后面是函数名称字符串;

    7、将IMAGE_IMPORT_BY_NAME结构的RVA赋值给INT和IAT表中的第一项,并修正导入表的OriginalFirstThunk和FirstThunk;

    8、分配空间存储DLL名称字符串 并将该字符串的RVA赋值给Name属性;

    9、修正IMAGE_DATA_DIRECTORY结构的VirtualAddress和Size。

    手动实现

    1、根据目录项,得到导入表的VritualAddress为01CE80,RVA->FOA = 1CE80

    干货分享丨表哥带你学习导入表及导入表注入

     

    2、定位到导入表

    干货分享丨表哥带你学习导入表及导入表注入

     

    导入表的大小为1cf48 - 1ce80 = 0xC8 = 200

    3、寻找代码空白区

    我们需要寻找一个空间大小>240字节的空白区[原先的导入表(200) + 新增导入表(20) + 结束符(20)]

    干货分享丨表哥带你学习导入表及导入表注入

     

    节区的代码空白区大小 = SizeofRawData - VirtualSize

    .text节满足我们的要求,我们定位到代码空白区

    干货分享丨表哥带你学习导入表及导入表注入

     

    4、将原导入表复制到空白区

    将原导入表复制到1a730处

    干货分享丨表哥带你学习导入表及导入表注入

     

    从后面的空白处开始删除200个字节(因为复制粘贴并不是覆盖原来的,而是新增了200个字节,并将原来的字节向后填充)。

    干货分享丨表哥带你学习导入表及导入表注入

     

    5、在新的导入表后,追加一个导入表。

    即从1a7f8开始,将20个字节修改为FFFFFFFF 00000000 00000000 FFFFFFFF FFFFFFFF,已备后续修改。

    对应如下:

    干货分享丨表哥带你学习导入表及导入表注入

     

    6、追加一个IMAGE_IMPORT_BY_NAME结构,前2个字节是0后面是函数名称字符串。

    原导入表的位置为1ce80,在原导入表所在的内存空间,追加8个字节的INT表,8个字节的IAT表。

    干货分享丨表哥带你学习导入表及导入表注入

     

    7、将IMAGE_IMPORT_BY_NAME结构的RVA赋值给INT和IAT表中的第一项。

    IMAGE_IMPORT_BY_NAME的FOA地址为1ce90,转换成RVA为1ce90。

    转换为小端显示:90 ce 01 00,将其填充到INT表和IAT表中。

    干货分享丨表哥带你学习导入表及导入表注入

     

    修正导入表的OriginalFirstThunk

    INT的FOA地址为:01ce80 RVA为01ce80,转换成小端显示为,80 CE 01 00修正OriginalFirstThunk。

    干货分享丨表哥带你学习导入表及导入表注入

     

    IAT的FOA地址为:01ce88 RVA为01ce88,转换成小端显示为,88 CE 01 00修正FirstThunk。

    干货分享丨表哥带你学习导入表及导入表注入

     

    8、分配空间存储DLL名称字符串,并将该字符串的RVA赋值给Name属性。

    在新增的IMAGE_IMPORT_BY_NAME后(地址为1cea1),追加DLL名称字符串。

    干货分享丨表哥带你学习导入表及导入表注入

     

    1cea1 FOA转换为RVA 1cea1,转换成小端显示A1 CE 01 00,修正导入表Name。

    干货分享丨表哥带你学习导入表及导入表注入

     

    9、修正IMAGE_DATA_DIRECTORY结构的VirtualAddress和Size。

    Size=DC+0x14(新增了一个导入表)

    VirtualAddress = FOA:1A730转换为RVA为1A730转换成小端显示:30 A7 01 00

    修正VirtualAddress 和Size:

    干货分享丨表哥带你学习导入表及导入表注入

     

    将InjectDll.dll放到与被修改的程序同一目录下,运行程序。

    干货分享丨表哥带你学习导入表及导入表注入

     

    成功弹出弹窗!

    代码实现:

    // ImportInject.cpp : Defines the entry point for the console application.
    //

    #include "stdafx.h"
    #include "stdlib.h"
    #include "windows.h"

    size_t FileLength(FILE *fp)
    {
        size_t num;

        fseek(fp,0,SEEK_END);//将文件指针移动到文件尾部
        num = ftell(fp);//获取文件指针的当前位置
        fseek(fp,0,SEEK_SET);
        return num;
    }

    LPVOID readFile(PCHAR path)//将文件中的数据读取到内存中{
        FILE* fp;
        size_t len;
        LPVOID pFileBuffer;

        fp = fopen(path,"rb");
        if(fp == NULL)
        {
            printf("error ");
        }

        len = FileLength(fp);//获取文件大小
        pFileBuffer = (LPVOID)malloc(len);//申请动态内存

        fread(pFileBuffer,len,1,fp);
        fclose(fp);
        return pFileBuffer;
    }

    DWORD RVATOFOA(DWORD RVA,LPVOID pFileBuffer){
        DWORD FOA = NULL;

        PIMAGE_DOS_HEADER pDosHeader = NULL;
        PIMAGE_NT_HEADERS pNtHeaders = NULL;
        PIMAGE_FILE_HEADER pFileHeader = NULL;
        PIMAGE_OPTIONAL_HEADER pOptionalHeader = NULL;
        PIMAGE_SECTION_HEADER pSectionHeader = NULL;

        pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
        pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
        pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNtHeaders + 4);
        pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
        pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);

        if(RVA <= pOptionalHeader->SizeOfHeaders)
            return RVA;

        //此处不需要减去ImageBase 此处的RVA是基于FileAliment和SectionAliment来说的,并不是真正的内存中的RVA,此处的程序并没有在内存中运行

        for(;RVA > (pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize);pSectionHeader++);//定位到所在节
        FOA = RVA - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
        return FOA;

    }

    DWORD FOATORVA(DWORD FOA,LPVOID pFileBuffer){
        DWORD RVA = NULL;

        PIMAGE_DOS_HEADER pDosHeader = NULL;
        PIMAGE_NT_HEADERS pNtHeaders = NULL;
        PIMAGE_FILE_HEADER pFileHeader = NULL;
        PIMAGE_OPTIONAL_HEADER pOptionalHeader = NULL;
        PIMAGE_SECTION_HEADER pSectionHeader = NULL;

        pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
        pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
        pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNtHeaders + 4);
        pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
        pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);

        if(FOA <= pOptionalHeader->SizeOfHeaders)
            return FOA;

        //此处不需要减去ImageBase 此处的RVA是基于FileAliment和SectionAliment来说的,并不是真正的内存中的RVA,此处的程序并没有在内存中运行

        for(;FOA > (pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize);pSectionHeader++);//定位到所在节
        RVA = FOA - pSectionHeader->PointerToRawData + pSectionHeader->VirtualAddress ;
        return RVA;

    }

    BOOL ImportInject(LPVOID pFileBuffer,PCHAR pInjectDllName,PCHAR pImportFunName)//Inject{
        PIMAGE_DOS_HEADER pDosHeader = NULL;//DOS头
        PIMAGE_NT_HEADERS pNtHeaders = NULL;//NT头
        PIMAGE_FILE_HEADER pFileHeader = NULL;//标准PE头
        PIMAGE_OPTIONAL_HEADER pOptionalHeader = NULL;//拓展PE头
        PIMAGE_SECTION_HEADER pSectionHeader = NULL;//节表
        PIMAGE_SECTION_HEADER pNewSec = NULL;//新节表结构

        pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
        pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
        pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNtHeaders + 4);
        pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
        pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);

        //判断是否有足够的空间添加节表
        if ((pOptionalHeader->SizeOfHeaders - ((DWORD)pSectionHeader - (DWORD)pFileBuffer + pFileHeader->NumberOfSections * 40)) < 80)
        {
            printf("空间不足");
            return false;
        }

        //新增节表结构
        pNewSec = (PIMAGE_SECTION_HEADER)(pSectionHeader + pFileHeader->NumberOfSections);

        //修改节表内容
        memcpy(pNewSec->Name,".export",8);//修改节表名

        PIMAGE_SECTION_HEADER upSecHeader = (PIMAGE_SECTION_HEADER)(pSectionHeader + pFileHeader->NumberOfSections-1);

        if(upSecHeader->Misc.VirtualSize > upSecHeader->SizeOfRawData)//修改节表VrituallAddress
        {
            pNewSec->VirtualAddress = upSecHeader->VirtualAddress + upSecHeader->Misc.VirtualSize;
        }else{
            pNewSec->VirtualAddress = upSecHeader->VirtualAddress + upSecHeader->SizeOfRawData;
        }

        pNewSec->SizeOfRawData = 0x1000;//新增的节区的大小
        pNewSec->PointerToRawData = upSecHeader->PointerToRawData + upSecHeader->SizeOfRawData;//文件中的偏移
        pNewSec->Characteristics = 0x60000020;//修改属性(可执行)

        //在新增节表后增加40个字节的空白区
        memset((LPVOID)(pNewSec+1), 0, sizeof(*pNewSec));

        //修改NT头属性

        pFileHeader->NumberOfSections += 1;//修改NumberOfSection数量
        pOptionalHeader->SizeOfImage += 0x1000;//修改SizeOfImage大小

        LPVOID NewBuffer = malloc(pOptionalHeader->SizeOfImage);//申请内存
        memset(NewBuffer, 0, pOptionalHeader->SizeOfImage);//初始化内存

        memcpy(NewBuffer, pFileBuffer,pOptionalHeader->SizeOfImage);//复制内存

        //定位导入表
        DWORD ImportDescrFOA = RVATOFOA(pOptionalHeader->DataDirectory[1].VirtualAddress,pFileBuffer);
        PIMAGE_IMPORT_DESCRIPTOR ImportDescr = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)NewBuffer + ImportDescrFOA);

        LPVOID pNewSecAddr = (LPVOID)((DWORD)NewBuffer+pNewSec->PointerToRawData);
        memcpy(pNewSecAddr,ImportDescr,pOptionalHeader->DataDirectory[1].Size);//复制导入表
        //添加新的导入表
        PIMAGE_IMPORT_DESCRIPTOR NewImport = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)NewBuffer + pNewSec->PointerToRawData + pOptionalHeader->DataDirectory[1].Size - sizeof(*NewImport));//新的导入表

        //添加INT、IAT、Name
        PIMAGE_THUNK_DATA NewINT = (PIMAGE_THUNK_DATA)((DWORD)NewBuffer + ImportDescrFOA);
        PIMAGE_THUNK_DATA NewIAT = (PIMAGE_THUNK_DATA)((DWORD)NewBuffer + ImportDescrFOA + 0x8);
        PCHAR NewDllName = (PCHAR)((DWORD)NewBuffer + ImportDescrFOA + 0x8 + 0x8);

        memcpy(NewDllName,pInjectDllName,strlen(pInjectDllName)+0x1);//Dll名

        PIMAGE_IMPORT_BY_NAME NewName = (PIMAGE_IMPORT_BY_NAME)((DWORD)NewBuffer + ImportDescrFOA + 0x8 + 0x8 + strlen(pInjectDllName)+0x1);
        NewName->Hint = 0x0;
        memcpy(NewName->Name,pImportFunName,strlen(pImportFunName)+1);//导出函数名
        memset(NewINT, 0, 0xf);//清空原有数据

        NewINT->u1.AddressOfData = (PIMAGE_IMPORT_BY_NAME)FOATORVA((DWORD)(ImportDescrFOA + 0x8 + 0x8 + strlen(pInjectDllName)+0x1),NewBuffer);
        NewIAT->u1.AddressOfData = (PIMAGE_IMPORT_BY_NAME)FOATORVA((DWORD)(ImportDescrFOA + 0x8 + 0x8 + strlen(pInjectDllName)+0x1),NewBuffer);

        //修正新增加的导入表
        NewImport->OriginalFirstThunk = FOATORVA(ImportDescrFOA,NewBuffer);
        NewImport->FirstThunk = FOATORVA(ImportDescrFOA + 0x8,NewBuffer);
        NewImport->Name = FOATORVA(ImportDescrFOA + 0x8 + 0x8,NewBuffer);

        memset((LPVOID)((DWORD)NewImport+sizeof(*NewImport)),0,sizeof(*NewImport));

        //修正 数据段
        pDosHeader = (PIMAGE_DOS_HEADER)NewBuffer;
        pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
        pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNtHeaders + 4);
        pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
        pOptionalHeader->DataDirectory[1].VirtualAddress = pNewSec->VirtualAddress;
        pOptionalHeader->DataDirectory[1].Size += 0x20;

        FILE* fp = fopen("C:\fg.exe","wb+");
        fwrite(NewBuffer,pOptionalHeader->SizeOfImage,1,fp);
        fclose(fp);

        return 0;

    }

    int main(int argc, char* argv[]){
        LPVOID pFileBuffer = NULL;
        LPVOID pNewSecFoa = NULL;
        PCHAR path = "C:\飞鸽传书.exe";

        pFileBuffer = readFile(path);
        ImportInject(pFileBuffer,"InjectDll.dll","ExportFunction");

        system("pause");
        return 0;
    }

    以上是今天分享的内容,大家看懂了吗?

  • 相关阅读:
    XDOJ1156: 等待队列
    XDOJ 1009: Josephus环的复仇
    cookie浏览器编写测试无效???
    express框架中cookie的使用
    JS设置Cookie过期时间
    CSS 伪元素
    art-template手动编写时间过滤器
    mongoose设置默认值、模块化及性能测试
    Mongoose: `findOneAndUpdate()` and `findOneAndDelete()` without the `useFindAndModify
    mongoose 联表查询之populate使用及注意事项
  • 原文地址:https://www.cnblogs.com/ichunqiu/p/12517180.html
Copyright © 2011-2022 走看看