逆向分析ISFB银行木马的第一阶段的加载程序(上)
导语:Ursnif是目前活动最为频繁的银行木马,它也被称为GOZI。在这篇文章中,我将详细介绍如何解压并分析第一阶段加载程序的可执行文件,然后使用这些信息提取第二阶段加载程序DLL,即rpcrt4.dll,我将在后面的文章中对其进行分析。
前言
Ursnif是目前活动最为频繁的银行木马,它也被称为GOZI。实际上,它是Gozi-ISFB银行木马的一个变种版本,自从该木马在2014年泄露了其源代码之后,攻击者这些年来一直都在升级和更新Gozi的功能。最近的一次版本更迭是在2019年年初,在这个变种版本中,Ursnif还嵌入了针对Office文档的攻击“武程序”,即恶意VBA宏,它可以作为Dropper或经过混淆的PowerShell脚本来隐藏真正的恶意载荷。除此之外,Ursnif还使用了隐写术来隐藏恶意代码并躲避检测。另外,这个变种还使用了QueueUserAPC进程注入技术来向explorer.可执行文件注入恶意代码,这种技术的隐蔽性更强,因为不需要在目标进程中创建远程线程。
在过去的几个月里,我一直在分析不同版本的Ursnif,以便更深入的了解其功能的演化。在这篇文章中,我将详细介绍如何解压并分析第一阶段加载程序的可执行文件,然后使用这些信息提取第二阶段加载程序DLL,即rpcrt4.dll,我将在后面的文章中对其进行分析。
简而言之,ISFB是一个银行木马,用于从毫无防备的受害者那里窃取财务信息。本文分析的ISFB样本是版本2.14.60,可以根据所使用的感染例程(特别是执行简单的Base64编码的powershell命令的宏),判断其幕后的开发团队是ISFB v2。另外,该团队还为不同的攻击活动重用加密密钥(默认密钥),与其他开发ISFB的大型团队相比,此团队更容易被识别出来。由于我一直无法找到该团队的具体名称,因此,根据FireEye的演示,我将把这个团队称为Group 53。
与其他利用ISFB获取经济利益的团队类似,Group 53使用包含嵌入式宏的恶意Word文档在目标系统上获得持久性注入,从而生成负责下载第一阶段可执行文件的Powershell脚本。而某些团队有时为了让自己开发的ISFB样本得到大范围传播,会与其他恶意传播团队合作,例如Hancitor背后的团队就是这么做的。Hancitor,也被称为Chanitor或Tordal,是一个基于宏的恶意软件,通过分布在垃圾电子邮件活动中的Microsoft Office文档传播。不过借助其他传播方式也有缺点,就是佣金过于昂贵,这就是为什么很多团队,包括Group 53,都不得不通过建立自己的传播渠道,来传播恶意文件的原因。在这篇文章中,我将重点分析ISFB样本在传播过程中所使用的可执行文件解压程序(unpacker 可执行文件cutable)和第一阶段可执行文件加载程序,而不是Word文档本身,因为它的功能非常简单。
与往常一样,样本已上传到VirusBay,需要的话,请你自行下载。
解压第一阶段可执行文件
第一阶段可执行文件的MD5:bc72604061732a9280edbe5e2c1db33b
通常我会在PEStudio(一款免费且实用的应用程序代码验证工具)中打开它或者执行一些静态分析,但是在本文的案例中,由于我已经确定它是基于Word宏的ISFB样本。首先,我会在IDA中打开它,尝试在存储区域或寄存程序中找到一个调用(或jmp),这可能是对解压阶段的调用。
基于上图中显示的长度和错综复杂的流程,看起来确实有一些解压过程正在进行。但在查看二进制文件中的字符串时,却没有看到很多有意义的字符串。 通常在解压样本时,我会从底部开始,然后逐步向上进行,大多数解压程序在文件解压后会自动退出。在本文的示例中,解压程序会执行自注入,并使用解压文件覆盖自身。这对于ISFB来说并不罕见,如果你分析一些其他样本,也会发现这种情况。这意味着解压程序在解压文件执行之前不会退出,尽管我可以假设最后调用的函数会将执行转移到已解压的可执行文件。
在刚刚的示例文件中,寄存程序或存储区域虽然没有调用或jmp,但是,却调用了loc_42A880。这是Win主中的最后一次调用,如前所述,此函数很可能负责可执行文件。
当我跳转到这个函数时,很明显它还没有被转换成功。因此,我将不得不以文本模式处理。当然,可以跟踪跳转和条件跳转来尝试找到调用或跳转到可执行文件。找到一个调用指令并单击它,可以加快调用速度。你只需从所在的位置向下或向上滚动,寻找其他调用示例。我找到的最后一个调用将lpAddress作为第一个参数,所以让我来看看这个。lpAddress表示它包含一个到存储区域的地址,这意味着它可以包含一个已解压的可执行文件的地址。
如果我直接跳到这个函数到末尾,就可以看到对ESI的调用。按空格密钥你可以从图形视图跳转回文本视图,以获取此调用的地址。这可能是对解压的可执行文件的调用,所以我需要它的地址在x32dbg中查看它并在其上放置一个断点。
所以这个调用的存储地址是0x0042A653,现在我可以在x32dbg中打开它并跳转到这个地址。只需在x32dbg中按CTRL-G并密钥入0042A653,然后按回车密钥,就会跳转到该地址,允许你在其上放置断点。在尝试解压本文的样本时,我更喜欢在同时也打开Process Hacker(一款拥有windows任务管理程序的开源软件),这样如果我在错误的地址上放置断点并执行解压的进程,你可以通过“网络”选项卡或“进程”选项卡轻松检测到该断点。
一旦断点被发现,就会进入ESI。在本文的样本中似乎是第二个解压过程,这个代码原本不在这里,事实上如果我尝试在IDA中查看它,就会看到很多问号和变量unk_58FC08。
由于很难对这部分进行静态分析,所以我将跳过该函数,而不是手动遍历它们。这也有助于加快分析速度,但前提是确保没有连接网络适配程序,因为其中一个函数可能会执行可执行文件。
在下面的GIF中,你可以看到存在某种形式的循环,其中XOR用于对存储在EAX中的存储地址进行异或运算,其值为DL。你可能还会注意到,随着每个循环的继续,程序集也会随着发生变化,不过这是第二个解压阶段的另一个示例。我可以简单地在jmp上放置一个断点,然后运行程序直到它被发现。
一旦执行跳转,就会遇到几个对[ebx+xxxxxx]的调用。其中每一个都可以跳转到解压的可执行文件。但随着我的深入分析,很明显,这些只是对Windows API函数的调用。不知你是否注意到对EDI的调用了吗?EDI指向一个函数,该函数会动态导入这些API,以便解压存根(stub)调用它们。调用结果存储在EAX中,如下图所示,这个特定的调用导入了API RtlExitUserThread。
向下滚动一点,我可以看到一个jmp eax,所以我可以在它上面放一个断点并运行它,直到跳转完成。
这个跳转将带我进入一个新分配的存储区域,其中包含更多代码。由于我并不想检查每个函数,所以我一直向下滚动,直到找到ret指令,并开始向后检查(是本地而不是API调用)函数。最后一个函数似乎不太可能是可执行的“executor” 工具,因为没有调用或跳转到不同存储区域的指令,但是最后一个函数对[ebp+14]和[ebp+C]有一些调用,所以我会在这些函数上设置一些断点。Executor是一个比Windows“运行”功能更为强大、自定义更强的工具,如果你需要经常查找程序或者想快速运行程序这个功能比较适合你。
但是,在执行程序并触发断点时,很明显它们只是调用LoadLibrary和GetProcAddress。
由于该函数只是导入API,这对我来说并不是非常重要。所以退出该函数后,我进行了几次API调用,特别是VirtualProtect。VirtualProtect负责更改不同存储区域的权限或保护,使它们可读、可写或可执行。在本文的示例中,我可以看到VirtualProtect用于更改存储区域0x00406000的保护,即.BSS部分。正如我前面提到的,这个解压程序用解压的可执行文件进行自我覆盖。所以可以肯定的说,我已经找到解压的可执行文件了。现在我可以开始转储它们了,但可能会有一些额外的解压过程,所以我先要跳转到0x00400000存储区域。
此时,函数中还没有太多有意义的内容,因此我可以简单地在ret指令上放置一个断点,然后从退出该函数。
退出该函数后,你会注意到存储地址位于0x00401000区域。这就是用于解压的程序!现在我可以将其进行转储,因此请确保你已打开Process Hacker。
要从存储中转储它,只需双击Process Hacker中的进程,然后右密钥单击0x400000存储区域,并选择保存。
现在我可以在PEBear中打开它并删除映射转储的文件。执行此操作后,你可以看到PEBear未成功解析导入的文件。这就是我需要删除映射文件的原因。当程序即将执行时,需要将其映射到存储中,以便可以正确解释它。因此,在我删除映射之前,PEBear无法解析导入的文件。
此时,我只需更改Raw Addr,使其与Virtual Addr匹配,然后相应地更改Raw Size 。此时,你应该会看到如下图所示的内容。
检查导入的内容,果然有4个导入的DLL,这意味着我得到了正确的未映射文件。现在我可以将其保存到桌面!
至此,我就彻底成功的解压了第一阶段的加载程序!接下来,我会在IDA中打开它们,并进行进一步分析。
分析转储的可执行文件
转储的可执行文件的MD5: 0063316975e55c765cd12e3d91820478
在IDA中打开文件后,我可以看到主函数只调用了一个本地函数,然后就退出了,因此找到恶意代码并不困难。如果你不确定找到的示例是否是ISFB,可以在字符串窗口中找到一个提示符。大多数ISFB有效载荷会以明文形式存储用于字符串解密的编译开始日期。在此示例中,编译日期是2019年1月28日,因此在撰写本文时,它是一个相对较新的样本。在介绍字符串解密方法之前,首先让我们看看在函数中发生了什么。
在这个函数中有很多个函数被调用,让我们先看一下前四个函数。从下图可以清楚地看出,sub_401C69(存储在eax中)的返回值需要与esi中的值匹配,否则它将跳转到出口。第二个函数sub_401E4F似乎也与它类似。第三个函数似乎是检查某些内容的,第三个函数似乎是检查某些东西的,因为根据 bit-wise AND(一种利用bit-wise索引进行查找的方式)在eax上的检查结果,1会被移入DWORD。第四个函数似乎以与第一个和第二个函数相同的方式运行,即将返回的值与esi中的值进行比较,如果不满足条件,它将自动退出。
看看第一个函数(主位),我可以看到恶意软件正在打开它自己的进程并将句柄存储在DWORD中。如果恶意软件无法打开该进程,则将其设置为-1,然后返回主函数,返回的结果与ESI中的值进行比较。
第二个函数则非常有趣,在下图中,你将注意到编译日期被移动到ESI中,正如我之前提到的,编译日期被用于字符串解密。由于ISFB包含.BSS部分,其中包含多个使用ROR-XOR算法加密的字符串。XOR密钥是根据给定日期计算的,为了解密字符串,ISFB需要再次执行计算以获得正确的密钥,这样我就可以轻松地进行逆向分析。但首先,我来看看事先调用的两个函数。
看看第一个函数,你会注意到 ssb(实际上是.BSS)与循环内的[ECX]的值进行比较。为了解密.BSS部分中的字符串,ISFB必须首先找到.BSS部分。为了做到这一点,它只需读取自己的PE标头并获取所需部分的大小和地址。如果[ECX]处的值与.BSS不匹配,则在ECX中的存储地址添加40,这是因为段描述符或结构(.text,.data等)之间的间距为40字节。此时,循环将继续。如果匹配,则检查字符串的长度,确保它不超过4个字节。恶意软件通过检查字符串后面的字节并将其与零进行比较来完成匹配操作,如果字符串的长度正确,则指向“.BSS”的存储地址将被移入EDX。如果一切顺利,ISFB将获取.BSS部分的地址和大小,并将其存储在存储中。在本文的示例中,地址是0x6000,大小为0x1000。如果这仍然很难理解,那么请看下面我在x32dbg中的截图,这应该可以帮助你了解它是如何获取地址和大小的。
函数:sub_401C0F # 2
下一个函数的主要目的是使用AddVectoredExceptionHandler添加一个向量异常处理程序,其中第二个参数是指向处理程序函数的指针,该函数将在程序运行到异常时执行,因此让我们看看exception tion_handle_function函数。
此函数会检查异常的值,无论值是EXCEPTION_ACCESS_VIOLATION还是EXCEPTION_SINGLE_STEP,它们最终都会执行相同的函数,所以进入该函数来看看。
该函数的重要功能会在函数执行后很快发生,在下图中就是我创建的一个循环,以及两个XOR指令和一个ROR指令。稍后创建的XOR密钥实际上是arg_8,请注意这一点。现在我可以确定会某个时候会有一个异常,且会执行BSS字符串解密函数。有了这些信息后,我可以转而从编译日期开始逆向分析恶意软件是如何创建XOR密钥的?
由于XOR密钥基于日期创建的,因此我需要查看日期。首先,将BSS部分的地址(0x6000)移入EAX,并将其大小(0x1000)移入EDX。然后,将编译日期的存储地址移入ESI,并将该地址(指向存储的空白部分)移入EDI。然后使用MOVSD将编译日期移动到存储的空白区域,MOVSD会将一个DWORD从ESI中的存储地址移动到EDI中的存储地址,接下来,将编译日期的第一个DWORD移动到ECX中,并对编译日期的第二个DWORD执行XOR运算。接着,将其结果添加到BSS部分的地址(此处为0x6000)和值0xE中,然后将其作为将被调用的下一个函数的第一个参数推送。
在下一篇文章中,我会接着分析剩余函数和它们对应的加载过程。
发表评论