PE格式是Windows系统下最常用的可执行文件格式,有些应用必须建立在了解PE文件格式的基础之上,如可执行文件的加密与解密,文件型病毒的查杀等,熟练掌握PE文件结构,有助于软件的分析.

在PE文件中,代码,已初始化的数据,资源和重定位信息等数据被按照属性分类放到不同的Section(节区/或简称为节)中,而每个节区的属性和位置等信息用一个IMAGE_SECTION_HEADER结构来描述,所有的IMAGE_SECTION_HEADER结构组成了一个节表(Section Table),节表数据在PE文件中被放在所有节数据的前面.

在Win32系统中,当我们执行了可执行文件之后,可执行文件会被映射到内存,并且以4kb的粒度进行对齐,这个4kb也就是一个页面的大小,而每个页面又分别具有,可执行,可读写等属性.

PE格式中的DOS部分由MZ格式的文件头和可执行代码部分组成,可执行代码被称为DOS块(DOS stub).MZ格式的文件头由IMAGE_DOS_HEADER结构定义,以下就是DOS头部分的关键属性.

		mov esi,lpMemory
assume esi:ptr IMAGE_DOS_HEADER
movzx eax,[esi].e_magic ; 读取DOS的头部
movzx eax,[esi].e_ss ; DOS代码段的初始堆栈段
movzx eax,[esi].e_sp ; DOS代码段的初始堆栈指针
movzx eax,[esi].e_cs ; DOS代码的入口地址
movzx eax,[esi].e_ip ; DOS代码的入口IP
movzx eax,[esi].e_lfanew ; 指向了PE文件的开头(重要)

第一个字段e_magic被定义为MZ,标志着DOS文件的开头部分,最后一个字段e_lfanew则指明了PE文件的开头位置,现在来说除了第一个字段和最后一个字段有些用处,其他的字段几乎已经废弃了,这里也不再介绍了.

解析PE头结构

从DOS文件头的e_lfanew字段(文件头偏移003ch),PE文件格式排列在DOS头的后面,也就是e_lfanew指针所指向的地址,而PE文件的第一个字节就是PE这两个字符,有了这些信息,我们就可以写一个小工具,来检测指定一个程序是否是可执行文件啦.

.data
szFileName db "lyshark.exe",0h
hFile dd ?
hMapFile dd ?
lpMemory dd ?
szText db "这是一个PE可执行文件 !",0h
.code
main PROC
; 打开文件,并创建内存映射镜像
invoke CreateFile,addr szFileName,GENERIC_READ,FILE_SHARE_READ or \
FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,NULL
mov hFile,eax
invoke CreateFileMapping,hFile,NULL,PAGE_READONLY,0,0,NULL
mov hMapFile,eax
invoke MapViewOfFile,eax,FILE_MAP_READ,0,0,0
mov lpMemory,eax
; -------------------------------------------------------------------
; 检测PE文件是否有效,是否是一个正常的PE
mov esi,lpMemory
assume esi:ptr IMAGE_DOS_HEADER
; 判断是否为DOS文件头部
.if [esi].e_magic == IMAGE_DOS_SIGNATURE
add esi,[esi].e_lfanew ; 递增指针
assume esi:ptr IMAGE_NT_HEADERS
; 判断是否为PE可执行文件
.if [esi].Signature == IMAGE_NT_SIGNATURE
invoke MessageBox,NULL,addr szText,0,MB_OK
.endif
.endif
; -------------------------------------------------------------------
invoke UnmapViewOfFile,addr lpMemory
invoke ExitProcess,NULL
main ENDP
END main

上面的核心代码原理也非常的简单,过程:读入文件,判断第一个字符是不是MZ,如果是MZ,则在判断e_lfanew指针指向的地址是不是PE如果是,则说明这是PE文件.

解析各区块信息

下面的代码,则用于读取PE文件的一些关键区块信息.

	.386
.model flat,stdcall
option casemap:none include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
include masm32.inc
includelib masm32.lib .data
szFileName db "lyshark.exe",0h
hFile dd ?
hMapFile dd ?
lpMemory dd ?
lpBuffer db 2048 dup(?)
.const
szMsg db "----------------------------------------",0dh,0ah
db "运行平台: 0x%04X",0dh,0ah
db "节区数量: %d",0dh,0ah
db "文件属性: 0x%04X",0dh,0ah
db "时间标记: %d",0dh,0ah
db "镜像装入基址: 0x%08X",0dh,0ah
db "程序的入口RVA: 0x%08X",0dh,0ah
db "代码节起始RVA: 0x%08X",0dh,0ah
db "数据节起始RVA: 0x%08X",0dh,0ah
db "----------------------------------------",0dh,0ah,0
.code
main PROC
; 打开文件,并创建内存映射镜像
invoke CreateFile,addr szFileName,GENERIC_READ,FILE_SHARE_READ or \
FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,NULL
mov hFile,eax
invoke CreateFileMapping,hFile,NULL,PAGE_READONLY,0,0,NULL
mov hMapFile,eax
invoke MapViewOfFile,eax,FILE_MAP_READ,0,0,0
mov lpMemory,eax
mov esi,lpMemory assume esi:ptr IMAGE_DOS_HEADER
add esi,[esi].e_lfanew
assume esi:ptr IMAGE_NT_HEADERS
invoke wsprintf,addr lpBuffer,addr szMsg,\
[esi].FileHeader.Machine, \ ; 运行平台
[esi].FileHeader.NumberOfSections, \ ; 节区数目
[esi].FileHeader.Characteristics, \ ; 文件属性
[esi].FileHeader.TimeDateStamp, \ ; 时间标记
[esi].OptionalHeader.ImageBase, \ ; 镜像基址
[esi].OptionalHeader.AddressOfEntryPoint, \ ; 入口RVA地址
[esi].OptionalHeader.BaseOfCode, \ ; 代码节起始RVA
[esi].OptionalHeader.BaseOfData
invoke StdOut,addr lpBuffer
invoke ExitProcess,NULL
main ENDP
END main

解析节与节表

系统装载可执行文件并不等同于内存映射,内存映射是将整个磁盘文件原封不动的搬到内存中去,而PE的加载则会处理一些其他数据,例如预处理,重定位等,装入以后页面位置,偏移等都会随之发生改变,Windows装载器在装载DOS部分,PE文件头部分和节表部分时不进行任何处理,而装载节的时候将根据节的属性做不同的处理.

	.386
.model flat,stdcall
option casemap:none include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
include masm32.inc
includelib masm32.lib .data
szFileName db "lyshark.exe",0h
hFile dd ?
hMapFile dd ?
lpMemory dd ?
lpBuffer db 2048 dup(?)
.const
szMsg db "----------------------------------------------------------",0dh,0ah
db "节区名称 节区大小 虚拟地址 Raw_尺寸 Raw_偏移 节区属性",0dh,0ah
db "----------------------------------------------------------",0dh,0ah,0
szFmt db "%s %08X %08X %08X %08X %08X",0dh,0ah,0
.code
main PROC
invoke CreateFile,addr szFileName,GENERIC_READ,FILE_SHARE_READ or \
FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,NULL
mov hFile,eax
invoke CreateFileMapping,hFile,NULL,PAGE_READONLY,0,0,NULL
mov hMapFile,eax
invoke MapViewOfFile,eax,FILE_MAP_READ,0,0,0
mov lpMemory,eax
mov esi,lpMemory assume esi:ptr IMAGE_DOS_HEADER ; 指向DOS开头
add esi,[esi].e_lfanew ; 递增指针到PE结构开头
assume esi:ptr IMAGE_NT_HEADERS invoke StdOut,addr szMsg ; 输出提示信息
movzx ecx,[esi].FileHeader.NumberOfSections ; 取出节的数量,作为循环条件
add esi,sizeof IMAGE_NT_HEADERS ; 指向.text节
assume esi:ptr IMAGE_SECTION_HEADER ; 指向节中的SECTION
.repeat
push ecx ; wsprintf影响ecx寄存器,所以这里必须压栈保存数据
mov eax,[esi].VirtualAddress invoke wsprintf,addr lpBuffer,addr szFmt,esi, \ ; 节区名称
[esi].Misc.VirtualSize, \ ; 节区大小
[esi].VirtualAddress, \ ; 虚拟地址
[esi].SizeOfRawData, \ ; Raw_尺寸
[esi].PointerToRawData, \ ; Raw_偏移
[esi].Characteristics ; 节区属性
invoke StdOut,addr lpBuffer ; 打印节区信息
pop ecx
add esi,sizeof IMAGE_SECTION_HEADER
.untilcxz
invoke ExitProcess,NULL
main ENDP
END main

