As previously described, the Lockdown modules attempt to disable the use of
the processor's complement of debug registers in order to make it difficult
to utilize so-called hardware breakpoints during the process of reverse
engineering or analyzing a Lockdown module. This scheme is, at present,
relatively easily compromised, however.
There are several possible attacks that could be used:
- Hook the SetThreadContext API and block attempts to disable debug registers
- Patch the import address table entry for SetThreadContext in the Lockdown
module to point to a custom routine that does nothing (programmatic).
- Patch the Lockdown module instruction code to not call SetThreadContext in
the first place (programmatic). However, this is approach is considered to
be generally untenable, due to the memory checksum protection scheme.
- Set a conditional breakpoint on `kernel32!SetThreadContext' that re-applies
the hardware breakpoint" state after the call, or simply alters execution
flow to immediately return (debugger).
Depending on whether the attacker wants to make programmatic alterations to the
behavior of the Lockdown module via hardware breakpoints, or simply wishes
to observe the behavior of the module in the debugger unperturbed, there are
several options available.
The suggested counters include techniques such as the following:
- Verify that the debug registers were really cleared. However, this could
simply be patched out as well. More subtle would be to include the value
of several debug registers in the checksum calculations, but this would also
be fairly obvious to attackers due to the fact that debug registers cannot be
directly accessed from user mode and require a call to Get/SetThreadContext,
or the underlying NtGet/SetContextThread system calls.
- Include additional calls to disable debug register usage in different
locations within the Lockdown module. To be most effective, these would
need to be inlined and use different means to set the debug register state.
For example, one location could use a direct import, another could use a
GetProcAddress dynamic import, a third could manually walk the EAT of
kernel32 to find the address of SetThreadContext, and a fourth could make
a call to NtSetContextThread in ntdll, and a fifth could disassemble the
opcodes comprising NtSetContextThread, determine the system call ordinal,
and make the system call directly (e.g. via `int 2e'). The goal here is to
add additional work and eliminate "single points of failure" from the
perspective of an attacker seeking to disable the anti-debugging feature.
Note that the direct system call approach will require additional work in
order to function under Wow64 (e.g. x64 computers running native Windows
- Verify that all IAT entries corresponding to kernel32 actually point to the
same module in-memory. This is risky, though, as in some cases (such as when
the Microsoft application compatibility layer module is in use), these APIs
may be legitimately detoured.