Chapter summary questions
- In two sentences or less, provide an overview of what this chapter is about.
- What are the three most important takeaways from this chapter?
- 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
andGetProcAddress
functions. After the unpacking stub unpacks the original executable, it reads the original import information, usesLoadLibrary
to load those libraries, and usesGetProcAddress
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
andGetProcAddress
. Then, the packer must find all functions needed from other libraries without using those functions, or it must findLoadLibrary
andGetProcAddress
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
andGetProcAddress
- 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:
- Automated static unpacking.
- Automated dynamic unpacking.
- 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.
-
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.
-
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.
-
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!)
-
Write down where OllyDbg broke. This is the OEP.
-
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