PE 空白区注入shellcode

实现流程:

1、在具有可执行属性的文件中进行写入相对应的shellcode,写入shellcode的时候需要先查看是否文件对齐内存对齐一致,如果不一致那需要先转换,如果一致就可以直接在空白的地址上进行填写!

2、填写完shellcode之后,最后的跳转要跳回原程序的入口地址,所以还需要更改跳转到原入口点 !

3、因为需要一打开就先执行shellcode,所以需要将原入口点改为当前shellcode的开始地址!


注入过程:

当前要注入的EXE的信息:


MESSAGEBOXW 0x77D66534

基址 0x1000000

AddressOfEntryPoint 入口地址为 0x739D

内存对齐0x1000

文件对齐0x200

1、先计算实际位置text节 实际文件大小 0x7748 文件对齐大小 0x7800 文件偏移0x400 内存偏移0x1000 ------> 节数据 7B48 ~ 7C00

2、当前shellode的开始地址需要进行转换为内存地址,当前地址为7B60,所以转换为 7B60-400 + 1000 + 1000000 = 1008760,这个就是内存中的地址

3、然后再进行CALL指令调用,根据公式:X = 真正要跳转的地址 - E8这条指令的下一行地址

那么就是 77D66534(要跳转的地址) - (1008760 + 8 + 5) = 76D5DDC7,则填写的就是E8 C7 DD D5 76,E8为CALL的硬编码

4、最后需要JMP到原来AddressOfEntryPoint 的地方,那么继续根据公式:X = 真正要跳转的地址(AddressOfEntryPoint) - E9这条指令的下一行地址,100739D - (1008760 + D + 5) = FFFFEC2B,则填写的就是E9 2B EC FF FF,E9为JMP的硬编码

5、因为我们还需要当程序一加载就运行shellcode,所以我们的程序入口点需要被修改,也就是AddressOfEntryPoint要被修改,修改为当前shellcode的入口地址,也就是修改为RVA,上面已经算过了1008760为VA,8760为RVA
!

6、运行测试,测试成功!

缺点:

1、这也只能算是单机版,因为没有进行动态加载获取MessageBoxA的地址,因为每个机器中的内存中user32.dll中MessageBoxA地址都不是唯一的

注意点:

1、这里的测试程序内存对齐和文件对齐都相同,所以好测试,但是如果使文件和内存对齐大小不同的话,那么就需要特别注意,此时我们在计算跳转地址的时候,需要先将FOA转换为RVA然后再进行计算填写对应的数据才行,比如入口点的地址这些填写的时候就需要先进行计算,先将FOA转换为RVA,然后再去改,原因就是对齐大小不同

2、这里的添加shellcode,我们只是在代码节中进行修改,大家都知道代码节的的属性是可执行的,如果想要在其他节中使用的话,那我们还需要注意该节的属性,也就是我们需要修改该节的属性为可执行!

代码实现:

DWORD CopyFileBufferToImageBuffer(PVOID pFileBuffer,PVOID pImageBuffer);		
DWORD CopyImageBufferToNewBuffer(PVOID pImageBuffer,PVOID* pNewBuffer);								
DWORD FOA_TO_RVA(PVOID FileAddress, DWORD FOA,PDWORD pRVA);
DWORD RVA_TO_FOA(PVOID FileAddress, DWORD RVA, PDWORD pFOA);
void MyReadFile(PVOID* pFileBuffer,PDWORD BufferLenth);
void MyWriteFile(PVOID pMemBuffer,size_t size);

#define FILENAME "C:\\Documents and Settings\\Administrator\\桌面\\NOTEPAD.EXE"
#define NEWFILENAME "C:\\Documents and Settings\\Administrator\\桌面\\NEW_NOTEPAD.EXE"



//ReadPEFile:将文件读取到缓冲区				
//参数说明:				
//lpszFile 文件路径				
//pFileBuffer 缓冲区指针				
//返回值说明:				
//读取失败返回0  否则返回实际读取的大小				
void MyReadFile(PVOID* pFileBuffer,PDWORD BufferLenth){
	FILE* File;
	File = fopen(FILENAME,"rb");

	if(File == NULL){
		printf("文件句柄打开失败");
		return;
	}

	//读取文件
	fseek(File,0,SEEK_END);
	*BufferLenth = ftell(File);
	fseek(File,0,SEEK_SET);

	//开辟新空间
	*pFileBuffer = (PVOID)malloc(*BufferLenth);

	//内存清零
	memset(*pFileBuffer,0,*BufferLenth);

	//读取到内存缓冲区
	fread(*pFileBuffer,*BufferLenth,1,File);

	//关闭文件句柄
	fclose(File);
}

