Does ASLR relocate all DLLs by the same offset?


I've seen multiple claims that the Windows implementation of ASLR chooses a single random offset and applies that same offset to all DLL base addresses.

When the operating system loads, it applies a fixed random value to the DLL base. … The ASLR doesn't move DLL randomly. Without ASLR, if you get collisions, then you will get them with ASLR.

If two DLLs have base addresses to designed to place them consecutively, they'll still be consecutive even with ASLR.

In other words, the claim is that if you have two DLLs, call them DLL1 with base address base1 and DLL2 with base address base2, then, assuming there are no base address collisions with already-loaded DLLs, ASLR will load the two DLLs at base1 + N and base2 + N for some value of N (possibly negative). In particular, this means that if base1 and base2 are adjacent, then the two DLLs will remain adjacent after ASLR, and if the two DLLs have colliding base addresses, then they will also have colliding base addresses after ASLR.

But it's not true, and as far as I can tell, it has never been true.

ASLR chooses the base address pseudo-randomly, though it does take some of the original base addresses into account. For example, if the original base address was below the 4GB boundary, then the new pseudo-random base address will also be below the 4GB boundary.

But it doesn't try to preserve relative base addresses. Each DLL is assigned a new pseudo-random base address independently. There is no correlation, or at least there is no conscious effort to correlate them.

Comments (20)
  1. Mark S says:

    Wouldn't a constant offset weaken ASLR to the point of near-irrelevance? Code that an exploit wishes to run would simply be at a constant offset from the current code, so instead of jumping directly to 0xSomeAddress, it could just jump to EIP + offset

  2. Mike Caron says:

    This is a crazy idea that all DLLs would be moved together... That rather defeats the point of ASLR!

  3. kantos says:

    The only ALSR related nonsense I've ever paid any sort of attention to, and even then only in "eh, probably" sense was that the loader maps the DLL once for the entire system for the duration of the module's lifetime in memory insofar as the module is ALSR compatible. I've taken this to mean that the system likes sharing the PTEs for the modules for as long as it can even though there is no requirement to do so; and I wouldn't be surprised if this was turned off in some cases. To me it also read as: The system may change the mapped address at whim if the module is unloaded and reloaded into physical ram (e.g. all processes had stopped using the module and then one loaded it again).

    1. poizan42 says:

      Yes, it's basically a trade-off between performance and security. x64 code can be position independent without requiring relocations, x86 PIC code comes with a performance penalty. But even if all the code is PIC so the pages can be shared no matter where they are loaded, there is still some performance penalty on context switches by the larger number of PTEs that must be changed.

      If you truly want security before all else you should probably use OpenBSD instead, which has even gone as far as having per-section ASLR (combine that with compiling with -ffunction-sections and -fdata-sections and you get per function and per data item ASLR)

  4. Medinoc says:

    By the way, (in the absence of ASLR), what is the best way to assign "preferred base addresses" to a solution's DLLs? Simply run the program that loads them all and write down the addresses at which they were loaded?

    1. kantos says:

      Don't, just set the no fixed base address and the ALSR flags. The loader will figure it out.

    2. Lars Viklund says:

      Recording the bases that the modules happen to load at seems a bit brittle in the face of changes, but sure, it's one way. I'd recommend a notebook and a list of module sizes.

      What's the thing you hope to achieve with this? A less fragmented address space in 32-bit? Some cache effects? Somewhat less slow load times? Warm fuzzy feeling?

  5. JM says:

    If you've written any kind of software where this would be a desirable or necessary property, you need to rethink your life choices.

    In fact, outside of malware writers, the need to rely on any kind of base address for any DLL whatsoever should be considered suspect in the post 16-bit era where such things are no longer possibly worthwhile for a micro-optimization, even before we take ASLR into account.

    1. Joshua says:

      Having a fixed base address lets you strip relocations. Loading a DLL at its base address was much faster than any other address prior to Windows 8. It may be obsolete *now* but it's no relic of 16 bit land.

      1. Mark S says:

        Right, setting a particular base address to prevent fixups may be an optimization, but your code certainly shouldn't depend on being at a particular address -- your code shouldn't know about it and it shouldn't affect the correctness of your program.

      2. cheong00 says:

        I'd think even with such benefit it'd only make sense for in-house software to "abuse" it, because it leads to "what if two DLLs requests the same base address" problem. In that case one of it would need to be moved anyway.

        1. Joshua says:

          Thankfully with relocs stripped this yields a link time error. If a system DLL got moved on top of one (MS goofed up an XP update that way once) it becomes a load time error.

      3. Darran Rowe says:

        The biggest point of note about this though is how much time does the DLL relocation actually take in the context of a non trivial process startup/initialisation? If you are going to have to wait for seconds for the process to become usable, is shaving of a few milliseconds really worth it?
        The oldest computer I know has no issues running notepad fast enough so that the window has been shown by the time you have finished the click on the icon in start (a core 2 duo). So unless you somehow have a cascade effect where you have hundreds of DLLs relocating, then I really don't see how this is something anyone would do.

    2. SI says:

      For 32bit processes, customizing the base addresses to make them sequential lets you maximize the largest allocatable block in the address space

  6. Ben Voigt (Visual Studio and Development Technologies MVP with C++ focus) says:

    This seems like simple misinterpretation of the adjective "fixed". The value is consistent across processes (unless collisions occur, and collisions for some system DLLs are impossible) on a single machine until the next reboot. Not consistent between different DLLs.

    1. Ben Voigt (Visual Studio and Development Technologies MVP with C++ focus) says:

      FWIW, the negatively voted answer Raymond quoted out of context got it right. It's the comments preceding the link to Old New Thing (presumably explaining the downvotes) that are wrong.

  7. Ismo says:

    How does ASLR affect code segment sharing between processes ? If the dll is relocated differently in every process then those pages can not be shared (where any fixups have been done). When some dlls are in use within many processes what is preferred way to keep their addresses same ? We rebase our dlls to save memory ( and also speed up loadup time). Should we just disable ASLR ?

      1. Ismo says:

        Thanks pointing that out, I've totally missed out that time period ( I was busy arranging fathers funeral ).

  8. smf says:

    "For example, if the original base address was below the 4GB boundary, then the new pseudo-random base address will also be below the 4GB boundary."

    Does that mean you should put your dll's above 4gb in case the first 4gb is full?

Comments are closed.

Skip to main content