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;
	
	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);
}
原文地址:https://www.cnblogs.com/zpchcbd/p/13072480.html