我们知道 一个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