英文:
Call to VirtualProtect sets the wrong protection
问题
我有一个应用程序(使用MSVC编译,调试,x64),我试图调用VirtualProtect来更改指向函数的指针的页面保护,将函数代码的保护从PAGE_EXECUTE_READ更改为PAGE_EXECUTE_READWRITE。
然后我查询了VirtualProtect调用后的区域。 VirtualQuery返回页面保护已从PAGE_EXECUTE_READ更改为PAGE_EXECUTE_WRITECOPY,这与我在调用VirtualProtect时指定的不符。这是我使用的代码:
```cpp
int foo(int n) { return n; }
int main() {
DWORD dwOldProtect;
if (!VirtualProtect(foo, 16, PAGE_EXECUTE_READWRITE, &dwOldProtect)) {
printf("VirtualProtect失败:0x%lX\n", GetLastError());
return -1;
}
MEMORY_BASIC_INFORMATION mbi;
if (!VirtualQuery(foo, &mbi, sizeof(mbi))) {
printf("VirtualQuery失败:0x%lX\n", GetLastError());
return -1;
}
if (mbi.Protect != PAGE_EXECUTE_READWRITE) {
printf("设置的保护不正确。保护常量:0x%lX\n", mbi.Protect);
return -1;
}
return 0;
}
当我编译并运行时,它产生以下输出:
设置的保护不正确。保护常量:0x80
我确保foo不指向jmp指令。我还尝试过使用VirtualProtectEx,而不是指定具有PROCESS_ALL_ACCESS权限的进程句柄,但结果相同。
编辑:
在发布模式下编译相同的代码似乎让问题消失了,为什么会这样?
<details>
<summary>英文:</summary>
I have an application (Compiled with MSVC, Debug, x64) where I attempt to call VirtualProtect on a pointer to a function in order to change the page protection of the function code from PAGE_EXECUTE_READ to PAGE_EXECUTE_READWRITE.
I then query the region after VirtualProtect has been called. VirtualQuery returns that the page protection is now changed from PAGE_EXECUTE_READ to PAGE_EXECUTE_WRITECOPY, which is not what I specified in the call to VirtualProtect. This is the code I am using:
```cpp
int foo(int n) { return n; }
int main() {
DWORD dwOldProtect;
if (!VirtualProtect(foo, 16, PAGE_EXECUTE_READWRITE, &dwOldProtect)) {
printf("VirtualProtect failed: 0x%lX\n", GetLastError());
return -1;
}
MEMORY_BASIC_INFORMATION mbi;
if (!VirtualQuery(foo, &mbi, sizeof(mbi))) {
printf("VirtualQuery failed: 0x%lX\n", GetLastError());
return -1;
}
if (mbi.Protect != PAGE_EXECUTE_READWRITE) {
printf("Wrong protection set. Protection constant: 0x%lX\n", mbi.Protect);
return -1;
}
return 0;
}
When compiled and ran by me it produces the following output:
Wrong protection set. Protection constant: 0x80
I have made sure that foo does not point to a jmp instruction. I have also tried using VirtualProtectEx instead specifying a handle to the process with PROCESS_ALL_ACCESS rights, which produces the same result.
EDIT:
Compiling the same code in Release instead of Debug seems to make the issue go away, why is that?
答案1
得分: 2
这是正常情况。我要说操作系统优化。直到页面没有被修改 - 它由图像文件本身支持。之后,您只需将保护标记更改为可写 - 系统将继续使用此页面,但将其标记为写时复制。但在第一次修改后 - 系统取消页面与图像文件之间的关联,并分配新的页面,由页面文件支持,从旧页面复制其内容,并且新页面已经具有PAGE_EXECUTE_READWRITE
。所以写时复制 - 这正是系统所做的。
我使用以下代码进行演示:
void mm(PVOID lpAddress)
{
ULONG dwOldProtect;
if (VirtualProtect(lpAddress, 1, PAGE_EXECUTE_READWRITE, &dwOldProtect))
{
DbgPrint("dwOldProtect = %x\n", dwOldProtect);
ULONG n = 2;
do
{
MEMORY_BASIC_INFORMATION mbi;
if (VirtualQuery(lpAddress, &mbi, sizeof(mbi)))
{
DbgPrint("%x / %x\n", mbi.Protect, mbi.AllocationProtect);
}
*(UCHAR*)lpAddress = *(UCHAR*)lpAddress;
} while (--n);
}
}
并且有以下输出:
dwOldProtect = 20
80 / 80
40 / 80
如果将其转换为符号名称:
dwOldProtect = PAGE_EXECUTE_READ
PAGE_EXECUTE_WRITECOPY / PAGE_EXECUTE_WRITECOPY
PAGE_EXECUTE_READWRITE / PAGE_EXECUTE_WRITECOPY
英文:
this is normal situation. i be say OS optimization. until page is not modified - it backed by image file itself. after you only change protection to writable - system continue use this page, but mark it as copy on write. but after first modification - system drop link between page and image file, and allocate new page, backed by page file, copy it content from old, and new page already will be have PAGE_EXECUTE_READWRITE
. so copy on write - this is exactly what system do.
i use such code for demo
void mm(PVOID lpAddress)
{
ULONG dwOldProtect;
if (VirtualProtect(lpAddress, 1, PAGE_EXECUTE_READWRITE, &dwOldProtect))
{
DbgPrint("dwOldProtect = %x\n", dwOldProtect);
ULONG n = 2;
do
{
MEMORY_BASIC_INFORMATION mbi;
if (VirtualQuery(lpAddress, &mbi, sizeof(mbi)))
{
DbgPrint("%x / %x\n", mbi.Protect, mbi.AllocationProtect);
}
*(UCHAR*)lpAddress = *(UCHAR*)lpAddress;
} while (--n);
}
}
and have next output -
dwOldProtect = 20
80 / 80
40 / 80
if convert it to symbolic names:
dwOldProtect = PAGE_EXECUTE_READ
PAGE_EXECUTE_WRITECOPY / PAGE_EXECUTE_WRITECOPY
PAGE_EXECUTE_READWRITE / PAGE_EXECUTE_WRITECOPY
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论