C语言实现PE的拉伸压缩和扩大、合并、增加节区

C语言实现PE的拉伸压缩和扩大、合并、增加节区

文章目录

  • C语言实现PE的拉伸压缩和扩大、合并、增加节区
    • 0.说明
    • 一.各个部分的子函数
      • 1.读取
      • 2.拉伸
      • 3.压缩
      • 4.存盘
      • 5.扩大节
      • 6.合并节
      • 7.新增节
    • 二.整体代码

0.说明

看滴水初期视频PE部分的笔记

然后自己写代码实现

文件的拉伸过程

C语言实现PE的拉伸压缩和扩大、合并、增加节区_第1张图片

PE节区扩大、合并、增大都是在拉伸之后实现的

这之中涉及了许多结构体和自定义数据,都是定义在winnt.h这个头文件中,当然也被包含于windows.h这个头文件。开始的时候都是不熟,只有多写,自然就记着了。

编译环境:vc++6.0

一.各个部分的子函数

1.读取


//功能:将文件PE读入内存1
//参数:文件地址
//返回值:指向内存1的指针
LPVOID ReadPeFile( char FileName[] )
{	
	LPVOID pFileBuffer = NULL;
	FILE* pFile = NULL;
	DWORD FileSize = 0;
	size_t flag = 0;

	//打开文件
	pFile = fopen(FileName , "rb");
	if(!pFile)
	{
		printf("open file failure!\n");
		return NULL;
	}
	
	//读取文件大小
	fseek(pFile , 0 , SEEK_END);
	FileSize = ftell( pFile );
	fseek(pFile , 0 , SEEK_SET);
	
	//分配内存1 空间 并初始化为0
	pFileBuffer = calloc( FileSize , 1 );
	if(!pFileBuffer)
	{
		printf("Failed to allocate memory space_1!\n");
		fclose(pFile);
		return NULL;
	}

	
	//读取文件数据 至 内存1
	flag = fread(pFileBuffer , FileSize , 1 , pFile );
	if(!flag)
	{
		printf("read data failure!\n");
		fclose(pFile);
		free(pFileBuffer);
		return NULL;
	}

	//关闭文件
	fclose(pFile);
//

//	printf("%x\n" , *(PWORD)(pFileBuffer));
	

	return pFileBuffer;
}

2.拉伸

在内存中,将读取到内存中的数据,按照内存对齐拉伸。

//功能:将内存1的文件copy到内存2,构造成PE内存状态
//参数:指向内存1的指针
//返回值:指向内存2的指针
LPVOID CreateImageBuffer( LPVOID pFileBuffer)
{
	LPVOID pImageBuffer = NULL;//指向内存2

	PIMAGE_DOS_HEADER pDosHeader = NULL;//DOS头 指针
	PIMAGE_NT_HEADERS32 pNtHeader = NULL ;//NT头 指针
	PIMAGE_FILE_HEADER pFileHeader = NULL;//文件头 指针
	PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;//可选头 指针
	PIMAGE_SECTION_HEADER pSectionHeader = NULL;//节区头 指针

	size_t i;//记录节区


	//检测是否是'MZ'标志
	if( *(PWORD)pFileBuffer != IMAGE_DOS_SIGNATURE )
	{
		printf("sorry , not a void 'MZ' signature!\n");
		free(pFileBuffer);
		return NULL;
	}
	pDosHeader = ( PIMAGE_DOS_HEADER )pFileBuffer;

	//检测是否是'PE'标志
	pNtHeader = ( PIMAGE_NT_HEADERS32 )( (DWORD)pFileBuffer + pDosHeader->e_lfanew );
	if(pNtHeader->Signature != IMAGE_NT_SIGNATURE )
	{
		printf("sorry , not a void 'PE' signature\n");
		free(pFileBuffer);
		return NULL;
	}

	pFileHeader = ( PIMAGE_FILE_HEADER )( (DWORD)pNtHeader + 4 );
	
	pOptionHeader = ( PIMAGE_OPTIONAL_HEADER)( (DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER );

	//分配内存2 空间 并初始化为0
	pImageBuffer = calloc( pOptionHeader->SizeOfImage , 1 );//
	if(!pImageBuffer)
	{
		printf("failed to allocate memory space_2!\n");
		free( pFileBuffer );
		return NULL;
	}


	

	//将header复制到ImageBuffer
	memcpy( pImageBuffer , pFileBuffer , pOptionHeader->SizeOfHeaders );

//	printf("SizeOfHeaders = %x\n",pOptionHeader->SizeOfHeaders );


	//将 多个 节区复制到ImageBuffer
	pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader );
	for(i=pFileHeader->NumberOfSections ; i>0 ; i--)
	{
		memcpy( (LPVOID)( (DWORD)pImageBuffer + pSectionHeader->VirtualAddress) , (LPVOID)( (DWORD)pDosHeader + pSectionHeader->PointerToRawData ) , pSectionHeader->SizeOfRawData  );
		//循环遍历 再 copy
		pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER );
	}

	//释放 内存1 的空间
	free(pFileBuffer);
	

