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.


Starting analysis

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:

hidden imports

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:

something 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:

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:

function pointer

Effectively, the 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:

  off_404010(0);

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:

big selection

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: ida_root/idc/renimp.idc. 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:

all imports renamed

Now that we have the imports renamed, the binary looks much better:

much better

Compare that to the old view:

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:

overview

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 after 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 binaries:

➜  ~ 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:

32-bit binary

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…


  1. Be careful: select from the exact start of the imports table, not from 1/2/3 bytes before - else IDA won’t recognize it and won’t help. ↩︎

  2. The x64 decompiler produces crappy output for this specific binary, which is why I chose the 32-bit version ↩︎