首页
社区
课程
招聘
[原创]PE加载过程 FileBuffer-ImageBuffer
发表于: 2022-9-22 22:28 13718

[原创]PE加载过程 FileBuffer-ImageBuffer

2022-9-22 22:28
13718

我们知道一个硬盘上的文件读入到内存中(FileBuffer),是原封不动的将硬盘上的文件数据复制一份放到内存中

接着如果文件要运行,需要先将FileBuffer中的文件数据"拉伸",重载到每一个可执行文件的4GB虚拟内存中!此时称文件印象或者内存印象,即ImageBuffer

但是ImageBuffer就是文件运行时真正在内存中状态吗?或者说文件在ImageBuffer中就是表示文件被执行了吗?不!!!!!!

在ImageBuffer中的文件数据由于按照一定的规则被"拉伸",只是已经无线接近于可被windows执行的文件格式了!但是此时还不代表文件已经被执行了,因为此时文件也只是处在4GB的虚拟内存中,如果文件被执行操作系统还需要做一些事情,将文件真正的装入内存中,等待CPU的分配执行

所以不要理解为ImageBuffer中的状态就是文件正在被执行,后面操作系统还要做很多事情才能让ImageBuffer中的文件真正执行起来的

SizeOfRawData表示此节在硬盘上经过文件对齐后的大小;Misc.VirtualSize表示此节没有经过对齐的在内存中的大小。那么是不是说SizeOfRawData一定大于Misc.VirtualSize呢?不一定!!!!!!!

我们写C语言的时候知道如果你定义一个数组已经初始化,比如int arr[1000] = {0};,此时编译成.exe文件存放在硬盘上时,这1000个int类型的0肯定会存放在某一个节中,并且分配1000个0的空间,这个空间大小是多少,最后重载到ImageBuffer时还是多少,即Misc.VirtualSize不管文件在硬盘上还是内存中的值都是一致的。所以,SizeOfRawData一般都是大于Misc.VirtualSize的

但是如果我们定义成int arr[1000];,表示数据还未初始化,并且如果程序中没有使用过或初始化过这块内存空间,那么我们平时看汇编会发现其实编译器还没有做任何事情,这就只是告诉编译器需要留出1000个int宽度大小的内存空间。所以如果某一个节中存在已经被定义过但还未初始化的数据,那么文件在硬盘上不会显式的留出空间,即SizeOfRawData中不会算上未初始化数据的空间;但是此节的Misc.VirtualSize为加载到内存中时节的未对齐的大小,那么这个值就需要算上给未初始化留出来空间后的整个节的大小,故在内存中的节本身的总大小可能会大于硬盘中的此节文件对齐后的大小。

img

为什么选择SizeOfRawData,不选择Misc.VirtualSize来确定需要复制的节的大小?因为上面说过,Misc.VirtualSize的值由于节中有未初始化的数据且未使用而计算出预留的空间装入内存后的总大小的值可能会很大,如果这个值大到已经包含了后面一个节的数据,那么按照这个值将FileBuffer中的数据复制到ImageBuffer中很可能会把下一个节的数据也复制过去,所以直接用SizeOfRawData就可以了。但是如果节中包含未初始化数据,这样做起始就不太准确了,但是可以大致模拟这个过程即可

img

比如一个文件加载到4GB内存中的某一个数据地址为0x501234,那么怎么算出这个内存地址对应到文件在硬盘上时的地址是多少,即算出相对于文件的偏移地址?

先算出此内存地址相对于文件在内存中的起始地址的偏移量

接着通过这个偏移量循环和每一个节的VirtualAddress做比较,当此偏移量大于某一个节的VirtualAddress并且小于此VirtualAddress + Misc.VirtualSize,就说明这个内存地址就在这个节中

再用此偏移量 - (此节的VirtualAddress + 文件在内存中的起始地址)得到这个内存地址相对于所在节的偏移量

接着找内存地址所在节的PointerToRawData,通过PointerToRawData + 聂村地址相对于所在节的偏移量来得到此内存地址在硬盘上时相对于文件的偏移量

举例:现在我们要找0x501234对应的文件偏移是多少?

0x501234 - 0x500000 = 0x1234

img

作用:

参数说明:

返回值说明:

示例

使用IN和OUT,这个是C++语法中允许的
允许#define NAME 这样不带替换表达式的定义,目的就是为了告诉用户参数是传入还是传出

LPSTR ----> typedef CHAR *LPSTR, *PSTR; 是一个char*指针;在WINNT.H头文件里面
LPVOID ----> typedef void far *LPVOID; 是一个void*指针,在WINDEF.H头文件里面

它是别名一个void far *类型的指针,其中far是以前针对16位系统的,而现在基本都是32位以上系统
所以这个far已经没有意义了,可以忽略,总结下来 LPVOID就是个void*指针类型

DWORD ---> typedef unsigned long DWORD; 是32位系统里面是无符号4字节整数

作用:

将文件从FileBuffer复制到ImageBuffer

参数说明:

返回值说明:

示例

作用:

ImageBuffer中的数据复制到新的缓冲区

参数说明:

返回值说明:

作用:

将内存中的数据复制到文件

参数说明:

返回值说明:

示例:

将内存偏移转换为文件偏移

参数说明:

返回值说明:

示例:

eg:

该函数内部做如下使用:

step1)WCHAR *pContent = new WCHAR[MAX_PATH];

step2)对pContent值赋值

step3)*pValue = pContent;

(由此可见,*pValue保存的是新分配内存的地址。)

Q:为啥形参不是void pValue而是void **pValue,实际用的时候也还是使 用pValue,只要传参时候传pValue, 而不是&pValue就好了呀。

A:因为只传void就是值传递,GetContext改变的是副本,传void**,其相当于void&,保证的是引用传递,改变的 是形参。

 
将文件读取到缓冲区
将文件读取到缓冲区
lpszFile 文件路径                              
 
pFileBuffer 缓冲区指针
lpszFile 文件路径                              
 
pFileBuffer 缓冲区指针
读取失败返回0,否则返回实际读取的大小
读取失败返回0,否则返回实际读取的大小
DWORD ReadPEFile(IN LPSTR lpszFile,OUT LPVOID* pFileBuffer);
DWORD ReadPEFile(IN LPSTR lpszFile,OUT LPVOID* pFileBuffer);
 
 
 
 
 
pFileBuffer  FileBuffer指针                              
 
pImageBuffer ImageBuffer指针
pFileBuffer  FileBuffer指针                              
 
pImageBuffer ImageBuffer指针
读取失败返回0,否则返回ImageBuffer的大小
读取失败返回0,否则返回ImageBuffer的大小
DWORD CopyFileBufferToImageBuffer(IN LPVOID pFileBuffer,OUT LPVOID* pImageBuffer);
DWORD CopyFileBufferToImageBuffer(IN LPVOID pFileBuffer,OUT LPVOID* pImageBuffer);
 
 
pImageBuffer ImageBuffer指针                             
 
pNewBuffer NewBuffer指针
pImageBuffer ImageBuffer指针                             
 
pNewBuffer NewBuffer指针
读取失败返回0,否则返回NewBuffer的大小   
DWORD CopyImageBufferToNewBuffer(IN LPVOID pImageBuffer,OUT LPVOID* pNewBuffer);
读取失败返回0,否则返回NewBuffer的大小   
DWORD CopyImageBufferToNewBuffer(IN LPVOID pImageBuffer,OUT LPVOID* pNewBuffer);
 
 
pMemBuffer 内存中数据的指针                            
 
size 要复制的大小                            
 
lpszFile 要存储的文件路径
pMemBuffer 内存中数据的指针                            
 
size 要复制的大小                            
 
lpszFile 要存储的文件路径
读取失败返回0,否则返回复制的大小
读取失败返回0,否则返回复制的大小
BOOL MemeryTOFile(IN LPVOID pMemBuffer,IN size_t size,OUT LPSTR lpszFile);
BOOL MemeryTOFile(IN LPVOID pMemBuffer,IN size_t size,OUT LPSTR lpszFile);
 
pFileBuffer FileBuffer指针                               
 
dwRva RVA的值
pFileBuffer FileBuffer指针                               
 
dwRva RVA的值
返回转换后的FOA的值,如果失败返回0
返回转换后的FOA的值,如果失败返回0
DWORD RvaToFileOffset(IN LPVOID pFileBuffer,IN DWORD dwRva);
DWORD RvaToFileOffset(IN LPVOID pFileBuffer,IN DWORD dwRva);
 
#include<stdio.h>
#include<string.h>
#include<malloc.h>
#include<stdlib.h>
#include<windows.h>
 
#define test 1
 
DWORD ToLoaderPE(LPSTR file_path, PVOID* pFileBuffer);
 
DWORD CopyFileBufferToImageBuffer(PVOID pFileBuffer, PVOID* pImageBuffer);
DWORD CopyImageBufferToNewFileBuffer(PVOID pImageBuffer, PVOID* pNewFileBuffer);
BOOL MemoryToFile(PVOID pMemBuffer, DWORD size, LPSTR lpszFile);
 
char file_path[] = "E:\\Reverse\\吾爱破解工具包2.0\\吾爱破解工具包\\Tools\\Others\\ipmsg.exe";
char write_file_path[] = "C:\\Users\\whl\\Desktop\\1.exe";
 
//返回PE文件大小
DWORD ToLoaderPE(LPSTR file_path, PVOID* pFileBuffer)
{
    FILE* pFile = NULL;
    DWORD FileSize = 0;
    PVOID pFileBufferTemp = NULL;
 
    pFile = fopen(file_path, "rb");
 
    if (!pFile)
    {
        printf("(ToLoaderPE)Can't open file!\n");
        return 0;
    }
 
    fseek(pFile, 0, SEEK_END);
    FileSize = ftell(pFile);
    printf("FileBuffer: %#x\n", FileSize);
    fseek(pFile, 0, SEEK_SET);
    pFileBufferTemp = malloc(FileSize);
 
    if (!pFileBufferTemp)
    {
        printf("(ToLoaderPE)Allocate dynamic memory failed!\n");
        fclose(pFile);
        return 0;
    }
 
    DWORD n = fread(pFileBufferTemp, FileSize, 1, pFile);
 
    if (!n)
    {
        printf("(ToLoaderPE)Read file failed!\n");
        free(pFileBufferTemp);
        fclose(pFile);
        return 0;
    }
    *pFileBuffer = pFileBufferTemp;
    pFileBufferTemp = NULL;
    fclose(pFile);
    return FileSize;
}
 
DWORD CopyFileBufferToImageBuffer(PVOID pFileBuffer, PVOID* pImageBuffer)
{
    PIMAGE_DOS_HEADER pDosHeader = NULL;
    PIMAGE_NT_HEADERS pNTHeader = NULL;
    PIMAGE_FILE_HEADER pPEHeader = NULL;
    PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
    PIMAGE_SECTION_HEADER pSectionHeader = NULL;
 
    PVOID pImageTemp = NULL;
 
    if (!pFileBuffer)
    {
        printf("(CopyFileBufferToImageBuffer)Can't open file!\n");
        return 0;
    }
 
    if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)
    {
        printf("(CopyFileBufferToImageBuffer)No MZ flag, not exe file!\n");
        return 0;
    }
 
    pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
 
    if (*((LPDWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
    {
        printf("(CopyFileBufferToImageBuffer)Not a valid PE flag!\n");
        return 0;
    }
 
    pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
    pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
    pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
    pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
 
    pImageTemp = malloc(pOptionHeader->SizeOfImage);
 
    if (!pImageTemp)
    {
        printf("(CopyFileBufferToImageBuffer)Allocate dynamic memory failed!\n");
        free(pImageTemp);
        return 0;
    }
 
    memset(pImageTemp, 0, pOptionHeader->SizeOfImage);
    memcpy(pImageTemp, pDosHeader, pOptionHeader->SizeOfHeaders);
 
    PIMAGE_SECTION_HEADER pSectionHeaderTemp = pSectionHeader;
 
    for (int n = 0; n < pPEHeader->NumberOfSections; n++, pSectionHeaderTemp++)
    {
        memcpy((PVOID)((DWORD)pImageTemp + pSectionHeaderTemp->VirtualAddress), (PVOID)((DWORD)pFileBuffer + pSectionHeaderTemp->PointerToRawData), pSectionHeaderTemp->SizeOfRawData);
        printf("VirtualAddress%d: %#10x         PointerToRawData%d: %#10x\n", n, (DWORD)pImageTemp + pSectionHeader->VirtualAddress, n, (DWORD)pFileBuffer + pSectionHeader->PointerToRawData);
    }
    *pImageBuffer = pImageTemp;
    pImageTemp = NULL;
    return pOptionHeader->SizeOfImage;
}
 
DWORD CopyImageBufferToNewFileBuffer(PVOID pImageBuffer, PVOID* pNewFileBuffer)
{
    PIMAGE_DOS_HEADER pDosHeader = NULL;
    PIMAGE_NT_HEADERS pNTHeader = NULL;
    PIMAGE_FILE_HEADER pPEHeader = NULL;
    PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
    PIMAGE_SECTION_HEADER pSectionHeader = NULL;
 
    LPVOID pTempNewbuffer = NULL;
 
    if (!pImageBuffer)
    {
        printf("(CopyImageBufferToNewBuffer)Can't open file!\n");
        return 0;
    }
 
    if (*((PWORD)pImageBuffer) != IMAGE_DOS_SIGNATURE)
    {
        printf("(CopyImageBufferToNewBuffer)No MZ flag, not exe file!\n");
        return 0;
    }
 
    pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
    if (*((PDWORD)((DWORD)pImageBuffer + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
    {
        printf("(CopyImageBufferToNewBuffer)Not a valid PE flag!\n");
        return 0;
    }
 
    pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer + pDosHeader->e_lfanew);
    pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4); // 这里必须强制类型转换
    pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
    pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
 
    //获取new_buffer的大小
    int new_buffer_size = pOptionHeader->SizeOfHeaders;
    for (DWORD i = 0; i < pPEHeader->NumberOfSections; i++)
    {
        new_buffer_size += pSectionHeader[i].SizeOfRawData;  // pSectionHeader[i]另一种加法
    }
    // 分配内存(newbuffer)
    pTempNewbuffer = malloc(new_buffer_size);
    if (!pTempNewbuffer)
    {
        printf("(CopyImageBufferToNewBuffer)Allocate dynamic memory failed!\n");
        return 0;
    }
    memset(pTempNewbuffer, 0, new_buffer_size);
    memcpy(pTempNewbuffer, pDosHeader, pOptionHeader->SizeOfHeaders);
    // 循环拷贝节区
    PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
    for (DWORD j = 0; j < pPEHeader->NumberOfSections; j++, pTempSectionHeader++)
    {    //PointerToRawData节区在文件中的偏移,VirtualAddress节区在内存中的偏移地址,SizeOfRawData节在文件中对齐后的尺寸
        memcpy((PDWORD)((DWORD)pTempNewbuffer + pTempSectionHeader->PointerToRawData), (PDWORD)((DWORD)pImageBuffer + pTempSectionHeader->VirtualAddress), pTempSectionHeader->SizeOfRawData);
    }
    //返回数据
    *pNewFileBuffer = pTempNewbuffer; //暂存的数据传给参数后释放
    pTempNewbuffer = NULL;
    return new_buffer_size;  // 返回计算得到的分配内存的大小
}
 
BOOL MemoryToFile(PVOID pMemBuffer, DWORD size, LPSTR lpszFile)
{
    FILE* fp;
    fp = fopen(lpszFile, "wb");
    if (fp != NULL)
    {
        fwrite(pMemBuffer, size, 1, fp);
    }
    fclose(fp);
    return 1;
}
 
VOID operate()
{
    LPVOID pFileBuffer = NULL;
    LPVOID pNewFileBuffer = NULL;
    LPVOID pImageBuffer = NULL;
 
    DWORD ret1 = ToLoaderPE(file_path, &pFileBuffer);  // &pFileBuffer(void**类型) 传递地址对其值可以进行修改
    printf("exe->filebuffer  返回值为计算所得文件大小:%#x\n", ret1);
 
    DWORD ret2 = CopyFileBufferToImageBuffer(pFileBuffer, &pImageBuffer);
    printf("filebuffer -> imagebuffer返回值为计算所得文件大小:%#x\n", ret2);
    DWORD ret3 = CopyImageBufferToNewFileBuffer(pImageBuffer, &pNewFileBuffer);
    printf("imagebuffer -> newfilebuffer返回值为计算所得文件大小:%#x\n", ret3);
    MemoryToFile(pNewFileBuffer, ret3, write_file_path);
 
    free(pFileBuffer);
    free(pNewFileBuffer);
    free(pImageBuffer);
}
 
int main()
{
    operate();
    getchar();
    return 0;
}
#include<stdio.h>
#include<string.h>
#include<malloc.h>
#include<stdlib.h>
#include<windows.h>
 
#define test 1
 
DWORD ToLoaderPE(LPSTR file_path, PVOID* pFileBuffer);
 
DWORD CopyFileBufferToImageBuffer(PVOID pFileBuffer, PVOID* pImageBuffer);
DWORD CopyImageBufferToNewFileBuffer(PVOID pImageBuffer, PVOID* pNewFileBuffer);
BOOL MemoryToFile(PVOID pMemBuffer, DWORD size, LPSTR lpszFile);
 
char file_path[] = "E:\\Reverse\\吾爱破解工具包2.0\\吾爱破解工具包\\Tools\\Others\\ipmsg.exe";
char write_file_path[] = "C:\\Users\\whl\\Desktop\\1.exe";
 
//返回PE文件大小
DWORD ToLoaderPE(LPSTR file_path, PVOID* pFileBuffer)
{
    FILE* pFile = NULL;
    DWORD FileSize = 0;
    PVOID pFileBufferTemp = NULL;
 
    pFile = fopen(file_path, "rb");
 
    if (!pFile)
    {
        printf("(ToLoaderPE)Can't open file!\n");
        return 0;
    }
 
    fseek(pFile, 0, SEEK_END);
    FileSize = ftell(pFile);
    printf("FileBuffer: %#x\n", FileSize);
    fseek(pFile, 0, SEEK_SET);
    pFileBufferTemp = malloc(FileSize);
 
    if (!pFileBufferTemp)
    {
        printf("(ToLoaderPE)Allocate dynamic memory failed!\n");
        fclose(pFile);
        return 0;
    }
 
    DWORD n = fread(pFileBufferTemp, FileSize, 1, pFile);
 
    if (!n)
    {
        printf("(ToLoaderPE)Read file failed!\n");
        free(pFileBufferTemp);
        fclose(pFile);
        return 0;
    }
    *pFileBuffer = pFileBufferTemp;
    pFileBufferTemp = NULL;
    fclose(pFile);
    return FileSize;
}
 
DWORD CopyFileBufferToImageBuffer(PVOID pFileBuffer, PVOID* pImageBuffer)
{
    PIMAGE_DOS_HEADER pDosHeader = NULL;
    PIMAGE_NT_HEADERS pNTHeader = NULL;
    PIMAGE_FILE_HEADER pPEHeader = NULL;
    PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
    PIMAGE_SECTION_HEADER pSectionHeader = NULL;
 
    PVOID pImageTemp = NULL;
 
    if (!pFileBuffer)
    {
        printf("(CopyFileBufferToImageBuffer)Can't open file!\n");
        return 0;
    }
 
    if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)
    {
        printf("(CopyFileBufferToImageBuffer)No MZ flag, not exe file!\n");
        return 0;

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 6
支持
分享
最新回复 (3)
雪    币: 10088
活跃值: (4491)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2022-9-23 11:45
0
雪    币: 227
活跃值: (55)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
感谢分享,今天调试成功了
2022-9-27 21:41
0
雪    币: 1482
活跃值: (2568)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
谢谢楼主的分享。
2022-10-5 11:17
0
游客
登录 | 注册 方可回帖
返回
//