向代码节添加代码编程实现

本文介绍了如何在PE文件的代码节中插入新的代码。首先分析PE结构找到代码节,计算插入点,然后添加调用MessageBoxA的指令及跳转回原入口点的指令。修改程序入口点以执行插入的代码,注意此方法仅适用于32位程序,且在不同开发环境中可能需要调整字符串类型。
摘要由CSDN通过智能技术生成

原理

分析PE结构,找到第一个节,假设它是代码节(后面的实验都是基于这个前提),获取其内存偏移 VirtualAddress,计算它最后一条指令的偏移的下一个字节,作为代码插入点。插入调用MessageBoxA的硬编码,和jmp到原入口点的代码。跳转地址计算公式是:X = 要跳转的地址 - 下一条指令的地址。要注意,公式中涉及的“地址”指的都是运行时的地址,即通过 [ImageBase + 偏移] 得到。
修改程序入口点,使运行时先执行我插入的代码,然后跳转到原来的入口点。

代码使用vc6开发,如果要迁移到其他环境如vs,则需要注意字符串参数的类型,要做相应的调整。另外,只适用于32位程序。

向代码节添加代码的函数

// 向代码节添加MessageBox代码
// 向代码节添加代码不需要担心内存对齐后大小发生变化
// 默认第一个节是代码节,但是这样判断不一定准确,应该遍历节表,根据属性找代码节
BOOL AddCodeToCodeSec(LPCSTR lpszFile, LPCSTR lpszOutFile)
{
   
	BYTE shellcode[] =
	{
   
		0x6A, 0x00, 0x6A, 0x00, 0x6A, 0x00, 0x6A, 0x00, // push 0 push 0 push 0 push 0
		0xE8, 0x00, 0x00, 0x00, 0x00,					// call MessageBoxA
		0xE9, 0x00, 0x00, 0x00, 0x00					// jmp OEP
	};
	DWORD dwShellCodeSize = 18;
	DWORD dwCodeRva = 0; // 插入的位置RVA
	LPVOID pFileBuffer = NULL;
	LPVOID pImageBuffer = NULL;
	LPVOID pNewBuffer = NULL;
	
	DWORD dwFileBufferSize = 0;
	DWORD dwImageBufferSize = 0;
	DWORD dwNewBufferSize = 0;
	// 读取PE到内存中
	if ((dwFileBufferSize = ReadPEFile(lpszFile, &pFileBuffer)) == 0)
	{
   
		printf("读取失败\n");
		return FALSE;
	}
// 拉伸成内存映像
dwImageBufferSize = CopyFileBufferToImageBuffer(pFileBuffer, &pImageBuffer);
if (0 == dwImageBufferSize)
{
   
	free(pFileBuffer);
	return FALSE;
}
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));
PIMAGE_SECTION_HEADER pSectionHeader = \
(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);

DWORD dwCodeSecIndex = -1;
// 遍历节表,找到代码节
for (int i = 0; i < pPEHeader->NumberOfSections; i++)
{
   
	if ((pSectionHeader[i].Characteristics & 0x60000020) == 0x60000020)
	{
   
		dwCodeSecIndex = i;
		break;
	}
}
if (dwCodeSecIndex == -1)
{
   
	printf("找不到代码节\n");
	free(pFileBuffer);
	free(pImageBuffer);
	return FALSE;
}

// 计算插入点RVA
dwCodeRva = pSectionHeader[dwCodeSecIndex].VirtualAddress + pSectionHeader[dwCodeSecIndex].Misc.VirtualSize;
// 是否有足够的空间插入代码,要考虑到代码节是最后一个节的情况
if (dwCodeSecIndex + 1 == pPEHeader->NumberOfSections)
{
   
	if (dwCodeRva + dwShellCodeSize > pOptionHeader->SizeOfImage)
	{
   
		printf("代码节没有足够的空间插入代码\n");
		free(pFileBuffer);
		free(pImageBuffer);
		return FALSE;
	}
}
else
{
   
	DWORD dwUnuseSize = pSectionHeader[dwCodeSecIndex + 1].VirtualAddress - \
		pSectionHeader[dwCodeSecIndex].VirtualAddress - pSectionHeader[dwCodeSecIndex].Misc.VirtualSize;
	if (dwUnuseSize < dwShellCodeSize)
	{
   
		printf("代码节没有足够的空间插入代码\n");
		free(pFileBuffer);
		free(pImageBuffer);
		return FALSE;
	}
}

// 代码插入点偏移 = VA + VSIZE	
dwCodeRva = pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize;
// 代码插入
memcpy((LPVOID)((DWORD)pImageBuffer + dwCodeRva), shellcode, dwShellCodeSize);
// 修正地址
DWORD MsgBoxAddr = (DWORD)&MessageBoxA; // 获取MessageBox的地址
DWORD hardCodeAddr = MsgBoxAddr - (pOptionHeader->ImageBase + dwCodeRva + 13);
memcpy((LPVOID)((DWORD)pImageBuffer + dwCodeRva + 9), &hardCodeAddr, 4);
hardCodeAddr = pOptionHeader->ImageBase + pOptionHeader->AddressOfEntryPoint \
- (pOptionHeader->ImageBase + dwCodeRva + 18);
memcpy((LPVOID)((DWORD)pImageBuffer + dwCodeRva + 14), &hardCodeAddr, 4);
// 修改入口点
pOptionHeader->AddressOfEntryPoint = dwCodeRva;
// 转成文件对齐
dwNewBufferSize = CopyImageBufferToFileBuffer(pImageBuffer, &pNewBuffer);
if (dwNewBufferSize != dwFileBufferSize)
{
   
	printf("可能丢失数据\n");
}
MemoryToFile(pNewBuffer, dwNewBufferSize, lpszOutFile);
free(pFileBuffer);
free(pImageBuffer);
free(pNewBuffer);
printf("插入代码成功\n");
return TRUE;
}

完整代码

#include "headers.h"

// 读取PE文件到内存中,返回读取的字节数;读取失败返回0
DWORD ReadPEFile(LPCSTR lpszFile, LPVOID *pFileBuffer)
{
   
	FILE *pFile = NULL;
	DWORD dwFileSize = 0;
	pFile = fopen(lpszFile, "rb");
	if (pFile == NULL) 
	{
   
		printf("打开文件失败\n");
		return 0;
	}
	fseek(pFile, 0, SEEK_END);
	dwFileSize = ftell(pFile);
	fseek(pFile, 0, SEEK_SET);
	*pFileBuffer = malloc(dwFileSize);
	if (*pFileBuffer == NULL)
	{
   
		printf("分配内存失败\n");
		fclose(pFile);
		return 0;
	}	
	DWORD dwRead = fread(*pFileBuffer, 1, dwFileSize, pFile);
	fclose(pFile);
	if (dwRead != dwFileSize)
	{
   
		printf("文件大小 = %d\t实际写入内存字节 = %d\t写入失败\n", dwFileSize, dwRead);
		return 0;
	}
	if (!IsPEFile(*pFileBuffer, dwRead))
	{
   
		printf("不是有效的PE文件\n");
		return 0;
	}
	return dwRead;
}

// 验证是否PE文件
BOOL IsPEFile(LPVOID pFileBuffer, DWORD dwSize)
{
   
	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
	PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
	PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);
	PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));
	PIMAGE_SECTION_HEADER pSectionHeader = \
		(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
	
	if (*((PWORD)pDosHeader) != IMAGE_DOS_SIGNATURE)
	{
   
		printf("不是有效的MZ标志\n");
		return FALSE;
	}
	if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
	{
   
		printf("不是有效的PE标记\n");
		return FALSE;
	}

	return TRUE;
}

// 打印PE头信息
VOID PrintNTHeaders(LPCSTR lpszFile)
{
   
	LPVOID pFileBuffer = NULL;
	DWORD dwFileSize = ReadPEFile(lpszFile, &pFileBuffer);
	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
	PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
	PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);
	PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));
	PIMAGE_SECTION_HEADER pSectionHeader = \
		(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
		
	if (dwFileSize == 0)
	{
   
		printf("读取文件失败\n");
		return;
	}

	//打印DOS头
	puts("----DOS HEADER----");
	printf("e_magic = %x\n", pDosHeader->e_magic);
	printf("e_lfanew = %x\n", pDosHeader->e_lfanew);	
	//打印NT头	
	printf("----标准PE头----\n");
	printf("Machine = %x\n", pPEHeader->Machine);
	printf("NumberOfSections = %x\n", pPEHeader->NumberOfSections);
	printf("TimeDateStamp = %x\n", pPEHead
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值