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.
Files changed (118) hide show
  1. reg2es-2.2.2/PKG-INFO +275 -0
  2. reg2es-2.2.2/README.md +259 -0
  3. reg2es-2.2.2/src/reg2es/__about__.py +1 -0
  4. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/models/Reg2es.py +38 -4
  5. reg2es-2.2.2/src/reg2es/plugins/mounteddevices.py +16 -0
  6. reg2es-2.2.2/src/reg2es/plugins/usbstor.py +28 -0
  7. reg2es-2.2.2/src/reg2es/plugins/wordwheelquery.py +59 -0
  8. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/presenters/Reg2esPresenter.py +17 -1
  9. reg2es-2.2.2/tests/test_case_plugins.py +70 -0
  10. {reg2es-2.2.0 → reg2es-2.2.2}/tests/test_es_contract.py +10 -2
  11. {reg2es-2.2.0 → reg2es-2.2.2}/tests/test_integration.py +1 -1
  12. {reg2es-2.2.0 → reg2es-2.2.2}/tests/test_model.py +46 -3
  13. {reg2es-2.2.0 → reg2es-2.2.2}/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.2}/.devcontainer/Dockerfile +0 -0
  18. {reg2es-2.2.0 → reg2es-2.2.2}/.devcontainer/devcontainer.json +0 -0
  19. {reg2es-2.2.0 → reg2es-2.2.2}/.devcontainer/docker-compose.yaml +0 -0
  20. {reg2es-2.2.0 → reg2es-2.2.2}/.devcontainer/postCreateCommands.fish +0 -0
  21. {reg2es-2.2.0 → reg2es-2.2.2}/.github/collect_runtime_licenses.py +0 -0
  22. {reg2es-2.2.0 → reg2es-2.2.2}/.github/workflows/publish-binary-pypi.yaml +0 -0
  23. {reg2es-2.2.0 → reg2es-2.2.2}/.github/workflows/publish-release.yaml +0 -0
  24. {reg2es-2.2.0 → reg2es-2.2.2}/.github/workflows/test.yaml +0 -0
  25. {reg2es-2.2.0 → reg2es-2.2.2}/.gitignore +0 -0
  26. {reg2es-2.2.0 → reg2es-2.2.2}/CONTRIBUTING.md +0 -0
  27. {reg2es-2.2.0 → reg2es-2.2.2}/LICENSE +0 -0
  28. {reg2es-2.2.0 → reg2es-2.2.2}/LICENSES/Apache-2.0.txt +0 -0
  29. {reg2es-2.2.0 → reg2es-2.2.2}/pyproject.toml +0 -0
  30. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/__init__.py +0 -0
  31. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/models/ElasticsearchUtils.py +0 -0
  32. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/__init__.py +0 -0
  33. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/antivirus.py +0 -0
  34. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/auditpol.py +0 -0
  35. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/base.py +0 -0
  36. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/compname.py +0 -0
  37. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/env.py +0 -0
  38. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/filedialogmru.py +0 -0
  39. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/gpo.py +0 -0
  40. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/kb.py +0 -0
  41. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/keyboard.py +0 -0
  42. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/lastloggedon.py +0 -0
  43. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/lastshutdown.py +0 -0
  44. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/localgroups.py +0 -0
  45. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/localusers.py +0 -0
  46. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/mndmru.py +0 -0
  47. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/mstscmru.py +0 -0
  48. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/office_macros.py +0 -0
  49. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/portproxy.py +0 -0
  50. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/printer_history.py +0 -0
  51. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/printer_ports.py +0 -0
  52. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/proxy.py +0 -0
  53. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/putty.py +0 -0
  54. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/rdphint.py +0 -0
  55. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/recentdocs.py +0 -0
  56. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/regtime.py +0 -0
  57. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/run.py +0 -0
  58. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/runmru.py +0 -0
  59. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/services.py +0 -0
  60. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/shimcache.py +0 -0
  61. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/srum.py +0 -0
  62. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/sysinternals.py +0 -0
  63. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/systeminfo.py +0 -0
  64. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/tasks.py +0 -0
  65. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/teamviewer.py +0 -0
  66. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/timezone.py +0 -0
  67. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/typedurls.py +0 -0
  68. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/uninstall.py +0 -0
  69. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/userassist.py +0 -0
  70. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/usersids.py +0 -0
  71. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/plugins/version.py +0 -0
  72. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/presenters/Reg2jsonPresenter.py +0 -0
  73. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/views/BaseView.py +0 -0
  74. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/views/Reg2esView.py +0 -0
  75. {reg2es-2.2.0 → reg2es-2.2.2}/src/reg2es/views/Reg2jsonView.py +0 -0
  76. {reg2es-2.2.0 → reg2es-2.2.2}/tests/__init__.py +0 -0
  77. {reg2es-2.2.0 → reg2es-2.2.2}/tests/conftest.py +0 -0
  78. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/__init__.py +0 -0
  79. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/reg_mock.py +0 -0
  80. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_antivirus.py +0 -0
  81. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_compname.py +0 -0
  82. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_env.py +0 -0
  83. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_filedialogmru.py +0 -0
  84. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_keyboard.py +0 -0
  85. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_lastloggedon.py +0 -0
  86. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_lastshutdown.py +0 -0
  87. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_localgroups.py +0 -0
  88. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_localusers.py +0 -0
  89. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_mndmru.py +0 -0
  90. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_mstscmru.py +0 -0
  91. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_portproxy.py +0 -0
  92. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_printer_history.py +0 -0
  93. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_printer_ports.py +0 -0
  94. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_proxy.py +0 -0
  95. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_putty.py +0 -0
  96. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_rdphint.py +0 -0
  97. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_recentdocs.py +0 -0
  98. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_run.py +0 -0
  99. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_runmru.py +0 -0
  100. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_services.py +0 -0
  101. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_shimcache.py +0 -0
  102. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_sysinternals.py +0 -0
  103. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_systeminfo.py +0 -0
  104. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_tasks.py +0 -0
  105. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_timezone.py +0 -0
  106. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_typedurls.py +0 -0
  107. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_uninstall.py +0 -0
  108. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_userassist.py +0 -0
  109. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_usersids.py +0 -0
  110. {reg2es-2.2.0 → reg2es-2.2.2}/tests/regrippy/test_version.py +0 -0
  111. {reg2es-2.2.0 → reg2es-2.2.2}/tests/test_output_contract.py +0 -0
  112. {reg2es-2.2.0 → reg2es-2.2.2}/tests/test_plugin_timestamps.py +0 -0
  113. {reg2es-2.2.0 → reg2es-2.2.2}/tests/test_runtime_licenses.py +0 -0
  114. {reg2es-2.2.0 → reg2es-2.2.2}/tests/test_shared_output.py +0 -0
  115. {reg2es-2.2.0 → reg2es-2.2.2}/tests/test_shimcache_entries.py +0 -0
  116. {reg2es-2.2.0 → reg2es-2.2.2}/tests/test_timestamp_review.py +0 -0
  117. {reg2es-2.2.0 → reg2es-2.2.2}/tests/test_timestamp_section_c.py +0 -0
  118. {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
+ [![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. 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
+ [![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. 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 = {"type": result.value_type}
596
- if isinstance(result.value_data, bytes):
597
- registry_data["bytes"] = len(result.value_data)
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
- doc["registry"]["data"] = registry_data
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