针对 Windows 操作系统(PE 格式)的恶意软件采用了多种混淆和打包技术,从而使代码分析过程复杂化。另一方面,针对 Linux 的恶意软件(ELF 格式)的打包技术只有少数几种,主要是基于 UPX 的。
具有反 UPX 解包技术的恶意软件
最知名的使用反 UPX 解包技术的恶意软件是 Mirai 及其变种,它们以物联网设备为目标。图 1 显示了 UPX 打包二进制文件和 Mirai 的标头。正常的 UPX 包装使用“UPX!” 作为一个幻数,而 Mirai 为每个样本分配不同的值。
UPX 打包的二进制文件(左)和 Mirai 标头(右)
UPX 打包的二进制文件在标头中包含以下信息。通常,只有“l_magic”被改变,但“p_filesize”和“p_blocksize”在一些样本中也是补零的。
struct l_info // 加载程序头中的 12 字节尾部(偏移量 116) { uint32_t l_checksum ; uint32_t l_magic ; // 幻数 = "UPX!" uint16_t l_lsize ; uint8_t l_version ; uint8_t l_format ; } ; struct p_info // 12 字节的打包程序头跟随 stub loader { uint32_t p_progid ; uint32_t p_filesize ; uint32_t p_blocksize ; } ;
除了 Mirai 之外,还有许多其他类型的恶意软件使用这种技术,包括 BoSSaBot(2014 年左右出现),以及最近的一些硬币矿工和 SBIDIOT 恶意软件。这也适用于 Lazarus 小组使用的某些类型的恶意软件。图 2 显示了 ELF-VSingle 的一部分代码,它与组相关联。幻数被“MEMS”取代。
ELF_VSingle 标头
解包 Anti-UPX 解包二进制文件
基于 Anti-UPX 解包技术的二进制文件不能使用普通的 upx 命令解包。不过,拆包其实很简单。在大多数情况下,对此类二进制文件所做的唯一更改是其幻数“UPX!”。您可以通过将该值更改回“UPX!”来使用 upx 命令解压缩它。图 3 显示了更改幻数以便使用 upx 命令解压缩它的过程。
我们创建了一个工具,可以使用 Anti-UPX 解包技术来解包二进制文件。此工具仅用于此目的,否则可能无法正常工作。
JPCERTCC/upx-mod – GitHub https://github.com/JPCERTCC/upx-mod/releases/tag/v4.00-beta
检测反UPX解包技术
只需查看代码,就可以手动识别使用这种技术打包的二进制文件。为了避免疏忽,我们推荐如下基于 Yara 的自动检测。此规则不检测使用普通 UPX 打包的二进制文件。
rule upx_antiunpack_elf32 { meta: description = "Anti-UPX Unpacking technique to magic renamed for ELF32" author = "JPCERT/CC Incident Response Group" condition: uint32(0) == 0x464C457F and uint8(4) == 1 and ( ( for any magic in (uint32(filesize - 0x24)) : (magic == uint32(uint16(0x2C) * uint16(0x2A) + uint16(0x28) + 4)) and not for any magic in (0x21585055, 0) : (magic == uint32(uint16(0x2C) * uint16(0x2A) + uint16(0x28) + 4)) ) or ( for any magic in (uint32(filesize - 0x24)) : (magic == uint32(uint16be(0x2C) * uint16be(0x2A) + uint16be(0x28) + 4)) and not for any magic in (0x21585055, 0) : (magic == uint32(uint16be(0x2C) * uint16be(0x2A) + uint16be(0x28) + 4)) ) ) } rule upx_antiunpack_elf64 { meta: description = "Anti-UPX Unpacking technique to magic renamed for ELF64" author = "JPCERT/CC Incident Response Group" condition: uint32(0) == 0x464C457F and uint8(4) == 2 and ( ( for any magic in (uint32(filesize - 0x24)) : (magic == uint32(uint16(0x36) * uint16(0x38) + uint16(0x34) + 4)) and not for any magic in (0x21585055, 0) : (magic == uint32(uint16(0x36) * uint16(0x38) + uint16(0x34) + 4)) ) or ( for any magic in (uint32(filesize - 0x24)) : (magic == uint32(uint16be(0x36) * uint16be(0x38) + uint16be(0x34) + 4)) and not for any magic in (0x21585055, 0) : (magic == uint32(uint16be(0x36) * uint16be(0x38) + uint16be(0x34) + 4)) ) ) }