首页
社区
课程
招聘
[原创]代码二次开发 C语言实现给自己的PE文件添加一个section(区段)
发表于: 2018-10-29 23:27 8759

[原创]代码二次开发 C语言实现给自己的PE文件添加一个section(区段)

2018-10-29 23:27
8759

关于添加section的相关知识或者其他方法,这里就不重复了,想具体了解可以先参考我另一篇文章——[原创]使用“PE文件加区段工具”、“LordPE”、“WinHex”、“OllyDbg”为PE文件添加section、dll(API),或者参考《加密与解密 (第4版)》第23章的相关内容,两处都有详细介绍,这里再说就重复了,阅读其他参考书籍或者教程也都可以,读者也可自己考虑使用shellcode为其添加一个API函数,关于PE文件的相关知识可参考——[原创]Windows环境下,利用VS2017编写C语言程序读取PE文件头中的各个结构,这里用的编译器同样是VS,废话不多说,直接上源码,关于源码的各部分介绍、解释都在注释里面,有疑问可以在下面讨论、留言。


#include <windows.h>
#include <Commdlg.h>
#include <stdio.h>

//引入外部库,也可以在project----setting---link中设置
#ifndef _IMAGEHLP_H 
#include "imagehlp.h"
#pragma comment ( lib, "imagehlp.lib" )
#endif

// 根据传入的dwAlign粒度调整内存中或文件中对齐后的大小
DWORD Align(DWORD dwNum, DWORD dwAlign)
{
	if (dwNum % dwAlign == 0)
	{
		return dwNum;
	}
	else
	{
		return (dwNum / dwAlign + 1) * dwAlign;
	}
}

