PyMemoryEditor 2.2.0__tar.gz → 3.0.0__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.2.0 → pymemoryeditor-3.0.0}/PKG-INFO +61 -17
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/__init__.py +1 -1
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/linux/functions.py +18 -2
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/macos/functions.py +39 -2
- pymemoryeditor-3.0.0/PyMemoryEditor/mcp/__init__.py +68 -0
- pymemoryeditor-3.0.0/PyMemoryEditor/mcp/__main__.py +17 -0
- pymemoryeditor-3.0.0/PyMemoryEditor/mcp/config.py +301 -0
- pymemoryeditor-3.0.0/PyMemoryEditor/mcp/policy.py +311 -0
- pymemoryeditor-3.0.0/PyMemoryEditor/mcp/server.py +513 -0
- pymemoryeditor-3.0.0/PyMemoryEditor/mcp/session.py +545 -0
- pymemoryeditor-3.0.0/PyMemoryEditor/mcp/toolset.py +2231 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/process/abstract.py +24 -6
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/process/scanning.py +17 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/process/util.py +19 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/util/__init__.py +3 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/util/convert.py +154 -10
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/util/scan.py +20 -10
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/win32/functions.py +4 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/README.md +56 -15
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/api/utilities.md +39 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/guide/searching.md +8 -3
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/index.md +2 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/installation.md +44 -0
- pymemoryeditor-3.0.0/docs/mcp.md +323 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/pyproject.toml +37 -3
- pymemoryeditor-3.0.0/tests/conftest.py +33 -0
- pymemoryeditor-3.0.0/tests/mcp_server/__init__.py +1 -0
- pymemoryeditor-3.0.0/tests/mcp_server/conftest.py +416 -0
- pymemoryeditor-3.0.0/tests/mcp_server/test_argument_fuzz.py +451 -0
- pymemoryeditor-3.0.0/tests/mcp_server/test_fake_fidelity.py +739 -0
- pymemoryeditor-3.0.0/tests/mcp_server/test_live.py +232 -0
- pymemoryeditor-3.0.0/tests/mcp_server/test_parsing.py +739 -0
- pymemoryeditor-3.0.0/tests/mcp_server/test_policy.py +228 -0
- pymemoryeditor-3.0.0/tests/mcp_server/test_server.py +736 -0
- pymemoryeditor-3.0.0/tests/mcp_server/test_session.py +590 -0
- pymemoryeditor-3.0.0/tests/mcp_server/test_toolset.py +1720 -0
- pymemoryeditor-3.0.0/tests/memory/test_narrow_width_consistency.py +222 -0
- pymemoryeditor-3.0.0/tests/memory/test_write_span.py +115 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/platforms/test_macos_transient_reads.py +39 -0
- pymemoryeditor-2.2.0/tests/conftest.py +0 -5
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/.flake8 +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/.gitignore +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/.readthedocs.yaml +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/CODE_OF_CONDUCT.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/CONTRIBUTING.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/LICENSE +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/Makefile +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/__main__.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/app/__init__.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/app/_auto_refresh_dialog.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/app/_icon.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/app/_widgets.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/app/application.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/app/assets/icon.svg +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/app/cheat_entry.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/app/cheat_poll_worker.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/app/cheat_table.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/app/log_console_dialog.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/app/main_window.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/app/memory_map_dialog.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/app/memory_viewer_dialog.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/app/modules_dialog.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/app/open_process_dialog.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/app/pointer_chain_dialog.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/app/pointer_scan_dialog.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/app/results_view.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/app/scan_types.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/app/scan_worker.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/app/scanner_panel.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/app/threads_dialog.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/app/value_types.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/enums.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/linux/libc.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/linux/process.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/linux/types.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/macos/__init__.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/macos/libsystem.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/macos/process.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/macos/types.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/process/__init__.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/process/errors.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/process/info.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/process/module_info.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/process/pointer_scan.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/process/region.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/process/remote_pointer.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/process/thread_info.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/py.typed +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/util/pattern.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/util/scan_numpy.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/win32/enums/__init__.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/win32/enums/memory_allocation_states.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/win32/enums/memory_protections.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/win32/enums/memory_types.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/win32/enums/process_operations.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/win32/enums/standard_access_rights.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/win32/process.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/PyMemoryEditor/win32/types.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/SECURITY.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/assets/screenshots/app.png +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/assets/social-preview.html +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/assets/social-preview.png +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/_static/.gitkeep +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/_static/custom.css +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/_templates/layout.html +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/api/enums.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/api/errors.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/api/index.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/api/memory-region.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/api/module-info.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/api/openprocess.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/api/pointer-path.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/api/remote-pointer.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/api/thread-info.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/app.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/conf.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/contributing.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/funding.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/glossary.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/guide/allocate-free.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/guide/index.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/guide/logging.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/guide/memory-regions.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/guide/modules-threads.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/guide/opening-process.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/guide/pattern-scan.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/guide/pointer-scan.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/guide/pointers.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/guide/read-write.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/license.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/platform-notes.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/quickstart.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/requirements.txt +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/troubleshooting.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/docs/why.md +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/app/__init__.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/app/test_app_cheat_entry.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/app/test_app_pointer_parsing.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/app/test_app_scan_request.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/app/test_app_smoke.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/app/test_app_value_types.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/app/test_app_widgets.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/app/test_auto_refresh_dialog.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/app/test_cheat_poll_worker.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/app/test_open_process_dialog.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/app/test_refine_scan_worker.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/core/__init__.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/core/test_errors.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/core/test_logger.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/memory/__init__.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/memory/test_allocate_free.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/memory/test_bufflength_inference.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/memory/test_editor.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/memory/test_partial_io.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/memory/test_read_into.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/memory/test_str_boundary.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/memory/test_str_decode_consistency.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/memory/test_typed_accessors.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/memory/test_write_safety.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/memory/test_write_str_bytes_width.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/platforms/__init__.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/platforms/test_bitness.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/platforms/test_bitness_fallback_warns.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/platforms/test_linux_types.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/platforms/test_macos_protect.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/platforms/test_win32_permissions.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/pointer/__init__.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/pointer/test_pointer_chain.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/pointer/test_pointer_scan.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/pointer/test_remote_pointer.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/process/__init__.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/process/test_memory_region.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/process/test_module_enumeration.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/process/test_partial_name_match.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/process/test_process_enumeration.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/process/test_process_lookup.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/process/test_thread_enumeration.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/scan/__init__.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/scan/test_chunking_integration.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/scan/test_pattern_compile.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/scan/test_pattern_scan.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/scan/test_region_snapshot.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/scan/test_scan.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/scan/test_scan_numpy.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/scan/test_scan_properties.py +0 -0
- {pymemoryeditor-2.2.0 → pymemoryeditor-3.0.0}/tests/scan/test_scanning_helper.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: PyMemoryEditor
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
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
|
|
@@ -12,7 +12,7 @@ Author-email: Jean Loui Bernard Silva de Jesus <contact@jeanloui.dev>
|
|
|
12
12
|
Maintainer-email: Jean Loui Bernard Silva de Jesus <contact@jeanloui.dev>
|
|
13
13
|
License-Expression: MIT
|
|
14
14
|
License-File: LICENSE
|
|
15
|
-
Keywords: aob-scan,cheat-engine,ctypes,debugging,game-hacking,introspection,memory-editor,memory-scanner,pattern-scan,pointer-scan,process-memory,reverse-engineering
|
|
15
|
+
Keywords: ai-agent,aob-scan,cheat-engine,ctypes,debugging,game-hacking,introspection,llm-tools,mcp,memory-editor,memory-scanner,model-context-protocol,pattern-scan,pointer-scan,process-memory,reverse-engineering
|
|
16
16
|
Classifier: Development Status :: 5 - Production/Stable
|
|
17
17
|
Classifier: Intended Audience :: Developers
|
|
18
18
|
Classifier: Intended Audience :: Information Technology
|
|
@@ -41,6 +41,7 @@ Provides-Extra: dev
|
|
|
41
41
|
Requires-Dist: build; extra == 'dev'
|
|
42
42
|
Requires-Dist: flake8; extra == 'dev'
|
|
43
43
|
Requires-Dist: hypothesis; extra == 'dev'
|
|
44
|
+
Requires-Dist: mcp<3,>=2; extra == 'dev'
|
|
44
45
|
Requires-Dist: mypy; extra == 'dev'
|
|
45
46
|
Requires-Dist: psutil<7,>=5.9; extra == 'dev'
|
|
46
47
|
Requires-Dist: pyside6>=6.5; extra == 'dev'
|
|
@@ -49,6 +50,8 @@ Requires-Dist: pytest-cov; extra == 'dev'
|
|
|
49
50
|
Requires-Dist: pytest-qt; extra == 'dev'
|
|
50
51
|
Requires-Dist: pytest-xdist; extra == 'dev'
|
|
51
52
|
Requires-Dist: twine; extra == 'dev'
|
|
53
|
+
Provides-Extra: mcp
|
|
54
|
+
Requires-Dist: mcp<3,>=2; extra == 'mcp'
|
|
52
55
|
Provides-Extra: speed
|
|
53
56
|
Requires-Dist: numpy>=1.24; extra == 'speed'
|
|
54
57
|
Provides-Extra: tests
|
|
@@ -71,18 +74,19 @@ A pure-Python library (built on [ctypes](https://docs.python.org/3/library/ctype
|
|
|
71
74
|
<i>One unified API. Three operating systems. No C compiler. No native build step.</i>
|
|
72
75
|
</p>
|
|
73
76
|
|
|
74
|
-
<p align="center">
|
|
75
|
-
Runs on <b>🪟 Windows</b> · <b>🐧 Linux</b> · <b>🍎 macOS</b> — 32-bit and 64-bit.
|
|
76
|
-
</p>
|
|
77
|
-
|
|
78
77
|
<p align="center">
|
|
79
78
|
<a href="https://github.com/JeanExtreme002/PyMemoryEditor/actions/workflows/python-package.yml"><img src="https://github.com/JeanExtreme002/PyMemoryEditor/actions/workflows/python-package.yml/badge.svg" alt="Python Package" /></a>
|
|
79
|
+
<a href="https://codecov.io/gh/JeanExtreme002/PyMemoryEditor"><img src="https://codecov.io/gh/JeanExtreme002/PyMemoryEditor/branch/main/graph/badge.svg" alt="Coverage" /></a>
|
|
80
80
|
<a href="https://pypi.org/project/PyMemoryEditor/"><img src="https://img.shields.io/pypi/v/PyMemoryEditor" alt="Pypi" /></a>
|
|
81
81
|
<a href="https://github.com/JeanExtreme002/PyMemoryEditor"><img src="https://img.shields.io/pypi/l/PyMemoryEditor" alt="License" /></a>
|
|
82
82
|
<a href="https://github.com/JeanExtreme002/PyMemoryEditor"><img src="https://img.shields.io/badge/python-3.10+-8A2BE2" alt="Python Version" /></a>
|
|
83
83
|
<a href="https://pypi.org/project/PyMemoryEditor/"><img src="https://static.pepy.tech/personalized-badge/pymemoryeditor?period=total&units=international_system&left_color=grey&right_color=orange&left_text=Downloads" alt="Downloads" /></a>
|
|
84
84
|
</p>
|
|
85
85
|
|
|
86
|
+
<p align="center">
|
|
87
|
+
Runs on <b>Windows</b> · <b>Linux</b> · <b>macOS</b> — 32-bit and 64-bit.
|
|
88
|
+
</p>
|
|
89
|
+
|
|
86
90
|
<p align="center">
|
|
87
91
|
<img src="https://raw.githubusercontent.com/JeanExtreme002/PyMemoryEditor/main/assets/screenshots/app.png" alt="PyMemoryEditor app attached to a running process" width="820" />
|
|
88
92
|
</p>
|
|
@@ -114,6 +118,13 @@ selective scans:
|
|
|
114
118
|
```bash
|
|
115
119
|
pip install "PyMemoryEditor[speed]"
|
|
116
120
|
```
|
|
121
|
+
|
|
122
|
+
To let an AI assistant drive the library over the Model Context Protocol, use
|
|
123
|
+
the `mcp` extra (see [below](#let-an-ai-assistant-do-it-mcp)):
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
pip install "PyMemoryEditor[mcp]"
|
|
127
|
+
```
|
|
117
128
|
📖 Full guide at **[Read the Docs](https://pymemoryeditor.readthedocs.io)**.
|
|
118
129
|
|
|
119
130
|
---
|
|
@@ -138,6 +149,38 @@ That's it — read, write or scan another process in three lines, the same way o
|
|
|
138
149
|
|
|
139
150
|
---
|
|
140
151
|
|
|
152
|
+
## Let an AI assistant do it (MCP)
|
|
153
|
+
|
|
154
|
+
PyMemoryEditor ships a **Model Context Protocol server**, so an AI assistant can run the whole loop itself:
|
|
155
|
+
|
|
156
|
+
> *"Find the health value in my game. It's 100 right now."*
|
|
157
|
+
>
|
|
158
|
+
> It scans, asks you to take damage, refines the matches, and hands you the address.
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
pip install "PyMemoryEditor[mcp]"
|
|
162
|
+
claude mcp add pymemoryeditor -- pymemoryeditor-mcp
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Or in any client that reads `mcpServers` JSON (Claude Desktop, editors, …):
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
{
|
|
169
|
+
"mcpServers": {
|
|
170
|
+
"pymemoryeditor": {
|
|
171
|
+
"command": "pymemoryeditor-mcp",
|
|
172
|
+
"args": []
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Fourteen tools cover the full workflow, with scan results kept server-side behind a handle, so a 40 000-hit first scan costs a few tokens instead of your whole context.
|
|
179
|
+
|
|
180
|
+
Read the [MCP guide](https://pymemoryeditor.readthedocs.io/en/latest/mcp.html) to learn more.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
141
184
|
## 📖 Documentation
|
|
142
185
|
|
|
143
186
|
Full documentation lives at **[pymemoryeditor.readthedocs.io](https://pymemoryeditor.readthedocs.io)** — installation, the Cheat Engine workflow, every method and parameter, the GUI app guide, platform notes and troubleshooting.
|
|
@@ -145,15 +188,16 @@ Full documentation lives at **[pymemoryeditor.readthedocs.io](https://pymemoryed
|
|
|
145
188
|
A quick map of where to go:
|
|
146
189
|
|
|
147
190
|
<table>
|
|
148
|
-
<tr><td><a href="
|
|
149
|
-
<tr><td><a href="
|
|
150
|
-
<tr><td><a href="
|
|
151
|
-
<tr><td><a href="
|
|
152
|
-
<tr><td><a href="
|
|
153
|
-
<tr><td><a href="
|
|
154
|
-
<tr><td><a href="
|
|
155
|
-
<tr><td><a href="
|
|
156
|
-
<tr><td><a href="
|
|
191
|
+
<tr><td><a href="https://pymemoryeditor.readthedocs.io/en/latest/quickstart.html"><b>Quick Start</b></a></td><td>Open a process, read, write and run your first scan.</td></tr>
|
|
192
|
+
<tr><td><a href="https://pymemoryeditor.readthedocs.io/en/latest/guide/searching.html"><b>Searching memory</b></a></td><td>Value scans, ranges, refining results, the Cheat Engine loop.</td></tr>
|
|
193
|
+
<tr><td><a href="https://pymemoryeditor.readthedocs.io/en/latest/guide/pattern-scan.html"><b>Pattern scan</b></a></td><td>Find code/data with byte signatures (AOB) and regex.</td></tr>
|
|
194
|
+
<tr><td><a href="https://pymemoryeditor.readthedocs.io/en/latest/guide/pointers.html"><b>Pointers</b></a></td><td>Multi-level pointer chains and the live <code>RemotePointer</code>.</td></tr>
|
|
195
|
+
<tr><td><a href="https://pymemoryeditor.readthedocs.io/en/latest/guide/pointer-scan.html"><b>Pointer scan</b></a></td><td>Find static pointers that survive ASLR.</td></tr>
|
|
196
|
+
<tr><td><a href="https://pymemoryeditor.readthedocs.io/en/latest/app.html"><b>The GUI app</b></a></td><td>The bundled Cheat Engine-style scanner.</td></tr>
|
|
197
|
+
<tr><td><a href="https://pymemoryeditor.readthedocs.io/en/latest/mcp.html"><b>The MCP server</b></a></td><td>Let an AI assistant drive the scan/refine loop.</td></tr>
|
|
198
|
+
<tr><td><a href="https://pymemoryeditor.readthedocs.io/en/latest/api/openprocess.html"><b>API reference</b></a></td><td>Every public class, method and parameter.</td></tr>
|
|
199
|
+
<tr><td><a href="https://pymemoryeditor.readthedocs.io/en/latest/platform-notes.html"><b>Platform notes</b></a></td><td>Permissions and quirks on Windows, Linux and macOS.</td></tr>
|
|
200
|
+
<tr><td><a href="https://pymemoryeditor.readthedocs.io/en/latest/troubleshooting.html"><b>Troubleshooting</b></a></td><td>Common errors and how to fix them.</td></tr>
|
|
157
201
|
</table>
|
|
158
202
|
|
|
159
203
|
---
|
|
@@ -175,7 +219,7 @@ A quick map of where to go:
|
|
|
175
219
|
## 🤝 Contributing
|
|
176
220
|
|
|
177
221
|
Pull requests, bug reports and feature ideas are very welcome. Read
|
|
178
|
-
[`CONTRIBUTING.md`](CONTRIBUTING.md) for the development setup, test layout and
|
|
222
|
+
[`CONTRIBUTING.md`](https://github.com/JeanExtreme002/PyMemoryEditor/blob/main/CONTRIBUTING.md) for the development setup, test layout and
|
|
179
223
|
the small set of platform-specific quirks to be aware of.
|
|
180
224
|
|
|
181
225
|
If PyMemoryEditor helped your project, please ⭐ the repo — it's the easiest way to
|
|
@@ -185,4 +229,4 @@ support the work and to help others discover the library.
|
|
|
185
229
|
|
|
186
230
|
## License
|
|
187
231
|
|
|
188
|
-
Released under the [MIT License](LICENSE) — free for personal and commercial use.
|
|
232
|
+
Released under the [MIT License](https://github.com/JeanExtreme002/PyMemoryEditor/blob/main/LICENSE) — free for personal and commercial use.
|
|
@@ -32,6 +32,7 @@ from ..util import (
|
|
|
32
32
|
_validate_pytype,
|
|
33
33
|
as_writable_c_buffer,
|
|
34
34
|
get_c_type_of,
|
|
35
|
+
sign_extend_narrow_int,
|
|
35
36
|
values_to_bytes,
|
|
36
37
|
)
|
|
37
38
|
from ..util.pattern import PatternLike, compile_pattern
|
|
@@ -369,13 +370,23 @@ def read_process_memory(pid: int, address: int, pytype: Type[T], bufflength: int
|
|
|
369
370
|
_validate_pytype(pytype)
|
|
370
371
|
|
|
371
372
|
data = get_c_type_of(pytype, bufflength)
|
|
372
|
-
|
|
373
|
+
# `bufflength`, not `sizeof(data)`. The two differ whenever the width
|
|
374
|
+
# rounds up to a wider C type — `int` at 3 bytes gets a `c_int32` — and
|
|
375
|
+
# passing the buffer size made this backend read 4 bytes where Windows and
|
|
376
|
+
# macOS read 3, so the same call returned a different value per platform
|
|
377
|
+
# (0x11223344 here against 0x223344 there). `get_c_type_of` guarantees
|
|
378
|
+
# `sizeof(data) >= bufflength`, so reading into the front of a larger,
|
|
379
|
+
# zero-initialised buffer is safe and matches the other two backends.
|
|
380
|
+
_process_vm_readv(pid, addressof(data), address, bufflength)
|
|
373
381
|
|
|
374
382
|
if pytype is str:
|
|
375
383
|
return bytes(data).decode("utf-8", errors="replace")
|
|
376
384
|
elif pytype is bytes:
|
|
377
385
|
return bytes(data)
|
|
378
386
|
else:
|
|
387
|
+
# Narrow widths pad with zeroes, which reads a signed value as
|
|
388
|
+
# unsigned -- and a scan for the same bytes is signed.
|
|
389
|
+
sign_extend_narrow_int(data, pytype, bufflength)
|
|
379
390
|
return data.value
|
|
380
391
|
|
|
381
392
|
|
|
@@ -525,7 +536,12 @@ def write_process_memory(
|
|
|
525
536
|
data = get_c_type_of(pytype, bufflength)
|
|
526
537
|
data.value = value.encode() if isinstance(value, str) else value
|
|
527
538
|
|
|
528
|
-
|
|
539
|
+
# `bufflength`, not `sizeof(data)` — this one corrupted memory rather than
|
|
540
|
+
# merely disagreeing. A write of 3 bytes sized a 4-byte `c_int32` and then
|
|
541
|
+
# wrote all four, destroying a byte the caller never asked to touch, while
|
|
542
|
+
# Windows and macOS wrote exactly three. Verified on Linux: `w=3` touched 4
|
|
543
|
+
# bytes and `w=5` touched 8.
|
|
544
|
+
_process_vm_writev(pid, addressof(data), address, bufflength)
|
|
529
545
|
return value
|
|
530
546
|
|
|
531
547
|
|
|
@@ -30,6 +30,7 @@ from ..util import (
|
|
|
30
30
|
_validate_pytype,
|
|
31
31
|
as_writable_c_buffer,
|
|
32
32
|
get_c_type_of,
|
|
33
|
+
sign_extend_narrow_int,
|
|
33
34
|
values_to_bytes,
|
|
34
35
|
)
|
|
35
36
|
from ..util.pattern import PatternLike, compile_pattern
|
|
@@ -311,6 +312,31 @@ _PAGE_GONE_KRS = (
|
|
|
311
312
|
KERN_MEMORY_ERROR,
|
|
312
313
|
)
|
|
313
314
|
|
|
315
|
+
# KERN_PROTECTION_FAILURE is tolerated by a full-address-space *sweep* only,
|
|
316
|
+
# never by a read of caller-supplied addresses — hence a second tuple rather
|
|
317
|
+
# than another entry above.
|
|
318
|
+
#
|
|
319
|
+
# The sweeps walk every region the filter accepted and have no opinion about
|
|
320
|
+
# any single one: a range the kernel declines to hand over is a range with no
|
|
321
|
+
# matches in it, and aborting the whole scan over it loses every region that
|
|
322
|
+
# came after. `search_values_by_addresses` is the opposite case — the caller
|
|
323
|
+
# named those addresses, and its documented `raise_error=True` must still be
|
|
324
|
+
# able to report that one of them could not be read. Anything in the tuple
|
|
325
|
+
# above is swallowed even when `raise_error` is set (see
|
|
326
|
+
# process.scanning.iter_values_for_addresses), so putting kr=2 there would
|
|
327
|
+
# silently turn "permission denied" into the same `(address, None)` the caller
|
|
328
|
+
# gets for an address in a gap.
|
|
329
|
+
#
|
|
330
|
+
# Honest limits of what is known here. This was added because a plain
|
|
331
|
+
# search_by_value over the current process died with kr=2 on GitHub's
|
|
332
|
+
# virtualized macOS runners, on a 22 MB region that reported VM_PROT_READ and
|
|
333
|
+
# Shared=False. The mechanism was not established. It does *not* reproduce
|
|
334
|
+
# locally: probing all 135 scannable regions of a live process yields only kr=1
|
|
335
|
+
# and kr=10, and a page deliberately re-protected to PROT_NONE (or never
|
|
336
|
+
# mapped) answers KERN_INVALID_ADDRESS, not kr=2 — so the usual
|
|
337
|
+
# "protection changed under us" story does not explain it.
|
|
338
|
+
_SWEEP_SKIPPABLE_KRS = _PAGE_GONE_KRS + (KERN_PROTECTION_FAILURE,)
|
|
339
|
+
|
|
314
340
|
|
|
315
341
|
class MachReadError(OSError):
|
|
316
342
|
"""OSError subclass that carries the underlying kern_return_t."""
|
|
@@ -494,6 +520,14 @@ def _is_transient(exc: BaseException) -> bool:
|
|
|
494
520
|
return isinstance(exc, MachReadError) and exc.kr in _PAGE_GONE_KRS
|
|
495
521
|
|
|
496
522
|
|
|
523
|
+
def _is_skippable_by_sweep(exc: BaseException) -> bool:
|
|
524
|
+
"""
|
|
525
|
+
Same as :func:`_is_transient`, plus the codes only a full-address-space
|
|
526
|
+
sweep may skip. See :data:`_SWEEP_SKIPPABLE_KRS`.
|
|
527
|
+
"""
|
|
528
|
+
return isinstance(exc, MachReadError) and exc.kr in _SWEEP_SKIPPABLE_KRS
|
|
529
|
+
|
|
530
|
+
|
|
497
531
|
def _query_region(task: int, address: int):
|
|
498
532
|
"""Return the region containing `address`, or None when the query fails."""
|
|
499
533
|
addr = mach_vm_address_t(address)
|
|
@@ -554,6 +588,9 @@ def read_process_memory(
|
|
|
554
588
|
elif pytype is bytes:
|
|
555
589
|
return bytes(data)
|
|
556
590
|
else:
|
|
591
|
+
# Narrow widths pad with zeroes, which reads a signed value as
|
|
592
|
+
# unsigned -- and a scan for the same bytes is signed.
|
|
593
|
+
sign_extend_narrow_int(data, pytype, bufflength)
|
|
557
594
|
return data.value
|
|
558
595
|
|
|
559
596
|
|
|
@@ -683,7 +720,7 @@ def search_addresses_by_value(
|
|
|
683
720
|
scan_type,
|
|
684
721
|
_make_read_chunk(task),
|
|
685
722
|
progress_information=progress_information,
|
|
686
|
-
transient_error_check=
|
|
723
|
+
transient_error_check=_is_skippable_by_sweep,
|
|
687
724
|
)
|
|
688
725
|
|
|
689
726
|
|
|
@@ -1044,7 +1081,7 @@ def search_addresses_by_pattern(
|
|
|
1044
1081
|
length,
|
|
1045
1082
|
_make_read_chunk(task),
|
|
1046
1083
|
progress_information=progress_information,
|
|
1047
|
-
transient_error_check=
|
|
1084
|
+
transient_error_check=_is_skippable_by_sweep,
|
|
1048
1085
|
)
|
|
1049
1086
|
|
|
1050
1087
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
A Model Context Protocol server for PyMemoryEditor.
|
|
5
|
+
|
|
6
|
+
Exposes the library's process-memory tools over MCP so an AI assistant can run
|
|
7
|
+
the Cheat Engine loop itself — enumerate processes, attach, scan for a value,
|
|
8
|
+
narrow the matches as the value changes, then read (and optionally write) the
|
|
9
|
+
address it converged on.
|
|
10
|
+
|
|
11
|
+
Install and run::
|
|
12
|
+
|
|
13
|
+
pip install "PyMemoryEditor[mcp]"
|
|
14
|
+
pymemoryeditor-mcp # asks before attaching; writes allowed
|
|
15
|
+
pymemoryeditor-mcp --read-only # never modifies a target
|
|
16
|
+
|
|
17
|
+
Or register it with a client — Claude Code::
|
|
18
|
+
|
|
19
|
+
claude mcp add pymemoryeditor -- pymemoryeditor-mcp
|
|
20
|
+
|
|
21
|
+
...and any client that reads ``mcpServers`` JSON::
|
|
22
|
+
|
|
23
|
+
{
|
|
24
|
+
"mcpServers": {
|
|
25
|
+
"pymemoryeditor": {
|
|
26
|
+
"command": "pymemoryeditor-mcp",
|
|
27
|
+
"args": []
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
No flags are needed, because consent is asked for where it can actually be
|
|
33
|
+
given: attaching to a process the operator did not pre-approve **asks the
|
|
34
|
+
user**, and every ``write_value`` call is confirmed by their client.
|
|
35
|
+
``--read-only`` drops the write tool entirely, ``--allow-process NAME``
|
|
36
|
+
pre-approves a target so it stops prompting, and ``--allow-any-process`` turns
|
|
37
|
+
prompting off for scripted runs. Read :doc:`the MCP guide </mcp>` before
|
|
38
|
+
pointing it at anything you care about — it runs with your privileges and it is
|
|
39
|
+
**not a sandbox**.
|
|
40
|
+
|
|
41
|
+
Layout: :mod:`~PyMemoryEditor.mcp.toolset` holds the tools (and imports no MCP
|
|
42
|
+
SDK, so it is testable on its own), :mod:`~PyMemoryEditor.mcp.session` the
|
|
43
|
+
process/scan handles, :mod:`~PyMemoryEditor.mcp.policy` the access rules, and
|
|
44
|
+
:mod:`~PyMemoryEditor.mcp.server` the protocol wiring.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
from .config import ServerConfig, build_parser, parse_args
|
|
48
|
+
from .policy import AccessDecision, ProcessPolicy
|
|
49
|
+
from .server import INSTRUCTIONS, build_server, main
|
|
50
|
+
from .session import ScanResult, Session, SessionError, SessionStore
|
|
51
|
+
from .toolset import MemoryToolset, ToolError
|
|
52
|
+
|
|
53
|
+
__all__ = (
|
|
54
|
+
"INSTRUCTIONS",
|
|
55
|
+
"AccessDecision",
|
|
56
|
+
"MemoryToolset",
|
|
57
|
+
"ProcessPolicy",
|
|
58
|
+
"ScanResult",
|
|
59
|
+
"ServerConfig",
|
|
60
|
+
"Session",
|
|
61
|
+
"SessionError",
|
|
62
|
+
"SessionStore",
|
|
63
|
+
"ToolError",
|
|
64
|
+
"build_parser",
|
|
65
|
+
"build_server",
|
|
66
|
+
"main",
|
|
67
|
+
"parse_args",
|
|
68
|
+
)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
"""``python -m PyMemoryEditor.mcp`` — the same entry point as the console script.
|
|
4
|
+
|
|
5
|
+
Useful when the console script is not on PATH, which is the usual situation
|
|
6
|
+
when an MCP client launches the server from a virtualenv it did not activate::
|
|
7
|
+
|
|
8
|
+
{"command": "/path/to/venv/bin/python",
|
|
9
|
+
"args": ["-m", "PyMemoryEditor.mcp", "--allow-process", "game.exe"]}
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
from .server import main
|
|
15
|
+
|
|
16
|
+
if __name__ == "__main__":
|
|
17
|
+
sys.exit(main())
|