Address space
Processes have a limited amount of address space. When running 32-bit, a process has roughly 2 GB (<0x7fffffff) of process address space to play with, and 2 GB (>0x8000000) of kernel-mode stuff. All memory allocations, file mappings, DLLs, and other things that you load occur within the application's 2 GB window.
Rebasing
A DLL specifies where it wants to be loaded. If you try to load a DLL whose base address is already in use, the load either fails or the DLL has to be rebased.
How it works
You have two DLLs (x.DLL and y.DLL). Both are compiled in Visual Studio .NET. In Visual Studio, the default or the "preferred" base address (memory location it;s loaded to) is 0x10000000.
- Run MyTestApp.exe.
- LoadLibrary(x.DLL);
- LoadLibrary(y.DLL);
The
x.DLL is loaded up at 0x10000000;
y.DLL tries to load up at 0x10000000, but it can't because
x.DLL is already using that address space. So,
y.DLL is "rebased." The operating system picks a new location to load up the
DLL. Part of the loading process requires the operating system to apply "fixups" so that
y.DLL runs correctly at a location other than 0x10000000.
A
DLL is optimized to load up at its "
preferred" base address. Applying "
fixups" has some overhead to it. Your
DLLs load faster if you configure a "preferred" base address. It mustn't collide with a "preferred" base address of another
DLL that the process loads. In other words, ACC injects into the process and if something else is already injecting into it, the process crashes. To prevent this situation from occurring, the
DLL is rebased (injected into another process).
NOTE: Changing the
DLL that ACC is rebasing from doesn't mean that it isn't injecting. Injection is needed for
SAU/MP/Execution control to get the command-line arguments when a script interpreter is started. It's also needed when running in interactive mode.
If you want to explicitly bypass injection into a particular process, you can add an
attr -w rule or remove that script interpreter from the
Scripts list.
With rebasing defined, changing the
DisableDeviceGuardCompat config might appear to be a workaround, but it isn't. All we're doing is rebasing another
DLL (hooking the
LdrLoadDLL deeper or higher in the stack –
kernel32.DLL ends calling
NTDLL.DLL).
If
DeviceGuardCompat is disabled on the host, and
DeviceGuard is enabled on your endpoint, the operating system doesn't allow injection. It blocks the execution of the binary.
DeviceGuard is an operating system feature (hardware and software) that restricts devices from running unauthorized apps. It allows authorized apps to run by using a feature called
configurable code integrity.
"
DeviceGuardCompat"
has nothing to do with "
Microsoft DeviceGuard."
Our injection mechanism detects when a process is loading a library (DLL). This detection is achieved by knowing the RVA of the
LoadLibrary function. When
DisableDeviceGuardCompat is enabled, the RVA being sought out is
LdrLoadDLL but in the
NTDLL.DLL. Conversely, with
DisableDeviceGuardCompat being disabled (by default), we look for
LoadLibraryW in the
Kernel32.DLL. After we have that address, we allow the injected process to communicate with our driver, when certain functions are invoked.
DisableDeviceGuardCompat doesn't disable or skip the
scinject.DLL to be injected. It changes which function is hooked. There's no risk associated with changing the injection
DLL as we're still protecting the system.