reg2es 2.2.0__tar.gz → 2.2.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.
Files changed (118) hide show
  1. reg2es-2.2.1/PKG-INFO +277 -0
  2. reg2es-2.2.1/README.md +261 -0
  3. reg2es-2.2.1/src/reg2es/__about__.py +1 -0
  4. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/models/Reg2es.py +42 -4
  5. reg2es-2.2.1/src/reg2es/plugins/mounteddevices.py +16 -0
  6. reg2es-2.2.1/src/reg2es/plugins/usbstor.py +28 -0
  7. reg2es-2.2.1/src/reg2es/plugins/wordwheelquery.py +59 -0
  8. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/presenters/Reg2esPresenter.py +17 -1
  9. reg2es-2.2.1/tests/test_case_plugins.py +70 -0
  10. {reg2es-2.2.0 → reg2es-2.2.1}/tests/test_es_contract.py +10 -2
  11. {reg2es-2.2.0 → reg2es-2.2.1}/tests/test_integration.py +1 -1
  12. {reg2es-2.2.0 → reg2es-2.2.1}/tests/test_model.py +47 -3
  13. {reg2es-2.2.0 → reg2es-2.2.1}/tests/test_plugins.py +4 -1
  14. reg2es-2.2.0/PKG-INFO +0 -375
  15. reg2es-2.2.0/README.md +0 -359
  16. reg2es-2.2.0/src/reg2es/__about__.py +0 -1
  17. {reg2es-2.2.0 → reg2es-2.2.1}/.devcontainer/Dockerfile +0 -0
  18. {reg2es-2.2.0 → reg2es-2.2.1}/.devcontainer/devcontainer.json +0 -0
  19. {reg2es-2.2.0 → reg2es-2.2.1}/.devcontainer/docker-compose.yaml +0 -0
  20. {reg2es-2.2.0 → reg2es-2.2.1}/.devcontainer/postCreateCommands.fish +0 -0
  21. {reg2es-2.2.0 → reg2es-2.2.1}/.github/collect_runtime_licenses.py +0 -0
  22. {reg2es-2.2.0 → reg2es-2.2.1}/.github/workflows/publish-binary-pypi.yaml +0 -0
  23. {reg2es-2.2.0 → reg2es-2.2.1}/.github/workflows/publish-release.yaml +0 -0
  24. {reg2es-2.2.0 → reg2es-2.2.1}/.github/workflows/test.yaml +0 -0
  25. {reg2es-2.2.0 → reg2es-2.2.1}/.gitignore +0 -0
  26. {reg2es-2.2.0 → reg2es-2.2.1}/CONTRIBUTING.md +0 -0
  27. {reg2es-2.2.0 → reg2es-2.2.1}/LICENSE +0 -0
  28. {reg2es-2.2.0 → reg2es-2.2.1}/LICENSES/Apache-2.0.txt +0 -0
  29. {reg2es-2.2.0 → reg2es-2.2.1}/pyproject.toml +0 -0
  30. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/__init__.py +0 -0
  31. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/models/ElasticsearchUtils.py +0 -0
  32. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/__init__.py +0 -0
  33. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/antivirus.py +0 -0
  34. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/auditpol.py +0 -0
  35. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/base.py +0 -0
  36. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/compname.py +0 -0
  37. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/env.py +0 -0
  38. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/filedialogmru.py +0 -0
  39. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/gpo.py +0 -0
  40. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/kb.py +0 -0
  41. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/keyboard.py +0 -0
  42. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/lastloggedon.py +0 -0
  43. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/lastshutdown.py +0 -0
  44. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/localgroups.py +0 -0
  45. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/localusers.py +0 -0
  46. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/mndmru.py +0 -0
  47. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/mstscmru.py +0 -0
  48. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/office_macros.py +0 -0
  49. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/portproxy.py +0 -0
  50. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/printer_history.py +0 -0
  51. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/printer_ports.py +0 -0
  52. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/proxy.py +0 -0
  53. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/putty.py +0 -0
  54. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/rdphint.py +0 -0
  55. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/recentdocs.py +0 -0
  56. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/regtime.py +0 -0
  57. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/run.py +0 -0
  58. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/runmru.py +0 -0
  59. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/services.py +0 -0
  60. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/shimcache.py +0 -0
  61. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/srum.py +0 -0
  62. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/sysinternals.py +0 -0
  63. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/systeminfo.py +0 -0
  64. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/tasks.py +0 -0
  65. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/teamviewer.py +0 -0
  66. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/timezone.py +0 -0
  67. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/typedurls.py +0 -0
  68. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/uninstall.py +0 -0
  69. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/userassist.py +0 -0
  70. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/usersids.py +0 -0
  71. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/plugins/version.py +0 -0
  72. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/presenters/Reg2jsonPresenter.py +0 -0
  73. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/views/BaseView.py +0 -0
  74. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/views/Reg2esView.py +0 -0
  75. {reg2es-2.2.0 → reg2es-2.2.1}/src/reg2es/views/Reg2jsonView.py +0 -0
  76. {reg2es-2.2.0 → reg2es-2.2.1}/tests/__init__.py +0 -0
  77. {reg2es-2.2.0 → reg2es-2.2.1}/tests/conftest.py +0 -0
  78. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/__init__.py +0 -0
  79. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/reg_mock.py +0 -0
  80. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_antivirus.py +0 -0
  81. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_compname.py +0 -0
  82. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_env.py +0 -0
  83. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_filedialogmru.py +0 -0
  84. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_keyboard.py +0 -0
  85. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_lastloggedon.py +0 -0
  86. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_lastshutdown.py +0 -0
  87. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_localgroups.py +0 -0
  88. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_localusers.py +0 -0
  89. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_mndmru.py +0 -0
  90. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_mstscmru.py +0 -0
  91. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_portproxy.py +0 -0
  92. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_printer_history.py +0 -0
  93. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_printer_ports.py +0 -0
  94. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_proxy.py +0 -0
  95. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_putty.py +0 -0
  96. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_rdphint.py +0 -0
  97. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_recentdocs.py +0 -0
  98. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_run.py +0 -0
  99. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_runmru.py +0 -0
  100. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_services.py +0 -0
  101. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_shimcache.py +0 -0
  102. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_sysinternals.py +0 -0
  103. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_systeminfo.py +0 -0
  104. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_tasks.py +0 -0
  105. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_timezone.py +0 -0
  106. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_typedurls.py +0 -0
  107. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_uninstall.py +0 -0
  108. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_userassist.py +0 -0
  109. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_usersids.py +0 -0
  110. {reg2es-2.2.0 → reg2es-2.2.1}/tests/regrippy/test_version.py +0 -0
  111. {reg2es-2.2.0 → reg2es-2.2.1}/tests/test_output_contract.py +0 -0
  112. {reg2es-2.2.0 → reg2es-2.2.1}/tests/test_plugin_timestamps.py +0 -0
  113. {reg2es-2.2.0 → reg2es-2.2.1}/tests/test_runtime_licenses.py +0 -0
  114. {reg2es-2.2.0 → reg2es-2.2.1}/tests/test_shared_output.py +0 -0
  115. {reg2es-2.2.0 → reg2es-2.2.1}/tests/test_shimcache_entries.py +0 -0
  116. {reg2es-2.2.0 → reg2es-2.2.1}/tests/test_timestamp_review.py +0 -0
  117. {reg2es-2.2.0 → reg2es-2.2.1}/tests/test_timestamp_section_c.py +0 -0
  118. {reg2es-2.2.0 → reg2es-2.2.1}/uv.lock +0 -0
reg2es-2.2.1/PKG-INFO ADDED
@@ -0,0 +1,277 @@
1
+ Metadata-Version: 2.5
2
+ Name: reg2es
3
+ Version: 2.2.1
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
+ [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE)
20
+ [![PyPI Version](https://img.shields.io/pypi/v/reg2es)](https://pypi.org/project/reg2es/)
21
+
22
+ ![reg2es logo](https://gist.githubusercontent.com/sumeshi/c2f430d352ae763273faadf9616a29e5/raw/bd51b2539d8bb639d4f630ef13639706bed1f905/reg2es.svg)
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 with `ecs.version` set to `8.17.0`.
176
+ Standard `event`,
177
+ `registry`, `log.file`, `tags`, and `@timestamp` fields describe the artifact.
178
+ Lossless plugin-specific data and the original offline-hive location are kept
179
+ under `reg2es`.
180
+
181
+ When a plugin has a reliable artifact time, it uses that time for `@timestamp`.
182
+ Otherwise it uses the registry key's LastWrite; if no valid time exists,
183
+ `@timestamp` is omitted. Check `reg2es.timestamp.source` and
184
+ `reg2es.timestamp.meaning` before treating a result as an activity time.
185
+ Original values and fallback reasons remain in the document.
186
+
187
+ The current ECS output changes `registry.data.bytes` from a byte count to
188
+ Base64, uses `REG_*` names in `registry.data.type`, and includes the value
189
+ name in `registry.path`. Existing Elasticsearch mappings may conflict with
190
+ these fields; use a new index for the updated output.
191
+
192
+ ```json
193
+ {
194
+ "ecs": {"version": "8.17.0"},
195
+ "@timestamp": "2015-10-30T07:24:57.814133+00:00",
196
+ "event": {
197
+ "kind": "event",
198
+ "category": ["registry"],
199
+ "type": ["info"],
200
+ "action": "compname"
201
+ },
202
+ "registry": {
203
+ "hive": "HKLM",
204
+ "key": "SYSTEM\\ControlSet001\\Control\\ComputerName\\ComputerName",
205
+ "path": "HKLM\\SYSTEM\\ControlSet001\\Control\\ComputerName\\ComputerName\\ComputerName",
206
+ "value": "ComputerName",
207
+ "data": {
208
+ "type": "REG_SZ",
209
+ "strings": ["DESKTOP-EXAMPLE"]
210
+ }
211
+ },
212
+ "log": {
213
+ "file": {"path": "/evidence/SYSTEM"}
214
+ },
215
+ "tags": ["registry", "host-01"],
216
+ "reg2es": {
217
+ "plugin": {"name": "compname"},
218
+ "source": {
219
+ "hive": "SYSTEM",
220
+ "key_path": "ROOT\\ControlSet001\\Control\\ComputerName\\ComputerName"
221
+ },
222
+ "value_data": "DESKTOP-EXAMPLE",
223
+ "value_type": "RegSZ"
224
+ }
225
+ }
226
+ ```
227
+
228
+
229
+ ## Installation
230
+
231
+ ### From PyPI
232
+
233
+ ```bash
234
+ $ pip install reg2es
235
+ ```
236
+
237
+ ### With uv
238
+
239
+ ```bash
240
+ $ uv add reg2es
241
+ ```
242
+
243
+ ### From GitHub Releases
244
+
245
+ Standalone binaries built with Nuitka are available from GitHub Releases for systems without a Python environment.
246
+
247
+ ```bash
248
+ $ chmod +x ./reg2es
249
+ $ ./reg2es {{options...}}
250
+ ```
251
+
252
+ ```powershell
253
+ > reg2es.exe {{options...}}
254
+ ```
255
+
256
+
257
+ ## Contributing
258
+
259
+ The source code for **reg2es** is hosted on GitHub: https://github.com/sumeshi/reg2es.
260
+ Please report issues and feature requests. :sushi: :sushi: :sushi:
261
+
262
+
263
+ ## License
264
+
265
+ Standalone release ZIPs include `LICENSES.txt` with the project, bundled plugin,
266
+ runtime dependency and build Python license notices. Keep it with the executables
267
+ when redistributing them.
268
+
269
+ **reg2es** is released under the [MIT](LICENSE) License.
270
+
271
+ ### Third-Party Notices
272
+
273
+ This product includes code derived from
274
+ [regrippy v2.0.3](https://github.com/airbus-cert/regrippy) by Airbus CERT,
275
+ licensed under Apache License 2.0. The included ShimCache parser carries
276
+ the original copyright notice of Andrew Davis, Mandiant (2012). See the
277
+ [Apache 2.0 license text](LICENSES/Apache-2.0.txt) and the source-file notices.
reg2es-2.2.1/README.md ADDED
@@ -0,0 +1,261 @@
1
+ # reg2es
2
+
3
+ [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE)
4
+ [![PyPI Version](https://img.shields.io/pypi/v/reg2es)](https://pypi.org/project/reg2es/)
5
+
6
+ ![reg2es logo](https://gist.githubusercontent.com/sumeshi/c2f430d352ae763273faadf9616a29e5/raw/bd51b2539d8bb639d4f630ef13639706bed1f905/reg2es.svg)
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 with `ecs.version` set to `8.17.0`.
160
+ Standard `event`,
161
+ `registry`, `log.file`, `tags`, and `@timestamp` fields describe the artifact.
162
+ Lossless plugin-specific data and the original offline-hive location are kept
163
+ under `reg2es`.
164
+
165
+ When a plugin has a reliable artifact time, it uses that time for `@timestamp`.
166
+ Otherwise it uses the registry key's LastWrite; if no valid time exists,
167
+ `@timestamp` is omitted. Check `reg2es.timestamp.source` and
168
+ `reg2es.timestamp.meaning` before treating a result as an activity time.
169
+ Original values and fallback reasons remain in the document.
170
+
171
+ The current ECS output changes `registry.data.bytes` from a byte count to
172
+ Base64, uses `REG_*` names in `registry.data.type`, and includes the value
173
+ name in `registry.path`. Existing Elasticsearch mappings may conflict with
174
+ these fields; use a new index for the updated output.
175
+
176
+ ```json
177
+ {
178
+ "ecs": {"version": "8.17.0"},
179
+ "@timestamp": "2015-10-30T07:24:57.814133+00:00",
180
+ "event": {
181
+ "kind": "event",
182
+ "category": ["registry"],
183
+ "type": ["info"],
184
+ "action": "compname"
185
+ },
186
+ "registry": {
187
+ "hive": "HKLM",
188
+ "key": "SYSTEM\\ControlSet001\\Control\\ComputerName\\ComputerName",
189
+ "path": "HKLM\\SYSTEM\\ControlSet001\\Control\\ComputerName\\ComputerName\\ComputerName",
190
+ "value": "ComputerName",
191
+ "data": {
192
+ "type": "REG_SZ",
193
+ "strings": ["DESKTOP-EXAMPLE"]
194
+ }
195
+ },
196
+ "log": {
197
+ "file": {"path": "/evidence/SYSTEM"}
198
+ },
199
+ "tags": ["registry", "host-01"],
200
+ "reg2es": {
201
+ "plugin": {"name": "compname"},
202
+ "source": {
203
+ "hive": "SYSTEM",
204
+ "key_path": "ROOT\\ControlSet001\\Control\\ComputerName\\ComputerName"
205
+ },
206
+ "value_data": "DESKTOP-EXAMPLE",
207
+ "value_type": "RegSZ"
208
+ }
209
+ }
210
+ ```
211
+
212
+
213
+ ## Installation
214
+
215
+ ### From PyPI
216
+
217
+ ```bash
218
+ $ pip install reg2es
219
+ ```
220
+
221
+ ### With uv
222
+
223
+ ```bash
224
+ $ uv add reg2es
225
+ ```
226
+
227
+ ### From GitHub Releases
228
+
229
+ Standalone binaries built with Nuitka are available from GitHub Releases for systems without a Python environment.
230
+
231
+ ```bash
232
+ $ chmod +x ./reg2es
233
+ $ ./reg2es {{options...}}
234
+ ```
235
+
236
+ ```powershell
237
+ > reg2es.exe {{options...}}
238
+ ```
239
+
240
+
241
+ ## Contributing
242
+
243
+ The source code for **reg2es** is hosted on GitHub: https://github.com/sumeshi/reg2es.
244
+ Please report issues and feature requests. :sushi: :sushi: :sushi:
245
+
246
+
247
+ ## License
248
+
249
+ Standalone release ZIPs include `LICENSES.txt` with the project, bundled plugin,
250
+ runtime dependency and build Python license notices. Keep it with the executables
251
+ when redistributing them.
252
+
253
+ **reg2es** is released under the [MIT](LICENSE) License.
254
+
255
+ ### Third-Party Notices
256
+
257
+ This product includes code derived from
258
+ [regrippy v2.0.3](https://github.com/airbus-cert/regrippy) by Airbus CERT,
259
+ licensed under Apache License 2.0. The included ShimCache parser carries
260
+ the original copyright notice of Andrew Davis, Mandiant (2012). See the
261
+ [Apache 2.0 license text](LICENSES/Apache-2.0.txt) and the source-file notices.
@@ -0,0 +1 @@
1
+ __version__ = "2.2.1"
@@ -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,26 @@ _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
+ }
472
+
473
+ _ECS_VERSION = "8.17.0"
474
+
454
475
 
455
476
  def _join_registry_path(*parts: Optional[str]) -> str:
456
477
  return "\\".join(part.strip("\\") for part in parts if part)
@@ -548,7 +569,10 @@ def plugin_result_to_document(
548
569
  registry_hive, registry_key, registry_path = _ecs_registry_location(
549
570
  hive_name, result.path
550
571
  )
572
+ if result.value_name is not None:
573
+ registry_path = _join_registry_path(registry_path, result.value_name)
551
574
  doc: dict = {
575
+ "ecs": {"version": _ECS_VERSION},
552
576
  "event": {
553
577
  "kind": "event",
554
578
  "category": ["registry"],
@@ -592,16 +616,30 @@ def plugin_result_to_document(
592
616
  if result.value_name is not None:
593
617
  normalized_data = _normalize_value(result.value_data)
594
618
  doc["registry"]["value"] = result.value_name
595
- registry_data: dict = {"type": result.value_type}
596
- if isinstance(result.value_data, bytes):
597
- registry_data["bytes"] = len(result.value_data)
619
+ registry_data: dict = {}
620
+ ecs_type = _ECS_REGISTRY_TYPES.get(result.value_type)
621
+ if ecs_type is not None:
622
+ registry_data["type"] = ecs_type
623
+ if result.value_type is not None:
624
+ doc["reg2es"]["value_type"] = result.value_type
625
+ if isinstance(result.value_data, (bytes, bytearray, memoryview)):
626
+ raw_bytes = bytes(result.value_data)
627
+ registry_data["bytes"] = base64.b64encode(raw_bytes).decode("ascii")
628
+ doc["reg2es"]["value_size"] = len(raw_bytes)
598
629
  elif isinstance(normalized_data, str):
599
630
  registry_data["strings"] = [normalized_data]
600
631
  elif isinstance(normalized_data, list) and all(
601
632
  isinstance(item, str) for item in normalized_data
602
633
  ):
603
634
  registry_data["strings"] = normalized_data
604
- doc["registry"]["data"] = registry_data
635
+ elif (
636
+ ecs_type in {"REG_DWORD", "REG_DWORD_BIG_ENDIAN", "REG_QWORD"}
637
+ and isinstance(result.value_data, int)
638
+ and not isinstance(result.value_data, bool)
639
+ ):
640
+ registry_data["strings"] = [str(result.value_data)]
641
+ if registry_data:
642
+ doc["registry"]["data"] = registry_data
605
643
  doc["reg2es"]["value_data"] = normalized_data
606
644
 
607
645
  # 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