Problem
I am using mingw-w64 GCC to compile and link a C program on Windows 10 Home, build 1809 (I know it's old--long story). I want to disable Address Space Layout Randomization (ASLR) so my heap addresses do not change from run to run while debugging. How do I do that?
Sample program:
// testaslr.c
#include <stdio.h> // printf
#include <stdlib.h> // malloc
int main()
{
printf("allocation: %p\n", malloc(16));
return 0;
}
Compiling and running:
$ gcc -o testaslr -g -Wall testaslr.c
$ ./testaslr
allocation: 00000000007A1420
$ ./testaslr
allocation: 0000000000A81420
$ ./testaslr
allocation: 0000000000BF1420
As you can see, the address returned by malloc
changes each time. I want it to be the same value on every run.
Toolchain
I'm using mingw-w64 x86_64-8.1.0-release-posix-seh-rt_v6-rev0.7z. (I was previously using x86_64-5.4.0-release-posix-seh-rt_v5-rev0.7z, and saw the same behavior.)
$ gcc --version
gcc.exe (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0
[...copyright stuff...]
$ ld --version
GNU ld (GNU Binutils) 2.30
[...copyright stuff...]
Although I am using cygwin shell utilities, I am not using the cygwin compiler or linker. The executable is only linked with MSVCRT plus the usual Windows libraries:
$ ldd testaslr.exe
ntdll.dll => /cygdrive/c/WINDOWS/SYSTEM32/ntdll.dll (0x7fff5ad40000)
KERNEL32.DLL => /cygdrive/c/WINDOWS/System32/KERNEL32.DLL (0x7fff57f60000)
KERNELBASE.dll => /cygdrive/c/WINDOWS/System32/KERNELBASE.dll (0x7fff56fa0000)
msvcrt.dll => /cygdrive/c/WINDOWS/System32/msvcrt.dll (0x7fff5a3b0000)
Attempt 1: --disable-dynamicbase
According to the binutils documentation, there should be an option to ld
called --disable-dynamicbase
to disable ASLR. But it is not recognized:
$ gcc -o testaslr -g -Wall testaslr.c -Wl,--disable-dynamicbase
E:/cygwin/home/Scott/opt/mingw64-8.1.0/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: unrecognized option '--disable-dynamicbase'
E:/cygwin/home/Scott/opt/mingw64-8.1.0/bin/../lib/gcc/x86_64-w64-mingw32/8.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe: use the --help option for usage information
collect2.exe: error: ld returned 1 exit status
Meanwhile, -Wl,--dynamicbase
is accepted but does not alter the behavior.
Attempt 2: Clear DYNAMIC_BASE afterward
I found a utility called setdllcharacteristics that purports to clear the ASLR flag in the executable. However, running it seems to indicate the flag is already false:
$ setdllcharacteristics.exe -d testaslr.exe
Original DLLCHARACTERISTICS = 0x0000
DYNAMIC_BASE = 0
NX_COMPAT = 0
FORCE_INTEGRITY = 0
Updated DLLCHARACTERISTICS = 0x0000
DYNAMIC_BASE = 0
NX_COMPAT = 0
FORCE_INTEGRITY = 0
and the address is still different on each run after running that tool.
Interestingly, if I pass -Wl,--dynamicbase
to the compiler, then setdllcharacteristics
does show the flag as having been set, but after clearing it, the addresses are still different on each run. This leads me to conclude that DYNAMIC_BASE
is not what is responsible for the varying addresses. But in that case, I don't know what is or how to change it, even after a lot of googling.
Not attempted: MoveImages registry setting
I am aware there is a registry setting, HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management\MoveImages
according to this blog post, that might disable ASLR. But according to that same blog post that requires a reboot, which is quite annoying, and ideally I only want this for the one program I am debugging, not my entire system. I'm also doubtful changing that would have any effect given that clearing DYNAMIC_BASE
did not.
Speculation: MSVCRT switch?
If DYNAMIC_BASE
is not the culprit, then perhaps this is something MSVCRT does on its own. I found an MSDN page that mentions heap randomization. The way the page is written, I can't tell whether that is controlled by the DYNAMIC_BASE
flag, but my experiments seem to indicate it is not. The text also says:
Heap randomization is enabled by default for all applications running on Windows Vista and later.
but does not say how to disable it.