PyMemoryEditor 1.5.7__tar.gz → 1.5.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/.github/workflows/python-package.yml +1 -1
  2. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PKG-INFO +15 -4
  3. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/__init__.py +1 -1
  4. pymemoryeditor-1.5.9/PyMemoryEditor/errors.py +6 -0
  5. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/linux/functions.py +68 -4
  6. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/linux/process.py +25 -4
  7. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/process/__init__.py +16 -1
  8. pymemoryeditor-1.5.9/PyMemoryEditor/sample/application.py +15 -0
  9. pymemoryeditor-1.5.9/PyMemoryEditor/sample/main_application_window.py +436 -0
  10. pymemoryeditor-1.5.9/PyMemoryEditor/sample/open_process_window.py +56 -0
  11. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/util/__init__.py +16 -1
  12. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/util/scan.py +3 -3
  13. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/win32/functions.py +71 -7
  14. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/win32/process.py +32 -6
  15. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/README.md +13 -2
  16. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/pyproject.toml +5 -2
  17. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/tests/test_editor.py +84 -19
  18. pymemoryeditor-1.5.7/PyMemoryEditor/errors.py +0 -3
  19. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/.gitignore +0 -0
  20. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/LICENSE +0 -0
  21. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/enums.py +0 -0
  22. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/linux/ptrace/__init__.py +0 -0
  23. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/linux/ptrace/enums.py +0 -0
  24. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/linux/types.py +0 -0
  25. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/process/errors.py +0 -0
  26. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/process/info.py +0 -0
  27. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/process/util.py +0 -0
  28. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/util/search/abstract.py +0 -0
  29. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/util/search/kmp.py +0 -0
  30. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/win32/enums/__init__.py +0 -0
  31. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/win32/enums/memory_allocation_states.py +0 -0
  32. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/win32/enums/memory_protections.py +0 -0
  33. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/win32/enums/memory_types.py +0 -0
  34. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/win32/enums/process_operations.py +0 -0
  35. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/win32/enums/standard_access_rights.py +0 -0
  36. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/win32/types.py +0 -0
  37. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/__init__.py +0 -0
  38. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/open_process.py +0 -0
  39. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/process/__init__.py +0 -0
  40. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/process/errors.py +0 -0
  41. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/process/util.py +0 -0
  42. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/win32/enums/__init__.py +0 -0
  43. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/win32/enums/memory_allocation_states.py +0 -0
  44. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/win32/enums/memory_protections.py +0 -0
  45. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/win32/enums/memory_types.py +0 -0
  46. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/win32/enums/process_operations.py +0 -0
  47. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/win32/enums/scan_types.py +0 -0
  48. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/win32/enums/standard_access_rights.py +0 -0
  49. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/win32/functions.py +0 -0
  50. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/win32/types.py +0 -0
  51. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/win32/util.py +0 -0
  52. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/requirements.txt +0 -0
  53. {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/tests/package.py +0 -0
@@ -34,7 +34,7 @@ jobs:
34
34
  pip install -r requirements.txt
35
35
  - name: Test with pytest
36
36
  run: |
37
- pytest tests -v -s
37
+ pytest tests -v -s -x
38
38
  - name: Install package
39
39
  run: |
40
40
  pip install PyMemoryEditor
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyMemoryEditor
3
- Version: 1.5.7
4
- Summary: Multi-platform library developed with ctypes for reading, writing and searching process memory, in a simple and friendly way with Python 3. It supports Windows and Linux (32-bit and 64-bit).
3
+ Version: 1.5.9
4
+ Summary: Multi-platform library developed with ctypes for reading, writing and searching process memory, in a simple and friendly way with Python 3.
5
5
  Project-URL: Homepage, https://github.com/JeanExtreme002/PyMemoryEditor
6
6
  Author-email: Jean Loui Bernard Silva de Jesus <jeanextreme002@gmail.com>
7
7
  License-Expression: MIT
@@ -47,6 +47,9 @@ reading and writing values in the process memory.
47
47
  pip3 install PyMemoryEditor
48
48
  ```
49
49
 
50
+ ### Tkinter application sample:
51
+ You can run a simple tkinter application to scan the memory of a process by typing `pymemoryeditor` at the CLI.
52
+
50
53
  # Basic Usage:
51
54
  Import `PyMemoryEditor` and open a process using the `OpenProcess` class, passing a window title, process name <br>
52
55
  or PID as an argument. You can use the context manager to do this.
@@ -103,14 +106,22 @@ All methods described above work even for strings, including the method `search_
103
106
  ## Progress information on searching:
104
107
  These methods has the `progress_information` parameter that returns a dictionary containing the search progress information.
105
108
  ```py
106
- for address, info process.search_by_value(..., progress_information = True):
109
+ for address, info in process.search_by_value(..., progress_information = True):
107
110
  template = "Address: 0x{:<10X} | Progress: {:.1f}%"
108
111
  progress = info["progress"] * 100
109
112
 
110
113
  print(template.format(address, progress))
111
114
  ```
112
115
 
113
- # Getting memory regions:
116
+ # Reading multiple addresses efficiently:
117
+ If you have a large number of addresses where their values need to be read from memory, using the `search_by_addresses` method is much more efficient than reading the value of each address one by one.
118
+ ```py
119
+ for address, value in process.search_by_addresses(int, 4, addresses_list):
120
+ print(f"Address", address, "holds the value", value)
121
+ ```
122
+ The key advantage of this method is that it reads a memory page just once, obtaining the values of the addresses within the page. This approach reduces the frequency of system calls.
123
+
124
+ ## Getting memory regions:
114
125
  Use the method `get_memory_regions()` to get the base address, size and more information of all memory regions used by the process.
115
126
 
116
127
  ```py
@@ -8,7 +8,7 @@ The package supports Windows and Linux (32-bit and 64-bit).
8
8
  """
9
9
 
10
10
  __author__ = "Jean Loui Bernard Silva de Jesus"
11
- __version__ = "1.5.7"
11
+ __version__ = "1.5.9"
12
12
 
13
13
 
14
14
  from .enums import ScanTypesEnum
@@ -0,0 +1,6 @@
1
+ from .process.errors import ProcessIDNotExistsError, ProcessNotFoundError
2
+
3
+
4
+ class ClosedProcess(Exception):
5
+ def __str__(self):
6
+ return "operation on closed process."
@@ -7,10 +7,10 @@
7
7
  # https://man7.org/linux/man-pages/man5/proc.5.html
8
8
 
9
9
  from ctypes import addressof, sizeof
10
- from typing import Generator, Tuple, Type, TypeVar, Union
10
+ from typing import Dict, Generator, Optional, Sequence, Tuple, Type, TypeVar, Union
11
11
 
12
12
  from ..enums import ScanTypesEnum
13
- from ..util import get_c_type_of, scan_memory, scan_memory_for_exact_value
13
+ from ..util import convert_from_bytearray, get_c_type_of, scan_memory, scan_memory_for_exact_value
14
14
  from .ptrace import libc
15
15
  from .types import MEMORY_BASIC_INFORMATION, iovec
16
16
 
@@ -67,10 +67,10 @@ def read_process_memory(
67
67
  pid, (iovec * 1)(iovec(addressof(data), sizeof(data))),
68
68
  1, (iovec * 1)(iovec(address, sizeof(data))), 1, 0
69
69
  )
70
- return str(data.value) if pytype is str else data.value
70
+ return data.value.decode() if pytype is str else data.value
71
71
 
72
72
 
73
- def search_all_memory(
73
+ def search_addresses_by_value(
74
74
  pid: int,
75
75
  pytype: Type[T],
76
76
  bufflength: int,
@@ -147,6 +147,70 @@ def search_all_memory(
147
147
  checked_memory_size += size
148
148
 
149
149
 
150
+ def search_values_by_addresses(
151
+ pid: int,
152
+ pytype: Type[T],
153
+ bufflength: int,
154
+ addresses: Sequence[int],
155
+ *,
156
+ memory_regions: Optional[Sequence[Dict]] = None,
157
+ raise_error: bool = False,
158
+ ) -> Generator[Tuple[int, Optional[T]], None, None]:
159
+ """
160
+ Search the whole memory space, accessible to the process,
161
+ for the provided list of addresses, returning their values.
162
+ """
163
+ if pytype not in [bool, int, float, str, bytes]:
164
+ raise ValueError("The type must be bool, int, float, str or bytes.")
165
+
166
+ memory_regions = list(memory_regions) if memory_regions else list()
167
+ addresses = sorted(addresses)
168
+
169
+ # If no memory page has been given, get all readable memory pages.
170
+ if not memory_regions:
171
+ for region in get_memory_regions(pid):
172
+ if not b"r" in region["struct"].Privileges: continue
173
+ memory_regions.append(region)
174
+
175
+ memory_regions.sort(key=lambda region: region["address"])
176
+ address_index = 0
177
+
178
+ # Walk by each memory region.
179
+ for region in memory_regions:
180
+ if address_index >= len(addresses): continue
181
+
182
+ target_address = addresses[address_index]
183
+
184
+ # Check if the memory region contains the target address.
185
+ base_address, size = region["address"], region["size"]
186
+ if not (base_address <= target_address < base_address + size): continue
187
+
188
+ region_data = (ctypes.c_byte * size)()
189
+
190
+ # Get data from the region.
191
+ libc.process_vm_readv(
192
+ pid, (iovec * 1)(iovec(addressof(region_data), sizeof(region_data))),
193
+ 1, (iovec * 1)(iovec(base_address, sizeof(region_data))), 1, 0
194
+ )
195
+
196
+ # Get the value of each address.
197
+ while base_address <= target_address < base_address + size:
198
+ offset = target_address - base_address
199
+ address_index += 1
200
+
201
+ try:
202
+ data = region_data[offset: offset + bufflength]
203
+ data = (ctypes.c_byte * bufflength)(*data)
204
+ yield target_address, convert_from_bytearray(data, pytype, bufflength)
205
+
206
+ except Exception as error:
207
+ if raise_error: raise error
208
+ yield target_address, None
209
+
210
+ if address_index >= len(addresses): break
211
+ target_address = addresses[address_index]
212
+
213
+
150
214
  def write_process_memory(
151
215
  pid: int,
152
216
  address: int,
@@ -3,8 +3,14 @@
3
3
  from ..enums import ScanTypesEnum
4
4
  from ..errors import ClosedProcess
5
5
  from ..process import AbstractProcess
6
- from .functions import get_memory_regions, read_process_memory, search_all_memory, write_process_memory
7
- from typing import Generator, Optional, Tuple, Type, TypeVar, Union
6
+ from .functions import (
7
+ get_memory_regions,
8
+ read_process_memory,
9
+ search_addresses_by_value,
10
+ search_values_by_addresses,
11
+ write_process_memory
12
+ )
13
+ from typing import Generator, Optional, Sequence, Tuple, Type, TypeVar, Union
8
14
 
9
15
 
10
16
  T = TypeVar("T")
@@ -68,6 +74,21 @@ class LinuxProcess(AbstractProcess):
68
74
 
69
75
  return read_process_memory(self.pid, address, pytype, bufflength)
70
76
 
77
+ def search_by_addresses(
78
+ self,
79
+ pytype: Type[T],
80
+ bufflength: int,
81
+ addresses: Sequence[int],
82
+ *,
83
+ raise_error: bool = False,
84
+ ) -> Generator[Tuple[int, Optional[T]], None, None]:
85
+ """
86
+ Search the whole memory space, accessible to the process,
87
+ for the provided list of addresses, returning their values.
88
+ """
89
+ if self.__closed: raise ClosedProcess()
90
+ return search_values_by_addresses(self.pid, pytype, bufflength, addresses, raise_error=raise_error)
91
+
71
92
  def search_by_value(
72
93
  self,
73
94
  pytype: Type[T],
@@ -94,7 +115,7 @@ class LinuxProcess(AbstractProcess):
94
115
  if scan_type in [ScanTypesEnum.VALUE_BETWEEN, ScanTypesEnum.NOT_VALUE_BETWEEN]:
95
116
  raise ValueError("Use the method search_by_value_between(...) to search within a range of values.")
96
117
 
97
- return search_all_memory(self.pid, pytype, bufflength, value, scan_type, progress_information, writeable_only)
118
+ return search_addresses_by_value(self.pid, pytype, bufflength, value, scan_type, progress_information, writeable_only)
98
119
 
99
120
  def search_by_value_between(
100
121
  self,
@@ -122,7 +143,7 @@ class LinuxProcess(AbstractProcess):
122
143
  if self.__closed: raise ClosedProcess()
123
144
 
124
145
  scan_type = ScanTypesEnum.NOT_VALUE_BETWEEN if not_between else ScanTypesEnum.VALUE_BETWEEN
125
- return search_all_memory(self.pid, pytype, bufflength, (start, end), scan_type, progress_information, writeable_only)
146
+ return search_addresses_by_value(self.pid, pytype, bufflength, (start, end), scan_type, progress_information, writeable_only)
126
147
 
127
148
  def write_process_memory(
128
149
  self,
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  from abc import ABC, abstractmethod
3
- from typing import Generator, Optional, Tuple, Type, TypeVar, Union
3
+ from typing import Generator, Optional, Sequence, Tuple, Type, TypeVar, Union
4
4
 
5
5
  from ..enums import ScanTypesEnum
6
6
  from ..process.info import ProcessInfo
@@ -61,6 +61,21 @@ class AbstractProcess(ABC):
61
61
  """
62
62
  raise NotImplementedError()
63
63
 
64
+ @abstractmethod
65
+ def search_by_addresses(
66
+ self,
67
+ pytype: Type[T],
68
+ bufflength: int,
69
+ addresses: Sequence[int],
70
+ *,
71
+ raise_error: bool = False,
72
+ ) -> Generator[Tuple[int, Optional[T]], None, None]:
73
+ """
74
+ Search the whole memory space, accessible to the process,
75
+ for the provided list of addresses, returning their values.
76
+ """
77
+ raise NotImplementedError()
78
+
64
79
  @abstractmethod
65
80
  def search_by_value(
66
81
  self,
@@ -0,0 +1,15 @@
1
+ from .main_application_window import ApplicationWindow
2
+ from .open_process_window import OpenProcessWindow
3
+
4
+
5
+ def main(*args, **kwargs):
6
+ open_process_window = OpenProcessWindow()
7
+ process = open_process_window.get_process()
8
+
9
+ if process:
10
+ ApplicationWindow(process)
11
+ process.close()
12
+
13
+
14
+ if __name__ == "__main__":
15
+ main()