Reverse engineering malware: TrickBot (part 2 - loader)
In my previous post, I explained how to unpack the TrickBot loader.
In this one, I will explain how to dump both versions of the core (x86 and x64). The dumping part in itself is not so interesting as are the ways you can make IDA help you easily understand what’s going on.
After dropping the binary in IDA and poking around a bit, you see a bunch of functions and hidden imports, which doesn’t help much:
This doesn’t really mean anything at this point, other than a bunch of random symbols. So, let’s get some runtime / dynamic information.
If you run the binary, you’ll see something particularly interesting:
It is the first called function, and some parameters are passed to it. If you step over it and check the values of those addresses, you’ll quickly see that, in the first parameter, there’s a number that looks like an offset:
IDA also marked is as a function pointer, so if you put the cursor over it and press O (to mark as an offset), you get:
0x7XXXXXXX offset and the type information hints from IDA were right: it’s a function pointer, pointing to a Windows
API call. This means that the function probably initializes the imports table.
However, after initializing the imports, they still look ugly in the decompiler:
Of course, you could go and rename each API call manually, but really, you don’t want to do that. You have the start of the imports table, and if you look at the second parameter of the previously mentioned IAT-loading function, you see:
.rdata:00403228 dword_403228 dd 200h ; DATA XREF: start+15↑o
Indeed, that’s the size of the imports table, but, news flash: you don’t even need to know it!
Prettifying the imports
dword_xxxxxxxx(arg) does not help, but IDA already knows about these tricks, and is ready to help. To fix the ugly imports, all you have to
do is find the start of the imports table, select it1 and then let IDA know about them.
To do this, go to the previously mentioned offset (start of imports table), put the cursor there, and press Alt + L to start a new selection: that is, you’re telling IDA that, no matter if you lose the focus of the window or click somewhere, it should still keep the selection. This is very useful for when you have to select a big chunk of memory, such as an imports table:
Then, as shown in the previous GIF, scroll down until you find the end, which you can find intuitively: when you stop seeing
0x7XXXXXXX offsets, then you’ve found
the end of the imports table. It’s as easy as it sounds. And if you have multiple import tables (or a single import table is split over multiple regions), you can repeat the process
with each chunk.
Now that you have the table selected, it’s time to rename these offsets. Luckily, IDA already ships with an IDC script designed exactly for this purpose:
To use it, press Alt + F7, then navigate to the script, select it, press Alt + L again (to cancel the selection), and all the imports
will be renamed:
Now that we have the imports renamed, the binary looks much better:
Compare that to the old view:
Now that the binary is much easier to understand, we can quickly reverse some functions (without going too deep into it) and get a global overview of what is the binary doing:
As mentioned in the title, this is just a loader that moves itself to a temporary directory, checks OS information (32-bit or 64-bit), and loads the appropiate binary. As you
can see in the image, I’ve highlighted the variable that is used to decide which resource to load and decode, so in order to dump it, all you have to do is put a breakpoint
DecodeStream and dump
len bytes from
buf, then run the binary again, invert the
if ( os_version ) check, and dump
len bytes from
buf, again, to have both
➜ ~ file * trickbot_core32: PE32 executable (GUI) Intel 80386, for MS Windows trickbot_core64: PE32+ executable (GUI) x86-64, for MS Windows
A quick check of the 32-bit2 binary in IDA looks great:
If you’ve ever reversed TrickBot, you can quickly tell that this, indeed, is TrickBot, because of the
CoInitialize* calls. Interestingly, IDA detects
wWinMain in the
32-bit version and jumps there as soon as you load the binary, while the 64-bit version sets you back to the real entry point,
start, because the actual
main (user entry point)
function couldn’t be found automatically. That, however, is analysis of the malware itself, and will happen in the next part.
To be continued…