调用VirtualProtect设置了错误的保护。

huangapple go评论53阅读模式
英文:

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, &amp;dwOldProtect)) {
		printf(&quot;VirtualProtect failed: 0x%lX\n&quot;, GetLastError());
		return -1;
	}

	MEMORY_BASIC_INFORMATION mbi;
	if (!VirtualQuery(foo, &amp;mbi, sizeof(mbi))) {
		printf(&quot;VirtualQuery failed: 0x%lX\n&quot;, GetLastError());
		return -1;
	}

	if (mbi.Protect != PAGE_EXECUTE_READWRITE) {
		printf(&quot;Wrong protection set. Protection constant: 0x%lX\n&quot;, 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, &amp;dwOldProtect))
	{
		DbgPrint(&quot;dwOldProtect = %x\n&quot;, dwOldProtect);

		ULONG n = 2;
		do 
		{
			MEMORY_BASIC_INFORMATION mbi;
			if (VirtualQuery(lpAddress, &amp;mbi, sizeof(mbi)))
			{
				DbgPrint(&quot;%x / %x\n&quot;, 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

huangapple
  • 本文由 发表于 2023年6月11日 21:40:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/76450740.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定