Reversing Data Structures and Algorithms in Malware

Jason Reaves

This book is for sale at http://leanpub.com/reversingdatastructuresandalgorithmsinmalware

This version was published on 2020-05-13

*   *   *   *   *

This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and many iterations to get reader feedback, pivot until you have the right book and build traction once you do.

*   *   *   *   *

© 2020 Jason Reaves

Introduction

There are many obstacles you encounter when doing malware analysis, from unpacking your first sample, mapping out your first routine or breaking into that malwares data encoding routine. One obstacle in particular I’ve seen give people problems more than others is being able to follow malware as it parses data, data that is sometimes seemingly random hex but instead used for configuration purposes that can depict how malware acts. One experience that seems to help people when reverse engineering these structures and the algorithms that process them is a past in low level development such as assembly or C programming, however this isn’t a luxury that everyone can come to malware analysis with such a background. As such I give you this book which is my humble attempt to walk the reader through my process of making sense of it all. From my experience there is a focus when it comes to reversing a packer/crypter that will involve algorithmic reverse engineering, as a construct when it comes to pulling out config data from a bot it will involve a data structure reverse engineering focus and finally C2 will commonly involve doing both.

Most malware comes with some sort of onboard configuration which could be as simple as a command and control server address. Not thinking of the data as strings but more in terms of pure binary data you can start to understand the data in whatever form the developer has chosen to store is ultimately just used by the bot to fulfill the tasks it needs to. Normally the easiest way to find this data is to first understand what it is you are dealing with, ex: if it’s ransomware and we want to find some of the data the bot will have on board then a list of file extensions or language flags is a good place to start the bot will have to use certain methods to get this information from the infected host and we can use these bottlenecks to find the locations in the bot where the data has already been decoded and is now being parsed. Once you find the data it’s usually a matter of backtracking, I usually use IDA and a debugger to accomplish this task and it can take quite a bit of time and experience to get good at it.

Other common methods I’ve used is setting breakpoints on suspicious functions such as those performing loops and bitwise instructions or breaking on suspicious data sections in the sample that could be storing information. When people write a bot they usually end up writing a template or stub with a builder in the same manner you would write a crypter or packer and so the configuration data in the bot must either static or be placed in a way that allows the bot to find it such as with a special marker or header this way the builder can update the stub properly and the bot can then make use of the data when it is executed.

Luckily this same system of identifying structures and routines is the same basic principles we will use for reversing protocols by studying the data and determining how the code deobfuscates, packs and organizes this data. Because in the same manner that I mentioned that regardless of how obfuscated a bots configuration data is it must eventually know where to find it this same principal holds true for C2 communications, if a bot wishes to communicate something back to its C2 then it must eventually be useable by the C2 to hold any meaning going forward.

Identifying Structure

There are usually four types of structures of data you will find within malware and how that data is laid out is usually very dependent on the language it was written in. Sometimes the structure is very noticeable such as a Cerber ransomware config usually stored in JSON format, this makes seeing the config and understanding it simplistic. What about a noticeable structure that isn’t as easy to understand such as Kovter, Kovter config structure is easy to see but can be difficult to make sense of.

At first glance you can make out what appears to be labels but it all kind of blends together. However if we look close we can see a pattern emerge where tags are used to surround data sections. Before we begin to try to make sense of the data let’s first parse the config by using these tags.

This turns into a loop we will first find the tag name and then position our working data appropriately.

1 name = re.search(r'[^(::)]+',conf_data).group()
2 conf_data = conf_data[len(name)+2:]

After that we will find the next occurrence of that same tag and use that offset to get the data for that section.

1 emp = conf_data.find('::'+name)
2 data = conf_data[:temp]

Then adjust our working data set and loop through this whole process until the working set is empty the entire script can be seen in (Appendix A 1). The only oddball I see is the mainanti which has subflags inside of it, instead of making our code recursive where we would check for these sub flags we simply add a special check for this section. This gives us a cleaner looking config when we run it.

This example we have gone over using Kovters config is a simple one but as we will see in later chapters it does become increasingly difficult to figure out where to break apart these data sets when they are not so neatly arranged. Luckily for us the human brain is a great pattern matching tool, all you need to do is practice the skill and you’ll find yourself spotting breakdowns of data before you know it.

Making sense of Data

Locky Config

In this chapter we’ll look at a slightly more complicated example than the last chapter; a config belonging to a ransomware variant that has ran rampant in 2016, Locky. We are going to skip the algorithms used by locky to decode the config for now but will come back to them later in our algorithms section. For now we will look at a decoded locky config and then walkthrough a static analysis of the bot to figure out how it uses the pieces of this config so we can properly map it out and culminate with a simple python script for parsing this mapped out structure.

In the image above different people will see different things, if you’ve ever done some low level development work or spent plenty of time in a debugger then you’ll see some obvious structure emerge but if you don’t have that experience then you might just see a list of IP addresses and a php filename. Fear not though because even if you see some structure stand out you can’t really prove what the majority of it means without diving into the bot, basically regardless of your experience then the need to prove out what the parts of the config means is the same.

Usually a bot will either parse out the config data into variables or use a single variable to hold the address of the structure. For the static portion of this I will be using IDA Pro simply because I’ve been using it in a professional sense longer than the alternatives have been around, but any disassembler that can show cross references should do. After scrolling down a bit past the config decrypted I see a register get stored into a variable, so I name it what I believe is the possible address of the config structure.

Following out the cross references to this leads me to believe that my initial suspicions are correct and it appears there is a single function using this address repeatedly which means that I should check out this function and how each value is used to be able to map it out appropriately.

Looking into this function, that I will go ahead and name parse_config, we can instantly see multiple occurences of the code referencing the address stored for the base config and then referencing data at various offsets in that config and performing actions after checking the appropriate values. A good example of this is seen in Figure 6 where a dword value is read and then subsequently used in a simple sleep routine. We can then map out this config structure by following this algorithm being used to parse the config data, the beauty of this is that we don’t necessarily need to know what each value is at this point we just care about getting an idea of what this structure looks like which we can tell by following the offsets and sizes of the data read from the config, in this case since the config is parsed as the values are used I’ve named them based on the first thing that popped in my head as I glanced over the code that followed.

 1 struct LockyConfig{
 2     dword affid; //0x0h
 3     dword unk; //0x4h
 4     dword sleep; //0x8h
 5     byte svchost_flag; //0xch
 6     byte set_regkey; //0xdh
 7     byte lang_flag; //0xeh
 8 string uri; //0xfh
 9     string ip_addrs; //0x3fh
10 };

No Nothing Example

For the next example we will believe that we have no forensic evidence from the machine and no malware but merely some packet data from TCP transmissions. I don’t personally find myself in this situation very often but I know one could find themselves in such a position when doing forensics or incident response work after an event has happened.

Jumping right into the data I can’t stress enough how useful it is to sometimes view the data multiple ways, not just an ASCII dump of the data sent over TCP but also hex and raw. Here we can see some structure start to show up by viewing the hex dump.

First off we see what looks like a dword value that matches the size of the data transmitted 0x6d and then the response data we can verify that this is indeed the case. Now that we know the first dword is the size of the data then I would assume the next dword given its size is also some sort of size value but why is it higher in the data sent and lower in the data received? Could be many possibilities like total transmission size but then the server wouldn’t normally respond with data before the entire data is transmitted so the more likely scenario is that the data is compressed.
After that dword we see a string that appears in both the data sent and the data received, my initial reaction to this is some sort of encoding key for the data however something sticks out at the end of “PCSHE” we see 0x78 0x9c which happens to be a ZLIB header. So could this mean that the data is compressed? The easiest way to check is simply to load up a scripting language and see if it decompresses properly.

Loading up the data we can verify our hypothesis on the sizes and also that the data is compressed. After this quick check in we can see that it contains some information related to the system, this may not tell us everything we would like to know but by simply being able to identify some structure to data we were able take a step in the right direction about the incident and inform the incident response team about the data we discovered being exfiltrated.

In the next chapter we will explorer reverse-engineering algorithms which can be easy in some situations but also significantly more challenging than data structures.

Approaches to reversing algorithms

Reversing or researching algorithms is just about the most fun you can have as a malware researcher. Is this a useful routine? Part of the algorithm? Garbage obfuscation? A known algorithm, and if it is, has it been modified? These questions and more can arise when you start looking at various parts of a malware family from their crypter/packer to how they store important data and how they communicate are just a few big pieces of the overall picture that you can find yourself asking about while looking at malware.
In this section we’re going to go through a number of routines as I explain some of the ways I attempt to either identify them or reverse them. It’s important to note that you can find yourself looking at a routine being used by a program that could look completely different compared to what you would expect it to be written, this is because the data we are looking at is normally compiler generated and can change depend on a number of factors including which compiler and optimization was used.

Simple data searching – Locky config

The first routine we’re going to look at is a simple one that Locky malware uses to find its onboard config data, it’s also one of the first things the malware does so finding the routine is pretty easy if you’d prefer to step through it in a debugger or find it yourself for practice.

Whenever I find myself looking at a routine I first try to identify what the routine is doing, this is something I’ve found comes easier to do statically as you gain more experience. If you’re newer however then it can be easier to step through it in a debugger dynamically while renaming sections of the routine. We can tell either statically or dynamically as previously described that this section from loc_405337 to loc_405355 is a loop that appears to be looking for 3 dwords of data that will allow it to flow past the loop and into the next section of code.
A quick check to see if this is a common routine that I will normally employ is to do an internet search for the immediate values being used(in this case 0x88bbdd8d and 0xddbca2b2) in order to try to identify the routine. In this case we are given search results related to Locky in the form of python scripts and blog posts. Since we are trying to learn let’s try to prove that the current research is correct by recreating it.

