I just finished unpacking PurpleHaze a couple of hours ago
I got the sample from Contagio (thank you, once again)
The packer seems really amazing, as it didn’t use any VirtualAlloc*,SetWindowsHookEx,etc functions, which got me a be insane about it, although I didn’t work much on it.
I will try writing about it soon, TDLn rocks, although the kernel driver is poorly written.
When I finished unpacking it and started skimming it in IDA I was quite suprised to see no antidebug stuff,
however I did see some usage of LoadLibrary + GetProcAddress to some functions which reminded me a bit of Spyeye
BTW, my queue has a few more posts which are not started (read: written in my head but not in wordpress ) which most reside about the old hardworking LD (runtime relocation of functions, loading, unloading, etc) and loading ELF on the fly (as nowadays I started fiddling around w/ generating ELFs from scratch ( btw, libelf sucks ) )
the packer, btw, used a really nice technique for calling functions, a simple
push func addr pop eax label: jmp label+1 call eax
so, Olly couldn’t disassemble it correctly, nor IDA btw.
A few more fun fragments where the loop_inc_check_false-call_inc_loop
.text:00415400 jmp loc_415410 .text:00415405 ; --------------------------------------------------------------------------- .text:00415405 .text:00415405 loc_415405: ; CODE XREF: .text:loc_415460j .text:00415405 xor eax, eax .text:00415407 xor eax, [ebp-2Ch] .text:0041540B inc eax .text:0041540C mov [ebp-2Ch], eax .text:00415410 .text:00415410 loc_415410: ; CODE XREF: .text:00415400j .text:00415410 cmp dword ptr [ebp-2Ch], 19h .text:00415418 jnb loc_415465 .text:0041541E cmp dword ptr [ebp-2Ch], 8 .text:00415426 jnz loc_415449 .text:0041542C mov eax, 645h .text:00415431 sub eax, 292h .text:00415436 push dword ptr [ebp-28h] .text:00415439 push 0 .text:0041543B push offset dword_4020B4 .text:00415440 push dword ptr [ebp-1Ch] .text:00415443 call ds:WaitForMultipleObjects .text:00415449 .text:00415449 loc_415449: ; CODE XREF: .text:00415426j .text:00415449 cmp dword ptr [ebp-2Ch], 7 .text:00415451 jnz loc_415460 .text:00415457 mov eax, [ebp-2Ch] .text:0041545B inc eax .text:0041545C mov [ebp-2Ch], eax .text:00415460 .text:00415460 loc_415460: ; CODE XREF: .text:00415451j .text:00415460 jmp loc_415405
The function there will be never be executed, it is only a false positive call
This is a smart move imho, it add more imports not to make the binary suspicious and also makes the debugger more frustrated of why this superficial call is made
I think however that the fun will be when I’ll start messing around the drivers themselves and understanding the packer in a more brief way.
I still want to know(after skimming the unpacked exe though) how the actual unpacking process works in depth other than the decryption part.
Diving in
I ran the malware on vbox, using Olly w/ phant0m, OD and OllyDump, other plugins like ollysync weren’t usable, as the malware has several encrypted portions which are only decrypted later on.
So a quick look on peid shows it’s encrypted/packed/protected, the fist thing I’ve done was to step the code a bit, this is how the packed entry point looked like :
.text:0041514B start: .text:0041514B sub eax, 50FBh .text:00415150 push ebp .text:00415151 mov ebp, esp .text:00415153 sub esp, 0CCh .text:00415159 push ebx .text:0041515A mov ebx, 44CE26Ah .text:0041515F mov [ebp-4], ebx .text:00415162 push offset aKdsiiuduikjdkl ; "KDSIiuduiKJDkljDYUOdOYHD" .text:00415167 mov dword ptr [ebp-8], 44CE269h .text:0041516E call ds:GetModuleHandleW .text:00415174 cmp esi, ds:sz._cx .text:0041517A sub ds:dword_4020C4, offset dword_402104 .text:00415184 xor ds:dword_4020C4, offset dword_4020EC .text:0041518E sbb ds:dword_4020C4, offset dword_402110 .text:00415198 test eax, eax .text:0041519A jz loc_41537C .text:004151A0 .text:004151A0 loc_4151A0: ; CODE XREF: .text:0041538Aj .text:004151A0 xor eax, eax .text:004151A2 inc eax .text:004151A3 jmp loc_4158CA
For a reason which is not known at this, the malware checks for the existence of the “KDSIiuduiKJDkljDYUOdOYHD” module
if it exists, the malware exits
.text:004158CA loc_4158CA: ; CODE XREF: .text:004151A3j .text:004158CA pop ebx .text:004158CB leave .text:004158CC retn 0Ch
The machine I ran it on wasn’t virgin it all, it was an unpatched winxp-sp2, running on virtualbox, along with virtualbox tools etc
I didn’t see any efforts at detecting virtualization but I haven’t tried enough.
Either way, the classical CreateToolhelp32Snapshot would easily defeat my box.
When I started debugging it I made Olly stop every time a new module is being loaded, so in case I’ll lose any stealthy API call
I’ve set a bp on VirtualAlloc and on FS:[0x30] aka PEB.
VirtualAlloc seems to be the main functions which might help unpacking, having any sort of fast phase to try finding how does it know the addresses failed (PEB trick is not used directly, anywho)
I have seen at least 4 allocations which one of them was a PE being written into memory, others were just data which I didn’t manage to parse yet, some of it gets freed so I assume it’s not used in unpacking and probably manipulated with other sections.
One thing I really liked about the dropper is that it doesn’t use any direct manipulations of the segments modifiers, something which was a for me in order to detect different behavior, all segments which need to be written are already RWX’ed, so it makes finding the sweet spots a bit harder.
A small note to the debugger would be to also add a breakpoint on VirtualFree to locate memory regions which were free()’ed
After a few VirtualAlloc’s there will be another PE which is dumped to memory , my lucky address was 0xA30000 , it is a bit important to note that the PE header will be “corrupted” with the filename at the beginning (mine was php.dll, thanks Contagio, again, for the sample), so I just removed the first few bytes until I got the classical 4D5A w/ hex workshop
After a quick look at this PE it looks like a dll, perhaps the dropper is planning to inject it to some processes ? let’s skim the DLL in one byte look
.text:10003D05 push ebp .text:10003D06 mov ebp, esp .text:10003D08 sub esp, 124h .text:10003D0E cmp [ebp+Str1], 1 .text:10003D12 push ebx .text:10003D13 push esi .text:10003D14 push edi .text:10003D15 jnz ret_loc_10003E23 .text:10003D1B xor ebx, ebx .text:10003D1D push ebx ; dwMaximumSize .text:10003D1E push ebx ; dwInitialSize .text:10003D1F push ebx ; flOptions .text:10003D20 call ds:HeapCreate .text:10003D26 mov hHeap, eax .text:10003D2B call ds:GetTickCount .text:10003D31 mov edi, ds:PathFindFileNameA .text:10003D37 mov GetTickCount_dword_10008264, eax .text:10003D3C mov [ebp+Str1], ebx ; zero .text:10003D3F cmp [ebp+Source], ebx .text:10003D42 jz short loc_10003D6A .text:10003D44 push [ebp+Source] ; pszPath .text:10003D47 call edi ; PathFindFileNameA .text:10003D49 push 104h ; Count .text:10003D4E push [ebp+Source] ; Source .text:10003D51 mov esi, offset byte_10008268 .text:10003D56 push esi ; Dest .text:10003D57 mov [ebp+Str1], eax .text:10003D5A call ds:strncpy .text:10003D60 add esp, 0Ch .text:10003D63 push esi ; pszPath .text:10003D64 call ds:PathRemoveFileSpecA
I don’t know the value of Str1, but it’s likely to not contain1 as if not the DLL would exit/return
We see a small dummy call to HeapCreate w/ zero values and then GetTickCount.
I didn’t analyze the whole DLL at all, just dumped it and gave a quick look but it is quite important to know this address
As timing attacks are the debugger’s worst enemy, and no plugin can detect them so easily.
A few more calls are made but the actual “meat” which really goes is starts from a sequence of several “strcmp” for interesting values
such as – svchost.exe netcvs jp2launcher and java
Another interesting thing is the CreateEvent call which is made here :
loc_10003E2C: xor eax, eax mov [ebp+var_20], 1 lea edi, [ebp+var_1F] stosd stosd stosd stosd stosw stosb push 4 lea eax, [ebp+var_20] mov [ebp+EventAttributes.lpSecurityDescriptor], eax pop eax push offset aPh0 ; "ph0" push offset aGlobal ; "Global" mov [ebp+var_1E], ax lea eax, [ebp+Name] push offset aSS ; "%s\\%s" push eax ; Dest mov [ebp+EventAttributes.nLength], 0Ch mov [ebp+EventAttributes.bInheritHandle], ebx call ds:sprintf add esp, 10h lea eax, [ebp+Name] push eax ; lpName push ebx ; bInitialState push ebx ; bManualReset lea eax, [ebp+EventAttributes] push eax ; lpEventAttributes call ds:CreateEventA mov esi, eax cmp esi, ebx jz short ret_loc_10003E23
If you skim a bit in msdn you’ll obviously understand that this is some sort of way to communicate to the outside world.
There is another call which creates a local event
push offset aPh0 ; "ph0" push offset aLocal ; "Local" lea eax, [ebp+Name] push offset aSS ; "%s\\%s" push eax ; Dest call ds:sprintf add esp, 10h lea eax, [ebp+Name] push eax ; lpName push ebx ; bInitialState push ebx ; bManualReset push ebx ; lpEventAttributes call ds:CreateEventA test eax, eax jz short ret_loc_10003E23
I have seen some Thread interaction and my guess is that this DLL is either a module or gets injected to other processes (as the interaction w/ the many strcmp’s of java/jp2launcher/etc)
Another interesting string I encountered was
<body><a id=link href='%s'></body><script>document.getElementById('link').click()</script>
Hum, a clicker ? Botnet ? hum hum hum who knows.
I got a bit tripped off the unpacking stage, as this DLL got my attention
Finding Kernel32.dll
One thing which got my attention while skimming the packed EXE was the fact that all calls to external (aka DLL) functions were made from something like
call [ebp+8]
An arithmetic manipulation was done in order to keep the actual value on the stack secret until the real call is made, a short XOR,ADD,SBB calls were made in order
to reveal the real value, but , how do they know the actual value of the function they wish to call ?
My journey actually began while I was debugging the VirtualAlloc/VirtualProtect calls, I knew who’s calling them ( all I had to do was to return from the call, as no special
push’s were made before the call ) but I didn’t know how they retrieve the address.
So I set my side on VirtualAlloc to try demonstrate the process I’ve done in order to reveal it
Let’s see:
7C809A7E 90 NOP 7C809A7F 90 NOP 7C809A80 90 NOP 7C809A81 kernel32.VirtualAlloc 8BFF MOV EDI,EDI 7C809A83 55 PUSH EBP 7C809A84 8BEC MOV EBP,ESP 7C809A86 FF75 14 PUSH DWORD PTR SS:[EBP+14] 7C809A89 FF75 10 PUSH DWORD PTR SS:[EBP+10] 7C809A8C FF75 0C PUSH DWORD PTR SS:[EBP+C] 7C809A8F FF75 08 PUSH DWORD PTR SS:[EBP+8] ; ntdll.7C960738 7C809A92 6A FF PUSH -1 7C809A94 E8 09000000 CALL kernel32.VirtualAllocEx 7C809A99 5D POP EBP ; ntdll.7C960738 7C809A9A C2 1000 RETN 10 7C809A9D 90 NOP 7C809A9E 90 NOP
This is the classical VirtualAlloc inside kernel32.dll as the EXE had tremendous API it was trivial to find the base of it w/ a simple lea eax, VirtualAlloc and then just looking for MZ and parsing the imports.
However, this is not the case, there are many ways to get kernel32.dll base in an environment where you don’t have GetProc and LoadLibrary , but none of them were used here, at least from what I’ve seen.
I tried setting breakpoints both at FS:[0x18],FS:[0] (SEH), FS:[0x30] but none worked, I always stopped at different DLL locations, which didn’t really find my interest
The call was made, a memory region was created and the function returned here :
004102FB .^\E0 C3 LOOPDNE SHORT w_php.004102C0 004102FD > FF55 08 CALL DWORD PTR SS:[EBP+8] ; kernel32.VirtualAlloc 00410300 . 837D 10 00 CMP DWORD PTR SS:[EBP+10],0 00410304 . 8945 14 MOV DWORD PTR SS:[EBP+14],EAX 00410307 . 0F84 24000000 JE w_php.00410331 0041030D . 837D 0C 00 CMP DWORD PTR SS:[EBP+C],0 00410311 . 0F85 1A000000 JNZ w_php.00410331
Remember the call [ebp+8] I mentioned earlier ? exactly.
Be no mistaken about the loopdne here, it’s just a call +ret to make olly think otherwise
So stepping a bit back leads us to
00410189 . 55 PUSH EBP 0041018A . 8BEC MOV EBP,ESP 0041018C . 83EC 54 SUB ESP,54 0041018F . 8D45 10 LEA EAX,DWORD PTR SS:[EBP+10] 00410192 . C745 F4 04000000 MOV DWORD PTR SS:[EBP-C],4 00410199 . 8945 F0 MOV DWORD PTR SS:[EBP-10],EAX 0041019C . 8B45 F0 MOV EAX,DWORD PTR SS:[EBP-10] 0041019F . 8138 6AE24C04 CMP DWORD PTR DS:[EAX],44CE26A 004101A5 . 53 PUSH EBX 004101A6 . 56 PUSH ESI 004101A7 . 0F84 50010000 JE w_php.004102FD
which is the start of the function,