Defeating the Packing of Malware Using Execute After Write

2020-02-12

by AbdElaziz Saad

In this article, I will show how easy and fast it is to dump the payload of a packed malware using a simple pyTycho python script. This explanation is based on the semantic breakpoints feature of Tycho and its open-source library pyTycho. If you are not familiar with Tycho, you can have a look at the previous blog posts.

Introduction

Packers, crypters, and protectors are tools that can be used to obfuscate malware by packing, compressing or encrypting its binary; to make it harder and tedious to analyze by the malware analyst using static analysis.

During the packing process, the malicious code of the malware is compressed and stored in the packed section of the new packed executable. Crypters do the same while the packed section is encrypted, and later on, it is decrypted at runtime. Protectors are a combination of packers and crypters with additional functionalities such as code virtualization and anti-analysis techniques.

The picture below shows some of the existing tools that include packers, crypters, and protectors altogether. However, not all of the tools you find in this image have been meant for obfuscating malware; some of them initially existed for benign use.

Landscape of different tools that work as packers, crypters or protectors

This image was designed by Ange Albertini https://corkami.blogspot.com

The Impact of Packing on Static Malware Analysis

Static malware analysis is usually performed by dissecting the different sections of a binary file without executing it. In these sections, the malware analyst can find important information such as the entry point and the import address table (IAT) of that binary.

The entry point of a binary is a worthwhile piece of information to the analyst as it is typically used to follow the correct flow of the binary after disassembling it using disassembler tools such as IDA.

The IAT is a table of pointers to external functions the binary uses. By inspecting the names of the functions in this table, the malware analyst will know what this malware is supposed to do. If there are, for example, functions related to any network socket functions in the IAT, this is most likely a good indication for communication between this malware and other computers on the local network or on the Internet. Malware that gets commands from a command and control C&C server is a good example.

Due to packing, these different pieces of information are obfuscated, which in turn makes the static analysis process tedious and arduous.

Existing tools

For all known packers, many tools exist that are able to unpack the payload which was previously packed. Sometimes the packing and the unpacking features even exist in the same packing tool.

For example, if the well-known UPX packer was used to pack a binary, the malware analyst can easily identify this type of packing simply by using *nix file command or any GUI tool such as exeinfo or PEStudio (the latter are Windows tools).

The *nix file command prints different output for packed and unpacked binaries

Consequently, the malware analyst can use the proper unpacking tool. In this case this would be the UPX unpacker feature, and easily unpack the malware.

If the packing tool is known, the right unpacking tool can easily extract the payload for further analysis

However, there are many packing tools; each one has its own way of packing. Therefore, it is not always easy to identify which packer was used. For this reason, there are some signature-based tools like Detect-it-easy, which try to identify the type of packing by using pattern matching based on signatures database. The signatures database contains a collection of known packers signatures. Also, the malware analyst can extend this database with her own signatures.

Nevertheless, these days, malware authors use custom obfuscation techniques that will evade detection by signature-based tools. Malware authors can simply test their malware against existing detection tools to make sure that their product won't be easily identified by the malware analyst. In this case, the malware analyst still won't know which packer was used. As a result, the deobfuscation process potentially becomes time-consuming and frustrating.

A potential solution is manual intervention such as the one demonstrated in this blog article or giving up static analysis and instead focusing on dynamic analysis. However, modern malware can exhibit a wide variety of deceptive techniques to also defeat dynamic analysis. This includes delaying of the execution of malicious payloads, testing for virtual environments or active debuggers, or requiring some sequence of interactive user input. For such reasons, static analysis remains an attractive method for malware analysis.

What can Tycho do?

Instead of tedious low-level work, an analyst usually has to do to dump the malicious payload from a packed malware, Tycho provides a very easy to use python API called pyTycho. pyTycho which is shipped with Tycho. In this article, we use it to write a high-level and simple python script to dump the binary including the malicious payload. I will show below how that is possible.

Use of Tycho to Detect and Unpack the Packed Malware by using Execute-After-Write Events

The typical behavior of packed malware is generally as follows: A self-extracting malware app first unpacks and then writes the malicious payload to memory. After unpacking, it executes the payload. From a high-level perspective this behavior is common to all packers and crypters. We call this behavior execute-after-write.

Luckily, the pyTycho API gives analysts access to structures in our Tycho hypervisor that allow for triggering events on e.g. specific system calls (with filters on their parameters), memory read/write/execute events (invisible to the guest system), and much more. Therefore, we can easily build an execute-after-write event detection mechanism by manipulating the page tables to trigger write and execute events, as described in the following.

