VirtualProtect、VirtualLock、VirtualUnlock

本文介绍了VirtualProtect函数的使用方法,它可以改变指定虚拟内存页的保护属性,包括只读、可执行等。此外还介绍了如何使用VirtualLock和VirtualUnlock函数锁定和解锁内存页。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

VirtualProtect改变指定虚拟内存分页的保护属性,函数原型如下:

BOOL VirtualProtect(

    LPVOID lpAddress,

    SIZE_T  dwSize,

    DWORD flNewProtect,

    PDWORD lpflOldProtect);

参数:
lpAddress: 输入参数,虚拟内存基地址。

dwSize: 输入参数,内存大小。

flNewProtect: 输入参数,需要设置的新的属性,具体为PAGE_READONLY、PAGE_EXECUTE、PAGE_EXECUTE_READ、PAGE_GUARD、PAGE_NOACCESS和PAGE_NOCACHE。

lpflOldProtect: 输出参数,指向保存原保护属性值的DWORD变量,可以为NULL。

返回值:
返回BOOL值,表示是否成功,可以使用GetLastError函数获取错误代码。

VirtualLock(

    LPVOID lpAddress,

    SIZE_T dwSize);

VirtualUnlock(

    LPVOID lpAddress,

    SIZE_T dwSize);

将页面锁定在物理内存中使用VirtualLock函数。这样页面将不会被交换到硬盘上,提高了访问的效率,如果要解除它,可以使用VirtualUnLock。


---------------------------------------------------------------------------------------------------------------------------------------------------------------


  VirtualProtect 方法
  当做对应之 Win32 函数的逻辑包装函数。VirtualProtect 的 Win32 实作会在呼叫处理序的虚拟位址空间里,变更认可页面区域上的保护。
  BOOL VirtualProtect(
  LPVOID lpAddress, // 目标地址起始位置
  DWORD dwSize, // 大小
  DWORD flNewProtect, // 请求的保护方式
  PDWORD lpflOldProtect // 保存老的保护方式
  );
  --------------------------------------------------------------------------------------------------------------------------------
  BOOL WINAPI VirtualProtect( __in LPVOID lpAddress, __in SIZE_T dwSize, __in DWORD flNewProtect, __out PDWORD lpflOldProtect );参数
  lpAddress
  [in] 指标,指向要变更保护属性的内存基址。
  dwSize
  [in] 要变更的记忆体分页区域的大小 (单位是字节)。但是需要注意,页面边界2字节的内存属性更改,有可能导致改变2个页的属性同时被改变
  flNewProtect
  [in] 要套用的记忆体保护类型。
  pflOldProtect
  [out] 上一个记忆体保护值的指标。
  传回值
  HRESULT
  描述
  S_OK
  VirtualProtect 已成功传回。
  HOST_E_CLRNOTAVAILABLE
  Common Language Runtime (CLR) 尚未载入至处理序中,或者此 CLR 目前的状态无法执行 Managed 程式码或成功处理呼叫。
  HOST_E_TIMEOUT
  呼叫已逾时。
  HOST_E_NOT_OWNER
  呼叫端不具备锁定功能。
  HOST_E_ABANDONED
  已封锁执行绪或 Fiber 在等候事件时,该事件已遭取消。
  E_FAIL
  发生未知的重大错误。当方法传回 E_FAIL 时,便无法在处理序内使用 CLR。对装载方法的後续呼叫会传回 HOST_E_CLRNOTAVAILABLE。
  备注 :
  1.这个 VirtualProtect 实作会传回 HRESULT 值,而 Win32 实作则会传回表示成功的非零值,以及表示失败的零值。如需详细资讯,请参阅 Windows 平台的说明文件。
  2.参数pflOldProtect 不能为null,最好使用PDWORD pflOldProtect=new DWORD;来定义,否则无法通过编译。
flNewProtect 访问类型
  PAGE_READONLY 该区域为只读。如果应用程序试图访问区域中的页的时候,将会被拒绝访问PAGE_READWRITE 区域可被应用程序读写
  PAGE_EXECUTE 区域包含可被系统执行的代码。试图读写该区域的操作将被拒绝。
  PAGE_EXECUTE_READ 区域包含可执行代码,应用程序可以读该区域。
  PAGE_EXECUTE_READWRITE 区域包含可执行代码,应用程序可以读写该区域。
  PAGE_GUARD 区域第一次被访问时进入一个STATUS_GUARD_PAGE异常,这个标志要和其他保护标志合并使用,表明区域被第一次访问的权限
  PAGE_NOACCESS 任何访问该区域的操作将被拒绝
  PAGE_NOCACHE RAM中的页映射到该区域时将不会被微处理器缓存(cached)
  注:PAGE_GUARD和PAGE_NOCHACHE标志可以和其他标志合并使用以进一步指定页的特征。PAGE_GUARD标志指定了一个防护页(guard page),即当一个页被提交时会因第一次被访问而产生一个one-shot异常,接着取得指定的访问权限。PAGE_NOCACHE防止当它映射到虚拟页的时候被微处理器缓存。这个标志方便设备驱动使用直接内存访问方式(DMA)来共享内存块。

<think>好的,我现在要回答用户关于C++中VirtualProtect()函数的问题。首先,我需要回忆VirtualProtect()的基本概念和用途。VirtualProtect是Windows API中的一个函数,用于更改内存页的保护属性,比如将某块内存设置为可执行、可读写或只读等。用户可能想知道它的功能、参数、返回值以及使用场景。 接下来,我需要确定用户的使用场景。可能他们在进行底层编程,比如修改内存权限来实现某些特定功能,如代码注入、hook技术或内存保护。需要指出正确的使用方法和注意事项,比如权限的合理设置,避免安全漏洞。 然后,我应该考虑参数部分。VirtualProtect的参数包括地址、大小、新保护属性、旧保护属性地址。需要详细解释每个参数的作用,例如lpAddress是内存起始地址,dwSize是区域大小,flNewProtect是新的保护标志如PAGE_EXECUTE_READWRITE,lpflOldProtect用于保存旧的属性。 返回值方面,成功返回非零,失败返回零,此时可以调用GetLastError获取错误信息。需要提醒用户检查返回值以确保函数调用成功,避免后续操作出错。 使用示例部分,给出一个简单的代码片段,展示如何将内存页改为可读写执行,并恢复原属性。同时强调错误处理的重要性,比如检查返回值,处理异常情况。 还需要注意事项,比如内存对齐的问题,通常以页为单位(4KB),修改保护属性可能影响整个页。另外,滥用可能导致安全风险,比如恶意代码利用此函数进行攻击,因此需谨慎使用。 最后,总结VirtualProtect的主要用途,比如动态修改内存权限,调试、hook或系统级开发中常见。提醒用户遵循最佳实践,确保程序的稳定性和安全性。</think>以下是关于C++中`VirtualProtect()`函数的详细介绍: --- ### **功能说明** `VirtualProtect()`是Windows API中的核心函数(定义于`<memoryapi.h>`),用于**动态修改内存区域的访问保护属性**。通过它可以将内存设置为可读/写/执行或禁止访问等状态,常用于以下场景: - 修改代码段实现Hook技术 - 动态生成可执行代码(如JIT编译) - 绕过内存保护机制进行调试 - 修复内存页防止被修改 --- ### **函数原型** ```cpp BOOL VirtualProtect( LPVOID lpAddress, // 目标内存起始地址 SIZE_T dwSize, // 要修改的内存区域大小(字节) DWORD flNewProtect, // 新保护属性 PDWORD lpflOldProtect // 用于接收旧保护属性的变量地址 ); ``` --- ### **关键参数详解** 1. **flNewProtect** 常用保护属性(通过按位或组合): - `PAGE_READONLY`:只读 - `PAGE_READWRITE`:可读写 - `PAGE_EXECUTE_READ`:可读且可执行 - `PAGE_EXECUTE_READWRITE`:可读、写、执行 - `PAGE_NOACCESS`:禁止访问(触发访问违规) 2. **lpflOldProtect** ```cpp DWORD oldProtect; VirtualProtect(addr, size, PAGE_READWRITE, &oldProtect); // 修改后可通过oldProtect获取原始权限 ``` --- ### **返回值与错误处理** - 成功:返回**非零值** - 失败:返回**0**,调用`GetLastError()`获取错误代码 ```cpp if (!VirtualProtect(...)) { DWORD err = GetLastError(); std::cerr << "Error: " << err << std::endl; } ``` --- ### **使用示例** ```cpp #include <Windows.h> #include <iostream> void ModifyMemory() { // 假设要修改0x00400000处内存的权限 void* address = (void*)0x00400000; DWORD oldProtect; // 修改为可读写执行 if (VirtualProtect(address, 4096, PAGE_EXECUTE_READWRITE, &oldProtect)) { // 这里进行内存操作(如写入机器码) // ... // 恢复原始保护属性(可选) VirtualProtect(address, 4096, oldProtect, &oldProtect); } else { std::cerr << "修改内存保护失败!" << std::endl; } } ``` --- ### **注意事项** 1. **内存对齐** 修改保护属性以**内存页(通常4KB)为单位**,即使指定较小区域也可能影响整个页。 2. **DEP保护绕过** 设置`PAGE_EXECUTE_READWRITE`可绕过数据执行保护(DEP),但可能被安全软件拦截。 3. **合法性** 修改未分配内存或系统保护区会导致`STATUS_ACCESS_VIOLATION`异常。 4. **多线程安全** 操作共享内存时需同步(如Critical Section或Mutex)。 --- ### **典型应用场景** - **动态代码生成** 允许在运行时写入并执行机器码: ```cpp // 分配可执行内存 void* mem = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); ``` - **函数Hook** 修改目标函数入口代码实现拦截: ```cpp // 将函数入口改为jmp指令 BYTE jmpCode[5] = {0xE9, 0x00, 0x00, 0x00, 0x00}; VirtualProtect(targetFunc, 5, PAGE_EXECUTE_READWRITE, &oldProtect); memcpy(targetFunc, jmpCode, 5); ``` --- 如果需要更深入的实现细节或具体问题,可以进一步探讨! 🛠️
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值