[英]How do I reserve memory regions before Windows maps my program's DLLs?
My Windows program needs to use very specific regions of memory. 我的Windows程序需要使用非常特定的内存区域。 Unfortunately, Windows loads quite a few DLLs in memory and because of ASLR, their locations are not predictable, so they could end up being mapped into regions that my program needs to use.
不幸的是,Windows在内存中加载了相当多的DLL,并且由于ASLR,它们的位置是不可预测的,因此它们可能最终被映射到我的程序需要使用的区域。 On Linux, Wine solves this problem by using a preloader application which reserves memory regions and then manually loads and executes the actual image and dynamic linker.
在Linux上,Wine通过使用预加载器应用程序来解决此问题,该应用程序保留内存区域,然后手动加载并执行实际图像和动态链接器。 I assume that specific method is not possible on Windows, but is there another way to get reserved regions of memory that are guaranteed to not be used by DLLs or the process heap?
我假设在Windows上不可能使用特定的方法,但是有另一种方法可以获得保证不被DLL或进程堆使用的内存保留区域吗?
If it helps, the memory regions are fixed and known at compile time. 如果有帮助,内存区域是固定的并且在编译时是已知的。 Also, I'm aware that ASLR can be disabled system-wide in the registry or per-process using the Enhanced Mitigation Experience Toolkit, but I don't want to require my users to do that.
另外,我知道可以使用增强型缓解体验工具包在注册表或每个进程中在系统范围内禁用ASLR,但我不想要求我的用户这样做。
I think I finally got it using a method similar to what dxiv suggested in the comments. 我想我终于使用了类似于dxiv在评论中建议的方法。 Instead of using a dummy DLL, I build a basic executable that loads at the beginning of my reserved region using the
/FIXED
and /BASE
compiler flags. 我没有使用虚拟DLL,而是使用
/FIXED
和/BASE
编译器标志构建一个基本可执行文件,该可执行文件在保留区域的开头加载。 The code for the executable contains an uninitialized array that ensures the image covers the needed addresses in memory, but doesn't take up any extra space in the file: 可执行文件的代码包含一个未初始化的数组,可确保图像覆盖内存中所需的地址,但不会占用文件中的任何额外空间:
unsigned char Reserved[4194304]; // 4MB
At runtime, the executable copies itself to a new location in memory and updates a couple of fields in the Process Environment Block to point to it. 在运行时,可执行文件将自身复制到内存中的新位置,并更新Process Environment Block中的几个字段以指向它。 Without updating the fields, calling certain functions like
FormatMessage
would cause a crash. 在不更新字段的情况下,调用
FormatMessage
等某些函数会导致崩溃。
#include <intrin.h>
#include <windows.h>
#include <winternl.h>
#pragma intrinsic(__movsb)
void Relocate() {
void *Base, *NewBase;
ULONG SizeOfImage;
PEB *Peb;
LIST_ENTRY *ModuleList, *NextEntry;
/* Get info about the PE image. */
Base = GetModuleHandleW(NULL);
SizeOfImage = ((IMAGE_NT_HEADERS *)(((ULONG_PTR)Base) +
((IMAGE_DOS_HEADER *)Base)->e_lfanew))->OptionalHeader.SizeOfImage;
/* Allocate memory to hold a copy of the PE image. */
NewBase = VirtualAlloc(NULL, SizeOfImage, MEM_COMMIT, PAGE_READWRITE);
if (!NewBase) {
ExitProcess(GetLastError());
}
/* Copy the PE image to the new location using __movsb since we don't have
a C library. */
__movsb(NewBase, Base, SizeOfImage);
/* Locate the Process Environment Block. */
Peb = (PEB *)__readfsdword(0x30);
/* Update the ImageBaseAddress field of the PEB. */
*((PVOID *)((ULONG_PTR)Peb + 0x08)) = NewBase;
/* Update the base address in the PEB's loader data table. */
ModuleList = &Peb->Ldr->InMemoryOrderModuleList;
NextEntry = ModuleList->Flink;
while (NextEntry != ModuleList) {
LDR_DATA_TABLE_ENTRY *LdrEntry = CONTAINING_RECORD(
NextEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
if (LdrEntry->DllBase == Base) {
LdrEntry->DllBase = NewBase;
break;
}
NextEntry = NextEntry->Flink;
}
}
I built the executable with /NODEFAULTLIB
just to reduce its size and the number of DLLs loaded at runtime, hence the use of the __movsb
intrinsic. 我使用
/NODEFAULTLIB
构建可执行文件只是为了减小它的大小和运行时加载的DLL的数量,因此使用了__movsb
内在函数。 You could probably get away with linking to MSVCRT if you wanted to and then replace __movsb
with memcpy
. 如果你愿意,你可以放弃链接到MSVCRT,然后用
memcpy
替换__movsb
。 You can also import memcpy
from ntdll.dll
or write your own. 您也可以从
ntdll.dll
导入memcpy
或自己编写。
Once the executable is moved out of the way, I call a function in a DLL that contains the rest of my code. 一旦将可执行文件移开,我就会调用包含其余代码的DLL中的函数。 The DLL uses
UnmapViewOfFile
to get rid of the original PE image, which gives me a nice 4MB+ chunk of memory to work with, guaranteed not to contain mapped files, thread stacks, or heaps. DLL使用
UnmapViewOfFile
来摆脱原始的PE映像,这为我提供了一个很好的4MB +内存块,保证不包含映射文件,线程堆栈或堆。
A few things to keep in mind with this technique: 使用这种技术时要记住以下几点:
/FIXED /BASE
, its code is not position-independent and you can't just jump to the relocated executable. /FIXED /BASE
构建的,因此其代码与位置无关,您不能只跳转到重定位的可执行文件。 UnmapViewOfFile
returns, the program will crash because the code section we called from doesn't exist anymore. UnmapViewOfFile
的DLL函数返回,程序将崩溃,因为我们调用的代码部分不再存在。 I use ExitProcess
to ensure the function never returns. ExitProcess
来确保函数永远不会返回。 VirtualFree
to free up some physical memory. VirtualFree
重定位的PE映像中的某些部分(如包含代码的部分)以释放一些物理内存。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.