forked from jan/mem_edit
		
	modernize some code style
indentation, type annotations, f-strings
This commit is contained in:
		
							parent
							
								
									f03ea6acad
								
							
						
					
					
						commit
						e56ec88761
					
				@ -2,7 +2,7 @@
 | 
			
		||||
Abstract class for cross-platform memory editing.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from typing import List, Tuple, Optional, Union, Generator
 | 
			
		||||
from typing import Generator
 | 
			
		||||
from abc import ABCMeta, abstractmethod
 | 
			
		||||
from contextlib import contextmanager
 | 
			
		||||
import copy
 | 
			
		||||
@ -122,7 +122,7 @@ class Process(metaclass=ABCMeta):
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def __init__(self, process_id: int):
 | 
			
		||||
    def __init__(self, process_id: int) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Constructing a Process object prepares the process with specified process_id for
 | 
			
		||||
          memory editing. Finding the `process_id` for the process you want to edit is often
 | 
			
		||||
@ -135,7 +135,7 @@ class Process(metaclass=ABCMeta):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def close(self):
 | 
			
		||||
    def close(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Detach from the process, removing our ability to edit it and
 | 
			
		||||
          letting other debuggers attach to it instead.
 | 
			
		||||
@ -147,7 +147,11 @@ class Process(metaclass=ABCMeta):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def write_memory(self, base_address: int, write_buffer: ctypes_buffer_t):
 | 
			
		||||
    def write_memory(
 | 
			
		||||
            self,
 | 
			
		||||
            base_address: int,
 | 
			
		||||
            write_buffer: ctypes_buffer_t,
 | 
			
		||||
            ) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Write the given buffer to the process's address space, starting at `base_address`.
 | 
			
		||||
 | 
			
		||||
@ -160,7 +164,11 @@ class Process(metaclass=ABCMeta):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def read_memory(self, base_address: int, read_buffer: ctypes_buffer_t) -> ctypes_buffer_t:
 | 
			
		||||
    def read_memory(
 | 
			
		||||
            self,
 | 
			
		||||
            base_address: int,
 | 
			
		||||
            read_buffer: ctypes_buffer_t,
 | 
			
		||||
            ) -> ctypes_buffer_t:
 | 
			
		||||
        """
 | 
			
		||||
        Read into the given buffer from the process's address space, starting at `base_address`.
 | 
			
		||||
 | 
			
		||||
@ -176,7 +184,7 @@ class Process(metaclass=ABCMeta):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def list_mapped_regions(self, writeable_only=True) -> List[Tuple[int, int]]:
 | 
			
		||||
    def list_mapped_regions(self, writeable_only: bool = True) -> list[tuple[int, int]]:
 | 
			
		||||
        """
 | 
			
		||||
        Return a list of `(start_address, stop_address)` for the regions of the address space
 | 
			
		||||
          accessible to (readable and possibly writable by) the process.
 | 
			
		||||
@ -192,18 +200,18 @@ class Process(metaclass=ABCMeta):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def get_path(self) -> str:
 | 
			
		||||
    def get_path(self) -> str | None:
 | 
			
		||||
        """
 | 
			
		||||
        Return the path to the executable file which was run to start this process.
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            A string containing the path.
 | 
			
		||||
            A string containing the path, or None if no path was found.
 | 
			
		||||
        """
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def list_available_pids() -> List[int]:
 | 
			
		||||
    def list_available_pids() -> list[int]:
 | 
			
		||||
        """
 | 
			
		||||
        Return a list of all process ids (pids) accessible on this system.
 | 
			
		||||
 | 
			
		||||
@ -214,7 +222,7 @@ class Process(metaclass=ABCMeta):
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def get_pid_by_name(target_name: str) -> Optional[int]:
 | 
			
		||||
    def get_pid_by_name(target_name: str) -> int | None:
 | 
			
		||||
        """
 | 
			
		||||
        Attempt to return the process id (pid) of a process which was run with an executable
 | 
			
		||||
          file with the provided name. If no process is found, return None.
 | 
			
		||||
@ -234,10 +242,11 @@ class Process(metaclass=ABCMeta):
 | 
			
		||||
        """
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def deref_struct_pointer(self,
 | 
			
		||||
                             base_address: int,
 | 
			
		||||
                             targets: List[Tuple[int, ctypes_buffer_t]],
 | 
			
		||||
                             ) -> List[ctypes_buffer_t]:
 | 
			
		||||
    def deref_struct_pointer(
 | 
			
		||||
            self,
 | 
			
		||||
            base_address: int,
 | 
			
		||||
            targets: list[tuple[int, ctypes_buffer_t]],
 | 
			
		||||
            ) -> list[ctypes_buffer_t]:
 | 
			
		||||
        """
 | 
			
		||||
        Take a pointer to a struct and read out the struct members:
 | 
			
		||||
        ```
 | 
			
		||||
@ -263,11 +272,12 @@ 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,
 | 
			
		||||
            verbatim: bool = True,
 | 
			
		||||
            ) -> list[int]:
 | 
			
		||||
        """
 | 
			
		||||
        Search for the provided value at each of the provided addresses, and return the addresses
 | 
			
		||||
          where it is found.
 | 
			
		||||
@ -298,11 +308,12 @@ class Process(metaclass=ABCMeta):
 | 
			
		||||
                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: 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.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,6 @@
 | 
			
		||||
Implementation of Process class for Linux
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from typing import List, Tuple, Optional
 | 
			
		||||
from os import strerror
 | 
			
		||||
import os
 | 
			
		||||
import os.path
 | 
			
		||||
@ -35,41 +34,46 @@ _ptrace.argtypes = (ctypes.c_ulong,) * 4
 | 
			
		||||
_ptrace.restype = ctypes.c_long
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def ptrace(command: int, pid: int = 0, arg1: int = 0, arg2: int = 0) -> int:
 | 
			
		||||
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```.
 | 
			
		||||
    Call ptrace() with the provided pid and arguments. See `man ptrace`.
 | 
			
		||||
    """
 | 
			
		||||
    logger.debug('ptrace({}, {}, {}, {})'.format(command, pid, arg1, arg2))
 | 
			
		||||
    logger.debug(f'ptrace({command}, {pid}, {arg1}, {arg2})')
 | 
			
		||||
    result = _ptrace(command, pid, arg1, arg2)
 | 
			
		||||
    if result == -1:
 | 
			
		||||
        err_no = ctypes.get_errno()
 | 
			
		||||
        if err_no:
 | 
			
		||||
            raise MemEditError('ptrace({}, {}, {}, {})'.format(command, pid, arg1, arg2) +
 | 
			
		||||
                               ' failed with error {}: {}'.format(err_no, strerror(err_no)))
 | 
			
		||||
            raise MemEditError(f'ptrace({command}, {pid}, {arg1}, {arg2})'
 | 
			
		||||
                               + f' failed with error {err_no}: {strerror(err_no)}')
 | 
			
		||||
    return result
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Process(AbstractProcess):
 | 
			
		||||
    pid = None
 | 
			
		||||
    pid: int | None
 | 
			
		||||
 | 
			
		||||
    def __init__(self, process_id: int):
 | 
			
		||||
    def __init__(self, process_id: int) -> None:
 | 
			
		||||
        ptrace(ptrace_commands['PTRACE_SEIZE'], process_id)
 | 
			
		||||
        self.pid = process_id
 | 
			
		||||
 | 
			
		||||
    def close(self):
 | 
			
		||||
    def close(self) -> None:
 | 
			
		||||
        os.kill(self.pid, signal.SIGSTOP)
 | 
			
		||||
        os.waitpid(self.pid, 0)
 | 
			
		||||
        ptrace(ptrace_commands['PTRACE_DETACH'], self.pid, 0, 0)
 | 
			
		||||
        os.kill(self.pid, signal.SIGCONT)
 | 
			
		||||
        self.pid = None
 | 
			
		||||
 | 
			
		||||
    def write_memory(self, base_address: int, write_buffer: ctypes_buffer_t):
 | 
			
		||||
        with open('/proc/{}/mem'.format(self.pid), 'rb+') as mem:
 | 
			
		||||
    def write_memory(self, base_address: int, write_buffer: ctypes_buffer_t) -> None:
 | 
			
		||||
        with open(f'/proc/{self.pid}/mem', 'rb+') as mem:
 | 
			
		||||
            mem.seek(base_address)
 | 
			
		||||
            mem.write(write_buffer)
 | 
			
		||||
 | 
			
		||||
    def read_memory(self, base_address: int, read_buffer: ctypes_buffer_t) -> ctypes_buffer_t:
 | 
			
		||||
        with open('/proc/{}/mem'.format(self.pid), 'rb+') as mem:
 | 
			
		||||
        with open(f'/proc/{self.pid}/mem', 'rb+') as mem:
 | 
			
		||||
            mem.seek(base_address)
 | 
			
		||||
            mem.readinto(read_buffer)
 | 
			
		||||
        return read_buffer
 | 
			
		||||
@ -82,7 +86,7 @@ class Process(AbstractProcess):
 | 
			
		||||
            return ''
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def list_available_pids() -> List[int]:
 | 
			
		||||
    def list_available_pids() -> list[int]:
 | 
			
		||||
        pids = []
 | 
			
		||||
        for pid_str in os.listdir('/proc'):
 | 
			
		||||
            try:
 | 
			
		||||
@ -92,26 +96,26 @@ class Process(AbstractProcess):
 | 
			
		||||
        return pids
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get_pid_by_name(target_name: str) -> Optional[int]:
 | 
			
		||||
    def get_pid_by_name(target_name: str) -> int | None:
 | 
			
		||||
        for pid in Process.list_available_pids():
 | 
			
		||||
            try:
 | 
			
		||||
                logger.debug('Checking name for pid {}'.format(pid))
 | 
			
		||||
                with open('/proc/{}/cmdline'.format(pid), 'rb') as cmdline:
 | 
			
		||||
                logger.debug(f'Checking name for pid {pid}')
 | 
			
		||||
                with open(f'/proc/{pid}/cmdline', 'rb') as cmdline:
 | 
			
		||||
                    path = cmdline.read().decode().split('\x00')[0]
 | 
			
		||||
            except FileNotFoundError:
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            name = os.path.basename(path)
 | 
			
		||||
            logger.debug('Name was "{}"'.format(name))
 | 
			
		||||
            logger.debug(f'Name was "{name}"')
 | 
			
		||||
            if path is not None and name == target_name:
 | 
			
		||||
                return pid
 | 
			
		||||
 | 
			
		||||
        logger.info('Found no process with name {}'.format(target_name))
 | 
			
		||||
        logger.info(f'Found no process with name {target_name}')
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    def list_mapped_regions(self, writeable_only: bool = True) -> List[Tuple[int, int]]:
 | 
			
		||||
    def list_mapped_regions(self, writeable_only: bool = True) -> list[tuple[int, int]]:
 | 
			
		||||
        regions = []
 | 
			
		||||
        with open('/proc/{}/maps'.format(self.pid), 'r') as maps:
 | 
			
		||||
        with open(f'/proc/{self.pid}/maps', 'r') as maps:
 | 
			
		||||
            for line in maps:
 | 
			
		||||
                bounds, privileges = line.split()[0:2]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -11,21 +11,26 @@ Utility functions and types:
 | 
			
		||||
  Check if two buffers (ctypes objects) store equal values:
 | 
			
		||||
    ctypes_equal(a, b)
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from typing import List, Union
 | 
			
		||||
from typing import Union
 | 
			
		||||
import ctypes
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
ctypes_buffer_t = Union[ctypes._SimpleCData, ctypes.Array, ctypes.Structure, ctypes.Union]
 | 
			
		||||
ctypes_buffer_t = Union[
 | 
			
		||||
    ctypes._SimpleCData,
 | 
			
		||||
    ctypes.Array,
 | 
			
		||||
    ctypes.Structure,
 | 
			
		||||
    ctypes.Union,
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MemEditError(Exception):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def search_buffer_verbatim(needle_buffer: ctypes_buffer_t,
 | 
			
		||||
                           haystack_buffer: ctypes_buffer_t,
 | 
			
		||||
                           ) -> List[int]:
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
@ -50,9 +55,10 @@ def search_buffer_verbatim(needle_buffer: ctypes_buffer_t,
 | 
			
		||||
    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, using `ctypes_equal` for comparison.
 | 
			
		||||
    Much slower than `search_buffer_verbatim`.
 | 
			
		||||
@ -73,9 +79,10 @@ def search_buffer(needle_buffer: ctypes_buffer_t,
 | 
			
		||||
    return found
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def ctypes_equal(a: ctypes_buffer_t,
 | 
			
		||||
                 b: ctypes_buffer_t,
 | 
			
		||||
                 ) -> bool:
 | 
			
		||||
def ctypes_equal(
 | 
			
		||||
        a: ctypes_buffer_t,
 | 
			
		||||
        b: ctypes_buffer_t,
 | 
			
		||||
        ) -> bool:
 | 
			
		||||
    """
 | 
			
		||||
    Check if the values stored inside two ctypes buffers are equal.
 | 
			
		||||
    """
 | 
			
		||||
@ -87,7 +94,7 @@ def ctypes_equal(a: ctypes_buffer_t,
 | 
			
		||||
    elif isinstance(a, ctypes.Structure) or isinstance(a, ctypes.Union):
 | 
			
		||||
        for attr_name, attr_type in a._fields_:
 | 
			
		||||
            a_attr, b_attr = (getattr(x, attr_name) for x in (a, b))
 | 
			
		||||
            if isinstance(a, ctypes_buffer_t):
 | 
			
		||||
            if isinstance(a, (ctypes.Array, ctypes.Structure, ctypes.Union, ctypes._SimpleCData)):
 | 
			
		||||
                if not ctypes_equal(a_attr, b_attr):
 | 
			
		||||
                    return False
 | 
			
		||||
            elif not a_attr == b_attr:
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,6 @@
 | 
			
		||||
Implementation of Process class for Windows
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from typing import List, Tuple, Optional
 | 
			
		||||
from math import floor
 | 
			
		||||
from os import strerror
 | 
			
		||||
import os.path
 | 
			
		||||
@ -25,10 +24,10 @@ privileges = {
 | 
			
		||||
    'PROCESS_VM_WRITE': 0x0020,
 | 
			
		||||
    }
 | 
			
		||||
privileges['PROCESS_RW'] = (
 | 
			
		||||
    privileges['PROCESS_QUERY_INFORMATION'] |
 | 
			
		||||
    privileges['PROCESS_VM_OPERATION'] |
 | 
			
		||||
    privileges['PROCESS_VM_READ'] |
 | 
			
		||||
    privileges['PROCESS_VM_WRITE']
 | 
			
		||||
    privileges['PROCESS_QUERY_INFORMATION']
 | 
			
		||||
    | privileges['PROCESS_VM_OPERATION']
 | 
			
		||||
    | privileges['PROCESS_VM_READ']
 | 
			
		||||
    | privileges['PROCESS_VM_WRITE']
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
# Memory region states
 | 
			
		||||
@ -50,13 +49,13 @@ page_protections = {
 | 
			
		||||
    }
 | 
			
		||||
# Custom (combined) permissions
 | 
			
		||||
page_protections['PAGE_READABLE'] = (
 | 
			
		||||
    page_protections['PAGE_EXECUTE_READ'] |
 | 
			
		||||
    page_protections['PAGE_EXECUTE_READWRITE'] |
 | 
			
		||||
    page_protections['PAGE_READWRITE']
 | 
			
		||||
    page_protections['PAGE_EXECUTE_READ']
 | 
			
		||||
    | page_protections['PAGE_EXECUTE_READWRITE']
 | 
			
		||||
    | page_protections['PAGE_READWRITE']
 | 
			
		||||
    )
 | 
			
		||||
page_protections['PAGE_READWRITEABLE'] = (
 | 
			
		||||
    page_protections['PAGE_EXECUTE_READWRITE'] |
 | 
			
		||||
    page_protections['PAGE_READWRITE']
 | 
			
		||||
    page_protections['PAGE_EXECUTE_READWRITE']
 | 
			
		||||
    | page_protections['PAGE_READWRITE']
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
# Memory types
 | 
			
		||||
@ -91,7 +90,9 @@ class MEMORY_BASIC_INFORMATION64(ctypes.Structure):
 | 
			
		||||
        ('__alignment2', ctypes.wintypes.DWORD),
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PTR_SIZE = ctypes.sizeof(ctypes.c_void_p)
 | 
			
		||||
MEMORY_BASIC_INFORMATION: ctypes.Structure
 | 
			
		||||
if PTR_SIZE == 8:       # 64-bit python
 | 
			
		||||
    MEMORY_BASIC_INFORMATION = MEMORY_BASIC_INFORMATION64
 | 
			
		||||
elif PTR_SIZE == 4:     # 32-bit python
 | 
			
		||||
@ -133,9 +134,9 @@ class SYSTEM_INFO(ctypes.Structure):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Process(AbstractProcess):
 | 
			
		||||
    process_handle = None
 | 
			
		||||
    process_handle: int | None
 | 
			
		||||
 | 
			
		||||
    def __init__(self, process_id: int):
 | 
			
		||||
    def __init__(self, process_id: int) -> None:
 | 
			
		||||
        process_handle = ctypes.windll.kernel32.OpenProcess(
 | 
			
		||||
            privileges['PROCESS_RW'],
 | 
			
		||||
            False,
 | 
			
		||||
@ -143,15 +144,15 @@ class Process(AbstractProcess):
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        if not process_handle:
 | 
			
		||||
            raise MemEditError('Couldn\'t open process {}'.format(process_id))
 | 
			
		||||
            raise MemEditError(f'Couldn\'t open process {process_id}')
 | 
			
		||||
 | 
			
		||||
        self.process_handle = process_handle
 | 
			
		||||
 | 
			
		||||
    def close(self):
 | 
			
		||||
    def close(self) -> None:
 | 
			
		||||
        ctypes.windll.kernel32.CloseHandle(self.process_handle)
 | 
			
		||||
        self.process_handle = None
 | 
			
		||||
 | 
			
		||||
    def write_memory(self, base_address: int, write_buffer: ctypes_buffer_t):
 | 
			
		||||
    def write_memory(self, base_address: int, write_buffer: ctypes_buffer_t) -> None:
 | 
			
		||||
        try:
 | 
			
		||||
            ctypes.windll.kernel32.WriteProcessMemory(
 | 
			
		||||
                self.process_handle,
 | 
			
		||||
@ -161,7 +162,7 @@ class Process(AbstractProcess):
 | 
			
		||||
                None
 | 
			
		||||
                )
 | 
			
		||||
        except (BufferError, ValueError, TypeError):
 | 
			
		||||
            raise MemEditError('Error with handle {}:  {}'.format(self.process_handle, self._get_last_error()))
 | 
			
		||||
            raise MemEditError(f'Error with handle {self.process_handle}:  {self._get_last_error()}')
 | 
			
		||||
 | 
			
		||||
    def read_memory(self, base_address: int, read_buffer: ctypes_buffer_t) -> ctypes_buffer_t:
 | 
			
		||||
        try:
 | 
			
		||||
@ -173,22 +174,23 @@ class Process(AbstractProcess):
 | 
			
		||||
                None
 | 
			
		||||
                )
 | 
			
		||||
        except (BufferError, ValueError, TypeError):
 | 
			
		||||
            raise MemEditError('Error with handle {}: {}'.format(self.process_handle, self._get_last_error()))
 | 
			
		||||
            raise MemEditError(f'Error with handle {self.process_handle}: {self._get_last_error()}')
 | 
			
		||||
 | 
			
		||||
        return read_buffer
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _get_last_error() -> Tuple[int, str]:
 | 
			
		||||
    def _get_last_error() -> tuple[int, str]:
 | 
			
		||||
        err = ctypes.windll.kernel32.GetLastError()
 | 
			
		||||
        return err, strerror(err)
 | 
			
		||||
 | 
			
		||||
    def get_path(self) -> str:
 | 
			
		||||
    def get_path(self) -> str | None:
 | 
			
		||||
        max_path_len = 260
 | 
			
		||||
        name_buffer = (ctypes.c_char * max_path_len)()
 | 
			
		||||
        rval = ctypes.windll.psapi.GetProcessImageFileNameA(
 | 
			
		||||
             self.process_handle,
 | 
			
		||||
             name_buffer,
 | 
			
		||||
             max_path_len)
 | 
			
		||||
            self.process_handle,
 | 
			
		||||
            name_buffer,
 | 
			
		||||
            max_path_len,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        if rval > 0:
 | 
			
		||||
            return name_buffer.value.decode()
 | 
			
		||||
@ -196,28 +198,28 @@ class Process(AbstractProcess):
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def list_available_pids() -> List[int]:
 | 
			
		||||
    def list_available_pids() -> list[int]:
 | 
			
		||||
        # According to EnumProcesses docs, you can't find out how many processes there are before
 | 
			
		||||
        #  fetching the list. As a result, we grab 100 on the first try, and if we get a full list
 | 
			
		||||
        #  of 100, repeatedly double the number until we get fewer than we asked for.
 | 
			
		||||
 | 
			
		||||
        n = 100
 | 
			
		||||
        nn = 100
 | 
			
		||||
        returned_size = ctypes.wintypes.DWORD()
 | 
			
		||||
        returned_size_ptr = ctypes.byref(returned_size)
 | 
			
		||||
 | 
			
		||||
        while True:
 | 
			
		||||
            pids = (ctypes.wintypes.DWORD * n)()
 | 
			
		||||
            pids = (ctypes.wintypes.DWORD * nn)()
 | 
			
		||||
            size = ctypes.sizeof(pids)
 | 
			
		||||
            pids_ptr = ctypes.byref(pids)
 | 
			
		||||
 | 
			
		||||
            success = ctypes.windll.Psapi.EnumProcesses(pids_ptr, size, returned_size_ptr)
 | 
			
		||||
            if not success:
 | 
			
		||||
                raise MemEditError('Failed to enumerate processes: n={}'.format(n))
 | 
			
		||||
                raise MemEditError(f'Failed to enumerate processes: nn={nn}')
 | 
			
		||||
 | 
			
		||||
            num_returned = floor(returned_size.value / ctypes.sizeof(ctypes.wintypes.DWORD))
 | 
			
		||||
 | 
			
		||||
            if n == num_returned:
 | 
			
		||||
                n *= 2
 | 
			
		||||
            if nn == num_returned:
 | 
			
		||||
                nn *= 2
 | 
			
		||||
                continue
 | 
			
		||||
            else:
 | 
			
		||||
                break
 | 
			
		||||
@ -225,15 +227,15 @@ class Process(AbstractProcess):
 | 
			
		||||
        return pids[:num_returned]
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get_pid_by_name(target_name: str) -> Optional[int]:
 | 
			
		||||
    def get_pid_by_name(target_name: str) -> int | None:
 | 
			
		||||
        for pid in Process.list_available_pids():
 | 
			
		||||
            try:
 | 
			
		||||
                logger.debug('Checking name for pid {}'.format(pid))
 | 
			
		||||
                logger.debug(f'Checking name for pid {pid}')
 | 
			
		||||
                with Process.open_process(pid) as process:
 | 
			
		||||
                    path = process.get_path()
 | 
			
		||||
 | 
			
		||||
                name = os.path.basename(path)
 | 
			
		||||
                logger.debug('Name was "{}"'.format(name))
 | 
			
		||||
                logger.debug(f'Name was "{name}"')
 | 
			
		||||
                if path is not None and name == target_name:
 | 
			
		||||
                    return pid
 | 
			
		||||
            except ValueError:
 | 
			
		||||
@ -241,10 +243,10 @@ class Process(AbstractProcess):
 | 
			
		||||
            except MemEditError as err:
 | 
			
		||||
                logger.debug(repr(err))
 | 
			
		||||
 | 
			
		||||
        logger.info('Found no process with name {}'.format(target_name))
 | 
			
		||||
        logger.info(f'Found no process with name {target_name}')
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    def list_mapped_regions(self, writeable_only: bool = True) -> List[Tuple[int, int]]:
 | 
			
		||||
    def list_mapped_regions(self, writeable_only: bool = True) -> list[tuple[int, int]]:
 | 
			
		||||
        sys_info = SYSTEM_INFO()
 | 
			
		||||
        sys_info_ptr = ctypes.byref(sys_info)
 | 
			
		||||
        ctypes.windll.kernel32.GetSystemInfo(sys_info_ptr)
 | 
			
		||||
@ -268,8 +270,8 @@ class Process(AbstractProcess):
 | 
			
		||||
 | 
			
		||||
            if success != mbi_size:
 | 
			
		||||
                if success == 0:
 | 
			
		||||
                    raise MemEditError('Failed VirtualQueryEx with handle ' +
 | 
			
		||||
                                       '{}: {}'.format(self.process_handle, self._get_last_error()))
 | 
			
		||||
                    raise MemEditError('Failed VirtualQueryEx with handle '
 | 
			
		||||
                                       + f'{self.process_handle}: {self._get_last_error()}')
 | 
			
		||||
                else:
 | 
			
		||||
                    raise MemEditError('VirtualQueryEx output too short!')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user