//**************************************************************************								
//CopyFileBufferToImageBuffer:将文件从FileBuffer复制到ImageBuffer								
//参数说明:								
//pFileBuffer  FileBuffer指针								
//pImageBuffer ImageBuffer指针								
//返回值说明:								
//读取失败返回0  否则返回复制的大小								
						
DWORD CopyFileBufferToImageBuffer(PVOID pFileBuffer,PVOID* pImageBuffer){
	PIMAGE_DOS_HEADER pImageDosHeader = NULL;
	PIMAGE_NT_HEADERS pImageNtHeader = NULL;
	PIMAGE_FILE_HEADER pImageFileHeader = NULL;
	PIMAGE_OPTIONAL_HEADER32 pImageOptionalHeader = NULL;
	PIMAGE_SECTION_HEADER pImageSectionHeaderGroup = NULL;
	DWORD ImageBufferSize = 0;
	int i=0;
	

	// DOS头
	pImageDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;

	// 标准PE
	pImageFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pImageDosHeader + pImageDosHeader->e_lfanew + 4);

	// 可选PE
	pImageOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pImageFileHeader + IMAGE_SIZEOF_FILE_HEADER);

	//节表组
	pImageSectionHeaderGroup = (PIMAGE_SECTION_HEADER)((DWORD)pImageOptionalHeader + pImageFileHeader->SizeOfOptionalHeader);

	//获取ImageBufffer的内存大小
	ImageBufferSize = pImageOptionalHeader->SizeOfImage;
	
	//为pImageBuffer分配内存空间
	*pImageBuffer = (PVOID)malloc(ImageBufferSize);

	if (*pImageBuffer == NULL)
	{
		printf("malloc failed");
		return -1;
	}

	//清零
	memset(*pImageBuffer, 0, ImageBufferSize);
	
	// 拷贝头+节表
	memcpy(*pImageBuffer, pFileBuffer, pImageOptionalHeader->SizeOfHeaders);


	//循环拷贝节表

	for(i=0;i<pImageFileHeader->NumberOfSections;i++){
		memcpy(
			(PVOID)((DWORD)*pImageBuffer + pImageSectionHeaderGroup[i].VirtualAddress), // 要拷贝的位置 ImageBuffer中的每个节数据的偏移位置
			(PVOID)((DWORD)pFileBuffer + pImageSectionHeaderGroup[i].PointerToRawData), // 被拷贝的位置是 Filebuffer中的每个节数据的偏移位置
			pImageSectionHeaderGroup[i].SizeOfRawData // 被拷贝的大小为 每个节数据的文件对齐大小
		);
	}

	return 0;
}						