Win32汇编:汇编版PE结构解析器的更多相关文章

  1. Win32汇编-编写PE结构解析工具

    汇编语言(assembly language)是一种用于电子计算机.微处理器.微控制器或其他可编程器件的低级语言,亦称为符号语言.在汇编语言中,用助记符(Mnemonics)代替机器指令的操作码,用地 ...

  2. 手写PE结构解析工具

    PE格式是 Windows下最常用的可执行文件格式,理解PE文件格式不仅可以了解操作系统的加载流程,还可以更好的理解操作系统对进程和内存相关的管理知识,而有些技术必须建立在了解PE文件格式的基础上,如 ...

  3. C++PE文件格式解析类(轻松制作自己的PE文件解析器)

    PE是Portable Executable File Format(可移植的运行体)简写,它是眼下Windows平台上的主流可运行文件格式. PE文件里包括的内容非常多,详细我就不在这解释了,有兴趣 ...

  4. PE文件解析器的编写(二)——PE文件头的解析

    之前在学习PE文件格式的时候,是通过自己查看各个结构,自己一步步计算各个成员在结构中的偏移,然后在计算出其在文件中的偏移,从而找到各个结构的值,但是在使用C语言编写这个工具的时候,就比这个方便的多,只 ...

  5. 自制C#版3DS文件的解析器并用SharpGL显示3DS模型

    自制C#版3DS文件的解析器并用SharpGL显示3DS模型 我已经重写了3ds解析器,详情在此(http://www.cnblogs.com/bitzhuwei/p/CSharpGL-2-parse ...

  6. PE解析器的编写(四)——数据目录表的解析

    在PE结构中最重要的就是区块表和数据目录表,上节已经说明了如何解析区块表,下面就是数据目录表,在数据目录表中一般只关心导入表,导出表和资源这几个部分,但是资源实在是太复杂了,而且在一般的病毒木马中也不 ...

  7. PE解析器的编写(一)——总体说明

    之前自己学习了PE文件的格式,后来自己写了个PE文件的解析器,这段时间工作上刚好要用到它,老板需要能查看某个exe中加载的dll的一个工具,我在使用之前自己写的这个东西的时候,发现很多东西都忘记了,所 ...

  8. PE解析器与加载器编写指南

    PE解析器与加载器编写指南 最近准备去实习,看公司要求应该开发PE相关的查杀引擎,因此再回头复习一下PE格式,重新写一个PE解析器和PE加载器,再此记录下有关坑. PE解析器部分: 1)如何确定节区表 ...

  9. Xamarin版的C# SVG路径解析器

    原文:Xamarin版的C# SVG路径解析器 Xamarin版的C# SVG路径解析器,对SVG的Path路径进行解析,其中包括: 主程序SvgPathParser.cs, 相关接口定义:ISour ...

  10. PE头结构解析(代码实现)

    PE头结构解析(代码实现) 图表实现在:https://www.cnblogs.com/juicyhumberger/articles/17064764.html #include "std ...

随机推荐

  1. QA32增强

    一.QA32报表新增字段 二.QA32报表程序RQEEAL10 结构增加字段,该结构就是报表展示字段列的结构 表新增字段 找到报表展示的子例程 找到程序RQEEAL10,子例程中新增隐式增强 隐式增强 ...

  2. Java 时间戳和时间相互转换 日期时间和字符串相互转换 日期时间相减差值 日期时间增加指定天数

    Java 时间戳和时间相互转换 日期时间和字符串相互转换 日期时间相减差值 日期时间增加指定天数 代码: package com.sux.demo; import java.text.ParseExc ...

  3. 前端科普系列(4):Babel —— 把 ES6 送上天的通天塔

    本文首发于 vivo互联网技术 微信公众号 链接: https://mp.weixin.qq.com/s/plJewhUd0xDXh3Ce4CGpHg作者:Morrain 一.前言 在上一节 < ...

  4. joi

  5. IDEA用上这十大插件绝对舒服

    本文翻译自国外论坛 medium,原文地址:https://medium.com/@xjpp22/top-10-plugins-for-intellij-idea-you-dont-want-to-m ...

  6. sipp3.6分支压测方案

    概述 SIP压测工具sipp,免费,开源,功能足够强大,配置灵活,优点多. 本文档介绍sipp工具的常用参数和测试脚本. 环境 centos7.9 sipp v3.6.2_rc1 常用参数 -sf 加 ...

  7. 永久激活Windows10专业版及其它版本【亲测有效2022-03-16,两种方式】

    前言 小编最近使用公司的电脑,开始出现让你激活的提示,而且每次开机都会弹出来,小编忍住了:但是过不了半个月开始给你在桌面上显示了,一直显示和水印一样.小编忍无可忍,必须找个办法给你消灭掉!!,于是小编 ...

  8. MySQL复习——20211027

    MYSQL MySQL创建数据库 我们可以在登录MySQL服务后,使用create命令创建数据库,语法如下: CREATE DATABASE 数据库名; 使用root用户登录,root用户拥有最高权限 ...

  9. [转帖]深入JVM - Code Cache内存池

    深入JVM - Code Cache内存池 1. 本文内容 本文简要介绍JVM的 Code Cache(本地代码缓存池). 2. Code Cache 简要介绍 简单来说,JVM会将字节码编译为本地机 ...

  10. [转帖]MySQL多版本并发控制机制(MVCC)-源码浅析

    https://zhuanlan.zhihu.com/p/144682180 MySQL多版本并发控制机制(MVCC)-源码浅析 前言 作为一个数据库爱好者,自己动手写过简单的SQL解析器以及存储引擎 ...