Reversing : The Hacker's Guide to Reverse Engineering

(ff) #1

one another using SBB(subtract with borrow). SBBsubtracts the two integers
and treats the carry flag (CF) as a borrow indicator in case the first subtraction
generated a borrow. For more information on 64-bit arithmetic refer to the sec-
tion on 64-bit arithmetic in Appendix B.
The result of the subtraction is compared to 77359400. If it is below, the
function just loops back to the beginning. If not (or if the SBBinstruction pro-
duces a nonzero result, indicating that the high part has changed), the function
goes through another exported function search, this time looking for a func-
tion whose string checksum is 1BF08AE, and then calls this API. You’re not
sure which API this is at this point, but stepping over this code is very insight-
ful. It turns out that when you step through this code the check almost always
fails (whether this is true or not depends on how fast your CPU is and how
quickly you step through the code). Once you get to that API call, stepping into
it in SoftICE you see that the program is calling NtTerminateProcess.
At this point, you’re starting to get a clear picture of what our thread is all
about. It is essentially a timing monitor that is meant to detect whether the
process is being “paused” and simply terminate it on the spot if it is. For this,
Defender is utilizing the RDTSCinstruction and is just checking for a reasonable
number of ticks. If between the two invocations of RDTSCtoo much time has
passed (in this case too much time means 77359400 clock ticks or 2 billion clock
ticks in decimal), the process is terminated using a direct call to the kernel.


Defeating the “Killer” Thread


It is going to be effectively impossible to debug Defender while this thread is
running, because the thread will terminate the process whenever it senses that
a debugger has stalled the process. To continue with the cracking process, you
must neutralize this thread. One way to do this is to just avoid calling the
thread creation function, but a simpler way is to just patch the function in
memory (after it is decoded) so that it never calls NtTerminateProcess. You
do this by making two changes in the code. First, you replace the JNZat
00403075 with NOPs(this check confirms that the result of the subtraction is
0 in the high-order word). Then you replace the JNZat address 0040307E
with a JMP, so that the final code looks like the following:


00403075 NOP
00403076 NOP
00403077 CMP DWORD PTR SS:[EBP-60],77359400
0040307E JMP SHORT Defender.004030C2

This means that the function never calls NtTerminateProcess, regardless
of the time that passes between the two invocations of RDTSC. Note that apply-
ing this patch to the executable so that you don’t have to reapply it every time
you launch the program is somewhat more difficult because this function is


Breaking Protections 399
Free download pdf