FlokiBot 银行木马详细分析

原文首发:http://bbs.pediy.com/thread-216639.htm 

☀ 介 绍

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

☀ FlokiBot Dropper

导入:模块/API 哈希处理与系统调用(syscall)

dropper 通过比较经过哈希处理的库名与内置哈希值来加载模块。哈希进程用到了一个基础的 CRC32,之后这个 CRC32 还要跟两个字节的密钥异或,那两个密钥随样本的不同而不同。

有两种方法来检索动态链接库(dll)库名:一是用

Process Environment Block 来检查 InMemoryOrderModuleList 的结构并读取

BaseDllName 的值来获取进程已经加载的dll。第二种方法是,通过在 Windows 系统文件夹中罗列库名。

FlokiBot 银行木马详细分析_第1张图片

移除点击此处添加图片说明文字

下面的模块都被 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 中实现的。

FlokiBot 银行木马详细分析_第2张图片

移除点击此处添加图片说明文字

正如我们所看到,系统调用的值放在

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 数组中。

FlokiBot 银行木马详细分析_第3张图片

移除点击此处添加图片说明文字

在我的虚拟机上,当所有的系统调用号都被 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在磁盘中的映射——干净版本

FlokiBot 银行木马详细分析_第4张图片

移除点击此处添加图片说明文字

由dropper导入的NTDLL——可能被钩住

现在,设置了正确的权限,FlokiBot 把映射干净 DLL 代码块的地址、导入的 dll 的地址入栈,然后调用解除挂钩操作。

移除点击此处添加图片说明文字

因为它要重写它的内存里的一些数据以删除钩子,恶意软件需要改变导入的

NTDLL 代码导出段的内存保护机制。而它是通过用 int 0x2E 和之前提取的系统调用号(在我的 windows 版本是 0x4D)调用

NtProtectVirtualMemory 来做到的。我们可以看到如果某个钩子被发现,某一部分的代码就会变得可写。

FlokiBot 银行木马详细分析_第5张图片

移除点击此处添加图片说明文字

解除挂钩函数可以描述为三步:对于 NTDLL 导出的每一个函数……

比较两个映射库的第一个操作码

如果它们不一致,说明导入的 ntdll 里的函数已经被钩住了。

改变导入的dll的内存保护机制,让它变成可写。

用从被映射到磁盘的 dll 复制来的操作码修补这个操作码。

解除挂钩操作的主要程序如下:

FlokiBot 银行木马详细分析_第6张图片

移除点击此处添加图片说明文字

这样以来,很多监测工具、杀毒软件和沙箱都无法追踪恶意软件的调用。这个非常有用,如果你想要避免来自像 malwr.com. 这些网上沙箱的自动分析。

☀ 从资源中提取 Bot

它的 dropper 有 3 个明确命名的资源: key, bot32 和 bot64 。Bot 被 RtlCompressBuffer() 和 LZNT1 压缩,然后用有 16 字节密钥的 RC4 加密它。在我的样本里,密钥是:

style="line-height: normal;">

14px;">A3 40 75 AD 2E C4 30 23 82 95 4C 89 A4 A7 84 00

你可以从

Talos 团队的 Github: https://github.com/vrtadmin/flokibot. 中找到可以备份这个

payload 和配置文件的 Python 脚本。要注意的是,它们并不能自己正确运行,因为它们要注入一个进程,还需要一些被 dropper

在内存中改写的数据。我们会在下一部分详谈注入进程。

提取资源的常用方法:

BOOL __userpurge extract_bot_from_rsrc@(int a1@, HMODULE hModule)

{

HRSRC v2; // eax@1

int v3; // eax@2

const void *v4; // esi@5

HRSRC v5; // eax@7

int v6; // eax@8

HRSRC v7; // eax@10

unsigned int v8; // eax@11

int v10; // [sp+4h] [bp-4h]@1

v10 = 0;

v2 = FindResourceW(hModule, L"key", (LPCWSTR)0xA);

if ( v2 )

v3 = extract_rsrc(hModule, (int)&v10, v2);

else

v3 = 0;

if ( v3 )

{

v4 = (const void *)v10;

if ( v10 )

{

qmemcpy((void *)(a1 + 84), (const void *)v10, 0x10u);

free_heap(v4);

}

}

v5 = FindResourceW(hModule, L"bot32", (LPCWSTR)0xA);

if ( v5 )

v6 = extract_rsrc(hModule, a1 + 4, v5);

else

v6 = 0;

*(_DWORD *)(a1 + 12) = v6;

v7 = FindResourceW(hModule, L"bot64", (LPCWSTR)0xA);

if ( v7 )

v8 = extract_rsrc(hModule, a1 + 8, v7);

else

v8 = 0;

*(_DWORD *)(a1 + 16) = v8;

return *(_DWORD *)(a1 + 4) && *(_DWORD *)(a1 + 12) > 0u && *(_DWORD *)(a1 + 8) && v8 > 0;

}

☀ 注入过程

dropper

并不是用常用的用 NtMapViewOfSection 和 NtWriteVirtualMemory 来将 payload 注入到

explorer.exe (或者svchost.exe,如果失败的话) 。它是用写并运行一个可以在进程内存中解密解压 payload 的

