滴水逆向作业——PE向代码区添加messagebox的shellcode

摘要:向PE结构的代码区添加messagebox的shellcode,使打开exe文件时,先执行shellcode的内容,再执行本身的功能。

1.具体流程

在这里插入图片描述
流程如下:
先将.exe文件以文件模式(filebuffer)读取到内存中,再将其拉伸读取到另一段内存中内存模式(imagebuffer)但注意这可不是运行状态。判断代码区是否有存放shellcode代码的空间,如果有的话,将准备好的shellcode存入(关于shellcode中的操作下面会讲)。将内存模式(imagebuffer)再转化为另一个文件模式(newbuffer),最终存盘。

2.硬编码

调用一个程序时,对应的硬编码:

int main()
{
	function(); //调用function函数时对应的硬编码
	system("pause");
	return 0;
}

查看反汇编,可以看到:function(); 对应的硬编码E8 53 FE FF FF。功能:可能时跳到程序处。
在这里插入图片描述
F11单步进入,看到还有个JMP,对应的硬编码:E9 2B 2B 00 00。功能:可能时跳到function()下一句。
在这里插入图片描述

3.跳转地址

1.如下图,E8后的四个字节并不是要跳转的地址。
在这里插入图片描述
而是要满足公式:真正要跳转的地址 = E8这条指令的下一行地址 + X
也就是:要跳转的地方 = E8当前的地址 + 5 + X
计算结果如下,注意在memory中时小端模式存储,刚好对应E8后的3C31FFFF
在这里插入图片描述
2.同理,E9后的四个字节也是这么计算的。
在这里插入图片描述
要跳转的地方 = E9当前的地址 + 5 + X
在这里插入图片描述
结果为00 00 00 12。小端存储,E9后为12 00 00 00

代码如下:

#include "stdafx.h"
#include "string.h"
#include <malloc.h>
#include <windows.h>


#define lpszFile "D:\\Lib\\IPMSG2007.exe"
#define size_shellcode 0x12
#define messagebox_add 0x77630c30  
//#define file_path "D:\\Lib\\IPMSG2007.exe"
//#define write_file_path "D:\\Lib\\cp_IPMSG2007.exe"


BYTE shellcode[] = {
	0x6A,00,0x6A,00,0x6A,00,0x6A,00,
	0XE8,00,00,00,00,
	0XE9,00,00,00,00
};

char file_path[] = "D:\\Lib\\IPMSG2007.exe";
char write_file_path[] = "D:\\Lib\\cp_IPMSG2007.exe";

// exe->filebuffer  返回值为计算所得文件大小
int ReadPEFile(char* file_path,PVOID* pFileBuffer)
{
	FILE* pfile = NULL;  // 文件指针
	DWORD file_size = 0;
	LPVOID pTempFilebuffer = NULL;
	
	// 打开文件
	pfile = fopen(file_path,"rb");  // 如果有新的指针,就要进行判断
	if(!pfile)
	{
		printf("打开exe文件失败!\n");//如果分配失败就要关闭文件、释放动态内存、指针指向NULL
		return 0;
	}	
	// 读取文件大小
	fseek(pfile,0,SEEK_END);
	file_size = ftell(pfile);
	fseek(pfile,0,SEEK_SET);
	// 分配空间
	pTempFilebuffer = malloc(file_size);  // 如果有新的指针,就要进行判断
	if(!pTempFilebuffer)
	{
		printf("分配空间失败!\n");//如果分配失败就要关闭文件、释放动态内存、指针指向NULL
		fclose(pfile);
		return 0 ;
	}
	// 将数据读取到内存中
	size_t n = fread(pTempFilebuffer,file_size,1,pfile);
	if(!n)
	{
		printf("数据读取到内存中失败!\n"); //如果分配失败就要关闭文件、释放动态内存、指针指向NULL
		fclose(pfile);
		free(pTempFilebuffer);
		return 0 ;
	}
	// 关闭文件(已经读取到内存了)
	*pFileBuffer = pTempFilebuffer;
	pTempFilebuffer = NULL;
	fclose(pfile); 
	return file_size;
}

