Chapter 18. Packers and Unpacking
Your goal for the labs in this chapter is simply to unpack the code for further analysis. For each lab, you should try to unpack the code so that other static analysis techniques can be used. While you may be able to find an automated unpacker that will work with some of these labs, automated unpackers won’t help you learn the skills you need when you encounter custom packers. Each lab is a packed version of a lab from a previous chapter. Your task in each case is to unpack the lab and identify the chapter in which it appeared. The files are Lab18-01.exe through Lab18-05.exe.
- Packers change an executable into an executable that stores the modified file (e.g. compressed, encrypted, and/or encoded etc) as data, and uses an ‘unpacking stub’ that is run by the operating system to unpack this data.
- Packers can transform all data in an exe, or only the code and data sections.
- Import information is stored in different formats after packing, and to properly RE a packed executable you generally need to reconstruct the import section.
- Unpacks the original executable (generally stored in extra sections of the file) into memory.
- Resolves all original imports.
- Transfers execution from the unpacking stub to the original entry point (OEP).
Loading the Executable:
- Similar to unpacked in that it formats the PE header so that any loader will successfully allocate space for the executable sections in memory.
- Unpacking stub unpacks code and copies it into the sections of memory which were allocated. The method of how this is done is generally within the stub.
- Most common approach is for packer to use ‘LoadLibrary’ and ‘GetProcAddress’ to get addresses for imported functions.
- Simplest approach is to keep import table intact; however, this defeats the purpose of obfuscating the imported functions.
- Another approach is to keep only one function of each imported library to keep table intact.
- Another approach which is useful nowadays is to remove all imports entirely; however, this makes the unpacking stub complicated.
- After unpacking stub finishes, execution must gopt to the original entry point (OEP).
- Instruction that does this is called the ‘Tail Jump’.
- Packers sometimes try to obscure this jump with a ‘ret’ or ‘call’ instruction. Sometimes the instead use OS functions such as ‘NtContinue’ or ‘ZwContinue’.
Notes on unpacking:
- Unpacked program still differs from the original program as it conains the unpacking stub and other code added by the packer.
- PE header is reconstructed by unpacker/in memory so won’t be identical to the original.
Indicators of a Packed Program:
- Minimal number of imported functions.
- Only a small amount (if any) of code is recognised by disassembler ‘auto analysis’.
- OllyDbg and a number of other debuggers identify what looks to be an unpacking stub and provide a warning.
- Program contains sections with names known to be used by packers e.g. UPX0.
- Program contains unusual section sizes such as a ‘Raw Data’ size of 0, but ‘Virtual Size’ much larger.
- Programs such as PEiD are also designed to identify common packers.
- High entropy as identified in previous chapters (when looking at encrypted or Base64-encoded data) may indicate a binary is packed and/or encrypted if it occurs on the binary sections itself.
- Best method if there’s an unpacker available.
- PE Explorer comes with a number of plugins and default unpackers embedded.
- Automatic dynamic unpackers run the malicious code whereas automatic static unpackers don’t.
- One common approach is to figure out the packing algorithm and develop a script to run it in reverse.
- Another common approach is to run the packed program so that the unpacking stub does its job. At this point you can dump the exe from memory and fix up any PE Header required.
OllyDump can find the OEP of a packed executable to make unpacking easier, just use the below without changing any settings to ensure everything is reconstructed correctly:
Plugins > OllyDump > Find OEP by Section Hop Plugins > OllyDump > Dump Debugged Process
Where this fails more manual steps will be required.
Rebuild Import Table with Import Reconstructor:
- Import Reconstructor (ImpRec) can be used to repair the import table for packed programs.
- Select the relative virtual address (RVA) of OEP in OEP after selecting running packed exe (Base - OEP address).
- Use IAT autosearch button and select ‘GetImports’.
- Run malicious program in debugger and use single steps + breakpoints.
- Most common tool is OllyDbg plug-in ‘OllyDump’ using ‘Find OEP by Section Hop’.
- Malware packers include call functions without a return to break debuggers and confuse analysts.
- Use both ‘Step-Into’ and ‘Step-Over’ methods.
- To find the OEP manually, you need to find the Tail Jump.
- This is often the last instruction before padding bytes which are invalid instructions in a disassembler.
- This jump generally goes to a location far away.
- Trailing 0x00 bytes after a jump are an indication it is the Tail Jump.
- Tail jumps often go to an address without valid instructions and is coloured red in IDA.
- OllyDbg attempts to debug 0x00 as ADD BYTE PTR DS:[EAX],AL.
- Most functions begin with a ‘Push’ instruction.
- Using a hardware ‘read’ breakpoint at the memory address of data which is ‘pushed’ can help to find the Tail Jump.
- Adding breakpoints after every loop can help to find the ‘Tail Jump’.
- Setting a breakpoint at ‘GetProcAddress’ if it is used by the packer helps to find the Tail Jump.
- Can set a breakpoint at a function known to be called by the program and work backwards to find Tail Jump. GetModuleHandleA is common for this. Sometimes dumping at the function which called this gets you the OEP.
- Can use ‘Run Trace’ in OllyDbg to find the OEP. This is always within the original .text section, so breaking on an instruction executing within .text can help to find OEP.
Repairing Import Table Manually:
- Import Table consists of 2 tables in memory.
- Names or Ordinals used by the loader/unpacking stub.
- List of addresses to functions that are imported.
- Need to repair imports manually by renaming calls to their corresponding imported function.
- Can be identified by looking for indirect calls to addresses outside of the loaded executable start/end ranges.
- Can use OllyDbg to jump to where IDA DWORD pointers are referencing to get the original function names.
Common Packers and their Attributes:
- Most common packer. Can use the same UPX program with the ‘-d’ parameter to decompress the packed executable.
- Malware can also be packed with modified versions of UPX or different packers where ‘UPX -d’ fails to unpack it (yet they appear to be packed with UPX).
- Often contains anti-debugging exceptions and obfuscated code.
- Unpacking largely involves debugging to find the Tail Jump which is often a ‘jmp eax’ followed by many 0x00 bytes.
- Uses self modifying code to make unpacking difficult and often terminates prematurely if it is being debugged.
- Is so popular that many automated unpackers now exist with varying levels of success depending on the version.
- Unpacking stub contains a PUSHAD instruction. Set a hardware break on a ‘read’ for the stack addresses storing these registers to easily find OEP.
- Similar to ASPack; however, also uses ‘single-step’ instructions to break the debugger, pass ‘single-step’ exceptions to the program to bypass.
- Use a similar method as ASPack with a hardware ‘read’ break on the relevant stack addresses.
- Keeps one import from each library in the import table.
- Generally hides the Tail Jump within the unpacking stub to make it difficult to find.
- Push instructions followed by a return instruction are very common for this packer.
- Best method for finding OEP is to set a breakpoint at ‘GetProcAddress’ and then ‘single-step’ searching for loops that repair the import table.
- Another method is to break at ‘GetModuleHandleA’ or ‘GetCommandLineA’ and work backwards.
- One of the most complicated and secure packers at the time of publication.
- Contains many Anti-VM, and Anti-Debugging components, in addition to kernel code.
- Code runs even after unpacking and the original program is running.
- One of the best approaches is to dump the program from memory if automated unpackers don’t work
Common obfuscators/packers resolved by de4dot - Note: This is .NET (e.g. malware written in C#) PE files only.
- Agile.NET (aka CliSecure)
- DeepSea Obfuscator
- .NET Reactor
Analysis Without Fully Unpacking:
- If you’re unable to fully unpack a program either due to the inability to repair the import table or PE header you can still use a disassembler such as IDA and use strings analysis.
- Some malware unpacks only part of a code as it is about to run to make analysis more difficult.
- If you can unpack part of the code, you can often create a script to unpack it all, or instead focus more on dynamic analysis.
- More complicated than packing executables so not all packers support it.
- Packer must account for not only the imports, but also the exports.
- OEP of a DLL is the original start of the DllMain function.
- OllyDbg has loadDll.exe to help load and debug Dlls.
- Debuggers will break at DllMain; however, this is after the unpacking stub has occurred, so to find this change the bit at ‘0x2000’ in the PE header from 1 to 0 to have it treated as an executable when unpacking.
Starting out we can do a multi-scan using PEiD to see if we can fingerprint what packer each executable is using.
From this we have determined the following which will be used as a guide for the rest of the lab:
- Lab18-01.exe: No Packer Detected
- Lab18-02.exe: FSG 1.0 Detected
- Lab18-03.exe: PECompact 1.68 - 1.84 Detected
- Lab18-04.exe: ASPack 2.12 Detected
- Lab18-05.exe: UPack 0.39 beta Detected
This is examining Lab 18-01.exe.
From our search with PEiD we begin to believe no packer has been used; however, given the context of this chapter and the malware in question this surely isn’t the case. To help find out whether a packer is in use we can open this in pestudio and examine the sections of this file to see if there’s any known section names used by a packer.
From the above we see a section which is known to be used by the packer UPX, in addition to indications that the executable sections are self-modifying and are marked as both ‘writeable’ and ‘executable’. As a first attempt we can try to unpack this using UPX; however, we’re promptly displayed a message that indicates this may have used a custom version of UPX or be further modified to prevent unpacking.
Opening this up in IDA we can see that the exported functions look to exist within this UPX section, in addition to what looks to be a Base64 index string, and a looping function no doubt used to unpack the program.
Examining an instruction at 0x409F43 we can see what looks to be a jump to a faraway address which seemingly doesn’t have any valid code in it right before the end of the ‘start’ function.
As we are currently looking for the Tail Jump to our original entry point, it currently looks like this would be a good candidate. By opening this up in OllyDbg we can see that this has a number of 0x00 trailing bytes which helps us to pinpoint it as the Tail Jump.
By creating a hardware break here for whenever it attempts to execute, we will see it hit our hardware breakpoint. By pressing F7 we will single step to what is the OEP of this program. From here we can use OllyDump to dump our debugged process.
Opening the dumped program in IDA, we can immediately see a lot more imported functions and a familiar use of imports looking for ‘GetCurrentHwProfileA’.
From this we can infer that this was a packed version of Lab14-01.exe as we identified how these components were being used in Lab 14-01 question. This proves we’ve successfully found the Tail Jump and unpacked this executable.
Bonus PE Identification using ‘DIE’:
Other tools exist to determine what packer may have been used and information about a file including ‘Detect-It-Easy (DIE)’
- DIE can easily be expanded on and has support for many file formats which have been contributed by the infosec community.
In this example if we use DIE to look at the file we can see it is packed using a modified version of UPX despite it not being identified using PEiD.
This is examining Lab 18-02.exe.
From our search with PEiD we begin to believe FSG 1.0 is the packer used on this executable. Opening in IDA we can see that this packer looks to be leveraging the ‘GetProcAddress’ Windows API.
First off we will attempt to unpack this using an automated unpacker. In this case we can use PEiD’s Generic Unpacker plugin to try and unpack the executable.
By attempting to automatically find the OEP we are presented with a message saying it was unable to find the OEP.
Based on this we need to look through other ways of unpacking it. By opening this using OllyDbg we can use the OllyDump plugin to find the OEP via Section Hop (Trace Over) method.
This immediately pauses execution of the program as it’s detected that EIP is outside of a specified range which may mean it is the Tail Jump.
What’s interesting here is that it looks to not have been disassembled as code. By pressing CTRL + A we can force OllyDbg to analyse and disassemble these bytes resulting in a different set of instructions.
Because we can see what looks to be valid function calls and a known string from the program, it looks like this isn’t inside of the unpacking stub, but rather is part of the unpacked program so may once again be our OEP. BY dumping this debugged process once again using OllyDump, and opening it in IDA we get what looks to be a successfully unpacked program.
At a glance we can see that this is the same as what we saw with Lab07-02.exe indicating we have successfully found the OEP and unpacked the program.
This is examining Lab 18-03.exe.
From our search with PEiD we begin to believe ‘PECompact 1.68 - 1.84’ is the packer used on this executable. Once again we attempt to unpack this and find the OEP using plugins for PEiD.
Both of these plugins have identified the OEP as 0x401577 which is promising. Attempting to unpack this using PEiD’s Generic Unpacker plugin we are prompted to rebuilt the imports with ImpREC. By proceeding to do this we now have a new file called Lab18-03.exe.unpacked_.exe.
A quick analysis of this in IDA reveals the string ‘1qaz2wsx3edc’ which we remember from Lab09-02.exe.
In this instance it looks like the automated unpacking method worked and successfully rebuilt the imports of this program.
To manually identify the above OEP had automated unpacking not worked, we have to examine this in OllyDbg once again. First we will attempt to use the OllyDump plugins ‘Find OEP By Section Hop (Trace into)’.
As we can see this has taken us to ‘0x40A110’, which isn’t the OEP we found previously using automated methods. Further although 0x40A111 looks to be a null byte (0x00) there doesn’t look to be many of these indicating this may not be the Tail Jump. If we then try ‘Find OEP By Section Hop (Trace over)’, we wind up at another address which doesn’t look to be the OEP.
What is interesting about this is that it follows a number of null byte instructions. Given this plugin doesn’t seem to have worked we can scroll up to follow any leads on jumps or returns prior to a number of null byte instructions.
Although in the above we can see a jump to 0x40754E right before some null byte instructions, we can also see that there are some calls to POPAD and POPFD. Although none of this looks to be related to the necessary Tail Jump, it does lead us to another avenue we can explore, and that’s seeing if any data is pushed to the stack prior to the unpacking stub executing. Using OllyDbg we can restart the program and search for any PUSHAD instructions.
This reveals a number of entries; however, one really stands out as it isn’t showing the PUSHAD instruction and instead shows a comment of (Initial CPU Selection) indicating it may be part of modifying code.
By examining 0x405130 further we can see that straight after PUSHAD and PUSHFD execute there is a call to a routine which indicates this may be the start of the unpacking stub given how early it executes and the use of the below operators:
- PUSHAD: Push all 8 general purpose registers to the stack.
- PUSHFD: Push the EFLAGS register to the stack.
By using F8 to step over these first instructions to 0x40513A (the call to the identified routine after PUSHAD and PUSHFD has executed), we can then set a hardware breakpoint to catch whenever these registers are popped which will likely be right before the OEP. By right clicking ESP and selecting ‘Follow in Dump’.
We can then create a Hardware break on if this is accessed.
By running the program with F9, we hit a breakpoint right before a jump at 0x407551 which looks to be our Tail Jump.
We can see that this is jumping to 0x401577 which is what we previously identified as our OEP. If we step into the instruction which takes us to ‘0x401577’ we can now dump the program using OllyDump. Opening in IDA, although we can see the 2 dumped processes differ slightly in file size due to how they were dumped and import tables rebuilt, we can see that the overall content of the program is very much the same.
This is examining Lab 18-04.exe.
From our search with PEiD we begin to believe ‘ASPack 2.12’ is the packer used on this executable. Using our automated methods above proves invaluable; however, examining this in IDA reveals it is performing a PUSHAD at 0x411001 similar to the previous executable.
Using this as our anchor we will repeat the same process as Lab18-02.exe to manually find the OEP. Running in OllyDbg we will step to ‘0x411002’ aftert PUSHAD has been run, then follow ESP to our Dump, before creating a hardware breakpoint again.
By running the program we hit a POPAD instruction right before a jump. This looks to be right before a RETN and as such a Tail Jump at 0x4113BF.
After taking this jump we arrive at some data at ‘0x403896’ which looks familiar to other data we’ve initially seen when arriving from a Tail Jump given OllyDbg hasn’t analysed this correctly.
By using CTRL + A to analyse this, we can see error handler installations in additon to a call to GetVersion and GetCommandLineA which helps us validate that was likely the Tail Jump.
Dumping this with OllyDump we can then open it in IDA and see some familiar command-line options present in Lab09-01.exe which helps us confirm we have successfully found the OEP and associated Tail Jump.
This is examining Lab 18-05.exe.
From our search with PEiD we begin to believe ‘UPack 0.39 beta’ is the packer used on this executable. We can attempt to automatically unpack this by leveraging PE Explorer and its ‘Upack Unpacker Plug-In’. Upon opening this in PE Explorer we can see it has identified that it is packed and attempted to unpack it.
From the above we can also see it has identified the OEP as 0x401190 and has managed to partially restore the file and import tables. By saving this executable to a new file we have a more or less complete file which we can see is related to Lab07-01.exe due to it searching for the service ‘MalService’.
If we open this in IDA we get messages indicating that it has unaligned section pointers and a virtual address error.
By continuing with analysis we can see that it uses ‘GetProcAddress’ which is typical for this packer to rebuild the import table.
Opening this in OllyDbg we are presented with an error message which indicates that the PE Header of this has been tampered with.
As a starting point we will look for calls to ‘GetProcAddress’ by using CTRL + G and putting in ‘GetProcAddress’. At this point we can create a breakpoint with F2.
Running the program we hit our breakpoint and can see that the first API imported by Lab18-05.exe is ‘CreateWaitableTimerA’.
If we continue to press F9 the unpacker will continue to find the necessary API in the original executable as it repairs the import table. Eventually we find it will get the address for ‘InternetOpenUrlA’.
After this an exception handler error is hit which we can pass to the program; however, given the error it may be a good spot to examine the program at 0x408EB4 which is where the calls to ‘GetProcAddress’ occur.
At this point we find a loop occurs which may be involved with repairing the import table. This is repeated a number of times as the loop goes through every API imported by the program. As we want to analyse right before the exception occurs we will use F9 to run the program up until we hit ‘InternetOpenUrlA’ and then step through manually using F8 until we reach 0x408EA5 where a jump should be taken to 0x408E91.
At 0x408E96 we can see another jump occurring which may be the Tail Jump given we have just gone through repairing the import table aspect of unpacking; however, if we examine where this is jumping to we see 0x408EB7 is just a call to ‘RETN’ so can’t be the start of this executable.
By using F7 to step into this we find that this ‘RETN’ takes us to a sight for sore eyes.
What we can now see is that the RETN was in fact our Tail Jump and we are at what looks to be the OEP. This is the same location found through our automated unpacking (0x401190). By dumping the program we may experience some errors or even OllyDbg crashing; however, we can still open this in IDA and see it is essentially the same as we unpacked using PE Explorer.
Once again we can see this is a packed version of Lab07-01.exe.
This concludes chapter 18, proceed to the next chapter.