int main(int argc, char* argv[])
{
	char szFilePath[MAX_PATH];//要分析的文件名及路径
	OPENFILENAME ofn;//定义结构,调用打开对话框选择要分析的文件及其保存路径

	HANDLE hFile;// 文件句柄
	HANDLE hMapping;// 映射文件句柄
	LPVOID ImageBase;// 映射基址

	PIMAGE_DOS_HEADER  pDH = NULL;//指向IMAGE_DOS结构的指针
	PIMAGE_NT_HEADERS  pNtH = NULL;//指向IMAGE_NT结构的指针
	PIMAGE_FILE_HEADER pFH = NULL;;//指向IMAGE_FILE结构的指针
	PIMAGE_OPTIONAL_HEADER pOH = NULL;//指向IMAGE_OPTIONALE结构的指针
	PIMAGE_SECTION_HEADER pSH1 = NULL;//指向IMAGE_SECTION_TABLE结构的指针first
	PIMAGE_SECTION_HEADER pSH2 = NULL;//指向IMAGE_SECTION_TABLE结构的指针two
	PIMAGE_SECTION_HEADER pSH3 = NULL;//指向IMAGE_SECTION_TABLE结构的指针three

	//必要的初始换
	memset(szFilePath, 0, MAX_PATH);
	memset(&ofn, 0, sizeof(ofn));
	ofn.lStructSize = sizeof(ofn);
	ofn.hwndOwner = NULL;
	ofn.hInstance = GetModuleHandle(NULL);
	ofn.nMaxFile = MAX_PATH;
	ofn.lpstrInitialDir = ".";
	ofn.lpstrFile = szFilePath;
	ofn.lpstrTitle = "选择 PE文件打开 by For";
	ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
	ofn.lpstrFilter = "*.exe\0*.exe\0";//过滤器

	if (!GetOpenFileName(&ofn))//调用打开对话框,选择要分析的文件
	{
		MessageBox(NULL, "打开文件错误", NULL, MB_OK);
		return 0;
	}

	//选择要分析的文件后,经过3步打开并映射选择的文件到虚拟内存中
	//1.创建文件内核对象,其句柄保存于hFile,将文件在物理存储器的位置通告给操作系统
	hFile = CreateFile(szFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
	if (!hFile)
	{
		MessageBox(NULL, "打开文件错误", NULL, MB_OK);
		return 0;
	}

	//2.创建文件映射内核对象(分配虚拟内存),句柄保存于hFileMapping
	hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
	if (!hMapping)
	{
		CloseHandle(hFile);
		return FALSE;
	}

	//3.将文件数据映射到进程的地址空间,返回的映射基址保存在ImageBase中
	ImageBase = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, 0);
	if (!ImageBase)
	{
		CloseHandle(hMapping);
		CloseHandle(hFile);
		return FALSE;
	}

	//IMAGE_DOS Header结构指针
	pDH = (PIMAGE_DOS_HEADER)ImageBase;
	//IMAGE_NT Header结构指针
	pNtH = (PIMAGE_NT_HEADERS)((DWORD)pDH + pDH->e_lfanew);
	//IMAGE_File Header结构指针
	pFH = &pNtH->FileHeader;
	//IMAGE_Optional Header结构指针
	pOH = &pNtH->OptionalHeader;

	//IMAGE_SECTION_TABLE结构的指针3中方法
	pSH1 = IMAGE_FIRST_SECTION(pNtH);// IMAGE_FIRST_SECTION宏
	pSH2 = (PIMAGE_SECTION_HEADER)((DWORD)pNtH + sizeof(IMAGE_NT_HEADERS));
	pSH3 = (PIMAGE_SECTION_HEADER)((DWORD)pDH + pOH->SizeOfHeaders);

	// 检查文件是否是一个有效的PE文件
	// IMAGE_DOS_SIGNATURE 该值为4D5A
	// IMAGE_NT_SIGNATURE 该值为PE00
	if (pDH->e_magic != IMAGE_DOS_SIGNATURE || pNtH->Signature != IMAGE_NT_SIGNATURE)
	{
		printf("Not valid PE file...");
		return -1;
	}

	// 创建PSection指针指向原程序中的第一个Section,并创建一个新的Section结构体secToAdd
	PIMAGE_SECTION_HEADER pSection = NULL;
	IMAGE_SECTION_HEADER secToAdd = { 0 };
	pSection = (PIMAGE_SECTION_HEADER)((BYTE*)pOH + pFH->SizeOfOptionalHeader);


	DWORD dwSectionNum = pFH->NumberOfSections;
	DWORD dwSectionAlign = pOH->SectionAlignment;
	DWORD dwFileAlign = pOH->FileAlignment;
	DWORD dwOEP = pOH->AddressOfEntryPoint;    // 程序执行入口地址
	dwOEP = (DWORD)(pOH->ImageBase + dwOEP);   // 映射起始地址+执行入口地址

	// 将PSection指向原程序中最后一个Section,根据最后一个Section的内容设置新的Section
	pSection = pSection + dwSectionNum - 1;

	// 设置新添加的section的名字
	strcpy((char *)secToAdd.Name, ".For");
	// 设置新添加的section的属性值,与最后一个section取值相同
	secToAdd.Characteristics = pSection->Characteristics;

	// 新section大小设置
	DWORD vsize = 0x234;
	secToAdd.Misc.VirtualSize = vsize;
	// 根据之前定义的Align函数调用,得到经过处理后的section尺寸大小,经调整后实际大小为0x234,对齐后大小为0x400
	secToAdd.SizeOfRawData = Align(secToAdd.Misc.VirtualSize, dwFileAlign);

	// 调用Align函数,得到经过内存对齐处理后的尺寸
	// 新Section的RVA等于原程序最后一个Section的RVA加上该节在内存中的映射尺寸
	secToAdd.VirtualAddress = pSection->VirtualAddress +
							  Align(pSection->Misc.VirtualSize, dwSectionAlign);

	// 新Section的FA地址等于最后一个Section的FA加上该节的文件对齐尺寸
	secToAdd.PointerToRawData = pSection->PointerToRawData + pSection->SizeOfRawData;
	
	// pSection指向原程序中最后一个节表的下一个,写入新的节表结构
	pSection++;
	//pSection->Characteristics = 0xE00000E0;
	secToAdd.Characteristics = 0xE00000E0;
	memcpy(pSection, &secToAdd, sizeof(IMAGE_SECTION_HEADER));

	// 输出新添加的section的信息
	char cName[9];
	char cBuff[9];
	printf("\n节表添加成功,新节表的信息为:\n");
	printf("\nName = %s", secToAdd.Name);
	//memset(cName, 0, sizeof(cName));
	//memcpy(cName, secToAdd.Name, 4);
	//puts(cName);

	printf("\nVirtualSize = %08lX", secToAdd.Misc.VirtualSize);
	//wsprintf(cBuff, "%08lX", secToAdd.Misc.VirtualSize);
	//puts(cBuff);

	printf("\nVirtualAddress = %08lX", secToAdd.VirtualAddress);
	/*wsprintf(cBuff, "%08lX", secToAdd.VirtualAddress);
	puts(cBuff);*/

	printf("\nSizeOfRawData = %08lX", secToAdd.SizeOfRawData);
	//wsprintf(cBuff, "%08lX", secToAdd.SizeOfRawData);
	//puts(cBuff);

	printf("\nPointerToRawData = %08lX", secToAdd.PointerToRawData);
	//wsprintf(cBuff, "%08lX", secToAdd.PointerToRawData);
	//puts(cBuff);
	printf("\n");

	// 更改PE文件中节表的数量
	WORD dwSizeAdd = 0x1;
	 pNtH->FileHeader.NumberOfSections+= dwSizeAdd ;
	 //pFH->NumberOfSections += 1;

	// 修改程序的映像大小
	pOH->SizeOfImage = pOH->SizeOfImage + Align(secToAdd.Misc.VirtualSize, dwSectionAlign);

	// 修改文件大小
	BYTE bNum = '\x0';
	DWORD dwWritten = 0;
	::SetFilePointer(hFile, 0, 0, FILE_END);
	::WriteFile(hFile, &bNum, secToAdd.SizeOfRawData, &dwWritten, NULL);
	::UnmapViewOfFile(ImageBase);
	::CloseHandle(hMapping);
	::CloseHandle(hFile);

	return 0;
}

