前言:
到目前的话,自己已经把滴水的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;
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->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
", pIMPORT_DESCRIPTOR);
/*
第三步:
将原导入表全部Copy到空白区
*/
SectionOfNewTemp = SectionOfNew;
while (pIMPORT_DESCRIPTOR->OriginalFirstThunk && pIMPORT_DESCRIPTOR->FirstThunk)
{
//printf("%x
", (DWORD)SectionOfNewTemp - (DWORD)*pNewBuffer);
memcpy(SectionOfNewTemp,pIMPORT_DESCRIPTOR,20);
pIMPORT_DESCRIPTOR++;
SectionOfNewTemp = (PVOID)((DWORD)SectionOfNewTemp + 20);
}
//保存复制完导入表之后的地址
pIMPORT_DESCRIPTOR_Temp = SectionOfNewTemp;
printf("开始添加自己的导入表的地址:%x
",(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->TimeDateStamp = 0;
pIMPORT_DESCRIPTOR->ForwarderChain = -1;
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个字节 最后一个字节为
pIMPORT_DESCRIPTOR_Temp->Name = RVA; // 这个是指向导入表相关的DLL名称 存的是RVA 所以前面还需要转换下
strcpy((PVOID)((DWORD)pIMPORT_DESCRIPTOR_Temp + 40 + 16),"abc.dll");
/*
第五步:
追加8个字节的INT表 8个字节的IAT表 ,一个_IMAGE_THUNK_DATA32结构是4个字节 但是还需要4个字节来作为结束的标识符 所以这里总共是占16个字节
*/
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 所以前面需要转换下
FOA_TO_RVA(*pNewBuffer, ((DWORD)pIMPORT_DESCRIPTOR_Temp + 40 + 24 - (DWORD)*pNewBuffer),&RVA);
*(PDWORD)((DWORD)pIMPORT_DESCRIPTOR_Temp + 40 + 8) = RVA; //指向PIMAGE_IMPORT_BY_NAME 存的是RVA 所以前面需要转换下
/*
第六步:
追加一个IMAGE_IMPORT_BY_NAME 结构,前2个字节是0 后面是函数名称字符串
*/
*(PWORD)((DWORD)pIMPORT_DESCRIPTOR_Temp + 40 + 26) = 0;
strcpy((PVOID)((DWORD)pIMPORT_DESCRIPTOR_Temp + 40 + 26),"myFun");//这里写死了,函数的名称为myFun
/*
第七步:
修正IMAGE_DATA_DIRECTORY结构的VirtualAddress和Size
*/
FOA_TO_RVA(*pNewBuffer,(DWORD)SectionOfNew - (DWORD)*pNewBuffer,&RVA);
pImageOptionalHeader->DataDirectory[1].VirtualAddress = RVA;
pImageOptionalHeader->DataDirectory[1].Size = (DWORD)pImageOptionalHeader->DataDirectory[1].Size + 20;
//最后进行存盘操作
MyWriteFile(*pNewBuffer, NewLength);
}