From 3b766be616ed76124b3273b33c42842b16def2e0 Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Sun, 24 Mar 2019 20:36:18 -0700 Subject: [PATCH 01/14] Add utils.search_buffer_verbatim() --- mem_edit/utils.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/mem_edit/utils.py b/mem_edit/utils.py index c2e691f..fc15970 100644 --- a/mem_edit/utils.py +++ b/mem_edit/utils.py @@ -23,9 +23,32 @@ class MemEditError(Exception): pass +def search_buffer_verbatim(needle_buffer: ctypes_buffer_t, haystack_buffer: ctypes_buffer_t) -> List[int]: + """ + Search for a buffer inside another buffer, using a direct (bitwise) comparison + + :param needle_buffer: Buffer to search for. + :param haystack_buffer: Buffer to search in. + :return: List of offsets where the needle_buffer was found. + """ + found = [] + + haystack = bytes(haystack_buffer) + needle = bytes(needle_buffer) + + start = 0 + result = haystack.find(needle, start) + while start < len(haystack) and result != -1: + found.append(result) + start = result + 1 + result = haystack.find(needle, start) + return found + + def search_buffer(needle_buffer: ctypes_buffer_t, haystack_buffer: ctypes_buffer_t) -> List[int]: """ - Search for a buffer inside another buffer. + Search for a buffer inside another buffer, using ctypes_equal for comparison. + Much slower than search_buffer_verbatim. :param needle_buffer: Buffer to search for. :param haystack_buffer: Buffer to search in. @@ -46,7 +69,7 @@ def ctypes_equal(a: ctypes_buffer_t, b: ctypes_buffer_t) -> bool: """ if not type(a) == type(b): return False - + if isinstance(a, ctypes.Array): return a[:] == b[:] elif isinstance(a, ctypes.Structure) or isinstance(a, ctypes.Union): From 522999cd61a9ac66fa33545477a587c737246366 Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Sun, 24 Mar 2019 20:39:58 -0700 Subject: [PATCH 02/14] Add verbatim mode to searches (default true) --- mem_edit/abstract.py | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/mem_edit/abstract.py b/mem_edit/abstract.py index e08740a..fb463f7 100644 --- a/mem_edit/abstract.py +++ b/mem_edit/abstract.py @@ -9,7 +9,8 @@ import copy import ctypes import logging -from .utils import ctypes_buffer_t, search_buffer, ctypes_equal +from . import utils +from .utils import ctypes_buffer_t logging.basicConfig(level=logging.INFO) @@ -240,7 +241,7 @@ class Process(metaclass=ABCMeta): values = [self.read_memory(base + offset, buffer) for offset, buffer in targets] return values - def search_addresses(self, addresses: List[int], needle_buffer: ctypes_buffer_t) -> List[int]: + def search_addresses(self, addresses: List[int], needle_buffer: ctypes_buffer_t, verbatim: bool=True) -> List[int]: """ Search for the provided value at each of the provided addresses, and return the addresses where it is found. @@ -249,18 +250,26 @@ class Process(metaclass=ABCMeta): :param needle_buffer: The value to search for. This should be a ctypes object of the same sorts as used by .read_memory(...), which will be compared to the contents of memory at each of the given addresses. + :param verbatim: If True, perform bitwise comparison when searching for needle_buffer. + If False, perform utils.ctypes_equal-based comparison. Default True. :return: List of addresses where the needle_buffer was found. """ found = [] read_buffer = copy.copy(needle_buffer) + if verbatim: + def compare(a, b): + return bytes(read_buffer) == bytes(needle_buffer) + else: + compare = utils.ctypes_equal + for address in addresses: - read = self.read_memory(address, read_buffer) - if ctypes_equal(needle_buffer, read): + self.read_memory(address, read_buffer) + if compare(needle_buffer, read_buffer): found.append(address) return found - def search_all_memory(self, needle_buffer, writeable_only=True) -> List[int]: + def search_all_memory(self, needle_buffer: ctypes_buffer_t, writeable_only: bool=True, verbatim: bool=True) -> List[int]: """ Search the entire memory space accessible to the process for the provided value. @@ -268,14 +277,22 @@ class Process(metaclass=ABCMeta): sorts as used by .read_memory(...), which will be compared to the contents of memory at each accessible address. :param writeable_only: If True, only search regions where the process has write access. + Default True. + :param verbatim: If True, perform bitwise comparison when searching for needle_buffer. + If False, perform utils.ctypes_equal-based comparison. Default True. :return: List of addresses where the needle_buffer was found. """ found = [] + if verbatim: + search = utils.search_buffer_verbatim + else: + search = utils.search_buffer + for start, stop in self.list_mapped_regions(writeable_only): try: region_buffer = (ctypes.c_byte * (stop - start))() self.read_memory(start, region_buffer) - found += [offset + start for offset in search_buffer(needle_buffer, region_buffer)] + found += [offset + start for offset in search(needle_buffer, region_buffer)] except OSError: logger.error('Failed to read in range 0x{} - 0x{}'.format(start, stop)) return found From 5021d5fb9ad38e9baef1c6f33d8cae8c69aabcff Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Sun, 24 Mar 2019 20:50:19 -0700 Subject: [PATCH 03/14] indicate python3 for setup --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 46b51f6..fff6ea7 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 from setuptools import setup, find_packages From 571ecc7a956b2d9cad6e09d90633a354d43729b6 Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Sun, 24 Mar 2019 20:51:26 -0700 Subject: [PATCH 04/14] Add readme as long_description --- setup.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/setup.py b/setup.py index fff6ea7..b34402f 100644 --- a/setup.py +++ b/setup.py @@ -2,9 +2,16 @@ from setuptools import setup, find_packages + +with open('README.md', 'r') as f: + long_description = f.read() + + setup(name='mem_edit', version='0.1', description='Multi-platform library for memory editing', + long_description=long_description, + long_description_content_type='text/markdown', author='Jan Petykiewicz', author_email='anewusername@gmail.com', url='https://mpxd.net/code/jan/mem_edit', From 53b1b1ade8a6c4ff82d52dcaea955303db026314 Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Sun, 24 Mar 2019 20:52:41 -0700 Subject: [PATCH 05/14] Bump python version used for writing/testing --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 71840ab..66ecc88 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ ## Installation **Dependencies:** -* python 3 (written and tested with 3.5) +* python 3 (written and tested with 3.7) * ctypes * typing (for type annotations) From 83e105dc302b8ccc4dd96b6c158b3da1a5cd27ec Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Sun, 24 Mar 2019 20:53:24 -0700 Subject: [PATCH 06/14] Bump version to 0.2; Move version number into module --- mem_edit/__init__.py | 1 + setup.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mem_edit/__init__.py b/mem_edit/__init__.py index 9f3fbf9..8391f4f 100644 --- a/mem_edit/__init__.py +++ b/mem_edit/__init__.py @@ -17,6 +17,7 @@ from .utils import MemEditError __author__ = 'Jan Petykiewicz' +version = '0.2' system = platform.system() diff --git a/setup.py b/setup.py index b34402f..2b6ecbe 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 from setuptools import setup, find_packages +import mem_edit with open('README.md', 'r') as f: @@ -8,7 +9,7 @@ with open('README.md', 'r') as f: setup(name='mem_edit', - version='0.1', + version=mem_edit.version, description='Multi-platform library for memory editing', long_description=long_description, long_description_content_type='text/markdown', From d0bbed8db1bcf279d27ff9f3c8a1b5f8291bc1d5 Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Sun, 24 Mar 2019 20:57:09 -0700 Subject: [PATCH 07/14] remove ctypes from reqs, should be built-in --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 2b6ecbe..bec3d13 100644 --- a/setup.py +++ b/setup.py @@ -51,7 +51,6 @@ setup(name='mem_edit', ], packages=find_packages(), install_requires=[ - 'ctypes', 'typing', ], extras_require={ From 6321d4221c1a9e5e7a20e706780c04e6184ae41c Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Sun, 24 Mar 2019 21:13:19 -0700 Subject: [PATCH 08/14] specify pip3 in readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 66ecc88..7367bc5 100644 --- a/README.md +++ b/README.md @@ -25,12 +25,12 @@ Install with pip, from PyPI (preferred): ```bash -pip install mem_edit +pip3 install mem_edit ``` Install with pip from git repository ```bash -pip install git+https://mpxd.net/code/jan/mem_edit.git@release +pip3 install git+https://mpxd.net/code/jan/mem_edit.git@release ``` From 49a7c21ed2c1d38e8cac2f65223da7884551ea91 Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Mon, 30 Sep 2019 23:59:20 -0700 Subject: [PATCH 09/14] Move version number into VERSION file to avoid importing mem_edit in setup.py --- MANIFEST.in | 1 + mem_edit/VERSION | 1 + mem_edit/__init__.py | 6 +++++- setup.py | 9 ++++++--- 4 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 mem_edit/VERSION diff --git a/MANIFEST.in b/MANIFEST.in index c28ab72..2b8b271 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,3 @@ include README.md include LICENSE.md +include mem_edit/VERSION diff --git a/mem_edit/VERSION b/mem_edit/VERSION new file mode 100644 index 0000000..3b04cfb --- /dev/null +++ b/mem_edit/VERSION @@ -0,0 +1 @@ +0.2 diff --git a/mem_edit/__init__.py b/mem_edit/__init__.py index 8391f4f..8947638 100644 --- a/mem_edit/__init__.py +++ b/mem_edit/__init__.py @@ -12,12 +12,16 @@ To get started, try: """ import platform +import pathlib from .utils import MemEditError __author__ = 'Jan Petykiewicz' -version = '0.2' + +with open(pathlib.Path(__file__).parent / 'VERSION', 'r') as f: + __version__ = f.read().strip() +version = __version__ system = platform.system() diff --git a/setup.py b/setup.py index bec3d13..5f6ecf2 100644 --- a/setup.py +++ b/setup.py @@ -1,15 +1,15 @@ #!/usr/bin/env python3 from setuptools import setup, find_packages -import mem_edit - with open('README.md', 'r') as f: long_description = f.read() +with open('mem_edit/VERSION', 'r') as f: + version = f.read().strip() setup(name='mem_edit', - version=mem_edit.version, + version=version, description='Multi-platform library for memory editing', long_description=long_description, long_description_content_type='text/markdown', @@ -50,6 +50,9 @@ setup(name='mem_edit', 'Topic :: Utilities', ], packages=find_packages(), + package_data={ + 'mem_edit': ['VERSION'] + }, install_requires=[ 'typing', ], From e842f8157509c6e19d47752fed4e3458d5698ebb Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Mon, 30 Sep 2019 23:59:28 -0700 Subject: [PATCH 10/14] update tags --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 5f6ecf2..5b53ab7 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,6 @@ setup(name='mem_edit', 'trainer', ], classifiers=[ - 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Development Status :: 4 - Beta', 'Environment :: Other Environment', From e8c6c4f74c393615e6ec20c386bd9361e0787a7a Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Mon, 30 Sep 2019 23:59:37 -0700 Subject: [PATCH 11/14] clean up whitespace --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7367bc5..53789b7 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,8 @@ **Dependencies:** * python 3 (written and tested with 3.7) -* ctypes -* typing (for type annotations) +* ctypes +* typing (for type annotations) Install with pip, from PyPI (preferred): @@ -55,7 +55,7 @@ Increment a magic number (unsigned long 1234567890) found in 'magic.exe': pid = Process.get_pid_by_name('magic.exe') with Process.open_process(pid) as p: addrs = p.search_all_memory(magic_number) - + # We don't want to edit if there's more than one result... assert(len(addrs) == 1) @@ -104,7 +104,7 @@ Read and alter a structure: s = MyStruct() s.first_member = 1234567890 s.second_member = 0x1234 - + addrs = p.search_all_memory(s) print(addrs) From 8b5d5af95b0abc0a06c95b8187dd120b1acbb7f2 Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Tue, 1 Oct 2019 00:00:06 -0700 Subject: [PATCH 12/14] bump version number to 0.3 --- mem_edit/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mem_edit/VERSION b/mem_edit/VERSION index 3b04cfb..be58634 100644 --- a/mem_edit/VERSION +++ b/mem_edit/VERSION @@ -1 +1 @@ -0.2 +0.3 From 4deaa41d7e8a48a15da53d2f363e00da6db91d8b Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Sun, 27 Oct 2019 13:05:27 -0700 Subject: [PATCH 13/14] whitespace fixes --- mem_edit/linux.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mem_edit/linux.py b/mem_edit/linux.py index 4dafd1c..ae8404d 100644 --- a/mem_edit/linux.py +++ b/mem_edit/linux.py @@ -40,7 +40,7 @@ def ptrace(command: int, pid: int = 0, arg1: int = 0, arg2: int = 0) -> int: """ Call ptrace() with the provided pid and arguments. See the ```man ptrace```. """ - logger.debug('ptrace({}, {}, {}, {})'.format(command, pid, arg1, arg2)) + logger.debug('ptrace({}, {}, {}, {})'.format(command, pid, arg1, arg2)) result = _ptrace(command, pid, arg1, arg2) if result == -1: err_no = ctypes.get_errno() @@ -58,7 +58,7 @@ class Process(AbstractProcess): self.pid = process_id def close(self): - os.kill(self.pid, signal.SIGSTOP) + os.kill(self.pid, signal.SIGSTOP) ptrace(ptrace_commands['PTRACE_DETACH'], self.pid, 0, 0) self.pid = None @@ -78,7 +78,7 @@ class Process(AbstractProcess): with open('/proc/{}/cmdline', 'rb') as f: return f.read().decode().split('\x00')[0] except FileNotFoundError: - return '' + return '' @staticmethod def list_available_pids() -> List[int]: From 260d67bf815796371d63e87cdd791c98f765d71b Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Sun, 27 Oct 2019 13:54:43 -0700 Subject: [PATCH 14/14] update gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 79a8109..6ad846b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ __pycache__ *.pyc *.egg-info/ +build/ dist/