shellcode 来完成的。这很不常见,很有趣。dropping 的过程可以用下面的图片来总结:

FlokiBot 银行木马详细分析_第7张图片

移除点击此处添加图片说明文字

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 和新的异或密钥。

int __stdcall sub_405E18(int a1)

{

[...]

if ( a1 && *(_DWORD *)(a1 + 4) && *(_DWORD *)a1 != -1 )

{

v1 = 0;

v34 = 0i64;

v35 = 0i64;

v36 = 0i64;

do /* CRC Polynoms */

{

v2 = v1 >> 1;

if ( v1 & 1 )

v2 ^= 0xEDB88320;

if ( v2 & 1 )

v3 = (v2 >> 1) ^ 0xEDB88320;

else

v3 = v2 >> 1;

[...]

if ( v8 & 1 )

v9 = (v8 >> 1) ^ 0xEDB88320;

else

v9 = v8 >> 1;

v40[v1++] = v9;

}

while ( v1 < 0x100 );

v10 = shellcode_imp_dll((int)v40, 0x6AE6AF84);

v11 = shellcode_imp_dll((int)v40, 0x84C06EC6);

v30 = v12;

v13 = v11;

LODWORD(v34) = shellcode_imp_api(v10, (int)v40, 0x9CE3DCC);

DWORD1(v34) = shellcode_imp_api(v10, (int)v40, 0xDF2761CD);

DWORD2(v34) = shellcode_imp_api(v10, (int)v40, 0xF7C79EC4);

LODWORD(v35) = shellcode_imp_api(v10, (int)v40, 0xCD53C55B);

DWORD1(v36) = shellcode_imp_api(v10, (int)v40, 0xC97C2F79);

LODWORD(v36) = shellcode_imp_api(v10, (int)v40, 0x3FC18D0B);

DWORD2(v36) = shellcode_imp_api(v13, (int)v40, 0xD09F7D6);

DWORD1(v35) = shellcode_imp_api(v13, (int)v40, 0x9EEE7B06);

DWORD2(v35) = shellcode_imp_api(v13, (int)v40, 0xA4160E3A);

DWORD3(v35) = shellcode_imp_api(v13, (int)v40, 0x90480F70);

DWORD3(v36) = shellcode_imp_api(v13, (int)v40, 0x52FE165E);

v14 = ((int (__stdcall *)(_DWORD, _DWORD, signed int, signed int))v34)(0, *(_DWORD *)(a1 + 8), 0x3000, 64);

[...]

}

前两个哈希值,0x6AE6AF84 和 0x84C06EC6

应该是 'kernel32.dll'和'ntdll.dll' 的。我用 Python 来实现哈希过程,证实导入的那两个 DLL 确实是

kernel32 和 ntdll,然后我修改我的 Python 程序去解析它的导出表,想要知道函数导入的 API 的名字。我运行程序得到下面的

API。

Python>run

[+] kernel32.dll (6AE6AF84) : Parsing...

0x09CE3DCC --> VirtualAlloc

0xDF2761CD --> OpenProcess

0xF7C79EC4 --> ReadProcessMemory

0xCD53C55B --> VirtualFree

0xC97C2F79 --> GetProcAddress

0x3FC18D0B --> LoadLibraryA

[+] ntdll.dll (84C06EC6) : Parsing...

0x0D09F7D6 --> NtClose

0x9EEE7B06 --> NtCreateSection

0xA4160E3A --> NtMapViewOfSection

0x90480F70 --> NtUnmapViewOfSection

0x52FE165E --> RtlDecompressBuffer

用这些函数,进程中的代码将可以读取 dropper 的资源(bot和RC4密钥)并且映射 payload 在内存中的位置。最后,远处终止的线程内容将会被修改,这样它的 EIP 将指向第一个 shellcode,该线程继续。

FlokiBot 银行木马详细分析_第8张图片

移除点击此处添加图片说明文字

流程图

FlokiBot 银行木马详细分析_第9张图片

移除点击此处添加图片说明文字

☀ FlokiBot Payload

这个 payload 是基于熟知并已经分析过的 ZeuS 木马,所以我不会每件事都详细描述。至于 dropper,我会更着重讲去混淆部分以及 FlokiBot 改进部分的实现。

配置

我运行 Talos 团队发布的 ConfigDump.py 程序,然后得到下面的C&C :

$ python ConfigDump.py payload_32.vir

Successfully dumped config.bin.

URL: https://extensivee[.]bid/000L7bo11Nq36ou9cfjfb0rDZ17E7ULo_4agents/gate[.]php

用 IDAPython 完全静态去混淆

本文由 看雪翻译小组 lumou 编译,来源 Arnaud Delmas

❤ 往期热门内容推荐

渗透测试 Node.js 应用

TI(德州仪器)TMS320C674x 逆向分析方法

Firefox 中一个 Cross-mmap 溢出的利用

UPDATE 查询中的 SQL 注入

绕过补丁实现欺骗地址栏和恶意软件警告

......

FlokiBot 银行木马详细分析_第10张图片

看雪论坛:http://bbs.pediy.com

微信公众号 ID:ikanxue

微博:看雪安全

投稿、合作:http://www.kanxue.com

你可能感兴趣的:(FlokiBot 银行木马详细分析)