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.
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/.github/workflows/python-package.yml +1 -1
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PKG-INFO +15 -4
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/__init__.py +1 -1
- pymemoryeditor-1.5.9/PyMemoryEditor/errors.py +6 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/linux/functions.py +68 -4
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/linux/process.py +25 -4
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/process/__init__.py +16 -1
- pymemoryeditor-1.5.9/PyMemoryEditor/sample/application.py +15 -0
- pymemoryeditor-1.5.9/PyMemoryEditor/sample/main_application_window.py +436 -0
- pymemoryeditor-1.5.9/PyMemoryEditor/sample/open_process_window.py +56 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/util/__init__.py +16 -1
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/util/scan.py +3 -3
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/win32/functions.py +71 -7
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/win32/process.py +32 -6
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/README.md +13 -2
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/pyproject.toml +5 -2
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/tests/test_editor.py +84 -19
- pymemoryeditor-1.5.7/PyMemoryEditor/errors.py +0 -3
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/.gitignore +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/LICENSE +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/enums.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/linux/ptrace/__init__.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/linux/ptrace/enums.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/linux/types.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/process/errors.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/process/info.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/process/util.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/util/search/abstract.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/util/search/kmp.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/win32/enums/__init__.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/win32/enums/memory_allocation_states.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/win32/enums/memory_protections.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/win32/enums/memory_types.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/win32/enums/process_operations.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/win32/enums/standard_access_rights.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/PyMemoryEditor/win32/types.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/__init__.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/open_process.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/process/__init__.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/process/errors.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/process/util.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/win32/enums/__init__.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/win32/enums/memory_allocation_states.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/win32/enums/memory_protections.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/win32/enums/memory_types.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/win32/enums/process_operations.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/win32/enums/scan_types.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/win32/enums/standard_access_rights.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/win32/functions.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/win32/types.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/build/lib/PyMemoryEditor/win32/util.py +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/requirements.txt +0 -0
- {pymemoryeditor-1.5.7 → pymemoryeditor-1.5.9}/tests/package.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PyMemoryEditor
|
|
3
|
-
Version: 1.5.
|
|
4
|
-
Summary: Multi-platform library developed with ctypes for reading, writing and searching process memory, in a simple and friendly way with Python 3.
|
|
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
|
-
#
|
|
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
|
|
@@ -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
|
|
70
|
+
return data.value.decode() if pytype is str else data.value
|
|
71
71
|
|
|
72
72
|
|
|
73
|
-
def
|
|
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
|
|
7
|
-
|
|
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
|
|
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
|
|
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()
|