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

今天的文章分享是 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\n");
    }

    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("\nDLLName:%s ",DllName);

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

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

        printf("======IAT======\n");
        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\n",*ImageThunk & 0x7FFFFFFF);
                }else
                {
                    DWORD IBNameFoa = RVATOFOA(*ImageThunk,pFileBuffer);
                    IBName = (PIMAGE_IMPORT_BY_NAME)((DWORD)pFileBuffer+IBNameFoa);
                    printf("Hit Name:%x-%s\n",IBName->Hint,IBName->Name);
                }
                ImageThunk++;
            }catch(...)
            {
                printf("\n");
                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\n");
    }

    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;
}

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

posted @ 2020-03-18 14:20  i春秋  阅读(991)  评论(0编辑  收藏  举报