Looking at this routine we can see that the beginning of the loop(loc_405337) and the generic end condition of the loop(loc_405355) are tied together. These two blocks look like a for loop that we can commonly find in C code, converting the sections into C code one piece of a time allows us to work through each section and even test them as we go.

1 edx = sizeOfImage-12;
2 for(int i=0; i<edx; i++)
3 {
4     memcpy(&ecx, base_image + i, sizeof(int));
5     if(ecx != 0)
6     {
7     }
8 }

Now we can fill in our take on the two if clauses that follow the zero check and then if we wanted to could even step through the program in the debugger to check if our code finds the same memory location.

1 memcpy(&test1, base_image+i+4, sizeof(int));
2 memcpy(&test2, base_image+i+8, sizeof(int)):
3 if(ecx ^ 0x88BBDD8D == test1)
4     if(ecx ^ 0xDDBCA2B2 == test2)
5         break;

Running this and then comparing where the bot stops in a debugger confirms that we have reversed how to find the same data that the bot is hunting for!

Decode the data - Locky config cont

If you take a look at loc_405371 in the image below we can see the point where the routine jumps to after it has found a match. The routine then loads in the first, fourth and fifth dword from the offset that it reached from the previous loop while also setting up a register with the offset of data past this point.

What’s interesting about these three DWORD values is how similar the two bytes are, this is an indicator that these values are possibly XORd together. My guess is that atleast one of these values is a length. After being XORd the two values turn out be to be 0x41 and 0x67 respectively. Keeping note of the values from the previous calculations that are saved off which is the 0x67, the first dword xor value and the memory address past the values we come to a memory allocation call via VirtualAlloc where the size of memory requests is 0x41.

Next the first dword value is taken and a hardcoded value is added to it before it starts a loop over the data it just found. The decoding loop may seem daunting at first but once you see that it’s XORing the data one byte at a time with the low order byte of the dword value that is being manipulated, this dword value is the first dword with the harcoded value added to it. So basically we have a key with a generation routine, this is basically a XOR cipher stream with a Psuedo-random generation algorithm(PRGA) based on a dword key. We can quickly write up what the decode routine would be before we even dive into the mess of bitwise operations going on.

1 def decode(start,length,data):
2     start = (start + 0xa078c405) & 0xFFFFFFFF
3     out = ""
4     for i in range(0,length):
5         start = prga(start,i)
6         out += chr((data[i] ^ (start & 0xff)))
7     return out

Diving into this loop now we can see that edx is the loop iterator meaning this can probably be thought of as a for loop to iterate through the data as can be seen in our previous code. This is why our PRGA function takes the iterator value as a parameter along with the current key ‘start’ which will be used to continue to seed the PRGA function each time in order to generate our stream of bytes needed to XOR decode the data. The algorithm is pretty straightforward if you just follow out the instructions, you can even pseudocode it out using the register names.

 1 out = ""
 2 ebx = start_xor_val
 3 ebx = (ebx + 0xa078c405) & 0xFFFFFFFF
 4 for edx in range(0,length):
 5     cl = edx & 0x1f
 6     eax = ebx
 7     eax = rol(eax, cl)
 8     ebx = ror(ebx, 3)
 9     ecx = edx
10     ebx = (ebx + eax) & 0xffffffff
11     ecx = ror(ecx,0xb)
12     ecx = (ecx + 0x72462828) & 0xffffffff
13     ebx ^= ecx
14     out += chr(ord(data[edx]) ^ (ebx & 0xff))

We can quickly compare if our routine outputs the same data as the malware binary as well by checking some of the output bytes, a quick comparison below shows that they appear to match!

Output from the malware:

Output from our python code:

While our routine works the same, I think it’s worth pointing out that simply generating that routine is not enough to say that you understand it. Frequently I will create such routines using temporary variable names or even registers as variable names, just the act of creating the routine can shed light into what is happening at different stages. However I will frequently go back and rename things to give them actual meaning when I start understanding what is going on, I don’t normally feel I’ve actually reversed something unless I can describe it in C code with meaningful variable and function names.
This helps greatly if you ever see the same routine again because you can usually quickly guess what it’s doing, this usually means you will end up burning research time constantly learning and might end up spending days or weeks reversing a very common routine such as base64 but the next time you see it you will easily be able to tell what it is related to. This means there is a high up front cost to learning new routines and algorithms with a delayed payoff that will pay huge dividends later in your career.

Data that shrinks or expands - Locky config cont

After decoding the data we can see the larger XORd dword 0x67 get used to allocate a new memory section before having the previous decoding data get passed off to an odd function. If you let this function run you’ll notice that it takes the previously decoded data and expands it out, basically the sheer act of taking a blob of data one size and turning it into a blob of data another size means that either the data was converted into something else such as being hashed, or there was data added to it such as encryption with padding or like in this case there is compression going on.

The next function after the memory allocation appears to be building boxes of numbers, compression routines can be very tricky to identify sometimes. An easy way I sometimes use is to look for these types of functions that are building boxes of numbers, these are usually initialization functions. Sometimes you can find these boxes already constructed manually inside C code on of the compression routine on github, so I will find one that stands out and google it. Luckily one in particular for this one called dist_base with the values 1,2,3,4,5,7,9,13 led me to a github for Tinflate or Tiny Inflate[[5]].

Tiny inflate init from Locky:

Tiny inflate boxes:

Pivoting off this newly discovered revelation we find another box in the source code dist_bits from the image above, a quick scan through memory shows us this other box was indeed built.

So let’s test this by simply decompressing the data we previously decoded, deflate is a compression library that is commonly used internally in other compression libraries as abstractions such as zlib(6). So to decompress it we just need to tell the python zlib library to skip the zlib header check with the proper wbits parameter which can be seen below.

Customized Encryption – Shifu Custom RC4

Let’s look at another example that honestly through me for a little loop. After unpacking this malware and decoding a number of the strings I quickly identified this malware as a banking Trojan called Shifu by comparing it with a report from VirusBulletin(2). However I noticed that I was unable to RC4 decrypt the communications even after decoding the RC4 key, seeing the RC4 routine and confirming its use of RC4 in other reports.
RC4(3) is a pretty frequently used encryption routine in malware and it’s one of those routines I frequently find myself telling novice malware researchers that they should attempt to write themselves in a low level language like C or ASM and then step through in a debugger to get familiar with it. The first part of the algorithm that always stands out is the construction of the SBOX using values 0-255.

After setting up the SBOX we would normally see a Key-scheduling algorithm(KSA) followed by a Psuedo-random generation algorithm(PRGA) that will serve up a byte from the keystream to be used to XOR the data with.
My first attempts at decrypting the C2 traffic using the key and RC4 had failed, so I decided to load up the program in a debugger and set the appropriate breakpoints on the RC4 routine. This can give you a copy of what the data looks like before and after encryption which can usually give you some insights into whether or not something else is being used on the data such as compression or another encoding routine either before or after.

Shifu Custom KSA:

RC4 PRGA:

Getting a sample of the data after is usually pretty easy if we’re talking about C2 traffic, simply letting the malware run in a controlled environment or in a sandbox is usually sufficient to get a sample of data that is being sent to the C2. Getting a sample of data prior to it being encoded however can be a little more difficult, luckily for us we’ve already done most of the work statically in regards to finding promising looking encryption routines.
If you’re having difficulty finding these routines because clicking through and studying 400+ functions doesn’t sound fun to you then you can do what I would normally do with a bot that I’m not familiar with and decode out the strings. The DLL we are wanting to look at in this case is injected into svchost after it has ran its initial checks including a mutex check. The program then does a CreateRemoteThread after injecting itself into svchost which is the address of execution we will want to follow post mutex check. Starting from this location if there is a string decryption routine you can usually find it pretty quickly, we just walk into the first function being called from the aforementioned thread start address and find a peculiar function call with the offset to data being passed to it.

Main thread post mutex:

String decoding routine:

Reversing Malware C2 Command Structure

As with most things I’ve discussed trying to reverse everything from a black box can be difficult and when it comes to malware you could even tip your hand if you do too much testing which could result in blacklisting or rapid changes that will make your hard work amount to nothing more than a learning exercise.
For this section we’ll be looking at a new variant of DeLoader being called ZLoader for Zeus Loader simply because the malware is a loader framework that loads a bunch of binaries including a zeus variant. We’re going to step from reversing strings to onboard configuration to C2 encryption to finally mapping out the deconstructed requests and responses to write a script that will basically let us pretend to be a bot. Our focus then will not be what or why the bot operates the way it does but simply to reverse the routines and data structures found within the malware.
Looking at an unpacked sample we step through the routines for a bit before we find a function that appears to control string decryption. This sample takes clicking through for a bit before we find a function being passed a number that appears to be decoding some data.

A quick glance through the function shows that the passed in number appears to be a lookup into a table and that we are dealing with a bit of obfuscation going on.

A simple trick I sometimes use to get around the obfuscation is to simply look for other samples whether it be another executable that this loader will deliver which in this case is true or hunting around the internet for older samples. Older samples will usually have less obfuscation but if that doesn’t worth out then simply keep looking at other samples if the obfuscation such as this one where there are useless instructions is being employed then odds are good it’s generated and other samples could lead to cleaner routines.
For our reversing pleasure I have simply found the exact same routine inside the DLL that our program will eventually decode out. This routine happened to be much cleaner in my case.

Now we can start reversing this routine, a number of techniques can be used such as working in small chunks while stepping through it dynamically can be employed. You also do not need to step through the routine inside the malware program, you can use a wrapped program to load in the bytecode of the routine in memory(fixing the addresses ofcourse) and then calling it with similar parameters. Then you can latch into this harmless program and step through the routine at your leasure, this may require you to fix up memory addresses in the debugger as you go however. If you are a more experienced researcher however then feel free to let the malware do the work for you!
Stepping through the program and validating how each block of code is operating, where it’s pulling data and what’s doing with that data then becomes very important as we begin to reverse the routine. Walking through the routine once quickly tells us that the two WORD values XORd first in the routine turns out to be the value used to control how many times the next block will loop which means that is the length of data to be decoded.