//**************************************************************************								
//CopyImageBufferToNewBuffer:将ImageBuffer中的数据复制到新的缓冲区								
//参数说明:								
//pImageBuffer ImageBuffer指针								
//pNewBuffer NewBuffer指针								
//返回值说明:								
//读取失败返回0  否则返回复制的大小															
DWORD CopyImageBufferToNewBuffer(PVOID pImageBuffer,PVOID* pNewBuffer){
	PIMAGE_DOS_HEADER pImageDosHeader = NULL;
	PIMAGE_NT_HEADERS pImageNtHeader = NULL;
	PIMAGE_FILE_HEADER pImageFileHeader = NULL;
	PIMAGE_OPTIONAL_HEADER32 pImageOptionalHeader = NULL;
	PIMAGE_SECTION_HEADER pImageSectionHeaderGroup = NULL;
	DWORD NewBufferSize = 0;
	int i;
	int j;
	
	// DOS头
	pImageDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
	
	//pImageNtHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageDosHeader + pImageDosHeader->e_lfanew);
	
	// 标准PE
	pImageFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pImageDosHeader + pImageDosHeader->e_lfanew + 4);
	
	// 可选PE
	pImageOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pImageFileHeader + IMAGE_SIZEOF_FILE_HEADER);
	
	//节表组
	pImageSectionHeaderGroup = (PIMAGE_SECTION_HEADER)((DWORD)pImageOptionalHeader + pImageFileHeader->SizeOfOptionalHeader);
	
	//获取NewBufferSize的内存大小
	NewBufferSize = pImageOptionalHeader->SizeOfHeaders;


	//再循环加上节数据的大小
	for(j=0;j<pImageFileHeader->NumberOfSections;j++){
		NewBufferSize += pImageSectionHeaderGroup[j].SizeOfRawData;
	}


	//为NewBufferSize分配内存空间
	*pNewBuffer = (PVOID)malloc(NewBufferSize);
		
	if (*pNewBuffer == NULL)
	{
		printf("malloc failed");
		return -1;
	}

	//清零
	memset(*pNewBuffer, 0, NewBufferSize);
	
	// 拷贝头+节表
	memcpy(*pNewBuffer, pImageBuffer, pImageOptionalHeader->SizeOfHeaders);
	
	
	//循环拷贝节表
	for(i=0;i<pImageFileHeader->NumberOfSections;i++){
		memcpy(
			(PVOID)((DWORD)*pNewBuffer + pImageSectionHeaderGroup[j].PointerToRawData),
			(PVOID)((DWORD)pImageBuffer + pImageSectionHeaderGroup[j].VirtualAddress),
			pImageSectionHeaderGroup[j].SizeOfRawData
		);
	}

	return NewBufferSize;
}	


//功能:FOA 转换 RVA

