静态逆向工程Shellcode技术(上)

2020-09-28 13:51:2014915人阅读

本文介绍的是分析恶意软件的逆向技术,而不是介绍恶意软件的“工作原理”类型的帖子,因此在本文中,我决定专注于研究与恶意软件紧密相关的概念,例如shellcode、加密算法等。而不是恶意软件本身,例如Dridex。本文我使用的是Metasploit,使用它来生成了一些shellcode。

Metasploit是一款开源的安全漏洞检测工具,可以帮助安全和IT专业人士识别安全性问题,验证漏洞的缓解措施,并管理专家驱动的安全性进行评估,提供真正的安全风险情报。这些功能包括智能开发,代码审计,Web应用程序扫描,社会工程。团队合作,在Metasploit和综合报告提出了他们的发现。

本文研究的恶意样本的MD5哈希:9d7e34250477acf632c6c32fc2c50d3b。

Shllcode分析第1阶段:静态分析

首先,你要在你选择的反汇编程序中打开shellcode ,我个人更喜欢IDA Pro,但是我不会使用伪代码功能,而是将重点放在反汇编视图上,因此这应该适用于你选择的任何工具。通过IDA Pro,你可以选择如何加载二进制数据(16位、32位或64位),因此,我将首先选择32位,如果反汇编有任何错误,我们可以对其进行简单地更改。加载二进制文件后,选择第一个字节并按“ C”,这将尝试将第一个字节转换为代码。因此,重新分析该文件,然后尝试将其余字节反汇编为部件。如下所示,入口点从位置0x00开始,shellcode一直向上到达位置0x2A,此时IDA无法将剩余的字节转换为程序集。

1582702424177955.png

仅基于这个事实,我们就可以假定这部分数据是以某种方式加密的,并且上面的shellcode解密并执行它。因此,让我们来看看第一个要运行的函数sub_0()。此函数仅负责跳转到偏移量0x25,它将执行sub_2(),我将其命名为main_func(),因为它包含了大部分代码,因此,让我们继续研究该函数。

1582702507111885.png

因此,这个函数内部的解密相当简单,只是一个基本的XOR循环,它将[edi]指向的XOR数据与[esi]指向的数据结合起来。在每一次XOR操作之后,edi(存储器地址)都会增加,并且程序会将[esi:esi + 1]与0x550D进行比较。在本例中,IDA以Little-Endian显示该值,,因此它被显示为0x0D55。如果值匹配,它将跳到ecx指向的地址,否则它将增加esi,将它现在指向的数据与0x73进行比较,如果匹配,它将恢复原始地址(存储在ebx中)。如果不匹配,它将循环回到XOR代码并继续。

1582702519133256.png

基于以上分析,我们知道esi用作密钥,而edi被用作加密的shellcode。我们还知道密钥长度可以是任何长度,并且ebx将包含指向密钥首字节的地址。

利用所有这些信息,让我们首先尝试找出加密数据的开始和停止位置。从XOR循环(loc_F)开始并向上移动。我们可以首先看到存储在edi中的地址被移动到ecx中,这是有意义的,因为一旦解密完成,程序就会跳转到ecx中的地址。

现在,我们找到edi从ebx处获取加密代码地址,在函数的最顶部,弹出堆栈顶部的值,并将其存储在ebx中,然后将该值移到edi中,然后将0x73移动到eax的较低位,调用cld,然后输入a scasb循环。问题是,当它弹出ebx时,堆栈的顶部是什么?

好了,简单地看一下调试器中的堆栈,可以发现,当调用一个函数时,堆栈顶部的值是要返回的地址,这是调用指令之后的指令地址。在下图中,返回地址为0x013B1CAB,调用该函数的地址为0x013B1CA6(此处的调用指令占用5个字节)。那么,这如何适用于我们的shellcode?

1582702533126268.png

上面我们说过,main_func是从位置0x25调用的。无法识别的数据从0x2A开始。位置0x25之后的5个字节可为调用指令腾出空间。因此,main_func()中位于堆栈顶部的数据为值0x2A。现在,我们终于知道了加密数据的起始位置!

1582702544173917.png

不过不要高兴得太早,还记得0x73移入al后如何调用cld吗? cld指令负责删除(设置为0)EFLAGS寄存器中的DF(direction)标志。当此标志为0时,任何字符串操作都会根据操作中使用的寄存器来递增索引寄存器,尤其是esi或edi。调用cld后,将进入一个循环,该循环不断执行scasb,直到ZF(0)标志被设置(设置为1)。 scasb会做什么?它所做的只是将al中的字节与[edi]进行比较,并根据结果设置状态标志。如果不匹配,则ZF将保持删除状态,否则将被设置,并且程序会跳出循环。此外,由于scasb是字符串操作,因此它将在每个循环后增加edi中的地址,这意味着直到[edi]等于0x73为止,程序将永远不会执行解密功能。现在我们知道,加密的数据从0x73开始,密钥是从地址0x2A一直到[address] = 0x73的数据。如上图所示,0x73是ebx指向的数据中的第二个字节。因此,XOR密钥为0x06,加密的数据从0x2B开始!向下滚动到函数的最后,我们可以找到字节0x550D,这意味着加密的数据长401个字节!

了解了所有内容之后,让我们进入解密阶段!

Shllcode分析第2阶段:解密

1582702555811602.png

因此,我们现在有了XOR密钥:0x06,以及加密数据开始的位置:0x2B。有了这些知识,我们就可以轻松地用Python编写一个基本的XOR解密函数,该函数带有2个参数:密钥和加密的数据。向函数添加一个简单的检查,以查看数据是否与0x550D匹配并且脚本是否已完成!

1582702567103291.png

但是,为了实际使用它,我们需要手动或使用YARA或Regex从IDA提取数据来定位它。然后我们必须在新的IDA会话中将其打开,并且在某些情况下第二个“阶段”从第一阶段开始调用函数,可能是对另一部分数据进行XOR。幸运的是,我们可以将此基本脚本转换为IDAPython格式,并用解密的Shellcode覆盖加密的Shellcode!

不用担心,迁移到IDAPython的过程非常简单。首先,我们需要导入所需的库idaapi,然后将for循环更改为while循环,然后才能对传入的数据调用len(),但是在这种情况下,我们不知道数据的长度如何,因为我们现在正在传递加密数据的地址。

除此之外,只要在address + i中仍有数据,就可以使while循环连续循环。这可以通过使用Byte()函数来实现,该函数从给定地址获取一个字节的数据。在本文的示例中,该地址就是我们的加密blob。现在,我们就可以将该数据字节分配给一个变量字节,然后将其与0x55进行比较。我们还将address+(i+1)与0x0D进行比较,以确保这两个标记都存在。

如果这些标记不存在,则使用给定的输入执行XOR字节操作,然后调用PatchByte(),它将用我们的变量byte在address+i处覆盖字节。最后,将i增加1,循环继续!

1582702581383555.png

将它导入IDA,并在命令行中输入ida_xor_crypt(0x06, 0x2B),将执行我们的函数,覆盖数据,如下图所示!它不再以0x73开头,因为现在是0x75。

1582702590451111.png

将它导入IDA,并在命令行中输入ida_xor_crypt(0x06, 0x2B),将执行我们的函数,覆盖数据,如下图所示我们可以在选择0x75的情况下再次按“ C”,然后将其完全分解。

1582702605925568.png


本文转载自:嘶吼

作者:lucywang

原文地址:https://www.4hou.com/posts/BR5Q

本文翻译自:https://0ffset.net/reverse-engineering/malware-analysis/common-shellcode-techniques/

0
现金券
0
兑换券
立即领取
领取成功