计算机系统引导过程
graph LR
a(主板BIOS<br>系统进行<br>自检) ==> b("硬盘主<br>引导程序<br>(MBR)")
b ==> c("活动分区<br>引导程序<br>(DBR)")
c ==> d("操作系统<br>引导<br>(如NTLDR)")
d ==> e(操作系统<br>内核启动)
e ==> f(驱动程序<br>及服务)
f ==> g(系统<br>自启动程序)
PE文件:可移植的可执行程序
PE文件格式与恶意软件的关系
-
文件感染:
使目标PE文件具备[或启动]病毒功能[或目标程序]
但不破坏目标PE文件原有功能和外在形态(如团标)等
-
病毒代码如何与目标PE文件融为一体
代码植入、控制权获取、及图标更改等
PE文件总体结构
1.DOS MZ header |
2.DOS Stub |
3.PE header |
4.Section table |
5-1 Section 1 |
5-2 Section 2 |
Section … |
5-n Section n |
MZ-DOS:MZ文件头(0x40+),定位PE文件头开始位置,也可用于PE文件合法检测。
3C处的值(末4位):指向PE文件头开始位置。
DOS Stub:小型DOS程序,在DOS下运行会提示“This is program cannot be run in DOS mode”。
PE header:由三部分组成
-
字串 “PE\0\0” (Signature) (0x04):本域为PE标记,可以此识别给定文件是否为有效PE文件
-
映像文件头(FileHeader)(0x14):包含了关于PE文件物理分布的信息,比如节数目、后续可选文件头大小、机器类型等。
顺序 名字 大小(字节) 描述 1 Machine * 2 机器类型,x86为14CH 2 NumberOfSection ** 2 文件中节的个数 3 TimeDataStamp 4 生成该文件的时间 4 PointerToSymbleTable 4 COFF符号表的偏移 5 NumberOfSymbols 4 符号数目 6 SizeOFOptionalHeader* 2 可选头的大小 7 Characteristics * 2 关于文件信息的标记,比如文件是exe还是dll -
可选映像头(OptionalHeader): 定义了PE文件许多关键信息
- 内存镜像加载地址(ImagaBase)
- 程序入口点(代码从哪里开始执行)
- 节在文件和内存中的对齐粒度
- 本程序在内存中的镜像大小、文件头大小等
- 目录Directory (16项 * 8字节) : 每一项对应后面重要的数据结构的开始位置、大小
名字 描述 AddressOfEntryPoint*
(位置: D8H,4字节)PE装载器准备运行的PE文件的第一条指令的RVA。
(病毒感染中通用关键字段)ImagaBase
(位置: E4H,4字节)PE文件的优先转载地址。如:如果该值是400000h,
PE装载器将尝试把文件虚拟地址空间的400000h处SectionAlignment
(位置: E8H,4字节)内存中节对齐的粒度 FileAlignment
(位置: ECH,4字节)文件中节对齐的粒度
节表:紧挨着 PE header 的一个结构数组。
每一个节表对应一个节表项:
- 节名;
- 节在文件和内存中的开始地址;
- 长度;
- 节属性等。
节:可执行文件的核心部分。
- 代码节
- 数据节
- 引入函数节
- 资源节(如图标)
- 引出函数节(DLL文件中常见)
- 重定位节(DLL文件中常见)
概念解析
ImagaBase: PE文件在内存中的优先装载地址(大部分为400000H)
RVA地址: Relative Virtual Address,相对虚拟地址,它是相对内存中ImagaBase的偏移
对齐粒度: 按照内存对齐粒度读取到内存,不足的用0填充。
例如:桶的容量为100L,现有367L水,则需要4个桶
文件中节对齐粒度:0x200; 内存中节对齐粒度:0x1000字节。
因此在PE文件中有很多“00”字节,恶意程序可以利用这些填充字节。
代码节
- 紧接着节表之后,一般名为 .text或CODE,该节含有程序的可执行代码
- 每个PE都有代码节
已初始化的数据节
- 一般名为 .data或DATA
- 已初始化的数据节中放的是在编译时刻就已经确定的数据
未初始化的数据节
- 一般名为 .bbs
- 节内放油未初始化的全局变量和静态变量
引入函数节
-
节名一般为 .rdata
-
是被程序调用但起执行代码又不在程序中的函数
- 这些函数位于一个或者多个DLL中,在调用这程序中只保留了函数信息,包括函数民及其驻留的DLL名等
- 动态链接库: 例如kernel32.dll、user32.dll、gdi32.dll等
-
该节含有四个表
名字 解释 IMPORT Address Table 引入地址表 IMPORT Directory Table 引入目录表 IMPORT Name Table 引入名称表 IMPORT Hints/Names & DLL Names 引入提示/名称和DLL名称 -
IMPORT Address Table(IAT):引入地址表,DWORD数组[可通过可选文件头中的DataDirectory的第13项定位]
- 在文件中时,其内容与Import Name Table完全一样
- 在内存中时,每个双字中存放着对应引入函数的地址
-
IMPORT Directory Table由一系列的IMAGE_IMPORT_DESCEIPTOR结构(5字段*4字节)组成
- 结构的数量取决于程序要使用DLL文件的数量,每一个结构对应一个DLL文件
- 在所以这些结构的最后,由一个内容全为0的IMAGE_IMPORT_DESCRIPTOR结果作为结束
-
IMPORT Name Table,DATA最高位:
- 为0时:表示通过函数名引入,指向 IMPORT Hints/Names
- 为1时:表示通过序号引入函数
-
IMPORT Hints/Names & DLL Names 分解说明
- IMAGE_IMPORT_BY_NAME结构
- 80 00 为Hints,ExitProcess为引入函数名
- 62 02 为Hints,wsprintfA为引入函数名
- 9D 01 为Hints,MessageBoxA为引入函数名
- DLL names字符串
- kernel32.dll 为dll文件名
- user32.dll 为dll文件名
- IMAGE_IMPORT_BY_NAME结构
-
引出函数节
-
节名一般为 .edata,有时和 .text节合在一起。这是本文件向其他程序提供调用函数的列表、函数所在的地址及具体代码实现
-
关键结构:引出目录表(导出表、输出表)
结构解析:
序号 位置 名称 大小 解释 1 00H Characteristics 4 一般为0 2 04H TimeDataStamp 4 文件生成时间 3 08H MajorVersion 2 主版本号 4 0AH MinorVersion 2 次版本号 5 0CH Name 4 指向DLL的名字 6 10H Base 4 开始的序列号 7 14H NumberOfFunctions 4 AddressOfFunctions 数组的项数 8 18H NumberOfNames 4 AddressOfNames 数组的项数 9 1CH AddressOfFunctions 4 指向“函数地址”数组—导出地址表 10 20H AddressOfNames 4 指向“函数名所在地址”数组—函数名地址表 11 24H AddressOfNameOrdinals 4 指向“函数索引序列号”数组—函数序列表 -
导出地址表 — EXPORT ADDRESS Table,Dword的两种可能含义
- dwExportRVA,指向导出地址(一般情况)
- dwForwarderRVA,指向另外一个DLL中的某个API函数名
-
导出名字表 — EXPORT Name Table,保存指向名字的指针
-
导出序号表 — EXPORT Ordinal Table,保存的是各导出函数的函数地址在导出地址表的序号
- 为何需要导出地址表?
导出函数名字和导出地址表中的地址不是一一对应关系 (Why?)
1.一个函数实现可能有多个名字
2.某些函数没有名字,仅通过序号导出
- 为何需要导出地址表?
程序调试
用户态调试:Ollydbg
内核代码调试:Windbg