From 0123fbd6aee4ac689fd2b414bf01c0ca041e5cd1 Mon Sep 17 00:00:00 2001 From: Vladislav Yarmak Date: Tue, 5 Feb 2019 21:05:17 +0200 Subject: [PATCH] Add tool for producing .1337 patches --- win/tools/1337-diff/1337-diff.py | 119 +++++++++++++++++++++++++++++++ win/tools/1337-diff/README.md | 46 ++++++++++++ 2 files changed, 165 insertions(+) create mode 100755 win/tools/1337-diff/1337-diff.py create mode 100644 win/tools/1337-diff/README.md diff --git a/win/tools/1337-diff/1337-diff.py b/win/tools/1337-diff/1337-diff.py new file mode 100755 index 0000000..c9ab947 --- /dev/null +++ b/win/tools/1337-diff/1337-diff.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 + +import sys +import argparse +import os.path +import itertools + +PATCH_EXT = ".1337" +CRLF = b"\x0d\x0a" +HEADER_FORMAT = b">%s" +LINE_FORMAT = CRLF + b"%016X:%02X->%02X" +OFFSET_ADJUSTMENT = 0xC00 + + +class ByteDiffException(Exception): + """ Base class for all exceptions """ + pass + + +class LengthMismatchException(ByteDiffException): + """ Throwed when length of input sequences do not match """ + pass + + +def check_positive_int(value): + value = int(value) + if value <= 0: + raise argparse.ArgumentTypeError( + "%s is an invalid positive number value" % value) + return value + + +def parse_args(): + parser = argparse.ArgumentParser( + description="Make .1337 patch file from original and patched files") + parser.add_argument("orig_file", + metavar="ORIG_FILE", + help="original file") + parser.add_argument("patched_file", + metavar="PATCHED_FILE", + help="patched file") + parser.add_argument("output_file", + metavar="OUTPUT_FILE", + nargs='?', + help="filename for patch output. Default: basename of " + "original filename with .1337 extension") + parser.add_argument("-f", "--header-filename", + metavar="FILENAME", + help="filename specified in resulting patch header. " + "Default: basename of original filename.") + parser.add_argument("-l", "--limit", + help="stop after number of differences", + type=check_positive_int) + args = parser.parse_args() + return args + + +def feed_chunks(f, chunk_size=4096): + """ Reads file-like object with chunks having up to `chunk_size` length """ + while True: + buf = f.read(chunk_size) + if not buf: + break + yield buf + + +def zip_files_bytes(*files): + """ Iterate over two files, returning pair of bytes. + Throw LengthMismatch if file sizes is uneven. """ + class EndMarker(object): + pass + end_marker = EndMarker() + + iterators = (itertools.chain.from_iterable(feed_chunks(f)) for f in files) + for tup in itertools.zip_longest(*iterators, fillvalue=end_marker): + if any(v is end_marker for v in tup): + raise LengthMismatchException("Length of input files inequal.") + yield tup + + +def diff(left, right): + for offset, (a, b) in enumerate(zip_files_bytes(left, right)): + if a != b: + yield offset, a, b + + +def compose_diff_file(orig, patched, output, header, offset_adjustment=True): + output.write(HEADER_FORMAT % (header.encode('latin-1'),)) + for offset, a, b in diff(orig, patched): + o = offset + OFFSET_ADJUSTMENT if offset_adjustment else offset + output.write(LINE_FORMAT % (o, a, b)) + + +def main(): + args = parse_args() + + output_filename = args.output_file + if not output_filename: + orig_bname = os.path.basename(args.orig_file) + before, _, after = orig_bname.rpartition('.') + orig_bname_noext = before if before else after + output_filename = orig_bname_noext + PATCH_EXT + + header_filename = args.header_filename + if not header_filename: + header_filename = os.path.basename(args.orig_file) + + with open(args.orig_file, 'rb') as orig,\ + open(args.patched_file, 'rb') as patched,\ + open(output_filename, 'wb') as output: + try: + compose_diff_file(orig, patched, output, header_filename) + except LengthMismatchException: + print("Input files have inequal length. Aborting...", + file=sys.stderr) + + +if __name__ == '__main__': + main() diff --git a/win/tools/1337-diff/README.md b/win/tools/1337-diff/README.md new file mode 100644 index 0000000..f6e25e4 --- /dev/null +++ b/win/tools/1337-diff/README.md @@ -0,0 +1,46 @@ +1337-diff +========= + +This tool accepts two input files and produces patch (diff) in `.1337`-file format. This format is compatible with patches produced by [x64dbg](https://x64dbg.com/) and applied by [Win\_1337\_Apply\_Patch tool](https://github.com/Deltafox79/Win_1337_Apply_Patch). + +This tool is intended for internal usage to automate routine tasks related to windows patches. Implementation is really slow and prefers code transparency and correctness over speed. + +## Requirements + +* Python 3 + +## Synopsys + +``` +$ ./1337-diff.py -h +usage: 1337-diff.py [-h] [-f FILENAME] [-l LIMIT] + ORIG_FILE PATCHED_FILE [OUTPUT_FILE] + +Make .1337 patch file from original and patched files + +positional arguments: + ORIG_FILE original file + PATCHED_FILE patched file + OUTPUT_FILE filename for patch output. Default: basename of + original filename with .1337 extension + +optional arguments: + -h, --help show this help message and exit + -f FILENAME, --header-filename FILENAME + filename specified in resulting patch header. Default: + basename of original filename. + -l LIMIT, --limit LIMIT + stop after number of differences +``` + +## Example + +``` +$ ./1337-diff.py ~/w418.81/orig/nvcuvid.dll ~/w418.81/patched/nvcuvid.dll +$ cat nvcuvid.1337 +>nvcuvid.dll +00000000000DE736:74->90 +00000000000DE737:08->90$ +``` + +Note no newline at the end of file and CRLF line breaks.