VirtualProtect失败或触发ACCESS_VIOLATION的根本原因是目标内存页未标记为可写,即使已设PAGE_EXECUTE_READ,默认仍为只读执行;必须先用VirtualProtect改为PAGE_READWRITE解密,再改回PAGE_EXECUTE_READ执行。

动态解密代码段时,为什么 VirtualProtect 失败或触发异常?
常见现象是调用 VirtualProtect 后返回 FALSE,或紧接着访问解密后代码就崩在 ACCESS_VIOLATION。根本原因不是权限没设对,而是目标内存页未被标记为可写——哪怕你刚用 VirtualAlloc 分配了 PAGE_EXECUTE_READ,它默认仍是只读执行(no write)。必须分两步:先用 VirtualProtect 改成 PAGE_READWRITE 解密,再改回 PAGE_EXECUTE_READ 执行。
实操建议:
- 解密前务必检查原始页面属性,用
VirtualQuery确认基址、大小、当前保护标志 - 不要跨页操作:若解密区域横跨多个内存页,需对每页单独调用
VirtualProtect - Windows 10+ 启用 CFG 或 HVCI 时,
PAGE_EXECUTE_READ可能被拦截,临时改用PAGE_EXECUTE_READWRITE(仅调试期,上线前必须移除)
如何让 OEP 不出现在 PE 头的 AddressOfEntryPoint 中?
OEP 被静态扫描器直接定位,本质是因为 PE 加载器严格按 OptionalHeader.AddressOfEntryPoint 跳转。绕过它的核心不是“隐藏”,而是“转移控制流”:把真实入口逻辑藏在壳代码里,再让 PE 头指向壳的初始化函数(即 IAT 修复/解密逻辑),而非最终业务代码。
关键点:
立即学习“C++免费学习笔记(深入)”;
- 修改
AddressOfEntryPoint指向壳代码首地址(通常在 .text 或新节中),该地址需满足:已映射、有执行权限、不依赖重定位(或自行处理) - 壳代码必须在跳转到真实 OEP 前完成 IAT 修复、TLS 回调抑制、反调试检测等,否则真实 OEP 无法运行
- 不要用跳转指令硬编码真实 OEP 地址——加壳后原 OEP RVA 已失效;应通过相对偏移 + 基址重算,或从导入表/重定位表中动态推导
用 C++ 实现解密循环时,memcpy 和手动字节循环哪个更安全?
二者都可能被静态分析识别为“可疑数据搬运”。但 memcpy 更危险:编译器优化后常内联为 rep movsb 或向量化指令,特征明显;而手动循环(尤其带异或/查表/伪随机密钥)更易混淆。
实操建议:
- 避免使用
memcpy或std::copy解密核心代码段;手写循环,并混入无副作用计算(如空转计数、条件分支扰动) - 密钥不要硬编码为常量数组,从 PE 头校验和、模块句柄哈希、或系统时间低 8 位动态生成
- 解密后立即清零密钥缓冲区(用
RtlSecureZeroMemory,而非memset),防止内存 dump 抓取
壳代码注入到目标进程后,为什么 TLS 回调导致解密失败?
TLS 回调函数在主线程进入 OEP 前自动执行,若壳代码未处理,它会以原始(加密态)镜像调用 TLS 函数,大概率崩溃。这不是 bug,是 Windows 加载机制的必然行为。
解决路径只有两条:
- 在壳代码中遍历
IMAGE_TLS_DIRECTORY,将所有 TLS 回调地址重定向到壳提供的 stub(该 stub 先解密再跳转原回调) - 更彻底的做法:在内存中 patch TLS 目录的
AddressOfCallBacks字段为NULL,并确保壳自身不注册 TLS 回调(否则自己也会被跳过) - 注意:TLS 回调地址是 RVA,需加上模块基址转换为 VA;且 patch 前必须用
VirtualProtect开启写权限
真正难的不是解密或跳转,而是所有这些操作必须在不破坏 SEH 链、不干扰结构化异常分发、不触发 ETW 采样点的前提下完成。稍有不慎,壳本身就成了 crash 的根源。


















