gpt4 book ai didi

c++ - 如何在 Windows 映射我的程序的 DLL 之前保留内存区域?

转载 作者:可可西里 更新时间:2023-11-01 13:42:27 32 4
gpt4 key购买 nike

我的 Windows 程序需要使用非常特定的内存区域。不幸的是,Windows 在内存中加载了相当多的 DLL,并且由于 ASLR,它们的位置是不可预测的,所以它们最终可能被映射到我的程序需要使用的区域。在 Linux 上,Wine 通过使用保留内存区域的预加载器应用程序解决了这个问题,然后手动加载和执行实际图像和动态链接器。我假设特定方法在 Windows 上是不可能的,但是是否有另一种方法来获取保证不会被 DLL 或进程堆使用的保留内存区域?

如果有帮助,内存区域是固定的并且在编译时已知。此外,我知道可以使用增强缓解体验工具包在注册表或每个进程中在系统范围内禁用 ASLR,但我不想要求我的用户这样做。

最佳答案

我想我终于用类似于 dxiv 在评论中建议的方法得到了它。我没有使用虚拟 DLL,而是构建了一个基本的可执行文件,它使用 /FIXED/BASE 编译器标志加载到我的保留区域的开头。可执行文件的代码包含一个未初始化的数组,可确保图像覆盖内存中所需的地址,但不会占用文件中的任何额外空间:

unsigned char Reserved[4194304]; // 4MB

在运行时,可执行文件将自身复制到内存中的新位置,并更新进程环境 block 中的几个字段以指向它。如果不更新字段,调用某些函数(例如 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;
}
}

我使用 /NODEFAULTLIB 构建可执行文件只是为了减少其大小和运行时加载的 DLL 数量,因此使用了 __movsb 内在函数。如果您愿意,您可以链接到 MSVCRT,然后将 __movsb 替换为 memcpy。您还可以从 ntdll.dll 导入 memcpy 或编写您自己的。

将可执行文件移开后,我将在包含我的其余代码的 DLL 中调用一个函数。 DLL 使用 UnmapViewOfFile 去除原始 PE 图像,这为我提供了 4MB 以上的内存块,保证不包含映射文件、线程堆栈或堆。

使用此技术需要牢记的几点:

  1. 这是一个巨大的黑客攻击。我觉得写它很脏,而且它很可能在未来的 Windows 版本中崩溃。 我也没有在 Windows 7 以外的任何系统上测试过它。此代码至少适用于 Windows 7 和 Windows 10。
  2. 由于可执行文件是使用 /FIXED/BASE 构建的,因此其代码不是位置独立的,您不能直接跳转到重定位的可执行文件。
  3. 如果调用UnmapViewOfFile 的DLL 函数返回,程序将崩溃,因为我们调用的代码段不再存在。我使用 ExitProcess 来确保函数永远不会返回。
  4. 可以使用 VirtualFree 释放重定位的 PE 镜像中的某些部分(例如包含代码的部分)以释放一些物理内存。
  5. 我的代码不会对加载程序数据表条目进行重新排序。这种方式似乎工作正常,但如果某些内容依赖于按图像地址排序的条目,它可能会中断。
  6. 一些反病毒程序可能会对这些东西产生怀疑。至少,Microsoft Security Essentials 没有提示。
  7. 事后看来,dxiv 的虚拟 DLL 方法可能更简单,因为我不需要弄乱 PEB。但我坚持使用这种技术,因为可执行文件更有可能加载到其所需的基地址。 虚拟 DLL 方法对我不起作用。在 Windows 已经保留了我需要的内存区域后,DLL 由 Ntdll 加载。

关于c++ - 如何在 Windows 映射我的程序的 DLL 之前保留内存区域?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35785677/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com