The next value pulled out is a DWORD and immediately following it a BYTE is pulled out from that address, so immediately suspicion that this is the address of the encoded data. This byte that is pulled out is then XORd against the first two bytes of the record and the index value of the current loop iteration. Notice I said record, that is because you can commonly think of data such as this as structures in C. We’ve already done all the work so throwing it all into a mock C structure is simple.

1 struct EncodedStringRecord
2 {
3     word xorkey;
4     word encoded_length;
5     dword addr_of_encoded_data;
6 }

So our table is nothing more than a collection of these records which are 8 bytes in length, explaining the purpose behind the value being passed into the routine and then being used to index into the table of records by multiplying it by 8.
The decoded strings gives us some idea of what this malware is trying to do but not so much an idea of where we need to look next for decoding the onboard configuration. There are some strings that look like this malware will inject into another process and you can verify that this will happen by throwing the malware in a sandbox or running it yourself.
So let’s take the number of the string corresponding with explorer.exe 114 in hex is 0x72 and look for where this value is decrypted. Doing an immediately value search from the top of the code lands us right where we need to be.

If we follow the next function call we can then see the call to CreateProcessW. A little ways later towards the end of the main routine is what looks like two large sections of data being copied around, a little further and we also see a smaller 0x2b section of data being copied and then a routine called after it that appears to be a decoding routine. Now if you had the dynamic analysis and followed the bots injection before switching to static you would know that the bot creates a section of memory in the injected process with the two big blobs of data copied into it and then overwrites the OEP(Original Entry Point) with the smaller section of data. My attention is immediately on what purpose this smaller section holds because it will be the first code to be executed, so as with most of this book you can either step through dynamically or statically. So for those of us that haven’t walked through the bot this far dynamically let’s go ahead and take a look to verify that what we think is happening is actually happening.
Setting a breakpoint where we are suspecting the program will spin up a new explorer process shows that our previous analysis was correct. Walking through the next few routines shows that the bot is reading the base address of the process and the size of the process in order to read the entire chunk of memory.
Next the program creates two sections one the size of the image from the created explorer process and one 0xc5b2 in size. This second section is then where the 0x3aa length chunk of memory is copied into, followed by a call to GetModuleFileName to store the name of itself after that and finally starting at 0x5b2 into our mapped section of memory the 0xc000 sized chunk of data is copied in. This section of memory is then mapped into the created explorer process.
Next the program copies over the memory image of explorer into the first section it created before moving on to this smaller 0x2b in size section of data.

Here we have a call to our suspected decoding routine and a perfect opportunity to copy off the data before it is called along with the call stack and then attempt to recreate it ourselves.

So now we have the data before and after along with the value that passed in as a parameter and used in the XOR loop(0xc9e5ffff).

With a routine this small I would normally write up a python function that is intended to perform one iteration of the loop and so we lay out the framework.

1 def routine(byte, key):
2     return

So the output byte is generated by XORing the input byte with the low order byte of the passed in DWORD XOR key.

1 def routine(byte, key):
2     ret_byte = byte ^ (key & 0xFF)
3 return ret_byte

So now all we are missing is the rolling the key by 8 and then adding 1 to it.

1 def routine(byte, key):
2     ret_byte = byte ^ (key & 0xFF)
3     ret_key = rol(key, 8) + 1
4 return(ret_byte, ret_key)

So now at the very least we can check that this function returns the proper output byte given that we have provided it with the input byte and the key. Let’s test it!

 1 >>> def routine(byte, key):
 2 ...  ret_byte = byte ^ (key & 0xFF)
 3 ...  ret_key = rol(key, 8) + 1
 4 ...  return(ret_byte, ret_key)
 5 ...
 6 >>> key = 0xc9e5ffff
 7 >>> byte = 0x45
 8 >>> routine(byte,key)
 9 (186, 3858759626)
10 >>> hex(186)
11 '0xba'

The first byte appears to of decoded properly, so now we can either play the pass back game with the key calling the routine in a loop or just rip out the code and throw it in a loop. Testing this again in python:

 1 >>> data = bytearray(binascii.unhexlify('45CAE6000072E80201CC510302CDEA37D8451B35120\
 2 E2E0E43927D44E424560807D2EF59B0D3F00A092B21'))
 3 >>> key = 0xc9e5ffff
 4 >>> for i in range(len(data)):
 5 ...   data[i] ^= (key & 0xff)
 6 ...   key = rol(key, 8)
 7 ...   key += 1
 8 ...
 9 >>> binascii.hexlify(data)
10 'ba00000000b900000000b80000000033db8bf03016c1c20846429043e2f5b80000000050b800000000f\
11 fd0'

We have success as our input and output matches what what we had saved off earlier from the bot! What is this data however? It looks like some bytecode with a bunch of nulls, we can disassemble it to check. Luckily Radare2 comes with a free disassembler that I like to use for these sorts of occasions.

 1 # rasm2 -D -b 32 -k windows "ba00000000b900000000b80000000033db8bf03016c1c2084642904\
 2 3e2f5b80000000050b800000000ffd0"
 3 0x00000000   5               ba00000000  mov edx, 0
 4 0x00000005   5               b900000000  mov ecx, 0
 5 0x0000000a   5               b800000000  mov eax, 0
 6 0x0000000f   2                     33db  xor ebx, ebx
 7 0x00000011   2                     8bf0  mov esi, eax
 8 0x00000013   2                     3016  xor byte [esi], dl
 9 0x00000015   3                   c1c208  rol edx, 8
10 0x00000018   1                       46  inc esi
11 0x00000019   1                       42  inc edx
12 0x0000001a   1                       90  nop
13 0x0000001b   1                       43  inc ebx
14 0x0000001c   2                     e2f5  loop 0x13
15 0x0000001e   5               b800000000  mov eax, 0
16 0x00000023   1                       50  push eax
17 0x00000024   5               b800000000  mov eax, 0
18 0x00000029   2                     ffd0  call eax

Looking over the disassembly of this code and we can see a striking resemblance to the decoding loop we just reversed. It would appear that the null bytes are placeholders, probably filled in with the XOR key, location and length of the data to be decoded along with the parameter and address of the next procedure to be called.
So would this mean that our decoding could work on the larger section? It’s a quick check by ripping out the previously mentioned 0xc000 sized chunk of data and testing it out.

 1 >>> data[:100]
 2 '\xb2\x90v\x00\x03\xcb\xe8\x02\x05\xcc\xe9\x03\xfd2\xea\x04\xbb\xce\xeb\x05\x04\xcf\\
 3 xec\x06E\xd0\xed\x07\x06\xd1\xee\x08\x07\xd2\xef\t\x08\xd3\xf0\n\t\xd4\xf1\x0b\n\xd5\
 4 \xf2\x0c\x0b\xd6\xf3\r\x0c\xd7\xf4\x0e\r\xd8\xf5\x0f\xc6\xd9\xf6\x10\x01\xc5M\x1f\x1\
 5 0o\xf1\xdf0d\xf8_\xdf\xfc\xae|z\xad\xdbef\xb0\x9bdt\x8d\xddtw\x8f\x90wc\xc2\x9d|'
 6 >>> key = 0xc9e5ffff
 7 >>> rol = lambda val, r_bits, max_bits=32: \
 8 ...     (val << r_bits%max_bits) & (2**max_bits-1) | \
 9 ...     ((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits)))
