PE 移动导入表/注入

前言:

到目前的话,自己已经把滴水的PE要写的全写完了,供大家参考!

项目地址:https://github.com/adezz/MyPe

导入表注入原理:

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

导入表移动的步骤:

第一步:新增节

第二步:复制原来的导入表到新的节数据中

第三步:追加一个新的导入表

第四步:新的导入表后面继续追加8个字节的INT表 8个字节的IAT表,为什么是两个表都是八个字节呢?因为还有4个字节是作为结束标识符的

第五步:继续在后面追加一个IMAGE_IMPORT_BY_NAME 结构,前2个字节是0 后面是函数名称字符串

第六步:因为RVA和FOA还有区别,所以需要在导入表中的三个属性 INT和IAT和导入表相关的DLL名称中记录的地址都需要转换为RVA再存储进去

第七步:修正IMAGE_DATA_DIRECTORY结构的VirtualAddress和Size

根据目录项(第二个就是导入表)得到导入表信息:

typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

实现代码:

void MoveAndInjectImportTable(PVOID pFileBuffer,PDWORD OldBufferSize,PVOID* pNewBuffer){
PIMAGE_DOS_HEADER pImageDosHeader = NULL;
PIMAGE_FILE_HEADER pImageFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pImageOptionalHeader = NULL;
PIMAGE_SECTION_HEADER pImageSectionHeaderGroup = NULL;
PIMAGE_SECTION_HEADER NewSec = NULL;
PIMAGE_IMPORT_DESCRIPTOR pIMPORT_DESCRIPTOR = NULL;
PIMAGE_IMPORT_DESCRIPTOR pIMPORT_DESCRIPTOR_Temp = NULL;
PIMAGE_IMPORT_BY_NAME IMPORT_BY_NAME = NULL;
DWORD RVA = 0;
DWORD FOA = 0;
DWORD isOk;
DWORD NewLength=0;
PVOID LastSection = NULL;
PVOID CodeSection = NULL;
PVOID SectionOfNew= NULL;
PVOID SectionOfNewTemp = NULL;
pImageDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pImageFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pImageDosHeader + pImageDosHeader->e_lfanew + 4);
pImageOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pImageFileHeader + sizeof(IMAGE_FILE_HEADER));
pImageSectionHeaderGroup = (PIMAGE_SECTION_HEADER)((DWORD)pImageOptionalHeader + pImageFileHeader->SizeOfOptionalHeader);
//判断是否可以容纳相应的节表
isOk = (DWORD)pImageOptionalHeader->SizeOfHeaders - ((DWORD)pImageDosHeader->e_lfanew + IMAGE_SIZEOF_FILE_HEADER + pImageFileHeader->SizeOfOptionalHeader + 40*pImageFileHeader->NumberOfSections);
if(isOk < 80){
printf("空间太小 无法进行添加!");
return;
}
//生成对应的内存大小的空间
NewLength += *OldBufferSize + 0x1000;
*pNewBuffer = (PVOID)malloc(NewLength);
ZeroMemory(*pNewBuffer,NewLength);
//拷贝之前内存空间 到 当前新生成的内存空间
memcpy(*pNewBuffer,pFileBuffer,*OldBufferSize);
//获取新的结构体
pImageDosHeader = (PIMAGE_DOS_HEADER)(*pNewBuffer);
pImageFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pImageDosHeader + pImageDosHeader->e_lfanew + 4);
pImageOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pImageFileHeader + sizeof(IMAGE_FILE_HEADER));
pImageSectionHeaderGroup = (PIMAGE_SECTION_HEADER)((DWORD)pImageOptionalHeader + pImageFileHeader->SizeOfOptionalHeader);
// pImageFileHeader->NumberOfSections修改
pImageFileHeader->NumberOfSections = pImageFileHeader->NumberOfSections + 1;
// pImageOptionalHeader->SizeOfImage修改
pImageOptionalHeader->SizeOfImage = (DWORD)pImageOptionalHeader->SizeOfImage + 0x1000;
//复制代码段的节数据到 当前最后一个节数据后面
CodeSection = (PVOID)(&pImageSectionHeaderGroup[0]);
//新增节的位置
LastSection = (PVOID)(DWORD)(&pImageSectionHeaderGroup[pImageFileHeader->NumberOfSections-1]);
memcpy(LastSection,CodeSection,40);
//修正相关属性
NewSec = (PIMAGE_SECTION_HEADER)LastSection;
strcpy(NewSec,".NewSec");
NewSec->Misc.VirtualSize = 0x1000;
NewSec->SizeOfRawData = 0x1000;
NewSec->Characteristics = 0xC0000040;
NewSec->VirtualAddress = pImageSectionHeaderGroup[pImageFileHeader->NumberOfSections-2].VirtualAddress
+ pImageSectionHeaderGroup[pImageFileHeader->NumberOfSections-2].SizeOfRawData;
NewSec->PointerToRawData = pImageSectionHeaderGroup[pImageFileHeader->NumberOfSections-2].PointerToRawData
+ pImageSectionHeaderGroup[pImageFileHeader->NumberOfSections-2].SizeOfRawData;
//修改大小长度
*OldBufferSize = NewLength;
//这里得到新节位置的指针
SectionOfNew = (PVOID)((DWORD)*pNewBuffer + (DWORD)NewSec->PointerToRawData);
//先获取导入表的地址
RVA_TO_FOA(*pNewBuffer,pImageOptionalHeader->DataDirectory[1].VirtualAddress,&FOA);
pIMPORT_DESCRIPTOR = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)*pNewBuffer + (DWORD)FOA);
//printf("start:%x\n", pIMPORT_DESCRIPTOR);
/*
第三步:
将原导入表全部Copy到空白区
*/
SectionOfNewTemp = SectionOfNew;
while (pIMPORT_DESCRIPTOR->OriginalFirstThunk && pIMPORT_DESCRIPTOR->FirstThunk)
{
//printf("%x\n", (DWORD)SectionOfNewTemp - (DWORD)*pNewBuffer);
memcpy(SectionOfNewTemp,pIMPORT_DESCRIPTOR,20);
pIMPORT_DESCRIPTOR++;
SectionOfNewTemp = (PVOID)((DWORD)SectionOfNewTemp + 20);
}
//保存复制完导入表之后的地址
pIMPORT_DESCRIPTOR_Temp = SectionOfNewTemp;
printf("开始添加自己的导入表的地址:%x\n",(DWORD)SectionOfNewTemp-(DWORD)*pNewBuffer);
/*
第四步:
在新的导入表后面,追加一个导入表.
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk;
};
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name;
DWORD FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
*/
pIMPORT_DESCRIPTOR_Temp->TimeDateStamp = 0;
pIMPORT_DESCRIPTOR_Temp->ForwarderChain = 0;
FOA_TO_RVA(*pNewBuffer,(DWORD)pIMPORT_DESCRIPTOR_Temp + 40 - (DWORD)*pNewBuffer,&RVA); // INT表占8个字节
pIMPORT_DESCRIPTOR_Temp->OriginalFirstThunk = RVA; //这个是指向导入表相关INT表 存的是RVA,所以前面还需要转换下
FOA_TO_RVA(*pNewBuffer,(DWORD)pIMPORT_DESCRIPTOR_Temp + 40 + 8 - (DWORD)*pNewBuffer,&RVA); // IAT表占8个字节
pIMPORT_DESCRIPTOR_Temp->FirstThunk = RVA;// 这个是指向导入表相关的IAT 存的是RVA,所以前面还需要转换下
FOA_TO_RVA(*pNewBuffer,(DWORD)pIMPORT_DESCRIPTOR_Temp + 40 + 16 - (DWORD)*pNewBuffer,&RVA); // dll函数名占8个字节,这里自己就模拟 dll名称为abc.dll 长度为7个字节 最后一个字节为\0
pIMPORT_DESCRIPTOR_Temp->Name = RVA; // 这个是指向导入表相关的DLL名称 存的是RVA 所以前面还需要转换下
strcpy((PVOID)((DWORD)pIMPORT_DESCRIPTOR_Temp + 40 + 16),"abc.dll");
/*
第五步:
追加 一个_IMAGE_THUNK_DATA32结构是4个字节 但是还需要4个字节来作为结束的标识符 所以这里IAT's IMAGE_THUNK_DATA32,INT's IMAGE_THUNK_DATA32总共是占16个字节
typedef struct _IMAGE_THUNK_DATA32 {
union {
PBYTE ForwarderString;
PDWORD Function;
DWORD Ordinal; //序号
PIMAGE_IMPORT_BY_NAME AddressOfData; //指定IMAGE_IMPORT_BY_NAME
} u1;
} IMAGE_THUNK_DATA32; */
// INT
FOA_TO_RVA(*pNewBuffer, ((DWORD)pIMPORT_DESCRIPTOR_Temp + 40 + 24 - (DWORD)*pNewBuffer),&RVA);
*(PDWORD)((DWORD)pIMPORT_DESCRIPTOR_Temp + 40) = RVA; //_IMAGE_THUNK_DATA32结构中的属性指向PIMAGE_IMPORT_BY_NAME 存的是RVA 所以前面需要转换下
//IAT
FOA_TO_RVA(*pNewBuffer, ((DWORD)pIMPORT_DESCRIPTOR_Temp + 40 + 24 - (DWORD)*pNewBuffer),&RVA);
*(PDWORD)((DWORD)pIMPORT_DESCRIPTOR_Temp + 40 + 8) = RVA; //_IMAGE_THUNK_DATA32结构中的属性指向PIMAGE_IMPORT_BY_NAME 存的是RVA 所以前面需要转换下
// *(PDWORD)((DWORD)pIMPORT_DESCRIPTOR_Temp + 40 + 8) = RVA; //指向PIMAGE_IMPORT_BY_NAME 存的是RVA 所以前面需要转换下
/*
第六步:
追加一个IMAGE_IMPORT_BY_NAME 结构,前2个字节是0 后面是函数名称字符串
*/
//IMPORT_BY_NAME = (PIMAGE_IMPORT_BY_NAME)((DWORD)pIMPORT_DESCRIPTOR_Temp + 40 + 26);
//IMPORT_BY_NAME->Hint = 0;
//IMPORT_BY_NAME->Name = "myFun";
//strcpy(&IMPORT_BY_NAME->Name,"myFun");
*(PWORD)((DWORD)pIMPORT_DESCRIPTOR_Temp + 40 + 24 + 2) = 0;
strcpy((PVOID)((DWORD)pIMPORT_DESCRIPTOR_Temp + 40 + 24 + 2),"myFun");//这里写死了,函数的名称为myFun
/*
第七步:
修正IMAGE_DATA_DIRECTORY结构的VirtualAddress和Size
*/
FOA_TO_RVA(*pNewBuffer,(DWORD)NewSec->PointerToRawData,&RVA);
pImageOptionalHeader->DataDirectory[1].VirtualAddress = RVA;
//pImageOptionalHeader->DataDirectory[1].Size = (DWORD)pImageOptionalHeader->DataDirectory[1].Size + 20;
//最后进行存盘操作
MyWriteFile(*pNewBuffer, NewLength);
}

posted @   zpchcbd  阅读(809)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示