目录
介绍
FlokiBot Dropper
API调用去混淆
FlokiBot Payload
配置
用IDAPython完全静态去混淆
挂钩API
最后的笔记和哈希值
介绍
FlokiBot 是最近一款针对于欧洲和巴西联邦共和国的银行木马,作为一款恶意软件工具集,它在一些黑客论坛上被卖到$1000。它通过垃圾邮件和渗透代码工具包来传播。虽说它是继承于ZeuS(宙斯),FlokiBot也做了很多有趣的改进。有诸如内存截取(RAM scraping),定制的dropper这样的新特性,还有似乎从泄露了源码的Carberp 那里借鉴了几行代码。
FlokiBot与其dropper都有很多常用或不常用的混淆技术,我们将解开它们的神秘面纱,并着重讨论如何使用IDA和IDAPython脚本来静态脱壳。因为你们已经在最近很多恶意软件上接触过这些技术了,所以我觉得这是一次很好的锻炼。
在看完@hasherezade写的这篇关于FlokiBot的dropper文章:https://blog.malwarebytes.com/threat-analysis/2016/11/floki-bot-and-the-stealthy-dropper/. 之后,我决定看一下FlokiBot。尽管大多数关于FlokiBot的文章都着重讲它的dropper,我还是想讲得更详细一些,然后再讲讲它的payload;我们将会看到它有很多有有趣的特性,而且不是你平常所看到的ZeuS不一样,虽然它的很多代码是来自ZeuS和Carberp leaks。还是比逆向勒索软件好。
Hash值:
$ rahash2 -a md5,sha1,sha256 -qq floki_dropper.vir
37768af89b093b96ab7671456de894bc
5ae4f380324ce93243504092592c7b275420a338
4bdd8bbdab3021d1d8cc23c388db83f1673bdab44288fccae932660eb11aec2a
$ rahash2 -a md5,sha1,sha256 -qq floki_payload32.vir
da4ea4e44ea3bb65e254b02b2cbc67e8
e8542a465810ff1396a316d1c46e96e042bf4189
9f1d2d251f693787dfc0ba8e64907e204f3cf2c7320f66007106caac0424a1f3
对这个dropper的自动分析报告:
VirusTotal Analysis, Hybrid-Analysis
FlokiBot Dropper
dropper通过比较经过哈希处理的库名与内置哈希值来加载模块。哈希进程用到了一个基础的CRC32,之后这个CRC32还要跟两个字节的密钥异或,那两个密钥随样本的不同而不同。
有两种方法来检索动态链接库(dll)库名:一是用 Process Environment Block 来检查InMemoryOrderModuleList
的结构并读取BaseDllName
的值来获取进程已经加载的dll。第二种方法是,通过在Windows系统文件夹中罗列库名。
下面的模块都被dropper导入了:
CRC Library Method
------------------------------------------------------
84C06AAD ntdll.dll load_imports_peb
6AE6ABEF kernel32.dll load_imports_peb
2C2B3C88
948B9CAB
C7F4511A wininet.dll load_imports_folder
F734DCF8 ws2_32.dll load_imports_folder
F16EE30D advapi32.dll load_imports_folder
C8A18E35 shell32.dll load_imports_folder
E20BF2CB shlwapi.dll load_imports_folder
1A50B19C secur32.dll load_imports_folder
630A1C77 crypt32.dll load_imports_folder
0248AE46 user32.dll load_imports_peb
BD00960A
4FF44795 gdi32.dll load_imports_peb
E069944C ole32.dll load_imports_folder
CAAD3C25
之后,FlokiBot将采取同样的操作来定位和加载这些模块用到的API.首先,当CRC后的名字是匹配的,那么它将检索LdrGetProcedureAddress 在ntdll中的地址,并用它来获取其他API的句柄。这样做的话,函数的地址就仅对debugger可见。如此,分析代码将会非常困难,因我我们不知道调用了哪个API。去混淆的一种方法将在下一章提到。
FlokiBot与其dropper的另一有趣之处在于它们调用一些原生API函数的方式。这些函数位于ntdll中,并且以 Nt* 或者Zw*为前缀。它们在实现的时候与其他API有些许不同,因为它们要用到syscall特别是int 0x2e。下面的截图说明了它们是如何在ntdll中实现的。
正如我们所看到,系统调用的值放在eax(在我64位Windows 7上,NtAllocateVirtualMemory 是在0x15 ),而且参数被传到edx。x86和64位的所有系统调用号(syscall number)都可以在这张网页找到:http://j00ru.vexillium.org/ntapi/.
在检查ntdll中的API时,FlokiBot会先检查函数的第一个操作码是否为 0xB8
= MOV EAX,
若是,并且CRC后的API名也复合,它将提取MOV EAX 后面的四个字节,也就是系统调用号,并将它保存在dwSyscallArray 数组中。
在我的虚拟机上,当所有的系统调用号都被dropper提取后,dwSyscallArray 长这样。
Index API Syscall number
-------------------------------------------------------
0x0 NtCreateSection 0x47
0x1 NtMapViewOfSection 0x25
0x2 NtAllocateVirtualMemory 0x15
0x3 NtWriteVirtualMemory 0x37
0x4 NtProtectVirtualMemory 0x4D
0x5 NtResumeThread 0x4F
0x6 NtOpenProcess 0x23
0x7 NtDuplicateObject 0x39
0x8 NtUnmapViewOfSection 0x27
当FlokiBot需要调用某个原生函数的时候,它将调用自身的一个函数,那个函数直接从dwSyscallArray 中检索系统调用号,传参,触发中断0x2E。这些都跟它在ntdll中的实现方式一样。这就是为什么你不会看到任何这些API调用的轨迹,而且专门用来钩住这些API的监测工具也监测不到有调用它们。
API调用去混淆
既然FlokiBot的payload用到了相同的函数和数据结构,你可以用“IDAPython完全静态去混淆”模块,稍稍修改IDAPython脚本就可以将dropper的API调用去混淆。
FlokiBot的一个有趣之处就在于它的dropper和payload都有解除挂钩操作。思路是卸载检测工具,沙箱和杀毒软件中的钩子。尽管这并不是恶意软件第一次使用这样的功能,比如说,Carberp就有一个能 不让Trusteer的Rapport发现 的功能,还有最近的Carbanak ,但这样的功能能真的非常罕见,应该值得注意。在这一部分,我将描述FlokiBot是如何解除挂钩的。
首先,FlokiBot通过罗列System32文件夹中的dll来获得ntdll.dll的句柄,然后用我们上面所提到的哈希处理过程,最后调用 MapViewOfFile
来映射它在内存中的位置。结果就是,FlokiBot有两个库在内存中映射的版本:一个是在导入期间导入的,这个可能会被监测工具的钩子改变,另一个是它直接从磁盘中映射的,这个是干净的。
NTDLL在磁盘中的映射——干净版本
由dropper导入的NTDLL——可能被钩住
现在,设置了正确的权限,FlokiBot把映射干净DLL代码块的地址、导入的dll的地址入栈,然后调用解除挂钩操作。
因为它要重写它的内存里的一些数据以删除钩子,恶意软件需要改变导入的NTDLL代码导出段的内存保护机制。而它是通过用int 0x2E和之前提取的系统调用号(在我的windows版本是0x4D)调用NtProtectVirtualMemory 来做到的。我们可以看到如果某个钩子被发现,某一部分的代码就会变得可写。
解除挂钩函数可以描述为三步:对于NTDLL导出的每一个函数……
比较两个映射库的第一个操作码
如果它们不一致,说明导入的ntdll里的函数已经被钩住了。
改变导入的dll的内存保护机制,让它变成可写。
用从被映射到磁盘的dll复制来的操作码修补这个操作码。
解除挂钩操作的主要程序如下:
这样以来,很多监测工具、杀毒软件和沙箱都无法追踪恶意软件的调用。这个非常有用,如果你想要避免来自像 malwr.com. 这些网上沙箱的自动分析。
它的dropper有3个明确命名的资源: key
, bot32
和 bot64
。Bot被RtlCompressBuffer()
和 LZNT1 压缩,然后用有16字节密钥的RC4加密它。在我的样本里,密钥是:
你可以从Talos 团队的Github: https://github.com/vrtadmin/flokibot. 中找到可以备份这个payload和配置文件的Python脚本。要注意的是,它们并不能自己正确运行,因为它们要注入一个进程,还需要一些被dropper在内存中改写的数据。我们会在下一部分详谈注入进程。
提取资源的常用方法:
dropper并不是用常用的用NtMapViewOfSection
和NtWriteVirtualMemory
来将payload注入到explorer.exe
(或者svchost.exe
,
如果失败的话) 。它是用写并运行一个可以在进程内存中解密解压payload的shellcode来完成的。这很不常见,很有趣。dropping的过程可以用下面的图片来总结:
dropper在explorer.exe / svchost.exe 里写一个trampoline shellcode 和它自己的一个函数。
当运行时,trampoline就会调用那个函数。
函数自动运行,并且动态解决导入、读取dropper的资源,并将它们提取到自己的进程内存中。(比如,在explorer.exe / svchost.exe 的地址空间里)
最后,dropper在目标进程中运行bot payload的入口点(entrypoint)。
第一个写在explorer.exe 的shellcode(称为trampoline )会休眠100ms,然后调用一个函数,那个函数dropper在进程内存中映射在0x80000000 ,在该dropper中默认称为 sub_405E18
。这第二个阶段是要提取bot payload,解密并解压它们。所有这些都发生在explorer.exe / svchost.exe
内存中。
$ rasm2 -a x86 -b 32 -D '558BEC51C745FCFF10B4766864000000FF55FCC745FC000008006800000900FF55FC83C4048BE55DC3'
0x00000000 1 55 push ebp
0x00000001 2 8bec mov ebp, esp
0x00000003 1 51 push ecx
0x00000004 7 c745fcff10b476 mov dword [ebp - 4], 0x76b410ff ; address of sleep()
0x0000000b 5 6864000000 push 0x64
0x00000010 3 ff55fc call dword [ebp - 4] ; sleep()
0x00000013 7 c745fc00000800 mov dword [ebp - 4], 0x80000
0x0000001a 5 6800000900 push 0x90000
0x0000001f 3 ff55fc call dword [ebp - 4] ; sub_405E18, 2nd stage
0x00000022 3 83c404 add esp, 4
0x00000025 2 8be5 mov esp, ebp
0x00000027 1 5d pop ebp
0x00000028 1 c3 ret
sub_405E18 将通过与dropper和payload相同的进程来导入它需要的资源,用有些许不用的crc32和新的异或密钥。
前两个哈希值,0x6AE6AF84
和 0x84C06EC6
应该是 'kernel32.dll'和'ntdll.dll'的。我用Python来实现哈希过程,证实导入的那两个DLL确实是kernel32 和 ntdll,然后我修改我的Python程序去解析它的导出表,想要知道函数导入的API的名字。我运行程序得到下面的API。
用这些函数,进程中的代码将可以读取dropper的资源(bot和RC4密钥)并且映射payload在内存中的位置。最后,远处终止的线程内容将会被修改,这样它的EIP将指向第一个shellcode,该线程继续。
这个payload 是基于熟知并已经分析过的ZeuS木马,所以我不会每件事都详细描述。至于dropper,我会更着重讲去混淆部分以及FlokiBot改进部分的实现。
配置
我运行 Talos 团队发布的 ConfigDump.py 程序,然后得到下面的C&C :
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!