Compare commits

...

14 commits

9 changed files with 79 additions and 22 deletions

1
.gitignore vendored
View file

@ -4,4 +4,5 @@ __pycache__
*.pyc *.pyc
*.egg-info/ *.egg-info/
build/
dist/ dist/

View file

@ -1,2 +1,3 @@
include README.md include README.md
include LICENSE.md include LICENSE.md
include mem_edit/VERSION

View file

@ -18,19 +18,19 @@
## Installation ## Installation
**Dependencies:** **Dependencies:**
* python 3 (written and tested with 3.5) * python 3 (written and tested with 3.7)
* ctypes * ctypes
* typing (for type annotations) * typing (for type annotations)
Install with pip, from PyPI (preferred): Install with pip, from PyPI (preferred):
```bash ```bash
pip install mem_edit pip3 install mem_edit
``` ```
Install with pip from git repository Install with pip from git repository
```bash ```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
``` ```

1
mem_edit/VERSION Normal file
View file

@ -0,0 +1 @@
0.3

View file

@ -12,12 +12,17 @@ To get started, try:
""" """
import platform import platform
import pathlib
from .utils import MemEditError from .utils import MemEditError
__author__ = 'Jan Petykiewicz' __author__ = 'Jan Petykiewicz'
with open(pathlib.Path(__file__).parent / 'VERSION', 'r') as f:
__version__ = f.read().strip()
version = __version__
system = platform.system() system = platform.system()
if system == 'Windows': if system == 'Windows':

View file

@ -9,7 +9,8 @@ import copy
import ctypes import ctypes
import logging 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) 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] values = [self.read_memory(base + offset, buffer) for offset, buffer in targets]
return values 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 Search for the provided value at each of the provided addresses, and return the addresses
where it is found. 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 :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 sorts as used by .read_memory(...), which will be compared to the contents of
memory at each of the given addresses. 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. :return: List of addresses where the needle_buffer was found.
""" """
found = [] found = []
read_buffer = copy.copy(needle_buffer) 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: for address in addresses:
read = self.read_memory(address, read_buffer) self.read_memory(address, read_buffer)
if ctypes_equal(needle_buffer, read): if compare(needle_buffer, read_buffer):
found.append(address) found.append(address)
return found 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. 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 sorts as used by .read_memory(...), which will be compared to the contents of
memory at each accessible address. memory at each accessible address.
:param writeable_only: If True, only search regions where the process has write access. :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. :return: List of addresses where the needle_buffer was found.
""" """
found = [] found = []
if verbatim:
search = utils.search_buffer_verbatim
else:
search = utils.search_buffer
for start, stop in self.list_mapped_regions(writeable_only): for start, stop in self.list_mapped_regions(writeable_only):
try: try:
region_buffer = (ctypes.c_byte * (stop - start))() region_buffer = (ctypes.c_byte * (stop - start))()
self.read_memory(start, region_buffer) 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: except OSError:
logger.error('Failed to read in range 0x{} - 0x{}'.format(start, stop)) logger.error('Failed to read in range 0x{} - 0x{}'.format(start, stop))
return found return found

View file

@ -23,9 +23,32 @@ class MemEditError(Exception):
pass 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]: 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 needle_buffer: Buffer to search for.
:param haystack_buffer: Buffer to search in. :param haystack_buffer: Buffer to search in.

View file

@ -1,10 +1,18 @@
#!/usr/bin/env python #!/usr/bin/env python3
from setuptools import setup, find_packages from setuptools import setup, find_packages
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', setup(name='mem_edit',
version='0.1', version=version,
description='Multi-platform library for memory editing', description='Multi-platform library for memory editing',
long_description=long_description,
long_description_content_type='text/markdown',
author='Jan Petykiewicz', author='Jan Petykiewicz',
author_email='anewusername@gmail.com', author_email='anewusername@gmail.com',
url='https://mpxd.net/code/jan/mem_edit', url='https://mpxd.net/code/jan/mem_edit',
@ -26,7 +34,6 @@ setup(name='mem_edit',
'trainer', 'trainer',
], ],
classifiers=[ classifiers=[
'Programming Language :: Python',
'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3',
'Development Status :: 4 - Beta', 'Development Status :: 4 - Beta',
'Environment :: Other Environment', 'Environment :: Other Environment',
@ -42,8 +49,10 @@ setup(name='mem_edit',
'Topic :: Utilities', 'Topic :: Utilities',
], ],
packages=find_packages(), packages=find_packages(),
package_data={
'mem_edit': ['VERSION']
},
install_requires=[ install_requires=[
'ctypes',
'typing', 'typing',
], ],
extras_require={ extras_require={