首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >逆向工程基础:从PE文件到进程地址空间

逆向工程基础:从PE文件到进程地址空间

作者头像
范蠡
发布2021-01-04 11:21:31
发布2021-01-04 11:21:31
1.2K00
代码可运行
举报
运行总次数:0
代码可运行

计算机鼻祖——图灵机

所谓的图灵机就是指一个抽象的机器,它有一条无限长的纸带,纸带分成了一个一个的小方格,每个方格有不同的颜色。 有一个机器头在纸带上移来移去。机器头有一组内部状态,还有一些固定的程序。 在每个时刻,机器头都要从当前纸带上读入一个方格信息,然后结合自己的内部状态查找程序表,根据程序输出信息到纸带方格上,并转换自己的内部状态,然后进行移动。 ——摘自 百度百科

编程语言是一组描述计算机,更准确的说是描述CPU如何执行指令的语法规则。

对于确定的处理器,它能执行的指令是确定的,这就是CPU的指令集。CPU的指令集是机器码,于是人们为了容易编程,发明了使用助记符的汇编语言。后来,又出现 了高级语言。高级语言只是用人更容易理解的语法来描述了 这些指令的组织规则,而最终翻译成机器指令则是由编译器来完成的。

不同的编程语言使用了不同的描述规则,这就如同各个不同 国家用不同的语言说“你好”,虽然表达各异,但最终说的 是同一个意思。

程序的两大组件

  • 指令
  • 数据

PE文件完成的任务就是:

用一套数据结构来组织编程语言编译后的指令和数据。因此,PE文件最重要的就是指令和数据的组织方法了。当然,除了指令和数据,还涉及操作系统的相关内容,比如资源等。

什么是PE文件?

全称Portable Execute —— 可移植的执行体,是 windows平台组织程序代码的一种文件格式。常⻅的exe、dll、sys、 ocx、com都属于PE文件。

学习PE文件,是深入windows机制的重 要一步,也是逆向工程基础课程。

PE文件分为以下四大块,依次是:

  • DOS实模式残留数据
  • NT文件头
  • 节表
  • 节内容

一、DOS实模式残留数据

在Windows NT之前的Windows系统是基于dos操作系统内核,为了兼容dos系统上可执行文件,Windows NT在设计可执行文件格式时保留兼容了之前的格式。

PE文件中的DOS实模式残留数据包括两部分: DOS头 + DOS Stub,如下图所示:

DOS头的数据结构是这样的:

代码语言:javascript
代码运行次数:0
运行
复制
typedef struct _IMAGE_DOS_HEADER {
  WORD e_magic; //”MZ”
  WORD e_cblp;
  WORD e_cp;
  WORD e_crlc;
  WORD e_cparhdr;
  WORD e_minalloc; 
  WORD e_maxalloc; 
  WORD e_ss;
  WORD e_sp;
  WORD e_csum; WORD e_ip;
  WORD e_cs;
  ......
  LONG e_lfanew;
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

重点关注的是最后一个字段, 有时称为3C字段,它指示了windows NT PE文件头的偏移位置。

Dos Stub是一段16位的程序,反汇编如下:

这一段16位汇编程序:调用21号中断的9号功能:向屏幕输出 一个字符串“this ...”,然后调用4C号功能退出程序。

二、NT文件头

前面提到: DOS头的最后一个字段指示了NT头的位置。

NT头分为三个部分:

代码语言:javascript
代码运行次数:0
运行
复制
typedef struct _IMAGE_NT_HEADERS{
  DWORD Signature;
  IMAGE_FILE_HEADER FileHeader;
  IMAGE_OPTIONAL_HEADER OptionalHeader;
}IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;

第一部分是一个PE标识符,长度4个字节:

第二部分数据包含了PE文件的一些最基本的信息,其结构如下:

代码语言:javascript
代码运行次数:0
运行
复制
typedef struct _IMAGE_FILE_HEADER {
  WORD  Machine;         //运行平台
  WORD  NumberOfSections;  //节数目
  DWORD TimeDateStamp;   //文件创建时间
  DWORD PointerToSymbolTable; //指向符号表指针
  DWORD NumberOfSymbols;  //符号数目
  WORD  SizeOfOptionalHeader; //可选头长度
  WORD  Characteristics;  //文件属性
}IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

第三部分,叫可选头,虽然名为可选头,但这个结构是整个PE文件中占重要地位,记录的PE文件的代码执行入口、运行平台、堆栈属性等等信息,绝对不能没有(以下为节选部分字段)

代码语言:javascript
代码运行次数:0
运行
复制
typedef struct _IMAGE_OPTIONAL_HEADER {
  WORD  Magic;
  DWORD AddressOfEntryPoint;  // 代码执行入口
  DWORD BaseOfCode;
  DWORD BaseOfData;
  DWORD ImageBase;        // 加载基地址
  DWORD SectionAlignment;
  DWORD FileAlignment;
  WORD  Subsystem;
  WORD  DllCharacteristics;
  DWORD SizeOfStackReserve;
  DWORD SizeOfStackCommit;
  DWORD SizeOfHeapReserve;
  DWORD SizeOfHeapCommit;
  IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
}IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;

可选头的最后一个字段是一个数组,名叫数据目录。数组大小是一个宏定义,其值为16:

代码语言:javascript
代码运行次数:0
运行
复制
#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
typedef struct _IMAGE_DATA_DIRECTORY {
 DWORD VirtualAddress; //RVA
 DWORD Size;   //大小
} IMAGE_DATA_DIRECTORY,  *PIMAGE_DATA_DIRECTORY;

这16个数据目录项的内容分别是:

三、节表

在NT文件头之后,就是节表信息了:

代码语言:javascript
代码运行次数:0
运行
复制
typedef struct _IMAGE_SECTION_HEADER {
  BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; //节名字
  union {
    DWORD PhysicalAddress;
    DWORD VirtualSize;
  } Misc;
  DWORD VirtualAddress;
  DWORD SizeOfRawData;
  DWORD PointerToRawData;
  DWORD PointerToRelocations;
  DWORD PointerToLinenumbers;
  WORD  NumberOfRelocations;
  WORD  NumberOfLinenumbers;
  DWORD Characteristics;
}IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

每个节表项占据40个字节,该结构体的数量在PE文件头的NumberOfSections指定。每一个 结构体描述了PE文件中的一个节的信息。

四、节内容

前面各种头部信息之后,我们迎来了PE文件的主体: 节区

节区没有特定的格式,不同的节承载的内容不一样,也就有不同的数 据结构。

常⻅的节如下:

  • .text :代码节(VC)
  • .code : 代码节(VB/Delphi)
  • .data : 数据节(一般存放已初始化的全局变量,静态变量)
  • .rdata : 只读数据节(一般存放只读数据,如常量字符串,C++虚表) .idata : 输入数据表(一般用来存放IAT和导入表)
  • .bss : 通常是指用来存放程序中未初始化的全局变量、静态变量 .textbss : 节中同时包含代码和未初始化全局变量、静态变量
  • .rsrc : 资源节
  • .reloc : 重定位表

进程

进程是操作系统结构的基础;

是一个正在执行的程序;

计算机中正在运行的程序实例;

可以分配给处理器并由处理器执行的一个实体;

由单一顺序的执行显示,一个当前状态和一组相关的系统资源所描 述的活动单元。

——百度百科

通俗的理解,一个可执行文件运行起来的实体称之为进程。在没有线程概念的时候,进程代表了多任务系统上的一个任务实体。

进程包含哪些内容?

在Intel 80386处理器诞生之前,程序运行时使用的地址是真实的物理地址。学过微机原理和单片机的知道,8051单片机有64KB的内存寻址空间,在8051上编写程序时我们的地址都是内存区域上的真实地址。

为应对软件技术的快速发展和对内存的需求增大,Intel 80386处理器应运而生,它提出了一种新的工作模式——保护模式。而之前的工作方式称之为——实模式。80386处理器的诞生直接加速了PC的发展。

在32位的操作系统中,在保护模式下,每个进程都拥有4GB的地址空间,不必再考虑和别的程序共享一个地址空间的问题。

每个进程4GB地址空间是虚拟的,通过⻚式存储机制,进程可以安全的享用这些内存。

正是因为是4GB地址空间,也就解释了为什么指针需要4B了。

看一下进程的地址空间分布:

我们经常说程序运行后在内存中,需要强调的是进程的4GB内存空间不是位于真正的内存条上。

事实上,进程的可执行文件和dll所占的“内存”⻚面实际上是映射到硬盘中的文件的。这就是内存映射文件机制。另外,4GB大小的空间中大部分都是空头支票,只有真正需要用到的时候才会予以分配。而在内存紧张时,不常用的页面还会被操作系统通过页交换文件的方式换到硬盘上去。

PE文件到进程地址空间的映射

PE文件按照⻚面大小映射到内存中,在硬盘上,文件对⻬粒度是512B(一个扇区大小),在内存中,⻚面粒度是4KB (32位的Windows系统上)。如下图所示:

关于PE文件到进程空间的对照关系,你弄懂了吗?

代码语言:javascript
代码运行次数:0
运行
复制
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-12-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 高性能服务器开发 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 计算机鼻祖——图灵机
  • 程序的两大组件
  • 什么是PE文件?
  • 一、DOS实模式残留数据
  • 二、NT文件头
  • 三、节表
  • 四、节内容
  • 进程
  • PE文件到进程地址空间的映射
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档