Delay loaded DLLs are loaded too far away in memory from each other

2

I have an x64 Windows application which sometimes crashes within a DLL library which I unfortunately have no control over. This library is actually a large set of inter-dependent DLLs (~40 of them). The crash isn't always in the same place, but here's an example of what is happening:

Exception thrown at 0x0000018393916B72 (foo.dll) in bar.exe: 0xC0000005: Access violation reading location 0x000001831C1F95A0.

At this location, the disassembly in Visual Studio shows the following:

0000018393916B72  cmp         dword ptr [1831C1F95A0h],3  

This at first seemed odd to me, since that means the DLL would be hard-coded to load a value at a bad location. So, I took a look at the actual instruction memory and found the following:

83 3d 27 2a 8e 88 03

Which disassembles exactly to:

cmp    DWORD PTR [rip+0xffffffff888e2a27],0x3

What's happened here is that an instruction was generated in the DLL which says "Compare the value located X bytes after this instruction to 3." This memory resides in a different DLL from this one, and X is a 32-bit value. I assume that the value of X gets patched when that other DLL is loaded.

Looking at the actual bytes of the instruction, it's clear that it wants rip+0x888e2a27, which does point to where one of the DLLs in this library resides, but unfortunately this 32-bit value gets sign extended and it ends up looking backwards in memory instead of forwards.

Ultimately, the root cause is that these DLLs were loaded into memory more than 0x7FFFFFFF bytes from each other, and thus when they try to reference each other with a 32-bit offset, they fail to do so. I'm unsure why the compiler which generated these DLLs opted to use an RIP+32-bit instruction instead of a 64-bit absolute address, but it seems to have done so quite frequently. Possibly a size-saving measure?

Either way, I figure the easiest way to resolve this issue is to force LoadLibrary to load these DLLs next to each other, or at least relatively close to each other. I have tried various way to do this, but it seems Windows is purposefully random and opaque about where it decides to load DLLs.

Is there any way around this problem? Here are my potential solutions, but I don't know how to do any of them:

  1. Force LoadLibrary to load the DLLs either at a specific address, or with some kind of option which forces these DLLs to be near each other in memory.

  2. Modify the DLLs themselves to remove any RIP+(32-bit value) instructions, and replace them with an instruction which supports 64-bit address values.

  3. If I could potentially get the vendor to recompile the DLLs, have them use a specific compiler flag to prevent these RIP+(32-bit value) instructions from being used. I don't know what this compiler flag would be, however. I assume they used MSVC but I'm not actually sure.

c++
windows
visual-studio
dll
loadlibrary
asked on Stack Overflow Jan 24, 2018 by David Clamage • edited Jan 24, 2018 by genpfault

1 Answer

0

There is no guarantee that this will work, but you could try to edit the DLLs in question with the EDITBIN command so that they are flagged as not supporting ASLR.

The command to do that would be:

editbin /DYNAMICBASE:NO /HIGHENTROPYVA:NO foo.dll

Editbin comes with your msvc / windows sdk installations.

For more info, see: https://msdn.microsoft.com/en-us/library/d25ddyfc.aspx

If mandatory ASLR is enabled, the loader will search for a base address for such DLLs starting from the bottom of the address space. So maybe that means they end up close enough with each other.

This behavior is explained in:

https://blogs.technet.microsoft.com/srd/2017/11/21/clarifying-the-behavior-of-mandatory-aslr/

The last bit to try would be to load the DLL as early as possbile in the execution of your application so as much as possible of the address space would be free.

Good luck :-).

answered on Stack Overflow Jan 26, 2018 by Sami Sallinen

User contributions licensed under CC BY-SA 3.0