PE文件学习笔记

PE的DOC头里面最后一个字节的数字-e8表示文件开头开始过E8个字节是文件真正开始的地方–PE头.注意e8不是一定的.

1 DOC头:
WORD e_magic “MZ标记”用于判断是否可执行文件
DWORD e_lfanew PE头相对于文件的偏移,用于定位PE文件
2 标准PE头:
WORD Machine 程序运行的CPU型号:0x0任何处理器/0x14C 386及后续处理器
WORD NumberOfSections 文件存在的节的总数,如果要新增或者合并节,要修改这个值
DWORD TimeDateStamp 时间戳:文件的创建时间,编译器填写
DWORD PointerToSymbolTable
DWORD NumberOfSymbols
DWORD SizeOfOptionalHeader 可选PE头的大小,32位默认E0h 64位默认F0h 可改
WORD Characteristics 每个位有不同的含义,可执行文件值为10F 即01238位置1
3 可选PE头:
WORD Magic 说明文件的类型:10B→32位 20B→64位
BYTE MajorLinkerVersion
BYTE MinorLinkerVersion
DWORD SizeOfCode 所有代码节的和,必须是FileAlignment整数倍 编译器填 没用
DWORD SizeOfInitializedData 已初始化数据大小的和,同上
DWORD SizeOfUninitializedData 未初始化数据大小的和,同上
DWORD AddressOfEntryPoint 程序入口
DWORD BaseOfCode 代码开始的基址
DWORD BaseOfData 数据开始的基址
DWORD ImageBase 内存镜像基址
DWORD SectionAlignment 内存对齐
DWORD FileAlignment 文件对齐
WORD MajorOperatingSystemVersion
WORD MinorOperatingSystemVersion
WORD MajorImageVersion
WORD MinorImageVersion
WORD MajorSubsystemVersion
WORD MinorSubsystemVersion
DWORD Win32VersionValue
DWORD SizeOfImage 内存中整个PE文件的映射的尺寸,可以比实际的值大,但必须是SectionAlignment的整数倍
DWORD SizeOfHeaders 所有头+节表按照文件对齐后的大小,否则加载会出错
DWORD CheckSum 校验
WORD Subsystem
WORD DllCharacteristics
DWORD SizeOfStackReserve 初始化时保留的栈大小
DWORD SizeOfStackCommit 初始化时栈实际提交的大小
DWORD SizeOfHeapReserve 初始化时保留的堆大小
DWORD SizeOfHeapCommit 初始化时堆实际提交的大小
DWORD LoaderFlags
DWORD NumberOfRvaAndSizes 目录项数目

节表
#define IMAGE_SIZEOF_SHORT_NAME 8
Typedef struct IMAGE_SECTION_HEADER{
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
}Misc; //可以不准确
DWORD VirtualAddress; //节区在内存中的偏移地址,加上ImageBase才是真正地址
DWORD SizeOfRawData; //节在文件中对齐后的大小
DWORD PointerToRawData //节区在文件中的偏移
DWORD PointerToRelocations //在obj文件中使用
DWORD PointerToLinenumbers //行号表位置,调试的时候使用
WORD NumberOfRelocations //在obj文件中使用
WORD NumberOfLinenumbers //行号表中的行号的数量,调试的时候使用
DWORD Characteristics //节的属性(可读/可写/可执行)
}IMAGE_SECTION_HEADER,*PIMAGE_SECTION_HEADER;


// PERead.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#pragma warning(suppress : 4996)
//#include <iostream>
#include <windows.h>
#include <stdio.h>
#include <winnt.h>
#include <windef.h>
#include <stdlib.h>

char path[] = "C:\\Program Files (x86)\\Notepad++\\notepad++.exe";
LPSTR FILEPATH = path;

LPVOID ReadPEFile(LPSTR lpszFile);
VOID PrintNTHeaders();

int main()
{
    PrintNTHeaders();
    return 0;
}

LPVOID ReadPEFile(LPSTR lpszFile)
{
    FILE* pFile = NULL;
    DWORD fileSize = 0;
    LPVOID pFileBuffer = NULL;
    
    //打开文件
    pFile = fopen(lpszFile,"rb");
    if ( !pFile )
    {
        printf("无法打开EXE文件\n");
        return NULL;
    }
    //读取文件大小
    fseek(pFile,0,SEEK_END);
    fileSize = ftell(pFile);
    fseek(pFile,0,SEEK_SET);
    //分配缓冲区
    pFileBuffer = malloc(fileSize);
    if (!pFileBuffer)
    {
        printf("空间分配失败!\n");
        fclose(pFile);
        return NULL;
    }
    //讲文件数据读取到缓冲区
    size_t n = fread(pFileBuffer,fileSize,1,pFile);
    if (!n)
    {
        printf("读取数据失败!\n");
        free(pFileBuffer);
        fclose(pFile);
        return NULL;
    }
    //关闭文件
    fclose(pFile);
    return pFileBuffer;
}

VOID PrintNTHeaders()
{
    LPVOID pFileBuffer                      = NULL;
    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;
    char tableName[9] = { 0 };


    pFileBuffer = ReadPEFile(FILEPATH);
    if (!pFileBuffer)
    {
        printf("文件读取失败\n");
        return;
    }
    //判断是否是有效地MZ标志
    if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)
    {
        printf("不是有效地MZ标志\n");
        free(pFileBuffer);
        return;
    }
    pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
    //打印dos头
    printf("--------------------------------------DOC------------------------------------------\n");
    printf("MZ标志: %x\n",pDosHeader->e_magic);
    printf("PE偏移: %x\n", pDosHeader->e_lfanew);
    //判断是否是有效地PE标志
    if ( *((PDWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE )
    {
        printf("不是有效的PE标志\n");
        free(pFileBuffer);
        return;
    }
    pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
    //打印NT头
    printf("--------------------------------------NT------------------------------------------\n");
    printf("NT:%x\n", pNTHeader->Signature);
    pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
    printf("--------------------------------------PE------------------------------------------\n");
    printf("PE类型10B→32位    20B→64位:%x\n", pPEHeader->Machine);
    printf("文件存在的节的总数:%x\n", pPEHeader->NumberOfSections);
    printf("可选PE头的大小,32位默认E0h  64位默认F0h:%x\n", pPEHeader->SizeOfOptionalHeader);
    printf("PE属性:%x\n", pPEHeader->Characteristics);
    //可选PE头	
    pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
    printf("********************OPTIOIN_PE头********************\n");
    printf("OPTION_PE 类型:10B→32位    20B→64位:%x\n", pOptionHeader->Magic);
    printf("所有代码节的和:%x\n", pOptionHeader->SizeOfCode);
    printf("已初始化数据大小的和:%x\n", pOptionHeader->SizeOfInitializedData);
    printf("未初始化数据大小的和:%x\n", pOptionHeader->SizeOfUninitializedData);
    printf("程序入口:%x\n", pOptionHeader->AddressOfEntryPoint);
    printf("代码开始的基址:%x\n", pOptionHeader->BaseOfCode);
    printf("数据开始的基址:%x\n", pOptionHeader->BaseOfData);
    printf("内存镜像基址:%x\n", pOptionHeader->ImageBase);
    printf("内存对齐:%x\n", pOptionHeader->SectionAlignment);
    printf("文件对齐:%x\n", pOptionHeader->FileAlignment);
    printf("内存中整个PE文件的映射的尺寸:%x\n", pOptionHeader->SizeOfImage);
    printf("所有头+节表按照文件对齐后的大小:%x\n", pOptionHeader->SizeOfHeaders);
    printf("初始化时保留的栈大小:%x\n", pOptionHeader->SizeOfStackReserve);
    printf("初始化时栈实际提交的大小:%x\n", pOptionHeader->SizeOfStackCommit);
    printf("初始化时保留的堆大小:%x\n", pOptionHeader->SizeOfHeapReserve);
    printf("初始化时堆实际提交的大小:%x\n", pOptionHeader->SizeOfHeapCommit);
    printf("目录项数目:%x\n", pOptionHeader->NumberOfRvaAndSizes);

    pSectionHeader = (PIMAGE_SECTION_HEADER)((char*)pOptionHeader + (pPEHeader->SizeOfOptionalHeader));
    for (int i = 0; i < (pPEHeader->NumberOfSections); i++)
    {
        char* c = (char*)pSectionHeader;
        for (int n = 0; n < 8; n++)
        {
            tableName[n] = *c;
            c++;
        }
        printf("第%d个节的名字:%s\n",i,tableName);
        printf("第%d个节的大小:%x\n", i, pSectionHeader->Misc.VirtualSize);
        printf("第%d个节的内存偏移:%x\n", i, pSectionHeader->VirtualAddress);
        printf("第%d个节在文件中对齐的大小:%x\n", i, pSectionHeader->SizeOfRawData);
        printf("第%d个节的文件偏移:%x\n", i, pSectionHeader->PointerToRawData);
        printf("第%d个节的属性:%x\n", i, pSectionHeader->Characteristics);
        pSectionHeader++;
    }
    //释放内存	
    free(pFileBuffer);

}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

vurtual

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值