Chapter summary questions

  1. In two sentences or less, provide an overview of what this chapter is about.
  2. What are the three most important takeaways from this chapter?
  3. What problems does this chapter address? In other words, why should we care about this chapter?

Overview of how packing works

Packers generally follow the same pattern: they transform (compress, encrypt, etc) an executable into raw data. This is the act of packing. Packers then prepend a valid executable (called an unpacking stub) to that data. The unpacking stub exists to decrypt or decode or unzip the packed portion of the executable into memory at run time, and then transfer execution to the unpacked code. This is significant because it conceals the program’s instructions to static analysis tools (excluding the instructions of the unpacking stub).

To maintain the functionality of the original program, a packing program needs to store the program’s import information. The information can be stored in any format, and there are several common strategies which will be discussed later.

Packers can pack the entire executable, including all data and the resource section, or they can pack only the code and data sections.

The Unpacking Stub

The purpose of the unpacking stub is typically to load the packed packed program, unpack it, resolve imports of the original executable, and transfer execution to the original entry point. It need not unpack the entire executable at any given point in time. Understanding how the stub works is almost always crucial to understanding the malware.

Resolving imports

Because the Windows loader cannot read import information that is packed, the unpacking stub must resolve the imports. There are a few approaches that can be taken to do this.

  • Most common: have the unpacking stub import LoadLibrary and GetProcAddress functions. After the unpacking stub unpacks the original executable, it reads the original import information, uses LoadLibrary to load those libraries, and uses GetProcAddress to get the address for functions in each library.
  • Another approach: Keep the imports table intact.
  • A third approach: keep one import function from each DLL in the original import table. In this case, LoadLibrary will not be needed, but the stub will still need to resolve the other functions.
  • The final discussed approach is to remove all imports including LoadLibrary and GetProcAddress. Then, the packer must find all functions needed from other libraries without using those functions, or it must find LoadLibrary and GetProcAddress and then use them to load other libraries. This is the stealthiest method, but also the most difficult to implement.

Recognizing packed programs

There are often giveaways that are indicative of a packed program.

  • One sign is a program has few imports, and those imports are often just LoadLibrary and GetProcAddress
  • When the program is opened in IDA, only a small amount of code is recognized by the automatic analysis.
  • When the program is opened in OllyDbg, there is a warning that the program is packed.
    • How does OllyDbg identify this? Do we even care in 64-bit land?
  • The program shows section names that indicate a particular packer
    • What are section names typically for executables?
  • the program has abnormal section sizes, such as a .text section that has a raw size of 0 but a virtual size of nonzero.

Entropy Calculation

Entropy Calculation is a technique where packed executables can be detected based on the randomness of the executable. Encrypted or otherwise packed programs usually have high entropy.

Unpacking executables

Three options:

  1. Automated static unpacking.
  2. Automated dynamic unpacking.
  3. Manual dynamic unpacking.

Manual static unpacking is completely infeasible.

Automated static unpacking

This is generally the best and fastest method, when it works, because it unpacks a program but does not run the unpacked program. Programs that perform automated static unpacking generally only work for one type of packer.

Automated dynamic unpacking

These run the packed program and allow the unpacking stub to unpack the original executable code. Once the unpacking stub finishes, it writes the unpacked program to disk, and the unpacker reconstructs the import table.

The unpacking program must determine where the unpacking stub ends and where the original executable begins, which is difficult.

As of the time of this book’s writing, “there are no good publicly available automated dynamic unpackers.”

Manual unpacking

There are two common approaches to manually unpacking a program.

  1. Discover the packing algorithm and write a program to run it in reverse. This is generally less efficient, but may be useful if dealing with many different executables that are packed in the same manner.

  2. Run the packed program up to the point where the unpacking stub finishes and does the work for you, and then dump the process out of memory and manually fix up the PE header so that the program is complete.

A walkthrough of the manual unpacking process for ideal packed executables

The first step is to use the following workflow to see if OllyDbg’s OllyDump plugin can unpack the executable for you. To begin, open the packed program with OllyDbg.

  1. Select Plugins -> OllyDump -> Find OEP by Section Hop. Olly will hit a breakpoint just before the OEP executes. (It’s sort of slow to run, mind you!)

  2. Write down where OllyDbg broke. This is the OEP.

  3. Select Plugins -> OllyDump -> Dump Debugged Process. This dumps everything from process memory to disk.

Using Import Reconstructor

If OllyDbg fails, you can try to use Import Reconstructor (ImpRec) to repair the import table for packed programs.In this case, see p390 for more details.

How OllyDump’s Find OEP by Section Hop tool works

Normally, the unpacking stub is in one section and the executable is packed in another section. OllyDbg detects whenthere is a transfer from one section to another and breaks there, using either the step-over or step-into method.

The step-over method will step over any call instruction. Calls are often used to execute code in another section, and this method is designed to prevent OllyDbg from incorrectly labeling one of those calls as the OEP. But, if a call never returns, of course OllyDbg won’t locate the OEP.

The step-into method will step into calls, but it might provide false positives as well.

Things to look for when manually finding the OEP

  • Jumps outside of a section
  • Jumps that are followed by a bunch of invalid code
  • Jumps that are unreasonably far away
  • Jumps immediately followed by classic function boiler plate code (push ebp, mov ebp, esp for example) :

Terminology

Page-to-page notes

  • here
    • here

Tools discussed in this section

here

Questions encountered (and answers, if found)

here