VirtualProtect 和 kernel32.dll - 尝试访问无效地址
Posted
技术标签:
【中文标题】VirtualProtect 和 kernel32.dll - 尝试访问无效地址【英文标题】:VirtualProtect and kernel32.dll - attempt to access invalid address 【发布时间】:2013-12-16 16:34:06 【问题描述】:我正在分析进程加载的各种模块。不幸的是,我无法创建kernel32.dll
内存快照,尽管该功能可以与其他模块一起正常工作(例如ntddl.dll
)。问题在于以下代码:
/* Copy code from memory */
if (VirtualProtect((BYTE*)virtualAddress, sizeOfCode, PAGE_EXECUTE_READWRITE, &flags) == 0)
std::cout << "VirtualProtect failed!" << std::endl;
std::cout << "Virtual address: " << virtualAddress << std::endl;
std::cout << "Size of code: " << sizeOfCode << std::endl;
std::cout << "Error code: " << GetLastError() << std::endl;
kernel32.dll
调用这段代码的结果是:
Virtual address: 747d0000
Size of code: 6a000
Error code: 0x1e7
错误描述说:
ERROR_INVALID_ADDRESS
487 (0x1E7)
Attempt to access invalid address.
我检查了进程的内存映射,kernel32.dll 地址是正确的。是什么原因?
【问题讨论】:
这不是正确的代码,您必须立即调用 GetLastError()。 cout 调用可以轻松修改值,因为它们也使用 winapi 调用。 在VirtualProtect
之后调用GetLastError()
的结果是一样的
kernel32.dll 有点特别。阻止你干扰它是有道理的——例如防止各种注入攻击。
winspool.drv也是同样的情况
只是说,当我使用“调试函数”API 编写调试器时,我遇到了同样的问题:这些调试函数允许您创建用户模式调试器,但不能创建内核模式调试器(我无法进入或暂停进程进入内核函数)。因此,如果我没记错的话,如果我在管理员模式下运行我的代码,kernerl32.dll 是可调试和可编辑的,但不是 ntdll.dll 和许多其他 nt*.dll 文件。在使用 Visual C++ 调试程序时,您可以看到同样的事实:它不允许您输入 NT 函数(即使在单步执行汇编程序时),因为它是一个用户模式调试器。
【参考方案1】:
很难验证您的地址是否正确,它异常低。我刚刚编写了另一个程序来测试它。它枚举 kernel32.dll 中的区域并在它们上调用 VirtualProtect():
#include <Windows.h>
#include <assert.h>
#include <iostream>
int main()
HMODULE hmod = GetModuleHandle(L"kernel32.dll");
MEMORY_BASIC_INFORMATION info;
// Start at PE32 header
SIZE_T len = VirtualQuery(hmod, &info, sizeof(info));
assert(len > 0);
BYTE* dllBase = (BYTE*)info.AllocationBase;
BYTE* address = dllBase;
for (;;)
len = VirtualQuery(address, &info, sizeof(info));
assert(len > 0);
if (info.AllocationBase != dllBase) break;
std::cout << "Address: " << std::hex << info.BaseAddress;
std::cout << " (" << std::hex << info.RegionSize << ") ";
std::cout << " protect = " << std::hex << info.Protect;
DWORD oldprotect;
if (info.Protect == 0) std::cout << ", VirtualProtect skipped" << std::endl;
else
BOOL ok = VirtualProtect(info.BaseAddress, info.RegionSize, PAGE_EXECUTE_READWRITE, &oldprotect);
std::cout << ", VirtualProtect = " << (ok ? "okay" : "Failed!") << std::endl;
address = (BYTE*)info.BaseAddress + info.RegionSize;
return 0;
此程序在我的机器上的输出,运行 Windows 8.1 x64:
Address: 77470000 (1000) protect = 2, VirtualProtect = okay
Address: 77471000 (f000) protect = 0, VirtualProtect skipped
Address: 77480000 (62000) protect = 20, VirtualProtect = okay
Address: 774E2000 (e000) protect = 0, VirtualProtect skipped
Address: 774F0000 (7e000) protect = 2, VirtualProtect = okay
Address: 7756E000 (2000) protect = 0, VirtualProtect skipped
Address: 77570000 (1000) protect = 4, VirtualProtect = okay
Address: 77571000 (f000) protect = 0, VirtualProtect skipped
Address: 77580000 (1000) protect = 2, VirtualProtect = okay
Address: 77581000 (f000) protect = 0, VirtualProtect skipped
Address: 77590000 (1a000) protect = 2, VirtualProtect = okay
Address: 775AA000 (6000) protect = 0, VirtualProtect skipped
在 64 位模式下运行:
Address: 00007FFC4F870000 (1000) protect = 2, VirtualProtect = okay
Address: 00007FFC4F871000 (112000) protect = 20, VirtualProtect = okay
Address: 00007FFC4F983000 (1000) protect = 4, VirtualProtect = okay
Address: 00007FFC4F984000 (1000) protect = 8, VirtualProtect = okay
Address: 00007FFC4F985000 (24000) protect = 2, VirtualProtect = okay
显然您有不同的 Windows 版本,因此请务必在您的计算机上运行此程序以获得可比较的结果。
我得出的结论是,这种代码失败并没有根本原因。如果它在您的机器上运行,那么这很可能是环境问题。有一个非常明显的候选者可以成为您的反恶意软件,这当然在防止代码与 kernel32.dll 混淆方面有很大的利害关系。我在我的机器上运行了最低限度的保护。
【讨论】:
以上是关于VirtualProtect 和 kernel32.dll - 尝试访问无效地址的主要内容,如果未能解决你的问题,请参考以下文章
当任何进程(包括恶意软件)都可以使用 VirtualProtect 时,它的意义何在?