Andromeda 2.07 analysis

Written by aaSSfxxx -

Hi folks !
As it's spring (and I've not written something for a while), malwares get updated, and this is also the case for Andromeda which got an update. I know I already wrote something about previous version, but this version has some fun tricks, so let's reverse it to see these tricks :)

First analysis

When we load the executable for the first time in OllyDBG, we get this

As we can see, the malware calls two functions: the first function walks trough the PEB to find loaded DLLs, converts unicode dll name into ANSI one and then hash it to compare it to the first argument (the hash of the wanted dll). So we can call this function "findLibraryByHash". Here, the functions gives to us the image base of kernel32.dll, checks if function is null and jumps into bullshit if the result is null. That's not the case here, so we go to the second function and we step into (F7)

The function begins to load some function by their hash (done by the function at 00401746, which i'll call getProcedureByHash). Then the function sets an exception handler (I'll explain later what it does), and there are some calls which are useless for us, except the call at 00401BF9 (the call before the two conditionnal jumps). The call is used to get a string pointer (and also as anti-disassembler trick). The first instruction of the call is "pop esi", so esi registers contains the address of return address, which is a string pointer (esi points to "guard32.dll" string). We also notice a "INC BYTE PTR DS:[ESI+B]", which increments 0xFF (aka -1), to have a null byte-terminated string. Then there is a call to GetModuleHandleA to check it module is loaded or not (anti-emulation protection). Then the malware checks if some processes are present in memory, just like the previous version of the bot.

As I'm using VirtualBox (with additions, yeah I know it sucks but I can't live with additions :3), I get redirected to 00401E95, which pushes some arguments and calls a function: let's analyze it :)

The function first loads some ntdll APIs (ZwAllocateVirtualMemory, RtlRandom, and other, just see what happens in ollydbg), then allocates virtual memory. After that, first argument is put into ebx, and then we have this call

This function is a RC4 decryption function, where key is stored at [ebx] and sizes 16 bytes, and data is stored at [ebx+28h]. Then we have a call to a jCalg1 decryption function (just like previous andromeda version), and to avoid stepping into, just put a breakpoint at 00401FBB to avoid relocation shit. Then the malware allocates again virtual memory and fills it with random bytes, before doing solving some APIs. But, after getting function address, the malware calls a strange function, which takes the random-filled memory space, the API address and an unknown variable... smells bad, so let's have a look into this.

This function calls another function which takes the address of the found function, and returns the length of the first instruction. Then this function compares if we have a jump, and if it is, follow the jump, otherwise copy the first instruction and then jump to the next instruction of desired API.

So, to bypass this shit, let's codecave a little to store original API address into the jump table (you can use both "Multiline Ultimate Assembler" or the integrated ollydbg assembler (there's only a few lines to assemble). We are lucky, there is some free space in 405150. in 401A4D (the "cmp eax,2"), make a jump to 405150, and assemble these lines:

  JMP 00401A9B

Then put a breakpoint in "CALL EAX" on the caller function and F9. Then trace into function (F7) and enjoy ur dummy payload :> (we could guess it, as we got here because we got detected by the anti-VM tricks :)). Fake payload didn't change at all, it's still a fuckin' lame remote shell with some persistance in registry.

Now... gimme the real payload !

As we saw before, the malware places an exception handler, and if we play with the code to escape anti-VM tricks (or if debug this on your real computer :]), you should see something like "OR WORD PTR DS:[EAX+46],80" which throws an exception (because adress is not writeable here) which get handled if the process is not debugged by the exception handler put by the malware. Let's see what the fuck happens in the exception handler with IDA (IDA is very good to deal with structs and shit like this).

We see that the exception handler plays with contexts to redefine EIP, and also changes the stack on the fly, to make a "fake call stack" by doing

push sub_401AA2
push dword_402058
push 40h ; here is the fake return address, never called in fact
jmp loaderingPayload

We can also notice that the program checks the "EXCEPTION_ACCESS_VIOLATION" flags and check if we really come from the instruction "OR WORD PTR DS:[EAX+46],80".

So restart the program, replace the entry point code by the code just above, do the codecave as I described above, break on "call eax" then F9 and F7. The payload is basically the same than the previous version, it still uses the ZwMapViewOfSection trick to inject into msiexec.exe (it's no longer svchost or wuauclt.exe on 32bit systems), so see my previous article about andromeda loader analysis if you are interested (in french, sorry non-french ppl, but I think you can google translate or shit like this).

Bot analysis

The loaders injects the function passed as first argument into msiexec.exe (the "sub_401AA2" for me), so restart the program (again :>), set EIP to 00401AA2, apply the codecave I described in the first part (otherwise, you won't be able to understand what the fuck is happening with APIs), and as usual, F9 until "call eax" and then F7.

Here there is a lot of useless code (dealing with environment variables, creating pipes, etc) and it's never called for me (maybe it's debug stuff, learn how to use ifdef malware coders). It also load some strings depending if the malware runs in a priviligied account or not (to avoid UAC popups & other things like this), tries to allow itself in the Windows firewall, then the malware creates a thread, with contains the real bot logic (ThreadFunc is at 7FFA370B). The thread proc loads some additionnal DLLs (in ole32.dll and somme winhttp functions too), by calling the function at 7FFA19F4 and then call 7FFA2494, which places some hooks on ZwMapViewOfSection/ZwUnmapViewOfSection functions (maybe to protect its ass when loading another dll, I didn't understand what's really happening here). Then, it calls QueueUserAPC with its own callback (this function just seems to create a thread, according to its behaviour). So let's have a look into the callback function (with IDA, and sorry for the background glitches, too lazy to fix it):

We see here that the function grab "data" from 7FFA04B8, and grabs in a loop the first dword, compute it's opposite (call it n) and allocates a space of computed dword + 5 (to store a "magic" dword and the null byte), and then decrypt the n+1 bytes after the dword with a custom implementation of RC4, and loop until the last dword is null (the key is a part of the botkey, the 16 bytes start to the second byte of the key). So, C&C configuration looks like this:

(not dwSize)|c&c URL with null byte encrypted|(not dwSize)|c&c URL|....|dwSize = 0

Finally, the bot restores the original implementation of RC4 by fixing modification in key scheduling algorithm before destroying the callback function (maybe a trick anti-debug, but useless). So, let's return to the thread function. The bot then load its plugins if any (downloaded from the c&c), builds the version number which will be sent to the C&C, checks connectivity by attempting to connect to (maybe to SE the user and make him to allow connections). Finally the bot starts another thread, which is the communication with the C&C (the other thread proc is at 7FFA3557).

Here, we can see that the C&C response changed a lot: reply is still cyphered with RC4 and the RC4 key is still the field "id" sent by the bot to the C&C, but we have base64-encoded data encosed by "gn(" and ")". Once base64 data is decoded, protocol is the same, so just look my previous analysis of andromeda ;).

The last funny thing I could see in this malware is the usage of "RtlWalkHeap" by the malware: the bot walks trough the heap to recover the C&C urls it decoded before (with the index made by xoring the VolumeSerial with a constant which is incremented each time in the loop), and does some weird things with "CreateStreamOnHGlobal" and shit like this to get server's response.

If you are interested, you can find the sample I analyzed at and the IDA databases are here.

Btw (unrelated), R.I.P to my netbook which died last Friday :(