//	printf("%x\n" , *(PWORD)(pImageBuffer));
	

	return pImageBuffer;
}

3.压缩

在内存中,将PE拉伸过后的内存状态压缩为文件状态。即将内存对齐构造为文件对齐。

//功能:将内存2中的PE内存状态copy至内存3,并构造成PE文件状态
//参数:指向内存2的指针
//返回值:指向内存3的指针
LPVOID CreateNewFileBuffer( LPVOID pImageBuffer )
{
	PIMAGE_DOS_HEADER pDosHeader = NULL;//DOS头
	PIMAGE_NT_HEADERS32 pNtHeader = NULL;//NT头
	PIMAGE_FILE_HEADER pFileHeader = NULL;//文件头
	PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;//可选头
	PIMAGE_SECTION_HEADER pSectionHeader = NULL;//节区头


	int i;//循环节区
	
	LPVOID pNewFileBuffer = NULL;//指向内存3的指针
	

	//定义 头指针
	pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;

	pNtHeader = (PIMAGE_NT_HEADERS32)( (DWORD)pDosHeader + pDosHeader->e_lfanew );

	pFileHeader = (PIMAGE_FILE_HEADER)( (DWORD)pNtHeader + 4);

	pOptionHeader = (PIMAGE_OPTIONAL_HEADER)( (DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);

	pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader );


	//计算文件Raw大小:最后 一个节区文件地址PointerToRawData+文件对齐大小SizeOfRawData
	pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader + ( (pFileHeader->NumberOfSections - 1) * IMAGE_SIZEOF_SECTION_HEADER) );
	NewFileSize = pSectionHeader->PointerToRawData + pSectionHeader->SizeOfRawData ; 

//	printf("File_Size = %x\n", FileSize );
	//分配内存, 并初始化为0
	pNewFileBuffer = calloc( NewFileSize , 1 );
	if(!pNewFileBuffer)
	{
		printf("failure to allocate memory space_3!\n");
		free( pImageBuffer );
		return NULL;
	}
	
	//将ImageBuffer复制到NewFileBuffer,并构造为文件状态
	//复制头区
	memcpy( pNewFileBuffer , pDosHeader , pOptionHeader->SizeOfHeaders );
	//复制节区
	pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader );
	for( i=pFileHeader->NumberOfSections ; i>0 ; i--)
	{
		
//这里 我复制的字节大小是Misc.VirtualSize,方便新增节区的时候,就不会填充垃圾数据。
		memcpy( (LPVOID)( (DWORD)pNewFileBuffer+pSectionHeader->PointerToRawData ) , (LPVOID)( (DWORD)pDosHeader+pSectionHeader->VirtualAddress ) , pSectionHeader->Misc.VirtualSize  );
		

		pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER );
	}
	
	//释放内存2的空间
	free(pImageBuffer);

	return pNewFileBuffer;
}

4.存盘

//功能:将内存3中的数据写入文件,更改名字
//参数:指向内存3的指针
//返回值:无
int WritePeFile( LPVOID pNewFileBuffer )
{
	FILE *pNewFile = NULL;

	int len = 0;

	//更改新的名字,写入New_文件。
	strcpy( NewFileName , FileName );

	len = strlen(NewFileName);

	strcpy( NewFileName + len - 4 , "_New.exe");

	
	pNewFile = fopen(NewFileName , "wb");
	if(!pNewFile)
	{
		printf("file creaction failed!\n");
		free( pNewFileBuffer );
		return 0;
	}

	//写入文件
	fwrite(pNewFileBuffer , NewFileSize , 1 ,  pNewFile);
	
	//关闭文件,缓冲区
	fclose(pNewFile);
	free(pNewFileBuffer);
	
	return 1;
}

5.扩大节

一般是扩大最后一个节区,然后更改节区的属性之后,即可实现添加代码之类的操作。

//功能:扩大最后一个节区的大小
//参数:指向内存2的指针
//返回值:int型变量flag,功能成功返回1,失败返回0
int EnlargeLastSection( LPVOID pImageBuffer )
{
	int flag = 0;
	
	PIMAGE_DOS_HEADER pDosHeader = NULL;
	PIMAGE_NT_HEADERS32 pNtHeader = NULL;
	PIMAGE_FILE_HEADER pFileHeader = NULL;
	PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL; 
	PIMAGE_SECTION_HEADER pSectionHeader = NULL;

	int i = 0;//循环节

	pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
	
	pNtHeader = (PIMAGE_NT_HEADERS32)( (DWORD)pDosHeader + pDosHeader->e_lfanew );

	pFileHeader = (PIMAGE_FILE_HEADER)( (DWORD)pNtHeader + 4 );

	pOptionHeader = (PIMAGE_OPTIONAL_HEADER)( (DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER );

	pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader );
	
	//扩大最后一个节的大小
	//循环遍历,指向最后一个节表	 
	for(i=pFileHeader->NumberOfSections ; i>1 ; i-- )
		pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER );
	//修改SizeOfRawData,一般我就扩大一个SectionAlignment / FileAliment的大小;
	pSectionHeader->SizeOfRawData += pOptionHeader->SectionAlignment;//pOptionHeader->FileAlignment;

	//判断是否超出SectionAlignment大小,是否需要更改SizeOfImage
	if( pSectionHeader->SizeOfRawData > pOptionHeader->SectionAlignment )
		pOptionHeader->SizeOfImage += ( pSectionHeader->SizeOfRawData  / pOptionHeader->SectionAlignment ) * pOptionHeader->SectionAlignment ;

	//最后可以修改节区属性Characteristics,这里我给他增加一个代码段的属性
	pSectionHeader->Characteristics |= 0x60000020;

	flag = 1;

	return flag;
}

6.合并节

要保证合并节区之后,原本节区里的数据依然能够引用,所以合并节的时候,存盘时,前一个节区要按照内存对齐SectionAlignment,而不是文件对齐FileAlignment。

//功能:合并最后 两个 节区
//参数:指向内存2的指针
//返回值:int型变量flag,功能成功返回1,失败返回0
int MergeLastSection( LPVOID pImageBuffer )
{
	int flag = 0;
		
	PIMAGE_DOS_HEADER pDosHeader = NULL;
	PIMAGE_NT_HEADERS32 pNtHeader = NULL;
	PIMAGE_FILE_HEADER pFileHeader = NULL;
	PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL; 
	PIMAGE_SECTION_HEADER pSectionHeader = NULL;

	int i = 0;//循环节

	pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
	
	pNtHeader = (PIMAGE_NT_HEADERS32)( (DWORD)pDosHeader + pDosHeader->e_lfanew );

	pFileHeader = (PIMAGE_FILE_HEADER)( (DWORD)pNtHeader + 4 );

	pOptionHeader = (PIMAGE_OPTIONAL_HEADER)( (DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER );

	pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader );



	//SizeOfImage可以不用改,应为合并节里面,倒数第二个节区要按照SectionAlignment对齐,而不是按照FileAlignment对齐。
 
	//循环遍历,指向倒数第二个节表,i>2。
	for(i=pFileHeader->NumberOfSections ; i>2 ; i-- )
		pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER );
	
	//更改节表的SizeOfRawData,等于倒数第二个节区的SectionAlignment对齐 加上 
	pSectionHeader->SizeOfRawData  = (pSectionHeader->SizeOfRawData / pOptionHeader->SectionAlignment + 1) * pOptionHeader->SectionAlignment + pSectionHeader->SizeOfRawData ;
	
	//将末尾两个节区的Characteristics相或,让他们有相同权限
	pSectionHeader->Characteristics |= ( (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER ) )->Characteristics ;

	//将最后一个节表 用0填充
	pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER );
	memset(pSectionHeader , 0 , IMAGE_SIZEOF_SECTION_HEADER);
	
	//最后更改NumberOfSection,我这里合并末尾你两个节,就只需减一即可。
	pFileHeader->NumberOfSections -= 1;
	flag  = 1;

	return flag;
}

7.新增节

新增节区比较麻烦,一般是新增到文件末尾,先添加节表,要考虑是否有足够的内存,所以就要做判断。

如果没有足够的内存,就将NT头上移,即减小DOS头中的e_lfanew(NT头偏移),再判断是否有足够的内存。

//功能:将内存2中的PE内存状态copy至内存3,并构造成PE文件状态
//参数:指向内存2的指针
//返回值:指向内存3的指针
LPVOID CreateNewFileBuffer( LPVOID pImageBuffer )
{
	PIMAGE_DOS_HEADER pDosHeader = NULL;//DOS头
	PIMAGE_NT_HEADERS32 pNtHeader = NULL;//NT头
	PIMAGE_FILE_HEADER pFileHeader = NULL;//文件头
	PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;//可选头
	PIMAGE_SECTION_HEADER pSectionHeader = NULL;//节区头


	int i;//循环节区
	
	LPVOID pNewFileBuffer = NULL;//指向内存3的指针
	

	//定义 头指针
	pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;

	pNtHeader = (PIMAGE_NT_HEADERS32)( (DWORD)pDosHeader + pDosHeader->e_lfanew );

	pFileHeader = (PIMAGE_FILE_HEADER)( (DWORD)pNtHeader + 4);

	pOptionHeader = (PIMAGE_OPTIONAL_HEADER)( (DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);

	pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader );


	//计算文件Raw大小:最后 一个节区文件地址PointerToRawData+文件对齐大小SizeOfRawData
	pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader + ( (pFileHeader->NumberOfSections - 1) * IMAGE_SIZEOF_SECTION_HEADER) );
	NewFileSize = pSectionHeader->PointerToRawData + pSectionHeader->SizeOfRawData ; 

//	printf("File_Size = %x\n", FileSize );
	//分配内存, 并初始化为0
	pNewFileBuffer = calloc( NewFileSize , 1 );
	if(!pNewFileBuffer)
	{
		printf("failure to allocate memory space_3!\n");
		free( pImageBuffer );
		return NULL;
	}
	
	//将ImageBuffer复制到NewFileBuffer,并构造为文件状态
	//复制头区
	memcpy( pNewFileBuffer , pDosHeader , pOptionHeader->SizeOfHeaders );
	//复制节区
	pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader );
	for( i=pFileHeader->NumberOfSections ; i>0 ; i--)
	{
		
//这里 我复制的字节大小是Misc.VirtualSize,方便新增节区的时候,就不会填充垃圾数据。
		memcpy( (LPVOID)( (DWORD)pNewFileBuffer+pSectionHeader->PointerToRawData ) , (LPVOID)( (DWORD)pDosHeader+pSectionHeader->VirtualAddress ) , pSectionHeader->Misc.VirtualSize  );
		

		pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER );
	}
	
	//释放内存2的空间
	free(pImageBuffer);

	return pNewFileBuffer;
}

二.整体代码

#include

#include

#include

#include

char FileName[500]={0};//读取的文件名(地址)

char NewFileName[555] = {0}; //写入的新的文件名(地址)

size_t NewFileSize = 0;//文件状态下的大小


//功能:将文件PE读入内存1
//参数:文件地址
//返回值:指向内存1的指针
LPVOID ReadPeFile( char FileName[] );

//功能:将内存1的文件copy到内存2,构造成PE内存状态
//参数:指向内存1的指针
//返回值:指向内存2的指针
LPVOID CreateImageBuffer( LPVOID pFileBuffer);

//功能:在内存2中,添加指定代码至节区
//参数:指向内存2的指针
//返回值:int型变量flag,功能成功返回1,失败返回0
int AddCodeToSection( LPVOID pImageBuffer);

//功能:扩大最后一个节区的大小
//参数:指向内存2的指针
//返回值:int型变量flag,功能成功返回1,失败返回0
int EnlargeLastSection( LPVOID pImageBuffer );

//功能:合并最后两个节区
//参数:指向内存2的指针
//返回值:int型变量flag,功能成功返回1,失败返回0
int MergeLastSection( LPVOID pImageBuffer );

//功能:添加一个节区
//参数:指向内存2的指针
//返回值:int型变量flag,功能成功返回1,失败返回0
int IncreaseSection( LPVOID pImageBuffer );


//功能:将内存2中的PE内存状态copy至内存3,并构造成PE文件状态
//参数:指向内存2的指针
//返回值:指向内存3的指针
LPVOID CreateNewFileBuffer( LPVOID pImageBuffer );

//功能:将内存3中的数据写入文件,更改名字
//参数:指向内存3的指针
//返回值:整数,成功返回1(非零),失败返回0
int WritePeFile( LPVOID pNewFileBuffer );//将文件状态的信息写入缓冲区




int main()
{
	LPVOID pFileBuffer= NULL;
	LPVOID pImageBuffer = NULL;
	LPVOID pNewFileBuffer = NULL;
	
	int flag = 0 , end = 0;
	int option = 0;

	printf("Please input:    (for example:  D:/user/Desktop/PE文件对齐、内存对齐/任意区段添加代码/实验.exe   )\n");
	gets(FileName);

	printf("***************************\n");
	printf(" 1.Add Code To Section  (Sorry,It's failed!) \n");
	printf(" 2.Enlarge Last Section\n");
	printf(" 3.Merge Last Section\n");
	printf(" 4.Increase Section\n");
	printf("***************************\n");
	printf("please input option:  (1 ~ 4)\n");
	scanf("%d",&option);
	if(option<1 || option>4)
	{
		printf("Sorry,not a void option!\n");
		return 0;
	}

		
	pFileBuffer = ReadPeFile( FileName );

	if(pFileBuffer)
	{
		pImageBuffer = CreateImageBuffer( pFileBuffer );

//printf("%x\n" , *(PWORD)(pFileBuffer));

		if(pImageBuffer)
		{
			switch( option )
			{
				case 1 :
					flag = AddCodeToSection( pImageBuffer );
					break;
				case 2:
					flag = EnlargeLastSection( pImageBuffer );
					break;
				case 3:
					flag = MergeLastSection( pImageBuffer );
					break;
				case 4:
					flag = IncreaseSection( pImageBuffer );
					break;
			}		
			
//flag = 1;		

			if(flag)
			{
				pNewFileBuffer = CreateNewFileBuffer( pImageBuffer);

				if(pNewFileBuffer)
				{
					end = WritePeFile( pNewFileBuffer);
					if( end )
						printf("nice,successfully!\n");
					else
						printf("sorry,failed!\n");
				}
			}
		}
	}	
//	puts(FileName);

	system("PAUSE");
	return 0;
}




//功能:将文件PE读入内存1
//参数:文件地址
//返回值:指向内存1的指针
LPVOID ReadPeFile( char FileName[] )
{	
	LPVOID pFileBuffer = NULL;
	FILE* pFile = NULL;
	DWORD FileSize = 0;
	size_t flag = 0;

	//打开文件
	pFile = fopen(FileName , "rb");
	if(!pFile)
	{
		printf("open file failure!\n");
		return NULL;
	}
	
	//读取文件大小
	fseek(pFile , 0 , SEEK_END);
	FileSize = ftell( pFile );
	fseek(pFile , 0 , SEEK_SET);
	
	//分配内存1 空间 并初始化为0
	pFileBuffer = calloc( FileSize , 1 );
	if(!pFileBuffer)
	{
		printf("Failed to allocate memory space_1!\n");
		fclose(pFile);
		return NULL;
	}

	
	//读取文件数据 至 内存1
	flag = fread(pFileBuffer , FileSize , 1 , pFile );
	if(!flag)
	{
		printf("read data failure!\n");
		fclose(pFile);
		free(pFileBuffer);
		return NULL;
	}

	//关闭文件
	fclose(pFile);
//

//	printf("%x\n" , *(PWORD)(pFileBuffer));
	

	return pFileBuffer;
}



//功能:将内存1的文件copy到内存2,构造成PE内存状态
//参数:指向内存1的指针
//返回值:指向内存2的指针
LPVOID CreateImageBuffer( LPVOID pFileBuffer)
{
	LPVOID pImageBuffer = NULL;//指向内存2

	PIMAGE_DOS_HEADER pDosHeader = NULL;//DOS头 指针
	PIMAGE_NT_HEADERS32 pNtHeader = NULL ;//NT头 指针
	PIMAGE_FILE_HEADER pFileHeader = NULL;//文件头 指针
	PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;//可选头 指针
	PIMAGE_SECTION_HEADER pSectionHeader = NULL;//节区头 指针

	size_t i;//记录节区

//	printf("%x\n" , *(PWORD)(pFileBuffer));

	//检测是否是'MZ'标志
	if( *(PWORD)pFileBuffer != IMAGE_DOS_SIGNATURE )
	{
		printf("sorry , not a void 'MZ' signature!\n");
		free(pFileBuffer);
		return NULL;
	}
	pDosHeader = ( PIMAGE_DOS_HEADER )pFileBuffer;

	//检测是否是'PE'标志
	pNtHeader = ( PIMAGE_NT_HEADERS32 )( (DWORD)pFileBuffer + pDosHeader->e_lfanew );
	if(pNtHeader->Signature != IMAGE_NT_SIGNATURE )
	{
		printf("sorry , not a void 'PE' signature\n");
		free(pFileBuffer);
		return NULL;
	}

	pFileHeader = ( PIMAGE_FILE_HEADER )( (DWORD)pNtHeader + 4 );
	
	pOptionHeader = ( PIMAGE_OPTIONAL_HEADER)( (DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER );

	//分配内存2 空间 并初始化为0
	pImageBuffer = calloc( pOptionHeader->SizeOfImage , 1 );//
	if(!pImageBuffer)
	{
		printf("failed to allocate memory space_2!\n");
		free( pFileBuffer );
		return NULL;
	}

//	printf("size = %x\n",pOptionHeader->SizeOfImage );

	

	//将header复制到ImageBuffer
	memcpy( pImageBuffer , pFileBuffer , pOptionHeader->SizeOfHeaders );

//	printf("SizeOfHeaders = %x\n",pOptionHeader->SizeOfHeaders );

//printf("1\n");

	//将 多个 节区复制到ImageBuffer
	pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader );
	for(i=pFileHeader->NumberOfSections ; i>0 ; i--)
	{
		memcpy( (LPVOID)( (DWORD)pImageBuffer + pSectionHeader->VirtualAddress) , (LPVOID)( (DWORD)pDosHeader + pSectionHeader->PointerToRawData ) , pSectionHeader->SizeOfRawData  );
		//循环遍历 再 copy
		pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER );
	}

	//释放 内存1 的空间
	free(pFileBuffer);
	

//	printf("%x\n" , *(PWORD)(pImageBuffer));
	

	return pImageBuffer;
}




//功能:在内存2中,添加指定代码至节区
//参数:指向内存2的指针
//返回值:int型变量flag,功能成功返回1,失败返回0
int AddCodeToSection( LPVOID pImageBuffer)
{
	int flag = 0;
	

	return flag;
}

//功能:扩大最后一个节区的大小
//参数:指向内存2的指针
//返回值:int型变量flag,功能成功返回1,失败返回0
int EnlargeLastSection( LPVOID pImageBuffer )
{
	int flag = 0;
	
	PIMAGE_DOS_HEADER pDosHeader = NULL;
	PIMAGE_NT_HEADERS32 pNtHeader = NULL;
	PIMAGE_FILE_HEADER pFileHeader = NULL;
	PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL; 
	PIMAGE_SECTION_HEADER pSectionHeader = NULL;

	int i = 0;//循环节

	pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
	
	pNtHeader = (PIMAGE_NT_HEADERS32)( (DWORD)pDosHeader + pDosHeader->e_lfanew );

	pFileHeader = (PIMAGE_FILE_HEADER)( (DWORD)pNtHeader + 4 );

	pOptionHeader = (PIMAGE_OPTIONAL_HEADER)( (DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER );

	pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader );
	
	//扩大最后一个节的大小
	//循环遍历,指向最后一个节表	 
	for(i=pFileHeader->NumberOfSections ; i>1 ; i-- )
		pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER );
	//修改SizeOfRawData,一般我就扩大一个SectionAlignment / FileAliment的大小;
	pSectionHeader->SizeOfRawData += pOptionHeader->SectionAlignment;//pOptionHeader->FileAlignment;

	//判断是否超出SectionAlignment大小,是否需要更改SizeOfImage
	if( pSectionHeader->SizeOfRawData > pOptionHeader->SectionAlignment )
		pOptionHeader->SizeOfImage += ( pSectionHeader->SizeOfRawData  / pOptionHeader->SectionAlignment ) * pOptionHeader->SectionAlignment ;

	//最后可以修改节区属性Characteristics,这里我给他增加一个代码段的属性
	pSectionHeader->Characteristics |= 0x60000020;

	flag = 1;

	return flag;
}

//功能:合并最后 两个 节区
//参数:指向内存2的指针
//返回值:int型变量flag,功能成功返回1,失败返回0
int MergeLastSection( LPVOID pImageBuffer )
{
	int flag = 0;
		
	PIMAGE_DOS_HEADER pDosHeader = NULL;
	PIMAGE_NT_HEADERS32 pNtHeader = NULL;
	PIMAGE_FILE_HEADER pFileHeader = NULL;
	PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL; 
	PIMAGE_SECTION_HEADER pSectionHeader = NULL;

	int i = 0;//循环节

	pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
	
	pNtHeader = (PIMAGE_NT_HEADERS32)( (DWORD)pDosHeader + pDosHeader->e_lfanew );

	pFileHeader = (PIMAGE_FILE_HEADER)( (DWORD)pNtHeader + 4 );

	pOptionHeader = (PIMAGE_OPTIONAL_HEADER)( (DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER );

	pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader );



	//SizeOfImage可以不用改,应为合并节里面,倒数第二个节区要按照SectionAlignment对齐,而不是按照FileAlignment对齐。
 
	//循环遍历,指向倒数第二个节表,i>2。
	for(i=pFileHeader->NumberOfSections ; i>2 ; i-- )
		pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER );
	
	//更改节表的SizeOfRawData,等于倒数第二个节区的SectionAlignment对齐 加上 
	pSectionHeader->SizeOfRawData  = (pSectionHeader->SizeOfRawData / pOptionHeader->SectionAlignment + 1) * pOptionHeader->SectionAlignment + pSectionHeader->SizeOfRawData ;
	
	//将末尾两个节区的Characteristics相或,让他们有相同权限
	pSectionHeader->Characteristics |= ( (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER ) )->Characteristics ;

	//将最后一个节表 用0填充
	pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER );
	memset(pSectionHeader , 0 , IMAGE_SIZEOF_SECTION_HEADER);
	
	//最后更改NumberOfSection,我这里合并末尾你两个节,就只需减一即可。
	pFileHeader->NumberOfSections -= 1;
	flag  = 1;

	return flag;
}


//功能:添加一个节表和节区
//参数:指向内存2的指针
//返回值:int型变量flag,功能成功返回1,失败返回0
int IncreaseSection( LPVOID pImageBuffer )
{
	int flag = 0;

	PIMAGE_DOS_HEADER pDosHeader = NULL;
	PIMAGE_NT_HEADERS32 pNtHeader = NULL;
	PIMAGE_FILE_HEADER pFileHeader = NULL;
	PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL; 
	PIMAGE_SECTION_HEADER pSectionHeader = NULL;

	PIMAGE_SECTION_HEADER pSectionHeader_First = NULL;//多增加一个节表指针指向第一个节表首地址,查看PointerToRawData。
	PIMAGE_SECTION_HEADER pSectionHeader_Last = NULL;//定义最后一个节表指针,方便获取VirtualAddress 和 SizeOfRawData

	int i = 0;//循环节

	pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
	
	pNtHeader = (PIMAGE_NT_HEADERS32)( (DWORD)pDosHeader + pDosHeader->e_lfanew );

	pFileHeader = (PIMAGE_FILE_HEADER)( (DWORD)pNtHeader + 4 );

	pOptionHeader = (PIMAGE_OPTIONAL_HEADER)( (DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER );

	pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader );

	pSectionHeader_First = pSectionHeader ;

	//循环遍历,指向倒数第一个节表再往后移,指向我们将要添加节表的位置 ,i>0。
	for(i=pFileHeader->NumberOfSections ; i>0 ; i-- )
		pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER );

	//先判断是否有节表的区域是否还有空间 用于添加节表。确保在SizeOfRawData范围内。
	//这里一般要空出两个节表位置,一个用于我们添加新节表,一个用于空的空间填充0(约定俗成)。
	if(  ((DWORD)pDosHeader + pSectionHeader_First->PointerToRawData) - (DWORD)pSectionHeader < 2*IMAGE_SIZEOF_SECTION_HEADER   )
	{	
		//如果没有足够空间,就考虑缩短 pDosHeader->e_lfanew,有没有足够空间,用于开辟新的空间。0x40为DOS头的大小
		if(  (pDosHeader->e_lfanew - 0x40) +  (  ((DWORD)pDosHeader + pSectionHeader_First->PointerToRawData) - (DWORD)pSectionHeader  ) > 2*IMAGE_SIZEOF_SECTION_HEADER  )
		{
			memcpy( (LPVOID)( (DWORD)pDosHeader+ 0x40) , pNtHeader , ( (DWORD)pSectionHeader- (DWORD)pFileHeader ) ); 
		
			//修改e_lfanew
			pDosHeader->e_lfanew = 0x40;

			//重新计算各头区的偏移
			pNtHeader = (PIMAGE_NT_HEADERS32)( (DWORD)pDosHeader + pDosHeader->e_lfanew );
			pFileHeader = (PIMAGE_FILE_HEADER)( (DWORD)pNtHeader + 4 );
			pOptionHeader = (PIMAGE_OPTIONAL_HEADER)( (DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER );
			pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader );

			pSectionHeader_First = pSectionHeader ;

			//同样  //循环遍历,指向倒数第一个节表再往后移,指向我们将要添加节表的位置 ,i>0。
			for(i=pFileHeader->NumberOfSections ; i>0 ; i-- )
				pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER );

			//此时已经符合条件,有足够空间用于新增节表头。
			//即退出if语句后。
		}
		else//没有足够空间则不能添加节,flag=0,失败
		{
			free(pImageBuffer);//释放空间
			flag = 0;
			printf("Sorry,There is not enough space of new section header!\n");
			return flag ; 
		}
	}

	//复制一个节表头过来。
	memcpy( pSectionHeader , pSectionHeader_First , IMAGE_SIZEOF_SECTION_HEADER );

	//更改节表名.NewSec
	strcpy( pSectionHeader->Name,".NewSec");

	//Misc.VirtualSize = 0,那下面构造内存3的文件状态时,就不会往节区填充垃圾数据
	pSectionHeader->Misc.VirtualSize = 0;

	//SizeOfRawData = FileAlignment
	pSectionHeader->SizeOfRawData = pOptionHeader->FileAlignment ;
	
	//RVA
	pSectionHeader_Last = (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader - IMAGE_SIZEOF_SECTION_HEADER);
	pSectionHeader->VirtualAddress = pSectionHeader_Last->VirtualAddress + ( (pSectionHeader_Last->SizeOfRawData-1) / pOptionHeader->SectionAlignment + 1 )*pOptionHeader->SectionAlignment ;

	//PointerToRawData
	pSectionHeader->PointerToRawData = pSectionHeader_Last->PointerToRawData + pSectionHeader_Last->SizeOfRawData;

	//Characteristics,权限,相 或 即可,我给他加上数据段的权限0x42000040
	pSectionHeader->Characteristics |= 0x42000040;

	//SizeOfImage 
	pOptionHeader->SizeOfImage += ( (pSectionHeader->SizeOfRawData-1) / pOptionHeader->FileAlignment +1 ) * pOptionHeader->SectionAlignment ;

	//NumberOfSection
	pFileHeader->NumberOfSections += 1;

	//将新添加的节区按照VitualSize大小,用6填充
	//memset( (LPVOID)( (DWORD)pDosHeader + pSectionHeader->VirtualAddress) , 66 , pSectionHeader->Misc.VirtualSize );
	//填充不了,应为这个地址没有开辟空间。。。。。。。

	//最后成功之后falg=1;
	flag = 1;

	return flag;
}


