This section covers just a sampling of popular packers that you are likely to encounter when analyzing malware. For each packer covered, we’ve included a description and a strategy for unpacking manually. Automated unpackers are also listed for some of these, but they do not always work. For each packer, strategies for finding the OEP and potential complications are also included.
The most common packer used for malware is the Ultimate Packer for eXecutables (UPX). UPX is open source, free, and easy to use, and it supports a wide variety of platforms. UPX compresses the executable, and is designed for performance rather than security. UPX is popular because of its high decompression speed, and the small size and low memory requirements of its decompression routine.
UPX was not designed to be difficult to reverse-engineer, and it does not pose much of a
challenge for a malware analyst. Most programs packed with UPX can be unpacked with UPX as well, and
the command line has a -d option that you can use to decompress a
UPX-packed executable.
Because it’s fairly easy to overcome, UPX is a good packer for learning how to manually unpack malware. However, many stealthy malicious programs are designed to appear to be packed with UPX, when they are really packed with another packer or a modified version of UPX. When this is the case, the UPX program will not be able to unpack the executable.
You can find the OEP for UPX by using many of the strategies outlined earlier in this chapter. You can also use the Find OEP by Section Hop feature in OllyDump, or simply page down through the unpacking stub until you see the tail jump. Dumping the file and reconstructing the import table with OllyDump will be successful.
PECompact is a commercial packer designed for speed and performance. A discontinued free student version is still often used by malware authors. Programs packed with this packer can be difficult to unpack, because it includes anti-debugging exceptions and obfuscated code. PECompact has a plug-in framework that allows third-party tools to be incorporated, and malware authors often include third-party tools that make unpacking even more difficult.
Unpacking PECompact manually is largely the same as unpacking UPX. The program generates some exceptions, so you will need to have OllyDbg set to pass exceptions to the program. This was discussed in detail in Chapter 16.
You can find the OEP by looking for the tail jump. Step over a few functions, and you will see
a tail jump consisting of a jmp eax followed by many 0x00
bytes.
ASPack is focused on security, and it employs techniques to make it difficult to unpack programs. ASPack uses self-modifying code, which makes it difficult to set breakpoints and to analyze in general.
Setting a breakpoint can cause programs packed with ASPack to terminate prematurely, but these programs can still be manually unpacked using hardware breakpoints set on the stack address. Additionally, ASPack is so popular that there are many automated unpackers available. Their effectiveness varies, but automated unpacking is always worth trying as a first option.
Although you may successfully unpack an ASPack packed file using automated techniques, most
likely you’ll need to unpack files manually. Begin by opening the code for the unpacking stub.
Early in the code, you will see a PUSHAD instruction. Determine
which stack addresses are used to store the registers, and set a hardware breakpoint on one of those
addresses. Ensure that it is set to break on a read instruction. When the corresponding POPAD instruction is called, the breakpoint will be triggered and you will
be just a few instructions away from the tail jump that leads to the OEP.
Petite is similar to ASPack in a number of ways. Petite also uses anti-debugging mechanisms to make it difficult to determine the OEP, and the Petite code uses single-step exceptions in order to break into the debugger. This can be resolved by passing single-step exceptions to the program, as described in Chapter 16. The best strategy is to use a hardware breakpoint on the stack to find the OEP, as with ASPack. Petite uses a complicated code structure that makes it easy to spot the OEP once you have gotten close because the original code looks normal unlike the Petite wrapper code.
Petite also keeps at least one import from each library in the original import table. Although this does not affect how difficult it is to unpack, you can easily determine which DLLs the malware uses without unpacking it.
WinUpack is a packer with a GUI front end, designed for optimal compression, and not for security. There is a command-line version of this packer called UPack, and there are automated unpackers specific to UPack and WinUpack.
Although security isn’t its focus, WinUpack does include security measures that make it difficult to find the OEP, and render techniques such as searching for the tail jump or using OllyDump useless. Example 18-5 shows the tail jump for this executable.
Example 18-5. Tail jump for a program packed with UPack
010103A6 POP ECX 010103A7 OR ECX,ECX 010103A9 MOV DWORD PTR SS:[EBP+3A8],EAX 010103AF POPAD 010103B0 JNZ SHORT Sample_upac.010103BA 010103B2 MOV EAX,1 010103B7 RETN 0C 010103BA ❷PUSH Sample_upac.01005F85 010103BF ❶RETN 010103C0 MOV EAX,DWORD PTR SS:[EBP+426] 010103C6 LEA ECX,DWORD PTR SS:[EBP+43B] 010103CC PUSH ECX 010103CD PUSH EAX 010103CE CALL DWORD PTR SS:[EBP+F49] 010103D4 MOV DWORD PTR SS:[EBP+555],EAX 010103DA LEA EAX,DWORD PTR SS:[EBP+447] 010103E0 PUSH EAX 010103E1 CALL DWORD PTR SS:[EBP+F51] 010103E7 MOV DWORD PTR SS:[EBP+42A],EAX
In this listing, the tail jump at ❶ is in the
middle of the unpacking stub, so it is difficult to spot. A push
instruction at ❷ followed by a return instruction is extremely common for a tail jump. The code jumps all around before
arriving at the tail jump in order to make it harder to spot. To further obscure the tail jump, the
push that precedes the retn instruction is modified by the packer
shortly before it is called. The jump is also not very far, so you can’t identify it by
searching for long jumps. Because the OEP is in the same section as the unpacking stub, OllyDump
cannot automatically identify the tail jump via its section-hopping method.
The best strategy for finding the OEP for a program packed with UPack is to set a breakpoint
on GetProcAddress, and then single-step carefully over
instructions looking for the loops that set the import resolution. If you set the breakpoints at
every jmp or call instruction,
you will be single-stepping forever, but if you set the breakpoints too sparsely, the program will
probably miss your breakpoints and run until completion.
Do not be discouraged if the program runs to completion without hitting your breakpoints.
Simply restart the application in the debugger and try again. Making mistakes is a part of the
process. Eventually, you will single-step onto a ret instruction
that is the tail jump.
Sometimes, recognizing the tail jump can be tricky. In this case, it jumps about 0x4000 bytes away. Most unpacking stubs are much smaller than 0x4000, and a jump of that size usually is a jump to the OEP. A good way to double-check is to examine the code around the OEP, which should look more like ordinary code compared to the unpacking stub. The unpacking stub often has many conditional jumps and returns in the middle of a function, but the code around the OEP should not have these unusual elements.
Another strategy that works on UPack is to set a breakpoint on GetModuleHandleA for GUI programs or GetCommandLineA
for command-line programs. In Windows, these functions are called shortly after the OEP. Once the
breakpoint is triggered, search backward through the code to find the OEP.
Sometimes WinUpack crashes OllyDbg by using a PE header that OllyDbg parses incorrectly. In Chapter 16, we showed that OllyDbg isn’t perfect and has issues parsing binaries that run just fine on Windows outside the debugger. If you encounter this problem, always try to use WinDbg before attempting to decipher PE header errors.
Themida is a very complicated packer with many features. Most of the features are anti-debugging and anti-analysis, which make it a very secure packer that’s difficult to unpack and analyze.
Themida contains features that prevent analysis with VMware, debuggers, and Process Monitor (procmon). Themida also has a kernel component, which makes it much more difficult to analyze. Code running in the kernel has very few restrictions, and analysis code generally runs in user space, and is therefore subject to more restrictions.
Because Themida includes so many features, the packed executable is unusually bulky. In addition, unlike most packers, Themida’s code continues to run the entire time that the original program is running.
Some automated tools are designed to unpack Themida files, but their success varies based on the version of Themida and the settings used when the program was packed. Themida has so many features and settings that it is impossible to find a single unpacking strategy that will always work.
If automated tools don’t work, another great strategy is to use ProcDump to dump the process from memory without debugging. ProcDump is a tool from Microsoft for dumping the contents of a Windows process. It’s designed to work with a debugger, but is not itself a debugger. The biggest advantage of ProcDump is that you can dump process memory without stopping or debugging the process, which is extremely useful for packers that have advanced anti-debugging measures. Even when you cannot debug an executable, you can use ProcDump to dump the unpacked contents while the executable is running. This process doesn’t completely restore the original executable, but it does allow you to run strings and do some analysis on the code.