Tuesday, July 9, 2013

Dealing with UPX Packed Executables

I know I said my next post was going to be about decoding obfuscated Javascript using some automation. Well, that post IS coming, but @undeadsecurity challenged me to write a post on dealing with packers by 12pm CET on 07/09/2013. For reference, that is approximately four hours from now (when I started writing this post). So yeah, this post was written practically on a dare. Challenge accepted. Let's do this!

First, let's talk a little bit about packers and what they're designed to do. In a small nutshell, packers are used to scramble or encrypt the original executable to make it more difficult for the analyst/reverser to figure out. When a packed executable is run, the exe first unpacks itself, and then loads the unpacked code into RAM. Probably the most common packer we see being used with malware is UPX, so that's what I'll be focusing on with this post. I'll be showing several examples using both OllyDbg and Immunity Debugger. I prefer Immunity for most things, but OllyDbg can be useful for the large number of plugins that have been developed. We're going to cover the following items:


  • Identifying Packed Executables
  • Unpacking UPX Packed Executables
  • Maybe some extra material (shh, it's a secret, keep reading)


Identifying Packed Executables



So, how do we identify if a piece of malware is packed or not? There are several ways. One way is to use Strings from SysInternals, or the *nix native "strings" command. Generally speaking, an executable that has been packed won't have much in the way of readable strings. Let's take a quick look at string output from a packed executable. Later on, we're going to see what the strings output looks like from the unpacked version.

The sample I'm working with here is a VERY old slackbot trojan, but I think it captures the basics of packers (and how to unpack them) quite well. Some quick data on my sample:

Malware Type: Slackbot v1.0
File hash: 365E5DF06D50FAA4A1229CDCEF0EA9BF
VT Info: https://www.virustotal.com/en/file/239b1f39200d280f705acad7d12a65cb28ddaa9a598c7937e03156022c8b1a8f/analysis/

So, where do we start? Let's take our original sample and see what kind of strings it gives us... I'm using Strings from SysInternals (http://technet.microsoft.com/en-us/sysinternals) to get this output.


You'll notice that it actually says "UPX" multiple times in the first few readable strings. This is pretty common of UPX packed executables. The remainder of the strings output doesn't give us much, other than some Windows API hooks (which are useless as far as we're concerned at this point):


That's about it for readable strings. Ignoring the fact that UPX is clearly visible in the strings, the general lack of readable text is a good indicator that the executable is packed. However, strings output is not the only method for detecting packed executables. PEiD is another great tool that is designed to collect various pieces of information about a given PE file.

Running our executable through PEiD, it's clearly identified as being packed with UPX:


There are other ways to detect packers, but PEiD and strings output are, in my opinion, the most reliable (VirusTotal uses PEiD to detect packers, for reference.. check out the "File Detail" tab).

Now that we've identified the packed executable, I want to talk a little bit about what effects packers have on reverse engineering.

The Effect of Packers on Reversing



If you've ever loaded a packed exe into a hex editor or into something like OllyDbg or IDA, you've probably noticed that it looks a lot different from a normal, unpacked exe. Probably one of the most noticeable effects a packer has on a PE file is that it destroys the import table, masks, encrypts, or obfuscates the PE header, and makes the original entry point (OEP) hard to find. The OEP of a file marks the first instruction that is executed by the operating system when a file is executed. Here's what a packed EXE looks like in a hex editor (Note: I'm using a hex editor here because unpacked PE files have a clearly visible PE header, which I'm going to show below):


Yes, we can still see the MZ file signature and the "description" block of text... but look toward the bottom. Normally we'd expect to see section headers like .data, .text. bss, etc. Clearly those aren't visible, but you can see that the section headers are labeled UPX0, UPX1, UPX2, etc. This is yet another indicator that the file is packed -- that is, the normal section headers aren't visible.

I already know what the OEP is for this program, but let's ignore that for the moment and take a look at the packed exe in Olly:



This is just one small part of the full exe, obviously, but you'll get a lot of stuff that looks like this (and subsequently - isn't very useful) if you load a packed exe into a debugger.

Alright, so we know what a packed exe is designed to do, what they look like, and how to identify them. What next? Let's talk about unpacking the executable. I'm going to cover a few different ways, but we're going to start with the "easy" method - automated unpacking.

Unpacking UPX Packed Exectuables



When it comes to UPX packed executables, it's pretty easy to unpack them using automation. The UPX program itself will do it via the "upx -d <filename>" command, but I'm not going to talk about that here in favor of showing other methods. One program in particular that is particularly good at unpacking UPX packed files is PE Explorer. It's not free, but you can get a 30 day trial of it from the developer here: http://www.heaventools.com/overview.htm

PE Explorer has a built-in unpacking plugin for UPX, NsPack, and Upack packers. More unpacking plugins are available online, just do a google search for "<packername> PE Explorer plugin" and you're bound to find something.

So, launch PE Explorer and go to Tools > Plugin Manager:


In the plugin manager window, you'll want to set the priority for UPX Unpacker Plugin to something higher than 1. Just enter in the number in the "Set priority" text box on the right. Once you've done that, click Close. From there, we just need to open our UPX packed file in PE Explorer. If you look at the log window at the bottom, you can scroll up a bit to see that the UPX unpacker plugin is executing to unpack the exe. The cool part about PE Explorer is that it also rebuilds the import table (which is something that not all methods will do automatically):


From there, all you have to do is choose File > Save File As.. and pick a filename. I've saved this unpacked exe as tnnbtib_unpacked.exe and opened it in a hex editor so you can see the difference in the section headers, seen here:


See how we can now see the section headers for .text, .bss, .data, and .idata? This is a clue that our exe has been successfully unpacked. Strings output also changes dramatically, seen here:



You can see a lot more useful information from the strings output of an unpacked exe. There's enough information here to not only conclude that this is an IRC-controlled trojan, but also that it's a version of Slackbot. I already identified this sample at the start of this post, but I wanted to show you where the information came from. :)

Now that we've covered how to unpack executables using automated methods, let's jump head first into the deep end and talk about getting the unpacked executable using static analysis. Remember that OllyDbg screenshot above? Well, we're going to go back into Olly for this first part.

First thing's first - open the packed exe in OllyDbg. From there, you're going to want to look for a large number of repeated "DB 00" instructions. This is usually going to be near the end of the main module. 

Note: When you first open a file in Olly or Immunity, it won't show you the main module by default. You'll probably end up seeing offsets in the 77xxxxxx range. If this is the case, right click on the disassembly window > View > "Module <filename>" (here, it's "Module 'tnnbtib'").

In the case of this exe, this block of repeating instructions starts at offset 4088B4 (which is near the end of the UPX1 section if you want to try to get here via the memory window) seen below using Olly (Note that Olly and Immunity both tell you the current thread and module name in the title bar of the program - this can be helpful in identifying what you're looking at):


Note: This set of repeated instructions is typical of UPX packed executables, so your process will be different for other packers (I may do a writeup on them in the future, but being that UPX is the most common, I found this quite fitting). 

Here we can see the final instruction that occurs before the repeating instructions is: JMP 004011CB

This tells us a few things. First, we now know that the OEP of the program lies at offset 4011CB, and we also have our "key" to getting this exectuable to unpack itself for us to extract from memory.

So, what do we do with this? First, we need to set a break point on the JMP instruction that occurs right before this block of DB 00 Instructions. You can do this in Olly or Immunity by selecting the instruction and pressing F2. I'm doing it in Immunity here, but the process remains the same for OllyDbg:



Again, if you can't see the 0040xxxx range of offsets, then you're not looking at the right module. You can push Ctrl+M or click "m" on the toolbar in both Olly and Immunity to see the "memory" view. This will show you the name of a module, the starting offset, and its size. The module tnnbtb in this case starts at offset 00400000 and has a size of 6000 bytes. To change the module you're currently debugging, right click the upper left pane (the debugger frame) in Olly/Immunity and choose "View" > "Module <filename>" to select which module you want to debug.

From here, whether using Immunity or Olly, you'll want to press F9 so the execution stops at this breakpoint (may have to press it more than once - this sample requires you to press F9 three times). From there, press F8 to get the debugger to execute the JMP instruction and then pause at it's 'jump' location - offset 4011CB:


Pressing F8 has now caused the JMP instruction to execute, and execution has now stopped at offset 4011CB -- this is the OEP for the program. Conveniently, this is also the point where the unpacked exe gets loaded into RAM, so we can dump the exe from memory at this instruction and use that for future analysis. :)

Note: OllyDbg is pretty good at finding the OEP, and you may notice that when you first load an exe into Olly that OEP is already selected. This may not always happen, but it is the case with this sample. You can set a breakpoint on the OEP, run the program, and dump the process from memory at that point if you wish, it'll have the same effect in the end.

There are a couple ways of dumping the exe from memory at this point. One thing to note is that when you acquire an unpacked exe in this manner, the imports table, PE, and section headers will likely not be rebuilt, depending on the particular method you use to dump the program from memory. You'll have to do that manually (which I'll probably cover in another post at some point...). The most common way of dumping an exe from a running process is by using the OllyDump plugin. For the sake of being different, I'm going to talk about a different method: LordPE. LordPE is pretty awesome at this sort of thing - you can find more info about LordPE here: http://www.woodmann.com/collaborative/tools/index.php/LordPE

Going back to our exe that we have sitting at our OEP, we now need to dump this exe from memory. To do this with LordPE, simply open LordPE and scroll down to the process you want to dump. From there, it's just a matter of right click > dump full.


By default, LordPE will save the file as "dumped.exe". Once it's dumped, you can go about fixing the import table, etc. However, at this point you have a fully unpacked executable with readable strings, and can decompile till your heart's content. :) Hope this helps!

