|Informative Information for the Uninformed|
One of the unique consequences of implementing Address Space Layout Randomization (ASLR) on Windows is the limitation that the system allocation granularity imposes on the number of bits that can be randomized within most memory allocations. In particular, the allocation granularity used by Windows enforces strict 16-page alignment for the base addresses of most memory mappings in user-mode. This restriction means that it is only possible to introduce entropy into the low 15 bits of the high-order 16 bits of a 32-bit memory mapping3. The low-order 16 bits remain unchanged relative to the high-order bits. This caveat means that it may be possible to perform a partial overwrite of an address and thus bypass the security features offered by ASLR. However, the ability to perform a partial overwrite also relies on the presence of useful code or data within a region that is relative to the address that is being overwritten.
To visualize how this type of information might be useful, consider a scenario where an attacker is performing a partial overwrite of a return address on the stack. In this situation, it is often necessary for one or more useful opcodes to be present at an address that is 16-page relative to the return address. For example, consider a scenario where the function may have a vulnerability that would permit a partial overwrite. In this example, is called by and . In order to permit the use of a partial overwrite, a useful opcode must be found within the same 16-page aligned region that either or reside on. If a useful opcode is present, an exploitation property can be attached to in order to indicate that a partial overwrite may be feasible due to the presence of a useful opcode within the same 16-page aligned region as either or . For example, consider the following pseudo-disassembly illustrating a case where the call f instruction in is on the same 16-page region as a useful opcode:
... useful jmp on same 16-page region 0x14c1XXXX 0x14c1fc04 jmp esp ... entry point to h() 0x14c1a910 push ebp 0x14c1a911 mov ebp, esp 0x14c1a914 call f ... entry point to y(), not on same 16-page region 0x137f44c8 push ebp
While this captures the basic concept, a better approach might be to view a binary in a different way. For example, consider the following approach to drawing the same conclusion: for each code region that contains a useful opcode, identify the subset of functions that are called from call sites within the same 16-page aligned region as the useful opcode. This has the effect of annotating all of the child functions that could potentially leverage a partial overwrite of the return address with respect to a particular collection of opcodes.
One important point that must be made about this exploitation property is that is entirely dependent upon the definition of "useful code or data". Exploitation is very much an art and it goes without saying that attempting to constrain the approaches that an attacker might make use of is likely to be folly. However, defining a known-set of useful opcodes and using that set as a base with which to draw the above conclusion can be said to be better than not doing so at all.