The malware sample is run on the target machine, while we employ the script on the analyst machine. The script on the analyst machine communicates with the hypervisor which controls the target machine's behavior during different phases of the malware execution. As mentioned before, Tycho gives us full control over the running process on the target machine. Therefore, we can invisibly remove the write permission from the malware process's memory pages. If the malware process tries to write to any of its memory pages, we will receive an event which suggests that it is unpacking parts of its payload.

At this point, we can detect where unpacked memory has been written to. We do not know yet, if it finished unpacking the whole payload. In order to detect that, we remove the execute permission from all pages we receive a write event from. This way, we will receive another event whenever the malware tries to execute instructions from pages that it has previously been writing to. Receiving a series of write events with one following execute event within the range of the pages that have been written to, lets us conclude that the payload is unpacked.

The code snippet below shows how this mechanism can easily be implemented.

while True:

    # Synchronously wait for events from Tycho hypervisor.
    event = process.wait_for_breakpoint()

    # Obtain the page address on which the event triggered.
    page_address = get_fault_address_from_thread(event.event_thread) & ~0xFFF

    # Obtain the current page's permissions.
    # We previously stored them in this local data structure.
    page_permission = protection_map.get(page_address)

    # Execute the triggering instruction to not disrupt the process.
    event.event_thread.step()

    """
    We need to distinguish if we had a write or an execute event:
    If page permissions are only RX but not W, we have a W event.
    If page permissions are only RW but not X, we have an X event.
    """
    if page_permission  == READ_EXEC:
        """
        Keep track of what memory page has which permissions.
        """
        protection_map.update({page_address: READ_WRITE})

        """
        Create an execution event trigger. This is equivalent to removing
        the execution permission from this page.

        We will receive an event when the malware tries to execute from that
        page.
        """
        create_execute_bp(page_address, process, service)

    else:
        """
        If the page was previously written to and triggered the
        execute breakpoint event, then we observe typical execute-after-write
        behavior.
        """
        print(">>> Packed Malware Detected <<<")

        """
        Dump the pages which are enumerated in the
        monitor list that were being used by the running malware.
        """
        dump_memory_regions(process, monitor_list)

        break

This code is part of the not yet released bachelor thesis of Sebastian Manns.

At this point, we can remotely kill the process using Tycho to make sure that the payload of the malware is not going to be executed.

Demo

I prepared a demo screencast which shows how to quickly dump the payload of a packed malware sample. The following tools are used in the demo:

  1. Pafish acts as a packed malware example. Pafish employs several techniques to detect sandboxes and analysis environments in the same way as malware does.

  2. The vmdetect YARA rule is used for the detection of anti-virtualization techniques used by malware to evade automated analysis. The detection is based on textual or binary patterns in malware binaries. These rules trigger very well on pafish.

  3. UPX packer

In the demo, the screen is divided into two main windows:

  • The right window shows the target machine's desktop via remote desktop access.
  • The left window is the terminal on the analyst machine.

The target machine is connected to the analyst machine in the same way we previously explained in this article.

We previously packed the pafish.exe executable and uploaded it to the target machine's desktop. Its filename is pafish-upx.exe.

The demo now shows the following procedure:

Minute 0:23

: The python script is armed to wait for the execution of our malware sample.

Minute 0:26

: pafish-upx.exe is executed. You will notice that the python script on the analyst terminal detects its execution.

Minute 0:30

: The execute-after-write behavior is detected. Our script prints its >>> EXECUTE AFTER WRITE DETECTED <<< message and dumps the unpacked payload to the analyst's file system.

Minute 0:55

: The VMdetect YARA rules are run on the packed executable.

Minute 1:06

: The VMdetect YARA rules are run on the unpacked executable dump. In this case, 28 different rules trigger!

You will find that we kept the malware running although we advertised that it is possible to stop it right before it executes its payload. In order to show that this technique is not disrupting the normal execution of the malware, we decided to continue its execution in the demo. pyTycho provides simple methods to kill any process on demand.

Summary

In this article, I have shown how easy, simple, and time-efficient the malware unpacking process can be when using Tycho. We successfully simplify payload extraction that by what we call execute-after-write behavior detection, which is the typical behavior of packers and crypters.

The approach we've used in this article doesn't work with malware that does multistage unpacking that doesn't completely unpack the malicious payload at once before it starts executing the payload. However, we can extend the mentioned technique to work with this type of packing by e.g. postponing the dumping process until the malware is completely unpacked.

If you have any questions, please, don't hesitate to contact us.


Share this article: