问题描述
我正在尝试使用VirtualAllocEx().当我将dwSize
(第三个参数)设置为大于约63 MB的数字时,当我查看GetLastError()
时,它将导致生成错误代码487.但是,它可以使用较小的大小,例如4MB.
I'm trying to use VirtualAllocEx(). When I set dwSize
(the third parameter) to a number larger than about 63 MB, it cause to generate error code 487 when I look at GetLastError()
. However, it works with smaller sizes such as 4MB.
这是我的代码的一部分:
Here is part of my code:
VirtualAllocEx(peProcessInformation.hProcess,
(LPVOID)(INH.OptionalHeader.ImageBase),
dwImageSize,
MEM_RESERVE | MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
在我使用4MB EXE文件的情况下,LPVOID返回值是0x00400000
,但是在其他情况下(20MB或更大文件)它将返回0x00000000
.
In the case that I used a 4MB EXE file, the LPVOID return value is 0x00400000
, but in other cases (20MB or bigger file) it returns 0x00000000
.
-
dwSize
参数是否有最大值?
我的问题是否还有其他解决方案,例如另一个功能?
Is there any other solution for my problem, such as another function?
推荐答案
我对您代码的猜测是,您正在尝试使用这项技术-是这样吗?我将在最后解决这个问题(双关语是故意的),但首先简要说明为什么VirtualAllocEx
失败.
My guess from your code is that you're trying to load a DLL or EXE into memory manually using something like this technique - is that right? I'll address this at the end (pun intended) but first a quick explanation of why VirtualAllocEx
is failing.
在特定地址分配内存的问题是,该地址需要有足够的空间来分配您请求的内存大小.这就是为什么通常在您请求内存时让操作系统决定将其放置在何处的原因. (另外,让OS/malloc库决定还可以带来其他好处,例如减少碎片等-超出了此答案的范围.)
The problem with allocating memory at a specific address is that there needs to be enough room at that address to allocate the memory size you request. This is why, generally, when you request memory you let the OS decide where to put it. (Plus letting the OS / malloc library decide can lead to other benefits, such as decreased fragmentation etc - out of scope for this answer.)
您遇到的问题不是VirtualAllocEx
无法分配64MB而不是4MB. VirtualAllocEx
可以(几乎)分配所需的内存.问题在于,在您指定的地址中,在您的过程中没有64MB的未分配内存.
The problem you're getting is not that VirtualAllocEx
is incapable of allocating 64MB rather than 4MB. VirtualAllocEx
can allocate (nearly) as much memory as you want it to. The problem is that at the address you specify, in your process, there isn't 64MB of unallocated memory.
考虑假设的地址0-15(0x0-0xF),其中-
标记为空内存,而x
标记为已分配内存:
Consider hypothetical addresses 0-15 (0x0 - 0xF), where -
marks empty memory and x
marks allocated memory:
0 1 2 3 4 5 6 7 8 9 A B C D E F
x x - x - - - - x - - - - - - -
这是您进程的内存空间.现在,您要在地址0x4分配4个字节.简单-0x4到0x7是免费的,因此您可以分配并获取(用X标记的新分配):
This is your process's memory space. Now, you want to allocate 4 bytes at address 0x4. Easy - 0x4 to 0x7 are free, so you allocate and get (new allocation marked with X):
0 1 2 3 4 5 6 7 8 9 A B C D E F
x x - x X X X X x - - - - - - -
太棒了.但是现在假设您想分配6个字节.地址0x4处没有六个空闲字节:0x8处使用了一些内存:
Fantastic. But now suppose that instead you wanted to allocate 6 bytes. There aren't six free bytes at address 0x4: there's some memory being used at 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.
You can't do it. The problem isn't that the memory allocator can't handle allocating 6 bytes, but that the memory isn't free for it to do it. Nor, most likely, can it shuffle the memory around - in a normal non-GC program you can't move memory to make space, because you might, say, leave dangling pointers which don't know the contents of the memory they were pointing at has changed address. The only thing to do is either fail and not allocate memory at all, or allocate where it has free space, say at 0x9 or 0xA.
您可能想知道为什么VirtualAllocEx
出现ERROR_INVALID_ADDRESS
而不是NULL
失败:最可能的原因是,您指定了无法分配的地址.因此,即使该地址(可能)上有一些可用内存,也仍然不够,并且该地址无效. 文档中对此有所提示:
You might wonder why VirtualAllocEx
is failing with ERROR_INVALID_ADDRESS
instead of NULL
: most likely, it is because you specified an address it couldn't allocate at; thus, even though there is some free memory at that address (maybe) there isn't enough and the address isn't valid. This is hinted at in the documentation:
这完全不是您的情况:您要同时指定两个标志,但是如果该方法无法保留,则实际上会陷入这种情况.它不能在该地址保留整个范围 ,因此它给出错误代码ERROR_INVALID_ADDRESS
.
This isn't quite your situation: you're specifying both flags at once, but if the method can't reserve then it effectively falls into this situation. It can't reserve the entire range at that address, so it gives error code ERROR_INVALID_ADDRESS
.
那么,您应该如何处理您的问题,我正在根据您的问题进行猜测,并且代码正在将DLL或EXE映像加载到内存中?
So, what should you do with your problem, which I am guessing from your question and code is loading a DLL or EXE image in memory?
这里,您需要在EXE文件中的图像位置上有一些背景知识.通常,将EXE加载到进程的虚拟地址位置0x400000的内存中.这是可选的:您的链接器可以要求将其放在任何地方,但.同样,DLL具有一个通用的默认位置:0x10000000.因此,对于一个EXE和一个DLL,您就可以了:图像加载器几乎可以肯定地将它们加载到其请求的位置.
Here you need a bit of background on image locations in an EXE file. Generally, an EXE is loaded into memory at the process's virtual address location 0x400000. It's optional: your linker can ask it be put wherever, but this value is common. Similarly, DLLs have a common default location: 0x10000000. So, for one EXE and one DLL, you're fine: the image loader can almost certainly load them at their requested locations.
当您有两个DLL都要求位于0x10000000时会发生什么?
What happens when you have two DLLs, both asking to be located at 0x10000000?
答案是图像重新定基.图像位置是可选的,不是必需的.可以通过图像加载器来调整依赖于特定地址加载的图像内部代码,因此第二个DLL可能加载的不是0x10000000,而是其他位置,例如0x1080000.那是一个0x80000的地址差,因此加载程序实际上修补了DLL中的一堆地址和代码,因此所有认为它们应指向0x10000000的位现在都指向了0x10800000.
The answer is image rebasing. The image location is optional, it's not necessary. Code inside the image that depends on being loaded at a specific address can be adjusted by the image loader, and so the second DLL might be loaded not at 0x10000000, but somewhere else - say, 0x1080000. That's an address difference of 0x80000, and so the loader actually patches up a bunch of addresses and code inside the DLL so all the bits that thought they should refer to 0x10000000 now refer to 0x10800000.
这是非常非常普遍的情况,每次加载EXE时,都会对多个DLL执行此操作. Microsoft拥有一个名为 rebase (对于重新设置",即调整基址),并在其中分发EXE和您自己的DLL时,可以使用此方法来确保每个DLL具有不同的基址,并且每个基址都已定位,以便在Windows加载时您的EXE和DLL它们将已经具有正确的地址,并且不太可能必须对它们中的任何一个进行变基处理(执行上述操作).对于某些应用程序,这可以显着缩短启动时间. (在Windows的现代版本中,有时DLL仍然会四处移动-这是地址空间布局随机化 ,这是一种安全技术,可确保每次运行时代码都位于不同的地址.)
This is really, really common, and every time you load an EXE this will be done to several DLLs. It is so common that Microsoft have a little optimisation tool called rebase, (for "rebasing", that is, adjusting the base address) and when you distribute your EXE and your own DLLs with it, you can use this to make sure each DLL has a different base address, each of which is located so that when Windows loads your EXE and the DLLs they will already have the right addresses and it is unlikely to have to rebase - perform the above operation - on any of them. For some applications this can make a noticeable improvement in starting time. (In modern versions of Windows, sometimes DLLs are moved around anyway - this is for address space layout randomization, a security technique to deliberately make sure code is not at the same address each time it's run.)
(另一件事是,某些DLL和EXE压缩工具会删除用于此重定位的数据.这很好,因为它会使EXE变小...直到需要重定位为止,并且因为数据丢失了,因此无法加载,或者您可以以固定的基数进行构建,它将神奇地工作直到它无法工作为止.请勿对您的EXE或DLL执行此操作.)
(One other thing is that some DLL and EXE compression tools strip out the data that is used for this relocation. That's fine, because it makes the EXE smaller... right up until it needs to be relocated, and because the data is missing it can't, and so can't be loaded at all. Or you can build with a fixed base, and it will magically work right until it doesn't. Don't do this to your EXEs or DLLs.)
因此,当您尝试将DLL手动加载到内存中时,如果它要求加载的地址处没有足够的空间,该怎么办?容易-这不是致命错误,只需将其加载到其他位置,然后自己执行重新基准即可.我建议如果您有问题要问一个新的SO问题,但是要给您一个起点,您可以使用 RebaseImage
函数,或者如果您不能使用它或想自己动手,我发现此代码从快速的概述看来似乎是手动执行的.不能保证其正确性.
So, what should you do when you try to manually load a DLL into memory, and there isn't enough space for it at the address it asks to be loaded at? Easy - it's not a fatal error, just load it somewhere else, and then perform the rebasing yourself. I would suggest if you have problems to ask a new SO question, but to give you a starting point you can use the RebaseImage
function, or if you can't use it or want to do it yourself, I found this code which from a quick overview seems to perform this manually. No guarantees about its correctness.
您的进程地址空间在您指定的地址处没有64MB的空白空间,因此您不能在那里分配64MB的内存.而是将其分配到其他位置,然后对加载的DLL或EXE映像进行修补/重新设置其基址以匹配新地址.
Your process address space doesn't have 64MB of empty space at the address you specify, so you can't allocate 64MB of memory there. Instead, allocate it somewhere else and patch / rebase your loaded DLL or EXE image to match the new address.
这篇关于使用VirtualAllocEX时出现错误代码487(ERROR_INVALID_ADDRESS)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!