RolfRolles <rolf rolles gmail com> |
Tuesday, February 5 2008 15:33.02 CST |
From time to time I notice an obvious spam account in the "Active in Last 5 Minutes" box or on the 'Users' page. This one in particular caught my attention. You can see several pages of links to useless things (such as "poster of vin diesel in shower"), with all of those links pointing to .cn domains. I figured I'd be infected by malware if I visited one of the sites, so I opened up the link inside of a VM.
It was not at all surprising when the page ran a fake spyware scan and then urged me to install its anti-spyware product to get rid of all of the dangerous rootkits infecting my system. The webpage serves you a small downloader which is packed with something I've never seen before. All in all the packer is trivial, but it uses an anti-debug trick that I hadn't seen before:
.text:004042F7 push 0
.text:004042F9 call dword ptr [eax] ; <- msvcrt!_CIacos
.text:004042FB mov edx, eax ; <- eax = 0x00321EA8
.text:004042FD imul edx, 10000h ; <- edx = 0x1EA80000
...
.text:004042D8 push 0E1A8A200h
.text:004042DD pop esi
.text:004042DE add esi, edx ; <- esi = 0x0050A200, read-only memory
.text:004042E0 mov edi, esi
.text:004042E2
.text:004042E2 loc_4042E2:
.text:004042E2 lodsd
.text:004042E3 xor eax, 0C2EA41h
.text:004042E8 stosd ; <- access violation writing to read-only memory
.text:004042E9 loop loc_4042E2
[EDIT 02/14: apparently the up arrow in the ; CODE XREF was causing problems with WoodMann's blog imports]
If, on line 004042DE, you modify the register esi to instead contain 0x0040A200 (e.g. subtract 0x00100000), unpacking proceeds as usual. Therefore I surmised that msvcrt!CIacos(0) returns 0x00321E98 if a debugger is not present, 0x00321EA8 otherwise. I wrote a small test application that confirms this behavior. Comments on this point would be appreciated.
The downloader installs itself as an autorun in the registry, downloads a file off of some Californian webserver, runs it, and then writes out a hard-coded file called c:\Program Files\SpyShredder\SpyShredder.lic. There's only three non-library functions inside of the downloader, so it makes for pretty quick work, although it's MSVC on high optimization settings so the instruction scheduling and inlined string operations are a bit hard to read.
At this point all of the "SpyShredder" references caught my attention, so I googled it and submitted the samples to VirusTotal. I stopped analyzing when I saw that F-Secure has a pretty good write-up on it -- it's one of those trojans that pops up many official-looking nag windows warning you about the sky falling, and promises that the windows will go away if you pay up. There's a technical term for that behavior. The United States' Federal Trade Committee calls it "extortion".
The moral of the story? Spamming malware onto a site for professional reverse engineers is plain stupid.
Hi RolfRolles,
you are right, try this too msvcrt!_CIasin().
it is an indirect NtGlobalFlag check.
|
|
I'll have to look into it ... it's strange to imagine that a trigonometric function would behave differently under a debugger, but that is indeed what is happening. It has "heisenbug" written all over it. Thanks for the pointer neoxfx, I'll be investigating it. |
Nice find Rolf. I was thinking it was more related to the difference in heap allocations when a target is run under a debugger. See here for reference:
http://www.openrce.org/forums/posts/594#1982
That magic value returned in the malware is directly related to TlsGetValue when used by _CIacos(). You can see the same effect in Notepad, which also uses msvcrt and ultimately some TlsGetValue calls.
For _CIacos that's here:
// msvcrt.dll WinXP
:77C39F25 sub_77C39F25 proc near
:77C39F25 mov edi, edi
:77C39F27 push esi
:77C39F28 push edi
:77C39F29 call ds:GetLastError
:77C39F2F push dwTlsIndex ; dwTlsIndex
:77C39F35 mov edi, eax
:77C39F37 call ds:TlsGetValue
TlsGetValue directly returns the value 0x00321E90 if a debugger is not present, 0x00321EA0 otherwise. The extra "8" is added a little later to give the final values seen.
kernel32!TlsGetValue gives us:
:7C809750 TlsGetValue proc near
:7C809750
:7C809750 dwTlsIndex = dword ptr 8
:7C809750
:7C809750 mov edi, edi
:7C809752 push ebp
:7C809753 mov ebp, esp
:7C809755 mov eax, large fs:18h
:7C80975B mov ecx, [ebp+dwTlsIndex]
:7C80975E cmp ecx, 40h
:7C809761 jnb loc_7C84463F
:7C809767 and dword ptr [eax+34h], 0
:7C80976B mov eax, [eax+ecx*4+0E10h] // TEB.TlsSlots
:7C809772
:7C809772 pop ebp
:7C809773 retn 4
:7C809773 TlsGetValue endp
[eax+ecx*4+0E10h] is TEB.TlsSlots. The slot indexes appear to contain allocated memory block addresses.
Now we know that TlsGetValue must have a corresponding TlsSetValue, so if you search around in msvcrt.dll you can see where the dll sets up Tls values as the returned value from calloc() through the lpParameter of CreateThread. For example:
:77C3A28E loc_77C3A28E: ; CODE XREF: _beginthread+E
:77C3A28E push esi
:77C3A28F push 88h
:77C3A294 push 1
:77C3A296 call calloc
:77C3A29B mov esi, eax
...
:77C3A2A9 mov eax, [ebp+arg_8]
:77C3A2AC pop ecx
:77C3A2AD push esi ; lpThreadId
:77C3A2AE push 4 ; dwCreationFlags
:77C3A2B0 push esi ; lpParameter
:77C3A2B1 push offset StartAddress ; lpStartAddress
:77C3A2B6 push [ebp+dwStackSize] ; dwStackSize
:77C3A2B9 mov [esi+4Ch], edi
:77C3A2BC push 0 ; lpThreadAttributes
:77C3A2BE mov [esi+50h], eax
:77C3A2C1 call ds:CreateThread
And if we check the beginning of the new thread at lpStartAddress, the first thing it does is to set the TlsValue as the calloc() mem allocation address from the lpParameter variable:
:77C3A1D7 StartAddress proc near ; DATA XREF: _beginthread+43
:77C3A1D7
:77C3A1D7 lpTlsValue = dword ptr 8
:77C3A1D7
:77C3A1D7 push 0Ch
:77C3A1D9 push offset stru_77C140C8
:77C3A1DE call __SEH_prolog
:77C3A1E3 push dwTlsIndex ; dwTlsIndex
:77C3A1E9 call ds:TlsGetValue
:77C3A1EF mov esi, eax
:77C3A1F1 test esi, esi
:77C3A1F3 jnz short loc_77C3A212
:77C3A1F5 mov esi, [ebp+lpTlsValue]
:77C3A1F8 push esi ; lpTlsValue
:77C3A1F9 push dwTlsIndex ; dwTlsIndex
:77C3A1FF call ds:TlsSetValue
So, that's how those TEB.TlsSlots values get in there and which the malware makes use of by indirectly calling TlsGetValue.
As for the address differences, when under a debugger (and debug heap allocations are used) the addresses are something like xx1EA0. When not under a debugger (and heap allocations are of a regular type), the addresses are xx1E90. The 0x10 difference in offset is likely due to the extra size of a debug heap allocation.
If you compare the calloc() allocations under Softice, with and without a ring3 debugger attached, you'll see the allocation begins with 0xABABABAB 0xABABABAB in the debug version, so there must be an extra 0x10 bytes padded to the start of the alloc.
Further details on that was provided be Ero in the thread I posted above and references the following article:
http://www.docsultant.com/site2/articles%5Cdebug_codes.html
Interestingly, the TlsValue itself (either debug/no debug case) points directly to the ThreadId of the new thread, which you can actually guess from the CreateThread code posted above, as esi is also used as the lpThreadId OUT variable.
Cheers,
Kayaker
|
|
Huh... Now that is interesting ;> Nice find Rolf ;> |
|
Thanks for the lucid explanation and reference links, Kayaker; now it's clear. Would be nice if there was a wiki for anti-debug tricks so we don't keep reinventing the wheel on various messageboards and papers. |
Read this too https://www.blackhat.com/presentations/bh-usa-07/Yason/Presentation/bh-usa-07-yason.pdf
this presentation gives a detailed explanation about the flags which get affected by NtGlobalFlag (page 8)
|
Hey Rolf, nice post!
About your suggestion for a wiki for anti debug tricks, the almighty Collaborative RCE Tool Library (which essentially is a wiki, or rather has all advantages of a wiki while not having most of the disadvantages) of course already has a category called "Technical PoC Tools", for exactly this kind of thing. :) I also just added a subcategory of it called "Anti Debug PoC Tools", here:
http://www.woodmann.com/collaborative/tools/index.php/Category:Anti_Debug_PoC_Tools
So, you (and everyone else) are very welcome to submit all your anti debug trick PoCs + information there, which will make up a very nice collection of information for anti debug tricks and accompanying PoCs! Other types of PoC categories are also very welcome of course! |
|