视频资源详见网盘搜索 或 在线的B站滴水逆向三期

其课件也能在CSDN或百度搜索到,以下部分为课件内容摘要,部分为自己的理解

最后附上详细注释的自写代码

PE(Portable Executable)文件,是windows上的可移植的可执行文件,常见的 .exe  .dll   .sys 等都是PE文件。那么PE文件和计算机网络的各种包头一样,都有自己独特的格式。

这里有张PE格式图的部分截图,完整版链接:https://pan.baidu.com/s/1ToG2wc_qOi0BfrLTeN0HPw 
提取码:PEst 
不要被这么多字段吓到,只有小部分是常用的

首先PE文件开头就是DOS头 DOS_HEADER,DOS头只需要看第一个字段magic和最后一个lfanew,magic字段为一个WORD即两字节,若为MZ则属于PE文件,lfanew是指向NT头NT_HEADER的相对偏移,即文件起始位置(magic字段所在位置)+ lfanew 即为NT头的位置

可以用winhex等十六进制查看器查看会有更直观的印象。lfanew字段到NT头之间会有一段说明字符串或者垃圾数据称为DOS stub

接下来NT头分为一个字段和两大部分,Signature字段,一大部分是FILE_HEADER(即COFF头或称文件头/标准PE头),另一大部分是OPTIONAL_HEADER 可选头。注意FILE_HEADER一共有0x14的长度,可以看成一个大的结构体,结构体内容就是图中_IMAGE_FILE_HEADER的部分。OPTIONAL_HEADER 可选头同理。

过了NT头,就是节表SECTION_HEADER 了。节表类似于结构体数组的形式,图所示会有多个同样结构的SECTION_HEADER重复。

以下就是PE头中的常用字段,这些字段现只需了解,具体的意义可能得等后续的文章讲解中逐渐都会用到后才能真正理解,后面用到了回头看也不迟

1、DOS头:
WORD   e_magic                * "MZ标记" 用于判断是否为可执行文件.
DWORD  e_lfanew;              * PE头相对于文件的偏移,用于定位PE文件
2、标准PE头(COFF头):
WORD    Machine;              * 程序运行的CPU型号:0x0 任何处理器/0x14C 386及后续处理器
WORD    NumberOfSections;     * 文件中存在的节的总数,如果要新增节或者合并节 就要修改这个值.
DWORD   TimeDateStamp;        * 时间戳:文件的创建时间(和操作系统的创建时间无关),编译器填写的.
DWORD   PointerToSymbolTable; 
DWORD   NumberOfSymbols; 
WORD    SizeOfOptionalHeader; * 可选PE头的大小,32位PE文件默认E0h 64位PE文件默认为F0h  大小可以自定义.
WORD    Characteristics;      * 每个位有不同的含义,可执行文件值为10F 即0 1 2 3 8位置1 
3、可选PE头:
WORD    Magic;        * 说明文件类型:10B 32位下的PE文件     20B 64位下的PE文件
BYTE    MajorLinkerVersion;
BYTE    MinorLinkerVersion;
DWORD   SizeOfCode;* 所有代码节的和,必须是FileAlignment的整数倍 编译器填的  没用
DWORD   SizeOfInitializedData;* 已初始化数据大小的和,必须是FileAlignment的整数倍 编译器填的  没用
DWORD   SizeOfUninitializedData;* 未初始化数据大小的和,必须是FileAlignment的整数倍 编译器填的  没用
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;* 目录项数目
DOS  PE  OPTION 
  
DOS + PE标记 + 标准PE头 + 可选PE

接下来就是写代码读取PE文件的部分字段了,其他更多字段可以自行添加.

看懂代码或者自己写一遍代码才是完全理解了PE头结构和这些字段分布

使用window.h 都有相匹配的结构体,直接.或者->都能找到需要的字段,在VS中会识别出比这里更多的高亮,比如DWORD 、 PIMAGE_DOS_HEADER  之类的windows.h 才有的宏定义

代码中用到的exe 可自行找一个小点的独立的exe,比如网上搜些逆向用的crackme  exe程序检测自写代码用

#include "stdio.h"
#include "windows.h"
inline LPVOID ReadPEFile(LPSTR lpszFile)  //LPSTR 即  CHAR *
{FILE *pFile = NULL;DWORD fileSize = 0;LPVOID pFileBuffer = NULL;    //LPVOID 即  void *//打开文件    pFile = fopen(lpszFile, "rb");if (!pFile){printf(" 无法打开 EXE 文件! ");return NULL;}//读取文件大小       fseek(pFile, 0, SEEK_END);fileSize = ftell(pFile);fseek(pFile, 0, SEEK_SET);//分配缓冲区    pFileBuffer = malloc(fileSize);  //pFileBuffer指向一段 分配的exe文件这么大的内存if (!pFileBuffer){printf(" 分配空间失败! ");fclose(pFile);return NULL;}//将文件数据读取到缓冲区  size_t n = fread(pFileBuffer, fileSize, 1, pFile);   //size_t 即 unsigned intif (!n){printf(" 读取数据失败! ");free(pFileBuffer);fclose(pFile);return NULL;}//关闭文件  fclose(pFile);return pFileBuffer;       //最后返回指向 已填充exe文件内容 的新开辟空间 的指针
}VOID h312()
{char FilePath[] = "CRACKME.EXE";LPVOID pFileBuffer = NULL;PIMAGE_DOS_HEADER pDosHeader = NULL;PIMAGE_NT_HEADERS pNTHeader = NULL;PIMAGE_FILE_HEADER pFileHeader = NULL;PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = NULL;PIMAGE_SECTION_HEADER pSectionHeader = NULL;pFileBuffer = ReadPEFile(FilePath);   //pFileBuffer即指向已装载到内存中的exe首部if (!pFileBuffer){printf("文件读取失败\n");return;}//判断是否是有效的MZ标志  if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)  //pFileBuffer强转 WORD* 后 取指针指向的内容{printf("不是有效的MZ标志\n");free(pFileBuffer);return;}pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;    // 强转 DOS_HEADER 结构体指针//打印DOS头  printf("********************DOS头********************\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)  //基址pFileBuffer + lfanew 为 NTHeader首址{printf("不是有效的PE标志\n");free(pFileBuffer);return;}pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);//打印NT头    printf("*****************NT HEADERS*****************\n");printf("NT:%x\n", pNTHeader->Signature);pFileHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);    //NT头地址 + 4 为 FileHeader 首址printf("****************FILE HEADERS****************\n");printf("PE:%x\n", pFileHeader->Machine);printf("节的数量:%x\n", pFileHeader->NumberOfSections);printf("SizeOfOptionalHeader:%x\n", pFileHeader->SizeOfOptionalHeader);//可选PE头    pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);//SIZEOF_FILE_HEADER为固定值且不存在于PE文件字段中printf("**************OPTIOINAL HEADERS*************\n");printf("Magic:%x\n", pOptionalHeader->Magic);//释放内存    free(pFileBuffer);
}

运行程序打印内容如下:

********************DOS头********************
MZ标志:5a4d
PE偏移:100
*****************NT HEADERS*****************
NT:4550
****************FILE HEADERS****************
PE:14c
节的数量:6
SizeOfOptionalHeader:e0
**************OPTIOINAL HEADERS*************
Magic:10b

可使用 PETool  或 LoadPE 等小软件查看相应PE文件内容。对照打印内容检测自写的程序是否正确。

滴水逆向三期实践1:PE头字段解析,附PE结构下载相关推荐

  1. 滴水逆向三期实践6:扩大节

    扩大节: 1.拉伸到内存(只是逻辑上,实际代码操作并不需要这一步),需要注入的代码为 ShellCode 2.分配一块新的空间:SizeOfImage + sizeof ( ShellCode) 3. ...

  2. 滴水逆向三期实践16:IAT表和导入表

    IAT表 在我们调用 dll 函数的时候,发现代码中的汇编,是通过间接寻址的.也就是不直接 call 函数地址,而是通过一个中间地址再跳转的. 比如调用MessageBox这类系统函数的时候(这也是个 ...

  3. 滴水逆向三期实践15:重定位表修正

    我们知道,重定位表是由于在代码中写入的绝对地址,而 DLL 不能按照设想的 ImageBase 作起始加载位置去了别的地方占坑,那么需要根据重定位表记录的这些绝对地址在内存中的位置(RVA),逐一去到 ...

  4. 滴水逆向三期实践10:动态链接库

    使用静态链接生成的可执行文件体积较大,造成浪费 我们常用的printf.memcpy.strcpy等就来自这种静态库 有维护性问题.不方便修改库中实现,修改后所有使用静态库的都要重新链接一遍 因为静态 ...

  5. 静态链接库,动态链接库【滴水逆向三期48笔记】

    在开发过程中,我们通常会有很多函数,需要多次使用或在不同的程序中使用该函数,也有可能我们会将我们写好的函数给别人使用,但是我们又不想给他源代码,毕竟代码是我们花了很多功夫写出来的,那么我们如何不发给其 ...

  6. WindowsPE(一)PE头字段节表

    PE头结构 PE头关键字段说明 DOS头 标准PE头 可选PE头 节表数据结构说明 通过程序,输出所有的PE头信息 #include<iostream> #include<Windo ...

  7. 滴水逆向三期 win10 ASLR UnmapViewOfSection傀儡进程 加密壳项目

    特意加了一个win10标题, 碰到0xc0000005的朋友就不要再浪费时间了, 我在这个问题上花了整整一天 网上全是xp的代码, 搜到了了几个win10的也是同样的错误没法解决, 唯一一个有用的答案 ...

  8. 滴水逆向三期笔记与作业——02C语言——02数据类型

    海哥牛逼 这里写自定义目录标题 一.C语言如何变成汇编 1.裸函数 二.调用约定 1.常见的几种调用约定 三.程序的真正入口 四.数据类型 4.1 C语言中的数据类型 作业 一.C语言如何变成汇编 1 ...

  9. 导入表解析,IAT表解析【滴水逆向三期53笔记】

    我们再上一章节简要介绍了IAT表,我们知道如果程序调用dll中的函数时,必须通过IAT表来找到函数,我们基本了解了IAT表之后,我们今天来讲解一下导入表,通过本章节的学习,我们可以了解导入表,也能对I ...

最新文章

  1. 解决IDEA中导入新的maven依赖后Language Level自动重置问题
  2. nyoj 42 一笔画问题 (搜索+队列)
  3. Android开发之简单修改TabLayout默认文字大小的方法(亲测可用)
  4. Python学习(二)语言基础
  5. mongodb数据库的备份与恢复
  6. android远程桌面软件毕设_向日葵远程管理软件
  7. iPhone 12机模曝光:继续刘海屏、回归直面边框
  8. qt show widget_QWidgetStack类 - Qt 参考中文帮助文档
  9. 11条重要的数据库设计原则
  10. 【以太坊源码阅读】椭圆曲线加密和EIP155
  11. Excel学习日记:L9-图表制作-柱状图
  12. python结构体_python中定义结构体
  13. 双硬盘笔记本电脑安装WIN10和Ubuntu双系统(二)
  14. 给春节的宴客小吃来点小惊喜---绿茶甜心曲奇
  15. 影之刃服务器维护,影之刃无法联机到服务器怎么办 解决办法
  16. 自动化立体仓库使用流程!海格里斯自动化立体库流程:入库——出库——拣选
  17. Asp.Net Core WebApi 身份验证、注册、用户管理
  18. 02.AOSP调试记录
  19. selenium 模拟人工登录 高德开发者平台(python)
  20. 解决:Mac brew Error: Your CLT does not support macOS 11.2

热门文章

  1. 拆机指点杆小红点的线序及PTPM754DR引脚定义
  2. @async注解_Spring @Async 使用
  3. ABP netcore多数据库连接,动态链接字符串
  4. 猜价格游戏(Java):给出相应的提示,并记录次数
  5. 计算机里没有四款小游戏,电脑里自带游戏没有怎么办 这个方法快速找回
  6. Github使用手册
  7. 我国常见的光伏电站分类
  8. 临时抱佛脚之计组知识点
  9. mtk HW FG电量计算过快或者过慢问题分析
  10. php中frameset,Frameset使用教程