干貨分享丨表哥帶你學習導入表及導入表注入


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

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM