我们知道 一个exe文件在硬盘中的存储形式跟在内存中的存储形式是不同的,主要表现在对齐方式不同,这次模拟了一个文件的加载动作,先将exe文件读到内存FileBuffer中,此时FileBuffer跟文件在硬盘中的格式是一样的,然后将FileBuffer拉伸,读取到内存ImageBuffer中,此时对齐方式由硬盘中的200h变为内存中的1000h,再将ImageBuffer还原成FileBuffer,放到NewBuffer里面,最后将NewBuffer的内容重新写到一个exe文件中,经过这一系列的变换,最终新旧exe的PE结构是一致的
拉伸和还原的过程,主要依据节表中的信息,DOS头 至 节表 这段内容 可以直接复制过去,大小是可选PE头中sizeofheaders属性,然后就是遍历复制每一节,用到节表中的四个属性确定每一个节的大小和位置
imisc 节在内存中的大小
virtualaddress 节在内存中的偏移
sizeofrawdata 节在硬盘中的大小
pointertorawdata 节在硬盘中的偏移
代码如下:
#include#include #include #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偏移:ttt%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地址:ttt%p, sizeofimage值:ttt%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; }
#include#include #include //获取文件大小 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,"r"); 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



