STATEMENT
声明
由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,雷神众测及文章作者不为此承担任何责任。
雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。
No.1 前言
通常情况下,为了保持隐蔽性,攻击者总是想方设法发明各种新技术。对此,我们非常感兴趣的一个方面是:他们是如何对二进制代码进行加壳的——利用加壳技术,攻击者可以有效逃避杀毒软件的追捕,从而秘密部署恶意二进制文件。具体来说,加壳技术的作用为:
· 在将恶意二进制文件部署到目标设备时,帮助攻击者绕过终端反病毒软件的检测;
· 大大减少二进制文件在磁盘上和传输过程中的大小:当攻击者需要低可见度时,这一点非常有用。例如,有时候,漏洞利用工具包只能传递微型的二进制文件,否则就可能遭到破坏,从而导致攻击者无法正常传播恶意二进制文件;
· 还可以隐藏二进制文件中的纯文本字符串,从而提高防御方分析相应二进制文件的难度。
No.2 关于加壳技术
虽然加壳技术通常分为4种类型,但通常这些类型之间的界限并不是非常清晰。这些类型为:
· 压缩器:显著减少二进制文件的大小;
· 加密器:使用加密算法来混淆二进制文件的内容;
· 保护器:广泛用于版权保护,例如:基于虚拟机(VM)的数字版权和复制保护;
· 安装器:为二进制文件封装到安装器中,便于安装。
识别经过加壳处理的二进制文件的方法:
· 检查二进制文件的可视化表示:通过可视化二进制的某些字节模式来探索相似性;这种方法的另一个应用,就是找出二进制文件中的重要结构,或者分析给定的文件格式,以便更好地理解它;
· 非标准的段名称;
· 同时具有可写和执行权限的段可能是加壳软件的标志;
· 入口点的地址位于其他段,而非第一段中;
· 某些函数调用的存在;
· 熵值增加:计算出现在每个块或段中各个字节值的频率,然后应用某个熵值公式计算特定段的熵值:较高的熵值通常表明存在加密或加壳处理;
· 极少的导入函数和极少的可识别字符串;
· 使用文件识别工具(如file、trid等)。
No.3 脱壳机制
对二进制代码进行加壳时,通常会将一个简单的例程存根代码嵌入被加壳的代码中,并充当其入口点。当它开始运行时,将分配一个新的内存区域,并在其中恢复出原始代码。然后,程序代码跳转到原始入口点(OEP),并继续执行原始的、未加壳的程序代码。
No.4 UPX
实际上,当前最著名的加壳器就是UPX了。其实,它就是一个高级文件压缩器的开源版本:不仅能处理多种可执行文件类型,而且还支持Linux和Windows系统。多年来,UPX既被认为是合法工具,也被认为是灰色地带工具,因为常规出现和恶意程序都喜欢使用它。
多年来,UPX被以多种不同的方式被滥用:
· 单纯使用Vanilla UPX进行加壳:恶意软件开发者直接使用原始的UPX压缩器处理他们的恶意软件。这种方法的特点是易于脱壳,无论是自动还是手动方式。
· 先使用Vanilla UPX进行加壳,然后对加壳后的二进制文件以十六进制的形式进行修改:从攻击者的角度来看,其目的是通过修改一些十六进制字节来防止自动脱壳。
下面是一些最常见的修改:
· 重写UPX! magic头部
· 修改ELF magic字节
· 修改版权字符串
· 修改段头名称
· 在整个二进制文件中添加额外的垃圾字节
· 定制UPX:由于UPX是开源的,任何人都可以在Github上下载其源代码,并修改某些方法或重写完整的功能。一旦定制的UPX程序代码被编译,然后应用于恶意二进制文件,防御方通常很难全面了解其运行机制,也很难立即搞清楚其定制功能或修改过的例程。要想了解自定义加壳器的运行机制,唯一的手段就是手动逆向分析。
No.5 滥用UPX头部结构
由于要想找到一个符合我们要求的经过加壳处理的恶意二进制代码示例并非易事,因此,我们自己创建了一个加壳的程序,以便在不同的滥用场景中引发不同的错误消息,以尝试模拟所有异常情况:
p_info已损坏
l_info已损坏
为了理解上面每一种损坏到底意味着什么,我们需要进一步深入挖掘,并尝试理解加壳后的UPX头部是如何组织起来的。为此,我们可以在开源项目的源代码中寻找有价值的信息,例如linux.h:
struct b_info // 12-byte header before each compressed block
{
uint32_t sz_unc; // uncompressed_size
uint32_t sz_cpr; // compressed_size
unsigned char b_method; // compression algorithm
unsigned char b_ftid; // filter id
unsigned char b_cto8; // filter parameter
unsigned char b_unused; // unused
};
struct l_info // 12-byte trailer in header for loader (offset 116)
{
uint32_t l_checksum; // checksum
uint32_t l_magic; // UPX! magic [55 50 58 21]
uint16_t l_lsize; // loader size
uint8_t l_version; // version info
uint8_t l_format; // UPX format
};
struct p_info // 12-byte packed program header follows stub loader
{
uint32_t p_progid; // program header id [00 00 00 00]
uint32_t p_filesize; // filesize [same as blocksize]
uint32_t p_blocksize; // blocksize [same as filesize]
};
由于这些结构体中含有加壳器正常工作所需的重要信息,所以,当脱壳代码启动时,目标程序会按照预定的方式进行解压。如果这些结构体对应的十六进制值被篡改,我们将会收到之前看到的错误信息。
No.6 滥用UPX的例子
上面,我们已经了解了相应的字段和结构体,接下来,让我们来看一个以某种方式滥用UPX的例子。首先,请看下面的哈希值:
bc88a57e1203f5eec08d34b59d9de43fa121f9d92cc773c17ebfbe848a2f88cd
经过加壳处理的恶意软件的UPX头部
经过加壳处理的恶意软件的UPX尾部
我们需要关注带下划线的十六进制值。对于有经验的读者来说,通常会立即注意到0x98,UPX!Magic头部已被改为YTS的十六进制字节,它是l_info结构体的一部分。如果我们进一步尝试将这些字节与之前显示的代码结构进行匹配,就会发现“20 08”其实就是加载程序的大小。其中,0D是版本信息,0C应该是UPX格式。紧跟在l_info结构体之后的是0xA0处的p_info结构体。通过阅读源代码我们知道:p_progid的值应该是“00 00 00 00”。在此之后,是p_filesize和p_blocksize,它们都存储相同的大小值,但在我们的例子中,它已被更改和删除。幸运的是,文件大小和块大小的值也存储在0x5C80中,即“58 B2 00 00”。我们只需要将这个值放入p_filesize和p_blocksize中即可。需要注意的是,这3个偏移处的值应始终相同。我们还在尾部看到字符串YTS出现了两次。最后,我们还需要将这些改回UPX! (55 50 58 21)。同时,尾部还包含“0D 0C”,这是来自l_info的版本和格式信息。
到目前为止,我们已经考察了l_info和p_info,但仍未触及b_info。实际上,在一个通过UPX加壳的二进制文件中,会有两个b_info结构体,一个是压缩的目标程序,一个是加载器本身的压缩部分。
如果我们考察i386-linux.elf-entry.S(ELF x86),就会发现被压缩的程序的第一个结构体b_info的偏移量是通过.long O_BINFO给出的。装载器的压缩部分的另一个b_info,就位于main:附近的call unfold指令后面,可以从_start到达该结构体:
被压缩的程序的第一个结构体b_info的偏移量
我们甚至可以调试整个过程,通过取消int3指令的注释,重新编译UPX二进制文件,来找到准确的偏移量。一旦我们调试了一个样本文件并找到了偏移量,我们就可以把它们记下来并查看相应的十六进制值:
取消对int3指令的注释,手动调试
第一个b_info结构体位于0x118处
第二个b_info结构体位于0x559A处
同一文件中的所有b_info的b_method和b_ftid必须相同。实际上,有一个快速的方法可以从二进制文件中获得这些信息,那就是通过upx ––fileinfo命令,来处理一个经过加壳处理的二进制样本。
UPX fileinfo的参数
到目前为止,我们还没有在野外发现b_info的值被篡改的情形,但这可能是将来使用UPX对二进制文件进行加壳的一个发展方向。目前,最普遍的UPX滥用方法是修改l_info和p_info。
下面,让我们详细总结一下我们对结构体相关值的重要发现:
一旦我们把这些值放进去,就能成功对使用UPX加壳的二进制文件进行脱壳了。
No.7 Mozi
下面,让我们来考察另一个样本。Mozi是2020年盛行的物联网恶意软件家族之一。同时,它也是改变p_info的一个完美示例,因为已经观察到UPX加壳的Mozi二进制文件的p_filesize和p_blocksize字段值为0。这将使自动脱壳失效,所以,为了得到脱壳后的二进制文件,我们必须找出这些字段的正确值。利用我们之前学到的知识,我们很快就能在尾部找到相应的文件大小值,然后,只需将其添加到p_info中即可,该值为“E1 A6 1E 00”。
破坏UPX头部并删除p_filesize
和p_blocksize字段的一个例子
修复受损的UPX头部以及p_filesize
和p_blocksize的值之后的效果
No.8 利用radare2手动脱壳
即使修复了前面提到的所有差异和修改的字段之后,如果仍然无法通过upx -d进行自动脱壳时,我们可以尝试通过手动方式,直接从内存中提取脱壳后的可执行映像,具体命令如下所示:
No.9 参考资料
https://github.com/upx/upx
https://github.com/radareorg/radare2
https://github.com/upx/upx/issues/389
https://github.com/upx/upx/blob/master/src/stub/src/i386-linux.elf-entry.S
https://github.com/upx/upx/blob/master/src/stub/src/amd64-linux.elf-entry.S
No.10 附录
附录A
附录B
附录C
本文所分析的恶意软件的哈希值:
bc88a57e1203f5eec08d34b59d9de43fa121f9d92cc773c17ebfbe848a2f88cd
在此,我们特别感谢@unixfreaxjp之前对ELF加壳技术所进行的深入研究。
原文地址:
https://cujo.com/upx-anti-unpacking-techniques-in-iot-malware/
RECRUITMENT
招聘启事
END
长按识别二维码关注我们
本文始发于微信公众号(雷神众测):深入研究IoT恶意软件中的UPX反脱壳技术
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论