memlapse 0.1.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.
- memlapse-0.1.1/CHANGELOG.md +59 -0
- memlapse-0.1.1/LICENSE +21 -0
- memlapse-0.1.1/MANIFEST.in +7 -0
- memlapse-0.1.1/PKG-INFO +154 -0
- memlapse-0.1.1/README.md +125 -0
- memlapse-0.1.1/memlapse/__init__.py +8 -0
- memlapse-0.1.1/memlapse/__main__.py +8 -0
- memlapse-0.1.1/memlapse/analytics.py +633 -0
- memlapse-0.1.1/memlapse/app.py +44 -0
- memlapse-0.1.1/memlapse/collectors/__init__.py +5 -0
- memlapse-0.1.1/memlapse/collectors/base.py +95 -0
- memlapse-0.1.1/memlapse/collectors/process.py +57 -0
- memlapse-0.1.1/memlapse/collectors/region.py +157 -0
- memlapse-0.1.1/memlapse/collectors/system.py +34 -0
- memlapse-0.1.1/memlapse/model/__init__.py +4 -0
- memlapse-0.1.1/memlapse/model/process.py +36 -0
- memlapse-0.1.1/memlapse/model/region.py +97 -0
- memlapse-0.1.1/memlapse/model/system.py +32 -0
- memlapse-0.1.1/memlapse/services/__init__.py +4 -0
- memlapse-0.1.1/memlapse/services/playback.py +516 -0
- memlapse-0.1.1/memlapse/services/recording.py +79 -0
- memlapse-0.1.1/memlapse/storage/__init__.py +3 -0
- memlapse-0.1.1/memlapse/storage/dao.py +449 -0
- memlapse-0.1.1/memlapse/storage/db.py +73 -0
- memlapse-0.1.1/memlapse/storage/schema.sql +118 -0
- memlapse-0.1.1/memlapse/ui/__init__.py +3 -0
- memlapse-0.1.1/memlapse/ui/dashboard.py +393 -0
- memlapse-0.1.1/memlapse/ui/gauges.py +113 -0
- memlapse-0.1.1/memlapse/ui/hexdump.py +16 -0
- memlapse-0.1.1/memlapse/ui/main_window.py +387 -0
- memlapse-0.1.1/memlapse/ui/process_view.py +175 -0
- memlapse-0.1.1/memlapse/ui/region_view.py +691 -0
- memlapse-0.1.1/memlapse/ui/theme.py +132 -0
- memlapse-0.1.1/memlapse/ui/timeline.py +181 -0
- memlapse-0.1.1/memlapse/win32/__init__.py +1 -0
- memlapse-0.1.1/memlapse/win32/memory.py +207 -0
- memlapse-0.1.1/memlapse/win32/privileges.py +136 -0
- memlapse-0.1.1/memlapse/win32/processes.py +142 -0
- memlapse-0.1.1/memlapse/win32/threads.py +146 -0
- memlapse-0.1.1/memlapse.egg-info/PKG-INFO +154 -0
- memlapse-0.1.1/memlapse.egg-info/SOURCES.txt +75 -0
- memlapse-0.1.1/memlapse.egg-info/dependency_links.txt +1 -0
- memlapse-0.1.1/memlapse.egg-info/entry_points.txt +2 -0
- memlapse-0.1.1/memlapse.egg-info/requires.txt +3 -0
- memlapse-0.1.1/memlapse.egg-info/top_level.txt +1 -0
- memlapse-0.1.1/pyproject.toml +89 -0
- memlapse-0.1.1/requirements-dev.txt +169 -0
- memlapse-0.1.1/requirements.txt +158 -0
- memlapse-0.1.1/setup.cfg +4 -0
- memlapse-0.1.1/tests/conftest.py +140 -0
- memlapse-0.1.1/tests/test_analytics.py +652 -0
- memlapse-0.1.1/tests/test_app.py +87 -0
- memlapse-0.1.1/tests/test_collector_base.py +86 -0
- memlapse-0.1.1/tests/test_dao.py +426 -0
- memlapse-0.1.1/tests/test_dashboard.py +250 -0
- memlapse-0.1.1/tests/test_gauges.py +64 -0
- memlapse-0.1.1/tests/test_hexdump.py +30 -0
- memlapse-0.1.1/tests/test_main_window.py +708 -0
- memlapse-0.1.1/tests/test_model.py +54 -0
- memlapse-0.1.1/tests/test_playback.py +1133 -0
- memlapse-0.1.1/tests/test_process_collector.py +91 -0
- memlapse-0.1.1/tests/test_process_view.py +161 -0
- memlapse-0.1.1/tests/test_recording_manager.py +181 -0
- memlapse-0.1.1/tests/test_region_sampler.py +265 -0
- memlapse-0.1.1/tests/test_region_view.py +1194 -0
- memlapse-0.1.1/tests/test_storage_db.py +153 -0
- memlapse-0.1.1/tests/test_system.py +49 -0
- memlapse-0.1.1/tests/test_system_collector.py +18 -0
- memlapse-0.1.1/tests/test_system_model.py +33 -0
- memlapse-0.1.1/tests/test_theme.py +30 -0
- memlapse-0.1.1/tests/test_timeline.py +179 -0
- memlapse-0.1.1/tests/test_version.py +72 -0
- memlapse-0.1.1/tests/test_win32_memory.py +167 -0
- memlapse-0.1.1/tests/test_win32_privileges.py +167 -0
- memlapse-0.1.1/tests/test_win32_processes.py +115 -0
- memlapse-0.1.1/tests/test_win32_threads.py +159 -0
- memlapse-0.1.1/tests/test_window_stats.py +20 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Notable changes to memlapse. Versions follow [semantic
|
|
4
|
+
versioning](https://semver.org/spec/v2.0.0.html): while the major version is 0
|
|
5
|
+
the interface may still change, and any such change is called out here under
|
|
6
|
+
**Changed** rather than assumed to be obvious from the version number.
|
|
7
|
+
|
|
8
|
+
What is versioned is the application: its command line, its window, and the
|
|
9
|
+
recordings it writes. The `memlapse` package is importable, but it is a
|
|
10
|
+
program rather than a library, and the names inside it may move without that
|
|
11
|
+
being a breaking change. A recording is the part with the longest life, so a
|
|
12
|
+
release after which an older recording no longer opens, or opens and scores
|
|
13
|
+
differently, says so here.
|
|
14
|
+
|
|
15
|
+
## [0.1.1] - 2026-09-11
|
|
16
|
+
|
|
17
|
+
First release, and the first as a package: until now memlapse ran only from a
|
|
18
|
+
checkout with `python main.py`. It installs with `pip install memlapse` and
|
|
19
|
+
starts with `memlapse`, or `memlapse --elevate` to relaunch through UAC.
|
|
20
|
+
Windows only, Python 3.14.
|
|
21
|
+
|
|
22
|
+
The name is written in lower case throughout, in the window, the docs and the
|
|
23
|
+
repository, as it already was on PyPI. 0.1.0 below was tagged and never
|
|
24
|
+
published, which is why the first release is numbered 0.1.1.
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- A live process table, sortable and filterable, complete whether or not the
|
|
29
|
+
app is elevated.
|
|
30
|
+
- A memory map for the selected process, read with `VirtualQueryEx` and
|
|
31
|
+
refreshed once a second while it is on screen, with a hex preview of a
|
|
32
|
+
region's first bytes and a bounded save of a region's contents for a
|
|
33
|
+
disassembler or a YARA rule.
|
|
34
|
+
- Recording of a process's memory map over time to SQLite, and playback of a
|
|
35
|
+
recording on a timeline that can be scrubbed or played.
|
|
36
|
+
- A 0 to 100 injection score for each executable region, from unbacked
|
|
37
|
+
private or mapped executable memory, RWX protection, a PE header, a NOP
|
|
38
|
+
sled, high entropy, a thread starting in memory no image backs, and code
|
|
39
|
+
rewritten in place or unpacked from packed to code-like. Scores fall into
|
|
40
|
+
bands of low, review and likely injection, and the top band needs a signal
|
|
41
|
+
the memory map alone cannot give, so a private RWX page of the kind a JIT
|
|
42
|
+
compiler leaves stops at review.
|
|
43
|
+
- In playback, each region's rewrite history across the whole recording,
|
|
44
|
+
shown in its tooltip and as ticks on the timeline to scrub to. It is kept
|
|
45
|
+
beside the score rather than in it.
|
|
46
|
+
- A dashboard of system memory gauges, a scrolling usage timeline, a ranked
|
|
47
|
+
list of the processes using the most memory, and CSV and JSON export.
|
|
48
|
+
|
|
49
|
+
## [0.1.0] - 2026-09-11
|
|
50
|
+
|
|
51
|
+
Tagged, never published. PyPI refused the upload because the repository was
|
|
52
|
+
then named `Memlapse` and the trusted publisher waiting for it named
|
|
53
|
+
`memlapse`, and a trusted publisher matches the repository name exactly. No
|
|
54
|
+
release was made anywhere, and the tag is left where it is so that the version
|
|
55
|
+
number is never used for anything else. 0.1.1 is the same program under the
|
|
56
|
+
lower case name.
|
|
57
|
+
|
|
58
|
+
[0.1.1]: https://github.com/mjaksn/memlapse/compare/v0.1.0...v0.1.1
|
|
59
|
+
[0.1.0]: https://github.com/mjaksn/memlapse/tree/v0.1.0
|
memlapse-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 mjaksn
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# What the sdist carries beyond the package. Left to itself setuptools takes
|
|
2
|
+
# tests/test_*.py and nothing else from tests/, so the source distribution
|
|
3
|
+
# would hold a suite without its conftest.py, and without the files
|
|
4
|
+
# test_version.py reads, that could not run. With these it can test itself.
|
|
5
|
+
graft tests
|
|
6
|
+
include CHANGELOG.md requirements.txt requirements-dev.txt
|
|
7
|
+
global-exclude *.py[cod]
|
memlapse-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: memlapse
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: A Windows memory forensics tool that records a process's memory map over time, replays it, and scores each region for signs of code injection
|
|
5
|
+
Author: mjaksn
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/mjaksn/memlapse
|
|
8
|
+
Project-URL: Source, https://github.com/mjaksn/memlapse
|
|
9
|
+
Project-URL: Issues, https://github.com/mjaksn/memlapse/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/mjaksn/memlapse/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: memory-forensics,dfir,process-injection,malware-detection,windows,virtualqueryex,process-explorer,pyside6
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Win32 (MS Windows)
|
|
14
|
+
Classifier: Intended Audience :: Information Technology
|
|
15
|
+
Classifier: Intended Audience :: System Administrators
|
|
16
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Topic :: Security
|
|
21
|
+
Classifier: Topic :: System :: Monitoring
|
|
22
|
+
Requires-Python: >=3.14
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: PySide6<6.12,>=6.11.2
|
|
26
|
+
Requires-Dist: psutil<7.3,>=7.2.2
|
|
27
|
+
Requires-Dist: pyqtgraph<0.15,>=0.14.0
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
|
|
30
|
+
# memlapse
|
|
31
|
+
|
|
32
|
+
[](https://github.com/mjaksn/memlapse/actions/workflows/ci.yml)
|
|
33
|
+
[](https://github.com/mjaksn/memlapse/actions/workflows/release.yml)
|
|
34
|
+
[](https://pypi.org/project/memlapse/)
|
|
35
|
+
[](https://github.com/mjaksn/memlapse/blob/main/LICENSE)
|
|
36
|
+
|
|
37
|
+
A Windows memory forensics tool, Process Explorer / System Informer-style
|
|
38
|
+
monitoring, that records a process's memory map over time and replays it.
|
|
39
|
+
Recording and playback of the memory activity of specific threads is the
|
|
40
|
+
planned next phase (Phase 5). See
|
|
41
|
+
[docs/ARCHITECTURE.md](https://github.com/mjaksn/memlapse/blob/main/docs/ARCHITECTURE.md)
|
|
42
|
+
for the design and
|
|
43
|
+
[docs/RESEARCH_NOTES.md](https://github.com/mjaksn/memlapse/blob/main/docs/RESEARCH_NOTES.md)
|
|
44
|
+
for the reference material behind the detection heuristics and the ideas
|
|
45
|
+
queued for later phases.
|
|
46
|
+
|
|
47
|
+
Antivirus checks are made mostly when a process starts, which leaves code
|
|
48
|
+
written into a process that is already running unscanned. Sampling a live
|
|
49
|
+
process on a timer covers exactly that gap, and recording the samples lets an
|
|
50
|
+
analyst scrub back to the moment the memory changed.
|
|
51
|
+
|
|
52
|
+
## Status
|
|
53
|
+
|
|
54
|
+
- **Phase 1**, live process monitor (sortable, filterable table).
|
|
55
|
+
- **Phase 2**, memory-map view: select a process to see its VirtualQueryEx
|
|
56
|
+
region map, refreshed every second while on screen; click a region for a
|
|
57
|
+
hex preview of its bytes.
|
|
58
|
+
- **Phase 3**, record a process's memory map over time to SQLite.
|
|
59
|
+
- **Phase 4**, playback: scrub the recording with the timeline to replay how
|
|
60
|
+
its regions and footprint evolved.
|
|
61
|
+
- **Phase 6**, injection heuristics: each executable region gets a 0 to 100
|
|
62
|
+
score (unbacked private or mapped executable memory, RWX, PE header, NOP
|
|
63
|
+
sled, high entropy, and a thread whose start address lands in memory no
|
|
64
|
+
image backs) shown as a Score column with a heat tint and a reason tooltip
|
|
65
|
+
in the region view, banded low, review or likely injection. The top band
|
|
66
|
+
needs a signal the memory map alone cannot give, so a page that is merely
|
|
67
|
+
private and RWX, which is what a JIT compiler leaves behind, stops at
|
|
68
|
+
review. A fourth band, allowlisted, exists for a region whose every scoring
|
|
69
|
+
rule an entry has excused, but nothing in the app creates an entry yet, so
|
|
70
|
+
no run of it shows that band. The score also rises for a region whose code
|
|
71
|
+
was rewritten in place, and again if its entropy fell from packed to
|
|
72
|
+
code-like, the traces an injector leaves when it overwrites executable
|
|
73
|
+
memory that already exists and when a payload decrypts itself there. Both
|
|
74
|
+
work in playback, between consecutive samples, and in live mode, between
|
|
75
|
+
one refresh and the next. A replay then says more than a live watch can:
|
|
76
|
+
every score is still the answer for the sample being shown, but a region's
|
|
77
|
+
tooltip also says how many times the whole recording saw it rewritten and
|
|
78
|
+
when that last happened, and the timeline carries a tick at each of those
|
|
79
|
+
samples to scrub to. A region rewritten once, minutes ago, scores nothing
|
|
80
|
+
now and would otherwise pass for quiet.
|
|
81
|
+
|
|
82
|
+
- **Dashboard**, a vivid, near-live overview tab: system RAM/swap gauges, a
|
|
83
|
+
scrolling usage timeline, a heat-ranked top-process list (click to drill into
|
|
84
|
+
the monitor), leak/anomaly interpretation, and CSV/JSON export of the window.
|
|
85
|
+
|
|
86
|
+
Next: per-thread memory activity via ETW (Phase 5).
|
|
87
|
+
|
|
88
|
+
## Use
|
|
89
|
+
|
|
90
|
+
1. Open the **Forensic Monitor** tab (the app starts on the Dashboard; clicking
|
|
91
|
+
a process bar there also jumps to the monitor), then select a process (left)
|
|
92
|
+
to inspect its live memory map (right).
|
|
93
|
+
2. Click **● Record** to sample it over time; **■ Stop** when done. Right-click
|
|
94
|
+
a region to save its bytes for a disassembler or a YARA rule.
|
|
95
|
+
3. **Open Recording ▾** → pick a recording to enter playback, then drag the
|
|
96
|
+
timeline (or press ▶) to replay it. A tick on the timeline is a sample
|
|
97
|
+
where a region's executable bytes changed, which is where to scrub.
|
|
98
|
+
**Live** returns to real-time mode.
|
|
99
|
+
|
|
100
|
+
## Install
|
|
101
|
+
|
|
102
|
+
Windows only, Python 3.14.
|
|
103
|
+
|
|
104
|
+
```powershell
|
|
105
|
+
pip install memlapse
|
|
106
|
+
|
|
107
|
+
memlapse # live monitor
|
|
108
|
+
memlapse --elevate # relaunch elevated to read system and other users' processes
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`python -m memlapse` runs the same thing. Running elevated enables
|
|
112
|
+
`SeDebugPrivilege`, required to read most system and other-user processes.
|
|
113
|
+
The status bar reports the privilege state when the window opens.
|
|
114
|
+
|
|
115
|
+
The package accepts a range of versions of its three dependencies. For the
|
|
116
|
+
exact versions it is tested with, each checked against the hashes PyPI
|
|
117
|
+
publishes, install from a checkout instead:
|
|
118
|
+
|
|
119
|
+
## Run from a checkout
|
|
120
|
+
|
|
121
|
+
```powershell
|
|
122
|
+
python -m venv .venv
|
|
123
|
+
.venv\Scripts\Activate.ps1
|
|
124
|
+
pip install --require-hashes -r requirements.txt
|
|
125
|
+
|
|
126
|
+
python main.py # live monitor
|
|
127
|
+
python main.py --elevate # relaunch elevated
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
VS Code and PyCharm each have a plain and an elevated launcher checked in, in
|
|
131
|
+
`.vscode/launch.json` and `.idea/runConfigurations/`. The elevated one takes
|
|
132
|
+
the same `--elevate` path as the command line above: Windows starts a separate
|
|
133
|
+
elevated process through UAC and the one the editor started exits immediately,
|
|
134
|
+
so nothing is left for a debugger to attach to. To debug with privileges,
|
|
135
|
+
start the editor elevated and use the plain launcher, which inherits them.
|
|
136
|
+
|
|
137
|
+
## Test
|
|
138
|
+
|
|
139
|
+
```powershell
|
|
140
|
+
pip install -r requirements-dev.txt
|
|
141
|
+
python -m pytest
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Dependencies are pinned by version and hash. The requirements files name every
|
|
145
|
+
package with an exact version, and `scripts/lock_hashes.py` writes the hashes
|
|
146
|
+
under each pin from the digests PyPI reports; the top of `requirements.txt`
|
|
147
|
+
says how to move a version.
|
|
148
|
+
|
|
149
|
+
## Licence
|
|
150
|
+
|
|
151
|
+
MIT. See [LICENSE](https://github.com/mjaksn/memlapse/blob/main/LICENSE).
|
|
152
|
+
|
|
153
|
+
Changes are listed in
|
|
154
|
+
[CHANGELOG.md](https://github.com/mjaksn/memlapse/blob/main/CHANGELOG.md).
|
memlapse-0.1.1/README.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# memlapse
|
|
2
|
+
|
|
3
|
+
[](https://github.com/mjaksn/memlapse/actions/workflows/ci.yml)
|
|
4
|
+
[](https://github.com/mjaksn/memlapse/actions/workflows/release.yml)
|
|
5
|
+
[](https://pypi.org/project/memlapse/)
|
|
6
|
+
[](https://github.com/mjaksn/memlapse/blob/main/LICENSE)
|
|
7
|
+
|
|
8
|
+
A Windows memory forensics tool, Process Explorer / System Informer-style
|
|
9
|
+
monitoring, that records a process's memory map over time and replays it.
|
|
10
|
+
Recording and playback of the memory activity of specific threads is the
|
|
11
|
+
planned next phase (Phase 5). See
|
|
12
|
+
[docs/ARCHITECTURE.md](https://github.com/mjaksn/memlapse/blob/main/docs/ARCHITECTURE.md)
|
|
13
|
+
for the design and
|
|
14
|
+
[docs/RESEARCH_NOTES.md](https://github.com/mjaksn/memlapse/blob/main/docs/RESEARCH_NOTES.md)
|
|
15
|
+
for the reference material behind the detection heuristics and the ideas
|
|
16
|
+
queued for later phases.
|
|
17
|
+
|
|
18
|
+
Antivirus checks are made mostly when a process starts, which leaves code
|
|
19
|
+
written into a process that is already running unscanned. Sampling a live
|
|
20
|
+
process on a timer covers exactly that gap, and recording the samples lets an
|
|
21
|
+
analyst scrub back to the moment the memory changed.
|
|
22
|
+
|
|
23
|
+
## Status
|
|
24
|
+
|
|
25
|
+
- **Phase 1**, live process monitor (sortable, filterable table).
|
|
26
|
+
- **Phase 2**, memory-map view: select a process to see its VirtualQueryEx
|
|
27
|
+
region map, refreshed every second while on screen; click a region for a
|
|
28
|
+
hex preview of its bytes.
|
|
29
|
+
- **Phase 3**, record a process's memory map over time to SQLite.
|
|
30
|
+
- **Phase 4**, playback: scrub the recording with the timeline to replay how
|
|
31
|
+
its regions and footprint evolved.
|
|
32
|
+
- **Phase 6**, injection heuristics: each executable region gets a 0 to 100
|
|
33
|
+
score (unbacked private or mapped executable memory, RWX, PE header, NOP
|
|
34
|
+
sled, high entropy, and a thread whose start address lands in memory no
|
|
35
|
+
image backs) shown as a Score column with a heat tint and a reason tooltip
|
|
36
|
+
in the region view, banded low, review or likely injection. The top band
|
|
37
|
+
needs a signal the memory map alone cannot give, so a page that is merely
|
|
38
|
+
private and RWX, which is what a JIT compiler leaves behind, stops at
|
|
39
|
+
review. A fourth band, allowlisted, exists for a region whose every scoring
|
|
40
|
+
rule an entry has excused, but nothing in the app creates an entry yet, so
|
|
41
|
+
no run of it shows that band. The score also rises for a region whose code
|
|
42
|
+
was rewritten in place, and again if its entropy fell from packed to
|
|
43
|
+
code-like, the traces an injector leaves when it overwrites executable
|
|
44
|
+
memory that already exists and when a payload decrypts itself there. Both
|
|
45
|
+
work in playback, between consecutive samples, and in live mode, between
|
|
46
|
+
one refresh and the next. A replay then says more than a live watch can:
|
|
47
|
+
every score is still the answer for the sample being shown, but a region's
|
|
48
|
+
tooltip also says how many times the whole recording saw it rewritten and
|
|
49
|
+
when that last happened, and the timeline carries a tick at each of those
|
|
50
|
+
samples to scrub to. A region rewritten once, minutes ago, scores nothing
|
|
51
|
+
now and would otherwise pass for quiet.
|
|
52
|
+
|
|
53
|
+
- **Dashboard**, a vivid, near-live overview tab: system RAM/swap gauges, a
|
|
54
|
+
scrolling usage timeline, a heat-ranked top-process list (click to drill into
|
|
55
|
+
the monitor), leak/anomaly interpretation, and CSV/JSON export of the window.
|
|
56
|
+
|
|
57
|
+
Next: per-thread memory activity via ETW (Phase 5).
|
|
58
|
+
|
|
59
|
+
## Use
|
|
60
|
+
|
|
61
|
+
1. Open the **Forensic Monitor** tab (the app starts on the Dashboard; clicking
|
|
62
|
+
a process bar there also jumps to the monitor), then select a process (left)
|
|
63
|
+
to inspect its live memory map (right).
|
|
64
|
+
2. Click **● Record** to sample it over time; **■ Stop** when done. Right-click
|
|
65
|
+
a region to save its bytes for a disassembler or a YARA rule.
|
|
66
|
+
3. **Open Recording ▾** → pick a recording to enter playback, then drag the
|
|
67
|
+
timeline (or press ▶) to replay it. A tick on the timeline is a sample
|
|
68
|
+
where a region's executable bytes changed, which is where to scrub.
|
|
69
|
+
**Live** returns to real-time mode.
|
|
70
|
+
|
|
71
|
+
## Install
|
|
72
|
+
|
|
73
|
+
Windows only, Python 3.14.
|
|
74
|
+
|
|
75
|
+
```powershell
|
|
76
|
+
pip install memlapse
|
|
77
|
+
|
|
78
|
+
memlapse # live monitor
|
|
79
|
+
memlapse --elevate # relaunch elevated to read system and other users' processes
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`python -m memlapse` runs the same thing. Running elevated enables
|
|
83
|
+
`SeDebugPrivilege`, required to read most system and other-user processes.
|
|
84
|
+
The status bar reports the privilege state when the window opens.
|
|
85
|
+
|
|
86
|
+
The package accepts a range of versions of its three dependencies. For the
|
|
87
|
+
exact versions it is tested with, each checked against the hashes PyPI
|
|
88
|
+
publishes, install from a checkout instead:
|
|
89
|
+
|
|
90
|
+
## Run from a checkout
|
|
91
|
+
|
|
92
|
+
```powershell
|
|
93
|
+
python -m venv .venv
|
|
94
|
+
.venv\Scripts\Activate.ps1
|
|
95
|
+
pip install --require-hashes -r requirements.txt
|
|
96
|
+
|
|
97
|
+
python main.py # live monitor
|
|
98
|
+
python main.py --elevate # relaunch elevated
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
VS Code and PyCharm each have a plain and an elevated launcher checked in, in
|
|
102
|
+
`.vscode/launch.json` and `.idea/runConfigurations/`. The elevated one takes
|
|
103
|
+
the same `--elevate` path as the command line above: Windows starts a separate
|
|
104
|
+
elevated process through UAC and the one the editor started exits immediately,
|
|
105
|
+
so nothing is left for a debugger to attach to. To debug with privileges,
|
|
106
|
+
start the editor elevated and use the plain launcher, which inherits them.
|
|
107
|
+
|
|
108
|
+
## Test
|
|
109
|
+
|
|
110
|
+
```powershell
|
|
111
|
+
pip install -r requirements-dev.txt
|
|
112
|
+
python -m pytest
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Dependencies are pinned by version and hash. The requirements files name every
|
|
116
|
+
package with an exact version, and `scripts/lock_hashes.py` writes the hashes
|
|
117
|
+
under each pin from the digests PyPI reports; the top of `requirements.txt`
|
|
118
|
+
says how to move a version.
|
|
119
|
+
|
|
120
|
+
## Licence
|
|
121
|
+
|
|
122
|
+
MIT. See [LICENSE](https://github.com/mjaksn/memlapse/blob/main/LICENSE).
|
|
123
|
+
|
|
124
|
+
Changes are listed in
|
|
125
|
+
[CHANGELOG.md](https://github.com/mjaksn/memlapse/blob/main/CHANGELOG.md).
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""memlapse, a Windows memory forensics tool.
|
|
2
|
+
|
|
3
|
+
Process Explorer / System Informer-style monitoring with recording and
|
|
4
|
+
playback of a process's memory map over time, and a per-region injection
|
|
5
|
+
score. Per-thread attribution via ETW is planned (see docs/ARCHITECTURE.md).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
__version__ = "0.1.1"
|