Koobface Anti-Emulation Time Lock Trick

Koobface contains a lot of interesting tricks, components, and schemes to write about. In the interest of keeping this post somewhat brief, we’ll focus on an anti-emulation technique that may be keeping the AV detection rates low for repacked and redistributed Koobface executables, while at the same time providing vendors with a false or confused sense of effectiveness.

At worm runtime in the lab, we observed that one unpacked loop in particular was taking excessively long to execute prior to any identifiable malicious behavior from the worm, and this same loop is present in four of the five binaries that the Koobface flash_update component is dropping. So we took another look.

Within the common loop, GetTickCount() was called and bytes were moved and compared, but it seemed that no real decryption was occuring. Persistent data was not rewritten or modified. This sort of activity is suggestive of something called a time-lock: “A common anti-emulation trick is to introduce loops that take a relatively long time to compute. The loop may in fact take so long to emulate that the antivirus scanner gives up.”
However, what appears to be a Koobface time lock implementation is very simple, much more simple in its implementation than the ones formalized in the paper linked to above. But the concept is just as interesting in that it implements a variable length lock duration at runtime. The amount of time dedicated to spinning through the loop is in part dependent on the amount of time that the system has been running. And yet, the technique’s usage is uncommon in that there are not two timing calls and a hardcoded value to compare against to detect emulation. Instead, this unusual variability is due to its unique use of a singular GetTickCount() call and a comparison to a counter value that is also being incremented.

Here is the loop that we were interested in, as viewed through a debugger:

00401712 |> /83C9 FF /or ecx, FFFFFFFF
00401715 |. |33C0 |xor eax, eax
00401717 |. |8D7C24 10 |lea edi, dword ptr ss:[esp+10]
0040171B |. |F2:AE |repne scas byte ptr es:[edi]
0040171D |. |F7D1 |not ecx
0040171F |. |49 |dec ecx
00401720 |. |3BF1 |cmp esi, ecx
00401722 |. |73 18 |jnb short DA3FE57A.0040173C
00401724 |. |8BC6 |mov eax, esi
00401726 |. |99 |cdq
00401727 |. |B9 05000000 |mov ecx, 5
0040172C |. |F7F9 |idiv ecx
0040172E |. |8A4C34 10 |mov cl, byte ptr ss:[esp+esi+10]
00401732 |. |B0 FB |mov al, 0FB
00401734 |. |2AC2 |sub al, dl
00401736 |. |02C8 |add cl, al
00401738 |. |884C34 10 |mov byte ptr ss:[esp+esi+10], cl
0040173C |> |46 |inc esi
0040173D |. |FFD3 |call near ebx ; kernel32.GetTickCount
0040173F |. |83C9 FF |or ecx, FFFFFFFF
00401742 |. |8BD0 |mov edx, eax
00401744 |. |33C0 |xor eax, eax
00401746 |. |8D7C24 10 |lea edi, dword ptr ss:[esp+10]
0040174A |. |F2:AE |repne scas byte ptr es:[edi]
0040174C |. |F7D1 |not ecx
0040174E |. |49 |dec ecx
0040174F |. |03D1 |add edx, ecx
00401751 |. |3BF2 |cmp esi, edx
00401753 |.^72 BD jb short DA3FE57A.00401712

When the first location is jumped into, esi is already set to “0″.
It’s a nice loop — the last insruction takes you back to the first almost every time, but the loop is executed an unpredicatable number of times. Mostly all of the instructions within the loop are arbitrary in that they do not modify any data. For example, the “repne scas byte ptr es:[edi]” instructions simply read through a hard-coded string, looking for the null byte at the end of the string. That same string is read again and again, almost like a strlen() that doesn’t return a value:
void strlen (const char * str) {
const char *pstr = str;
while( *pstr++ ) ;
When GetTickCount is called, it returns the number of milliseconds that have elapsed since the system was started. Because this value changes on every system it is run and every time it is called, the duration of the time lock will be unpredictable. When edx (our TickCount) is sub’d from esi, the CF flag is set to “1″, and the jb instruction sees the value as “below”, so we jump back to the first instruction location. Every loop execution, both the esi and edx values are incremented and then compared. When they are equal, the loop is exited.

Therefore, the effect is that emulators may “give up” on this executable due to the loop, but the behavior is somewhat unpredictable. Sometimes, the lock duration will be short enough for an emulator to hang on and perform an erratic detection. This erratic detection may cause confusion for AV scanner vendors relying on emulation capabilities and create a false sense of effective detection.

Finally, it is interesting that the “worm” components and droppers contain this same loop, as though it were a macro simply copied and pasted into the source for the various executables. The final and money making adware payload, “tinyproxy.exe”, did not contain the loop. The adware/spyware component most likely was obtained from another party.

This entry was posted in Online Fraud. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *


You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>