DWORD FOA_TO_RVA(PVOID FileAddress, DWORD FOA,PDWORD pRVA)
{
	int ret = 0;
	int i;
	
	PIMAGE_DOS_HEADER pDosHeader				= (PIMAGE_DOS_HEADER)(FileAddress);
	PIMAGE_FILE_HEADER pFileHeader				= (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);
	PIMAGE_OPTIONAL_HEADER32 pOptionalHeader	= (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + sizeof(IMAGE_FILE_HEADER));
	PIMAGE_SECTION_HEADER pSectionGroup			= (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
	
	//RVA在文件头中 或 SectionAlignment 等于 FileAlignment 时RVA等于FOA
	if (FOA < pOptionalHeader->SizeOfHeaders || pOptionalHeader->SectionAlignment == pOptionalHeader->FileAlignment)
	{
		*pRVA = FOA;
		return ret;
	}
	
	//循环判断FOA在节区中
	for (i=0;i < pFileHeader->NumberOfSections; i++)
	{
		if (FOA >= pSectionGroup[i].PointerToRawData && FOA < pSectionGroup[i].PointerToRawData + pSectionGroup[i].SizeOfRawData)
		{
			*pRVA = FOA - pSectionGroup[i].PointerToRawData + pSectionGroup[i].VirtualAddress;
			
			return *pRVA;
		}
	}
	
	//没有找到地址
	ret = -4;
	printf("func FOA_TO_RVA() Error: %d 地址转换失败!\n", ret);
	return ret;
}


//功能:RVA 转换 FOA
DWORD RVA_TO_FOA(PVOID FileAddress, DWORD RVA, PDWORD pFOA)
{
	int ret = 0;
	int i=0;
	PIMAGE_DOS_HEADER pDosHeader				= (PIMAGE_DOS_HEADER)(FileAddress);
	PIMAGE_FILE_HEADER pFileHeader				= (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);
	PIMAGE_OPTIONAL_HEADER32 pOptionalHeader	= (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + sizeof(IMAGE_FILE_HEADER));
	PIMAGE_SECTION_HEADER pSectionGroup			= (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);
	
	//RVA在文件头中 或 SectionAlignment 等于 FileAlignment 时RVA等于FOA
	if (RVA < pOptionalHeader->SizeOfHeaders || pOptionalHeader->SectionAlignment == pOptionalHeader->FileAlignment)
	{
		*pFOA = RVA;
		return ret;
	}
	
	//循环判断RVA在节区中
	for (i = 0; i < pFileHeader->NumberOfSections; i++)
	{
		if (RVA >= pSectionGroup[i].VirtualAddress && RVA < pSectionGroup[i].VirtualAddress + pSectionGroup[i].Misc.VirtualSize)
		{
			*pFOA = pSectionGroup[i].PointerToRawData + RVA - pSectionGroup[i].VirtualAddress;
			return ret;
		}
	}
	
	//没有找到地址
	ret = -4;
	printf("func RAV_TO_FOA() Error: %d 地址转换失败!\n", ret);
	return ret;
}
								

//功能:保存文件 
void MyWriteFile(PVOID pMemBuffer,size_t size){
	
	FILE* File;
	File = fopen(NEWFILENAME,"wb");
	if(File == NULL){
		printf("文件句柄打开失败");
		return;
	}
	fwrite(pMemBuffer,size,1,File);
	printf("文件保存成功!");
	fclose(File);

}


//功能:添加shellcode
void FileBufferToAddShellcode(PVOID pFileBuffer){
	PIMAGE_DOS_HEADER pImageDosHeader = NULL;
	PIMAGE_FILE_HEADER pImageFileHeader = NULL;
	PIMAGE_OPTIONAL_HEADER32 pImageOptionalHeader = NULL;
	PIMAGE_SECTION_HEADER pImageSectionHeaderGroup = NULL;

	DWORD CodeAddress = 0; //要添加shellcode的地址
	DWORD FuncAddress; //MESSAGEBOX地址
	HMODULE hModule; //加载User32

	DWORD FOA = 0;
	DWORD RVA = 0;

	BYTE SHELLCODE[] = {
		0X6A,0X00,0X6A,0X00,0X6A,0X00,0X6A,0X00,
		0XE8,0X00,0X00,0X00,0X00,
		0XE9,0X00,0X00,0X00,0X00
	};

	DWORD E8_Next_Address;
	DWORD E9_Next_Address;
	DWORD EntryOfAddress;
	DWORD CodeAddress_bak;



	// DOS头
	pImageDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
	
	// 标准PE
	pImageFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pImageDosHeader + pImageDosHeader->e_lfanew + 4);
	
	// 可选PE
	pImageOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pImageFileHeader + IMAGE_SIZEOF_FILE_HEADER);
	
	//节表组
	pImageSectionHeaderGroup = (PIMAGE_SECTION_HEADER)((DWORD)pImageOptionalHeader + pImageFileHeader->SizeOfOptionalHeader);

	//获取Messagebox的地址
	hModule = LoadLibrary("User32.dll");
	FuncAddress = (DWORD)GetProcAddress(hModule, "MessageBoxA");


	// CodeAddress为SHELLCODE在文件中的起始地址
	CodeAddress = (DWORD)pImageSectionHeaderGroup[0].PointerToRawData + (DWORD)pImageSectionHeaderGroup[0].Misc.VirtualSize;
	CodeAddress_bak = CodeAddress;


	// 计算E8这条指令的下一行地址的RVA
	E8_Next_Address = CodeAddress + 13;
	
	FOA_TO_RVA(pFileBuffer,E8_Next_Address,&RVA);


	// X = 真正要跳转的地址 - E8这条指令的下一行地址
	E8_Next_Address = FuncAddress - (RVA + pImageOptionalHeader->ImageBase);

	//填充E8空白的空白部分
	memcpy(&SHELLCODE[9], &E8_Next_Address, 4);


	// 计算E9这条指令的下一行地址的RVA
	E9_Next_Address = CodeAddress + 18;

	FOA_TO_RVA(pFileBuffer,E9_Next_Address,&RVA);

	//再获取原来入口地址的VA
	EntryOfAddress = pImageOptionalHeader->AddressOfEntryPoint;


	// X = 真正要跳转的地址 - E9这条指令的下一行地址
	E9_Next_Address = EntryOfAddress - RVA;


	memcpy(&SHELLCODE[14],&E9_Next_Address,4);

	//填充完了shellcode,最后再把shellcode放进去
	memcpy((PVOID)((DWORD)pFileBuffer+CodeAddress_bak),SHELLCODE,0x20);
	
	


	//最后替换OEP的位置,替换为shellcode的地址

	FOA_TO_RVA(pFileBuffer,CodeAddress_bak,&RVA);

	
	pImageOptionalHeader->AddressOfEntryPoint = RVA;
	
	
}
posted @ 2020-02-16 16:06  zpchcbd  阅读(1028)  评论(0编辑  收藏  举报