Skip to content
Samuel Hopstock edited this page Feb 8, 2019 · 5 revisions

Usage

When Un{i}packer starts, the user is prompted with a file selection dialogue. Here, the 10 last loaded samples are listed, including the packer name we identified the last time the file was opened. When selecting to open a new file, please be aware that at the moment, Un{i}packer only supports Windows PE32 executables and will reject ELF files.

After loading the binary, you will be presented with a shell, where further commands can be entered. The prompt of the shell always shows the current address the instruction pointer points to at the moment. Now we will guide you through all of the shell’s commands. The order will correspond to the typical workflow we followed, when analyzing a new kind of packer.

Sample analysis

At this point, Un{i}packer will have identified the packer that was used to obfuscate this binary, if it is one of the supported ones (UPX, ASPack, PEtite and FSG). The aaa command will show you general information over four categories:

  • File analysis: What are the YARA matches found by our rules collection (located in malwrsig.yar) and which unpacker has been chosen. If the packer is known, this chosen unpacker provides a set of config presets that might help unpacking. Those presets are also shown in this info section (e.g. allowed sections for section hopping detection).

  • PE stats: How big is the declared amount of memory the loaded PE file will occupy (and how mach is actually used, so discrepancies can be spotted easily). Additionally, the image base address is displayed (usually 0x400000 or 0x10000000) and the address spaces we reserved for stack and API-call hook memory.

  • Imports: Here we list the imports declared in the PE header, including the respective DLL and the address in of the call stub. You might notice a “Dynamic imports” section that is still empty. In here we will list used library functions that aren’t declared in the PE header but rather loaded dynamically during runtime. So when running aaa after the emulation has finished, this section might list many more library functions, depending on the used packer.

  • Register status: The values of the emulator registers. At this point those are still presets from the loading process.

After looking at those general infos about the current sample, we can go to the next step. By the way, there is an alternative command, aaaa, which displays all of the infos mentioned above but also starts emulation automatically.

Setting the log level

Before we start emulation, we should set the logging level by using the log command. Otherwise, no further information would be displayed during emulation, until it finishes. This improves emulation speed but for deeper analysis of the sample, we will tune this to our liking, using any combination of the following options:

  • i: Enables instruction tracing. Like this, you can follow all executed instructions step by step by looking at the log. As this will produce a lot of output, log redirection to an external file is recommended.

  • r and w: Log memory read and write accesses accordingly, including target addresses and values

  • s: Log external API calls. When this is enabled, the external function name and the provided parameters will be logged, as well as some additional info, depending on the function (e.g. VirtualAlloc will display at which address the memory has been allocated)

  • a: shortcut for irws, enables full logging with all of the options above enabled

Our recommendation for the first run of a binary is to only enable API call logging.

Breakpoints

You may want to set breakpoints to pause emulation at specific times. The b command has several options for this:

  • Code breakpoints: b 0x1010 sets a breakpoint at address 0x1010 and pauses the emulation when this instruction is about to be executed

  • API call breakpoint: If you would like to set a breakpoint when an external library function is called, pass the function name as an argument, prefixed with a $: b $VirtualAlloc pauses whenever VirtualAlloc is being called. If the function name is known (declared in the sample’s import table), the API hook is set as the breakpoint address. Otherwise, the breakpoint is marked as “pending”, meaning that as soon as the function address is dynamically resolved via GetProcAddress, the breakpoint is activated.

  • Memory breakpoints: Prefixing the breakpoint address with an m, e.g. b m0x42 will pause the emulation whenever a read or write access occurs to this address. Memory breakpoints also support address ranges like so: b m0x10-0x200. This makes the emulation pause when a memory access to any address within this range is registered

  • Stack breakpoint: A special case of memory breakpoint, triggered by option stack. Any read or write access inside the stack space pauses the emulation (use aaa for details on where the stack space is)

Deleting breakpoints uses the same options as setting them, the only difference is that you need to use the del command. Any number of the above mentioned options can be combined in a single b or del command

Set unpacking detection method

The detect command allows toggling different unpacking detection mechanisms:

  • Section hopping: The h or hop options toggle section hopping detection. Many packers have one section filled with zeros which is then filled with instructions at runtime. After unpacking, a jump is made into this section and the unpacked code is being executed. This final jump triggers section hopping detection and pauses emulation.

  • Write+execute: The wx or write_exec options toggle write+execute detection. This pauses execution whenever code would be executed, which has previously been modified by the program. This means that either the unpacking stub is finished and now the unpacked program will be executed, or the unpacking mechanism uses self-modifications. If the latter is the case, write+execute detection cannot be reliably used to detect that unpacking is finished.

Running

Now we can start the emulation by invoking the r command. This emulates the sample’s instruction until either a breakpoint is hit, an error occurs (e.g. reading/writing to unmapped memory areas or CPU exception) or the end of the unpacking process is being detected.

Stepping and resuming after a breakpoint

If you would like to single-step through the program’s instructions, use the s command. If you would like to resume normal emulation (e.g. after some single steps or after hitting a breakpoint), simply use the c command. If for some reason you fail to choose c but instead press r, the Un{i}packer shell will notify you that the program is already running. It then interprets your command as a c command.

Inspecting memory and register values

When debugging, you will certainly come across the wish to view register values, display memory contents at a specific address or even modify some of the values. Here are your options:

  • Show register values: This is implemented as the i r command. By default, it prints the values of the most common registers like EAX, EBX,..., but you can also specify the list of registers that are of interest to you, e.g i r eax ecx eip

  • Modify register values: Use the set command with a $-prefixed register name like so: set $eax = 0x1010 to write 0x1010 into EAX

  • Inspect memory: All mapped memory areas (stack/hock space, loaded sample memory and dynamically allocated space) can be inspected using the x command. This supports some options: x[/n] [{FORMAT}] address

    • n: how many elements should be displayed, starting from the base address

    • Format: you can choose to dump either byte-wise with {byte} or in 32bit steps with {int}. By providing {str}, a zero-terminated string starting at the base address is dumped and additionally all of the characters’ byte values

    Register values can also be used as the base address by using a $-prefixed register name.

Dumping the binary

After the emulation has finished or maybe also after hitting a specific breakpoint, you will most likely want to find out whether the unpacking was successful. For this you can first look at some statistics with the stats command. It will show you details about which sections have been executed, read from and written to. When analyzing a new packer for example, this can give you information on whether the packer uses a separate section to write the unpacked binary to or if it is self-modifying.

The dump command will dump all of the loaded sample memory (image base address to base address + declared virtual memory size) to a file. By default this is called unpacked.dump, but you can specify a different path.

Lastly, if you already know which binary has been unpacked (e.g. you are more interested in debugging support for a new packer), the yara command lets you run YARA rules against the dump. This is helpful if you want to check that some known malware has been unpacked, as you can run yara before and after emulation and only after successful unpacking it should recognize the sample.

The dumped memory image also contains the original PE headers. If you would like to modify the entry point declaration in those headers, e.g. for quicker statical analysis, use the fix command providing a new entrypoint address. Note that this is automatically being done if section hopping control is active: When section hopping is detected, the image is dumped with the jump’s target address set as the new entrypoint.

After emulation

After you are done with the current sample, you can quit Un{i}packer by pressing Ctrl+D or with the exit command. If you would like to go back to the initial file-selection prompt to start again, use rst.