文件镜像和内存镜像

直接将一个pe文件读到内存中是无法运行的,必须经过拉伸,然后再做其它处理才能可运行;
文件中的pe文件状态为文件映像;
拉伸后的状态为内存映像;
 
1.pe加载过程
1)根据SizeOfImage的大小,开辟一块缓冲区(ImageBuffer). 
SizeOfImage是可选pe头中的一个属性;   
                                    
2)根据SizeOfHeaders的大小,将头信息从FileBuffer拷贝到ImageBuffer
文件映像和内存影响的头部信息是相同的,可以直接拷贝过去;
头部信息包括:dos头、标准pe头、可选pe头、节表;
SizeOfHeaders是可选pe头的属性;表示所有头加起来然后按文件对齐后的大小;
 
3)根据节表中的信息循环讲FileBuffer中的节拷贝到ImageBuffer中.  
节表中的属性PointerToRawData,表示文件映像中节的位置 ,决定了从哪里开始复制;
节表中有个属性VirtualAddress,表示内存镜像中该节相对于dos头开始的偏移;决定了该复制到哪个地方;
                                   
4)Misc.VirtualSize 和 SizeOfRawData谁大?
VirtualSize是实际节的大小,也就是不包含文件对齐补的0;
SizeOfRawData是节文件对齐后的大小;
VirtualSize是可能比SizeOfRawData大的;
例如:char buf[1000];这段代码声明了一个数组,但没初始化,编译器编译时没给它分配空间;
但加载到内存时会分配空间,导致实际节大小变大,可能大于文件对齐节的大小;  
拷贝节时可以用两种方案:1】按VirtualSize拷;2】按SizeOfRawData拷;都可以只要保证数据不会丢失即可;
有一种极端情况,可能未初始化的数据太多,导致VirtualSize过大,按VirtualSize拷贝可能占了下个节的空间;
按SizeOfRawData来拷贝最靠谱,因为不会超过下个节的起始地址;
                                
5)FileBuffer与ImageBuffer谁大?    
 
6)ImageBuffer状态的开始地址
注意:ImageBuffer状态还不是可运行状态,只是比文件映像更加接近可运行状态;
因此不是以ImageBase作为起始地址的;
真正的起始地址是malloc申请的内存地址;
真正以ImageBase开头是文件被加载到独立的4gb空间能运行时;  
                  
 
2.用程序模拟pe文件的拉伸
如图:将一个exe文件读到内存,然后拉伸;
 
1)初始化内存的memset函数
memset 函数是内存赋值函数,用来给某一块内存空间进行赋值的;
包含在<string.h>头文件中,可以用它对一片内存空间逐字节进行初始化;
原型为 :
void *memset(void *s, int v, size_t n);  
这里s可以是数组名,也可以是指向某一内在空间的指针;
v为要填充的值;
n为要填充的字节数;
 
2)memcpy内存拷贝函数
原型:
void *memcpy(void *dest, const void *src, size_t n);
功能:由src所指内存区域复制n个字节到dest所指内存区域。
说明:src和dest所指内存区域不能重叠,函数返回指向dest的指针。
 
 
3)代码
头文件petool.h
#ifndef PETOOL_H
#define PETOOL_H
 
#include "stdafx.h"
#include <stdlib.h>
#include <windows.h>
 
//函数声明                                
//**************************************************************************                                
//ReadPEFile:将文件读取到缓冲区                                
//参数说明:                                
//lpszFile 文件路径                                
//pFileBuffer 缓冲区指针                                
//返回值说明:                                
//读取失败返回0  否则返回实际读取的大小                                
//**************************************************************************                                
DWORD ReadPEFile(IN LPSTR lpszFile,OUT LPVOID* pFileBuffer);                                
//**************************************************************************                                
//CopyFileBufferToImageBuffer:将文件从FileBuffer复制到ImageBuffer                                
//参数说明:                                
//pFileBuffer  FileBuffer指针                                
//pImageBuffer ImageBuffer指针                                
//返回值说明:                                
//读取失败返回0  否则返回复制的大小                                
//**************************************************************************                                
DWORD CopyFileBufferToImageBuffer(IN LPVOID pFileBuffer,OUT LPVOID* pImageBuffer);                                
                           
 
#endif
 
头文件实现petool.cpp:
#include "stdafx.h"
#include "petool.h"
 
//ReadPEFile:将文件读取到缓冲区
DWORD ReadPEFile(IN LPSTR lpszFile,OUT LPVOID* pFileBuffer){
 
    //1.打开文件
    FILE* file = fopen(lpszFile, "rb");
 
    if(!file){
        printf("打开文件失败\n");
        return 0;
    }
 
    //2.计算文件大小
    fseek(file, 0, SEEK_END);
    DWORD len = ftell(file);
    fseek(file, 0, SEEK_SET);
 
    //3.申请内存
    LPVOID buf = malloc(len);
    if(!buf){
        fclose(file);
        printf("申请内存失败\n");
        return 0;
    }
    
    //4.读取文件到内存
    size_t n = fread(buf, len, 1, file);
    if(!n){
        printf("读取文件失败\n");
        free(buf);
        fclose(file);
        return 0;
    }
 
    //5.返回
    printf("读取文件到缓冲区成功\n");
    *pFileBuffer = buf;
    buf = NULL;
    fclose(file);
    return len;
    
}
 
 
//将文件从FileBuffer复制到ImageBuffer
DWORD CopyFileBufferToImageBuffer(IN LPVOID pFileBuffer,OUT LPVOID* pImageBuffer){
    //定义pe头结构指针
    PIMAGE_DOS_HEADER dosHeader = NULL;        //dos头指针
    PIMAGE_NT_HEADERS ntHeader = NULL;        //nt头指针
    PIMAGE_FILE_HEADER peHeader = NULL;        //pe头指针
    PIMAGE_OPTIONAL_HEADER32 optionHeader = NULL;    //可选pe头指针
    PIMAGE_SECTION_HEADER sectionHeader = NULL;    //节表指针
 
    if(!pFileBuffer){
        printf("文件加载失败\n");
        return 0;
    }
 
    //判断是否有mz标记
    if(*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE){
        printf("不是有效mz标记\n");
        free(pFileBuffer);
        return 0;
    }
    //找到dos头
    dosHeader = (PIMAGE_DOS_HEADER) pFileBuffer;
 
    //根据dos头的e_flanew找到nt头并判断是否有pe标记
    if(*((PDWORD)((DWORD)pFileBuffer + dosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE){
        printf("不是有效pe标记\n");
        free(pFileBuffer);
        return 0;
    }
    //找到pe头
    peHeader = (PIMAGE_FILE_HEADER) ((DWORD)pFileBuffer + dosHeader->e_lfanew +4);
 
    //找到可选pe头
    optionHeader = (PIMAGE_OPTIONAL_HEADER32) ((DWORD)peHeader + IMAGE_SIZEOF_FILE_HEADER);
 
    //**********开始复制***********
    //1.申请内存空间,拉伸后的大小在可选pe头的SizeOfImage中
    DWORD imageSize = optionHeader ->SizeOfImage;
    LPVOID image = malloc(imageSize);
    if(!image){
        printf("申请imagebuf内存空间失败\n");
        return 0;
    }
    //初始化内存空间
    memset(image, 0, imageSize);
 
    //2.拷贝头部文件,内存镜像和文件镜像的头部是一样的
    DWORD headSize = optionHeader->SizeOfHeaders;
    memcpy(image, pFileBuffer, headSize);
    
    //3.拷贝各个节
    WORD sectionNum = peHeader->NumberOfSections;    //节的数量在pe头中
    WORD opHeaderSize = peHeader->SizeOfOptionalHeader;    //可选pe头的字节数,用来计算节表文件镜像的位置;
    //找到节表开头
    sectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)optionHeader + opHeaderSize);
    for(int i=0; i<sectionNum; i++,sectionHeader++){
        memcpy((LPVOID)((DWORD)image + sectionHeader->VirtualAddress),
            (LPVOID)((DWORD)pFileBuffer + sectionHeader->PointerToRawData),
            sectionHeader->SizeOfRawData);
    }
    
    //4.返回
    *pImageBuffer = image;
    image = NULL;
    printf("拉伸文件镜像成功\n");
    return imageSize;
 
}
 
主函数:
#include "stdafx.h"
#include "petool.h"
 
int main(int argc, char* argv[])
{
    //读取文件到内存
    LPVOID buf = NULL;
    ReadPEFile("C:\\Users\\Administrator\\Desktop\\CRACKME.EXE", &buf);
 
    //拉伸文件
    LPVOID image = NULL;
    CopyFileBufferToImageBuffer(buf, &image);
    
    getchar();
    
    printf("释放内存空间\n");
    free(buf);
    free(image);
    return 0;
}
 
 
 
 
 
posted @ 2019-10-17 16:27  L丶银甲闪闪  阅读(1089)  评论(0编辑  收藏  举报