5

My Windows program needs to use very specific regions of memory. 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. 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. 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?

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.

David Brown
  • 35,411
  • 11
  • 83
  • 132
  • You cannot simply "reserve" memory at compile time and then expect it to be available. If you want memory, you have to allocate if dynamically. – H. Guijt Mar 04 '16 at 00:20
  • Using gcc it looks possible, maybe this could help: http://stackoverflow.com/questions/22457446/gcc-linker-description-file-force-symbol-to-be-at-specific-address – SHR Mar 04 '16 at 00:37
  • So yo want to reserve the memory region before your program load ? even if it is not running ? What would be the use of such memory region (IPC) ? – dvhh Mar 04 '16 at 00:39
  • 1
    @dvhh My programs needs to load and execute some legacy code (for which I don't have the source) that assumes specific memory addresses are writable. I was hoping there was some way to indicate to Windows that the pages containing those addresses should not be used to map DLLs or the process heap. – David Brown Mar 04 '16 at 00:48
  • You won't be able to do it from code, since the memory is mapped already by the time your code gets control. You _might_ be able to link to a dummy DLL built to only load at its preferred address - if using MSVC see [/BASE](https://msdn.microsoft.com/en-us/library/f7f5138s.aspx) and [/FIXED](https://msdn.microsoft.com/en-us/library/w368ysh2.aspx) - whose sole purpose would be to reserve the memory in the given range. However, I don't know how nicely that plays along with ASLR. – dxiv Mar 04 '16 at 00:55
  • 2
    You might be able to use a loader application, that launches the target process in suspended state ([CreateProcess](https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425.aspx), [Process Creation Flags](https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863.aspx)), and then reserve memory calling [VirtualAllocEx](https://msdn.microsoft.com/en-us/library/windows/desktop/aa366890.aspx). But that is dangerous territory, fiddling with a process that hasn't initialized itself yet. – IInspectable Mar 04 '16 at 01:15
  • 1
    Using `VirtualAlloc` on a process created in suspended state is totally safe, as far as I recall. – Collin Dauphinee Mar 04 '16 at 02:13
  • @IInspectable I just tried your method, but it looks like Windows maps DLLs even when the process is suspended. Some of the strings returned by `GetModuleFileNameEx` are also garbled. – David Brown Mar 04 '16 at 02:15
  • IIRC, EXE sections are loaded at specific memory addresses, unless ASLR is in use. And Windows must support that because it's compatible with all the old programs that use fixed memory addresses. – user253751 Mar 04 '16 at 02:16
  • Is the issue that system libraries are taking the address space you want? – Collin Dauphinee Mar 04 '16 at 02:16
  • @CollinDauphinee They potentially could due to ASLR, yes. In practice, I haven't actually had a collision yet. I'm mostly just curious if it would be possible to achieve this in case it becomes a problem. – David Brown Mar 04 '16 at 02:18
  • As far as I'm aware, most(all?) of the critical system DLLs are required to be loaded at the same base address. You may be able to create a 'host' process with minimal dependencies and use process hollowing to replace it with your application, after reserving the address space you need. This will certainly be picked up by anti-malware applications, but I think that will be an issue with anything you do to accomplish this. – Collin Dauphinee Mar 04 '16 at 02:22
  • @DavidBrown: I believe, only ntdll.dll and kernel32.dll are mapped into the address space of a process that was created in suspended state. At that point, calling `GetModuleFileNameEx` won't work as expected either ([Why do I get ERROR_INVALID_HANDLE from GetModuleFileNameEx when I know the process handle is valid?](https://blogs.msdn.microsoft.com/oldnewthing/20150716-00/?p=45131)). – IInspectable Mar 04 '16 at 02:23
  • 2
    It should be possible to construct an .exe file with data section(s) that include your address ranges, though it might be necessary to do so by hand, I'm not sure there's any way to make Microsoft's linker do so. But if one of the critical system DLLs is already occupying any part of the address range in the file, it won't be able to run. (In practice they seem to load towards the upper part of the first 2GB, so perhaps that won't be a problem.) But it would certainly be preferable to eliminate this requirement - there's no reason I can see why you should need to do this. – Harry Johnston Mar 04 '16 at 03:20
  • Could you re-phrase your question - ASLR moves things around, but system dlls have different locations to normal DLLs - how much space do you want to reserve, and where about is it (0x10xxxxxx?) – mksteve Mar 05 '16 at 22:28
  • I did finally run into a situation where I needed to use address space reserved by the system (specifically, 2MB starting at `0x00010000`). `apisetschema.dll` and `locale.nls` were the first offenders, but they can be moved and their locations updated in the PEB. Unfortunately, the process heaps and thread stacks are also in the way and I don't see any fix for those. I guess what I want to do just isn't possible. – David Brown Mar 11 '16 at 00:33

1 Answers1

2

I think I finally got it using a method similar to what dxiv suggested in the comments. 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. 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. Without updating the fields, calling certain functions like FormatMessage would cause a crash.

#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. You could probably get away with linking to MSVCRT if you wanted to and then replace __movsb with memcpy. You can also import memcpy from ntdll.dll or write your own.

Once the executable is moved out of the way, I call a function in a DLL that contains the rest of my code. 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.

A few things to keep in mind with this technique:

  1. This is a huge hack. I felt dirty writing it and it very well could fall apart in future versions of Windows. I also haven't tested this on anything other than Windows 7. This code works on Windows 7 and Windows 10, at least.
  2. Since the executable is built with /FIXED /BASE, its code is not position-independent and you can't just jump to the relocated executable.
  3. If the DLL function that calls UnmapViewOfFile returns, the program will crash because the code section we called from doesn't exist anymore. I use ExitProcess to ensure the function never returns.
  4. Some sections in the relocated PE image like those containing code can be released using VirtualFree to free up some physical memory.
  5. My code doesn't bother re-sorting the loader data table entries. It seems to work fine that way, but it could break if something were to depend on the entries being ordered by image address.
  6. Some anti-virus programs might get suspicious about this stuff. Microsoft Security Essentials didn't complain, at least.
  7. In hindsight, dxiv's dummy DLL method may have been easier, because I wouldn't need to mess with the PEB. But I stuck with this technique because the executable is more likely to be loaded at its desired base address. The dummy DLL method didn't work for me. DLLs are loaded by Ntdll after Windows has already reserved regions of memory that I need.
David Brown
  • 35,411
  • 11
  • 83
  • 132
  • 1
    Impressive, but looks really fragile. +1 for the technical feat, but the fake dll method looks way more dependable (since it relies on features that the loader explicitly support). – Matteo Italia Mar 22 '16 at 02:17
  • I agree, it's probably fragile. It relies on Windows components always going through the PEB and not caching the image's original base address. I think I'm also going to give the fake DLL method a shot, but this was a fun experiment nonetheless. – David Brown Mar 22 '16 at 02:26
  • For the record, the use of `UnmapViewOfFile` to unmap the executable is also unsupported. (In fact I think this might fail in Windows 10? I have a vague memory of a previous question about this.) – Harry Johnston Mar 22 '16 at 02:35
  • Well that certainly would throw a wrench into things, wouldn't it? :/ – David Brown Mar 22 '16 at 02:37
  • I just tried the fake DLL method and it didn't seem to work. Windows encroaches on my desired region of memory before loading the DLL. – David Brown Mar 22 '16 at 03:06