FlokiBot 银行木马详细分析 (二) emotet银行木马
liebian365 2024-10-19 08:01 20 浏览 0 评论
? 用 IDAPython 完全静态去混淆
→ 鉴别函数
首先,我们注意到 dropper 重用了一些 payload 的重要函数。创造该 dropper 的一个 Rizzo 签名并在 payload 中加载它能够 IDA 让识别并重命名少部分函数。
? API 调用和钩子的静态去混淆
思路是用 Python 重新实现哈希过程,哈希所有被 FlokiBot 加载的所有 API,然后将他们和我们用代码收集到的哈希值进行比较。如果匹配,我们就用 IDAPython 重命名该函数,使得反汇编更具可读性。因为 payload 用的是同样的 CRC 函数和同样的异或密钥,所以这个脚本对它们都管用。
→ 字符串去混淆
跟 ZeuS 和 Fobber(Tinaba 的进化版)一样,很多字符串都用它们自己的一字节的密钥异或加密了。恶意软件将所有的 ENCRYPTED_STRING 存储在一个数组中,并将在传输过程中通过下标去混淆。加密过的字符串将以下面的数据结构展现:
typedef struct {
char xor_key;
WORD size;
void* strEncrypted;
} ENCRYPTED_STRING;
首先,为弄明白如何没有错误的检索出它们,我会运行一段代码罗列 decrypt_string 的参数是如何入栈的。
运行完我们的脚本后,这里有一个在 IDA 中反汇编后的样本:
→ 完整的 IDAPython 脚本
这是我用来去混淆该 payload 的完整的 Python 脚本:https://gist.github.com/adelmas/8c864315648a21ddabbd6bc7e0b64119.
它基于 IDAPython 和 PeFile。它专为静态分析设计,你不用开启任何 debugger 来让这段程序工作。它将完成以下的工作:
明确bot引入的所有函数并以[API name]_wrap 的格式重命名它们。
解析WINAPIHOOK 结构并以hook_[API name] 的格式重命名钩子函数。
解密字符串并将解密后的值放在解密字符串函数调用处的注释中。
# coding: utf-8
# ====================================================== #
# #
# FLOKIBOT BOT32 DEOBFUSCATION IDA SCRIPT #
# #
# http://adelmas.com/blog/flokibot.php #
# #
# ====================================================== #
# IDAPython script to deobfuscate statically the bot32 payload of the banking malware FlokiBot.
# Imports are fully resolved, hooks are identified and named and strings are decrypted and added in comments, without using any debugger.
# May take a few minutes to resolve imports.
# Works with FlokiBot dropper with some small changes.
import sys
# sys.path.append("/usr/local/lib/python2.7/dist-packages")
# idaapi.enable_extlang_python(True)
import pefile
# RunPlugin("python", 3)
CRC_POLY = 0xEDB88320 # Depending on sample
XOR_KEY = 0x34ED # Depending on sample
ARRAY_ADDR = 0x41B350 # Depending on sample
ARRAY_ITER = 12 # Size of a triplet (3*sizeof(DWORD))
i = 0
# ----------------------------------------------------
......(代码省略)
→ 持久性
bot 用一个伪随机名字把自己复制到 C:\Documents and Settings\[username]\Application Data 并通过在 Windows 的启动文件夹创建一个 .lnk 来获得持久性。
int startup_lnk() {
int v0; // edi@1
_WORD *v1; // ecx@1
int v2; // eax@2
_WORD *v3; // ecx@2
const void *v4; // eax@2
const void *v5; // esi@3
int strStartupFolder; // [sp+8h] [bp-20Ch]@1
int v8; // [sp+210h] [bp-4h]@6
v0 = 0;
SHGetFolderPathW_wrap(0, 7, 0, 0, &strStartupFolder); // 7 = CSIDL_STARTUP
v1 = (_WORD *)PathFindFileNameW_wrap(&pFilename);
if ( v1 && (v2 = cstm_strlen(v1), sub_40FECB(v2 - 4, v3), v4) )
v5 = v4;
else
v5 = 0;
if ( v5 ) {
v8 = 0;
if ( build_lnk((int)&v8, (const char *)L"%s\\%s.lnk", &strStartupFolder, v5) > 0 )
v0 = v8;
cstm_FreeHeap(v5);
}
return v0;
}
? 挂钩API
→ 概述
基于ZeuS,FlokiBot 用了同一种但又有些许不同的结构数组来存储它的钩子:
typedef struct
{
void *functionForHook;
void *hookerFunction;
void *originalFunction;
DWORD originalFunctionSize;
DWORD dllHash;
DWORD apiHash;
} HOOKWINAPI;
在我们运行完前面用来去混淆 API 调用的脚本,以及定位好钩子结构数组之后,我们就可以很轻易的用其他的 IDA 脚本来解析它,以确定和命名钩子函数(hook_* )。我们最后得到下面的表格:
Parsing hook table @ 0x41B000...Original Function Hooked Hooker Function DLL Hash API Hash-------------------------------------------------------------------------------------------------------------NtProtectVirtualMemory_wrap hook_NtProtectVirtualMemory_wrap 84C06AAD (ntdll) 5C2D2E7ANtResumeThread_wrap hook_NtResumeThread_wrap 84C06AAD (ntdll) 6273819FLdrLoadDll_wrap hook_LdrLoadDll_wrap 84C06AAD (ntdll) 18364D1FNtQueryVirtualMemory_wrap hook_NtQueryVirtualMemory_wrap 84C06AAD (ntdll) 03F6C761NtFreeVirtualMemory_wrap hook_NtFreeVirtualMemory_wrap 84C06AAD (ntdll) E9D6FAB3NtAllocateVirtualMemory_wrap hook_NtAllocateVirtualMemory_wrap 84C06AAD
......代码省略
它们中的大多数都有安装在 ZeuS 和其他银行恶意软件中。尽管如此,我们还是能够注意到 NtFreeVirtualMemory 和 NtProtectVirtualMemory 的一些有趣的、新的钩子。我们将在下一部分看到它们的用途。
→ 浏览器中间人(Man-in-the-Browser)
Floki 通过把自己注入到 Firefox 和 Chrome 进程中并拦截 LdrLoadDll 来实现浏览器中间人攻击。如果浏览器加载的 DLL 的哈希值和 nss3.dll, nspr4.dll 或 chrome.dll 任一个的哈希值匹配,API 钩子就会自动安装,让恶意软件可以实现表单抓取和网站注入。
int __stdcall hook_LdrLoadDll_wrap(int PathToFile, int Flags, int ModuleFileName, int *ModuleHandle)
{
int result; // eax@2
int filename_len; // eax@8
int dll_hash; // eax@8
[...]
if ( cstm_WaitForSingleObject() ) {
v5 = LdrGetDllHandle_wrap(PathToFile, 0, ModuleFileName, ModuleHandle);
v6 = LdrLoadDll_wrap(PathToFile, Flags, ModuleFileName, ModuleHandle);
v12 = v6;
if ( v5 < 0 && v6 >= 0 && ModuleHandle && *ModuleHandle && ModuleFileName )
{
RtlEnterCriticalSection_wrap(&unk_41D9F4);
filename_len = cstm_strlen(*(_WORD **)(ModuleFileName + 4));
dll_hash = hash_filename(filename_len, v8);
if ( !(dword_41DA0C & 1) ) {
if ( dll_hash == 0x2C2B3C88 || dll_hash == 0x948B9CAB ) { // hash nss3.dll & nspr4.dll
sub_416DBD(*ModuleHandle, dll_hash);
if ( dword_41DC2C )
v11 = setNspr4Hooks(v10, dword_41DC2C);
}
else if ( dll_hash == 0xCAAD3C25 ) { // hash chrome.dll
if ( byte_41B2CC ) {
if ( setChromeHooks() )
dword_41DA0C |= 2u;
}
[...]
}
else
{
result = LdrLoadDll_wrap(PathToFile, Flags, ModuleFileName, ModuleHandle);
}
return result;
}
→ 证书窃取
通过挂钩 PFXImportCertStore ,FlokiBot 可以窃取数字证书。此法 Zeus 和 Carberp 也有用到。
→ 保护钩子
FlokiBot 通过放置一个钩子和过滤 NtProtectVirtualMemory 调用来保护它的钩子,以防止它们被累死杀毒软件复位到原函数中。无论何时,当一个程序想要改变Floki已经注入的进程的内存保护机制的时候,Floki会阻断该调用并返回STATUS_ACCESS_DENIED.
unsigned int __stdcall hook_NtProtectVirtualMemory_wrap(void *ProcessHandle, int *BaseAddress, int NumberOfBytesToProtect, int NewAccessProtection, int OldAccessProtection)
{
int retBaseAddress; // [sp+18h] [bp+Ch]@7
[...]
v11 = 0;
v5 = BaseAddress;
if ( cstm_WaitForSingleObject() && BaseAddress && ProcessHandle == GetCurrentProcess() )
{
if ( check_base_addr(*BaseAddress) )
return 0xC0000022; // STATUS_ACCESS_DENIED
RtlEnterCriticalSection_wrap(&unk_41E6E8);
v11 = 1;
}
retBaseAddress = NtProtectVirtualMemory_wrap(
ProcessHandle,
BaseAddress,
NumberOfBytesToProtect,
NewAccessProtection,
OldAccessProtection);
[...]
LABEL_18:
if ( v11 )
RtlLeaveCriticalSection_wrap(&unk_41E6E8);
return retBaseAddress;
}
→ PoS恶意软件特征:内存截取
在我的前一篇文章中,我逆向了一款非常基础的叫做 TreasureHunter 的 PoS 恶意软件。它主要用内存截取为主要手段来窃取主账号(PAN)。
像大多数PoS恶意软件,FlokiBot 通过定期读取进程内存来搜索 track2 PAN 。显然,这并不是很有效,因为你不能时刻监测内存,这样就会漏掉很多潜在的 PAN。为克服这个问题,在 Floki 把自己注入到某一个进程后,它会放置一个钩子到 NtFreeVirtualMemory 中,这样当该进程想要释放一大块内存的时候它就可以提前搜寻 track2 PAN 。用这种方法,它就不太可能会错失PAN.
int __stdcall hook_NtFreeVirtualMemory_wrap(HANDLE ProcessHandle, PVOID *BaseAddress, PSIZE_T RegionSize, ULONG FreeType)
{
PVOID v4; // ebx@1
int v5; // edi@3
RtlEnterCriticalSection_wrap(&unk_41E6E8);
v4 = 0;
if ( BaseAddress )
v4 = *BaseAddress;
v5 = NtFreeVirtualMemory_wrap(ProcessHandle, BaseAddress, RegionSize, FreeType);
if ( v5 >= 0 && !dword_41E6A8 && ProcessHandle == (HANDLE)-1 && cstm_WaitForSingleObject() )
trigger_ram_scraping((int)v4);
RtlLeaveCriticalSection_wrap(&unk_41E6E8);
return v5;
}
当 Floki 发现 track2 数据,它就会通过查看 PAN 的开头来确定发行方。在这个饱含信息量的网页,你可以找到一系列发行方的识别号:
http://www.stevemorse.org/ssn/List_of_Bank_Identification_Numbers.html.
Floki 并没有查看整个IIN (6 位),而是只检查了第一位看它是否符合下面的发行方:
3: Amex / Dinners / JP
4:VISA
5:Mastercard
6: Discover
FlokiBot identify_mii 流程:
然后,它根据 Luhn 算法查看 PAN 是否有效:
char __usercall check_mii_luhn@<al>(void *a1@<ecx>, _BYTE *a2@<esi>)
{
char result; // al@1
[...]
result = identify_mii(*a2, a1);
if ( result )
{
v7 = 0; v3 = 1; v8 = 2;
v9 = 4; v10 = 6; v11 = 8;
v12 = 1; v13 = 3; v14 = 5;
v15 = 7; v16 = 9; v4 = 0; v5 = 16;
do // Luhn Algorithm
{
v6 = a2[--v5] - '0';
if ( !v3 )
v6 = *(&v7 + v6);
v4 += v6;
v3 = v3 == 0;
}
while ( v5 );
result = v4 % 10 == 0;
}
return result;
}
→ 通讯
通讯是用 RC4 和异或混合加密的。我们用来去混淆字符串的代码可以帮我们识别下面这些明确命名的命令行:
user_flashplayer_removeuser_flashplayer_getuser_homepage_setuser_url_unblockuser_url_blockuser_certs_removeuser_certs_getuser_cookies_removeuser_cookies_getuser_executeuser_logoffuser_destroyfs_search_removefs_search_addfs_path_getbot_ddos_stopbot_ddos_startbot_httpinject_enablebot_httpinject_disablebot_bc_removebot_bc_addbot_update_exebot_updatebot_uninstallos_rebootos_shutdown
现在 FlokiBot 还没有只是 TOR,但你可以在代码中找到这个特征的一些痕迹。
→ 激活远程桌面协议(RDP)
这个 payload 想要通过寄存器来手动激活远程 Windows 桌面,然后执行控制台命令行添加一个隐形的管理员账号 test_account:test_password 。
enable_remote_desktop 函数的伪码:
void enable_remote_desktop()
{
signed int v0; // eax@3
int v1; // [sp+0h] [bp-Ch]@2
int v2; // [sp+4h] [bp-8h]@2
int v3; // [sp+8h] [bp-4h]@2
if ( byte_41E43C ) {
v2 = 0;
v1 = 4;
v3 = 0x80000002;
if ( RegOpenKeyExW_wrap(0x80000002, L"SYSTEM\\CurrentControlSet\\Control\\Terminal Server", 0, 1, &v3) )
v0 = -1;
else
v0 = cstm_RegQueryValueExW(&v3, (int)L"fDenyTSConnections", (int)&v1, (int)&v2, 4);
if ( v0 != -1 ) {
if ( v2 ) {
v3 = 0; // 0 = Enables remote desktop connections
cstm_RegSetValueExW(
0x80000002,
(int)L"SYSTEM\\CurrentControlSet\\Control\\Terminal Server",
(int)L"fDenyTSConnections",
4,
(int)&v3,
4);
}
}
}
}
自从 ATS 这种方式因为太复杂而不能编程以及太难部署后,使用远程桌面进行网络犯罪成为了新的方式。通过这种方式,它们可以获取被感染的电脑的所有权限,从而获得目标的信息,并执行欺诈任务,例如手动转移钱财。
? 最后需要注意的和哈希值
FlokiBot 是又一基于 ZeuS 的恶意软件,有些代码甚至是直接从 Carberp 拿来的。虽然如此,它的解除挂钩操作和 PoS 恶意软件特征都很有趣,值得分析。而且,它的混淆技术很简单,可以不用 AppCall,只用 IDA 脚本就可以进行静态分析。
针对最近的 FlokiBot 样本,@v0id_hunter 上传了了下面这些 SHA256.
23E8B7D0F9C7391825677C3F13FD2642885F6134636E475A3924BA5BDD1D4852997841515222dbfa65d1aea79e9e6a89a0142819eaeec3467c31fa169e57076af778ca5942d3b762367be1fd85cf7add557d26794fad187c4511b3318aff5cfd......省略
感谢阅读。
阅读原文http://bbs.pediy.com/thread-216639.htm,查看完整代码~
本文由 看雪翻译小组 lumou 编译,来源 Arnaud Delmas
? 往期热门内容推荐
渗透测试 Node.js 应用
TI(德州仪器)TMS320C674x 逆向分析方法
Firefox 中一个 Cross-mmap 溢出的利用
UPDATE 查询中的 SQL 注入
绕过补丁实现欺骗地址栏和恶意软件警告
FlokiBot 银行木马详细分析
看雪论坛:http://bbs.pediy.com/
微信公众号 ID:ikanxue
微博:看雪安全
投稿、合作:www.kanxue.com
相关推荐
- “版本末期”了?下周平衡补丁!国服最强5套牌!上分首选
-
明天,酒馆战棋就将迎来大更新,也聊了很多天战棋相关的内容了,趁此机会,给兄弟们穿插一篇构筑模式的卡组推荐!老规矩,我们先来看10职业胜率。目前10职业胜率排名与一周前基本类似,没有太多的变化。平衡补丁...
- VS2017 C++ 程序报错“error C2065:“M_PI”: 未声明的标识符"
-
首先,程序中头文件的选择,要选择头文件,在文件中是没有对M_PI的定义的。选择:项目——>”XXX属性"——>配置属性——>C/C++——>预处理器——>预处理器定义,...
- 东营交警实名曝光一批酒驾人员名单 88人受处罚
-
齐鲁网·闪电新闻5月24日讯酒后驾驶是对自己和他人生命安全极不负责的行为,为守护大家的平安出行路,东营交警一直将酒驾作为重点打击对象。5月23日,东营交警公布最新一批饮酒、醉酒名单。对以下驾驶人醉酒...
- Qt界面——搭配QCustomPlot(qt platform)
-
这是我第一个使用QCustomPlot控件的上位机,通过串口精确的5ms发送一次数据,再将读取的数据绘制到图表中。界面方面,尝试卡片式设计,外加QSS简单的配了个色。QCustomPlot官网:Qt...
- 大话西游2分享赢取种族坐骑手办!PK趣闻录由你书写
-
老友相聚,仗剑江湖!《大话西游2》2021全民PK季4月激燃打响,各PK玩法鏖战齐开,零门槛参与热情高涨。PK季期间,不仅各种玩法奖励丰厚,参与PK趣闻录活动,投稿自己在PK季遇到的趣事,还有机会带走...
- 测试谷歌VS Code AI 编程插件 Gemini Code Assist
-
用ClaudeSonnet3.7的天气测试编码,让谷歌VSCodeAI编程插件GeminiCodeAssist自动编程。生成的文件在浏览器中的效果如下:(附源代码)VSCode...
- 顾爷想知道第4.5期 国服便利性到底需优化啥?
-
前段时间DNF国服推出了名为“阿拉德B计划”的系列改版计划,截至目前我们已经看到了两项实装。不过关于便利性上,国服似乎还有很多路要走。自从顾爷回归DNF以来,几乎每天都在跟我抱怨关于DNF里面各种各样...
- 掌握Visual Studio项目配置【基础篇】
-
1.前言VisualStudio是Windows上最常用的C++集成开发环境之一,简称VS。VS功能十分强大,对应的,其配置系统较为复杂。不管是对于初学者还是有一定开发经验的开发者来说,捋清楚VS...
- 还嫌LED驱动设计套路深?那就来看看这篇文章吧
-
随着LED在各个领域的不同应用需求,LED驱动电路也在不断进步和发展。本文从LED的特性入手,推导出适合LED的电源驱动类型,再进一步介绍各类LED驱动设计。设计必读:LED四个关键特性特性一:非线...
- Visual Studio Community 2022(VS2022)安装图文方法
-
直接上步骤:1,首先可以下载安装一个VisualStudio安装器,叫做VisualStudioinstaller。这个安装文件很小,很快就安装完成了。2,打开VisualStudioins...
- Qt添加MSVC构建套件的方法(qt添加c++11)
-
前言有些时候,在Windows下因为某些需求需要使用MSVC编译器对程序进行编译,假设我们安装Qt的时候又只是安装了MingW构建套件,那么此时我们该如何给现有的Qt添加一个MSVC构建套件呢?本文以...
- Qt为什么站稳c++GUI的top1(qt c)
-
为什么现在QT越来越成为c++界面编程的第一选择,从事QT编程多年,在这之前做C++界面都是基于MFC。当时为什么会从MFC转到QT?主要原因是MFC开发界面想做得好看一些十分困难,引用第三方基于MF...
- qt开发IDE应该选择VS还是qt creator
-
如果一个公司选择了qt来开发自己的产品,在面临IDE的选择时会出现vs或者qtcreator,选择qt的IDE需要结合产品需求、部署平台、项目定位、程序猿本身和公司战略,因为大的软件产品需要明确IDE...
- Qt 5.14.2超详细安装教程,不会来打我
-
Qt简介Qt(官方发音[kju:t],音同cute)是一个跨平台的C++开库,主要用来开发图形用户界面(GraphicalUserInterface,GUI)程序。Qt是纯C++开...
- Cygwin配置与使用(四)——VI字体和颜色的配置
-
简介:VI的操作模式,基本上VI可以分为三种状态,分别是命令模式(commandmode)、插入模式(Insertmode)和底行模式(lastlinemode),各模式的功能区分如下:1)...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- “版本末期”了?下周平衡补丁!国服最强5套牌!上分首选
- VS2017 C++ 程序报错“error C2065:“M_PI”: 未声明的标识符"
- 东营交警实名曝光一批酒驾人员名单 88人受处罚
- Qt界面——搭配QCustomPlot(qt platform)
- 大话西游2分享赢取种族坐骑手办!PK趣闻录由你书写
- 测试谷歌VS Code AI 编程插件 Gemini Code Assist
- 顾爷想知道第4.5期 国服便利性到底需优化啥?
- 掌握Visual Studio项目配置【基础篇】
- 还嫌LED驱动设计套路深?那就来看看这篇文章吧
- Visual Studio Community 2022(VS2022)安装图文方法
- 标签列表
-
- wireshark怎么抓包 (75)
- qt sleep (64)
- cs1.6指令代码大全 (55)
- factory-method (60)
- sqlite3_bind_blob (52)
- hibernate update (63)
- c++ base64 (70)
- nc 命令 (52)
- wm_close (51)
- epollin (51)
- sqlca.sqlcode (57)
- lua ipairs (60)
- tv_usec (64)
- 命令行进入文件夹 (53)
- postgresql array (57)
- statfs函数 (57)
- .project文件 (54)
- lua require (56)
- for_each (67)
- c#工厂模式 (57)
- wxsqlite3 (66)
- dmesg -c (58)
- fopen参数 (53)
- tar -zxvf -c (55)
- 速递查询 (52)