Reversing : The Hacker's Guide to Reverse Engineering

(ff) #1
previous, encrypted block, and then to XOR the result with the key. This algo-
rithm is quite secure and should not be compared to a simple XOR algorithm,
which is highly vulnerable. In a simple XOR algorithm, the key is fairly easily
retrievable as soon as you determine its length. All you have to do is find bytes
that you know are encrypted within your encrypted block and XOR them with
the encrypted data. The result is the key (assuming that you have at least as
many bytes as the length of the key).
Of course, as I’ve demonstrated, a CBC is vulnerable to brute-force attacks,
but for this it would be enough to just increase the key length to 64-bits or
above. The real problem in copy protection technologies is that eventually the
key must be available to the program, and without special hardware it is
impossible to hide the key from cracker’s eyes.

Reencrypting

Defender reencrypts each function before that function returns to the caller.
This creates an (admittedly minor) inconvenience to crackers because they
never get to the point where they have the entire program decrypted in mem-
ory (which is a perfect time to dump the entire decrypted program to a file and
then conveniently reverse it from there).

Obfuscated Application/Operating System Interface


One of the key protection features in Defender is its obfuscated interface with
the operating system, which is actually quite unusual. The idea is to make it
very difficult to identify calls from the program into the operating system, and
almost impossible to set breakpoints on operating system APIs. This greatly
complicates cracking because most crackers rely on operating system calls for
finding important code areas in the target program (think of the Message
BoxAcall you caught in our KeygenMe3 session).
The interface attempts to attach to the operating system without making a
single direct API call. This is done by manually finding the first system com-
ponent (NTDLL.DLL) using the TEB, and then manually searching through its
export table for APIs.
Except for a single call that takes place during initialization, APIs are never
called through the user-mode component. All user-mode OS components are
copied to a random memory address when the program starts, and the OS is
accessed through this copied code instead of using the original module. Any
breakpoints placed on any user-mode API would never be hit. Needless to say,
this has a significant memory consumption impact on the program and a cer-
tain performance impact (because the program must copy significant amounts
of code every time it is started).

416 Chapter 11

Free download pdf