This post is about a sort of anti-debugging trick that I discovered while analyzing a malware named Tilon. Well, to be precise, it's more a deception trick than an anti-debugging one but, as we will see later, it's really easy to tweak it to tamper with debugging.
Tilon is a banker that has been spotted by Trusteer in July 2012 and, aside from some pretty standard stuffs, like a Man In The Browser implementation, it's better known for the peculiarity of making use of several evasion techniques. I found one of them, in the attempt of digging deeper in its various encryption/packer layers, that hasn't been reported yet.
All the layers are very easy to bypass: they roughly consist of basic crypto operations and UPX compression. After solving them you will see the following listing:
008D0079 CALL 008D0233
008D007E CALL 008D04DC
008D0084 ADD ESP,4
008D0087 MOV EAX,DWORD PTR SS:[EBP+4020BF]
008D008D JMP EAX
So, we have two CALL and one JMP.
If we step into the first call, we will notice that the malware is going to set an "hook" (it's not properly API hooking as it's only inside the process itself) on the KiUserExceptionDispatcher API (a function of NTDLL.dll that is being called when some types of exception occur) to call (what we will discover to be) a decryption routine.
Pay attention to the code while stepping... As I reminded you before, Tilon is famous for the number of the deception tricks implemented!
For example, the next call makes use of a well known PEB related anti-debugging trick:
008D04DC MOV EAX,DWORD PTR FS:
008D04E2 MOV EAX,DWORD PTR DS:[EAX+30]
008D04E5 MOVZX EAX,BYTE PTR DS:[EAX+2]
008D04E9 TEST EAX,EAX
008D04EB JNZ SHORT 008D04EE
Finally, there is a jump that brings us to an encrypted code:
Access violation, indeed!
Since there is no Exception Handler installed, one may think that the code has crashed for some reason that he missed and he will restart the debugger to conduct a more precise analysis. On the other hand, if we use Shift+F8 the malware will decrypt the code because of the previous "hook"! Finally, it will also delete the "hook" from the KiUserExeceptionDispatcher API and jump to the decrypted bytes.
So, this is really not an anti-debugging trick, even if it will work fine in some cases, like against emulators, but, let's stay focused: our goal is to fool the debugger... How can we do it? Well, there are surely several ways to do that, the one I have in mind consists of mixing this trick, the PEB one and... one more finding!
Let me explain it briefly: when Tilon generates the exception, by pressing Shift+F8 the debugger will execute the first instruction of the "hook", but will break only on the second one.
Thus we have:
7C91EAEC 68 FA028D00 PUSH 8D02FA ; the first instruction is executed...
7C91EAF1 C3 RETN ; but the debugger will break only here!
That gives us the possibility of hiding an instruction!
Putting the pieces together:
Tilon: you are doing it wrong...!
Now I will show you one way to improve the trick in the Tilon's code:
STEP 1. We need to change the decryption key with a wrong one and to set a global variable (for instance, 00BD0AB0) containing the address of the decryption routine (in my case 008D02FA).
STEP 2. We modify the hook by writing: JMP [008D0AB0](this instruction will be hidden).
STEP 3. We modify the PEB trick (inside the last call before the jump to the encrypted bytes; see the listing at the beginning of this blog-entry) in the following way:
008D0A00 MOV EAX,DWORD PTR FS:
008D0A06 MOV EAX,DWORD PTR DS:[EAX+30]
008D0A09 MOVZX EAX,BYTE PTR DS:[EAX+2]
008D0A0D JNZ SHORT 008D0A19
008D0A0F MOV DWORD PTR DS:[8D0AB0], 8D0A1A ; no debugger!
008D0A1A MOV BYTE PTR DS:[8D036D],7A ; small snippet in case of no debugger
008D0A21 JMP 008D02FA
008D0A26 ADD BYTE PTR DS:[EAX],AL
When the code will jump to the encrypted bytes, the exception will call the "hooked" KiUserExceptionDispatcher API, that:
- will jump directly to 008D2FA (the decryption routine), using the wrong key, if the debugger is detected;
- will jump to the small snippet in the listing above, that will restore the correct decryption key and then jump to 008D2FA, otherwise.
In this way, if an analyst doesn't notice the PEB control (stepping over its call, for instance), the bytes won't be decrypted in the right way and this will cause a crash.
In my opinion, this version of the trick is way better than Tilon's original implementation, but we can improve it much more if we chose not to consider how it was originally structured (that is strongly related to the exception caused by the execution of the encrypted bytes...).
Another way to implement the trick (my fav one ;))
Another variant of Tilon's original implementation of the trick is the following.
We have to set a global variable (let's say 00BD0AB0) that contains an address memory, depending on the result of the PEB anti-debugging trick. Then, we need to generate an appropriate exception (for example, by reaching a null pointer) and to "hook" the KiUserDispatcher API by injecting a JMP 00BD0AB0 (the hidden instruction!).
Thus, we have something like:
mov eax, 0
mov [eax], 0
* junk code *
The idea is to use the PEB check to set the global variable 00BD0AB0 to the address of * junk code * if the debugger is revealed, and to set it to the right address (where the real code is) otherwise. In this way, the analyst may not notice the "hook" at all, but will use shift+F8 to continue its debugging from the instruction right after the exception.
The following diagram will clarify the procedure:
Of course you can choose a different (more subtle and less visible) way to generate the exception, but the thing is that you can really confuse the analysis using the * junk code * and this can be really time consuming from the analyst perspective. For instance, you can insert some junk code and then terminate the process, or anything else.
Moreover, it's less detectable than setting the jump directly in the PEB check, because of the hidden instruction and the fact that the debugging will continue its execution after the exception itself like you would normally expect.
Note also that the PEB check is only one of the possible tricks to detect the debugger and you can obviously chose a different one!
You can use this technique on its own, or mixing it with other tricks. In case you chose to combine different tricks together, the risk of being detected will increase... but so will the number of possible uses you can make!