阅读时间: 8-10 分钟
前置知识: 理解进程注入技术、Windows API 基础、数字签名概念
学习目标: 掌握模块归属检测技术,实现基于数字签名的进程防御系统
📺 配套视频教程
本文配套视频教程已发布在 B 站,建议结合视频学习效果更佳:
💡 提示: 点击视频右下角可全屏观看,建议配合文章食用!
视频链接: https://www.bilibili.com/video/BV1dtyaBbErC/
06-防御篇-检测模块归属(博客版)
承接上节:攻击者的致命疏忽
上节课,攻击者通过 APC 注入成功绕过了我们的 ETW 监控:
- ✅ 绕过 ETW 监控:不创建新线程,复用现有线程的 APC 队列
- ✅ 利用 Alertable 状态:等待线程进入可警告状态后执行 Shellcode
- ✅ 隐蔽执行:ETW 事件中看不到远程线程创建的痕迹
但是,攻击者忽略了一个关键问题:无论注入方式多么隐蔽,恶意代码最终要以某种形式存在于目标进程的内存空间中。
作为防御者,我们的思路是:不监控注入行为,而是检查进程内存中已经存在的模块,通过模块归属来判断是否存在恶意注入。
防御者的思考:为什么需要模块归属检测
传统检测方法的局限性:
- 线程起始地址检测:只能检测直接使用
LoadLibrary的注入 - ETW 事件监控:只能检测创建新线程的注入行为
- 行为监控:攻击者可以通过各种方式绕过行为记录
模块归属检测的优势:
- 技术无关性:不管注入方式如何(远程线程、APC、线程劫持等),恶意代码模块都会出现在进程空间
- 事后检测能力:即使注入过程被绕过,仍能发现已加载的恶意模块
- 简单可靠:基于文件路径的黑白名单判断,逻辑清晰,误报率低
核心原理讲解
模块归属检测的基本概念
每个 Windows 进程都有一个模块列表,记录了所有加载到该进程地址空间的 DLL 和 EXE 文件。我们可以通过枚举这些模块,检查它们的可信度来判断是否存在恶意注入。
路径检测的局限性:
通常系统模块都在 C:WindowsSystem32 目录下,但只检测路径是很不可靠的:
- 路径伪装:攻击者可以将恶意DLL放到系统目录下伪装成系统模块
- 无法识别可信第三方软件:其他厂商的合法软件不在系统目录
- 准确性有限:单纯路径检查无法提供可靠的可信度判断
更好的解决方案:数字签名验证
数字签名是现代软件可信度判断的标准:
- Microsoft 官方签名:系统模块的可信度保证
- 第三方厂商签名:合法商业软件的身份认证
- 无签名模块:极有可能是恶意代码或未经验证的软件
基于数字签名的模块分类:
进程模块分类:
├── 有数字签名的模块 (Trusted Modules)
│ ├── Microsoft 签名的系统模块
│ ├── 其他可信厂商签名的模块
│ └── 有效数字签名的任意模块
├── 白名单模块 (Whitelisted Modules)
│ └── 我们自己的程序文件
└── 可疑模块 (Suspicious Modules)
└── 无数字签名或签名无效的模块
检测逻辑的核心思想
核心假设:
- 可信模块:具有有效数字签名的模块,包括系统模块和可信第三方模块
- 白名单模块:我们自己的程序,明确可信(即使没有签名)
- 可疑模块:没有数字签名或签名验证失败的模块,需要重点关注
判断流程:
枚举进程模块 → 验证数字签名 → 模块分类 → 输出可疑模块
数字签名验证的实际应用
在实际应用中,数字签名也常常被用于验证软件的可信度:
1. 游戏反外挂系统
外挂程序通常没有有效的数字签名,游戏客户端通过验证所有加载模块的签名来检测和阻止外挂注入。
2. 杀毒软件的自我保护
杀毒软件监控自身进程,阻止未签名的恶意模块注入,保护核心功能不被破坏。
动手实现:构建实时模块监控器
从 main 函数开始
首先创建程序的入口点,建立实时监控的基本框架:
#include <windows.h>
#include <psapi.h>
#include <iostream>
#include <string>
#include <algorithm>
#include <wintrust.h>
#pragma comment(lib, "psapi.lib")
#pragma comment(lib, "wintrust.lib")
// 函数声明(我们先声明函数,稍后实现具体内容)
void DetectSuspiciousModules(DWORD processId);
/**
* @brief 程序入口
* @return 程序退出码
*/
int main()
{
// 获取并打印当前进程ID(只需要获取一次)
DWORD currentPid = GetCurrentProcessId();
// 无限循环检测
while (true)
{
// 执行检测逻辑
DetectSuspiciousModules(currentPid);
// 等待1秒后自动重新检测
Sleep(1000);
// 清屏并重新开始
system("cls");
} // 无限循环
return 0;
}知识点讲解:
GetCurrentProcessId():获取当前进程的唯一标识符while(true):建立无限循环,实现持续监控Sleep(1000):暂停1秒,控制检测频率system("cls"):清空控制台屏幕#pragma comment(lib, ...):告诉链接器自动链接指定的库- 函数声明:在实际编程中,当我们需要使用还未实现的函数时,通常先声明函数名,稍后再实现
第一步:获取进程句柄
// ========== 第一步:获取进程句柄 ==========
HANDLE hProcess = GetCurrentProcess();为什么使用 GetCurrentProcess()?
- 返回当前进程的伪句柄(特殊值 -1)
- 伪句柄不需要调用
CloseHandle()关闭 - 如果要检测其他进程,需要使用
OpenProcess()并处理权限问题
第二步:枚举进程模块
// ========== 第二步:枚举进程模块 ==========
HMODULE hModules[1024];
DWORD cbNeeded;
if (!EnumProcessModules(hProcess, hModules, sizeof(hModules), &cbNeeded))
{
std::cout << "获取模块列表失败rn";
return;
}
DWORD moduleCount = cbNeeded / sizeof(HMODULE);
std::cout << "当前进程共有 " << moduleCount << " 个模块rnrn";EnumProcessModules API 的使用:
hProcess:进程句柄hModules:模块句柄数组(输出)sizeof(hModules):数组大小&cbNeeded:实际使用的字节数(输出)- 1024 是经验值,一般进程不会加载超过这个数量的模块
第三步:初始化统计变量
// ========== 第三步:初始化统计变量 ==========
int suspiciousCount = 0;
int systemCount = 0;
int whitelistCount = 0;为什么设计三类统计变量?
systemCount:有数字签名的可信模块whitelistCount:我们自己的程序文件suspiciousCount:无签名且不在白名单中的可疑模块- “可疑”比”恶意”更准确,体现了安全检测的谨慎态度
第四步:遍历检测每个模块
// ========== 第四步:遍历检测每个模块 ==========
for (DWORD i = 0; i < moduleCount; i++)
{
// 获取模块完整路径
char modulePath[MAX_PATH];
if (GetModuleFileNameExA(hProcess, hModules[i], modulePath, MAX_PATH))
{
std::string fullPath = modulePath;
// 从完整路径中提取文件名(用于白名单检查)
size_t lastSlash = fullPath.find_last_of("\");
std::string fileName = (lastSlash != std::string::npos) ?
fullPath.substr(lastSlash + 1) : fullPath;
// 模块分类判断:数字签名 > 白名单 > 可疑
if (VerifyDigitalSignature(fullPath))
{
// 有数字签名的模块视为可信系统模块
systemCount++;
}
else if (IsWhitelistDLL(fileName))
{
// 我们自己的程序文件,即使没有签名也是可信的
whitelistCount++;
}
else
{
// 无签名且不在白名单中的模块,标记为可疑
suspiciousCount++;
std::cout << "🚨 [可疑模块] " << fileName << "rn";
std::cout << " 路径: " << fullPath << "rn";
std::cout << " 基址: 0x" << std::hex << (uintptr_t)hModules[i] << std::dec << "rn";
// 获取模块的详细信息(大小)
MODULEINFO moduleInfo;
if (GetModuleInformation(hProcess, hModules[i], &moduleInfo, sizeof(moduleInfo)))
{
std::cout << " 大小: " << moduleInfo.SizeOfImage << " 字节rn";
}
std::cout << "rn";
}
}
}为什么需要两种路径形式?
- 完整路径:数字签名验证需要检查文件的完整位置
- 文件名:白名单检查只需要文件名,更灵活
三层分类逻辑的优先级设计:
VerifyDigitalSignature():第一层,最客观的可信标准IsWhitelistDLL():第二层,我们的程序,主观可信else:第三层,其他所有情况,需要人工判断
为什么用 else if 链?
- 提高效率:满足第一个条件后不再检查后续条件
- 逻辑清晰:明确的优先级关系
- 避免重复:一个模块只属于一个类别
第五步:输出统计结果
// ========== 第五步:输出统计结果 ==========
std::cout << "=== 检测结果 ===rn";
std::cout << "系统模块: " << systemCount << " 个(已过滤)rn";
std::cout << "白名单模块: " << whitelistCount << " 个rn";
std::cout << "可疑模块: " << suspiciousCount << " 个rnrn";
if (suspiciousCount > 0)
{
std::cout << "⚠️ 检测到可疑模块!可能遭受注入攻击!rn";
std::cout << "建议检查这些模块是否为您手动加载的DLL。rn";
}
else
{
std::cout << "✓ 未检测到可疑模块,进程安全。rn";
}
std::cout << "rn1秒后自动重新检测,Ctrl+C 退出程序...rn";第六步:实现数字签名验证功能
在主检测函数中,我们需要VerifyDigitalSignature函数来判断模块是否有数字签名。这是模块可信度判断的核心功能:
/**
* @brief 验证文件数字签名
* @param filePath 文件路径
* @return 签名验证成功返回TRUE,失败返回FALSE
*/
BOOL VerifyDigitalSignature(const std::string& filePath)
{
// 检查文件是否存在
DWORD fileAttr = GetFileAttributesA(filePath.c_str());
if (fileAttr == INVALID_FILE_ATTRIBUTES)
{
return FALSE;
}
// 转换为宽字符(Windows API通常需要Unicode)
std::wstring widePath(filePath.begin(), filePath.end());
// 设置文件信息结构
WINTRUST_FILE_INFO fileInfo = {0};
fileInfo.cbStruct = sizeof(WINTRUST_FILE_INFO);
fileInfo.pcwszFilePath = widePath.c_str();
// 设置WinTrust验证数据结构
WINTRUST_DATA winTrustData = {0};
winTrustData.cbStruct = sizeof(WINTRUST_DATA);
winTrustData.pPolicyCallbackData = NULL;
winTrustData.pSIPClientData = NULL;
winTrustData.dwUIChoice = 2; // WTD_UI_NONE - 禁用UI提示
winTrustData.fdwRevocationChecks = 0; // WTD_REVOKE_NONE - 不检查吊销
winTrustData.dwUnionChoice = 1; // WTD_CHOICE_FILE - 验证文件
winTrustData.dwStateAction = 1; // WTD_STATEACTION_VERIFY - 执行验证
winTrustData.hWVTStateData = NULL;
winTrustData.pwszURLReference = NULL;
winTrustData.dwProvFlags = 0x1000; // WTD_SAFER_FLAG - 安全模式
winTrustData.pSignatureSettings = NULL;
winTrustData.pFile = &fileInfo;
// 定义Windows验证策略GUID
GUID policyGUID = {0xaac56b, 0xcd44, 0x11d0, {0x8c, 0xc2, 0x00, 0xc0, 0x4f, 0xc2, 0x95, 0xee}};
// 执行数字签名验证
LONG result = WinVerifyTrust(NULL, &policyGUID, &winTrustData);
// 返回验证结果:ERROR_SUCCESS 表示验证成功
return (result == ERROR_SUCCESS);
}知识点讲解:
GetFileAttributesA:检查文件是否存在,避免验证不存在的文件WinVerifyTrust:Windows核心的数字签名验证APIWINTRUST_FILE_INFO:描述要验证的文件信息WINTRUST_DATA:控制验证行为的详细参数- 关键配置参数说明:
dwUIChoice = 2:禁用所有用户界面提示fdwRevocationChecks = 0:不检查证书吊销状态(提高速度)dwProvFlags = 0x1000:启用更安全的验证模式
第七步:实现白名单检查功能
为了减少误报,我们需要实现IsWhitelistDLL函数来识别我们自己的程序文件:
/**
* @brief 检查是否为白名单DLL(我们的程序)
* @param dllName DLL名称
* @return 如果是白名单DLL返回TRUE
*/
BOOL IsWhitelistDLL(const std::string& dllName)
{
// 转换为小写进行比较,确保大小写不敏感
std::string lowerDllName = dllName;
std::transform(lowerDllName.begin(), lowerDllName.end(), lowerDllName.begin(), ::tolower);
// 定义白名单数组(我们的程序文件)
const std::string whitelistDLLs[] = {
"uxtheme.dll",
"06-防御篇-检测模块归属.exe"
};
// 遍历白名单数组进行检查
for (const auto& whitelistDLL : whitelistDLLs)
{
if (lowerDllName == whitelistDLL)
{
return TRUE;
}
}
return FALSE;
}知识点讲解:
std::transform:C++标准库算法,用于字符大小写转换::tolower:C标准库函数,将字符转换为小写- 白名单机制:预先定义的可信文件列表,避免误报
- 大小写不敏感比较:确保匹配的准确性
核心步骤的深度解析
获取进程句柄的考虑
在实际编程中,要访问进程信息首先需要进程句柄,这就像要进入房间需要钥匙一样。
GetCurrentProcess()返回当前进程的伪句柄(特殊值 -1)- 伪句柄不需要调用
CloseHandle()关闭 - 如果要检测其他进程,需要使用
OpenProcess()并处理权限问题
枚举模块的完整流程
“获取模块列表”是一个完整的业务目标,包含:
- 准备存储空间:
HMODULE hModules[1024] - 调用系统API:
EnumProcessModules - 处理返回结果:检查返回值和错误处理
- 计算有效数据:
DWORD moduleCount = cbNeeded / sizeof(HMODULE)
关键技术理解:
- 1024 是经验值,一般进程不会加载超过这个数量的模块
- 如果不够大,
cbNeeded会告诉我们实际需要多少空间 - 错误处理要检查返回值而不是依赖异常
分类统计的设计思路
我们为什么设计三类而不是简单的两类(可信/不可信)?
- 数字签名模块:客观可信,系统标准
- 白名单模块:主观可信,我们自己的程序
- 可疑模块:需要人工判断的其他模块
变量命名的含义:
suspiciousCount:为什么不是badCount?- 体现了安全检测的谨慎态度
- 我们的检测方法可能有误报
- “可疑”比”恶意”更准确
遍历检测的核心逻辑
这是最复杂的步骤,包含了完整的检测逻辑:
路径获取与处理:
std::string fullPath = modulePath; // 完整路径,用于数字签名验证
std::string fileName = ...; // 文件名,用于白名单检查- 完整路径:数字签名验证需要检查文件的完整位置
- 文件名:白名单检查只需要文件名,更灵活
三层分类逻辑的优先级设计:
if (VerifyDigitalSignature(fullPath)) {
// 第一层:最客观的可信标准
}
else if (IsWhitelistDLL(fileName)) {
// 第二层:我们的程序,主观可信
}
else {
// 第三层:其他所有情况,需要人工判断
}为什么用 else if 链而不是独立 if?
- 提高效率:满足第一个条件后不再检查后续条件
- 逻辑清晰:明确的优先级关系
- 避免重复:一个模块只属于一个类别
完整代码
#include <windows.h>
#include <psapi.h>
#include <iostream>
#include <string>
#include <algorithm>
#include <wintrust.h>
#pragma comment(lib, "psapi.lib")
#pragma comment(lib, "wintrust.lib")
/**
* @brief 验证文件数字签名
* @param filePath 文件路径
* @return 签名验证成功返回TRUE,失败返回FALSE
*/
BOOL VerifyDigitalSignature(const std::string& filePath)
{
// 检查文件是否存在
DWORD fileAttr = GetFileAttributesA(filePath.c_str());
if (fileAttr == INVALID_FILE_ATTRIBUTES)
{
return FALSE;
}
// 转换为宽字符
std::wstring widePath(filePath.begin(), filePath.end());
// 设置文件信息
WINTRUST_FILE_INFO fileInfo = {0};
fileInfo.cbStruct = sizeof(WINTRUST_FILE_INFO);
fileInfo.pcwszFilePath = widePath.c_str();
// 设置WinTrust数据
WINTRUST_DATA winTrustData = {0};
winTrustData.cbStruct = sizeof(WINTRUST_DATA);
winTrustData.pPolicyCallbackData = NULL;
winTrustData.pSIPClientData = NULL;
winTrustData.dwUIChoice = 2; // WTD_UI_NONE - 禁用UI提示
winTrustData.fdwRevocationChecks = 0; // WTD_REVOKE_NONE - 不检查吊销
winTrustData.dwUnionChoice = 1; // WTD_CHOICE_FILE - 验证文件
winTrustData.dwStateAction = 1; // WTD_STATEACTION_VERIFY - 执行验证
winTrustData.hWVTStateData = NULL;
winTrustData.pwszURLReference = NULL;
winTrustData.dwProvFlags = 0x1000; // WTD_SAFER_FLAG - 安全模式
winTrustData.pSignatureSettings = NULL;
winTrustData.pFile = &fileInfo;
// 定义策略GUID
GUID policyGUID = {0xaac56b, 0xcd44, 0x11d0, {0x8c, 0xc2, 0x00, 0xc0, 0x4f, 0xc2, 0x95, 0xee}};
// 执行验证
LONG result = WinVerifyTrust(NULL, &policyGUID, &winTrustData);
// 返回验证结果
return (result == ERROR_SUCCESS);
}
/**
* @brief 检查是否为白名单DLL(我们的程序)
* @param dllName DLL名称
* @return 如果是白名单DLL返回TRUE
*/
BOOL IsWhitelistDLL(const std::string& dllName)
{
// 转换为小写进行比较
std::string lowerDllName = dllName;
std::transform(lowerDllName.begin(), lowerDllName.end(), lowerDllName.begin(), ::tolower);
// 我们的程序白名单
const std::string whitelistDLLs[] = {
"uxtheme.dll",
"06-防御篇-检测模块归属.exe"
};
for (const auto& whitelistDLL : whitelistDLLs)
{
if (lowerDllName == whitelistDLL)
{
return TRUE;
}
}
return FALSE;
}
/**
* @brief 检测进程中的可疑模块
* @param processId 进程ID(用于显示)
*/
void DetectSuspiciousModules(DWORD processId)
{
std::cout << "=== 简单模块检测器(教学版) ===rn";
std::cout << "当前进程ID: " << processId << "rn";
std::cout << "正在检测进程中的可疑模块...rnrn";
// === 第一步:获取进程句柄 ===
HANDLE hProcess = GetCurrentProcess();
// === 第二步:枚举所有模块 ===
HMODULE hModules[1024];
DWORD cbNeeded;
if (!EnumProcessModules(hProcess, hModules, sizeof(hModules), &cbNeeded))
{
std::cout << "获取模块列表失败rn";
return;
}
DWORD moduleCount = cbNeeded / sizeof(HMODULE);
std::cout << "当前进程共有 " << moduleCount << " 个模块rnrn";
// === 第三步:初始化统计变量 ===
int suspiciousCount = 0;
int systemCount = 0;
int whitelistCount = 0;
// === 第四步:遍历检测每个模块 ===
for (DWORD i = 0; i < moduleCount; i++)
{
// 获取模块完整路径
char modulePath[MAX_PATH];
if (GetModuleFileNameExA(hProcess, hModules[i], modulePath, MAX_PATH))
{
std::string fullPath = modulePath;
// 从完整路径中提取文件名(用于白名单检查)
size_t lastSlash = fullPath.find_last_of("\");
std::string fileName = (lastSlash != std::string::npos) ?
fullPath.substr(lastSlash + 1) : fullPath;
// 模块分类判断:数字签名 > 白名单 > 可疑
if (VerifyDigitalSignature(fullPath))
{
// 有数字签名的模块视为可信系统模块
systemCount++;
}
else if (IsWhitelistDLL(fileName))
{
// 我们自己的程序文件,即使没有签名也是可信的
whitelistCount++;
}
else
{
// 无签名且不在白名单中的模块,标记为可疑
suspiciousCount++;
std::cout << "🚨 [可疑模块] " << fileName << "rn";
std::cout << " 路径: " << fullPath << "rn";
std::cout << " 基址: 0x" << std::hex << (uintptr_t)hModules[i] << std::dec << "rn";
// 获取模块的详细信息(大小)
MODULEINFO moduleInfo;
if (GetModuleInformation(hProcess, hModules[i], &moduleInfo, sizeof(moduleInfo)))
{
std::cout << " 大小: " << moduleInfo.SizeOfImage << " 字节rn";
}
std::cout << "rn";
}
}
}
// 输出统计结果
std::cout << "=== 检测结果 ===rn";
std::cout << "系统模块: " << systemCount << " 个(已过滤)rn";
std::cout << "白名单模块: " << whitelistCount << " 个rn";
std::cout << "可疑模块: " << suspiciousCount << " 个rnrn";
if (suspiciousCount > 0)
{
std::cout << "⚠️ 检测到可疑模块!可能遭受注入攻击!rn";
std::cout << "建议检查这些模块是否为您手动加载的DLL。rn";
}
else
{
std::cout << "✓ 未检测到可疑模块,进程安全。rn";
}
std::cout << "rn1秒后自动重新检测,Ctrl+C 退出程序...rn";
}
/**
* @brief 程序入口
* @return 程序退出码
*/
int main()
{
// 获取并打印当前进程ID(只需要获取一次)
DWORD currentPid = GetCurrentProcessId();
// 无限循环检测
while (true)
{
// 执行检测逻辑
DetectSuspiciousModules(currentPid);
// 等待1秒后自动重新检测
Sleep(1000);
// 清屏并重新开始
system("cls");
} // 无限循环
return 0;
}检测效果演示
模块检测器运行效果:

如图所示,程序能够成功检测到:
- ✅ 正常进程显示所有加载的模块分类统计
- ✅ 系统模块(已过滤)和白名单模块数量清晰
- ✅ 无可疑模块时显示安全状态提示
- ✅ 每秒自动刷新,实时监控进程模块变化
优势与局限
优势
- 技术无关性:不依赖特定的注入技术检测,任何形式的模块加载都能发现
- 实时监控能力:每秒自动检测,能快速发现模块变化
- 签名验证可靠:基于数字签名的可信度判断,有效防止路径伪装攻击
- 输出清晰直观:分类明确,重点突出可疑模块
- 教学友好:代码结构清晰,便于理解和扩展
局限
- 签名依赖性:完全依赖数字签名,未签名的合法模块可能被误报
- 静态白名单:白名单需要预先配置,不够灵活
- 无法识别注入时机:只能检测已加载的模块,无法知道何时注入的
- 内存模块检测有限:对于不通过文件加载的内存模块检测能力有限
- 签名吊销检查:为提高验证速度,未进行实时的签名吊销状态检查
教学版简化说明
当前实现为教学简化版,实际生产环境中还应考虑:
- 数字签名验证:验证模块的数字签名和发布者信息
- 动态白名单管理:支持动态添加和修改白名单
- 更复杂的路径分析:识别伪装的系统路径
- 模块完整性检查:检查模块是否被修改
- 网络行为监控:结合网络行为进行综合判断
防御建议
基础防御措施
- 定期模块检查:在关键业务流程中定期检查进程模块列表
- 白名单维护:建立完善的白名单策略,明确允许加载的模块
- 路径规范:将应用程序放置在规范的目录中,避免路径混淆
进阶防御策略
- 多层检测:结合线程检测、ETW监控、模块检测形成多层次防御体系
- 行为分析:不仅检查模块存在,还要分析模块的行为特征
- 基线建立:为正常应用建立模块基线,发现偏差时告警
实战应用场景
- 游戏反外挂:实时监控游戏进程,检测第三方注入模块
- 安全软件:作为进程保护的一部分,防止恶意代码注入
- 企业安全:监控关键业务进程,发现异常模块加载
下节课预告:攻击者的新思路 – Module Stomping
虽然我们的模块归属检测很有效,但攻击者总能找到新的绕过方法。下节课,作为攻击者,我们将学习一种更加隐蔽的注入技术:Module Stomping(模块践踏)。
Module Stomping 的核心思想:
- 不创建新的模块,而是”践踏”现有的合法模块
- 将恶意代码加载到已存在的系统模块内存空间中
- 从模块列表角度看,一切正常,没有新增模块
这种技术能够完美绕过我们的模块归属检测,因为它根本不会在进程模块列表中留下痕迹。我们将学习如何:
- 选择目标系统模块
- 在合法模块的内存空间中注入恶意代码
- 修改模块内存保护属性
- 实现代码执行
Module Stomping 代表了注入技术的一个重要发展方向:从”添加”到”伪装”的转变。这要求防御者必须从模块检查转向内存完整性检查。
下节课关键词: Module Stomping、内存伪装、代码注入、绕过检测
互动时间
💬 评论区讨论:
思维扩展:除了路径检查,还有哪些方法可以判断模块的可信度?
实战思考:如果恶意DLL将自身伪装成系统DLL(比如放在System32目录下),我们的检测器还能有效工作吗?应该如何加强检测?
技术探讨:实时监控虽然能快速发现问题,但也会消耗系统资源。你能在性能和检测效果之间找到平衡点吗?比如设计一个智能的检测频率调节机制?
扩展应用:这个检测原理能否应用到其他领域?比如检测注册表项、服务、计划任务等系统组件的异常?
🔥 下期预告关键词: Module Stomping、内存伪装、现有模块利用
