forked from jan/mem_edit
Compare commits
No commits in common. "master" and "0.1" have entirely different histories.
9 changed files with 22 additions and 79 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -4,5 +4,4 @@ __pycache__
|
|||
*.pyc
|
||||
|
||||
*.egg-info/
|
||||
build/
|
||||
dist/
|
||||
|
|
|
|||
|
|
@ -1,3 +1,2 @@
|
|||
include README.md
|
||||
include LICENSE.md
|
||||
include mem_edit/VERSION
|
||||
|
|
|
|||
14
README.md
14
README.md
|
|
@ -18,19 +18,19 @@
|
|||
## Installation
|
||||
|
||||
**Dependencies:**
|
||||
* python 3 (written and tested with 3.7)
|
||||
* ctypes
|
||||
* typing (for type annotations)
|
||||
* python 3 (written and tested with 3.5)
|
||||
* ctypes
|
||||
* typing (for type annotations)
|
||||
|
||||
|
||||
Install with pip, from PyPI (preferred):
|
||||
```bash
|
||||
pip3 install mem_edit
|
||||
pip install mem_edit
|
||||
```
|
||||
|
||||
Install with pip from git repository
|
||||
```bash
|
||||
pip3 install git+https://mpxd.net/code/jan/mem_edit.git@release
|
||||
pip install git+https://mpxd.net/code/jan/mem_edit.git@release
|
||||
```
|
||||
|
||||
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
0.3
|
||||
|
|
@ -12,17 +12,12 @@ 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':
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ import copy
|
|||
import ctypes
|
||||
import logging
|
||||
|
||||
from . import utils
|
||||
from .utils import ctypes_buffer_t
|
||||
from .utils import ctypes_buffer_t, search_buffer, ctypes_equal
|
||||
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
|
@ -241,7 +240,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, verbatim: bool=True) -> List[int]:
|
||||
def search_addresses(self, addresses: List[int], needle_buffer: ctypes_buffer_t) -> List[int]:
|
||||
"""
|
||||
Search for the provided value at each of the provided addresses, and return the addresses
|
||||
where it is found.
|
||||
|
|
@ -250,26 +249,18 @@ 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:
|
||||
self.read_memory(address, read_buffer)
|
||||
if compare(needle_buffer, read_buffer):
|
||||
read = self.read_memory(address, read_buffer)
|
||||
if ctypes_equal(needle_buffer, read):
|
||||
found.append(address)
|
||||
return found
|
||||
|
||||
def search_all_memory(self, needle_buffer: ctypes_buffer_t, writeable_only: bool=True, verbatim: bool=True) -> List[int]:
|
||||
def search_all_memory(self, needle_buffer, writeable_only=True) -> List[int]:
|
||||
"""
|
||||
Search the entire memory space accessible to the process for the provided value.
|
||||
|
||||
|
|
@ -277,22 +268,14 @@ 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(needle_buffer, region_buffer)]
|
||||
found += [offset + start for offset in search_buffer(needle_buffer, region_buffer)]
|
||||
except OSError:
|
||||
logger.error('Failed to read in range 0x{} - 0x{}'.format(start, stop))
|
||||
return found
|
||||
|
|
|
|||
|
|
@ -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]:
|
||||
|
|
|
|||
|
|
@ -23,32 +23,9 @@ 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, using ctypes_equal for comparison.
|
||||
Much slower than search_buffer_verbatim.
|
||||
Search for a buffer inside another buffer.
|
||||
|
||||
:param needle_buffer: Buffer to search for.
|
||||
:param haystack_buffer: Buffer to search in.
|
||||
|
|
@ -69,7 +46,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):
|
||||
|
|
|
|||
17
setup.py
17
setup.py
|
|
@ -1,18 +1,10 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python
|
||||
|
||||
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=version,
|
||||
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',
|
||||
|
|
@ -34,6 +26,7 @@ setup(name='mem_edit',
|
|||
'trainer',
|
||||
],
|
||||
classifiers=[
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Development Status :: 4 - Beta',
|
||||
'Environment :: Other Environment',
|
||||
|
|
@ -49,10 +42,8 @@ setup(name='mem_edit',
|
|||
'Topic :: Utilities',
|
||||
],
|
||||
packages=find_packages(),
|
||||
package_data={
|
||||
'mem_edit': ['VERSION']
|
||||
},
|
||||
install_requires=[
|
||||
'ctypes',
|
||||
'typing',
|
||||
],
|
||||
extras_require={
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue