reg2es 2.2.0__tar.gz → 2.2.2__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.
- reg2es-2.2.2/PKG-INFO +275 -0
- reg2es-2.2.2/README.md +259 -0
- reg2es-2.2.2/src/reg2es/__about__.py +1 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/models/Reg2es.py +38 -4
- reg2es-2.2.2/src/reg2es/plugins/mounteddevices.py +16 -0
- reg2es-2.2.2/src/reg2es/plugins/usbstor.py +28 -0
- reg2es-2.2.2/src/reg2es/plugins/wordwheelquery.py +59 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/presenters/Reg2esPresenter.py +17 -1
- reg2es-2.2.2/tests/test_case_plugins.py +70 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/test_es_contract.py +10 -2
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/test_integration.py +1 -1
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/test_model.py +46 -3
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/test_plugins.py +4 -1
- reg2es-2.2.0/PKG-INFO +0 -375
- reg2es-2.2.0/README.md +0 -359
- reg2es-2.2.0/src/reg2es/__about__.py +0 -1
- {reg2es-2.2.0 → reg2es-2.2.2}/.devcontainer/Dockerfile +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/.devcontainer/devcontainer.json +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/.devcontainer/docker-compose.yaml +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/.devcontainer/postCreateCommands.fish +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/.github/collect_runtime_licenses.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/.github/workflows/publish-binary-pypi.yaml +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/.github/workflows/publish-release.yaml +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/.github/workflows/test.yaml +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/.gitignore +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/CONTRIBUTING.md +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/LICENSE +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/LICENSES/Apache-2.0.txt +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/pyproject.toml +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/__init__.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/models/ElasticsearchUtils.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/__init__.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/antivirus.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/auditpol.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/base.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/compname.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/env.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/filedialogmru.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/gpo.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/kb.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/keyboard.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/lastloggedon.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/lastshutdown.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/localgroups.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/localusers.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/mndmru.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/mstscmru.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/office_macros.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/portproxy.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/printer_history.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/printer_ports.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/proxy.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/putty.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/rdphint.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/recentdocs.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/regtime.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/run.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/runmru.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/services.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/shimcache.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/srum.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/sysinternals.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/systeminfo.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/tasks.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/teamviewer.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/timezone.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/typedurls.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/uninstall.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/userassist.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/usersids.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/version.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/presenters/Reg2jsonPresenter.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/views/BaseView.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/views/Reg2esView.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/views/Reg2jsonView.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/__init__.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/conftest.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/__init__.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/reg_mock.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_antivirus.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_compname.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_env.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_filedialogmru.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_keyboard.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_lastloggedon.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_lastshutdown.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_localgroups.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_localusers.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_mndmru.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_mstscmru.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_portproxy.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_printer_history.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_printer_ports.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_proxy.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_putty.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_rdphint.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_recentdocs.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_run.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_runmru.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_services.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_shimcache.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_sysinternals.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_systeminfo.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_tasks.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_timezone.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_typedurls.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_uninstall.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_userassist.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_usersids.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_version.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/test_output_contract.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/test_plugin_timestamps.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/test_runtime_licenses.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/test_shared_output.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/test_shimcache_entries.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/test_timestamp_review.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/tests/test_timestamp_section_c.py +0 -0
- {reg2es-2.2.0 → reg2es-2.2.2}/uv.lock +0 -0
reg2es-2.2.2/PKG-INFO
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: reg2es
|
|
3
|
+
Version: 2.2.2
|
|
4
|
+
Summary: A fast library for parsing and importing Windows NT Registry(REGF) into Elasticsearch.
|
|
5
|
+
Author-email: sumeshi <j15322sn@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: elasticsearch,json,registry
|
|
9
|
+
Requires-Python: >=3.13
|
|
10
|
+
Requires-Dist: elasticsearch>=9.4.1
|
|
11
|
+
Requires-Dist: orjson>=3.11.9
|
|
12
|
+
Requires-Dist: python-registry>=1.3.1
|
|
13
|
+
Requires-Dist: tqdm>=4.67.3
|
|
14
|
+
Requires-Dist: urllib3>=2.6.3
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# reg2es
|
|
18
|
+
|
|
19
|
+
[](LICENSE)
|
|
20
|
+
[](https://pypi.org/project/reg2es/)
|
|
21
|
+
|
|
22
|
+

|
|
23
|
+
|
|
24
|
+
A command-line tool and Python library for extracting forensic artifacts from
|
|
25
|
+
Windows NT Registry (REGF) hives and importing them into Elasticsearch.
|
|
26
|
+
|
|
27
|
+
The original plugin set is based on
|
|
28
|
+
[airbus-cert/regrippy](https://github.com/airbus-cert/regrippy); the package
|
|
29
|
+
also includes reg2es-specific additions. The project also draws inspiration
|
|
30
|
+
from [RegRipper](https://github.com/keydet89/RegRipper3.0). `reg2es` runs
|
|
31
|
+
standalone and does not require the `regrippy` package at runtime. Both
|
|
32
|
+
`reg2es` and `reg2json` use the same plugin runner and emit the same
|
|
33
|
+
ECS-oriented documents.
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
**reg2es** can be used as a standalone command-line tool or integrated directly into your Python scripts.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
reg2es SYSTEM SOFTWARE SAM
|
|
42
|
+
reg2json NTUSER.DAT -o artifacts.json
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from reg2es import reg2es
|
|
47
|
+
|
|
48
|
+
reg2es(["SYSTEM", "SOFTWARE", "SAM"])
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
### Arguments
|
|
53
|
+
|
|
54
|
+
Multiple inputs passed in one invocation form one registry dataset. This lets
|
|
55
|
+
plugins such as `localgroups` enrich SAM results using SOFTWARE data, regardless
|
|
56
|
+
of the order of input paths.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
reg2es SAM SOFTWARE
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
reg2es can recursively process all registry files under a specified directory:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
tree .
|
|
66
|
+
regfiles/
|
|
67
|
+
├── NTUSER.DAT
|
|
68
|
+
├── NTUSER.MAN
|
|
69
|
+
├── SAM
|
|
70
|
+
└── subdirectory/
|
|
71
|
+
├── SOFTWARE
|
|
72
|
+
└── subsubdirectory/
|
|
73
|
+
├── SYSTEM
|
|
74
|
+
└── UsrClass.dat
|
|
75
|
+
|
|
76
|
+
reg2es /regfiles/ # Recursively collects hives as one dataset.
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Directory scans process REGF hive files only. Registry transaction logs and
|
|
80
|
+
unrelated files are not treated as standalone hives.
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
### Common options
|
|
84
|
+
|
|
85
|
+
- `--plugin NAME`: run one plugin; repeat to select several. By default,
|
|
86
|
+
compatible, default-enabled plugins run. The exhaustive `regtime` plugin is
|
|
87
|
+
opt-in.
|
|
88
|
+
- `--list-plugins`: print the 41 bundled plugins and exit.
|
|
89
|
+
- `--size N`: set the generation and indexing chunk size (default: 500).
|
|
90
|
+
- `--tags tag1,tag2`: add tags to every document.
|
|
91
|
+
- `--quiet`: suppress progress output.
|
|
92
|
+
|
|
93
|
+
`reg2es` also accepts Elasticsearch connection options including
|
|
94
|
+
`--host`, `--port`, `--index`, `--scheme`, `--pipeline`, `--login`, `--pwd`,
|
|
95
|
+
`--ca-certs`, and `--no-verify-certs`. TLS verification is enabled by default;
|
|
96
|
+
use `--ca-certs /path/to/ca.pem` for a private CA bundle. Run `reg2es --help`
|
|
97
|
+
or `reg2json --help` for the full current interface.
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
### Examples
|
|
101
|
+
|
|
102
|
+
When using from the command line:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
reg2es SYSTEM --plugin services --host localhost --index registry-artifacts
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
When using from a Python script:
|
|
109
|
+
|
|
110
|
+
```py
|
|
111
|
+
reg2es(
|
|
112
|
+
["SYSTEM", "SOFTWARE"],
|
|
113
|
+
host="localhost",
|
|
114
|
+
index="registry-artifacts",
|
|
115
|
+
plugin_names=["services", "systeminfo"],
|
|
116
|
+
additional_tags=["host-01", "case-42"],
|
|
117
|
+
)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
With Elasticsearch authentication:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
reg2es SYSTEM --login elastic --pwd '******'
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
## Appendix
|
|
128
|
+
|
|
129
|
+
### reg2json
|
|
130
|
+
|
|
131
|
+
**reg2es** also includes `reg2json`, a command-line tool for converting Windows NT Registry into JSON files. :sushi: :sushi: :sushi:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
reg2json NTUSER.DAT --plugin userassist -o artifacts.json
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
`reg2json` also supports line-delimited output. `--format jsonl` (or `ndjson`)
|
|
138
|
+
writes one record per line without holding the entire dataset in memory. When
|
|
139
|
+
no output path is specified, the default extension is `.jsonl`:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
reg2json NTUSER.DAT --plugin userassist --format jsonl -o artifacts.jsonl
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Use `--split` to write one file per plugin. With `--split`, `-o` names the
|
|
146
|
+
output directory:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
reg2json collected-hives/ --split -o artifacts/
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The exhaustive `regtime` timeline is excluded from the default plugin set
|
|
153
|
+
because it emits one record for every registry key. Run it explicitly when
|
|
154
|
+
needed:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
reg2json collected-hives/ --plugin regtime -o regtime.json
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
You can also convert registry files directly into a Python `list[dict]`:
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
from reg2es import reg2json
|
|
164
|
+
|
|
165
|
+
result: list[dict] = reg2json(
|
|
166
|
+
["SOFTWARE", "SAM"],
|
|
167
|
+
plugin_names=["localgroups"],
|
|
168
|
+
additional_tags=["host-01"],
|
|
169
|
+
)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
## Output Format Example
|
|
174
|
+
|
|
175
|
+
Each plugin result becomes one document. Standard `event`,
|
|
176
|
+
`registry`, `log.file`, `tags`, and `@timestamp` fields describe the artifact.
|
|
177
|
+
Lossless plugin-specific data and the original offline-hive location are kept
|
|
178
|
+
under `reg2es`.
|
|
179
|
+
|
|
180
|
+
When a plugin has a reliable artifact time, it uses that time for `@timestamp`.
|
|
181
|
+
Otherwise it uses the registry key's LastWrite; if no valid time exists,
|
|
182
|
+
`@timestamp` is omitted. Check `reg2es.timestamp.source` and
|
|
183
|
+
`reg2es.timestamp.meaning` before treating a result as an activity time.
|
|
184
|
+
Original values and fallback reasons remain in the document.
|
|
185
|
+
|
|
186
|
+
The current ECS output changes `registry.data.bytes` from a byte count to
|
|
187
|
+
Base64, uses `REG_*` names in `registry.data.type`, and includes the value
|
|
188
|
+
name in `registry.path`. Existing Elasticsearch mappings may conflict with
|
|
189
|
+
these fields; use a new index for the updated output.
|
|
190
|
+
|
|
191
|
+
```json
|
|
192
|
+
{
|
|
193
|
+
"@timestamp": "2015-10-30T07:24:57.814133+00:00",
|
|
194
|
+
"event": {
|
|
195
|
+
"kind": "event",
|
|
196
|
+
"category": ["registry"],
|
|
197
|
+
"type": ["info"],
|
|
198
|
+
"action": "compname"
|
|
199
|
+
},
|
|
200
|
+
"registry": {
|
|
201
|
+
"hive": "HKLM",
|
|
202
|
+
"key": "SYSTEM\\ControlSet001\\Control\\ComputerName\\ComputerName",
|
|
203
|
+
"path": "HKLM\\SYSTEM\\ControlSet001\\Control\\ComputerName\\ComputerName\\ComputerName",
|
|
204
|
+
"value": "ComputerName",
|
|
205
|
+
"data": {
|
|
206
|
+
"type": "REG_SZ",
|
|
207
|
+
"strings": ["DESKTOP-EXAMPLE"]
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
"log": {
|
|
211
|
+
"file": {"path": "/evidence/SYSTEM"}
|
|
212
|
+
},
|
|
213
|
+
"tags": ["registry", "host-01"],
|
|
214
|
+
"reg2es": {
|
|
215
|
+
"plugin": {"name": "compname"},
|
|
216
|
+
"source": {
|
|
217
|
+
"hive": "SYSTEM",
|
|
218
|
+
"key_path": "ROOT\\ControlSet001\\Control\\ComputerName\\ComputerName"
|
|
219
|
+
},
|
|
220
|
+
"value_data": "DESKTOP-EXAMPLE",
|
|
221
|
+
"value_type": "RegSZ"
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
## Installation
|
|
228
|
+
|
|
229
|
+
### From PyPI
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
$ pip install reg2es
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### With uv
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
$ uv add reg2es
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### From GitHub Releases
|
|
242
|
+
|
|
243
|
+
Standalone binaries built with Nuitka are available from GitHub Releases for systems without a Python environment.
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
$ chmod +x ./reg2es
|
|
247
|
+
$ ./reg2es {{options...}}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
```powershell
|
|
251
|
+
> reg2es.exe {{options...}}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
## Contributing
|
|
256
|
+
|
|
257
|
+
The source code for **reg2es** is hosted on GitHub: https://github.com/sumeshi/reg2es.
|
|
258
|
+
Please report issues and feature requests. :sushi: :sushi: :sushi:
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
## License
|
|
262
|
+
|
|
263
|
+
Standalone release ZIPs include `LICENSES.txt` with the project, bundled plugin,
|
|
264
|
+
runtime dependency and build Python license notices. Keep it with the executables
|
|
265
|
+
when redistributing them.
|
|
266
|
+
|
|
267
|
+
**reg2es** is released under the [MIT](LICENSE) License.
|
|
268
|
+
|
|
269
|
+
### Third-Party Notices
|
|
270
|
+
|
|
271
|
+
This product includes code derived from
|
|
272
|
+
[regrippy v2.0.3](https://github.com/airbus-cert/regrippy) by Airbus CERT,
|
|
273
|
+
licensed under Apache License 2.0. The included ShimCache parser carries
|
|
274
|
+
the original copyright notice of Andrew Davis, Mandiant (2012). See the
|
|
275
|
+
[Apache 2.0 license text](LICENSES/Apache-2.0.txt) and the source-file notices.
|
reg2es-2.2.2/README.md
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# reg2es
|
|
2
|
+
|
|
3
|
+
[](LICENSE)
|
|
4
|
+
[](https://pypi.org/project/reg2es/)
|
|
5
|
+
|
|
6
|
+

|
|
7
|
+
|
|
8
|
+
A command-line tool and Python library for extracting forensic artifacts from
|
|
9
|
+
Windows NT Registry (REGF) hives and importing them into Elasticsearch.
|
|
10
|
+
|
|
11
|
+
The original plugin set is based on
|
|
12
|
+
[airbus-cert/regrippy](https://github.com/airbus-cert/regrippy); the package
|
|
13
|
+
also includes reg2es-specific additions. The project also draws inspiration
|
|
14
|
+
from [RegRipper](https://github.com/keydet89/RegRipper3.0). `reg2es` runs
|
|
15
|
+
standalone and does not require the `regrippy` package at runtime. Both
|
|
16
|
+
`reg2es` and `reg2json` use the same plugin runner and emit the same
|
|
17
|
+
ECS-oriented documents.
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
**reg2es** can be used as a standalone command-line tool or integrated directly into your Python scripts.
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
reg2es SYSTEM SOFTWARE SAM
|
|
26
|
+
reg2json NTUSER.DAT -o artifacts.json
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from reg2es import reg2es
|
|
31
|
+
|
|
32
|
+
reg2es(["SYSTEM", "SOFTWARE", "SAM"])
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
### Arguments
|
|
37
|
+
|
|
38
|
+
Multiple inputs passed in one invocation form one registry dataset. This lets
|
|
39
|
+
plugins such as `localgroups` enrich SAM results using SOFTWARE data, regardless
|
|
40
|
+
of the order of input paths.
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
reg2es SAM SOFTWARE
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
reg2es can recursively process all registry files under a specified directory:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
tree .
|
|
50
|
+
regfiles/
|
|
51
|
+
├── NTUSER.DAT
|
|
52
|
+
├── NTUSER.MAN
|
|
53
|
+
├── SAM
|
|
54
|
+
└── subdirectory/
|
|
55
|
+
├── SOFTWARE
|
|
56
|
+
└── subsubdirectory/
|
|
57
|
+
├── SYSTEM
|
|
58
|
+
└── UsrClass.dat
|
|
59
|
+
|
|
60
|
+
reg2es /regfiles/ # Recursively collects hives as one dataset.
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Directory scans process REGF hive files only. Registry transaction logs and
|
|
64
|
+
unrelated files are not treated as standalone hives.
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
### Common options
|
|
68
|
+
|
|
69
|
+
- `--plugin NAME`: run one plugin; repeat to select several. By default,
|
|
70
|
+
compatible, default-enabled plugins run. The exhaustive `regtime` plugin is
|
|
71
|
+
opt-in.
|
|
72
|
+
- `--list-plugins`: print the 41 bundled plugins and exit.
|
|
73
|
+
- `--size N`: set the generation and indexing chunk size (default: 500).
|
|
74
|
+
- `--tags tag1,tag2`: add tags to every document.
|
|
75
|
+
- `--quiet`: suppress progress output.
|
|
76
|
+
|
|
77
|
+
`reg2es` also accepts Elasticsearch connection options including
|
|
78
|
+
`--host`, `--port`, `--index`, `--scheme`, `--pipeline`, `--login`, `--pwd`,
|
|
79
|
+
`--ca-certs`, and `--no-verify-certs`. TLS verification is enabled by default;
|
|
80
|
+
use `--ca-certs /path/to/ca.pem` for a private CA bundle. Run `reg2es --help`
|
|
81
|
+
or `reg2json --help` for the full current interface.
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
### Examples
|
|
85
|
+
|
|
86
|
+
When using from the command line:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
reg2es SYSTEM --plugin services --host localhost --index registry-artifacts
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
When using from a Python script:
|
|
93
|
+
|
|
94
|
+
```py
|
|
95
|
+
reg2es(
|
|
96
|
+
["SYSTEM", "SOFTWARE"],
|
|
97
|
+
host="localhost",
|
|
98
|
+
index="registry-artifacts",
|
|
99
|
+
plugin_names=["services", "systeminfo"],
|
|
100
|
+
additional_tags=["host-01", "case-42"],
|
|
101
|
+
)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
With Elasticsearch authentication:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
reg2es SYSTEM --login elastic --pwd '******'
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
## Appendix
|
|
112
|
+
|
|
113
|
+
### reg2json
|
|
114
|
+
|
|
115
|
+
**reg2es** also includes `reg2json`, a command-line tool for converting Windows NT Registry into JSON files. :sushi: :sushi: :sushi:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
reg2json NTUSER.DAT --plugin userassist -o artifacts.json
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
`reg2json` also supports line-delimited output. `--format jsonl` (or `ndjson`)
|
|
122
|
+
writes one record per line without holding the entire dataset in memory. When
|
|
123
|
+
no output path is specified, the default extension is `.jsonl`:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
reg2json NTUSER.DAT --plugin userassist --format jsonl -o artifacts.jsonl
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Use `--split` to write one file per plugin. With `--split`, `-o` names the
|
|
130
|
+
output directory:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
reg2json collected-hives/ --split -o artifacts/
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The exhaustive `regtime` timeline is excluded from the default plugin set
|
|
137
|
+
because it emits one record for every registry key. Run it explicitly when
|
|
138
|
+
needed:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
reg2json collected-hives/ --plugin regtime -o regtime.json
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
You can also convert registry files directly into a Python `list[dict]`:
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
from reg2es import reg2json
|
|
148
|
+
|
|
149
|
+
result: list[dict] = reg2json(
|
|
150
|
+
["SOFTWARE", "SAM"],
|
|
151
|
+
plugin_names=["localgroups"],
|
|
152
|
+
additional_tags=["host-01"],
|
|
153
|
+
)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
## Output Format Example
|
|
158
|
+
|
|
159
|
+
Each plugin result becomes one document. Standard `event`,
|
|
160
|
+
`registry`, `log.file`, `tags`, and `@timestamp` fields describe the artifact.
|
|
161
|
+
Lossless plugin-specific data and the original offline-hive location are kept
|
|
162
|
+
under `reg2es`.
|
|
163
|
+
|
|
164
|
+
When a plugin has a reliable artifact time, it uses that time for `@timestamp`.
|
|
165
|
+
Otherwise it uses the registry key's LastWrite; if no valid time exists,
|
|
166
|
+
`@timestamp` is omitted. Check `reg2es.timestamp.source` and
|
|
167
|
+
`reg2es.timestamp.meaning` before treating a result as an activity time.
|
|
168
|
+
Original values and fallback reasons remain in the document.
|
|
169
|
+
|
|
170
|
+
The current ECS output changes `registry.data.bytes` from a byte count to
|
|
171
|
+
Base64, uses `REG_*` names in `registry.data.type`, and includes the value
|
|
172
|
+
name in `registry.path`. Existing Elasticsearch mappings may conflict with
|
|
173
|
+
these fields; use a new index for the updated output.
|
|
174
|
+
|
|
175
|
+
```json
|
|
176
|
+
{
|
|
177
|
+
"@timestamp": "2015-10-30T07:24:57.814133+00:00",
|
|
178
|
+
"event": {
|
|
179
|
+
"kind": "event",
|
|
180
|
+
"category": ["registry"],
|
|
181
|
+
"type": ["info"],
|
|
182
|
+
"action": "compname"
|
|
183
|
+
},
|
|
184
|
+
"registry": {
|
|
185
|
+
"hive": "HKLM",
|
|
186
|
+
"key": "SYSTEM\\ControlSet001\\Control\\ComputerName\\ComputerName",
|
|
187
|
+
"path": "HKLM\\SYSTEM\\ControlSet001\\Control\\ComputerName\\ComputerName\\ComputerName",
|
|
188
|
+
"value": "ComputerName",
|
|
189
|
+
"data": {
|
|
190
|
+
"type": "REG_SZ",
|
|
191
|
+
"strings": ["DESKTOP-EXAMPLE"]
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
"log": {
|
|
195
|
+
"file": {"path": "/evidence/SYSTEM"}
|
|
196
|
+
},
|
|
197
|
+
"tags": ["registry", "host-01"],
|
|
198
|
+
"reg2es": {
|
|
199
|
+
"plugin": {"name": "compname"},
|
|
200
|
+
"source": {
|
|
201
|
+
"hive": "SYSTEM",
|
|
202
|
+
"key_path": "ROOT\\ControlSet001\\Control\\ComputerName\\ComputerName"
|
|
203
|
+
},
|
|
204
|
+
"value_data": "DESKTOP-EXAMPLE",
|
|
205
|
+
"value_type": "RegSZ"
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
## Installation
|
|
212
|
+
|
|
213
|
+
### From PyPI
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
$ pip install reg2es
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### With uv
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
$ uv add reg2es
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### From GitHub Releases
|
|
226
|
+
|
|
227
|
+
Standalone binaries built with Nuitka are available from GitHub Releases for systems without a Python environment.
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
$ chmod +x ./reg2es
|
|
231
|
+
$ ./reg2es {{options...}}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
```powershell
|
|
235
|
+
> reg2es.exe {{options...}}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
## Contributing
|
|
240
|
+
|
|
241
|
+
The source code for **reg2es** is hosted on GitHub: https://github.com/sumeshi/reg2es.
|
|
242
|
+
Please report issues and feature requests. :sushi: :sushi: :sushi:
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
## License
|
|
246
|
+
|
|
247
|
+
Standalone release ZIPs include `LICENSES.txt` with the project, bundled plugin,
|
|
248
|
+
runtime dependency and build Python license notices. Keep it with the executables
|
|
249
|
+
when redistributing them.
|
|
250
|
+
|
|
251
|
+
**reg2es** is released under the [MIT](LICENSE) License.
|
|
252
|
+
|
|
253
|
+
### Third-Party Notices
|
|
254
|
+
|
|
255
|
+
This product includes code derived from
|
|
256
|
+
[regrippy v2.0.3](https://github.com/airbus-cert/regrippy) by Airbus CERT,
|
|
257
|
+
licensed under Apache License 2.0. The included ShimCache parser carries
|
|
258
|
+
the original copyright notice of Andrew Davis, Mandiant (2012). See the
|
|
259
|
+
[Apache 2.0 license text](LICENSES/Apache-2.0.txt) and the source-file notices.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.2.2"
|
|
@@ -20,6 +20,7 @@ import re
|
|
|
20
20
|
import shutil
|
|
21
21
|
import tempfile
|
|
22
22
|
import math
|
|
23
|
+
import base64
|
|
23
24
|
from copy import deepcopy
|
|
24
25
|
from datetime import datetime, timezone
|
|
25
26
|
from enum import Enum
|
|
@@ -451,6 +452,23 @@ _ECS_HKLM_HIVES = {
|
|
|
451
452
|
"SYSTEM",
|
|
452
453
|
}
|
|
453
454
|
|
|
455
|
+
# ECS 8.17.0 registry.data.type uses the Windows REG_* names. Keep this
|
|
456
|
+
# explicit: python-registry's names are useful provenance, but are not ECS
|
|
457
|
+
# values and unknown names must not be guessed into a known type.
|
|
458
|
+
_ECS_REGISTRY_TYPES = {
|
|
459
|
+
"RegNone": "REG_NONE",
|
|
460
|
+
"RegSZ": "REG_SZ",
|
|
461
|
+
"RegExpandSZ": "REG_EXPAND_SZ",
|
|
462
|
+
"RegBin": "REG_BINARY",
|
|
463
|
+
"RegDWord": "REG_DWORD",
|
|
464
|
+
"RegBigEndian": "REG_DWORD_BIG_ENDIAN",
|
|
465
|
+
"RegLink": "REG_LINK",
|
|
466
|
+
"RegMultiSZ": "REG_MULTI_SZ",
|
|
467
|
+
"RegResourceList": "REG_RESOURCE_LIST",
|
|
468
|
+
"RegFullResourceDescriptor": "REG_FULL_RESOURCE_DESCRIPTOR",
|
|
469
|
+
"RegResourceRequirementsList": "REG_RESOURCE_REQUIREMENTS_LIST",
|
|
470
|
+
"RegQWord": "REG_QWORD",
|
|
471
|
+
}
|
|
454
472
|
|
|
455
473
|
def _join_registry_path(*parts: Optional[str]) -> str:
|
|
456
474
|
return "\\".join(part.strip("\\") for part in parts if part)
|
|
@@ -548,6 +566,8 @@ def plugin_result_to_document(
|
|
|
548
566
|
registry_hive, registry_key, registry_path = _ecs_registry_location(
|
|
549
567
|
hive_name, result.path
|
|
550
568
|
)
|
|
569
|
+
if result.value_name is not None:
|
|
570
|
+
registry_path = _join_registry_path(registry_path, result.value_name)
|
|
551
571
|
doc: dict = {
|
|
552
572
|
"event": {
|
|
553
573
|
"kind": "event",
|
|
@@ -592,16 +612,30 @@ def plugin_result_to_document(
|
|
|
592
612
|
if result.value_name is not None:
|
|
593
613
|
normalized_data = _normalize_value(result.value_data)
|
|
594
614
|
doc["registry"]["value"] = result.value_name
|
|
595
|
-
registry_data: dict = {
|
|
596
|
-
|
|
597
|
-
|
|
615
|
+
registry_data: dict = {}
|
|
616
|
+
ecs_type = _ECS_REGISTRY_TYPES.get(result.value_type)
|
|
617
|
+
if ecs_type is not None:
|
|
618
|
+
registry_data["type"] = ecs_type
|
|
619
|
+
if result.value_type is not None:
|
|
620
|
+
doc["reg2es"]["value_type"] = result.value_type
|
|
621
|
+
if isinstance(result.value_data, (bytes, bytearray, memoryview)):
|
|
622
|
+
raw_bytes = bytes(result.value_data)
|
|
623
|
+
registry_data["bytes"] = base64.b64encode(raw_bytes).decode("ascii")
|
|
624
|
+
doc["reg2es"]["value_size"] = len(raw_bytes)
|
|
598
625
|
elif isinstance(normalized_data, str):
|
|
599
626
|
registry_data["strings"] = [normalized_data]
|
|
600
627
|
elif isinstance(normalized_data, list) and all(
|
|
601
628
|
isinstance(item, str) for item in normalized_data
|
|
602
629
|
):
|
|
603
630
|
registry_data["strings"] = normalized_data
|
|
604
|
-
|
|
631
|
+
elif (
|
|
632
|
+
ecs_type in {"REG_DWORD", "REG_DWORD_BIG_ENDIAN", "REG_QWORD"}
|
|
633
|
+
and isinstance(result.value_data, int)
|
|
634
|
+
and not isinstance(result.value_data, bool)
|
|
635
|
+
):
|
|
636
|
+
registry_data["strings"] = [str(result.value_data)]
|
|
637
|
+
if registry_data:
|
|
638
|
+
doc["registry"]["data"] = registry_data
|
|
605
639
|
doc["reg2es"]["value_data"] = normalized_data
|
|
606
640
|
|
|
607
641
|
# Custom fields (lossless).
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Extract raw drive-letter and volume mappings from SYSTEM\\MountedDevices."""
|
|
2
|
+
|
|
3
|
+
from reg2es.plugins import BasePlugin, PluginResult
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Plugin(BasePlugin):
|
|
7
|
+
"""Emit each MountedDevices value without guessing its binary encoding."""
|
|
8
|
+
|
|
9
|
+
__REGHIVE__ = "SYSTEM"
|
|
10
|
+
|
|
11
|
+
def run(self):
|
|
12
|
+
key = self.open_key("MountedDevices")
|
|
13
|
+
if not key:
|
|
14
|
+
return
|
|
15
|
+
for value in key.values():
|
|
16
|
+
yield PluginResult(key=key, value=value)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Extract USB mass-storage device instances recorded by the SYSTEM hive."""
|
|
2
|
+
|
|
3
|
+
from reg2es.plugins import BasePlugin, PluginResult
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Plugin(BasePlugin):
|
|
7
|
+
"""Emit each USBSTOR hardware instance with its raw registry values."""
|
|
8
|
+
|
|
9
|
+
__REGHIVE__ = "SYSTEM"
|
|
10
|
+
|
|
11
|
+
def run(self):
|
|
12
|
+
control_set = self.get_currentcontrolset_path()
|
|
13
|
+
if not control_set:
|
|
14
|
+
return
|
|
15
|
+
root = self.open_key(f"{control_set}\\Enum\\USBSTOR")
|
|
16
|
+
if not root:
|
|
17
|
+
return
|
|
18
|
+
|
|
19
|
+
for device_type in root.subkeys():
|
|
20
|
+
for instance in device_type.subkeys():
|
|
21
|
+
result = PluginResult(key=instance)
|
|
22
|
+
result.custom["device_type"] = device_type.name()
|
|
23
|
+
result.custom["instance_id"] = instance.name()
|
|
24
|
+
values = {}
|
|
25
|
+
for value in instance.values():
|
|
26
|
+
values[value.name()] = value.value()
|
|
27
|
+
result.custom["registry_values"] = values
|
|
28
|
+
yield result
|