利用010进行简单的PE文件分析

作业正好写利用010分析PE文件,写一个博客复盘一下。

正在努力学习的小白,可能会有错误希望大佬们可以指出来(保命)。

参考资料:

https://cloud.tencent.com/developer/article/1910254

https://www.cnblogs.com/bonelee/p/17388067.html

利用到的工具

Masm32,010editor

一、PE文件概念

1.PE文件是什么?

PE文件的全称是Portable Executable,意为可移植的可执行的文件,常见的EXE、DLL、OCX、SYS、COM都是PE文件,PE文件是微软Windows操作系统上的程序文件(可能是间接被执行,如DLL)

PE文件是指32位可执行文件,也称为PE32。64位的可执行文件称为PE+或PE32+,是PE(PE32)的一种扩展形式。

2.PE的文件结构

PE加载到内存后的映射

image内存展开

(4)PE文件加载

Windows 加载器在加载PE文件至内存的过程如下

1.先读入PE文件DOS头、PE头、Section头

2.判断PE头ImageBase所存储的加载地址是否可用,如果已被占用,则重新分配空间

3.根据Section头部信息,把文件各个Section映射至系统分配的空间,并根据各个Section定义的数据来修改所映射的页属性

4.如果文件加载地址不是ImageBase所定义的地址,则重新修改ImageBase值

5.根据PE文件的输入表加载所需的DLL至进程空间

6.替换IAT表中的数据为实际调用函数地址

7.根据PE头生成初始化堆栈

8.创建初始化线程,开始运行PE文件进程

二、准备工作

下面利用实例来分析一下PE文件

首先利用masm32编写了一个非常简单的win32汇编程序

代码如下:

.386
.model flat,stdcall
option casemap:none

include windows.inc
include user32.inc
includelib C:\masm32\lib\user32.lib
include kernel32.inc
includelib kernel32.lib

.data
szText db 'Hello World! Welcome to School.',0ah,0dh,0ah,0dh
szTText db 'HAHA',0
szOption db 'summer',0ah,0dh

.code
start:
invoke MessageBox,NULL,offset szText,offset szOption,MB_OK
invoke ExitProcess,NULL
end start

在cmd下生成链接:

ml /c /coff 1.asm
link /subsystem:windows 1.obj

运行程序:

三、010 PE文件分析

使用010Editor工具打开PE文件运行模板

DOS头

在之前我们已经了解了PE文件的整体结构了,并且我们进行了静动态差异的文件分析,其开头部分就是DOS部分,包含了DOS MZ文件头和DOS块,那么我们来了解一些DOS部分的结构和其相关意义。

Windows PE 文件DOS头包含64B,其目的是兼容早期的DOS操作系统。

(1)MZ文件头

该部分固定大小为40H个字节。

这个结构体作用是给16位平台看的,而我们现在的环境大部分都是32位和64位的,所以现在的平台不再需要这个完整的结构体了,只需要其中的两个成员MZSignature(4D5A是一种标识)和AddressOfNewExeHeader(指示“NT映象头的偏移地址”,其中000000B0是NT映象头的文件偏移地址,定位PE文件头开始位置,用于PE文件合法性检验。)。

000000B0指向PE文件开头

利用010进行修改,除了MZSignature和AddressOfNewExeHeader都变为0

(2)DOS块(DOS stub)

DOS块就是夹在DOS MZ文件头和PE文件头之间的内容

DOS Stub 紧接着DOS头后,是链接器链接执行文件时加入的数据,通常为“This program cannot be run in DOS mode”。出现这句话是因为没有在DOS系统运行等原因,随便修改这个地方不会影响程序的运行。

PE头

PE整体的头包括以下内容

DWORD Signature        //PE标识

struct IMAGE_FILE_HEADER FileHeader        //标准PE头

struct IMAGE_OPTIONAL_HEADER32 OptionalHeader        //扩展PE头

(1)Signature        //PE标识

字串“PE\0\0”,改标识不能够被破坏。

(2)IMAGE_FILE_HEADER FileHeader         //标准PE头

WORD Machine; // 可以运行在什么样的CPU上,如果它的值为0x0则表示可以运行在任意的CPU上,支持在Intel 386以及后续的型号CPU运行则值为0x14c,支持64位的CPU型号则值为0x8664
WORD NumberOfSections; // 表示节的数量
DWORD TimeDateStamp; // 编译器编译的时候插入的时间戳,可以进行修改,与文件属性里面的创建时间和修改时间是无关的
DWORD PointerToSymbolTable; // 调试相关
DWORD NumberOfSymbols; // 调试相关
WORD SizeOfOptionalHeader; // 扩展PE头的大小
WORD Characteristics; // 文件属性

根据上面的图可以了解到,这个文件是在Intel 386以及后续的型号CPU上运行,有3个节表,创建时间是2022年9月19日 2:26:45,扩展PE头大小是224。

struct FILE_CHARACTERISTICS Characteristics里面的属性如下图所示

(3)IMAGE_OPTIONAL_HEADER32 OptionalHeader        //扩展PE头

分析的文件是32位,结构体如下:

typedef struct _IMAGE_OPTIONAL_HEADER {

WORD Magic; // Magic表示当前PE文件是32位还是64位,32位时该值对应0x10B,64位时该值对应0x20B

BYTE MajorLinkerVersion; // 链接器版本号

BYTE MinorLinkerVersion; // 链接器版本号

DWORD SizeOfCode; // 所有代码节的总和(文件对齐后的大小),编译器填的(没用)

DWORD SizeOfInitializedData; // 包含所有已经初始化数据的节的总大小(文件对齐后的大小),编译器填的(没用)

DWORD SizeOfUninitializedData; // 包含未初始化数据的节的总大小(文件对齐后的大小),编译器填的(没用)

DWORD AddressOfEntryPoint; // 程序入口

DWORD BaseOfCode; // 代码开始的基址,编译器填的(没用)

DWORD BaseOfData; // 数据开始的基址,编译器填的(没用)

DWORD ImageBase; // 内存镜像基址

DWORD SectionAlignment; // 内存对齐

DWORD FileAlignment; // 文件对齐

WORD MajorOperatingSystemVersion; // 标识操作系统版本号,主版本号

WORD MinorOperatingSystemVersion; // 标识操作系统版本号,次版本号

WORD MajorImageVersion; // PE文件自身的版本号

WORD MinorImageVersion; // PE文件自身的版本号

WORD MajorSubsystemVersion; // 运行所需子系统版本号

WORD MinorSubsystemVersion; // 运行所需子系统版本号

DWORD Win32VersionValue; // 子系统版本的值,必须为0

DWORD SizeOfImage; // 内存中整个PE文件的映射的尺寸

DWORD SizeOfHeaders; // 所有头加节表按照文件对齐后的大小,否则加载会出错

DWORD CheckSum; // 校验和

WORD Subsystem; // 子系统,驱动程序(1)、图形界面(2) 、控制台/DLL(3)

WORD DllCharacteristics; // 文件特性

DWORD SizeOfStackReserve; // 初始化时保留的栈大小

DWORD SizeOfStackCommit; // 初始化时实际提交的大小

DWORD SizeOfHeapReserve; // 初始化时保留的堆大小

DWORD SizeOfHeapCommit; // 初始化时实践提交的大小

DWORD LoaderFlags; // 调试相关

DWORD NumberOfRvaAndSizes; // 目录项数目

IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; // 表,结构体数组

} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

成员DWORD AddressOfEntryPoint表示当前程序入口地址(红色)900h

成员DWORD ImageBase表示示内存镜像基址 (蓝色)地址为400000h

打开ollydbg最终程序的入口地址为400900h

节表

在PE中,节数据有几个,分别对应着什么类型以及其他相关的属性都是由PE节表来决定的,PE节表是一个结构体数组,结构体的定义如下所示

#define IMAGE_SIZEOF_SHORT_NAME 8

typedef struct _IMAGE_SECTION_HEADER {

BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; // ASCII字符串(节名),可自定义,只截取8个字节,可以8个字节都是名字

union { // Misc,双字,是该节在没有对齐前的真实尺寸,该值可以不准确

DWORD PhysicalAddress; // 真实宽度,这两个值是一个联合结构,可以使用其中的任何一个

DWORD VirtualSize; // 一般是取后一个

} Misc;

DWORD VirtualAddress; // 在内存中的偏移地址,加上ImageBase才是在内存中的真正地址

DWORD SizeOfRawData; // 节在文件中对齐后的尺寸

DWORD PointerToRawData; // 节区在文件中的偏移

DWORD PointerToRelocations; // 调试相关

DWORD PointerToLinenumbers; // 调试相关

WORD NumberOfRelocations; // 调试相关

WORD NumberOfLinenumbers; // 调试相关

DWORD Characteristics; // 节的属性

} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

DWORD PointerToRawData节区在文件中的偏移如下图所示其中.text(红色).rdata(黑色).data(绿色) 

偏移地址与框内的三个节的开始的位置相同

3个节 

struct IMAGE_SECTION_DATA Section[0] 400H-5ffH(.text):代码节

struct IMAGE_SECTION_DATA Section[1] 600H-7ffH(.rdata):引入函数节

struct IMAGE_SECTION_DATA Section[2] 800H-9ffH(.data):数据节

代码节

Opcode Data[512]:操作码,本次用的是win32汇编语言编写

引入函数节

UCHAR Data[512]:字节型

与程序中的这部分对应

数据节

UCHAR Data[512]:字节类型

与程序中这部分对应

在010中可以对这部分的内容进行更改

导入表 IMPORT_DESCRIPTOR

与.rdata相对应

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;         // 包含指向IMAGE_DATA(输入名称表)RVA 的结构数组
    };
    DWORD   TimeDateStamp;                  //当可执行文件不与被导入的DLL进行绑定时,此字段为0
    DWORD   ForwarderChain;                 //第一个被转向的API索引
    DWORD   Name;                           //指向被导入的DLL 名称
    DWORD   FirstThunk;                     //指向输入地址表(IAT)RVA,IAT是一个IMAGE_THUNK_DATA结构的数组
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR

到这里就分析完我所编写简单的PE文件

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值