- 看日常分享: AwesomeCS FB
- 看技术文章: AwesomeCS Wiki
笔者最近在阅读 aaaddress1 的大作: Windows APT Warfare:恶意程序前线战术指南,因为书中围绕着 PE File 进行,脑容量太小的我又一直忘记 PE 的档案结构,最後决定还是认真的把它研究一遍并写成笔记。
PE (Portable Executable) 是一种用於可执行文件、目标文件和动态连结库的文件格式,主要使用在 32 位和 64 位的 Windows 作业系统上。
有点像是 Linux 作业系统中的 elf 档。
在一串连续的记忆体中, DOS Header 一定会是记忆体中的首段内容, DOS Header 中的几项资讯会是比较重要的:
e_magic
e_magic 帮助我们辨认该 PE 档案是否合法,一般来说,它应该永远等於 MZ 字串。
如果以 C/C++ 检查 PE File ,可以这样做:
#include <windows.h>
// ...
void parser(char* filePtr){
IMAGE_DOS_HEADER* dosHdr = (IMAGE_DOS_HEADER *)filePtr;
if(dosHdr->e_magic != IMAGE_DOS_SIGNATURE){
return;
}
// ...
}
// ...
e_lfanew
观察 e_lfanew 之前,必须先了解什麽是 RVA (Relative Virtual Address), RVA 是程序入口点的参考位址,举例来说:
如果程序被放入虚拟地址(Virtual address, VA)的 0x01000000
处,且 RVA 位於 0x102D6C
处,那麽程序在记忆体中的实际入口就会是 VA + RVA:
0x01000000
+ 0x00102D6C
= 0x01102D6C
e_lfanew 其实就是指向了 NT Headers 的 RVA ,换个角度思考,我们将前面的 dosHdr 加上偏移量 (在这边指 RVA),就可以获得 NT Headers 的起始位址:
IMAGE_NT_HEADERS* ntHdrs = (IMAGE_NT_HEADERS *)((size_t)dosHdr + dosHdr->e_lfanew);
透过读取 DOS Header 获得 NT Headers 的起始位址以後,我们就可以对 PE 档案做更进一步的检验。
NT Headers 共包含了两大结构,分别是 File Header 以及 Optional Header 。
参考上图,在 File Header 结构中有多个属性,每个属性代表的资讯如下:
Machine
纪录 PE 档案所存放的机械码属於哪一种指令集架构:
NumberOfSections
一个 PE File 通常会有好几段块状区域, NumberofSections 纪录了 PE 档案的区段数量。
这个参数对我们撰写程序解析 PE File 非常有帮助,至於那些块状区段存了什麽,晚点会提到。
TimeDateStamp
纪录程序编译时间的时间戳。
PointerToSymbolTable
符号表地址,用於除错,一般为 0 。
NumberOfSymbols
如果符号表存在,这边会记录符号数量。
Characteristics
纪录了整个 PE 的属性,包含:
Optional Header 的中文称可选段,实际上,如果要让 PE 能够顺利地被执行程序装载器使用, Optional Header 为必备的。
补充:
Optional Header 不存在於 Object File (COFF),而是在编译的连结阶段才会由连结器补上。
参考上图, Optional Header 包含了很多参数,下面针对重要的参数作介绍:
Address of entry point
程序码编译後,程序的入口点,也就代表当 Program 被作业系统载入时, Process 会从这边开始执行。
一般来说,入口点会指向 .text section 的函式开头。
ImageBase
记录了 PE 档案 mapping 到记忆体上的预设位址,通常为 0x400000
或是 0x800000
。
SizeOfImage
记录了当程序处於动态执行阶段需要多少空间才能存放整个 Image 。
Section alignment
动态的区域对齐, 32-bit 的环境下预设大小为 0x1000 bytes 。
File alignment
静态的区域对齐, 32-bit 的环境下预设大小为 0x200 bytes 。
假设有不足 0x200 bytes 的资料要放进块状区段,块状区段的大小为 0x200 bytes ,如果资料多於预设大小,块状区段的大小则为 0x400 bytes 。
Size of headers
DOS Header + NT Headers + Section Headers 的大小。
Data directory
Section Headers 的位址紧随在 NT Headers 的後方,使用 C/C++ 可以轻松的获得其位址:
IMAGE_SECTION_HEADER* sectHdr = (IMAGE_SECTION_HEADER *)((size_t)ntHdrs + sizeof(*ntHdrs));
至於 Section Headers 的本体到底是什麽呢?它其实就是一个存放块状区段资讯的阵列:
for (size_t i = 0; i < ntHdrs->FileHeader.NumberOfSections; i++){
printf("\t#%.2x - %8s - %.8x - %.8x \n", i, sectHdr[i].Name, sectHdr[i].PointerToRawData, sectHdr[i].SizeOfRawData);
}
每一块存放块状区段资讯的空间都会有以下属性:
PointerToRawData
该区段处存在静态档案的偏移量。
SizeofRawData
该区段的实际大小。
VirtualAddress
相较於映像基址的相对偏移量。
VirtualSize
显示该区段需要被分配多少动态空间。
Characteristics
纪录该区段是否可读、可写、可执行。
.text
用於存放程序码。
.data
用来宣告已初始化的资料与常数。
.bss
存放已宣告但尚未初始化的变数。
.rdata
存放唯读资料。
.idata
存放引入的函式与资料,这些资料会在 Process 建立时,由执行程序装载器负责填充。
.edata
存放用来导出给其他程序使用的函式与资料。
.rsrc
用於记录程序使用了哪些资源。
reloc
重定位,当 PE 程序载入失败,会以此段作为参考进行调整。
了解 PE File 的基本结构後,我们就可以将恶意 shellcode 添加至目标档案再将 Entry point 指向恶意程序区段的 Virtual Address 做些坏坏的事(?)
本文章介绍的内容大概只有 Windows APT Warfare:恶意程序前线战术指南整本书的皮毛,如果想学更多就去下单买一本 Windows APT Warfare:恶意程序前线战术指南吧!
接续昨天 根据规格书,我们要用永丰提供的四组hash值拼出hash id 可以看出,hash id会...
大家好,我是毛毛。ヾ(´∀ ˋ)ノ 废话不多说开始今天的解题Day~ 520. Detect Cap...
优惠码BFCM2021 优惠时间:只到2021/12/01 折扣内容:首四个月6折(适用於所有方案)...
前言 走过了资料分析、演算法选择後, 我们得知了有些可以改善模型的方向: 解决资料不平衡(Now) ...
这次相比长条图使用多一点的资料,阵列如下并且一样先宣告svg变数绘制一个宽800高450的画布 co...