To make it very difficult to determine which API the program is trying to
call APIs are searched using a checksum value computed from their names,
instead of storing their actual names. Retrieving the API name from its check-
sum is not possible.
There are several weaknesses in this technique. First of all, the implementa-
tion in Defender maintained the APIs order from the export table, which sim-
plified the process of determining which API was being called. Randomly
reorganizing the table during initialization would prevent crackers from using
this approach. Also, for some APIs, it is possible to just directly step into the
kernel in a kernel debugger and find out which API is being called. There
doesn’t seem to be a simple way to work around this problem, but keep in
mind that this is primarily true for native NTDLL APIs, and is less true for
Win32 APIs.
One more thing—remember how you saw that Defender was statically
linked to KERNEL32.DLLand had an import entry for IsDebuggerPresent?
The call to that API was obviously irrelevant—it was actually in unreachable
code. The reason I added that call was that older versions of Windows
(Windows NT 4.0 and Windows 2000) just wouldn’t let Defender load without
it. It looks like Windows expects all programs to make at least one system call.
Processor Time-Stamp Verification Thread
Defender includes what is, in my opinion, a fairly solid mechanism for making
the process of live debugging on the protected application very difficult. The
idea is to create a dedicated thread that constantly monitors the hardware
time-stamp counter and kills the process if it looks like the process has been
stopped in some way (as in by a debugger). It is important to directly access
the counter using a low-level instruction such as RDTSCand not using some
system API, so that crackers can’t just hook or replace the function that obtains
this value.
Combined with a good encryption on each key function a verification
thread makes reversing the program a lot more annoying than it would have
been otherwise. Keep in mind that without encryption this technique wouldn’t
be very effective because crackers can just load the program in a disassembler
and read the code.
Why was it so easy for us to remove the time-stamp verification thread in
our cracking session? As I’ve already mentioned, I’ve intentionally made
Defender somewhat easier to break to make it feasible to crack in the confines
of this chapter. The following are several modifications that would make a
time-stamp verification thread far more difficult to remove (of course it would
alwaysremain possible to remove, but the question is how long it would take):
Breaking Protections 417