One of the far more simpler techniques I’ve seen from malware as of recently, many from Hackforums would do something like this in .NET language, but seems to still be slightly effective with AV detection (although, when I analysed it the detection rate was very high). The particular variant to come out of this unpacking was ‘Shade Ransomware’, which has been a prominent strand from around 2014. Shade ransomware is spread like many other ransomware through email attachments and exploiting vulnerabilities from a clients machine. When a user is infected, you will see your desktop background replaced with a set of instructions. These are in what I assume are in Russian and English. You will provided a unique identifier for which you must send to an email address to be able to recieve help for decryption of your important files. The actual ransomware will not be analysed in this post, although possibly a follow up post will show us more about the ransomware internals. For this post, I am concentrating on the intial infection, in how it unpacks rather easily. The hashes and links to analysis will be available at the bottom of the blog post.
The binary makes the most out of trying not to look suspicous, adding random bitmaps and ‘accelerators’ to make it seem legitimate. I’m unsure on how useful actually implementing these into binary it is, every binary I analyse with ‘accelerators’ or random bitmaps appended to it are detected quite well. Most bitmaps I see from malicious binaries have randomised bitmaps, this binary I analysed is no exception, I presume this is down to the possibility of images being hashed from binaries to flag malicious intent. A large amount of free icons actually add detections to binaries if used I have noticed. ‘Icon packs’ and are other image resources are regularly shared between hackers to ensure the lowest of detections.
As we can see the resources holds officially some bitmaps, icons, accelerators and icon groups but no base64 string which we will be analysing. It is quite evident though after using a hex editor on the binary that this is indeed packed. The binary is 1.1MB and the base64 string is 804kb in size, it is quite hard to miss. The portable executable identifiers I use have rightly found that the binary has been compiled in Visual Studio, but none have been able to identify that a packer has been used, presumably due to it being homebrew and no signatures of packers within it. This large blob of data can be identified as base64 by the padding it uses (‘==’), the pattern of uppercase and lowercase characters and the constant use of ‘/’ within it. With such a large amount of base64 data I assumed correctly that this would be the main thing to analyse.
Looking into the binary its evident there are some procedures to put off the malware analyst, some calls that are basically useless which I have avoided showing here. The first interesting thing to note is the XOR esi, esi operation which clears the register, this is then used to make sure a variable is first set to 0 and then set to a number which is later used by LocalAlloc to set space up for the base64 data. There is also a variable set which essentially is boolean, either true or false, 0 or 1. I’ve named it a flag which allows later execution of code dependent on the condition of a counter reaching to a certain point. This is it’s intialisation. At the bottom of the picture we can see the comparison of the counter, to which a rather large number is compared.
The first execution of this loop allows the loading of kernel32.dll, in which the address of the module is returned. There are many different ways this could of been done, a more inventive way I’ve seen is from H1N1 loader which actually states it doesn’t have any imports in its binary, to which it knows kernel32.dll will be loaded by default (by convention). Later kernel32 functions will be used to allocate memory and set as executable, this essentially only uses two functions LocalAlloc and VirtualProtect (discluding getting the address for these functions GetProcAddress()), the rest is done by replacing where execution is set and reading from memory the instructions.
After certain conditions are met, we will GetProcAddress where we wish to prepare to set memory for later use. We can see that the flag is set to 0 in this subroutine, the call for localalloc is set and we can clearly see that the length of memory is being moved in for the push to the arguments for the call. (Note: this was first set as var4 and was renamed as ‘base64’ half way through, here to just avoid any confusion you may get).
After allocating the memory it is set to we will then move to decoding our large blob of data into instructions to which we have. This memory address will be later used for the call which has the unpacked program. At this point before the call we have our EDI register set to where the base64 data will go, the memory location is filled with 00’s and is ready to be manipulated. At first analysing the function for base64 decoding I thought it was more complicated but after a comparison of base64 decoded data I realised it simply was, just base64 decoding into a memory location.
After stepping over the call we can clearly see the memory location is not empty anymore, it is filled up with various values from base64 decoded data from within the binary. We are not done just yet, we also have to XOR the data before we can call it correctly, the instructions at this point will not provide anything meaningful for us, at least for the ransomware author that is.
Before we XOR the data we have put into a memory location, we first provide some extra permissions for our memory entry, this is because we wish to execute this part of memory. At the moment we simply have “RW” permissions, we are going to add “X” or execute permissions so that we can correctly execute the code in the call. The ‘push 40’ instruction allows us to confirm this, 0x40 is the value for read, write, execute permissions.
After we step over this call we can see we now have a RWX section, if we look at it, we can confirm it is the code we are looking at in our dump (although this is obviously not needed, we can read the locations!)
Then it is on to the XOR operation. We hold a value that is set earlier on in the binary, it is modified in this loop for each byte it comes across, this means the values are added and multiplied in each round. The EAX register is utilised for this, to which it is stored back into the DWORD for the next round while EAX is used to for the shift-rotate instruction once again. The 16 bit register AL is then utilised for the all important XOR. What’s most important here is that the values are very much predictable and are held within the binary, there is no real magic here aside from the initial value being set earlier on, probably to put more time on an analyst. There is a comparison to ensure that all of the bytes have been correctly XOR’d, ESI is essentially the counter in this, to which it increments and is used as an index for the XOR operation.
We can see that all bytes have been modified into another value. It is apparent the start has a lot of ‘CC’, int 3 which is something to take note of. This is the final form of the packed data, the instructions will be executed from the call.
I compared XOR and base64’d data I had dissassembled in CFF explorer from the ones I see in the debugger, they were identical. I didn’t really need to do this, but I felt it was a good idea to confirm I hadn’t done anything wrong.
The call will bring us to the unpacked form, if you look at Malwr or Hyrbid analysis and look at the behaviour analysis. You will see the use of ‘MessageBox’ within it. ‘User32’ is also imported, this is a rather rudimentary form of string obfuscation.
Hash SHA256: e68fa88129860e227664b2e7a69ca69d601c5095e61f100b2f861a55b0e7d969
Hybrid Analysis: https://www.hybrid-analysis.com/sample/e68fa88129860e227664b2e7a69ca69d601c5095e61f100b2f861a55b0e7d969?environmentId=100
Thanks for reading.