This is the second part of our three-part “Power of Execution Graph” blog series. The first part which introduces Execution Graphs can be found at here.
As you may recall, Execution Graphs are highly condensed control flow graphs, showing information about which part of the code has been executed and which not. Execution Graphs highlight additional attributes such API calls, threats starts, and key decisions.
Analyzing PackersIn this blog post, we are going to focus on an interesting sample we already have analyzed previously with pure Hybrid Code Analysis (HCA). The sample includes various sandbox detection tricks including one trick to identify specifically Joe Sandbox. In the following text, we outline how to spot these tricks by using Execution Graph.
The analyzed sample relies on packing and encryption as a first layer of evasion. This technique is quite challenging to inspect manually from the PE file and generally poses a major problem for static analysis approaches. Hybrid Code Analysis is resilient against packing and therefore facilitates the analysis of unpacked code.
Let us start with having a look at the Execution Graph summary tab:
The first important striking fact is that 99% of the code is tagged as “Dynamic/Decrypted”. When looking at the prefix of the Execution Graph in some detail, we notice the following:
- The code starts by allocating dynamic memory using NtAllocateVirtualMemory native API calls.
- Once the allocation is performed, the code reaches the node labeled 401065 which is flagged as Unpacker code. At this point, the code is written into the previously allocated memory sections.
- After execution the unpacker code then branches to the dynamically generated code.
By clicking on the node 401065, we can check that it indeed contains unpacker code:
Checking the basic blocks leads us to the unpacker code itself:
Similarly, by clicking on the following node 164a00, we can see that the corresponding code is located in a dynamically allocated memory (as almost all nodes reachable from this point):
Please note that according to the status dynamic or unpacked code status the nodes are highlighted with different color:
There are three different branches starting from node 401065. By clicking on node 401065, (which covers several basic blocks) then following the hyper-link to basic block 4010CC, we jump to the following disassembled code:
The computed call call edx at the virtual address 04010E5 represents the execution branching to the unpacked code.
The Hybrid Code Analysis found three potential targets represented by the three target nodes, while the executed code starting at node 164a00 is the most interesting with respect to its behavior.
The sub-graph below outlines the various evasion tricks:
- The sample first checks the its file name (call to GetModuleFileName) and may stall if it looks suspicious (e.g. in this case a file name like “sample”) by sleeping (branch to node 164838).
- After checking the serial ID information of the volume C: (call to GetVolumeInformationA), it may stall again if it matches a given magic value (sandbox detection via disk serial number).
Here both evasions fail since the execution proceeds as illustrated by the node coloring.
Later in the code, at node 1548dc the sample tries to detect if it is being run on a virtual machine. To do so it reads the disk names via registry System\CurrentControlSet\Services\Disk\Enum and compares to well-known products names such as VMWare. Checking disk names of virtualization products is a well-known anti-VM trick which we see in nearly 70% of all samples.
Finally, one last check has more success and the execution ends up stalling in an endless Sleep loop:
Selecting the key-decision node 16499d shows us the disassembly, which indicates the trick is related to the registry key AutoItv3CCleanerWIC:
The code enumerates all software uninstallers. This enables to collect a list of all installed software on the machine. The fingerprint AutoItv3CCleanerWIC is then used to check if AutoIt, CCleaner and WIC are installed. If true the sample falls asleep. AutoIt and CCleaner are two additional software we often install on machines to make administration more easily. Likely, the guys behind this malware were extracting the fingerprint by using our free Joe Sandbox Cloud Basic online service.
Beside the evasion tricks, the Execution Graph can also be browsed for finding hidden / non executed functionalities, in the form of suspicious sub-graphs. Here is an example:
This sub-graph outlines a remote process injection technique, which has not been executed during analysis but still can be found easily in the graph. The various edges to the lower nodes are error handling, e.g. if CreateToolhelp32Snapshot fails then CloseHandle is directly called. The code is quite extensive and spans several functions. Thanks to the condensed and connected graph it is easy to detect and understand.
An Execution Graph often consists of a main graph as well as several independent graphs:
The main graph contains executed nodes (marked as red) while the independent graphs do not have any executed code. The reason behind this is the difficulty to generate a completely connected graph. E.g. consider the non-executed instruction call eax, where eax is previously computed. It is not possible to determine which code location is being called.
In order to focus on the main graph, we added a new feature to hide independent graphs. Simply click on the Hide Nodes/Edges label found at the top-left of the Execution Graph panel to hide independent graphs and focus on the main graph. Click again to restore the full view.
Graph based Signatures
Of course, manually browsing through the Execution Graph is not the only way for detecting evasive behavior. Execution Graph Analysis uses an extensive set of behavior signatures to automatically detect evasion tricks. A nice feature we have recently added is the ability to jump to the incriminated Execution Graph nodes from the signature by using the links in the report:
The sample covered in this blog post uses a large panel of techniques to avoid detection by sandboxes. But thanks to the Execution Graph Analysis, the following information could be quickly obtained:
- The execution starts by dynamically generating code. The Execution Graph enables to easily find the unpacker code as well as the newly generated code.
- The unpacked code uses various evasion tricks that Execution Graph Analysis automatically detected and rated as malicious. The evasion tricks can be further analyzed in-depth by navigating from the signature hits to the Execution Graph nodes and from the nodes to the disassembly code.
- Besides the detection of evasive behavior, the Execution Graph provides a good way of detecting complex malware functionalities (such as remote process injection) in the form of sub-graphs.
Stay tuned for our last blog post in our Power of Execution Graphs series!
Report available at:
Report available at: