文件鏡像和內存鏡像


直接將一個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;
}
 
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM