In one of our analysis of the TrickBot banking trojan, we found an interesting anti-sandbox that catches (almost) all user-mode (ring3) sandboxes, and we would like to share it with you.
The sample does something like this:
lea eax, [ebp+time] ; 1. prepare buffer for GetSystemTime push eax call GetSystemTime ; 2. malware goes straight to Windows API pushf pop eax ; 3. read EFLAGS add eax, offset sub_x ; 4. skip EFLAGS bytes from sub_x jmp eax
The sample starts by calling GetSystemTime – a Windows API call, which does its internal work, and returns a meaningful value to whoever called it; in this case, it fills the buffer noted above with the current system time. However, this result is discarded – the code looks for side-effects of that call.
The side-effect that the code is looking for, is the value of the EFLAGS register. This register is made up from many flags, such as the CF (carry) flag, the ZF (zero) flag, etc., which are set when instructions related to mathematical operations or comparisons are executed, and carry information about the result of the operation.
The Windows API call GetSystemTime is made up from a specific, defined set of instructions, and each and every one of them modifies the EFLAGS register in a certain way. That is, every time you call this API, it will set that register to a specific, constant value as a side-effect.
However, when sandboxes attempt to modify the behavior of the call (e.g. in order to do its original work, but also log it afterward), they have to hook the function. Hooking it means that they’re changing its instructions to something else, in order to alter the execution flow. And when they alter the execution flow, they end up producing different side-effects. Thus, the final value of EFLAGS will not be the same as before the modification, because the function is now executing different instructions.
This sample abuses those side-effect values as a checksum: it knows that, if a sandbox modifies the API call, it will also modify its side-effects, and that means that the final value of the EFLAGS register won’t be the expected one, and therefore will not be a valid checksum.
In fact, the checksum is used as an offset to a function, meaning that if the code is run outside of a sandbox (and calculates the proper checksum), the offset will be right, and execution will continue. Otherwise, the offset will be mismatched, the sandbox detected, and the process will crash, preventing further monitoring.
Concluding, the sample is using the result of the EFLAGS register as a correctness check. You can think of it as a checksum: given a defined set of values and instructions executed, you always get the same EFLAGS after execution. When it calls GetSystemTime, the function modifies EFLAGS to always have the same value; in this case, the result is 0x246. But if the function is hooked, the set of instructions is not the same anymore, resulting in a different value for EFLAGS, which invalidates the checksum, detecting the hook.
In order to avoid this detection, every hook should preserve the EFLAGS register. However, our upcoming technology “Kernel Monitor” runs completely in kernel space and is not affected by any detection of this class, as there are no hooks that would inadvertently alter execution flow in subtle ways. (beta / not public yet)