// filebuffer -> imagebuffer
DWORD CopyFileBufferToImageBuffer(PVOID pFileBuffer,PVOID* pImageBuffer)
{
	// 初始化PE头部结构体
	PIMAGE_DOS_HEADER pDosHeader = NULL;	
	PIMAGE_NT_HEADERS pNTHeader = NULL;	
	PIMAGE_FILE_HEADER pPEHeader = NULL;	
	PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
	PIMAGE_SECTION_HEADER pSectionHeader = NULL;
	// 初始化IMAGE_BUFFER指针(temparay)
	LPVOID pTempImagebuffer = NULL;

	if(!pFileBuffer)
	{
		printf("(2pimagebuffer阶段)读取到内存的pfilebuffer无效!\n");
		return 0 ;
	}
	// 判断是否是可执行文件
	if(*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)  // IMAGE_DOS_SIGNATURE是4字节,将pFileBuffer强制类型转换为4字节指针类型(PWORD)
	{
		printf("(2pimagebuffer阶段)不含MZ标志,不是exe文件!\n");
		return 0;
	}
	//强制结构体类型转换pDosHeader
	pDosHeader = PIMAGE_DOS_HEADER(pFileBuffer);
	//判断是否含有PE标志       
	if(*((PDWORD)((DWORD)pFileBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) // 注意指针的加法是:去掉一个*后的类型相加。必须转换为DWORD类型再加减。
	{																			  //相加后的和 强制类型转换为4字节指针类型(PWORD) IMAGE_NT_SIGNATURE 4BYTES
		printf("(2pimagebuffer阶段)不是有效的PE标志!\n");	
		return 0;
	}
	// 强制结构体类型转换
	pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
	pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader+4);
	pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
	pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader+pPEHeader->SizeOfOptionalHeader);

	// 分配动态内存
	pTempImagebuffer = malloc(pOptionHeader->SizeOfImage);
	if(!pTempImagebuffer)
	{
		printf("分配动态内存失败!\n");
		free(pTempImagebuffer);
		return 0;
	}
	// 初始化动态内存
	memset(pTempImagebuffer,0,pOptionHeader->SizeOfImage);
	// 拷贝头部
	memcpy(pTempImagebuffer,pDosHeader,pOptionHeader->SizeOfHeaders);
	// 循环拷贝节表
	PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
	for(DWORD i = 0;i<pPEHeader->NumberOfSections;i++,pTempSectionHeader++)
	{
		memcpy((void*)((DWORD)pTempImagebuffer+pTempSectionHeader->VirtualAddress),(void*)((DWORD)pFileBuffer+pTempSectionHeader->PointerToRawData),pTempSectionHeader->SizeOfRawData);
	}
	// 返回数据
	*pImageBuffer = pTempImagebuffer;
	pTempImagebuffer = NULL;
	return pOptionHeader->SizeOfImage;
}

//imagebuffer->newbuffer
DWORD CopyImageBufferToNewBuffer(PVOID pImageBuffer,PVOID* pNewBuffer)
{
	// 初始化PE头部结构体
	PIMAGE_DOS_HEADER pDosHeader = NULL;	
	PIMAGE_NT_HEADERS pNTHeader = NULL;	
	PIMAGE_FILE_HEADER pPEHeader = NULL;	
	PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
	PIMAGE_SECTION_HEADER pSectionHeader = NULL;

	// 初始化NEW_BUFFER指针(temparay)
	LPVOID pTempNewbuffer = NULL;

	// 判断pImageBuffer是否有效
	if(!pImageBuffer)
	{
		printf("(2pnewbuffer阶段)读取到内存的pimagebuffer无效!\n");
		return 0;
	}
	//判断是不是exe文件
	if(*((PWORD)pImageBuffer) != IMAGE_DOS_SIGNATURE)
	{
		printf("(2pnewbuffer阶段)不含MZ标志,不是exe文件!\n");
		return 0;
	}
	// 强制结构体类型转换
	pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
	if(*((PDWORD)((DWORD)pImageBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
	{
		printf("(2pnewbuffer阶段)不是有效的PE标志!\n");	
		return 0;
	}
	// 强制结构体类型转换
	pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer+pDosHeader->e_lfanew);
	pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader+4); // 这里必须强制类型转换
	pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
	pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader+pPEHeader->SizeOfOptionalHeader);

	//获取new_buffer的大小
	int new_buffer_size = pOptionHeader->SizeOfHeaders;
	for(DWORD i = 0;i<pPEHeader->NumberOfSections;i++)
	{
		new_buffer_size += pSectionHeader[i].SizeOfRawData;  // pSectionHeader[i]另一种加法
	}
	// 分配内存(newbuffer)
	pTempNewbuffer = malloc(new_buffer_size);
	if(!pTempNewbuffer)
	{
		printf("(2pnewbuffer阶段)分配Newbuffer失败!\n");
		return 0;
	}
	memset(pTempNewbuffer,0,new_buffer_size);
	// 拷贝头部
	memcpy(pTempNewbuffer,pDosHeader,pOptionHeader->SizeOfHeaders);
	// 循环拷贝节区
	PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
	for(DWORD j = 0;j<pPEHeader->NumberOfSections;j++,pTempSectionHeader++)
	{	//PointerToRawData节区在文件中的偏移,VirtualAddress节区在内存中的偏移地址,SizeOfRawData节在文件中对齐后的尺寸
		memcpy((PDWORD)((DWORD)pTempNewbuffer+pTempSectionHeader->PointerToRawData),(PDWORD)((DWORD)pImageBuffer+pTempSectionHeader->VirtualAddress),pTempSectionHeader->SizeOfRawData);
	}
	//返回数据
	*pNewBuffer = pTempNewbuffer; //暂存的数据传给参数后释放
	pTempNewbuffer = NULL;
	return new_buffer_size;  // 返回计算得到的分配内存的大小
}