10 >>> test = bytearray(data)
11 >>> for i in range(len(test)):
12 ...   test[i] ^= (key & 0xff)
13 ...   key = rol(key,8)
14 ...   key += 1
15 ...
16 >>> test[:100]
17 bytearray(b'MZ\x90\x00\x03\x00\x00\x00\x04\x00\x00\x00\xff\xff\x00\x00\xb8\x00\x00\x\
18 00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\\
19 x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc8\x00\\
20 x00\x00\x0e\x1f\xba\x0e\x00\xb4\t\xcd!\xb8\x01L\xcd!This program cannot be')

Looks like a PE file, so writing this out to a file and then checking it in Exeinfo shows that it is a 32 bit DLL.

Taking a quick peak at the strings shows that it is named ‘payload.dll’ and there is a string referencing a ‘BASECONFIG’. That is interesting, everybody loves config data right?

The structure makes me think that it’s a placeholder and if I recall there was a 0x3aa sized chunk of data copied over with the DLL during the injection process.

Taking a quick peak at the data shows that it is near the top of the rdata section so should it should be easy to find using pythons pefile library. Alternatively you could find it pretty easily using any PE file viewer or hex editor.

 1 >>> import pefile
 2 >>> pe = pefile.PE('./zloader.exe')
 3 >>> for sect in pe.sections:
 4 ...   if 'rdata' in sect.Name:
 5 ...     rdata = sect.get_data()
 6 ...
 7 >>> len(rdata)
 8 53760
 9 >>> rdata[:100]
10 '\xa8\x11\x01\x00\x00\x00\x00\x00\xc8\x11\x01\x00\x00\x00\x00\x00RC\x18\x89\x1c[]\xd\
11 0KGG\x84E\x03W\x82S\x04\x1c\xdeGLK\xd6GU\x03\x8fOKI\xc5+]W\xdfTT]\xd8\x19RA\xd0ZQZ\x\
12 cbDJ\x07\xd1HJS\xdc\x03FH\x8aYt\x0e\x88ZxI\xf2Z5I\xbbZ3K\xa4G%_\xafD-Q\xf3'
13 >>> rdata[16:100]
14 'RC\x18\x89\x1c[]\xd0KGG\x84E\x03W\x82S\x04\x1c\xdeGLK\xd6GU\x03\x8fOKI\xc5+]W\xdfTT\
15 ]\xd8\x19RA\xd0ZQZ\xcbDJ\x07\xd1HJS\xdc\x03FH\x8aYt\x0e\x88ZxI\xf2Z5I\xbbZ3K\xa4G%_\\
16 xafD-Q\xf3'
17 >>> test = rdata[16:]
18 >>> test = test[:0x3aa]
19 >>> test[:100]
20 'RC\x18\x89\x1c[]\xd0KGG\x84E\x03W\x82S\x04\x1c\xdeGLK\xd6GU\x03\x8fOKI\xc5+]W\xdfTT\
21 ]\xd8\x19RA\xd0ZQZ\xcbDJ\x07\xd1HJS\xdc\x03FH\x8aYt\x0e\x88ZxI\xf2Z5I\xbbZ3K\xa4G%_\\
22 xafD-Q\xf3B>V\xa1\x08,5\xa9I<4\xabO%1\xfc'
23 >>> open('3aa_data.bin','wb').write(test)

Now we can attempt to decode this data like we did the previous DLL, unfortunately it doesn’t appear to decode to anything meaningful. So do we have the wrong key or is this data decoded differently than the other? Since it doesn’t appear to be decoded in the loader piece but simply copied over during injection, it might be referenced by the DLL? Different ways to go about this ofcourse we could latch in to the injected process and follow this out to find where this data gets referenced or the offset gets stored. Sometimes you can get lucky by searching for things statically however such as the immediate value 0x3aa or perhaps the opcodes used in the decoding routine we previously reversed or even trying to follow out the references to the BASECONFIG string we found previously. I’m going to start by searching for 0x3aa because the DLL will need the size in order to decode and/or copy the data.

After following out a few hits on our search for the immediate value 0x3aa we find a function being called on some data with 0x3aa passed as parameter. It appears that something is being copied over into this ‘BASECONFIG’ we found earlier. This first function I’ve went ahead and labeled as memcpy as it is a commonly found onboard data copy routine. What is more interesting is the hardcoded value that is then passed along with the address of the copied data. Could this be the key? Looking at this function shows that it is indeed the same loop we have previously reversed from the loader piece but we can go ahead and run the decode over it to check what will be decoded out.

 1 >>> data = open('3aa_data.bin','rb').read()
 2 >>> test = bytearray(data)
 3 >>> rol = lambda val, r_bits, max_bits=32: \
 4 ...     (val << r_bits%max_bits) & (2**max_bits-1) | \
 5 ...     ((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits)))
 6 >>> key = 0x3029af23
 7 >>> test = bytearray(data)
 8 >>> for i in range(len(test)):
 9 ...   test[i] ^= (key & 0xff)
10 ...   key = rol(key,8)
11 ...   key += 1
12 ...
13 >>> test
14 bytearray(b'qr298ivantk6c7z1t12jozdcnb39esxr\x00degxnna4iujtmopkw1mxtda2yp4k477i9s2n\
15 wrzopwfqablsho7zxid1kuostultls4xgg40117\x006oesk96euo4bhrqxazn35zfa8iqghmyd8ibhuxlf3\
16 dwpjw41xzxjr82eu2kvkb9h5bhttp://jjolasretokermas.su/dfrt/olert.php\x005kvxephcbdd511\
17 z4tb2un3jyp4fil5mgddqhvhea2guzumgagikrq8pp3bkslim6z1suufk93nl9g8a3y27y1uhz576ftcanfv\
18 5zwfl4ee4kqyau7osbh1oabtn319ktvfbb1il4pddakbkh63tsxdcpjseoazd87qcnotzlj639zahjtx2n7g\
19 9gmwvwcq9lwxopm22inxip6vmwil1y7mxb2om\x03\x00\r\x00pay\x03\x00\r\x00ov6h73ev4dHY28hA\
20 uikzh\x00ig2lzszkv2yziedamix63eqyr3pfa26lunw1wxpbfgicjx6i2krczt2b7ewo481wb22nfwf2jxf\
21 8uay92pxdffqahwlaqasbrtdky1pcdbzl8mk19rrdjrb7yiiy\x00\x00\x00\x00bir48fhawbobudqskzt\
22 yy\x003r9wpkxrrpai7m3ibn\x00dvpbf@\xf6,{9u32a3\x0f\x00\x00\x00ngnqc2pi\x86M\xca\x04\\
23 xa6\xb1a\x176t7yu6sqnv3zot97lb8yhod1wicom1ec6q1r7rrc17yqet7x36motr9uacgqdkvokgsglhwa\
24 fuahsowtt1fp4fhy95udd3c5o8a1fsw2lso5xpqnwf951hw1aitbwmzpb4weyr4nhezqdxnf8bpmrakgkcrz\
25 sv3qj4ev77ddq7dfke1h1qu5sba4affvquvkwmt9bkdbgmw633dz3lhyi19ktdyyz2vxl2xup75bb7xjm7wz\
26 hk\x95\t\x17\x10\x96\n\x18\x11\x97\x0b\x19\x12\x98\x0c\x1a\x13\x99\r\x1b\x14\x9a\x0e\
27 \x1c')

So at the very least we can see a URL starting with ‘http’, however it looks like there could be some random data added in which could be to make it difficult to brute force out the data and to conceal some other data in there.
After that we see some indexes into the data that was in there, ‘[edi+1c8h]’ in particular appears to be referencing a null terminated string?

1 >>> test[0x1c8:]
2 bytearray(b'dHY28hAuikzh\x00ig2lzszkv2yziedamix63eqyr3pfa26lunw1wxpbfgicjx6i2krczt2b\
3 7ewo481wb22nfwf2jxf8uay92pxdffqahwlaqasbrtdky1pcdbzl8mk19rrdjrb7yiiy\x00\x00\x00\x00\
4 bir48fhawbobudqskztyy\x003r9wpkxrrpai7m3ibn\x00dvpbf@\xf6,{9u32a3\x0f\x00\x00\x00ngn\
5 qc2pi\x86M\xca\x04\xa6\xb1a\x176t7yu6sqnv3zot97lb8yhod1wicom1ec6q1r7rrc17yqet7x36mot\
6 r9uacgqdkvokgsglhwafuahsowtt1fp4fhy95udd3c5o8a1fsw2lso5xpqnwf951hw1aitbwmzpb4weyr4nh\
7 ezqdxnf8bpmrakgkcrzsv3qj4ev77ddq7dfke1h1qu5sba4affvquvkwmt9bkdbgmw633dz3lhyi19ktdyyz\
8 2vxl2xup75bb7xjm7wzhk\x95\t\x17\x10\x96\n\x18\x11\x97\x0b\x19\x12\x98\x0c\x1a\x13\x9\
9 9\r\x1b\x14\x9a\x0e\x1c')

That is interesting considering the next function call appears to be counting the byte length of a string until a NULL character is reached(Figure 31). The next function after that is one we have talked about before, a standard RC4 init function where the sbox is built and then the key scheduling is performed.

Interesting so it looks like this function is simply decoding the onboard config data and pulling out an RC4 key. Following the cross references to this function and we find ourselves with the rc4 crypt routine as well! Now we are starting to really map out some interesting functions in this bot that can let us pivot by xrefs and find out what this bot is trying to do. Kind of like this one where I saw a call to memcpy followed by rc4_crypt which made me interested in what the surrounding calls were, as it turns out the one before the memcpy was visualencrypt.

RC4:

VisualEncrypt:

Visualencrypt is interesting because it dates back to Zeus and Citadel and has been widely used ever since for C2 communications. So now I wonder if the RC4 key is in fact the key for C2 traffic. The easiest way to check is to either simply let the bot run and capture traffic using a proxy such as fiddler or find your favorite blogger and ask or look for a pcap of traffic. Forums can also be a good source of malware such as kernelmode forums, you can frequently find a related sample there and then push it to an online sandbox that allows you to download pcaps for free or even ask for help with a particular routine you are reversing. Moving on with a pcap(db3ad662e08d9feb5d1b977d9e84ea7d7151435d12df13d70a14f7309e9948a5) and a new sample(4df8cd39c1e158f9d084ca87384359aa3a4fa221026a3d713ffe03370ec4bbd4) we can use all that we learned so far to brute out the config from the .rdata section.

First we need to get the data from the .rdata section:

1 def brute_conf(fdata):
2     pe = pefile.PE(data=fdata)
3     data = None
4     for section in pe.sections:
5         if '.rdata' in section.Name:
6             data = section.get_data()

Next we setup a way to loop through the data in chunks of 0x3aa length in order to try to find known strings in the decoded data.

 1     conf = None
 2     if data != None:
 3         for i in range(len(data)-0x3aa):
 4             test = bytearray(data[i:i+0x3aa])
 5             key = 0x3029af23
 6
 7             for i in range(len(test)):
 8                 test[i] ^= (key & 0xFF)
 9                 key = rol(key, 8)
10                 key += 1
11             if 'http:' in test:
12                 conf = str(test)
13                 break
14     return conf

Here we have simple setup a generic return variable and set it to None and then begin looping through the data up until the last 0x3aa block, if we find a decoded chunk of data with the string ‘http’ in it then we assume we found the config and return it.
Running our script on this new sample appears to show we have correctly reversed this aspect of this version of the bot atleast!

 1 >>> conf
 2 't5tfslwyb5ih7rw75l5lvd4zyvmwqlat\x00hln9z5vgeedyrpwvyxpkh4vezneeqijhmugawemtyeapds6\
 3 k7u9dqcgzskcsmr1hpu1aojgNEW CRYPTER\x001w1adp7b9qhgacux5bqpin7t1kzmo8wqq3sfeowwpjjzr\
 4 vy8ayil5ybcbishttp://manoftheg.su/FE8hVs3/gs98h.php\x00eumnjk3i3qxayp7ljgwdloh7d1mfo\
 5 bcs5dwd44zb5t9qf6l4sxi44b2ugkzbyu9wzqff57emww1h8pap5ktmy66xfjozbicioqct7n49mxhn5m91i\
 6 rjfqf8g4i9lgxmbxd2lwvl2o8jtbh56zdazmp9xj8r6l4xtufvoquqptjf37pnjvqwdpq818ebk7ocrcpsvh\
 7 d6x3afmxriitozgolyix4nkesc\x03\x00\r\x00eao\x03\x00\r\x004rywspa5bbeb7S8GwOfDB\x00e2\
 8 kdwykxusndfqcb6aiay9a4h2xp7wb94ud6s4jluy5zukoe3jf8hb26jfyfg4dpqmyszv512ma9ozwwqgqhr2\
 9 szuk8csa5dwwncbzdhabglijxi6xv9d9lzkjy4\x01\x00\x00\x00w6hnh3y1jkbegbr5jx\x003lkdq9k4\
10 1kwcz7wy5zwshp\x009rp5\xc5\xfd\xfd{k6k47j\x05\x00\x00\x0035g16bt1\xddN^U\xfd\xb2\xf5\
11 F7dcsfto69yda7cy1jzkfocyf5ozls4jourzxi2ju84k5hbv7otajmj5cmgywpcphn4w7vktxsvj2armlowq\
12 8k5xiamozslqwfw9qzrc6mrbqigzhhzg7h9ymmgpw7prssed5y916eh7a4j1pwuijufi62j8m1wwetf7pdnu\
13 htr19xkpvnkr55a2b2mfl2gc63zuuofcfjffe6vnxt3j9tob79wj2jenac5j5orpmjv3dpl8gdg\x95\t\x1\
14 7\x10\x96\n\x18\x11\x97\x0b\x19\x12\x98\x0c\x1a\x13\x99\r\x1b\x14\x9a\x0e\x1c'
15 >>> conf[0x1c8:]
16 'beb7S8GwOfDB\x00e2kdwykxusndfqcb6aiay9a4h2xp7wb94ud6s4jluy5zukoe3jf8hb26jfyfg4dpqmy\
17 szv512ma9ozwwqgqhr2szuk8csa5dwwncbzdhabglijxi6xv9d9lzkjy4\x01\x00\x00\x00w6hnh3y1jkb\
18 egbr5jx\x003lkdq9k41kwcz7wy5zwshp\x009rp5\xc5\xfd\xfd{k6k47j\x05\x00\x00\x0035g16bt1\
19 \xddN^U\xfd\xb2\xf5F7dcsfto69yda7cy1jzkfocyf5ozls4jourzxi2ju84k5hbv7otajmj5cmgywpcph\
20 n4w7vktxsvj2armlowq8k5xiamozslqwfw9qzrc6mrbqigzhhzg7h9ymmgpw7prssed5y916eh7a4j1pwuij\
21 ufi62j8m1wwetf7pdnuhtr19xkpvnkr55a2b2mfl2gc63zuuofcfjffe6vnxt3j9tob79wj2jenac5j5orpm\
22 jv3dpl8gdg\x95\t\x17\x10\x96\n\x18\x11\x97\x0b\x19\x12\x98\x0c\x1a\x13\x99\r\x1b\x14\
23 \x9a\x0e\x1c'

Now with our potential RC4 key in hand we go about dumping the requests and responses out of the pcap, whatever system you use to keep track of this I recommend it to be as detailed as possible. Mapping out C2 communications of malware can be tricky and usually require large amounts of data, as such I usually dump each request and response paired appropriately and then as I look at each one I write it down in notes labeled appropriately.

For dumping the sections I’m going to load the pcap into fiddler, dump out each decoded request and then md5 hash them to see if any are the same. While doing that we can also write down each requests size
After dumping all the requests from the pcap you should have 12 requests and 12 responses in terms of data POSTed to the C2 and data responses from the C2. Now we can test out our RC4 theory on request 1 data using an RC4 script that is included in Appendix A.

It looks like the response data from the first two requests are pretty large, they could be the same size because it is the same data but then why is the requests different? First let’s go ahead and rc4 decrypt the first response with the key we found previously.

 1 # xxd req1.decr
 2 00000000: ef1b 2830 4ea7 d1ad dd35 e25f 6bca 7d1b  ..(0N....5._k.}.
 3 00000010: 43c3 0e14 0400 0000 0000 0000 0404 0404  C...............
 4 00000020: e30c 29cb 83c1 32b2 14a4 3f8a 837f ad43  ..)...2...?....C
 5 00000030: 5374 7474 7474 7474 7070 7070 7474 7474  Stttttttpppptttt
 6 00000040: 7474 7474 6542 4242 4242 4242 4646 4646  tttteBBBBBBBFFFF
 7 00000050: 4242 4242 4242 4242 4c67 6767 6767 6767  BBBBBBBBLggggggg
 8 00000060: 6363 6363 6767 6767 6767 6767 ab10 ba45  ccccgggggggg...E
 9 00000070: 4545 4545 d1d2 d2d2 4645 4545 2a23 8589  EEEE....FEEE*#..
10 00000080: aae6 0a3d 1e5a f6da bcd5 b460 aaa7 61d9  ...=.Z.....`..a.

If you’ve ever looked at xor encoded data then this is pretty obviously encoded due to the frequency of the repeating characters, we had previously seen that the bot made use of VisualEncrypt so let’s try to VisualDecrypt it and see what happens.

 1 # xxd req1.decr.visdec
 2 00000000: eff4 3318 7ee9 767c 70e8 d7bd 34a1 b766  ..3.~.v|p...4..f
 3 00000010: 5880 cd1a 1004 0000 0000 0000 0400 0000  X...............
 4 00000020: e7ef 25e2 4842 f380 a6b0 9bb5 09fc d2ee  ..%.HB..........
 5 00000030: 1027 0000 0000 0000 0400 0000 0400 0000  .'..............
 6 00000040: 0000 0000 1127 0000 0000 0000 0400 0000  .....'..........
 7 00000050: 0400 0000 0000 0000 0e2b 0000 0000 0000  .........+......
 8 00000060: 0400 0000 0400 0000 0000 0000 ccbb aaff  ................
 9 00000070: 0000 0000 9403 0000 9403 0000 6f09 a60c  ............o...
10 00000080: 234c ec37 2344 ac2c 6669 61d4 ca0d c6b8  #L.7#D.,fia.....

Sometimes you can see something like this and think “I must of gotten something wrong” or “perhaps there’s another layer”. Remember though that regular language data structures such as C structs will sometimes look like blobs of data so instead of looking for blatantly obvious strings you could have flags or integers or any number of things. If we focus in on the data at 0x30 through 0x80 you can see that there are values repeating twice at a similar interval, this isn’t something I would expect to see in encoded or compressed data. So we have confirmed so far that this traffic is unknown -> VisualEncrypt -> Rc4_crypt. Possibly then we have some sort of data structure, so how to go about figuring it out? Well you have the long and thorough way of following it out in the bot to see how it is built and/or how it handles parsing the data from the C2 response. Studying over the data however we can attempt to break down any structure to it that might be visible such as looking for length indicators, the first 20 bytes I just immediately put off into the unknown category whether you list them as 5 unknown dwords or one unknown byte array of length 20 is up to you but the next dword value after that is interesting in that it is the little endian(LE) value of the length of the data. This makes me think we have a header at the very least describing the entire data segment, after this we have LE 0 and 4 followed by 16 bytes of unknown data which once again I label as an unknown byte array of length 16 bytes.

1 struct request{
2     byte unknown1[20];
3     dword total_length;
4     dword unknown2;
5     dword unknown3;
6     byte unknown4[16];

The next dword isn’t length unless it’s broken down further into bytes but that would give you a record length of only 255 which is doable but limiting. So marking down again an unknown dword of value 10000 followed by another one of value 0 we find two dwords of value 4 back to back with a dword of value 0 following that.

1     dword unknown5;
2     dword unknown6;
3     dword unknown7;
4     dword unknown8;
5     dword unknown9;

Kind of odd but it appears this structure holds true for the rest of the data right up until we get to the 5th entry which instead of two values of 4 we have two values of 916, taking the rest of the data in the request past the two dword values of 916 and getting the length gives us 916! Interesting so the two values represent the length of the data in the record but then why do we have two? I reserve that question and look back over what we have so far before realizing that the dword of value 4 near the beginning matches the number of normal looking records.

 1 struct request{
 2     byte unknown1[20];
 3     dword total_length;
 4     dword unknown2;
 5     dword num_records;
 6     byte unknown4[16];
 7     record records[num_records];
 8     record long_record;
 9 }
10 struct record{
11     dword unknown5;
12     dword unknown6;
13     dword length1;
14     dword length2;
15     byte data[length1];
16 }

My attention now moves on to the unknown4 16 byte array, could this be a hash? A simple md5 of the data after those 16 bytes confirms that this is indeed an md5 hash. Interesting that we have mapped out quite a bit about this data by merely studying it and making observations, while our observations could be completely wrong we have proven in a few instances that they are correct without even really looking at how the malware is manipulating this data. Our endgame on this one however isn’t just to understand but to reverse parts of the bots c2 protocol to the point of being able to mimick a bot in certain situations. So to figure out what this request is leading us to I shortly diverted my attention to the response which was rather large. Decrypting the data in the response shows us a large collection of compressed PE files, you can use whatever trick you want or one we have previously explained to find the decompress routine. Personally I just sat on the rc4_crypt routine and did a pcap replay to find where the bot walks through the top part of the decoded data.
The next routine called then calls this same function a bunch of times, a few steps into this routine and we find a call to a function that looks like a good candidate for a decompress routine.
There’s one obviously hardcoded value of 0xd00 used in this routine for a comparison, a quick online search for “decompress >0xd00” gives us a few results for nrv2b which is interesting because nrv2b was used by Zeus. At this point I decided to go the community route for multiple reasons but primarily because I had a contact with a researcher who was very well versed with Zeus variants who I know was also working on one of these samples at the time(@maciekkotowicz). He quickly confirmed that the structure of the data was also related to Zeus as binstruct. Now with this knowledge in hand and thanks to Zeus being open source we can go check out the code responsible for parsing that data structure and write own version in python(Appendix A 2 Zeus Binstruct Parser). Now we should have everything we need to start parsing out the C2 requests and most of the responses, I say most because the first request is a bit special as it gives you a bunch of DLLs which turn out to be all the modules that will be used, we’ll find out what that all happens as we map out the C2 protocol a bit more.

Zeus Binstruct structure from source:

Now that we’ve peeled back the layers surrounding the C2 traffic it’s time to start parsing and recording the requests and the responses to fill out our previous table. Let’s take a look at request 1 which returns the DLLs from the C2 as previously mentioned.

 1 HEADER:
 2 SIZE: 1040
 3 FLAGS: 0x0
 4 COUNT: 4
 5 MD5: e7ef25e24842f380a6b09bb509fcd2ee
 6
 7 ITEMS:
 8 ID: 10000
 9 flags: 0
10 UNKNOWN: 0
11 ID: 10001
12 flags: 0
13 UNKNOWN: 0
14 ID: 11022
15 flags: 0
16 UNKNOWN: 0
17 ID: 4289379276
18 flags: 0
19 UNKNOWN: 6f09a60c234cec37234..<snip>

Looks like everything is in order for HEADER structure but we are left with some immediate questions to be answered by looking at the ITEMS.

What do the IDs map to related to data

Why is most of the data 0

What is the giant blob of random looking data at the end

Depending on the bot could greatly determine the recommended course of action to take when trying to answer these questions, we could for instance find the relevant function in the bot to try to determine how these fields are populated or we could simply continue processing the traffic requests and try to come to our own conclusions based on the data. For brevity we’ll be going with the second option for now and then if we decided we need more data we can always fall back on the more thorough option.
The second request deconstructs to the same thing but the last ITEM has different randomish data, my guess at this point is that this is actually random data of random length to make the request size different each time. Let’s take a look at the third request.

 1 ITEMS:
 2 ID: 10000
 3 flags: 0
 4 UNKNOWN: MARRS-PC_E6B6B0E458EAACCC
 5 ID: 10010
 6 flags: 0
 7 UNKNOWN: NEW CRYPTER
 8 ID: 10001
 9 flags: 0
10 UNKNOWN: 17696256
11 ID: 11024
12 flags: 0
13 UNKNOWN: C:\\Users\\rouchard.mars\\AppData\\Roaming\\Ypveek\\tyes.zoe
14 ID: 11025
15 flags: 0
16 UNKNOWN: 4190148601

Interestingly enough we have some overlap of ID values but this time they are filled in. ID 10000 which appears to contain a bot id, it’s very common for malware to make the botid the computer name followed by a hash or combination of some data from the computer. ID 10001 which now has an integer value, since this malware downloads a Zeus DLL and has all this Zeus code reuse I wonder if it is perhaps a version. Instead of looking at Zeus source I decided to talk to another researcher that spends about as much time as I do following banking trojans, Jason Milletary(@jasonmilletary). He was presently already looking at another sample of the same malware and we were quickly able to confirm that this was the version. To decode this we just take break the number up into each byte, 17696256 which is 010e0600 in hex or version 1.14.6.0. Not entirely sure what the others are yet but maybe the response can give us some information?

1 ID: 11026
2 flags: 0
3 PHP_FILE: <?php $GLOBALS['428621570']=Array('so' .'c' .'ket' .'

So it returns a php script which is interesting because some of the DLLs downloaded in the first response were related to PHP windows binaries for running php scripts on windows. Figuring out what this php script does basically requires deobfuscating it, since deobfuscating scripts is mostly the same process I’ll run through my process real quick. There’s two scenarios possible here, either the script is ‘packed’ where it will need to be ‘unpacked’ and then executed which is a trick you see commonly with php webshells or it’s some form of obfuscation. Commonly the obfuscations just come down to string names being replaced and functions or important strings being encoded and stored in arrays. First thing I usually do is to beautify what I’m looking at and then for this script we just need to replace all the array references for $GLOBALS[‘428621570’] where it’s string equivalent. After that we identify the decode routine, reverse it or run it against the encoded data and then print out the decoded array that way similar to what we just did we can then replace the references to those strings with their decoded versions. After all that is done you just start renaming functions as you figure out what they do.

Now all we’ve done is replace the references to the GLOBALS array with the string from the array and identified the decode function based on the variables names used to access an array of binary strings.

So as an overview it looks like one of the strings is a filename to read from and then this is decoded and the file is written out. To find out these strings I prefer to let the code do the work for me so I take out all the relevant functions that will be needed and throw them in another PHP file and then add in the calls to decode and echo out the results, detonating this via command line ‘php –f test.php’.

This tells us first what the output filename will be which is the variable ‘$mylgwit’ and then the input filename which is ‘$svjzpzs’.

1 C:\Users\rouchard.mars\AppData\Roaming\Ypveek\tyes.zoe.exe
2 C:\Users\rouchard.mars\AppData\Roaming\Ypveek\tyes.zoe

The input filename matches what was sent to the C2, so the C2 took that file path and then wrapped it up into this PHP script, appened an ‘.exe’ to the end and it appears will use this to execute the encoded file it has stored on disk. Since it downloaded the PHP DLLs needed and generates PHP scripts to execute the files then this is probably part of the persistence piece of the malware.

Enough down that rabbit hole, hopefully it showed you a process by which you can deobfuscate PHP scripts or any script really, either the data is obfuscated and you just start deobfuscating it pieces at a time or it’s packed/encoded in some way and you just need to figure out how or when that data will be used in the clear and then let the code do the work to show you the next piece.

The next request has some new values in it that we haven’t yet identified.

 1 ID: 10000
 2 flags: 0
 3 BOT_ID: MARRS-PC_E6B6B0E458EAACCC
 4 ID: 10010
 5 flags: 0
 6 BOTNET: NEW CRYPTER
 7 ID: 10001
 8 flags: 0
 9 VERSION: 1.14.6.0
10 ID: 10002
11 flags: 0
12 UNKNOWN: 0601b11d0000
13 ID: 10003
14 flags: 0
15 UNKNOWN: 2167908544
16 ID: 10004
17 flags: 0
18 UNKNOWN: fe80000000000000f5a4092790153b82

What’s more interesting about this however turns out to be the response of a large list of C2 URLs and a section for WebFakes and WebFilters which is related to the Zeus configuration.

Making note of our observations in our table we can continue this process of looking at what data is being sent to the C2 and what data is returned thus letting us determine atleast at a high level a breakdown of the traffic protocol used by the bot. There are ofcourse as we have seen with other systems where some data being returned could be harder to determine its purpose and require the researcher to delve into various parts of the bot at a static and dynamic level to full breakdown and understand all pieces of the protocol. Ofcourse how far you go depends on any number of circumstances that are up to each researcher and their goals. For us, right now, the goal is simply to be able to pretend to be a bot as far enough to be able to harvest configuration data from C2s.

After continuing our process as we previously mentioned, let’s take a look at our table of mapped requests and responses. Request 11 and 12 are simply requests for the URL list again but this time with a new botnet name which is probably due to the update binary.

The ID 0 responses appear to come after the bot has posted data to the C2 in some form, which means it’s probably a way to tell the bot ‘ok’. Let’s clean up this table a bit though by taking all the common ID elements out which should leave us with the elements of the request that are particular to the requesting of the response data.

We now have enough information about this bots C2 protocol to begin trying to recreate it ourselves. I’ll be using python for this and stepping through code in chunks as I build it.
Let’s start by identifying what the script will require as user input, ideally we would want to be able to handle user input for the BotID and BotNet portion of the request data as well as the RC4 key and the URL. For demonstration however I’m just going to have a generic BotID hardcoded, that leaves us with accepting 3 parameters(BotNet, RC4 Key, C2 URL).

1 if __name__ == "__main__":
2     botnet = sys.argv[1]
3     rc4_key = sys.argv[2]
4     c2 = sys.argv[3]

Since the data is in zeus binstruct form we can take our previous zeus binstruct code and change it up a bit to be able to handle generating data instead of just decoding the data, most of the changes will involve adding in code for ‘putters’ or methods for adding data. For our main Python Class we can take out the data that would normally be used to parse the header and just have an empty list setup to store items in.

1 class binstruct_data:
2     def __init__(self):
3             self.bin_items = []

Next up is our ‘putter’ or a method we can use to store a binsuct item in out list of items.

1 def put_item(self, id, flags, sz, realsize, data):
2         self.bin_items.append(binstruct_item(id,flags,sz,realsize,data))

The ‘getter’ is basically a method we can call to get the binstructed data, so it’s going to get each item and then construct the header and the last garbage item in the list. For a simple example I’ve hardcoded a garbage items data but also have generated garbage data for the header to give you an example of how to do it both ways.

 1     def get_items(self):
 2         ret_val = ""
 3         for item in self.bin_items:
 4             ret_val += item.get_item()
 5         g_item_data = '''\xd9i\xe8\xef\x03\xcdj1BpkXP\xba\x04\xcb\xb1\\P8xw\xcf\x92\\
 6 x9f\x85\xd2@\xe1\xeb\x08_\x15\xb7\xe0@*\xa1\xfd\xf0\x13\x9a\\\xdbs\x11b\x96\t\xa7\xd\
 7 4B\xeb\xd8\x98\x1f\x1b\xa6\xcakD\x08\xdc\xe4\xe6\x101\xc9\xfe \x93Ezl\x9c\xa6)\xb5q\\
 8 x99!To(@\xe6\x1a\x89\x11?\x19\x19\x17\x7fr\xa4\xa6\x858r\x11\x9a\xae\x9c\xd8#`\x1d\x\
 9 ee\xa7\xb4$\x7f\xfbt\xa0{\xd9\\\x9fM\xc9\r\x11)P\xffU\x85\xd1\x1e\xc5\x14\x08\x92\xc\
