Metasploit是一款开源安全漏洞检测工具,可帮助安全和IT专业人员识别安全问题、验证漏洞缓解措施以及管理专家驱动的安全评估,提供真实的安全风险情报。这些功能包括智能开发、代码审计、Web应用程序扫描、社会工程学。该团队通力合作,在Metasploit中展示了他们的发现和一份综合报告。本文研究的恶意样本的MD5哈希值:9d7e34250477acf632c6c32fc2c50d3b。Shllcode分析阶段1:静态分析首先,在你选择的反汇编器中打开shellcode,我个人更喜欢IDAPro,但我不会使用伪代码功能,相反我会专注于反汇编视图,所以这应该可以工作使用您选择的任何工具。使用IDAPro,您可以选择如何加载二进制数据(16、32或64位),所以我将从32位开始,如果反汇编有任何错误,我们可以简单地更改它。加载二进制文件后,选择第一个字节并按“C”,这将尝试将第一个字节转换为代码。于是重新分析文件,尝试将剩余的字节拆成几部分。如下所示,入口点从位置0x00开始,shellcode一直到位置0x2A,此时IDA无法将剩余字节转换为汇编。仅基于这个事实,我们可以假设这部分数据以某种方式被加密,并且上面的shellcode解密并执行它。因此,让我们看一下要运行的第一个函数sub_0()。这个函数只负责跳转到偏移量0x25,这里会执行sub_2(),我把它命名为main_func()是因为它包含了大部分代码,所以我们继续看那个函数。所以这个函数内部的解密相当简单,只是一个基本的XOR循环,将[edi]指向的XOR数据与[esi]指向的数据组合起来。在每次异或运算后,edi(内存地址)递增,程序将[esi:esi+1]与0x550D进行比较。在本例中,IDA以Little-Endian方式显示值,因此显示为0x0D55。如果值匹配,则跳转到ecx指向的地址,否则将esi递增,将现在指向的数据与0x73进行比较,匹配则恢复原来的地址(存放在ebx中)。如果不匹配,则循环回到XOR代码并继续。根据上面的分析,我们知道esi作为key,edi作为加密后的shellcode。我们还知道密钥长度可以是任意长度,ebx将包含密钥第一个字节的地址。有了所有这些信息,让我们首先尝试找出加密数据的开始和停止位置。从XOR循环(loc_F)开始并向上移动。我们首先可以看到,edi中存放的地址被移到了ecx中,这是有道理的,因为一旦解密完成,程序就会跳转到ecx中的地址。现在,我们找到edi从ebx中获取加密代码地址,在函数的最顶部,弹出堆栈顶部的值,并将其存储在ebx中,然后将该值移动到edi中,然后将0x73移动到eax的下半部分Low,调用cld,然后进入一个scasb循环。问题是,弹出ebx时栈顶是什么?好吧,在调试器中快速查看一下堆栈就会发现,调用函数时,堆栈顶部的值是要返回的地址,也就是调用指令之后的指令地址。下图中,返回地址为0x013B1CAB,调用该函数的地址为0x013B1CA6(这里的调用指令占用5个字节)。那么,这如何应用于我们的shellcode?我们在上面说过,main_func是从位置0x25调用的。无法识别的数据从0x2A开始。位置0x25之后的5个字节为调用指令腾出空间。因此,main_func()中栈顶的数据值为0x2A。现在,我们终于知道加密数据从哪里开始了!不过也别高兴得太早,还记得0x73移入al后如何调用cld吗?cld指令负责删除(设置为0)EFLAGS寄存器中的DF(方向)标志。当此标志为0时,任何字符串操作都会递增索引寄存器,尤其是esi或edi,具体取决于操作中使用的寄存器。调用cld后,进入一个循环,不断执行scasb,直到设置ZF(0)标志(设置为1)。scasb会做什么?它所做的只是将al中的字节与[edi]中的字节进行比较,并根据结果设置状态标志。如果没有匹配,ZF将保持移除状态,否则将被设置并且程序将跳出循环。另外,由于scasb是一个字符串操作,每次循环后它都会递增edi中的地址,这意味着直到[edi]等于0x73,程序永远不会执行解密函数。现在我们知道加密数据是从0x73开始的,密钥是从地址0x2A一直到[address]=0x73的数据。如上所示,0x73是ebx指向的数据中的第二个字节。因此,异或密钥为0x06,加密数据从0x2B开始!向下滚动到函数的末尾,我们可以找到字节0x550D,这意味着加密数据的长度为401字节!现在我们都知道了,让我们进入解密阶段吧!Shllcode分析第2阶段:解密现在我们有了XOR密钥:0x06,以及加密数据开始的位置:0x2B。有了这些知识,我们就可以轻松地用Python编写一个基本的XOR解密函数,它有两个参数:密钥和加密数据。向函数添加一个简单的检查以查看数据是否与0x550D匹配并且脚本已完成!然而,为了实际使用它,我们需要手动定位它,或者使用YARA或Regex从IDA中提取数据。然后我们必须在新的IDA会话中打开它,在某些情况下,第二个“阶段”会调用第一阶段的函数,可能会对数据的另一部分进行异或运算。幸运的是,我们可以将这个基本脚本转换为IDAPython格式,并用解密的shellcode覆盖加密的shellcode!别担心,迁移到IDAPython的过程非常简单。首先我们需要导入所需的库idaapi然后将for循环更改为while循环,然后我们才能对传入的数据调用len(),但是在这种情况下我们不知道数据会有多长,因为我们现在传递加密数据的地址。除此之外,只要address+i中还有数据,你就可以让while循环一直循环下去。这可以通过使用Byte()函数来实现,该函数从给定地址获取一个字节的数据。在本文的示例中,该地址是我们加密的blob。我们现在可以将该数据字节分配给一个可变字节并将其与0x55进行比较。我们还将address+(i+1)与0x0D进行比较,以确保两个标记都存在。如果这些标记不存在,则对给定的输入执行XOR字节,然后调用PatchByte(),这将用我们的可变字节覆盖地址+i处的字节。最后将i加1,循环下去!将其导入到IDA中,在命令行中输入ida_xor_crypt(0x06,0x2B),我们的函数就会被执行,数据就会被覆盖,如下图所示!它不再以0x73开头,因为它现在是0x75。将其导入IDA,在命令行输入ida_xor_crypt(0x06,0x2B),就会执行我们的函数,覆盖数据,如下图我们可以再次按“C”,选择0x75,就可以了完全拆解。本文翻译自:https://0ffset.net/reverse-engineering/malware-analysis/common-shellcode-techniques/如有转载请注明出处