//newbuffer->存盘
int newbuffer_write2_exe(PVOID NewFileBuffer,DWORD FileSize, char* FilePath)
{
	FILE* fp1 = fopen(FilePath,"wb");
	if(fp1 != NULL)
	{
		fwrite(NewFileBuffer,FileSize,1,fp1);
	}
	fclose(fp1);
	return 1;

}

int add_section(PVOID* pNewFileBuffer,PVOID pImageBuffer,DWORD FileSize)
{
	// 初始化PE头部结构体
	PIMAGE_DOS_HEADER pDosHeader = NULL;	
	PIMAGE_NT_HEADERS pNTHeader = NULL;	
	PIMAGE_FILE_HEADER pPEHeader = NULL;	
	PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
	PIMAGE_SECTION_HEADER pSectionHeader = NULL;

	// 判断pImageBuffer是否有效
	if(!pImageBuffer)
	{
		printf("读取到内存的pfilebuffer无效!\n");
		return 0;
	}
	//判断是不是exe文件
	if(*((PWORD)pImageBuffer) != IMAGE_DOS_SIGNATURE)
	{
		printf("不含MZ标志,不是exe文件!\n");
		return 0;
	}
	// 强制结构体类型转换
	pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
	if(*((PDWORD)((DWORD)pImageBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
	{
		printf("不是有效的PE标志!\n");	
		return 0;
	}
	// 强制结构体类型转换
	pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer+pDosHeader->e_lfanew);
	pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader+4); // 这里必须强制类型转换
	pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
	pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader+pPEHeader->SizeOfOptionalHeader);

	// 判断代码区大小够不够
	if(size_shellcode>(pSectionHeader->SizeOfRawData-pSectionHeader->Misc.VirtualSize))
	{
		printf("代码区空间不足!\n"); 
		free(pImageBuffer);
		return 0;
	}
	printf("pSectionHeader->PointerToRawData:%#x  pSectionHeader->Misc.VirtualSize:%#x\n",pSectionHeader->PointerToRawData,pSectionHeader->Misc.VirtualSize);
	printf("pImageBuffer:%#x\n",pImageBuffer);
	// 将代码复制到空白区
	PBYTE code_begin = (PBYTE)((DWORD)pImageBuffer+pSectionHeader->VirtualAddress+pSectionHeader->Misc.VirtualSize);
	printf("pSectionHeader->VirtualAddress:%#X\n",pSectionHeader->VirtualAddress);
	printf("code_begin:%#x\n",code_begin);
	memcpy(code_begin,shellcode,size_shellcode);
	// 修改E8 跳到messagebox
	DWORD callAddr = (messagebox_add-(pOptionHeader->ImageBase+((DWORD)(code_begin+0xD)-(DWORD)pImageBuffer)));  //(DWORD)code_begin+0xoD-(DWORD)pImageBuffer是E8下一条指令的地址的相对偏移
	printf("callAddr:%#X\n",callAddr);
	*(PDWORD)(code_begin+0x09) = callAddr; //填充数据
	// 修改E9 跳回程序开始
	DWORD jmpAddr = ((pOptionHeader->ImageBase + pOptionHeader->AddressOfEntryPoint)-(pOptionHeader->ImageBase+((DWORD)code_begin+size_shellcode-(DWORD)pImageBuffer)));
	*(PDWORD)(code_begin+0x0E) = jmpAddr;
	// 修改OEP
	pOptionHeader->AddressOfEntryPoint = (DWORD)code_begin - (DWORD)pImageBuffer;
	// imagebuffer->newfilebuffer
	int size = CopyImageBufferToNewBuffer(pImageBuffer,pNewFileBuffer);

	if(size = 0 || !pNewFileBuffer)
	{
		printf("imagebuffer->newfilebuffer失败!\n");
		free(pImageBuffer);
		free(pNewFileBuffer);
	}
	// 存盘
	int ret4 = newbuffer_write2_exe(*pNewFileBuffer,FileSize, write_file_path);
	if(!ret4)
	{
		printf("存盘失败!\n");
		return 0;
	}
	return 1;

}

