汪道之

有人的地方就有江湖

0%

病毒_Win_PE结构和末节寄生

PE格式

在Win32位平台可执行文件命名为可移植的可执行文件(Portable Executable File),该文件的格式就是PE格式

在Win32系统中,常见的EXE,DLL,SYS,COM等可执行文件都是PE文件

图示

image-20210517151959406

映像

  1. PE文件的加载要完成虚拟地址(内存)和PE文件(硬盘)之间的映射关系,所以又被称为映像文件
  2. 当真正执行某个内存页的指令或访问一个页的数据时,这个页面才会真正读入内存
  3. 所以文件装入速度与文件大小关系不大
  4. 注意区分文件位置与虚拟地址与相对虚拟地址

相对虚拟地址(RVA)

  1. 虚拟地址,即我们前面提到的逻辑地址,指的是内存中的地址(注意和硬盘上文件中的位置相区分)

  2. 相对地址,即相对PE文件加载到内存后占用的最开始的那个内存单元的逻辑地址(基地址)

  3. 区分RVA和FOA

    RVA:内存中的相对位置,相对的是加载到内存的基地址

    FOA:文件中的相对位置,相对的是文件的开始位置(即0)

    在文件中,一个节往往按512B(200H)的粒度对齐

    在内存中,一个节通常按4096(1000H)的粒度对齐

    所以内存的RVA和文件的FOA通常是不一致的

    image-20210517153940694

    (由于装载时前面的一般不动,DOS部分、PE文件头部分、节表部分、和第一个节的RVA和FOA通常一致)

  4. 图示

    image-20210517153120815

    在图示中,基地址是0x00400000,.text节的RVA是0x1560,VA是0x00401560

DOS头部分

图示,仅供参考无需记忆:

image-20210517154415235

头部的e_magic,就是两个字符MZ,代表DOS文件
最后一个字段e_lfanew是偏移量,就是文件开始到PE文件头(NT头)的偏移量

PE头

包含3个部分

  1. PE文件标志(Signature)
  2. 映像文件头(IMAGE_FILE_HEADER)
  3. 可选映像文件头(IMAGE_OPTIONAL_HEADER32)

PE文件标志

两个字节为PE表明是PE格式文件

故而判断文件是否为PE格式可以通过:

  1. 先判断文件头2个字节是否为MZ
  2. 判断NT头(PE头)的Signature是否为PE

映像文件头

image-20210517155329619

对病毒来说,可能需要用到NumberOfSections

可选映像头

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Option Header 
TImage_Optional_Header32 = record
//
// Standard fields.
//
Magic: Word;
MajorLinkerVersion: Byte;
MinorLinkerVersion: Byte;
SizeOfCode: Cardinal;
SizeOfInitializedData: Cardinal;
SizeOfUninitializedData: Cardinal;
AddressOfEntryPoint: Cardinal; //代码入口RVA,第一条指令的RAV
BaseOfCode: Cardinal;
BaseOfData: Cardinal;

//
// NT additional fields.
//
ImageBase: Cardinal; //载入程序的首选RAV
SectionAlignment: Cardinal; //节在内存中对齐方式
FileAlignment: Cardinal; //节在文件中对齐方式
MajorOperatingSystemVersion: Word;
MinorOperatingSystemVersion: Word;
MajorImageVersion: Word;
MinorImageVersion: Word;
MajorSubsystemVersion: Word;
MinorSubsystemVersion: Word;
Win32VersionValue: Cardinal;
SizeOfImage: Cardinal; //内存中整个PE文件的总大小,按内存对齐
SizeOfHeaders: Cardinal;
CheckSum: Cardinal;
Subsystem: Word;
DllCharacteristics: Word;
SizeOfStackReserve: Cardinal;
SizeOfStackCommit: Cardinal;
SizeOfHeapReserve: Cardinal;
SizeOfHeapCommit: Cardinal;
LoaderFlags: Cardinal;
NumberOfRvaAndSizes: Cardinal; //数据目录的项数
DataDirectory: array[0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES - 1] of
TImage_Data_Directory; //数据目录表
end;

上述注释为可能用到的字段,“AddressOfEntryPoint”很关键

利用入口RVA实现病毒执行

修改入口地址对应指令

  1. 用PEView定位到AddressOfEntryPoint

  2. 用UE定位到AddressOfEntryPoint的值位置(注意值是RVA)

  3. 通过RVA找到文件的FOA

    入口点的RVA(AddressOfEntryPoint)- 节的RVA =

    入口点的FOA - 节的起始文件位置(PointerToRawData)

  4. 用eb 02 90 90替代原来内容,该指令汇编为

    1
    2
    3
    4
    jmp aa
    nop
    nop
    aa:
  5. 用OD启动修改后程序,OD将停在第一条指令,观察第一条指令是eb 02 90 90

  6. 成功

  7. 注:程序的ImageBase+AddressOfEntryPoint就是入口点地址

直接修改入口点地址

  1. 找到AddressOfEntryPoint字段在文件中的偏移
  2. 用UE修改就好

例题

  1. 下列哪个字段不在PE文件的可选头中( )
    A.入口点地址
    B.文件对齐大小
    C.子系统
    D.节表

参考答案:D

解析:由前面的图知道,节表是在PE文件头后面的

病毒加载到内存的问题

节表

  1. 节表紧跟在PE文件头后面,节表中每一个结构ImageSectionHeader(28H)都对应一个节,其中,SizeOfRawData描述了对应节的文件大小,VirtualSize描述了加载到内存的大小(两者可能不同,文件大小可以大于也可以小于内存大小,小于时将在内存补0)
  2. 在PE文件头的可选映像头中,SizeOfImage给出了整个程序包括所有头部加载到内存后的大小,其大小是SectionAlignment的整数倍(SectionAlignment是内存对齐的粒度、FileAlignment是文件对齐的粒度)
  3. 简单说,就是PE文件总大小和每个节的大小都有参数

图示:

image-20210519083535334

末节寄生

思想

  1. 如果该节内存大小<文件大小,我们就在文件中将指令加载到该节的多余部分(对齐后的空洞)
  2. 然后修改节表SectionHeader中的VirtualSize字段(加载到内存的字节数)为修改后的大小,而对齐后的文件大小SizeOfRowData保持不变
  3. 注意,有时exe最后一个字节后有一些调试信息,但它不会被加载到内存,这也许就是SizeOfImage的意义,它阻止尾部多余信息进入内存

具体操作

  1. 首先找到最后一个节在文件中的位置,即其节表项中PointerToRawData字段
  2. 找到节中的寄生位置,就是VirtualSize字段后面
  3. 找到在文件中的寄生位置,PointerToRawData+VirtualSize
  4. 用UE在文件偏移到该处进行修改
  5. 修改最后一个节表中的VirtualSize
  6. 计算SizeOfImage=程序大小/SectionOfImage并向上取整,查看是否需要修改
  7. 修改AddressOfEntryPoint为新的程序入口RVA

文件长度变大

  1. 在reloc节原VirutalSize后添加JMP xx xx,在DOS部分已知机器码偏移量为两字节E9 xx xx
  2. 在reloc节后添加两个NOP指令
  3. 修改reloc节头的SizeOfRawData,加一个FileAlignment(1000h),为2000h
  4. 修改reloc节头的VirtualSize为原SizeOfRawData+2(两个nop) ,现在NOP才是实际结尾
  5. 修改可选映像头的SizeOfImage = (relocRVA+新VirutalSize)除以SectionAlign取上整
  6. 在NOP后手动增加1000h-2个字节,内容不论,为对齐后填充内容,以前是编译器自动填充
  7. 修改入口点RAV(AddressOfEntryPoint)

图示:

image-20210519085921966

例题

  1. 关于PE文件病毒,下列说法不正确的是( )
    A. 需要对PE文件头的某些字段进行修改,保证感染后的PE文件合法
    B. PE文件感染必然会增加PE文件的大小
    C. 病毒可以通过修改PE文件入口点的值或者入口点处的指令来获得执行机会
    D. 病毒可以将自身且分为多段,分别插入到PE文件各节的空洞中

参考答案:B

解析:PE文件感染有的方式是不改变PE文件大小的