在没有源码的情况下,如果想要修改程序或者给程序添加功能,那么就可以通过打补丁的方式来实现。此外,恶意代码为了隐藏自身会将代码注入到目标系统的合法程序中,该行为被称为恶意代码的感染性。
打补丁和病毒感染文件都是对目标程序的 PE 文件进行操作,由于 PE 文件每个节区在磁盘中的对齐单位为 0x200 字节,所以每个节区间可能会存在空隙,如果补丁代码或病毒需注入的代码量较少时,可以把代码写入到这些空隙中。对于恶意代码而言,以这种方式感染目标文件更具隐蔽性。
当补丁代码或病毒需注入的代码量较大时,可以在 PE 文件的末尾添加一个节区用于存储这些代码。下面介绍如何在 PE 文件中添加一个节区。
0x01 手动添加
使用 C32asm 可以很方便地定位并修改 PE 文件的各个字段,点击 “查看” / “PE信息” 可打开 PE 结构字段的解析面板。添加节区的具体流程如下。
1. 添加一个 IMAGE_SECTION_HEADER
首先在原来节表的末尾添加一个节表,IMAGE_SECTION_HEADER 结构体中要设置的字段有以下6个:
2. 修改 NumberOfSection
添加一个节表之后需要修改 IMAGE_FILE_HEADER 中的 NumberOfSection 字段,将节区数量由 4 改为 5。
3. 修改 SizeOfImage
接着修改文件映像大小,即 IMAGE_OPTIONAL_HEADER 中的 SizeOfImage 字段,该字段按内存对齐方式对齐,在原大小(0x9000)的基础上加上新节区的大小(0x450),对齐后为 0xa000。
4. 添加节区数据
最后添加新增节区的数据,把光标移到文件的末尾,点击 “编辑” / “插入数据”,插入数据大小为 1536(0x600),使用 00 填充,点击确认,保存即可。
到此,已成功添加了一个节区,修改之后的程序仍是可运行的,使用 PEview 查看新增节区如下:
这里需要注意插入数据的大小要按磁盘对齐方式对齐,不然最终修改后的文件无法运行,并提示“该文件不是有效的 Win32 应用程序”。
0x02 编程实现
恶意代码为了实现其隐蔽性,在其感染 PE 文件时会将代码执行权交给被插入的代码,所以恶意代码通常会先被执行,执行完后再跳转至原 PE 文件中的代码继续执行。
添加节区主要通过内存映射文件和 PE 操作完成,将文件映射到内存中后可以通过内存指针方便地访问文件。下面主要介绍添加新节区的代码实现。
1. 将文件映射到内存
首先用 CreateFile() 打开文件,然后使用 CreateFileMapping() 和 MapViewOfFile() 函数把文件映射到内存中。
CreateFileMapping() 函数定义如下:
MapViewOfFile() 函数定义如下:
2. 检查 PE 文件
文件映射后要检查是否为有效的 PE 文件,同时为了避免重复感染,需要检查目标文件是否已被感染。
IsPeFile() 和 IsInfected() 函数的实现如下:
3. 添加节表
添加一个节区需要在 PE 文件中添加一个节表,此外还需修改 NumberOfSections 和 SizeOfImage 字段。
VS2010 中默认设置时,计算 Shellcode 长度时无法正确获取函数在内存中的地址,需要将修改项目属性:配置属性/链接器/常规/关闭增量链接。
4. 插入节区数据
病毒通常会将带有恶意行为的代码插入新节区的数据段,被插入的代码称为 Shellcode,这里只是插入一段弹消息框的代码,Shellcode 通常使用汇编实现,下面是内联汇编代码:
从以上代码可知,Shellcode 执行完后会 ret 到原入口地址(OEP)处继续执行。Shellcode 中 MessageBoxA 函数的地址和 OEP 只是占位符,需要在运行时修正这两个地址。
大多数程序都会加载 user32.dll, 并且在同一系统中,user32.dll 会被加载到自身固有的 ImageBase,而 MessageBoxA 是该动态链接库的一个导出函数,所以同一系统中运行的所有进程的 MessageBoxA 函数地址是相同的。
修正 Shellcode 中地址之后将其复制到 PE 文件的末尾,首先使用 SetFilePointer() 函数将文件指针指向文件末尾,再通过 WriteFile() 函数将 Shellcode 函数写入文件。SetFilePointer() 函数定义如下:
5. 关闭目标程序 ASLR
由于 Shellcode 中 MessageBoxA() 函数地址和原 OEP 都是硬编码的,而在 Windows Vista 系统开始都默认启用 ASLR,所以目标程序每次启动时加载到内存的地址(ImageBase)都不同,导致 Shellcode 在跳转至原入口地址时因地址错误而不能正常执行。
普通的 EXE 文件不存在 .reloc 节区,编译器默认情况下都启用 ASLR(“目属性/链接器/高级/随机基址” 可关闭 ASLR),所以编译生成的可执行文件会包含用于重定位的 .reloc 节区。PE 文件中与 ASLR 相关的字段主要有以下几个:
下面通过编程的方式关闭目标程序的 ASLR:
注:
IMAGE_OPTIONAL_HEADER/DllCharacteristics 中 IMAGE_DLLCHARACTERISTICS_NX_COMPAT 为与 DEP 相关的属性值,开启 DEP 时会设置改属性值,同样可以用以下代码关闭目标程序的 DEP:
6. 修改入口地址 OEP
为了让新添加节区中的代码获得优先执行权,要把程序的入口地址设置为新节区的起始地址,即新节表中 VirtualAddress 的值。修改完后调用 FlushViewOfFile() 函数将对文件的修改写入到磁盘中。
7. 测试
以下代码遍历当前目录下所有.exe 文件,并感染除程序自身外的所有.exe文件。
运行被感染后的文件,会弹出以下消息框,使用 PEview 可以看到添加了一个名为 .new 的节区。
完整代码可以在 此链接 下载。
References:
[1] PE File Infection
[2] 《小小黑客之路》
[3] 《黑客编辑揭秘与防范》
[4] 《逆向工程核心原理》