The process of making changes to a binary and modifying its instruction flow, from a malware analysts perspective, this can be utilised to overcome measures the malware author has put in place to evade analysis, such as detecting if the target is a virtual machine.

Example 1 - Rock, Paper, Scissors

Taken from the Huntress CTF, this is a good example of how effective binary patching can be.

image

The aim is to win a game of rock, paper, scissors against a program which knows your input.

The first step is to copy the binary to have an original and a copy to which we will make changes.

We’ll use Cutter to open the binary in write mode.

image

We identify that it is a Nim binary, and so navigate to the NimMainInner function, and follow that to main__main62.

image

Following the function we soon find what we’re interested in.

image

The program calls a ‘determineWinner__main_58’ function, followed by a test operation on register AL, and then performs a Conditional Jump (JNE - Jump if Not Equal)

Contextually, and through some conveniently named functions, we know that reversing this jump should mean that winning = losing, and losing = winning

We can reverse the jump by right-clicking the instruction

image

Close Cutter and execute the newly modified binary to see if the outputs have swapped.

image

Example 2 - Targeted Malware

Also taken from the Huntress CTF, crab rave is a challenge where you need to get a DLL to execute to present the flag.

Upon execution, nothing happens.

Just like before, we’ll make a copy of the file, and open it in Cutter with write mode enabled.

Traversing the program we find the ‘NtCheckOSArchitecture’ function, which, after a series of other functions and instructions, calls a function which injects the flag.

image

API Calls associated with process injection

image

Without worrying too much about that, we’ll go back to the ‘NtCheckOSArchitecture’ function and review the instructions in-between the calling of this inject flag function, and the start of NtCheckOSArchitecture.

Multiple whoami functions are called, and the result is compared against a value, if the value does not match what the program is expecting, the payload will not execute.

In this context, whoami is a rust crate used to query the username and hostname.

https://docs.rs/whoami/latest/whoami/

image

We’ll bypass these checks by making note of the address where the inject function is called and changing the very first jump instruction to jump to this address.

image

image

image

Now when we execute the DLL, the payload executes.

image

Example 3 - .NET Binary - Virtualisation Check

This example is taken from my NJRat blog post.

The binary does a basic check to decide if the host is in a virtualised environment by querying Win32_CacheMemory

image

If there is a value for Win32_CacheMemory, the program assumes the host is not a virtual machine and will execute the next function.

Upon execution in my virtual machine with no changes, nothing happens.

To overcome this, we open the binary in DNSpy, locate the class containing the function, right-click on the class in the assembly explorer and select edit class

image

We can simply change ‘if (!Program.VM())’ to ‘if (Program.VM())’, so that the binary will only execute if it’s running in a virtualised environment.

Once done, click compile, and save to a new binary

image

Now when the binary is executed, the payload executes fully and we see C2 communication and further files being dropped onto the host.

image