Compare commits

...

14 commits

9 changed files with 79 additions and 22 deletions

1
.gitignore vendored
View file

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

View file

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

View file

@ -18,19 +18,19 @@
## Installation
**Dependencies:**
* python 3 (written and tested with 3.5)
* python 3 (written and tested with 3.7)
* ctypes
* typing (for type annotations)
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
```

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 pathlib
from .utils import MemEditError
__author__ = 'Jan Petykiewicz'
with open(pathlib.Path(__file__).parent / 'VERSION', 'r') as f:
__version__ = f.read().strip()
version = __version__
system = platform.system()
if system == 'Windows':

View file

@ -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

View file

@ -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.

View file

@ -1,10 +1,18 @@
#!/usr/bin/env python
#!/usr/bin/env python3
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',
version='0.1',
version=version,
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',
@ -26,7 +34,6 @@ setup(name='mem_edit',
'trainer',
],
classifiers=[
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Development Status :: 4 - Beta',
'Environment :: Other Environment',
@ -42,8 +49,10 @@ setup(name='mem_edit',
'Topic :: Utilities',
],
packages=find_packages(),
package_data={
'mem_edit': ['VERSION']
},
install_requires=[
'ctypes',
'typing',
],
extras_require={