Note: If you dumped your process using OllyDump, you may have fixed the import table automatically using the plugin. The Immunity version of OllyDump doesn't give you the choice to not fix imports. If you use LordPE, however, you'll have to fix the import table yourself.

Bonus Material: Using OllyDump and Rebuilding the Import Table with ImpRec



Okay, I know I said I wasn't going to cover this, but I figured I'd show you at least one method of rebuilding the import table, as well as using OllyDump to dump the executable from memory (as opposed to using LordPE).

First, let's touch briefly on using OllyDump. Note that I'm still using Immunity Debugger here, but it's the same when performed in OllyDbg.

Once you have your program execution paused on the OEP, it's simply a matter of choosing Plugins > OllyDump > Make dump of process. You'll see this window when you click it:


Notice the Entry Point is listed at offset 00008760? Well, we know that this isn't correct. Thankfully, OllyDump is smart enough to modify the OEP for us to the proper Relative Value Address (RVA) of 11CB (Note: In order to calculate the RVA of an OEP, simply subtract the Start Address from the OEP. In this case the OEP is 4011CB. The start address is 400000. So 4011CB minus 400000 = 11CB). You can see that OllyDump has an option for fixing the import table. Since we're going to be using Import Reconstructor (more below), be sure to uncheck the Rebuild Import box. If you do this with Immunity's version of OllyDump, you don't have the option of *not* fixing the import table.



Alright, let's fix our import table and be done! I prefer using a program called ImpRec which stands for "Import REConstructor". You can download it from here: http://deioncube.in/files/cw2k/Tools/Import%20REConstructor%20v1.7f.7z

Before launching ImpRec, you'll want to be sure you have dumped your process at the OEP and also be sure you still have the debugged process running in Olly/Immunity. Once you have those things, go ahead and launch ImpRec and select the process you're currently debugging:


Notice how the OEP is still listed at RVA 8760? That's because we're working with the process that's still in memory - not the exe we previously dumped with the corrected OEP. In the OEP box, enter the RVA of the proper entry point - 11CB, then click IAT AutoSearch. If you did it correctly, you should see a message pop up about finding an IAT. Click OK on that box then click Get Imports. Once you do that, click on Fix Dump. Select the previously dumped exe and click Save. This will then add the imports as a new section on the dumped exe from earlier. Here's a quick screenshot to see what the message looks like if everything was successful:


At this point, you're done! Your new, fixed exe is saved with a "_" at the end of the filename (before the extension). Happy reversing!

-SM

No comments:

Post a Comment