void operate_pe()
{   
	// 初始化操作
	PVOID pFileBuffer = NULL;
	PVOID pNewFileBuffer = NULL;
	PVOID pImageBuffer = NULL;
	DWORD NewFileBufferSize = 0;
	// exe->filebuffer
	int ret1 = ReadPEFile(file_path,&pFileBuffer);  // &pFileBuffer(void**类型) 传递地址对其值可以进行修改
	printf("exe->filebuffer  返回值为计算所得文件大小:%#x\n",ret1);

	// filebuffer -> imagebuffer
	int ret2 = CopyFileBufferToImageBuffer(pFileBuffer,&pImageBuffer);
	printf("filebuffer -> imagebuffer返回值为计算所得文件大小:%#x\n",ret2);
	
	// add_section
	int ret3 = add_section(&pNewFileBuffer,pImageBuffer,ret1);
	printf("%d",ret3);
	free(pFileBuffer);
	free(pNewFileBuffer);
	free(pImageBuffer);
}

int main()
{	
	operate_pe();
	getchar();
	return 0;
}

结果如下:
在这里插入图片描述

需要注意的点:
1.不仅需要修改E8、E9的值还需要修改OEP的值。修改OEP是为了先执行shellcode。
2.修改E8、E9的值:注意他们要带入运行状态下的地址进行运算。
3.code_begin是shellcode的地址,要将它定义为PBYTE类型。加法运算时,可不进行强制类型转换,因为它去掉一颗星后时BYTE类型。
4.message_box的地址在每台机器上不一样,我们要先查出来。
5.OEP的值要修改为code_begin相对开始的位置的相对偏移。
6.填充数据时要强制转化为PDWORD后取星再赋值,这是因为E8、E9后都是4个字节。
程序:

// 将代码复制到空白区
	PBYTE code_begin = (PBYTE)((DWORD)pImageBuffer+pSectionHeader->VirtualAddress+pSectionHeader->Misc.VirtualSize);
	//printf("pSectionHeader->VirtualAddress:%#X\n",pSectionHeader->VirtualAddress);
	//printf("code_begin:%#x\n",code_begin);
	memcpy(code_begin,shellcode,size_shellcode);
	// 修改E8 跳到messagebox
	DWORD callAddr = (messagebox_add-(pOptionHeader->ImageBase+((DWORD)(code_begin+0xD)-(DWORD)pImageBuffer)));  //(DWORD)code_begin+0xoD-(DWORD)pImageBuffer是E8下一条指令的地址的相对偏移
	printf("callAddr:%#X\n",callAddr);
	*(PDWORD)(code_begin+0x09) = callAddr; //填充数据
	// 修改E9 跳回程序开始
	DWORD jmpAddr = ((pOptionHeader->ImageBase + pOptionHeader->AddressOfEntryPoint)-(pOptionHeader->ImageBase+((DWORD)code_begin+size_shellcode-(DWORD)pImageBuffer)));
	*(PDWORD)(code_begin+0x0E) = jmpAddr;
	// 修改OEP
	pOptionHeader->AddressOfEntryPoint = (DWORD)code_begin - (DWORD)pImageBuffer;

7.在add_section函数中调用了imagebuffer->newbuffer的函数及存盘的函数
在这里插入图片描述
main函数中传入的是pNewFileBuffer的地址,以此来实现对其的操作。
&pNewFileBuffer后变成了PVOID*类型也就是VOID 星星 类型。
在这里插入图片描述
所以在addsection函数的内部,pNewFileBuffer就是PVOID类型(VOID 星星 类型)。
1.在imagebuffer->newfilebuffer过程中要对newfilebuffer进行修改,但是因为它的类型,所以CopyImageBufferToNewBuffer函数中就不再对pNewFileBuffer取地址(因为它在addsection函数内部就是地址)
2.在存盘时要将其取
满足newbuffer_write2_exe形参的定义
在这里插入图片描述
3.CopyImageBufferToNewBuffer函数和newbuffer_write2_exe函数的形参如下
在这里插入图片描述
在这里插入图片描述

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值