Uninformed: Informative Information for the Uninformed

Vol 7» 2007.May


There are a few considerations that should be noted about this approach. On the positive side, this approach is unique when compared to the others described in this paper due to the fact that, in principle, it should be possible to use it to trap memory accesses in kernel-mode, although it is expected that the implementation may be much more complicated. This approach is also much simpler than the other approaches in that it requires far less code. While these are all good things, there are some negative considerations that should also be pointed out. These are enumerated below:

  1. Will not work on x64
    The segmentation approach described in this section will not work on x64 due to the fact that the DS, ES, and even SS segment selectors are effectively ignored when the processor is in 64-bit mode[1].
  2. Significant performance overhead
    Like many of the other approaches, this one also suffers from significant performance overhead involved in having to take a general protection and debug exception fault for every address reference. This approach could be be further optimized by creating a custom LDT entry (using NtSetLdtEntries) that describes a region whose base address is 0 and length is n where n is just below the address of the region(s) that should be monitored. This would have the effect of allowing memory accesses to succeed within the lower portion of the address space and fail in the higher portion (which is being monitored). It's important to note that the base address of the LDT entry must be zero. This is problematic since most of the regions that one would like to monitor (heap) are allocated low in the address space. It would be possible to work around this issue by having NtAllocateVirtualMemory allocate using MEM_TOP_DOWN.
  3. Requires a disassembler
    Unfortunately, this approach also requires the use of a disassembler in order to extract the effective address that caused the access violation exception to occur. This is necessary because general protection faults that occur due to a segment selector issue generate exception records that flag the fault address as being 0xffffffff. This makes sense in the context that without a valid segment selector, there is no way to accurately calculate the effective address. The use of a disassembler means that the code is inherently more complicated than it would otherwise need to be. There may be some way to craft a special LDT entry that would still make it possible to determine the address that cause the fault, but the author has not investigated this.