PyMemoryEditor 3.3.2__py3-none-any.whl → 3.3.3__py3-none-any.whl
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/__init__.py +1 -1
- PyMemoryEditor/app/_gc.py +71 -0
- PyMemoryEditor/app/application.py +13 -4
- PyMemoryEditor/mcp/policy.py +7 -3
- PyMemoryEditor/process/abstract.py +67 -5
- PyMemoryEditor/util/pattern.py +10 -1
- PyMemoryEditor/win32/functions.py +7 -4
- {pymemoryeditor-3.3.2.dist-info → pymemoryeditor-3.3.3.dist-info}/METADATA +1 -1
- {pymemoryeditor-3.3.2.dist-info → pymemoryeditor-3.3.3.dist-info}/RECORD +12 -11
- {pymemoryeditor-3.3.2.dist-info → pymemoryeditor-3.3.3.dist-info}/WHEEL +0 -0
- {pymemoryeditor-3.3.2.dist-info → pymemoryeditor-3.3.3.dist-info}/entry_points.txt +0 -0
- {pymemoryeditor-3.3.2.dist-info → pymemoryeditor-3.3.3.dist-info}/licenses/LICENSE +0 -0
PyMemoryEditor/__init__.py
CHANGED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
"""Run Python's cyclic garbage collector on the GUI thread only.
|
|
4
|
+
|
|
5
|
+
The automatic collector runs in whichever thread happens to allocate when a
|
|
6
|
+
threshold is crossed -- in this app, often a ``QThread`` worker in the middle
|
|
7
|
+
of a scan. If that collection frees a reference cycle holding Qt objects that
|
|
8
|
+
belong to the GUI thread, they are destroyed from the worker, which crashes
|
|
9
|
+
the process (a segfault on macOS, an access violation on Windows; seen on CI
|
|
10
|
+
in the pointer-scan worker's ``_hand_over``).
|
|
11
|
+
|
|
12
|
+
So while the app runs, automatic collection is switched off and a timer on the
|
|
13
|
+
GUI thread collects instead, generation by generation, as the automatic
|
|
14
|
+
collector would have. Plain reference counting still frees everything that is
|
|
15
|
+
not in a cycle immediately, in any thread.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import contextlib
|
|
19
|
+
import gc
|
|
20
|
+
from typing import Iterator, Optional
|
|
21
|
+
|
|
22
|
+
from PySide6.QtCore import QObject, QTimer
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
#: How often the GUI thread checks whether a collection is due.
|
|
26
|
+
COLLECT_INTERVAL_MS = 500
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class MainThreadGarbageCollector(QObject):
|
|
30
|
+
"""Disable automatic collection and collect from a GUI-thread timer.
|
|
31
|
+
|
|
32
|
+
Create it on the GUI thread; :meth:`stop` restores the collector's previous
|
|
33
|
+
state, so an embedder's own setting survives the app.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self, parent: Optional[QObject] = None, interval_ms: int = COLLECT_INTERVAL_MS
|
|
38
|
+
) -> None:
|
|
39
|
+
super().__init__(parent)
|
|
40
|
+
self._was_enabled = gc.isenabled()
|
|
41
|
+
self._threshold = gc.get_threshold()
|
|
42
|
+
gc.disable()
|
|
43
|
+
self._timer = QTimer(self)
|
|
44
|
+
self._timer.timeout.connect(self.collect_if_due)
|
|
45
|
+
self._timer.start(interval_ms)
|
|
46
|
+
|
|
47
|
+
def collect_if_due(self) -> None:
|
|
48
|
+
"""Collect each generation whose allocation count passed its threshold."""
|
|
49
|
+
count0, count1, count2 = gc.get_count()
|
|
50
|
+
if count0 > self._threshold[0]:
|
|
51
|
+
gc.collect(0)
|
|
52
|
+
if count1 > self._threshold[1]:
|
|
53
|
+
gc.collect(1)
|
|
54
|
+
if count2 > self._threshold[2]:
|
|
55
|
+
gc.collect(2)
|
|
56
|
+
|
|
57
|
+
def stop(self) -> None:
|
|
58
|
+
"""Stop the timer and give automatic collection back."""
|
|
59
|
+
self._timer.stop()
|
|
60
|
+
if self._was_enabled:
|
|
61
|
+
gc.enable()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@contextlib.contextmanager
|
|
65
|
+
def garbage_collected_on_gui_thread(parent: Optional[QObject] = None) -> Iterator[None]:
|
|
66
|
+
"""Run the body with :class:`MainThreadGarbageCollector` in charge."""
|
|
67
|
+
collector = MainThreadGarbageCollector(parent)
|
|
68
|
+
try:
|
|
69
|
+
yield
|
|
70
|
+
finally:
|
|
71
|
+
collector.stop()
|
|
@@ -560,10 +560,7 @@ def main(argv=None):
|
|
|
560
560
|
from PySide6.QtCore import QSettings
|
|
561
561
|
from PySide6.QtWidgets import QApplication
|
|
562
562
|
|
|
563
|
-
from .
|
|
564
|
-
from .main_window import MainWindow
|
|
565
|
-
from .open_process_dialog import OpenProcessDialog
|
|
566
|
-
|
|
563
|
+
from ._gc import garbage_collected_on_gui_thread
|
|
567
564
|
from ._icon import app_icon
|
|
568
565
|
|
|
569
566
|
app = QApplication.instance() or QApplication(argv)
|
|
@@ -577,6 +574,18 @@ def main(argv=None):
|
|
|
577
574
|
saved_theme = str(QSettings().value("theme", DEFAULT_THEME_ID))
|
|
578
575
|
apply_theme(app, saved_theme)
|
|
579
576
|
|
|
577
|
+
# Every dialog from here on runs QThread workers; see _gc for why the
|
|
578
|
+
# cyclic collector must not run inside them.
|
|
579
|
+
with garbage_collected_on_gui_thread(app):
|
|
580
|
+
_pick_process_and_run(app)
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
def _pick_process_and_run(app) -> None:
|
|
584
|
+
"""The picker, then the main window until the event loop exits."""
|
|
585
|
+
from ._widgets import detached_workers_running
|
|
586
|
+
from .main_window import MainWindow
|
|
587
|
+
from .open_process_dialog import OpenProcessDialog
|
|
588
|
+
|
|
580
589
|
picker = OpenProcessDialog()
|
|
581
590
|
if picker.exec() != picker.DialogCode.Accepted:
|
|
582
591
|
return
|
PyMemoryEditor/mcp/policy.py
CHANGED
|
@@ -218,6 +218,12 @@ class AccessDecision:
|
|
|
218
218
|
needs_approval: bool = False
|
|
219
219
|
|
|
220
220
|
|
|
221
|
+
def _self_and_ancestor_pids() -> FrozenSet[int]:
|
|
222
|
+
"""This process, its parent and every ancestor: what ``protect_self``
|
|
223
|
+
refuses. A function of its own so tests can swap the real chain out."""
|
|
224
|
+
return frozenset({os.getpid(), os.getppid()}) | ancestor_pids()
|
|
225
|
+
|
|
226
|
+
|
|
221
227
|
class ProcessPolicy:
|
|
222
228
|
"""Decides whether a given ``(pid, name)`` may be attached to.
|
|
223
229
|
|
|
@@ -282,9 +288,7 @@ class ProcessPolicy:
|
|
|
282
288
|
# further up. Taken once: the chain that launched the server does not
|
|
283
289
|
# change while it runs.
|
|
284
290
|
self._protected: FrozenSet[int] = (
|
|
285
|
-
|
|
286
|
-
if protect_self
|
|
287
|
-
else frozenset()
|
|
291
|
+
_self_and_ancestor_pids() if protect_self else frozenset()
|
|
288
292
|
)
|
|
289
293
|
self._identify = identify
|
|
290
294
|
# Only Linux truncates names; applying the rule elsewhere would let a
|
|
@@ -1303,6 +1303,15 @@ class AbstractProcess(ABC):
|
|
|
1303
1303
|
bounds it. (To abort the map-building phase, raise from
|
|
1304
1304
|
``progress_callback``.)
|
|
1305
1305
|
|
|
1306
|
+
:raises ValueError: ``ptr_size`` other than 4 or 8, ``max_depth`` or
|
|
1307
|
+
``max_results`` below 1, or a negative ``max_offset``.
|
|
1308
|
+
:raises ClosedProcess: the process was closed (also raised by the
|
|
1309
|
+
returned generator once it is).
|
|
1310
|
+
|
|
1311
|
+
Arguments and access are checked at the call, like every ``search_*``
|
|
1312
|
+
method; the pointer map is built and walked lazily, as the generator
|
|
1313
|
+
is consumed.
|
|
1314
|
+
|
|
1306
1315
|
Example
|
|
1307
1316
|
-------
|
|
1308
1317
|
::
|
|
@@ -1316,11 +1325,26 @@ class AbstractProcess(ABC):
|
|
|
1316
1325
|
live = path.rebase(process).to_pointer(process, pytype=int, bufflength=4)
|
|
1317
1326
|
live.value = 9999
|
|
1318
1327
|
"""
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1328
|
+
# Validate eagerly, like every search_* method: a bad argument or a
|
|
1329
|
+
# closed process raises here, at the call, not on the first next().
|
|
1330
|
+
# The map build and path search stay lazy in _iter_pointer_paths.
|
|
1331
|
+
#
|
|
1332
|
+
# A subclass written against the 3.3 contract implements the listings
|
|
1333
|
+
# itself and need not define these hooks (see __init_subclass__): each
|
|
1334
|
+
# check runs only where the subclass provides it, instead of hitting
|
|
1335
|
+
# the NotImplementedError of the base _require_open.
|
|
1336
|
+
cls = type(self)
|
|
1337
|
+
guarded = cls._require_open is not AbstractProcess._require_open
|
|
1338
|
+
if guarded or cls._require_scanning is not AbstractProcess._require_scanning:
|
|
1339
|
+
self._require_scanning()
|
|
1340
|
+
|
|
1341
|
+
if max_depth < 1:
|
|
1342
|
+
raise ValueError("max_depth must be at least 1.")
|
|
1343
|
+
if max_offset < 0:
|
|
1344
|
+
raise ValueError("max_offset must not be negative.")
|
|
1345
|
+
if max_results is not None and max_results < 1:
|
|
1346
|
+
# 0 would build the whole pointer map only to yield nothing.
|
|
1347
|
+
raise ValueError("max_results must be at least 1 (or None for no cap).")
|
|
1324
1348
|
|
|
1325
1349
|
if ptr_size is None:
|
|
1326
1350
|
ptr_size = self.pointer_size
|
|
@@ -1330,6 +1354,44 @@ class AbstractProcess(ABC):
|
|
|
1330
1354
|
"ptr_size must be 4 (32-bit target) or 8 (64-bit target)."
|
|
1331
1355
|
)
|
|
1332
1356
|
|
|
1357
|
+
scan = self._iter_pointer_paths(
|
|
1358
|
+
target_address,
|
|
1359
|
+
max_depth=max_depth,
|
|
1360
|
+
max_offset=max_offset,
|
|
1361
|
+
ptr_size=ptr_size,
|
|
1362
|
+
aligned=aligned,
|
|
1363
|
+
writable_only=writable_only,
|
|
1364
|
+
static_ranges=static_ranges,
|
|
1365
|
+
max_results=max_results,
|
|
1366
|
+
memory_regions=memory_regions,
|
|
1367
|
+
progress_callback=progress_callback,
|
|
1368
|
+
should_stop=should_stop,
|
|
1369
|
+
)
|
|
1370
|
+
return guard_generator(scan, self._require_open) if guarded else scan
|
|
1371
|
+
|
|
1372
|
+
def _iter_pointer_paths(
|
|
1373
|
+
self,
|
|
1374
|
+
target_address: int,
|
|
1375
|
+
*,
|
|
1376
|
+
max_depth: int,
|
|
1377
|
+
max_offset: int,
|
|
1378
|
+
ptr_size: int,
|
|
1379
|
+
aligned: bool,
|
|
1380
|
+
writable_only: bool,
|
|
1381
|
+
static_ranges: Optional[Sequence[Tuple[int, int]]],
|
|
1382
|
+
max_results: Optional[int],
|
|
1383
|
+
memory_regions: Optional[Sequence[MemoryRegion]],
|
|
1384
|
+
progress_callback: Optional["Callable[[float], None]"],
|
|
1385
|
+
should_stop: Optional["Callable[[], bool]"],
|
|
1386
|
+
) -> Generator["PointerPath", None, None]:
|
|
1387
|
+
"""The lazy body of :meth:`scan_pointer_paths`, run once arguments are
|
|
1388
|
+
validated."""
|
|
1389
|
+
from .pointer_scan import (
|
|
1390
|
+
AddressRanges,
|
|
1391
|
+
build_pointer_map,
|
|
1392
|
+
find_pointer_paths,
|
|
1393
|
+
)
|
|
1394
|
+
|
|
1333
1395
|
if memory_regions is None:
|
|
1334
1396
|
memory_regions = list(self.get_memory_regions())
|
|
1335
1397
|
|
PyMemoryEditor/util/pattern.py
CHANGED
|
@@ -71,9 +71,18 @@ def compile_pattern(
|
|
|
71
71
|
:raises ValueError: malformed IDA-style token, or ``byte_length`` omitted
|
|
72
72
|
for a regex / pre-compiled pattern.
|
|
73
73
|
:raises TypeError: if ``pattern`` is not a ``str``, ``bytes`` or
|
|
74
|
-
``re.Pattern[bytes]
|
|
74
|
+
``re.Pattern[bytes]`` — a compiled ``str`` regex included, as it can
|
|
75
|
+
never match process memory.
|
|
75
76
|
"""
|
|
76
77
|
if isinstance(pattern, re.Pattern):
|
|
78
|
+
# A str pattern can't match process memory: it would only fail later,
|
|
79
|
+
# deep inside the scan, with "cannot use a string pattern on a
|
|
80
|
+
# bytes-like object".
|
|
81
|
+
if not isinstance(pattern.pattern, bytes):
|
|
82
|
+
raise TypeError(
|
|
83
|
+
"Pattern must be a compiled bytes regex (re.compile(rb'...')), "
|
|
84
|
+
"not a str one."
|
|
85
|
+
)
|
|
77
86
|
if byte_length <= 0:
|
|
78
87
|
raise ValueError(
|
|
79
88
|
"byte_length= is required when passing a pre-compiled regex "
|
|
@@ -251,6 +251,8 @@ _PROCESSOR_ARCHITECTURE_64BIT = (6, 9, 12)
|
|
|
251
251
|
_OS_IS_64BIT = (
|
|
252
252
|
_native_system_information.wProcessorArchitecture in _PROCESSOR_ARCHITECTURE_64BIT
|
|
253
253
|
)
|
|
254
|
+
# The running Python's bitness: the fallback guess when a target's can't be read.
|
|
255
|
+
_HOST_IS_64BIT = ctypes.sizeof(ctypes.c_void_p) == 8
|
|
254
256
|
|
|
255
257
|
|
|
256
258
|
def mbi_class_for_handle(process_handle: int):
|
|
@@ -318,12 +320,13 @@ def IsProcess64Bit(process_handle: int) -> bool:
|
|
|
318
320
|
return detected
|
|
319
321
|
|
|
320
322
|
_logger.warning(
|
|
321
|
-
"IsWow64Process failed (err=%d); assuming the target
|
|
322
|
-
"
|
|
323
|
-
"
|
|
323
|
+
"IsWow64Process failed (err=%d); assuming the target matches the "
|
|
324
|
+
"host bitness (%d-bit). Pointer-width detection may be wrong if it "
|
|
325
|
+
"does not.",
|
|
324
326
|
ctypes.get_last_error(),
|
|
327
|
+
64 if _HOST_IS_64BIT else 32,
|
|
325
328
|
)
|
|
326
|
-
return
|
|
329
|
+
return _HOST_IS_64BIT
|
|
327
330
|
|
|
328
331
|
|
|
329
332
|
T = TypeVar("T")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: PyMemoryEditor
|
|
3
|
-
Version: 3.3.
|
|
3
|
+
Version: 3.3.3
|
|
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
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
PyMemoryEditor/__init__.py,sha256=
|
|
1
|
+
PyMemoryEditor/__init__.py,sha256=bwjUj-mN7Qc2d8r-9cSsNJr4SmKgq8tHIYAFauTtf9Y,3234
|
|
2
2
|
PyMemoryEditor/__main__.py,sha256=xukvvLjQ0BnYnnxMZda1cvyZ-BurSUk8dAGQybIqMD4,95
|
|
3
3
|
PyMemoryEditor/enums.py,sha256=q_SWQYgh4F7yyAa7sZvNo2Sw91TSm3aTqPfTA7OqldM,322
|
|
4
4
|
PyMemoryEditor/app/__init__.py,sha256=DGCYWpqGKPSeBa7cvzL7GmmhIfDDgF6vtim0VtLcfOo,357
|
|
5
5
|
PyMemoryEditor/app/_auto_refresh_dialog.py,sha256=ktvsOaXUooQ9Hvfw2Y__IzWNZby0sW_0QlhNGrPseoE,10048
|
|
6
|
+
PyMemoryEditor/app/_gc.py,sha256=ulvh129qptIBxzFjfbIDQm4DMPt6WwDEniiRngv3FQ8,2533
|
|
6
7
|
PyMemoryEditor/app/_icon.py,sha256=1HaaMX3HRCUD2EVIRwj_0BwPkdfj1nsVjwoTRwriJ_k,1447
|
|
7
8
|
PyMemoryEditor/app/_widgets.py,sha256=vMqBfOHylEHB0jlxWmfbILEIRzjj7d6EIAFG5f7oxE0,12639
|
|
8
|
-
PyMemoryEditor/app/application.py,sha256=
|
|
9
|
+
PyMemoryEditor/app/application.py,sha256=RzPAPKmMmXjT6La5O_lSyqPWont6xM356DjDkWFttmY,20278
|
|
9
10
|
PyMemoryEditor/app/cheat_entry.py,sha256=KOLcams-xkVEkV7rfrzhEI7IrsVvmGwsIr8cGcWdLM0,2841
|
|
10
11
|
PyMemoryEditor/app/cheat_poll_worker.py,sha256=ZhDOXAGI9ObAEBSgkZLLOySUTekhlwICIfuA1XOp2Rw,12716
|
|
11
12
|
PyMemoryEditor/app/cheat_table.py,sha256=FK0ZUkuEvvuXXOB64-hpxSDoGFlHNKhMFyFGgpsyXU0,46278
|
|
@@ -35,14 +36,14 @@ PyMemoryEditor/macos/types.py,sha256=pYVGpROHKt8-s1C-sFsG8xEk1vmBVdh5_5-BIqtsSak
|
|
|
35
36
|
PyMemoryEditor/mcp/__init__.py,sha256=g30FgXeR3_eHq5hsGSGsPEJQlMPQ3A0jstD-MAFbU2M,2393
|
|
36
37
|
PyMemoryEditor/mcp/__main__.py,sha256=GXMMDJMMZfFMjzYnRcw20wRRb3v_IKxvv_-ZOyxkK1U,475
|
|
37
38
|
PyMemoryEditor/mcp/config.py,sha256=bpqkaWMI314byflMrMnySdLuuyqQJT3XTbuKIDgVLME,15862
|
|
38
|
-
PyMemoryEditor/mcp/policy.py,sha256=
|
|
39
|
+
PyMemoryEditor/mcp/policy.py,sha256=cfG1A-IR1kTe_neLZFtd-JAqkZ_vcE4QYrPgNUv3yb0,25844
|
|
39
40
|
PyMemoryEditor/mcp/procinfo.py,sha256=ufT07yoffyfA7KiBrdaQm7Hkf1WbpGveZMwcSmR8NQQ,10136
|
|
40
41
|
PyMemoryEditor/mcp/server.py,sha256=YBJDMM2pXaavG_o-CnO8yL6aFecdsNfv6wEIqqard-o,33564
|
|
41
42
|
PyMemoryEditor/mcp/session.py,sha256=0gy629j8lJrq4VxqUTMbxJ-D-D1IJo47T-UwtAIEzZE,31645
|
|
42
43
|
PyMemoryEditor/mcp/toolset.py,sha256=kxkByx5YUowX7ldj1o1k0VXCPMoRNR4kxH1NHn3Z8ts,118338
|
|
43
44
|
PyMemoryEditor/process/__init__.py,sha256=Xh5jngBqZyNIA9y3uwE49LxK8uH8YYsu6qotx1mP_WE,213
|
|
44
45
|
PyMemoryEditor/process/_guard.py,sha256=91TKDEpFthFn99QNOioMD9hp2kzAOHA_qtkEgW9tJtI,1471
|
|
45
|
-
PyMemoryEditor/process/abstract.py,sha256=
|
|
46
|
+
PyMemoryEditor/process/abstract.py,sha256=JbgKqR6tBPb6aCyuI2onLH-_9g9pICGxrv-PI-Yi6mk,74035
|
|
46
47
|
PyMemoryEditor/process/errors.py,sha256=485SXkidg9_E_KjuhuF6AsQNuFZlY-Ag-sG1C6NsGMw,2054
|
|
47
48
|
PyMemoryEditor/process/info.py,sha256=yF7kE-wMEs_h3rpn2dPgBpuwn2EhhVq3lfl5u7GwCfY,1484
|
|
48
49
|
PyMemoryEditor/process/module_info.py,sha256=D7qAoBymhe6vrL0IYc2R6ZhWE-OPmYQzf6hSp_f2KmA,2365
|
|
@@ -54,10 +55,10 @@ PyMemoryEditor/process/thread_info.py,sha256=vwTDd3Yg2gOidU7I630Nsut-iryE5imSXnX
|
|
|
54
55
|
PyMemoryEditor/process/util.py,sha256=QHwwjX79nygYB9ochCXtXCa8wYZ8CwU57q-cIhNajvQ,4014
|
|
55
56
|
PyMemoryEditor/util/__init__.py,sha256=QI_tG9V3HA68IP-BA2-C-rLZWAADhZrbpxXGypBc69s,611
|
|
56
57
|
PyMemoryEditor/util/convert.py,sha256=L-jhVBhmedd6g9IO0rL4KMKU8EZpxMNbB4eiRQkevZc,21954
|
|
57
|
-
PyMemoryEditor/util/pattern.py,sha256=
|
|
58
|
+
PyMemoryEditor/util/pattern.py,sha256=ONRljRxGYhiElQG1E5D5LusgyZR_YLLdFtZrHFjYYkE,7724
|
|
58
59
|
PyMemoryEditor/util/scan.py,sha256=-VZdwrBeXB7CQ_DKePUWjb-h-ox6_ogwYrloGkw0v6A,28752
|
|
59
60
|
PyMemoryEditor/util/scan_numpy.py,sha256=SRTuhrhYP5azSab3k8FBTPVCMK9_1vZHogSSofkFCAA,6940
|
|
60
|
-
PyMemoryEditor/win32/functions.py,sha256=
|
|
61
|
+
PyMemoryEditor/win32/functions.py,sha256=mzgo1DZDFGgK0gFFaw3Hc2ljPDWjeEBzfqmac4F233U,47972
|
|
61
62
|
PyMemoryEditor/win32/process.py,sha256=9t9xtmW_rBIWh6-Jdr3CPLshmmuBNBonBgzQA3bZGc8,13562
|
|
62
63
|
PyMemoryEditor/win32/types.py,sha256=QkFRFKviS-7DNGgsK2eCTeiinYm2BZdxsPh_pw6I570,5177
|
|
63
64
|
PyMemoryEditor/win32/enums/__init__.py,sha256=RZXhXb-yCGHVDEi0OxV27w0g2l6b68BIbKhHXyTM3cw,301
|
|
@@ -68,8 +69,8 @@ PyMemoryEditor/win32/enums/process_operations.py,sha256=wn-aMl01VVg-2rFavR9szBNR
|
|
|
68
69
|
PyMemoryEditor/win32/enums/standard_access_rights.py,sha256=VGWlTagW4hvbm2AjZ4NK56ZrQwRmEl9Kz9GSCG8o-HI,738
|
|
69
70
|
PyMemoryEditor/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
70
71
|
PyMemoryEditor/app/assets/icon.svg,sha256=T7OqR7ldqC_dK2Ib2ntsH2rWO-Z5RzNK4WdD1wFwgM4,7132
|
|
71
|
-
pymemoryeditor-3.3.
|
|
72
|
-
pymemoryeditor-3.3.
|
|
73
|
-
pymemoryeditor-3.3.
|
|
74
|
-
pymemoryeditor-3.3.
|
|
75
|
-
pymemoryeditor-3.3.
|
|
72
|
+
pymemoryeditor-3.3.3.dist-info/METADATA,sha256=6Pm1UqVnlDrzaK1VWVPqPvWAOLmwujsaw9iZlv56fps,11037
|
|
73
|
+
pymemoryeditor-3.3.3.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
74
|
+
pymemoryeditor-3.3.3.dist-info/entry_points.txt,sha256=qxto0CWCXsjLmE0jClEgz1Xriljr6IV6d3AySjIlw7E,127
|
|
75
|
+
pymemoryeditor-3.3.3.dist-info/licenses/LICENSE,sha256=YkOx6Nvkavq36bgiAc3eF85SBG0Qu14KMAvxmv6Ssjc,1089
|
|
76
|
+
pymemoryeditor-3.3.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|