I started reversing about a year ago and I knew that Python was a valuable tool that I would need to pick up. The opportunity presented itself when I was asked to help with patching a PE file. I needed to make something that could be easily distributed publicly while also being very transparent about what it was doing. Python was the perfect choice for this.

The project originally started as a request for help from a small cryptocurrency community on Reddit. They wanted to patch some outdated mining software. The project unfortunately didn’t go anywhere but it was time well spent learning Python, regardless if it gets used.

There’s two parts to the script: patcher.py and sig_scanner.py. There’s also a small program called numprint.exe I created to help me test my Python script.

import sig_scanner
import os
import sys
import argparse

def base_sixteen(x):
    return int(x, 16)

parser = argparse.ArgumentParser(description="Open a file and scan for a signature. If a unique signature is found, edit a byte and save the changes to a new file.",
                                epilog='example use: patcher.py "D:\\Desktop\\file.exe" "6A 01 E8 ?? ?? ?? ?? 83 C4 04 68" 1 0x2')
parser.add_argument("input_file", type=str,
                    help="The path of the file to patch.")
parser.add_argument("sig", type=str,
                    help="A unique signature, use '??' for any unknown values.")
parser.add_argument("patch_posisition", type=int,
                    help="The offset, starting from the beginning of the signature, to the byte to be patched.")
parser.add_argument("patch_value", type=base_sixteen,
                    help="The value of the byte to be patched.")
args = parser.parse_args()

with open(args.input_file, 'rb') as file:
    data = bytearray(file.read())

print(f'Read {len(data)} bytes from "{args.input_file}".')

# find_signature takes a bytearray and a string.
offset = sig_scanner.find_signature(data, args.sig)

if not offset:
    print(f"Couldn't find signature '{args.sig}' in {args.input_file}.")
    exit(0)

if (len(offset) > 1):
    print(f"Signature is not unique!")
    for o in offset:
        print(f"Signature '{args.sig}' found at offset {hex(o)}.")
    exit(0)

print(f"Unique signature '{args.sig}' found at offset {hex(offset[0])}.")

data[offset[0] + args.patch_posisition] = args.patch_value

output_file = os.path.splitext(args.input_file)[0] + "_edit" + os.path.splitext(args.input_file)[1]

with open(output_file, 'wb') as file:
    file.write(data)

print(f"File '{output_file}' patched successfully!")
# find_signature will find any matches of the needle in a haystack. 
def find_signature(haystack: bytearray, needle: str) -> list:
    sigs = []

    needle = needle.replace(" ", "")
    mask = create_mask(needle)

    needle = needle.replace("??", "00")
    needle_bytes = bytearray.fromhex(needle)

    needle_index = 0
    haystack_index = 0

    for b in haystack:

        if (needle_index >= len(needle_bytes)):
            sigs.append(haystack_index - len(needle_bytes))
            needle_index = 0
            continue

        if (mask[needle_index] == True):
            haystack_index += 1
            needle_index += 1
            continue

        if (b == needle_bytes[needle_index]):
            needle_index += 1
        else:
            needle_index = 0

        haystack_index += 1

    if not sigs:
        return None
    else:
        return sigs

# The mask is an array that mirrors the signature where each mask element is "True" for any sig "??" elements and "False" for all other hex byte elements.
# For example, if your signature looks like: "6A 01 E8 ?? ?? ?? ?? 83 C4 04 68"
# Then the mask array will look like: "0 0 0 1 1 1 1 0 0 0 0"
def create_mask(s: str) -> list:
    mask = []

    # https://stackoverflow.com/questions/1162592/iterate-over-a-string-2-or-n-characters-at-a-time-in-python
    for (op, code) in zip(s[0::2], s[1::2]):
        char = op + code
        if (char == "??"):
            mask.append(True)
        else:
            mask.append(False)

    return mask
#include <Windows.h>
#include <stdio.h>

void PrintNumber(int number)
{
	printf("The number is: %d.\n", number);
}

int main()
{
	PrintNumber(1); // Patch this 1 to something different with Python.

	system("pause");
}

Here’s numprint.exe running, before being patched.

Here’s numprint.exe in the debugger so you can see what the signature is pointing at.

Running the Python script gives some output about the patch.

And now when I run numprint_edit.exe we can see the patch in action.

This was a lot of fun and I look forward to writing, and using, more Python in the future!

Last modified: June 17, 2019