手工解析PE(文件拉伸)

我们知道 一个exe文件在硬盘中的存储形式跟在内存中的存储形式是不同的,主要表现在对齐方式不同,这次模拟了一个文件的加载动作,先将exe文件读到内存FileBuffer中,此时FileBuffer跟文件在硬盘中的格式是一样的,然后将FileBuffer拉伸,读取到内存ImageBuffer中,此时对齐方式由硬盘中的200h变为内存中的1000h,再将ImageBuffer还原成FileBuffer,放到NewBuffer里面,最后将NewBuffer的内容重新写到一个exe文件中,经过这一系列的变换,最终新旧exe的PE结构是一致的

拉伸和还原的过程,主要依据节表中的信息,DOS头 至 节表 这段内容 可以直接复制过去,大小是可选PE头中sizeofheaders属性,然后就是遍历复制每一节,用到节表中的四个属性确定每一个节的大小和位置

imisc                            节在内存中的大小
virtualaddress              节在内存中的偏移
sizeofrawdata              节在硬盘中的大小                             
pointertorawdata         节在硬盘中的偏移                                 

代码如下:

#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include "mycode.h"

// 文件 -> FileBuffer -> ImageBuffer -> NewFileBuffer -> 文件
//          硬盘对齐       内存对齐       硬盘对齐

int main()
{
	char* ptr=read_file_to_file_buffer("D:\\T\\zx.exe");
 
	//找到PE偏移
	int* p_e_lfanew=(int*)ptr+15; 
	printf("PE偏移:\t\t\t%x	\n",*p_e_lfanew);
	//PE标志
	char* p_signature = ptr+(*p_e_lfanew);	
	
	//获取ImageBuffer大小  是可选PE头中的sizeofimage值
	int* p_sizeofimage=(int*)(p_signature+80);
	printf("sizeofimage地址:\t\t\t%p,	sizeofimage值:\t\t\t%x \n",p_sizeofimage,*p_sizeofimage);
	 
	//为ImageBuffer申请空间
	char* p_imagebuffer;
	p_imagebuffer = (char*) malloc(*p_sizeofimage);
	if( p_imagebuffer == NULL)
	{
		printf("空间不足\n");
		return 0;
	}
	else
	{
		printf("ImageBuffer地址:%x \n",p_imagebuffer);
	}
	memset(p_imagebuffer,0,*p_sizeofimage);

	//FileBuffer内容 复制到 ImageBuffer
	trans_file_buffer_to_image_buffer(ptr,p_imagebuffer);

	//ImageBuffer    还原到  NewFileBuffer 
	//为newfilebuffer申请空间
	char* p_newfilebuffer;
	int sizeofp_newfilebuffer=get_file_size("D:\\T\\zx.exe");
	p_newfilebuffer = (char*) malloc(sizeofp_newfilebuffer);
	if( p_newfilebuffer == NULL)
	{
		printf("空间不足\n");
		return 0;
	}
	else
	{
		printf("Newfilebuffer地址:%x \n",p_newfilebuffer);
	}
	memset(p_newfilebuffer,0,sizeofp_newfilebuffer);
	//ImageBuffer    还原到 NewFileBuffer 
	trans_image_buffe_to_file_buffer(p_imagebuffer,p_newfilebuffer);

	//将NewFileBuffer 中的内容写入到一个新的exe文件
	trans_file_buffer_to_file(p_newfilebuffer,"D:\\T\\zx2.exe",sizeofp_newfilebuffer);

	//释放内存
	free(p_imagebuffer);
	p_imagebuffer=NULL;
	free(p_newfilebuffer);
	p_newfilebuffer=NULL;
	read_file_to_file_buffer_free(ptr);

	return 0;
}
 

<mycode.cpp>

#include <stdio.h>
#include <malloc.h>
#include <string.h>

//获取文件大小
int get_file_size(char* filename)
{
	FILE* fp = fopen(filename,"r");
	int size;
	if( fp == NULL )
	{
		printf("open fail \n");
		return -1;
	}
	fseek(fp,0,SEEK_END);
	size=ftell(fp);
	fclose(fp);
	return size;
}


//将文件读取到内存中 返回读取到哪个内存地址
char* read_file_to_file_buffer(char* filename)
{
	//char* filename="D:\\T\\zx.exe"; 
	int file_size=get_file_size(filename);
	//申请空间
	char* ptr;
	ptr = (char*) malloc(file_size);
	if( ptr == NULL)
	{
		printf("空间不足\n");
		return 0;
	}
	memset(ptr,0,file_size);
	printf("文件读取到:%x \n",ptr);
	//将文件内容 读取到内存
	FILE* fp = fopen(filename,"rb+");
	fread(ptr,file_size,1,fp);
	fclose(fp);
	return ptr;
}
//释放内存
void read_file_to_file_buffer_free(char* ptr)
{
	free(ptr);
	ptr=NULL;
}


//FileBuffer 转化到 ImageBuffer
int trans_file_buffer_to_image_buffer(char* pfilebuffer,char* pimagebuffer)
{
	//找到PE偏移
	int* p_e_lfanew=(int*)pfilebuffer+15; 
	//PE标志
	char* p_signature = pfilebuffer+(*p_e_lfanew);	
	//节数
	short* p_numberofsections=(short*)(p_signature+6);	

	//DOS头-节表  这段可以直接复制过去 不用做内存转换  大小在sizeofheaders 记录着
	int* p_sizeofheaders=(int*)(p_signature+84);
	memcpy(pimagebuffer,pfilebuffer,*p_sizeofheaders);
	
	//节区 复制到 ImageBuffer  每节大小为 sizeofrawdata
	//遍历节表  复制每一节
	short* p_sizeofoptionalheader=(short*)(p_signature+20);	
	char* image_section_header_base=p_signature+4+20+(*p_sizeofoptionalheader);

	int i=1;
	for(i;i<=*p_numberofsections;i++)
	{
		int *pvirtualaddress=(int*)(image_section_header_base+12);		//节内存中的偏移
		int *psizeofrawdata=(int*)(image_section_header_base+16);		//节文件中的大小
		int *ppointertorawdata=(int*)(image_section_header_base+20);	//节文件中的偏移

		printf("复制第 %d 个节	起始地址:%x \t 结束地址:%x \n",i,pfilebuffer + (*ppointertorawdata),pfilebuffer + (*ppointertorawdata)+ *psizeofrawdata);
		
	//								    偏移	                                
		memcpy( pimagebuffer  +  (*pvirtualaddress)      //Image中 节的位置
			   ,pfilebuffer   +  (*ppointertorawdata)    //文件中  节的位置 
			   ,*psizeofrawdata);						 //节大小
	
		image_section_header_base=image_section_header_base+40;
	}
	return 0;
}

//将ImageBuffer还原到FileBuffer内存
int trans_image_buffe_to_file_buffer(char* pimagebuffer,char* pfilebuffer)
{
	//找到PE偏移
	int* p_e_lfanew=(int*)pimagebuffer+15; 
	//PE标志
	char* p_signature = pimagebuffer+(*p_e_lfanew);	
	//节数
	short* p_numberofsections=(short*)(p_signature+6);	

	//DOS头-节表  这段可以直接复制过去 不用做内存转换  大小在sizeofheaders 记录着
	int* p_sizeofheaders=(int*)(p_signature+84);
	memcpy(pfilebuffer,pimagebuffer,*p_sizeofheaders);
	
	//节区 复制到 ImageBuffer  每节大小为 sizeofrawdata
	//遍历节表  复制每一节
	short* p_sizeofoptionalheader=(short*)(p_signature+20);	
	char* image_section_header_base=p_signature+4+20+(*p_sizeofoptionalheader);

	int i=1;
	for(i;i<=*p_numberofsections;i++)
	{
		int *pmisc=(int*)(image_section_header_base+8);					//节内存中的大小
		int *pvirtualaddress=(int*)(image_section_header_base+12);		//节内存中的偏移
		int *psizeofrawdata=(int*)(image_section_header_base+16);		//节文件中的大小
		int *ppointertorawdata=(int*)(image_section_header_base+20);	//节文件中的偏移
		printf("复制第 %d 个节	起始地址:%x \t 结束地址:%x \n",i,pimagebuffer + (*pvirtualaddress),pimagebuffer + (*pvirtualaddress)+ *pmisc);
		
	//								    偏移	                                
		memcpy( pfilebuffer    +  (*ppointertorawdata)      //FileBuffer中   节的位置
			   ,pimagebuffer   +  (*pvirtualaddress)	    //ImageBuffer中  节的位置 
			   ,*pmisc);									//节大小

		image_section_header_base=image_section_header_base+40;
	}
	return 0;
}


//将 NewFileBuffer内容  写到文件
int trans_file_buffer_to_file(char* pfilebuffer,char* filename,int size)
{
 
	printf("将 %x 写入到 %s ",pfilebuffer,filename);
	FILE *fp;
	fp = fopen(filename,"wb+" );
	fwrite(pfilebuffer, size , 1, fp );
	fclose(fp);
	return 0;
}


执行结果

新旧exe文件PE对比

旧exe的PE

新exe的PE

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值