//功能:将内存2中的PE内存状态copy至内存3,并构造成PE文件状态
//参数:指向内存2的指针
//返回值:指向内存3的指针
LPVOID CreateNewFileBuffer( LPVOID pImageBuffer )
{
	PIMAGE_DOS_HEADER pDosHeader = NULL;//DOS头
	PIMAGE_NT_HEADERS32 pNtHeader = NULL;//NT头
	PIMAGE_FILE_HEADER pFileHeader = NULL;//文件头
	PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;//可选头
	PIMAGE_SECTION_HEADER pSectionHeader = NULL;//节区头


	int i;//循环节区
	
	LPVOID pNewFileBuffer = NULL;//指向内存3的指针
	

	//定义 头指针
	pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;

	pNtHeader = (PIMAGE_NT_HEADERS32)( (DWORD)pDosHeader + pDosHeader->e_lfanew );

	pFileHeader = (PIMAGE_FILE_HEADER)( (DWORD)pNtHeader + 4);

	pOptionHeader = (PIMAGE_OPTIONAL_HEADER)( (DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);

	pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader );


	//计算文件Raw大小:最后 一个节区文件地址PointerToRawData+文件对齐大小SizeOfRawData
	pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader + ( (pFileHeader->NumberOfSections - 1) * IMAGE_SIZEOF_SECTION_HEADER) );
	NewFileSize = pSectionHeader->PointerToRawData + pSectionHeader->SizeOfRawData ; 

//	printf("File_Size = %x\n", FileSize );
	//分配内存, 并初始化为0
	pNewFileBuffer = calloc( NewFileSize , 1 );
	if(!pNewFileBuffer)
	{
		printf("failure to allocate memory space_3!\n");
		free( pImageBuffer );
		return NULL;
	}
	
	//将ImageBuffer复制到NewFileBuffer,并构造为文件状态
	//复制头区
	memcpy( pNewFileBuffer , pDosHeader , pOptionHeader->SizeOfHeaders );
	//复制节区
	pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader );
	for( i=pFileHeader->NumberOfSections ; i>0 ; i--)
	{
		
//这里 我复制的字节大小是Misc.VirtualSize,方便新增节区的时候,就不会填充垃圾数据。
		memcpy( (LPVOID)( (DWORD)pNewFileBuffer+pSectionHeader->PointerToRawData ) , (LPVOID)( (DWORD)pDosHeader+pSectionHeader->VirtualAddress ) , pSectionHeader->Misc.VirtualSize  );
		

		pSectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER );
	}
	
	//释放内存2的空间
	free(pImageBuffer);

	return pNewFileBuffer;
}


//功能:将内存3中的数据写入文件,更改名字
//参数:指向内存3的指针
//返回值:无
int WritePeFile( LPVOID pNewFileBuffer )
{
	FILE *pNewFile = NULL;

	int len = 0;

	//更改新的名字,写入New_文件。
	strcpy( NewFileName , FileName );

	len = strlen(NewFileName);

	strcpy( NewFileName + len - 4 , "_New.exe");

	
	pNewFile = fopen(NewFileName , "wb");
	if(!pNewFile)
	{
		printf("file creaction failed!\n");
		free( pNewFileBuffer );
		return 0;
	}

	//写入文件
	fwrite(pNewFileBuffer , NewFileSize , 1 ,  pNewFile);
	
	//关闭文件,缓冲区
	fclose(pNewFile);
	free(pNewFileBuffer);
	
	return 1;
}

————————————————
欢迎大家留言交流

你可能感兴趣的:(PE文件结构,C语言)