Winapi 使用VirtualAllocEX时出现错误代码487(错误地址无效)

Winapi 使用VirtualAllocEX时出现错误代码487(错误地址无效),winapi,memory-management,error-code,Winapi,Memory Management,Error Code,我正在尝试使用VirtualAllocEx()。当我将dwSize(第三个参数)设置为大于63 MB的数字时,当我查看GetLastError()时,它会导致生成错误代码487。但是,它适用于较小的尺寸,如4MB 以下是我的部分代码: VirtualAllocEx(peProcessInformation.hProcess, (LPVOID)(INH.OptionalHeader.ImageBase), dwImageSize,

我正在尝试使用VirtualAllocEx()。当我将
dwSize
(第三个参数)设置为大于63 MB的数字时,当我查看
GetLastError()
时,它会导致生成错误代码487。但是,它适用于较小的尺寸,如4MB

以下是我的部分代码:

VirtualAllocEx(peProcessInformation.hProcess,
               (LPVOID)(INH.OptionalHeader.ImageBase),
               dwImageSize, 
               MEM_RESERVE | MEM_COMMIT,
               PAGE_EXECUTE_READWRITE);
在我使用4MB EXE文件的情况下,LPVOID返回值是
0x00400000
,但在其他情况下(20MB或更大的文件),它返回
0x00000000

  • dwSize
    参数是否有最大值

  • 我的问题还有其他解决方案吗,比如另一个函数


  • 我从您的代码中猜测,您正在尝试使用类似的方式手动将DLL或EXE加载到内存中-是这样吗?我将在最后讨论这个问题(双关语),但首先快速解释一下
    VirtualAllocEx
    失败的原因

    为什么
    VirtualAllocEx
    会给出此错误? 在特定地址分配内存的问题是,该地址需要有足够的空间来分配您请求的内存大小。这就是为什么一般来说,当你请求内存时,你让操作系统决定把它放在哪里。(另外,让OS/malloc库决定还可以带来其他好处,如减少碎片等,这超出了这个答案的范围。)

    您遇到的问题不是
    VirtualAllocEx
    无法分配64MB而不是4MB
    VirtualAllocEx
    可以分配(几乎)您想要的内存。问题是在您指定的地址,在您的进程中,没有64MB的未分配内存

    假设地址为0-15(0x0-0xF),其中
    -
    标记为空内存,
    x
    标记为已分配内存:

    0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
    x  x  -  x  -  -  -  -  x  -  -  -  -  -  -  -
    
    这是进程的内存空间。现在,您希望在地址0x4处分配4个字节。Easy-0x4到0x7是免费的,因此您可以分配并获取(新分配标记为X):

    太棒了。但是现在假设您想要分配6个字节。地址0x4处没有六个可用字节:0x8处正在使用一些内存:

    0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
    x  x  -  x  -  -  -  -  x  -  -  -  -  -  -  -
                1  2  3  4  bang!
    
    你不能这么做。问题不在于内存分配器无法处理分配6个字节的操作,而在于内存不是空闲的。最有可能的是,它也不能随意移动内存——在一个普通的非GC程序中,你不能移动内存来腾出空间,因为你可能会,比如说,留下悬空的指针,这些指针不知道它们指向的内存的内容已经改变了地址。唯一要做的是要么失败,根本不分配内存,要么在有空闲空间的地方分配内存,比如0x9或0xA

    您可能想知道为什么
    VirtualAllocEx
    会失败,出现
    错误\u无效\u地址
    而不是
    NULL
    :很可能是因为您指定了一个无法分配的地址;因此,即使该地址有一些可用内存(可能),也没有足够的内存,并且该地址无效。这一点在以下章节中有所暗示:

    试图通过指定MEM_commit来提交特定的地址范围 如果没有MEM_RESERVE和非空lpAddress,则除非整个 范围已被保留。产生的错误代码为 错误\u无效\u地址

    这与您的情况不同:您同时指定了两个标志,但是如果方法不能保留,那么它实际上就陷入了这种情况。它不能在该地址保留整个范围,因此它会给出错误代码
    error\u INVALID\u address

    加载DLL或EXE映像 那么,您应该如何处理您的问题呢?我从您的问题中猜到,代码正在内存中加载DLL或EXE映像

    在这里,您需要一点EXE文件中图像位置的背景信息。通常,EXE加载到进程虚拟地址位置0x400000处的内存中。它是可选的:你的链接器可以要求它放在任何地方,但是。类似地,DLL有一个通用的默认位置:0x10000000。因此,对于一个EXE和一个DLL,您就可以了:图像加载程序几乎可以肯定地在它们请求的位置加载它们

    当您有两个DLL,都要求位于0x10000000时会发生什么情况

    答案是图像重定。图像位置是可选的,不需要。映像加载程序可以调整映像中依赖于在特定地址加载的代码,因此第二个DLL可能不是在0x10000000处加载,而是在其他地方加载,例如0x1080000。这是一个0x80000的地址差,因此加载程序实际上在DLL中修补了一组地址和代码,因此所有认为应该引用0x10000000的位现在都引用0x10800000

    这是非常非常常见的,每次加载EXE时,都会对几个DLL执行此操作。微软有一个叫做“重定基址”的小优化工具是很常见的,当你分发你的EXE和你自己的DLL时,你可以用它来确保每个DLL有一个不同的基址,每个都位于这样的位置:当Windows加载您的EXE和DLL时,它们将已经具有正确的地址,并且不太可能需要对其中任何一个重新设置基础(执行上述操作)。对于某些应用,这可以显著缩短启动时间。(在现代版本的Windows中,有时DLL会被移动——这是一种安全技术,用于故意确保代码在每次运行时不在同一地址。)

    (另一件事是,一些DLL和EXE压缩工具会剥离用于此重新定位的数据。这很好,因为它会使EXE变小…直到需要重新定位为止,而且因为数据丢失,所以无法加载。或者,您不能对EXE或DLL执行此操作。)

    那么,当您尝试手动将DLL加载到内存中,以及
    0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
    x  x  -  x  -  -  -  -  x  -  -  -  -  -  -  -
                1  2  3  4  bang!
    
    void* pImageBase = VirtualAllocEx(peProcessInformation.hProcess,
                   NULL,
                   dwImageSize, 
                   MEM_RESERVE | MEM_COMMIT,
                   PAGE_EXECUTE_READWRITE);