Unpacking StrelaStealer
I was digging into a new version of StrelaStealer the other day and I figured it may help someone if I wrote a quick blog post about it. This post is not an in-depth analysis of the packer. It’s just one method of quickly getting to the Strela payload.
Here is the sample I am analysing (SHA256:3b1b5dfb8c3605227c131e388379ad19d2ad6d240e69beb858d5ea50a7d506f9). Before proceeding, make sure to disable ASLR for the Strela executable by setting its DLL characteristics. Ok, let’s dig in.
A quick assessment of the executable in PEStudio reveals a few interesting things that I’ve highlighted. Note the TLS storage (callbacks). When the sample first executes, it makes two TLS callbacks as we’ll see in a bit.
Viewing the strings in PEStudio reveals several large strings with high-entropy data. These strings are part of the packed payload.
Let’s open the file in a disassembler to investigate further. I’ll be using IDA Pro for this analysis. If we inspect the “histogram” at the top of the IDA Pro window, we can see a large olive green segment which indicates data or code that IDA can’t make sense of. IDA Pro calls this data blob unk_14012A010:
As we saw in the strings earlier, this is likely the packed payload. I’ll rename this blob in IDA Pro to obfuscated_payload_blob. If we view the cross-references to this blob (ctrl+x in IDA), we can see several references:
Double-click one of these (I’ll select the 2nd one from the bottom), and you’ll see the following:
It seems our blob is being loaded into register rdx (lea rdx, obfuscated_payload_blob), and a few instructions later there is a call instruction to the function sub_140096BA0. Inspecting the code of this function and you may notice there are quite a few mathematical instructions (such as add and sub), as well as lots of mov instructions and a loop. This all indicates that this is highly likely a deobfuscation routine. Let’s rename this function deobfuscate_data. We won’t be analysing the unpacking code in depth, but if you wished to do so, you should rename the functions you analyse in a similar manner to better help you make sense of the code.
If we then get the cross-references to the deobfuscate_data function, we’ll see similar output to the cross-references for the obfuscated payload blob:
Inspect these more closely and you’ll see that the obfuscated blob is almost always being loaded into a register followed by a call to the deobfuscate_data function. This malware is unpacking its payload in multiple stages.
If we walk backwards to identify the “parent” function of all this decryption code, we should eventually spot a call to a qword address (0x14008978D) followed by a return instruction. This call looks like a good place to put a breakpoint as this is likely the end of the deobfuscation routine (given that there is also a return instruction that will take us back to the main code):
Let’s test this theory by launching the malware in a debugger (I’ll be using x64dbg). When you run the malware, you’ll hit two TLS callbacks (remember I mentioned those earlier?), like the below:
Just run past these. TLS callbacks are normally worth investigating in malware but in this case, we are just trying to unpack the payload here and will not investigate these further. You’ll eventually get to the PE entry point:
Put a breakpoint on the call instruction at 0x14008978D (using the command bp 14008978D) and run the malware. You should break on that call instruction:
If we step into this call instruction, we’ll get to the OEP (original entry point) of the payload! Inspect the Memory Map and you’ll see a new region of memory with protection class ERW (Execute-Read-Write):
This new memory segment (highlighted in gray in the image above) contains our payload. Don’t believe me? Dump it from memory (right-click -> Dump to file) and take a look at the strings. You should see something like the following:
You’ll spot some interesting data like an IP address, a user agent string, registry keys, and so on. If you don’t see any cleartext strings, you likely dumped the payload too early (before the malware deobfuscated all the data in this memory region), or too late, after the malware cleared its memory. Start reading this blog post again and try again ☺
Let’s open this dump file in IDA. After opening the file in IDA, be sure to rebase it (Edit -> Segments -> Rebase Program) to match it to the memory address in x64dbg:
After opening this dumped payload in IDA, you’ll see some inconsistencies, however:
See the problem? Some call instructions are not resolved to function names. However, in x64dbg, these functions are labeled properly:
This is because in x64dbg, these function names are being resolved to addresses in memory. In our IDA dump, they are not mapped properly.
Normally, what I would do next is try get my IDA database as close as possible to the code in x64dbg. We could spend more time analysing the unpacking code to identify where the malware is resolving its imports and this may help us get a better dump of the payload. Or, we could automate this by writing a python script to export all function names from x64dbg and import them into IDA. But why spend 1 hour automating something when we can spend 2 hours doing it manually? 🙂
We can manually fix this up IDA by cross-referencing each unknown function with the function name in x64dbg. For example, at address 0x1B1042 there is a call to InternetOpenA (according to our x64dbg output) and address at 0x1B107B is a call to InternetConnectA.
And now, we have something a lot more readable in IDA:
After you spend a bit of time manually renaming the unknown functions in your IDA database file, you should have some fairly readable code. Congrats! You unpacked Strela’s payload. Spend some time analysing the payload and see what you can learn about this sample.
Happy reversing! 🙂
— d4rksystem