00404219 TEST EAX,EAX
0040421B JNZ SHORT Defender.0040420D
0040421D XOR ECX,ECX
0040421F PUSH Defender.0040322E
00404224 CALL ECX
00404226 RETN
00404227 MOV ECX,DWORD PTR DS:[ECX+4]
0040422A ADD ECX,DWORD PTR DS:[406014]
00404230 JMP SHORT Defender.0040421F
This function performs another one of the familiar copied export table
searches, this time on the copied KERNEL32memory block (whose pointer is
stored at 406004 ). It then immediately calls the found function. You’ll use the
function index trick that you used before in order to determine which API is
being called. For this you put a breakpoint on 404227 and observe the address
loaded into ECX. You then subtract KERNEL32’s copied base address (which
is stored at 406004 ) from this address and divide the result by 8. This gives
us the current API’s index. You quickly run DUMPBIN /EXPORTS on
KERNEL32.DLLand find the API name: SetUnhandledExceptionFilter.
It looks like Defender is setting up 0040322Eas its unhandled exception fil-
ter. Unhandled exception filters are routines that are called when a process
generates an exception and no handlers are available to handle it. You’ll worry
about this exception filter and what it does later on.
Let’s proceed to another call to NtDelayExecution, followed by a call to
another internal function, 401746. This function starts with a very familiar
sequence that appears to be another decryption sequence; this function is also
encrypted. I won’t go over the decryption sequence, but there’s one detail I
want to discuss. Before the code starts decrypting, the following two lines are
executed:
00401785 MOV EAX,DWORD PTR DS:[406008]
0040178A MOV DWORD PTR SS:[EBP-9C0],EAX
The reason I’m mentioning this is that the variable [EBP-9C0]is used a few
lines later as the decryption key (the value against which the code is XORed to
decrypt it). You probably don’t remember this, but you’ve seen this global
variable 406008 earlier. Remember when the first encrypted function was
about to return, how it reencrypted itself? During encryption the code calcu-
lated a checksum of the encrypted data, and the resulting checksum was
stored in a global variable at 406008. The reason I’m telling you all of this is
that this is an unusual property in this code—the decryption key is calculated
at runtime. One side effect this has is that any breakpoint installed on
encrypted code that is not removed before the function is reencrypted would
change this checksum, preventing the next function from properly decrypting!
Defender is doing as its name implies: It’s defending!
Breaking Protections 403