Quick And Dirty Binary Patching With A Hex Editor

Whether it's to circumvent an anti-analysis check, or simply a bug that needs to be fixed, patching a binary is a useful technique to have in the reverse engineer's toolbox. Normally patching refers to a process where a patch file is generated and then applied to a binary. IDA Pro actually contains functionality to do this and there is an excellent tutorial on the subject here. But today we will be patching by simply editing a few bytes in the binary with a hex editor. It's quick, it's dirty, and it works.

Watch the tutorial video!

Patching a binary will almost guarantee that it will not execute as intended. Quick patching should only be used when the focus of your analysis is limited to certain functionality that won’t be affected (ex. C2 communication, or encryption routines).

A while back a friend asked me to look at a malware sample[1] that wasn't running in his sandbox. It turns out the malware contained two threads; one with some juicy indicators, and one that did some "other stuff" (official revers engineering terminology). Unfortunately the "other stuff" thread was killing the process before the "juicy indicator" thread had a chance to reveal itself to the sandbox. All I needed to do was patch the binary so the "other stuff" thread was never created. The following article outlines the steps I took to patch the binary.

Before we proceed we need to make sure we have the following tools:

  • IDA disassembler
  • Hex editor

Identify The Code

The first step in patching the binary is to identify the code that we want to patch. For this we need to load the malware in IDA look at the disassembly view. The image below shows the main function of the malware. Here we can see where the the two threads are created.
Figure 1 - Two threads created

Next we need to see the opcodes[2] for the disassembled instructions. The opcodes will match the hex bytes in the binary file that we intend to edit. To show the the opcodes in IDA you need to set the number of opcodes to be shown in the following menu:
Options -> General -> Number of opcode bytes
We will choose to show 15 bytes as this is maximum length of an Intel 64 and IA-32 instruction[3].

Figure 2 - IDA opcode display

Now that we can see the opcode we are able to determine which bytes we want to edit in the binary.

Figure 3 - Opcodes for the instructions we want to remove

In our example we want to remove the second call to CreateThread as well as the arguments that were pushed onto the stack for the call. This means removing the bytes 6A 00 6A 00 6A 00 68 90 24 00 10 6A 00 6A 00 E8 8F 12 00 00. We want to keep the instruction that moves the returned thread handle eax from the original call to CreateThread into esi as this is later passed to WaitForSingleObject in order to wait for the thread to terminate before the process exits.

Edit The Binary

Now that we know which bytes we need to change we can open the binary in a hex editor and start editing. Your choice of hex editor doesn't matter much using an editor that has a built in binary search function will make the process a lot quicker. My preferred hex editor is Synalyze It! for OSX.

With the binary search feature we can search for the opcode bytes 6A 00 6A 00 6A 00 68 90 24 00 10 6A 00 6A 00 E8 8F 12 00 00 that we identified in the previous step. This way we don't have to mess around with calculating the RAV[4] from the IDA address.

Figure 4 - Hex editor opcodes located

Now that we have found the opcode bytes in the binary we can simply edit them. But, we can't just delete them! Remember these are bytes in a PE file and if we start removing bytes we could corrupt the PE by changing the size of the sections. Instead we need to replace the bytes with an instruction the does nothing - the NOP[5] instruction. The hex value for a NOP is 0x90, so we can use this to replace the opcode bytes we want to remove.

Figure 5 - Opcodes replaced by NOP

Remember we need to preserve the bytes 8B F0 which we are now using to copy the handle to the thread we are interested in to the WaitForSingleObject function. Once the binary is edited we can save it and open it again in IDA to verify we patched the right bytes.

Figure 6 - Verify the patch in IDA

In IDA we can see that the all of the code used to create the second thread has been removed. Our patch is a success!

  1. Sha256 - f88bc84fea3695cd1da1a315eb09c65f21cfc6b764defc3c8efd94d6c6396e0c ↩︎

  2. The x86 instruction set ↩︎

  3. Section 2.3.11 - Intel® 64 and IA-32 ArchitecturesSoftware Developer’s Manual ↩︎

  4. Relative Virtual Address explained ↩︎

  5. No Operation ↩︎