二、运行结果及说明

#include <windows.h>
#include <Commdlg.h>
#include <stdio.h>

//引入外部库,也可以在project----setting---link中设置
#ifndef _IMAGEHLP_H 
#include "imagehlp.h"
#pragma comment ( lib, "imagehlp.lib" )
#endif

// 根据传入的dwAlign粒度调整内存中或文件中对齐后的大小
DWORD Align(DWORD dwNum, DWORD dwAlign)
{
	if (dwNum % dwAlign == 0)
	{
		return dwNum;
	}
	else
	{
		return (dwNum / dwAlign + 1) * dwAlign;
	}
}

int main(int argc, char* argv[])
{
	char szFilePath[MAX_PATH];//要分析的文件名及路径
	OPENFILENAME ofn;//定义结构,调用打开对话框选择要分析的文件及其保存路径

	HANDLE hFile;// 文件句柄
	HANDLE hMapping;// 映射文件句柄
	LPVOID ImageBase;// 映射基址

	PIMAGE_DOS_HEADER  pDH = NULL;//指向IMAGE_DOS结构的指针
	PIMAGE_NT_HEADERS  pNtH = NULL;//指向IMAGE_NT结构的指针
	PIMAGE_FILE_HEADER pFH = NULL;;//指向IMAGE_FILE结构的指针
	PIMAGE_OPTIONAL_HEADER pOH = NULL;//指向IMAGE_OPTIONALE结构的指针
	PIMAGE_SECTION_HEADER pSH1 = NULL;//指向IMAGE_SECTION_TABLE结构的指针first
	PIMAGE_SECTION_HEADER pSH2 = NULL;//指向IMAGE_SECTION_TABLE结构的指针two
	PIMAGE_SECTION_HEADER pSH3 = NULL;//指向IMAGE_SECTION_TABLE结构的指针three

	//必要的初始换
	memset(szFilePath, 0, MAX_PATH);
	memset(&ofn, 0, sizeof(ofn));
	ofn.lStructSize = sizeof(ofn);
	ofn.hwndOwner = NULL;
	ofn.hInstance = GetModuleHandle(NULL);
	ofn.nMaxFile = MAX_PATH;
	ofn.lpstrInitialDir = ".";
	ofn.lpstrFile = szFilePath;
	ofn.lpstrTitle = "选择 PE文件打开 by For";
	ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
	ofn.lpstrFilter = "*.exe\0*.exe\0";//过滤器

	if (!GetOpenFileName(&ofn))//调用打开对话框,选择要分析的文件
	{
		MessageBox(NULL, "打开文件错误", NULL, MB_OK);
		return 0;
	}

	//选择要分析的文件后,经过3步打开并映射选择的文件到虚拟内存中
	//1.创建文件内核对象,其句柄保存于hFile,将文件在物理存储器的位置通告给操作系统
	hFile = CreateFile(szFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
	if (!hFile)
	{
		MessageBox(NULL, "打开文件错误", NULL, MB_OK);
		return 0;
	}

	//2.创建文件映射内核对象(分配虚拟内存),句柄保存于hFileMapping
	hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
	if (!hMapping)
	{
		CloseHandle(hFile);
		return FALSE;
	}

	//3.将文件数据映射到进程的地址空间,返回的映射基址保存在ImageBase中
	ImageBase = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, 0);
	if (!ImageBase)
	{
		CloseHandle(hMapping);
		CloseHandle(hFile);
		return FALSE;
	}

	//IMAGE_DOS Header结构指针
	pDH = (PIMAGE_DOS_HEADER)ImageBase;
	//IMAGE_NT Header结构指针
	pNtH = (PIMAGE_NT_HEADERS)((DWORD)pDH + pDH->e_lfanew);
	//IMAGE_File Header结构指针
	pFH = &pNtH->FileHeader;
	//IMAGE_Optional Header结构指针
	pOH = &pNtH->OptionalHeader;

	//IMAGE_SECTION_TABLE结构的指针3中方法
	pSH1 = IMAGE_FIRST_SECTION(pNtH);// IMAGE_FIRST_SECTION宏
	pSH2 = (PIMAGE_SECTION_HEADER)((DWORD)pNtH + sizeof(IMAGE_NT_HEADERS));
	pSH3 = (PIMAGE_SECTION_HEADER)((DWORD)pDH + pOH->SizeOfHeaders);

	// 检查文件是否是一个有效的PE文件
	// IMAGE_DOS_SIGNATURE 该值为4D5A
	// IMAGE_NT_SIGNATURE 该值为PE00
	if (pDH->e_magic != IMAGE_DOS_SIGNATURE || pNtH->Signature != IMAGE_NT_SIGNATURE)
	{
		printf("Not valid PE file...");
		return -1;
	}

	// 创建PSection指针指向原程序中的第一个Section,并创建一个新的Section结构体secToAdd
	PIMAGE_SECTION_HEADER pSection = NULL;
	IMAGE_SECTION_HEADER secToAdd = { 0 };
	pSection = (PIMAGE_SECTION_HEADER)((BYTE*)pOH + pFH->SizeOfOptionalHeader);


	DWORD dwSectionNum = pFH->NumberOfSections;
	DWORD dwSectionAlign = pOH->SectionAlignment;
	DWORD dwFileAlign = pOH->FileAlignment;
	DWORD dwOEP = pOH->AddressOfEntryPoint;    // 程序执行入口地址
	dwOEP = (DWORD)(pOH->ImageBase + dwOEP);   // 映射起始地址+执行入口地址

	// 将PSection指向原程序中最后一个Section,根据最后一个Section的内容设置新的Section
	pSection = pSection + dwSectionNum - 1;

	// 设置新添加的section的名字
	strcpy((char *)secToAdd.Name, ".For");
	// 设置新添加的section的属性值,与最后一个section取值相同
	secToAdd.Characteristics = pSection->Characteristics;

	// 新section大小设置
	DWORD vsize = 0x234;
	secToAdd.Misc.VirtualSize = vsize;
	// 根据之前定义的Align函数调用,得到经过处理后的section尺寸大小,经调整后实际大小为0x234,对齐后大小为0x400
	secToAdd.SizeOfRawData = Align(secToAdd.Misc.VirtualSize, dwFileAlign);

	// 调用Align函数,得到经过内存对齐处理后的尺寸
	// 新Section的RVA等于原程序最后一个Section的RVA加上该节在内存中的映射尺寸
	secToAdd.VirtualAddress = pSection->VirtualAddress +
							  Align(pSection->Misc.VirtualSize, dwSectionAlign);

	// 新Section的FA地址等于最后一个Section的FA加上该节的文件对齐尺寸
	secToAdd.PointerToRawData = pSection->PointerToRawData + pSection->SizeOfRawData;
	
	// pSection指向原程序中最后一个节表的下一个,写入新的节表结构
	pSection++;
	//pSection->Characteristics = 0xE00000E0;
	secToAdd.Characteristics = 0xE00000E0;
	memcpy(pSection, &secToAdd, sizeof(IMAGE_SECTION_HEADER));

	// 输出新添加的section的信息
	char cName[9];
	char cBuff[9];
	printf("\n节表添加成功,新节表的信息为:\n");
	printf("\nName = %s", secToAdd.Name);
	//memset(cName, 0, sizeof(cName));
	//memcpy(cName, secToAdd.Name, 4);
	//puts(cName);

	printf("\nVirtualSize = %08lX", secToAdd.Misc.VirtualSize);
	//wsprintf(cBuff, "%08lX", secToAdd.Misc.VirtualSize);
	//puts(cBuff);

	printf("\nVirtualAddress = %08lX", secToAdd.VirtualAddress);
	/*wsprintf(cBuff, "%08lX", secToAdd.VirtualAddress);
	puts(cBuff);*/

	printf("\nSizeOfRawData = %08lX", secToAdd.SizeOfRawData);
	//wsprintf(cBuff, "%08lX", secToAdd.SizeOfRawData);
	//puts(cBuff);

	printf("\nPointerToRawData = %08lX", secToAdd.PointerToRawData);
	//wsprintf(cBuff, "%08lX", secToAdd.PointerToRawData);
	//puts(cBuff);
	printf("\n");

	// 更改PE文件中节表的数量
	WORD dwSizeAdd = 0x1;
	 pNtH->FileHeader.NumberOfSections+= dwSizeAdd ;
	 //pFH->NumberOfSections += 1;

	// 修改程序的映像大小
	pOH->SizeOfImage = pOH->SizeOfImage + Align(secToAdd.Misc.VirtualSize, dwSectionAlign);

	// 修改文件大小
	BYTE bNum = '\x0';
	DWORD dwWritten = 0;
	::SetFilePointer(hFile, 0, 0, FILE_END);
	::WriteFile(hFile, &bNum, secToAdd.SizeOfRawData, &dwWritten, NULL);
	::UnmapViewOfFile(ImageBase);
	::CloseHandle(hMapping);
	::CloseHandle(hFile);

	return 0;
}

这里打开的同样还是自建的一个HelloWorld程序

运行结果如下


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2018-10-29 23:40 被For@*编辑 ,原因:
收藏
免费 3
支持
分享
最新回复 (5)
雪    币: 3
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
优秀优秀
2018-11-1 17:34
0
雪    币: 573
活跃值: (222)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
好文啊 必须要顶。感谢分享。
2020-1-23 18:27
0
雪    币: 573
活跃值: (222)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
我有个疑问,这里为什么非要用debug 版本呢?
2020-1-29 01:28
0
雪    币: 399
活跃值: (68)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
supersoar 我有个疑问,这里为什么非要用debug 版本呢?
应该是为了后续可调式,要不就只能扔到od了
2020-1-29 10:30
0
雪    币: 220
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6

WriteFile 似乎并不能将区段内容全写成0啊,我这里用的 \x90,但是只有第一个字节是 90 :

有时候还会报错 WriteFile 还会报错1784。。 

最后于 2023-2-16 18:43 被yanghaoi编辑 ,原因:
2023-2-16 18:41
0
游客
登录 | 注册 方可回帖
返回
//