10 8\xf3+\x08`Y<\xc9\x99\xd1Qn\xe0\x1e\xc1\x08!v9\xf4je\t\xb8G\xc7\xd4\xf6\x7f\xf3\xe5\\
11 x82\xa1d\\\xb7\x0elj\x02\xc5\xf00~\xfa^u\xcc\'@\x9a[\xa1\xd4\x06\xa0\xd3\x8f\xd8\xf8\
12 \xb7\x15\x06\x03{\xf7BF\x03-\x1b\xc8\xca@\xba\xf4\xd8\xc8\xf2g\xc6\xa3\x01Y\x01\x18\\
13 x1fh;}l\x90\xe9\x9c\xdb\xbeZ\xf9\xc6=\x18\x94r_B\xc5\xbcU\xed_\t\x03Hu\x94\t\x08,2GL\
14 \x08\x88\x87\xc7\xd0\x98\xc7\x88\xf3\x148^M\xb9t+\x18\te\xab\x02\x85\xb1\x81pXu\xa8\\
15 xa2\x1e,q\x9a\x83\x88\xd2Qj\xadz\xf2K\x8ar\x06%*\xfda\x8cs\xb5\xe6mg\x82\x05\x0bK\xc\
16 5\xaf\x95\x18\x8f\xd9\x8b\xe1Y\xfaB.z\xdb\xd1\x99\x957\xc0Rg\x14\xa8\xc0\x8a\xfd\x89\
17 @;\x97\x0b#uI\xa3)DSLj\tG\x9f\xe6\xbb\xcb\xc0\'\xc7$\xd9t\xa9\xc1MY\xcdc\x07d\x90\x9\
18 d\xde\x07\xf2s\x14a\xdb\x84\xb0a\x92W)%\xbem\x89\x85sb\x079\xf2\x88\xeb\x10\x91\x9dw\
19 \x11e\x1agT\xe3^r\x97\xf7\x8d\xc8;\xa5)7`\xab\x1f\xab\x9deF\xe2\xf4M\xb0\xab\x8c\xd6\
20 \xff\xd8p{e\xc1\xf1\x1a^\x81\xc8(\xda\xe6Hd\xab\x90co"<\xf3\xdb\xc6\xc9\xd2B\xdb\xec\
21 \x8d\xa3\xc3\xd7;t\rMi\xf1H8\xf9~>\x11\x8b\xcdJV\\\x82,\xea\x98\xd1I\xcf\xbeJm\xfdf'\
22 ''
23         garbage_item = binstruct_item(4289379276, 0, len(g_item_data), len(g_item_da\
24 ta), g_item_data)
25         ret_val += garbage_item.get_item()
26
27         #Generate header
28         header = struct.pack('<III', len(ret_val), 0, len(self.bin_items))
29         header += hashlib.md5(ret_val).digest()
30         garbage = ""
31         for i in range(5):
32                     garbage += struct.pack('<I', random.randint(3333,99999999))
33         return(garbage+header+ret_val)

The binstruct_item class remains pretty much the same except that we replace the str method with our own ‘getter’ method that will return the constructed data.

1     def get_item(self):
2         ret_data = struct.pack('<IIII', self.id, self.flags, self.size, self.realSiz\
3 e)
4         if self.realSize == 4:
5             ret_data += struct.pack('<I', self.data)
6         else:
7             ret_data += self.data
8     return(ret_data)

So now with that out of the way we can focus in on the main function that we started earlier. Basically the first part of the main function will just be us creating an object of binstruct_data class and then handling all of the items that are in every request.

1     b = binstruct_data()
2     bot_id = "COMPNAMEA_1F3D59E96522DF69"
3     b.put_item(10000, 0, len(bot_id), len(bot_id), bot_id)
4     b.put_item(10010, 0, len(botnet), len(botnet), botnet)
5     b.put_item(10001, 0, 4, 4, 17760256)

Now comes an optional argument that is a flag that tells the script what to try to download, the available flags are the same as the data we previously focused in on which are the injects, update binaries and webfakes with c2 data.

 1     if len(sys.argv) > 4:
 2         command = sys.argv[4]
 3     else:
 4         command = '-i'
 5     if command == '-i':
 6         #webinjects
 7         b.put_item(11021, 0, 4, 4, 0)
 8     elif command == '-f':
 9         #Webfakes
10         b.put_item(10002, 0, 6, 6, '\x06\x01\xb1\x1d\x09\x00')
11         b.put_item(10011, 0, 4, 4, 3513500427)
12         b.put_item(10003, 0, 4, 4, 167880896)
13     elif command == '-u':
14         b.put_item(11020,0,4,4,0)
15     temp = visenc.visual_encrypt(bytearray(b.get_items()))
16     rc4 = ARC4.new(rc4_key)
17     post_data = rc4.encrypt(temp)

All that’s left after that is simply posting the data up to the C2 and then retrieving the response, decrypting it and then deconstructing the binstructed data. A fully functioning example of this script is included in the Appendix.
Using this new script we can make requests to the C2 URLs and pull down the larger lists of C2s along with the webinjects!

#References

https://www.botconf.eu/wp-content/uploads/2014/12/2014-3.6-ZeuS-Meets-VM-%E2%80%93-Story-so-Far.pdf

https://www.virusbulletin.com/virusbulletin/2015/11/shifu-rise-self-destructive-banking-trojan

https://en.wikipedia.org/wiki/RC4

https://en.wikipedia.org/wiki/Linear_congruential_generator

https://github.com/pfalcon/uzlib/blob/master/src/tinflate.c

https://en.wikipedia.org/wiki/Zlib

Appendix A

Kovter Config Parser

 1 import re
 2
 3 class Config:
 4     def __init__(self, entries=[]):
 5         self.count = 0
 6         self.entries = entries
 7
 8     def put_entry(self, entry):
 9         self.entries.append(entry)
10         self.count += 1
11
12     def __str__(self):
13         ret_val = ""
14         for entry in self.entries:
15             ret_val += str(entry)+'\n'
16
17         return ret_val
18
19 class ConfigEntry:
20     def __init__(self, name, data=""):
21         self.name = name
22         self.data = data
23
24     def get_name(self):
25         return self.name
26     def get_data(self):
27         return self.data
28
29     def __str__(self):
30         ret_val = ""
31         if self.name == 'mainanti':
32             ret_val = self.name+": \n"
33             temp = self.data
34             while temp != "":
35                 anti_name = re.search(r'[^(:)]+',temp).group()
36                 temp = temp[len(anti_name)+1:]
37                 temp2 = temp.find(':'+anti_name)
38                 anti_data = temp[:temp2]
39                 temp = temp[len(anti_data)+len(anti_name)+1:]
40                 ret_val += '  '+anti_name+': '+anti_data
41                 if temp != "":
42                     ret_val += '\n'
43         else:
44             ret_val = self.name+": "+str(self.data)
45         return ret_val
46
47 def parse_config(conf_data):
48     config = Config()
49
50     conf = conf_data.split('::')
51
52     while conf_data != "":
53         name = re.search(r'[^(::)]+',conf_data).group()
54         conf_data = conf_data[len(name)+2:]
55         temp = conf_data.find('::'+name)
56         data = conf_data[:temp]
57         conf_data = conf_data[len(data)+len(name)+2:]
58
59         config.put_entry(ConfigEntry(name, data))
60     print(config)

Zeus Binstruct Parser

 1 import sys
 2 import struct
 3 import binascii
 4
 5 class binstruct_hdr:
 6     def __init__(self, sz, flags, count, md5):
 7         self.size = sz
 8         self.flags = flags
 9         self.count = count
10         self.md5 = binascii.hexlify(md5)
11     def __str__(self):
12         ret_val = "HEADER:"+'\n'
13         ret_val += "SIZE: "+str(self.size)+'\n'
14         ret_val += "FLAGS: "+hex(self.flags)+'\n'
15         ret_val += "COUNT: "+str(self.count)+'\n'
16         ret_val += "MD5: "+self.md5+'\n'
17         return ret_val
18
19 class binstruct_data:
20     def __init__(self,data,binItems):
21         self.bin_items = binItems
22         (junk, sz, flags, count, md5,) = struct.unpack_from('<20sIII16s', data)
23         if count > 50000:
24             print("Sanity fail on too many items")
25             sys.exit(-1)
26         self.header = binstruct_hdr(sz, flags, count, md5)
27         temp = data[48:]
28         for i in range(count):
29             (id, flags, sz, realSize,) = struct.unpack_from('<IIII', temp)
30             if realSize == 4:
31                 data = struct.unpack_from('<I', temp[16:])[0]
32             else:
33                 data = struct.unpack_from('<'+str(realSize)+'s', temp[16:])[0]
34             self.bin_items.append(binstruct_item(id, flags, sz, realSize, data))
35             temp = temp[16+realSize:]
36     def put_item(self, item):
37         self.bin_items.append(item)
38     def __str__(self):
39         ret_val = ""
40         ret_val += str(self.header)
41         ret_val += "\nITEMS:\n"
42         for bitem in self.bin_items:
43             ret_val += str(bitem)+'\n'
44         return ret_val
45
46
47 class binstruct_item:
48     def __init__(self, id, flags, sz, realSize, data):
49         self.id = id
50         self.name = "UNKNOWN"
51         self.flags = flags
52         self.size = sz
53         self.realSize = realSize
54         self.data = data
55     def __str__(self):
56         ret_val = "ID: "+str(self.id)+"\nflags: "+str(self.flags)+"\n"+self.name+': '
57         if self.realSize == 4:
58             ret_val += str(self.data)
59         else:
60             ret_val += binascii.hexlify(self.data)
61         return ret_val
62
63 def decoder(data):
64     b = binstruct_data(data, [])
65     print(b)
66
67
68 if __name__ == "__main__":
69     data = open(sys.argv[1], 'rb').read()
70     b = binstruct_data(data, [])
71     print(b)

Visual encrypt

 1 import sys
 2
 3
 4 def visual_decrypt(data):
 5     for i in range(len(data)-1,0,-1):
 6         data[i] ^= data[i-1]
 7     return(str(data))
 8
 9
10 def visual_encrypt(data):
11     for i in range(1,len(data)):
12         data[i] ^= data[i-1]
13     return(str(data))
14
15 if __name__ == "__main__":
16     data = bytearray(open(sys.argv[1],'rb').read())
17     encoded = visual_encrypt(data)
18     print(str(encoded))

Zloader C2 Data Pull Script

  1 import sys
  2 import struct
  3 import binascii
  4 import hashlib
  5 import random
  6 import requests
  7 import visenc
  8 import zeus_binstruct
  9 from Crypto.Cipher import ARC4
 10
 11
 12 def generic(data):
 13     return(data)
 14
 15 #expects an int
 16 def version(data):
 17     t = struct.pack('>I', data)
 18     return('.'.join(map(str,list(struct.unpack_from('<BBBB', t)))))
 19
 20 KNOWN_DATA = {10000: ("BOT_ID",generic),10001: ("VERSION",version),10006: ("DATA_THE\
 21 FT",generic),10010: ("BOTNET",generic),11005: ("C2_URLS",generic),11011: ("WEBFAKES"\
 22 ,generic),11012: ("WEBINJECTS",generic),11024:("BOT_LOCATION",generic),11026:("PHP_F\
 23 ILE",generic)}
 24
 25
 26 class binstruct_hdr:
 27     def __init__(self, sz, flags, count, md5):
 28         self.size = sz
 29         self.flags = flags
 30         self.count = count
 31         self.md5 = binascii.hexlify(md5)
 32
 33 class binstruct_data:
 34     def __init__(self):
 35         self.bin_items = []
 36         #(junk, sz, flags, count, md5,) = struct.unpack_from('<20sIII16s', data)
 37         #self.header = binstruct_hdr(sz, flags, count, md5)
 38     def put_item(self, id, flags, sz, realsize, data):
 39         self.bin_items.append(binstruct_item(id,flags,sz,realsize,data))
 40     def get_items(self):
 41         ret_val = ""
 42         for item in self.bin_items:
 43             ret_val += item.get_item()
 44         g_item_data = '''\xd9i\xe8\xef\x03\xcdj1BpkXP\xba\x04\xcb\xb1\\P8xw\xcf\x92\\
 45 x9f\x85\xd2@\xe1\xeb\x08_\x15\xb7\xe0@*\xa1\xfd\xf0\x13\x9a\\\xdbs\x11b\x96\t\xa7\xd\
 46 4B\xeb\xd8\x98\x1f\x1b\xa6\xcakD\x08\xdc\xe4\xe6\x101\xc9\xfe \x93Ezl\x9c\xa6)\xb5q\\
 47 x99!To(@\xe6\x1a\x89\x11?\x19\x19\x17\x7fr\xa4\xa6\x858r\x11\x9a\xae\x9c\xd8#`\x1d\x\
 48 ee\xa7\xb4$\x7f\xfbt\xa0{\xd9\\\x9fM\xc9\r\x11)P\xffU\x85\xd1\x1e\xc5\x14\x08\x92\xc\
 49 8\xf3+\x08`Y<\xc9\x99\xd1Qn\xe0\x1e\xc1\x08!v9\xf4je\t\xb8G\xc7\xd4\xf6\x7f\xf3\xe5\\
 50 x82\xa1d\\\xb7\x0elj\x02\xc5\xf00~\xfa^u\xcc\'@\x9a[\xa1\xd4\x06\xa0\xd3\x8f\xd8\xf8\
 51 \xb7\x15\x06\x03{\xf7BF\x03-\x1b\xc8\xca@\xba\xf4\xd8\xc8\xf2g\xc6\xa3\x01Y\x01\x18\\
 52 x1fh;}l\x90\xe9\x9c\xdb\xbeZ\xf9\xc6=\x18\x94r_B\xc5\xbcU\xed_\t\x03Hu\x94\t\x08,2GL\
 53 \x08\x88\x87\xc7\xd0\x98\xc7\x88\xf3\x148^M\xb9t+\x18\te\xab\x02\x85\xb1\x81pXu\xa8\\
 54 xa2\x1e,q\x9a\x83\x88\xd2Qj\xadz\xf2K\x8ar\x06%*\xfda\x8cs\xb5\xe6mg\x82\x05\x0bK\xc\
 55 5\xaf\x95\x18\x8f\xd9\x8b\xe1Y\xfaB.z\xdb\xd1\x99\x957\xc0Rg\x14\xa8\xc0\x8a\xfd\x89\
 56 @;\x97\x0b#uI\xa3)DSLj\tG\x9f\xe6\xbb\xcb\xc0\'\xc7$\xd9t\xa9\xc1MY\xcdc\x07d\x90\x9\
 57 d\xde\x07\xf2s\x14a\xdb\x84\xb0a\x92W)%\xbem\x89\x85sb\x079\xf2\x88\xeb\x10\x91\x9dw\
 58 \x11e\x1agT\xe3^r\x97\xf7\x8d\xc8;\xa5)7`\xab\x1f\xab\x9deF\xe2\xf4M\xb0\xab\x8c\xd6\
 59 \xff\xd8p{e\xc1\xf1\x1a^\x81\xc8(\xda\xe6Hd\xab\x90co"<\xf3\xdb\xc6\xc9\xd2B\xdb\xec\
 60 \x8d\xa3\xc3\xd7;t\rMi\xf1H8\xf9~>\x11\x8b\xcdJV\\\x82,\xea\x98\xd1I\xcf\xbeJm\xfdf'\
 61 ''
 62         garbage_item = binstruct_item(4289379276, 0, len(g_item_data), len(g_item_da\
 63 ta), g_item_data)
 64         ret_val += garbage_item.get_item()
 65         #Generate header
 66         header = struct.pack('<III', len(ret_val), 0, len(self.bin_items))
 67         header += hashlib.md5(ret_val).digest()
 68         garbage = ""
 69         for i in range(5):
 70             garbage += struct.pack('<I', random.randint(3333,99999999))
 71         return(garbage+header+ret_val)
 72
 73
 74 class binstruct_item:
 75     def __init__(self, id, flags, sz, realSize, data):
 76         self.id = id
 77         self.flags = flags
 78         self.size = sz
 79         self.realSize = realSize
 80         self.data = data
 81     def get_item(self):
 82         ret_data = struct.pack('<IIII', self.id, self.flags, self.size, self.realSiz\
 83 e)
 84         if self.realSize == 4:
 85             ret_data += struct.pack('<I', self.data)
 86         else:
 87             ret_data += self.data
 88         return(ret_data)
 89 if __name__ == "__main__":
 90     botnet = sys.argv[1]
 91     rc4_key = sys.argv[2]
 92     c2 = sys.argv[3]
 93     if len(sys.argv) > 4:
 94         command = sys.argv[4]
 95     else:
 96         command = '-i'
 97     b = binstruct_data()
 98     bot_id = "COMPNAMEA_1F3D59E96522DF69"
 99     b.put_item(10000, 0, len(bot_id), len(bot_id), bot_id)
100     b.put_item(10010, 0, len(botnet), len(botnet), botnet)
101     b.put_item(10001, 0, 4, 4, 17760256)
102     if command == '-i':
103         #webinjects
104         b.put_item(11021, 0, 4, 4, 0)
105     elif command == '-f':
106         #Webfakes
107         b.put_item(10002, 0, 6, 6, '\x06\x01\xb1\x1d\x09\x00')
108         b.put_item(10011, 0, 4, 4, 3513500427)
109         b.put_item(10003, 0, 4, 4, 167880896)
110     elif command == '-u':
111         b.put_item(11020,0,4,4,0)
112     temp = visenc.visual_encrypt(bytearray(b.get_items()))
113     rc4 = ARC4.new(rc4_key)
114     post_data = rc4.encrypt(temp)
115
116     headers = {'Cache-Control': 'no-cache', 'Accept': '*/*', 'Connection': 'Close','\
117 User-Agent': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/4.0;\
118  SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC \
119 6.0)'}
120     rdata = None
121     try:
122         r = requests.post(c2, headers=headers, data=post_data, timeout=5, verify=Fal\
123 se)
124         if r.status_code == 200:
125             #open('test.bin','wb').write(r.content)
126             rdata = r.content
127     except:
128         print("Failure on request")
129     if rdata != None:
130         rc4 = ARC4.new(rc4_key)
131         decode = rc4.decrypt(rdata)
132         vdecode = visenc.visual_decrypt(bytearray(decode))
133         zeus_binstruct.decoder(vdecode)