PyMemoryEditor 2.0.0__tar.gz → 2.0.1__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-2.0.0 → pymemoryeditor-2.0.1}/PKG-INFO +1 -1
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/__init__.py +1 -1
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/linux/functions.py +15 -2
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/linux/process.py +5 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/macos/functions.py +13 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/macos/process.py +5 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/process/abstract.py +43 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/util/__init__.py +1 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/util/convert.py +48 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/win32/functions.py +35 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/win32/process.py +6 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/api/openprocess.md +17 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/guide/read-write.md +54 -0
- pymemoryeditor-2.0.1/tests/memory/test_read_into.py +132 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/.flake8 +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/.gitignore +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/.readthedocs.yaml +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/CODE_OF_CONDUCT.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/CONTRIBUTING.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/LICENSE +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/Makefile +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/__main__.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/app/__init__.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/app/_auto_refresh_dialog.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/app/_icon.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/app/_widgets.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/app/application.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/app/assets/icon.svg +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/app/cheat_entry.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/app/cheat_poll_worker.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/app/cheat_table.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/app/log_console_dialog.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/app/main_window.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/app/memory_map_dialog.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/app/memory_viewer_dialog.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/app/modules_dialog.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/app/open_process_dialog.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/app/pointer_chain_dialog.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/app/pointer_scan_dialog.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/app/results_view.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/app/scan_types.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/app/scan_worker.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/app/scanner_panel.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/app/threads_dialog.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/app/value_types.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/enums.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/linux/libc.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/linux/types.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/macos/__init__.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/macos/libsystem.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/macos/types.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/process/__init__.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/process/errors.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/process/info.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/process/module_info.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/process/pointer_scan.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/process/region.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/process/remote_pointer.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/process/scanning.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/process/thread_info.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/process/util.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/py.typed +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/util/pattern.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/util/scan.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/util/scan_numpy.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/win32/enums/__init__.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/win32/enums/memory_allocation_states.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/win32/enums/memory_protections.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/win32/enums/memory_types.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/win32/enums/process_operations.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/win32/enums/standard_access_rights.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/win32/types.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/README.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/SECURITY.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/assets/screenshots/app.png +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/assets/social-preview.html +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/assets/social-preview.png +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/_static/.gitkeep +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/_static/custom.css +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/_templates/layout.html +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/api/enums.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/api/errors.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/api/index.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/api/memory-region.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/api/module-info.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/api/pointer-path.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/api/remote-pointer.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/api/thread-info.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/api/utilities.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/app.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/conf.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/contributing.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/funding.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/glossary.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/guide/allocate-free.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/guide/index.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/guide/logging.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/guide/memory-regions.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/guide/modules-threads.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/guide/opening-process.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/guide/pattern-scan.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/guide/pointer-scan.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/guide/pointers.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/guide/searching.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/index.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/installation.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/license.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/platform-notes.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/quickstart.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/requirements.txt +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/troubleshooting.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/docs/why.md +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/pyproject.toml +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/app/__init__.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/app/test_app_cheat_entry.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/app/test_app_pointer_parsing.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/app/test_app_scan_request.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/app/test_app_smoke.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/app/test_app_value_types.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/app/test_cheat_poll_worker.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/app/test_refine_scan_worker.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/conftest.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/core/__init__.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/core/test_errors.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/core/test_logger.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/memory/__init__.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/memory/test_allocate_free.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/memory/test_bufflength_inference.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/memory/test_editor.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/memory/test_partial_io.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/memory/test_str_boundary.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/memory/test_str_decode_consistency.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/memory/test_typed_accessors.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/memory/test_write_safety.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/memory/test_write_str_bytes_width.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/platforms/__init__.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/platforms/test_bitness.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/platforms/test_bitness_fallback_warns.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/platforms/test_linux_types.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/platforms/test_macos_protect.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/platforms/test_win32_permissions.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/pointer/__init__.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/pointer/test_pointer_chain.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/pointer/test_pointer_scan.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/pointer/test_remote_pointer.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/process/__init__.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/process/test_memory_region.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/process/test_module_enumeration.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/process/test_partial_name_match.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/process/test_process_enumeration.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/process/test_process_lookup.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/process/test_thread_enumeration.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/scan/__init__.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/scan/test_chunking_integration.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/scan/test_pattern_compile.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/scan/test_pattern_scan.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/scan/test_region_snapshot.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/scan/test_scan.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/scan/test_scan_numpy.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/scan/test_scan_properties.py +0 -0
- {pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/scan/test_scanning_helper.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyMemoryEditor
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.1
|
|
4
4
|
Summary: Read, write and scan process memory in a few lines of Python — Cheat Engine-style scans, pointer chains and AOB search on Windows, Linux and macOS.
|
|
5
5
|
Project-URL: Homepage, https://github.com/JeanExtreme002/PyMemoryEditor
|
|
6
6
|
Project-URL: Documentation, https://pymemoryeditor.readthedocs.io
|
|
@@ -30,6 +30,7 @@ from ..process.scanning import (
|
|
|
30
30
|
from ..process.thread_info import ThreadInfo
|
|
31
31
|
from ..util import (
|
|
32
32
|
_validate_pytype,
|
|
33
|
+
as_writable_c_buffer,
|
|
33
34
|
get_c_type_of,
|
|
34
35
|
values_to_bytes,
|
|
35
36
|
)
|
|
@@ -187,13 +188,13 @@ def get_memory_regions(pid: int) -> Generator["MemoryRegion", None, None]:
|
|
|
187
188
|
|
|
188
189
|
with mapping_file:
|
|
189
190
|
for line in mapping_file:
|
|
190
|
-
region_information = line.split()
|
|
191
|
+
region_information = line.split(maxsplit=5)
|
|
191
192
|
|
|
192
193
|
try:
|
|
193
194
|
addressing_range, privileges, offset, device, inode = (
|
|
194
195
|
region_information[0:5]
|
|
195
196
|
)
|
|
196
|
-
path = region_information[5] if len(region_information) >= 6 else ""
|
|
197
|
+
path = region_information[5].strip() if len(region_information) >= 6 else ""
|
|
197
198
|
|
|
198
199
|
start_address, end_address = [
|
|
199
200
|
int(addr, 16) for addr in addressing_range.split("-")
|
|
@@ -378,6 +379,18 @@ def read_process_memory(pid: int, address: int, pytype: Type[T], bufflength: int
|
|
|
378
379
|
return data.value
|
|
379
380
|
|
|
380
381
|
|
|
382
|
+
def read_process_memory_into(pid: int, address: int, buffer) -> int:
|
|
383
|
+
"""
|
|
384
|
+
Read ``len(buffer)`` bytes from ``address`` directly into the writable
|
|
385
|
+
``buffer``, with no intermediate allocation. Returns the number of bytes
|
|
386
|
+
read (always the buffer's byte length on success; a short read raises
|
|
387
|
+
``_LinuxPartialIOError``).
|
|
388
|
+
"""
|
|
389
|
+
c_buffer = as_writable_c_buffer(buffer)
|
|
390
|
+
size = len(c_buffer)
|
|
391
|
+
return _process_vm_readv(pid, addressof(c_buffer), address, size)
|
|
392
|
+
|
|
393
|
+
|
|
381
394
|
def search_addresses_by_value(
|
|
382
395
|
pid: int,
|
|
383
396
|
pytype: Type[T],
|
|
@@ -21,6 +21,7 @@ from .functions import (
|
|
|
21
21
|
get_modules,
|
|
22
22
|
get_threads,
|
|
23
23
|
read_process_memory,
|
|
24
|
+
read_process_memory_into,
|
|
24
25
|
search_addresses_by_pattern,
|
|
25
26
|
search_addresses_by_value,
|
|
26
27
|
search_values_by_addresses,
|
|
@@ -117,6 +118,10 @@ class LinuxProcess(AbstractProcess):
|
|
|
117
118
|
self.pid, address, pytype, resolve_bufflength(pytype, bufflength)
|
|
118
119
|
)
|
|
119
120
|
|
|
121
|
+
def read_process_memory_into(self, address: int, buffer: object) -> int:
|
|
122
|
+
self.__require_open()
|
|
123
|
+
return read_process_memory_into(self.pid, address, buffer)
|
|
124
|
+
|
|
120
125
|
def search_by_addresses(
|
|
121
126
|
self,
|
|
122
127
|
pytype: Type[T],
|
|
@@ -28,6 +28,7 @@ from ..process.scanning import (
|
|
|
28
28
|
from ..process.thread_info import ThreadInfo
|
|
29
29
|
from ..util import (
|
|
30
30
|
_validate_pytype,
|
|
31
|
+
as_writable_c_buffer,
|
|
31
32
|
get_c_type_of,
|
|
32
33
|
values_to_bytes,
|
|
33
34
|
)
|
|
@@ -545,6 +546,18 @@ def read_process_memory(
|
|
|
545
546
|
return data.value
|
|
546
547
|
|
|
547
548
|
|
|
549
|
+
def read_process_memory_into(task: int, address: int, buffer) -> int:
|
|
550
|
+
"""
|
|
551
|
+
Read ``len(buffer)`` bytes from ``address`` directly into the writable
|
|
552
|
+
``buffer``, with no intermediate allocation. Returns the number of bytes
|
|
553
|
+
read (always the buffer's byte length on success; a short read raises
|
|
554
|
+
``MachPartialReadError``).
|
|
555
|
+
"""
|
|
556
|
+
c_buffer = as_writable_c_buffer(buffer)
|
|
557
|
+
size = len(c_buffer)
|
|
558
|
+
return _mach_read(task, address, ctypes.addressof(c_buffer), size)
|
|
559
|
+
|
|
560
|
+
|
|
548
561
|
def write_process_memory(
|
|
549
562
|
task: int,
|
|
550
563
|
address: int,
|
|
@@ -25,6 +25,7 @@ from .functions import (
|
|
|
25
25
|
get_task_for_pid,
|
|
26
26
|
get_threads,
|
|
27
27
|
read_process_memory,
|
|
28
|
+
read_process_memory_into,
|
|
28
29
|
release_task,
|
|
29
30
|
search_addresses_by_pattern,
|
|
30
31
|
search_addresses_by_value,
|
|
@@ -292,6 +293,10 @@ class MacProcess(AbstractProcess):
|
|
|
292
293
|
self.__task, address, pytype, resolve_bufflength(pytype, bufflength)
|
|
293
294
|
)
|
|
294
295
|
|
|
296
|
+
def read_process_memory_into(self, address: int, buffer: object) -> int:
|
|
297
|
+
self.__require_open()
|
|
298
|
+
return read_process_memory_into(self.__task, address, buffer)
|
|
299
|
+
|
|
295
300
|
def write_process_memory(
|
|
296
301
|
self,
|
|
297
302
|
address: int,
|
|
@@ -449,6 +449,49 @@ class AbstractProcess(ABC):
|
|
|
449
449
|
"""
|
|
450
450
|
raise NotImplementedError()
|
|
451
451
|
|
|
452
|
+
@abstractmethod
|
|
453
|
+
def read_process_memory_into(self, address: int, buffer: object) -> int:
|
|
454
|
+
"""
|
|
455
|
+
Read ``len(buffer)`` bytes from ``address`` directly into a
|
|
456
|
+
pre-allocated, writable ``buffer`` and return the number of bytes read.
|
|
457
|
+
|
|
458
|
+
This is the zero-copy counterpart of :meth:`read_process_memory`: where
|
|
459
|
+
that method allocates a fresh object on every call, this one fills a
|
|
460
|
+
buffer you own and reuse. In a tight polling / recording loop that reads
|
|
461
|
+
the same-sized region over and over, reusing one buffer keeps memory use
|
|
462
|
+
constant instead of producing a stream of short-lived objects for the
|
|
463
|
+
garbage collector to reclaim.
|
|
464
|
+
|
|
465
|
+
:param address: target memory address (ex: 0x006A9EC0).
|
|
466
|
+
:param buffer: any writable, contiguous buffer-protocol object — a
|
|
467
|
+
``bytearray``, a ``ctypes`` array, a writable ``memoryview``, a
|
|
468
|
+
``numpy`` array, etc. Its byte length determines how many bytes are
|
|
469
|
+
read; an element-typed buffer (e.g. a ``numpy`` ``int32`` array) is
|
|
470
|
+
sized in **bytes**, not elements. The bytes are written in place; no
|
|
471
|
+
decoding is performed (this is the raw-``bytes`` read path). Decode
|
|
472
|
+
or reinterpret them yourself afterwards
|
|
473
|
+
(``int.from_bytes`` / ``struct.unpack`` / ``numpy`` views / ...).
|
|
474
|
+
:return: the number of bytes read — always equal to the buffer's byte
|
|
475
|
+
length on success (a short read raises instead, mirroring
|
|
476
|
+
:meth:`read_process_memory`).
|
|
477
|
+
|
|
478
|
+
:raises TypeError: if ``buffer`` is not a writable buffer (e.g. an
|
|
479
|
+
immutable ``bytes`` object).
|
|
480
|
+
:raises ValueError: if ``buffer`` is empty or not contiguous.
|
|
481
|
+
:raises OSError: if the read fails, or returns fewer bytes than
|
|
482
|
+
requested (e.g. the range crosses an unreadable/freed page).
|
|
483
|
+
|
|
484
|
+
Example
|
|
485
|
+
-------
|
|
486
|
+
::
|
|
487
|
+
|
|
488
|
+
buffer = bytearray(16)
|
|
489
|
+
while recording:
|
|
490
|
+
process.read_process_memory_into(addr, buffer)
|
|
491
|
+
handle(buffer) # same buffer reused every iteration
|
|
492
|
+
"""
|
|
493
|
+
raise NotImplementedError()
|
|
494
|
+
|
|
452
495
|
@abstractmethod
|
|
453
496
|
def write_process_memory(
|
|
454
497
|
self,
|
|
@@ -209,6 +209,54 @@ def prepare_write(
|
|
|
209
209
|
return pytype, length, value
|
|
210
210
|
|
|
211
211
|
|
|
212
|
+
def as_writable_c_buffer(buffer: Any) -> "ctypes.Array":
|
|
213
|
+
"""
|
|
214
|
+
Wrap a writable buffer-protocol object as a ``ctypes`` ``c_char`` array
|
|
215
|
+
that shares the same underlying storage, so a backend can read process
|
|
216
|
+
memory *directly into the caller's buffer* with no intermediate
|
|
217
|
+
allocation. Returning the ``ctypes`` array (rather than a bare address)
|
|
218
|
+
keeps a reference to the source alive for as long as the caller holds it,
|
|
219
|
+
which is what makes the address safe to pass to the OS read call.
|
|
220
|
+
|
|
221
|
+
Accepts anything exposing a writable, contiguous buffer — ``bytearray``,
|
|
222
|
+
a ``ctypes`` array, a writable ``memoryview``, a ``numpy`` array, etc. The
|
|
223
|
+
array's byte length is taken from the buffer itself (``memoryview.nbytes``),
|
|
224
|
+
so element-typed buffers like a ``numpy`` ``int32`` array are sized in
|
|
225
|
+
bytes, not elements.
|
|
226
|
+
|
|
227
|
+
:raises TypeError: if ``buffer`` does not support the buffer protocol or is
|
|
228
|
+
read-only (e.g. ``bytes`` — use ``bytearray`` instead).
|
|
229
|
+
:raises ValueError: if ``buffer`` is empty or not contiguous.
|
|
230
|
+
"""
|
|
231
|
+
try:
|
|
232
|
+
view = memoryview(buffer)
|
|
233
|
+
except TypeError:
|
|
234
|
+
raise TypeError(
|
|
235
|
+
"buffer must support the writable buffer protocol "
|
|
236
|
+
"(e.g. bytearray, a ctypes array, or a numpy array), got %s."
|
|
237
|
+
% type(buffer).__name__
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
try:
|
|
241
|
+
if view.readonly:
|
|
242
|
+
raise TypeError(
|
|
243
|
+
"buffer must be writable; got a read-only buffer "
|
|
244
|
+
"(e.g. bytes). Use bytearray or another writable buffer."
|
|
245
|
+
)
|
|
246
|
+
if not view.contiguous:
|
|
247
|
+
raise ValueError("buffer must be contiguous.")
|
|
248
|
+
nbytes = view.nbytes
|
|
249
|
+
finally:
|
|
250
|
+
# Release the inspection view promptly so it never lingers as an extra
|
|
251
|
+
# export on the source object (e.g. blocking a later bytearray resize).
|
|
252
|
+
view.release()
|
|
253
|
+
|
|
254
|
+
if nbytes == 0:
|
|
255
|
+
raise ValueError("buffer must have a non-zero length.")
|
|
256
|
+
|
|
257
|
+
return (ctypes.c_char * nbytes).from_buffer(buffer)
|
|
258
|
+
|
|
259
|
+
|
|
212
260
|
def convert_from_byte_array(
|
|
213
261
|
byte_array: ctypes.Array, pytype: Type[T], length: int
|
|
214
262
|
) -> T:
|
|
@@ -27,6 +27,7 @@ from ..process.scanning import (
|
|
|
27
27
|
from ..process.thread_info import ThreadInfo
|
|
28
28
|
from ..util import (
|
|
29
29
|
_validate_pytype,
|
|
30
|
+
as_writable_c_buffer,
|
|
30
31
|
get_c_type_of,
|
|
31
32
|
values_to_bytes,
|
|
32
33
|
)
|
|
@@ -501,6 +502,40 @@ def ReadProcessMemory(
|
|
|
501
502
|
return data.value
|
|
502
503
|
|
|
503
504
|
|
|
505
|
+
def ReadProcessMemoryInto(process_handle: int, address: int, buffer) -> int:
|
|
506
|
+
"""
|
|
507
|
+
Read ``len(buffer)`` bytes from ``address`` directly into the writable
|
|
508
|
+
``buffer``, with no intermediate allocation. Returns the number of bytes
|
|
509
|
+
read.
|
|
510
|
+
|
|
511
|
+
Raises OSError if the read fails or returns fewer bytes than requested
|
|
512
|
+
(same partial-read guard as :func:`ReadProcessMemory`).
|
|
513
|
+
"""
|
|
514
|
+
c_buffer = as_writable_c_buffer(buffer)
|
|
515
|
+
size = len(c_buffer)
|
|
516
|
+
bytes_read = ctypes.c_size_t(0)
|
|
517
|
+
|
|
518
|
+
ctypes.set_last_error(0)
|
|
519
|
+
success = kernel32.ReadProcessMemory(
|
|
520
|
+
process_handle,
|
|
521
|
+
ctypes.c_void_p(address),
|
|
522
|
+
ctypes.byref(c_buffer),
|
|
523
|
+
size,
|
|
524
|
+
ctypes.byref(bytes_read),
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
if not success:
|
|
528
|
+
_raise_last_error("ReadProcessMemory")
|
|
529
|
+
|
|
530
|
+
if bytes_read.value != size:
|
|
531
|
+
raise OSError(
|
|
532
|
+
"ReadProcessMemory partial read at 0x%X: %d of %d bytes read."
|
|
533
|
+
% (address, bytes_read.value, size)
|
|
534
|
+
)
|
|
535
|
+
|
|
536
|
+
return bytes_read.value
|
|
537
|
+
|
|
538
|
+
|
|
504
539
|
def _read_region(process_handle: int, address: int, size: int):
|
|
505
540
|
"""Read a memory region; returns the byte buffer or None on failure."""
|
|
506
541
|
region_data = (ctypes.c_byte * size)()
|
|
@@ -27,6 +27,7 @@ from .functions import (
|
|
|
27
27
|
GetProcessHandle,
|
|
28
28
|
GetThreads,
|
|
29
29
|
ReadProcessMemory,
|
|
30
|
+
ReadProcessMemoryInto,
|
|
30
31
|
SearchAddressesByPattern,
|
|
31
32
|
SearchAddressesByValue,
|
|
32
33
|
SearchValuesByAddresses,
|
|
@@ -313,6 +314,11 @@ class WindowsProcess(AbstractProcess):
|
|
|
313
314
|
resolve_bufflength(pytype, bufflength),
|
|
314
315
|
)
|
|
315
316
|
|
|
317
|
+
def read_process_memory_into(self, address: int, buffer: object) -> int:
|
|
318
|
+
self.__require_open()
|
|
319
|
+
self.__require_read()
|
|
320
|
+
return ReadProcessMemoryInto(self.__process_handle, address, buffer)
|
|
321
|
+
|
|
316
322
|
def write_process_memory(
|
|
317
323
|
self,
|
|
318
324
|
address: int,
|
|
@@ -158,6 +158,23 @@ with OpenProcess(
|
|
|
158
158
|
:returns: the original ``value`` you passed in — **not** the truncated/encoded
|
|
159
159
|
form actually written (a capped ``str``/``bytes`` write returns the full
|
|
160
160
|
original value).
|
|
161
|
+
|
|
162
|
+
.. py:method:: read_process_memory_into(address, buffer)
|
|
163
|
+
|
|
164
|
+
Read ``len(buffer)`` raw bytes from ``address`` directly into a
|
|
165
|
+
pre-allocated, writable ``buffer`` (no intermediate allocation) — the
|
|
166
|
+
zero-copy counterpart of :py:meth:`read_process_memory` for tight
|
|
167
|
+
read-the-same-region loops. See :doc:`../guide/read-write` for examples.
|
|
168
|
+
|
|
169
|
+
:param int address: target memory address.
|
|
170
|
+
:param buffer: any writable, contiguous buffer-protocol object
|
|
171
|
+
(``bytearray``, ``ctypes`` array, writable ``memoryview``, ``numpy``
|
|
172
|
+
array, …). Its byte length sets how many bytes are read; the bytes land
|
|
173
|
+
verbatim (no decoding).
|
|
174
|
+
:returns: the number of bytes read (the buffer's byte length on success).
|
|
175
|
+
:raises TypeError: if ``buffer`` is not a writable buffer (e.g. ``bytes``).
|
|
176
|
+
:raises ValueError: if ``buffer`` is empty or not contiguous.
|
|
177
|
+
:raises OSError: if the read fails or returns fewer bytes than requested.
|
|
161
178
|
```
|
|
162
179
|
|
|
163
180
|
### Typed shortcuts
|
|
@@ -195,6 +195,60 @@ Need the raw bytes with zero interpretation? Use `read_bytes(address, length)`
|
|
|
195
195
|
and `write_bytes(address, data)`.
|
|
196
196
|
```
|
|
197
197
|
|
|
198
|
+
## Reusing a buffer (zero-copy reads)
|
|
199
|
+
|
|
200
|
+
Every `read_process_memory` call **allocates a fresh Python object** for the
|
|
201
|
+
result. That's fine for one-off reads, but in a tight loop that reads the same
|
|
202
|
+
region thousands of times — a recorder, a live overlay, a poller — those
|
|
203
|
+
throwaway objects pile up and keep the garbage collector busy.
|
|
204
|
+
|
|
205
|
+
`read_process_memory_into(address, buffer)` reads straight into a buffer **you
|
|
206
|
+
own and reuse**, so a long-running loop runs with constant memory instead of a
|
|
207
|
+
steady stream of allocations:
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
buffer = bytearray(16) # allocate once
|
|
211
|
+
|
|
212
|
+
with OpenProcess(name="game.exe") as process:
|
|
213
|
+
while recording:
|
|
214
|
+
process.read_process_memory_into(0x7FF40010, buffer)
|
|
215
|
+
handle(buffer) # same buffer, refilled in place every loop
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
It fills **`len(buffer)` bytes** (the buffer's size decides how much is read)
|
|
219
|
+
and returns the number of bytes read. The bytes land verbatim — no decoding —
|
|
220
|
+
so reinterpret them yourself with `int.from_bytes`, `struct.unpack`, a `numpy`
|
|
221
|
+
view, and so on.
|
|
222
|
+
|
|
223
|
+
Any writable, contiguous buffer works — a `bytearray`, a `ctypes` array, a
|
|
224
|
+
writable `memoryview`, or a `numpy` array (sized in **bytes**, so a 4-element
|
|
225
|
+
`int32` array reads 16 bytes):
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
import numpy as np
|
|
229
|
+
|
|
230
|
+
frame = np.zeros(4, dtype=np.int32) # 16 bytes
|
|
231
|
+
process.read_process_memory_into(0x7FF40010, frame)
|
|
232
|
+
# frame now holds the four int32 values, no per-read allocation
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Method signature
|
|
236
|
+
|
|
237
|
+
```{eval-rst}
|
|
238
|
+
.. py:method:: read_process_memory_into(address, buffer)
|
|
239
|
+
:no-index:
|
|
240
|
+
|
|
241
|
+
:param int address: target memory address.
|
|
242
|
+
:param buffer: a writable, contiguous buffer-protocol object
|
|
243
|
+
(``bytearray``, ``ctypes`` array, writable ``memoryview``, ``numpy``
|
|
244
|
+
array, …). Its byte length sets how many bytes are read; the bytes are
|
|
245
|
+
written in place with no decoding.
|
|
246
|
+
:return: the number of bytes read (the buffer's byte length on success).
|
|
247
|
+
:raises TypeError: if ``buffer`` is not a writable buffer (e.g. ``bytes``).
|
|
248
|
+
:raises ValueError: if ``buffer`` is empty or not contiguous.
|
|
249
|
+
:raises OSError: if the read fails or returns fewer bytes than requested.
|
|
250
|
+
```
|
|
251
|
+
|
|
198
252
|
## Common errors
|
|
199
253
|
|
|
200
254
|
- **`OSError`** — the address may have been freed between scan and write, or
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Tests for ``read_process_memory_into`` — the zero-copy read that fills a
|
|
5
|
+
caller-owned, reusable buffer instead of allocating a fresh object per call
|
|
6
|
+
(GitHub issue #71).
|
|
7
|
+
|
|
8
|
+
As with the typed-accessor tests, we plant ctypes values on the test's own
|
|
9
|
+
heap and read them back through the public API.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import ctypes
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
|
|
16
|
+
import pytest
|
|
17
|
+
|
|
18
|
+
if sys.platform not in ("win32", "darwin") and not sys.platform.startswith("linux"):
|
|
19
|
+
pytest.skip("Platform not supported by PyMemoryEditor", allow_module_level=True)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
from PyMemoryEditor import OpenProcess # noqa: E402
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@pytest.fixture
|
|
26
|
+
def process():
|
|
27
|
+
handle = OpenProcess(pid=os.getpid())
|
|
28
|
+
try:
|
|
29
|
+
yield handle
|
|
30
|
+
finally:
|
|
31
|
+
handle.close()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_reads_into_bytearray(process):
|
|
35
|
+
source = (ctypes.c_uint8 * 4)(0xDE, 0xAD, 0xBE, 0xEF)
|
|
36
|
+
buffer = bytearray(4)
|
|
37
|
+
|
|
38
|
+
read = process.read_process_memory_into(ctypes.addressof(source), buffer)
|
|
39
|
+
|
|
40
|
+
assert read == 4
|
|
41
|
+
assert bytes(buffer) == b"\xde\xad\xbe\xef"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_returns_buffer_byte_length(process):
|
|
45
|
+
source = (ctypes.c_uint8 * 16)(*range(16))
|
|
46
|
+
buffer = bytearray(16)
|
|
47
|
+
|
|
48
|
+
assert process.read_process_memory_into(ctypes.addressof(source), buffer) == 16
|
|
49
|
+
assert bytes(buffer) == bytes(source)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_buffer_can_be_reused_across_reads(process):
|
|
53
|
+
"""The whole point of the API: one buffer, many reads, no new objects."""
|
|
54
|
+
first = (ctypes.c_uint8 * 8)(1, 2, 3, 4, 5, 6, 7, 8)
|
|
55
|
+
second = (ctypes.c_uint8 * 8)(8, 7, 6, 5, 4, 3, 2, 1)
|
|
56
|
+
buffer = bytearray(8)
|
|
57
|
+
|
|
58
|
+
process.read_process_memory_into(ctypes.addressof(first), buffer)
|
|
59
|
+
assert bytes(buffer) == bytes(first)
|
|
60
|
+
|
|
61
|
+
process.read_process_memory_into(ctypes.addressof(second), buffer)
|
|
62
|
+
assert bytes(buffer) == bytes(second)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_buffer_length_decides_how_many_bytes_are_read(process):
|
|
66
|
+
"""A 4-byte buffer reads exactly 4 bytes from an 8-byte source."""
|
|
67
|
+
source = (ctypes.c_uint8 * 8)(0xAA, 0xBB, 0xCC, 0xDD, 0x11, 0x22, 0x33, 0x44)
|
|
68
|
+
buffer = bytearray(4)
|
|
69
|
+
|
|
70
|
+
process.read_process_memory_into(ctypes.addressof(source), buffer)
|
|
71
|
+
|
|
72
|
+
assert bytes(buffer) == b"\xaa\xbb\xcc\xdd"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_reads_into_ctypes_array(process):
|
|
76
|
+
source = (ctypes.c_uint8 * 4)(0x01, 0x02, 0x03, 0x04)
|
|
77
|
+
destination = (ctypes.c_uint8 * 4)()
|
|
78
|
+
|
|
79
|
+
process.read_process_memory_into(ctypes.addressof(source), destination)
|
|
80
|
+
|
|
81
|
+
assert bytes(destination) == b"\x01\x02\x03\x04"
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_reads_into_writable_memoryview(process):
|
|
85
|
+
source = (ctypes.c_uint8 * 4)(0x10, 0x20, 0x30, 0x40)
|
|
86
|
+
backing = bytearray(4)
|
|
87
|
+
|
|
88
|
+
process.read_process_memory_into(ctypes.addressof(source), memoryview(backing))
|
|
89
|
+
|
|
90
|
+
assert bytes(backing) == b"\x10\x20\x30\x40"
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_element_typed_buffer_sized_in_bytes(process):
|
|
94
|
+
"""A 2-element int32 buffer is 8 bytes, not 2."""
|
|
95
|
+
source = (ctypes.c_int32 * 2)(0x01020304, 0x05060708)
|
|
96
|
+
destination = (ctypes.c_int32 * 2)()
|
|
97
|
+
|
|
98
|
+
read = process.read_process_memory_into(ctypes.addressof(source), destination)
|
|
99
|
+
|
|
100
|
+
assert read == 8
|
|
101
|
+
assert destination[0] == 0x01020304
|
|
102
|
+
assert destination[1] == 0x05060708
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_matches_read_process_memory(process):
|
|
106
|
+
"""The bytes filled in must equal the plain read_process_memory result."""
|
|
107
|
+
source = (ctypes.c_uint8 * 6)(0x09, 0x08, 0x07, 0x06, 0x05, 0x04)
|
|
108
|
+
addr = ctypes.addressof(source)
|
|
109
|
+
|
|
110
|
+
expected = process.read_process_memory(addr, bytes, 6)
|
|
111
|
+
buffer = bytearray(6)
|
|
112
|
+
process.read_process_memory_into(addr, buffer)
|
|
113
|
+
|
|
114
|
+
assert bytes(buffer) == expected
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def test_read_only_buffer_is_rejected(process):
|
|
118
|
+
source = (ctypes.c_uint8 * 4)(0, 0, 0, 0)
|
|
119
|
+
with pytest.raises(TypeError):
|
|
120
|
+
process.read_process_memory_into(ctypes.addressof(source), b"immutable")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def test_empty_buffer_is_rejected(process):
|
|
124
|
+
source = (ctypes.c_uint8 * 4)(0, 0, 0, 0)
|
|
125
|
+
with pytest.raises(ValueError):
|
|
126
|
+
process.read_process_memory_into(ctypes.addressof(source), bytearray(0))
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def test_non_buffer_is_rejected(process):
|
|
130
|
+
source = (ctypes.c_uint8 * 4)(0, 0, 0, 0)
|
|
131
|
+
with pytest.raises(TypeError):
|
|
132
|
+
process.read_process_memory_into(ctypes.addressof(source), 12345)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/win32/enums/memory_allocation_states.py
RENAMED
|
File without changes
|
{pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/win32/enums/memory_protections.py
RENAMED
|
File without changes
|
|
File without changes
|
{pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/win32/enums/process_operations.py
RENAMED
|
File without changes
|
{pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/PyMemoryEditor/win32/enums/standard_access_rights.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pymemoryeditor-2.0.0 → pymemoryeditor-2.0.1}/tests/platforms/test_bitness_fallback_warns.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|