首页 > 技术文章 > 导入表注入

ShiningArmor 2019-11-21 10:26 原文

1.主要思路
    新建一个测试用的dll,在dll的入口函数中添加一个在加载阶段执行的函数Init();
    给目标程序添加一个新节,将导入表移动到新节中;
        IAT表不能移动,因为程序在调用dll中的函数时,都是call的IAT表的绝对地址;然后在IAT表中找到真实的函数地址;
        如果移动了IAT表,需要修复程序中所有使用了IAT表绝对地址的地方,而这些地方难以确定,加上数量多,代价太大;
    在新节中添加一个新的导入表,引用测试用的dll;
        必须至少导入一个dll中的函数,也就是IAT和INT表中至少插入一项,否则系统会忽略该dll;  
    
    踩坑:  
        新节表的Characteristics属性应该为:c0000040,否则程序无法运行;
 
2.实现
#include "stdafx.h"
#include "PeTool.h"
#include "string.h"
 
#define SRC "C:\\Users\\Administrator\\Desktop\\TraceMe.exe"
#define DEST "C:\\Users\\Administrator\\Desktop\\TraceMe_new.exe"
 
//新增一个节
DWORD addSec(LPVOID pFileBuffer, LPVOID* pSec){
    //1.定义pe头结构指针
    PIMAGE_DOS_HEADER dosHeader = NULL;        //dos头指针
    PIMAGE_FILE_HEADER peHeader = NULL;        //pe头指针
    PIMAGE_OPTIONAL_HEADER32 opHeader = NULL;    //可选pe头指针
    PIMAGE_SECTION_HEADER seHeader = NULL;    //节表指针
 
    //2.初始化头结构指针
    dosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
    if(dosHeader->e_magic != IMAGE_DOS_SIGNATURE){
        printf("不是有效MZ标记\n");
        return 0;
    }
    if(*((PDWORD)((DWORD)pFileBuffer + dosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE){
        printf("不是有效PE标记\n");
        free(pFileBuffer);
        return 0;
    }
    peHeader = (PIMAGE_FILE_HEADER) ((DWORD)pFileBuffer + dosHeader->e_lfanew + 4);
    opHeader = (PIMAGE_OPTIONAL_HEADER32) ((DWORD)peHeader + IMAGE_SIZEOF_FILE_HEADER);
    seHeader = (PIMAGE_SECTION_HEADER) ((DWORD)opHeader + peHeader->SizeOfOptionalHeader);
 
    //3.新增一个节表
    PIMAGE_SECTION_HEADER newSec = seHeader + peHeader->NumberOfSections;    //新节表的指针
    if(((DWORD)pFileBuffer + opHeader->SizeOfHeaders - (DWORD)newSec) < 80){
        printf("空间不足插入新的节表\n");
        return 0;
    }
 
    //4.设置新节表
    strcpy((char*)newSec->Name, ".Inject");
    newSec->Misc.VirtualSize = 0x1000;                    //新节的内存镜像大小为1000
    newSec->VirtualAddress = opHeader->SizeOfImage;        //新节的内存偏移为内存镜像大小
    newSec->SizeOfRawData = 0x1000;                        //新节的文件镜像大小为1000
    PIMAGE_SECTION_HEADER lastSec = seHeader + (peHeader->NumberOfSections -1);    //最后一个节表
    newSec->PointerToRawData = lastSec->PointerToRawData + lastSec->SizeOfRawData;    //新节的文件偏移紧接最后一个节
    newSec->Characteristics = 0xc0000040;        //导入表所在节的属性为c0000040,否则程序无法运行
    
    //5.设置全0节表
    memset((LPVOID)(newSec+1), 0, 40);
 
    //6.修头信息
    peHeader->NumberOfSections = peHeader->NumberOfSections + 1;
    opHeader->SizeOfImage = opHeader->SizeOfImage + 0x1000;
 
    //7.申请内存
    LPVOID sec = malloc(0x1000);
    if(!sec){
        printf("给新节申请内存失败\n");
        return 0;
    }
    memset(sec, 0, 0x1000);
 
    //8.返回
    *pSec = sec;
    return 0x1000;
}
 
//导入表注入
void dllInject(){
    //定义pe头结构指针
    PIMAGE_DOS_HEADER dosHeader = NULL;        //dos头指针
    PIMAGE_FILE_HEADER peHeader = NULL;        //pe头指针
    PIMAGE_OPTIONAL_HEADER32 opHeader = NULL;    //可选pe头指针
    PIMAGE_DATA_DIRECTORY dataDir = NULL;        //数据目录指针
    PIMAGE_IMPORT_DESCRIPTOR importDir= NULL;    //导入表指针;   
 
    //1.将文件读入内存
    LPVOID pFileBuffer = NULL;
    DWORD fileSize = ReadPEFile(SRC, &pFileBuffer);
    if(!pFileBuffer){
        printf("读取dll文件失败\n");
        return;
    }
 
    //2.初始化头结构指针
    dosHeader = (PIMAGE_DOS_HEADER) pFileBuffer;
    peHeader = (PIMAGE_FILE_HEADER) ((DWORD)pFileBuffer + dosHeader->e_lfanew + 4);
    opHeader = (PIMAGE_OPTIONAL_HEADER32) ((DWORD)peHeader + IMAGE_SIZEOF_FILE_HEADER);
    dataDir = opHeader ->DataDirectory;
    importDir = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, (dataDir + 1)->VirtualAddress ));
 
    //3.新增一个节
    LPVOID newSec = NULL;
    DWORD secSize = addSec(pFileBuffer, &newSec);
    if(!newSec){
        printf("新增节失败\n");
        return;
    }
 
    //4.复制导入表
    PIMAGE_IMPORT_DESCRIPTOR copyImport = (PIMAGE_IMPORT_DESCRIPTOR)newSec;
    LPVOID copyDir = newSec;
    int i = 0;
    while((importDir+i) -> OriginalFirstThunk){
        memcpy(copyDir, (LPVOID)(importDir+i), 20);
        copyDir = (LPVOID)((DWORD)copyDir + 20);
        i++;
    }
    (dataDir + 1) -> VirtualAddress = fileSize;    //修复数据目录
 
    //5.新增一个导入表
    PIMAGE_IMPORT_DESCRIPTOR newImport = (PIMAGE_IMPORT_DESCRIPTOR)copyDir;
 
    //添加IMAGE_IMPORT_BY_NAME
    PIMAGE_IMPORT_BY_NAME newImportName = (PIMAGE_IMPORT_BY_NAME)((DWORD)newImport + 20*2);
    newImportName -> Hint = 1;
    LPSTR newFunName = (LPSTR)((DWORD)newImportName + 2);
    strcpy(newFunName, "ExportFunction");    //将待注入的dll中的导出的函数名设置到这里
 
    //添加INT表
    PDWORD newInt = (PDWORD) ((DWORD)newFunName + strlen(newFunName) + 1);
    *newInt = fileSize + ((DWORD)newImportName - (DWORD)newSec);
    newImport ->OriginalFirstThunk = fileSize + ((DWORD)newInt - (DWORD)newSec);    //修复新导入表   
 
    //添加IAT表
    LPVOID newIat = (LPVOID) ((DWORD)newInt + 8);
    memcpy(newIat, (LPVOID)newInt, 8);
    newImport ->FirstThunk = fileSize + ((DWORD)newIat - (DWORD)newSec);    //修复新导入表  
    
    //添加dll名
    LPSTR newDllName = (LPSTR)((DWORD)newIat + 8);
    strcpy(newDllName, "InjectDll.dll");    //将待注入的dll名设置在这里
    newImport ->Name = fileSize + ((DWORD)newDllName - (DWORD)newSec);    //修复新导入表  
    
    //6.写出新文件
    FILE* newFile = fopen(DEST, "a+b");
    if(!newFile){
        printf("打开新文件失败\n");
        free(pFileBuffer);
        free(newSec);
        return;
    }
    size_t m = fwrite(pFileBuffer, fileSize, 1, newFile);
    if(!m){
        printf("写出文件第一部分失败\n");
        fclose(newFile);
        free(pFileBuffer);
        free(newSec);
        return;
    }
    //写出新节
    size_t n = fwrite(newSec, secSize, 1, newFile);
    if(!n){
        printf("写出文件第二部分失败\n");
        fclose(newFile);
        free(pFileBuffer);
        free(newSec);
        return;
    }
 
    //关闭文件并返回
    fclose(newFile);
    free(pFileBuffer);
    free(newSec);
    printf("done\n");
    return;
}
 
int main(int argc, char* argv[])
{
    //导入表注入
    dllInject();
    getchar();
}

 

结果:可以看到在程序运行前弹出了窗口;
    说明dll的入后函数被调用;
    也就是dll被成功注入;
 
 
 
 

推荐阅读