Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
ZarKyo authored Aug 28, 2024
2 parents 66cacbe + efbaee5 commit a835040
Show file tree
Hide file tree
Showing 41 changed files with 1,034 additions and 199 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
- name: Test with pytest
run: |
pip install -e .[cli]
pytest regipy_tests/tests.py regipy_tests/plugin_tests.py -vvvs
- name: Plugin Validation (will replace plugin tests in the future)
pytest regipy_tests/tests.py -vvvs
- name: Plugin Validation
run: |
python regipy_tests/validation/plugin_validation.py
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ run_relevant_plugins(reg, as_json=True)
## Validation cases
[Validation cases report](regipy_tests/validation/plugin_validation.md)

all new plugins, (and in the future, all plugins) should have a one or more basic validation cases (which can be expanded in the future), for example:
all new plugins should have a one or more basic validation cases (which can be expanded in the future), for example:
```
from regipy.plugins.system.bam import BAMPlugin
from regipy_tests.validation.validation import ValidationCase
Expand Down
35 changes: 3 additions & 32 deletions docs/PLUGINS.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,8 @@
## regipy Plugins

* The plugin system is a robust and extensive feature that auto-detects the hive type and execute the relevant plugins
* The plugin system is a robust and extensive feature that auto-detects the hive type and execute the relevant plugins.

### Plugins

#### System Hive
- [ ] persistence
- [X] Installed services
- [X] List routes
- [X] Get computer name
- [X] Shimcache
- [ ] Timezone data

#### NTUSER Hive
- [X] Persistence
- [ ] runmru
- [ ] Recent documents
- [ ] Typed URLs
- [X] User Assist
- [ ] Word Wheel Query

#### Amcache Hive
- [X] Parse amcache

#### SOFTWARE Hive
- [X] Persistence
- [ ] Installed programs
- [ ] Sysinternals EULA
- [ ] User SIDs
- [ ] Windows version info
- [X] Profile List

#### SAM Hive
- [ ] Users and groups
To see a comprehensive list, please refer to the [Validation cases report](../validation/plugin_validation.md)

## Contributing new plugins
Adding a new plugin is very straight forward:
Expand All @@ -41,3 +11,4 @@ Adding a new plugin is very straight forward:
* Update the `NAME` parameter and the Class name accordingly (NAME in snake case, Class name in camel case)
* Feel free to use/add any utility function to `regipy/utils.py`
* Import your class in `regipy/plugins/__init__.py`
3. Add a [validation case](../README.md#validation-cases). This is mandatory and replaces the old regipy tests.
4 changes: 2 additions & 2 deletions regipy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# flake8: noqa
# flake8: noqa
from .registry import *

__title__ = "regipy"
__version__ = "4.9.0"
__version__ = "5.0.0"
1 change: 0 additions & 1 deletion regipy/plugins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,3 @@
from .software.spp_clients import SppClientsPlugin
from .system.backuprestore import BackupRestorePlugin
from .system.timezone_data2 import TimezoneDataPlugin2

17 changes: 11 additions & 6 deletions regipy/plugins/software/disablesr.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,15 @@


SYS_RESTORE_PATH = r"\Microsoft\Windows NT\CurrentVersion\SystemRestore"
value_list = ("DisableSR")
value_list = "DisableSR"


class DisableSRPlugin(Plugin):
NAME = 'disablesr_plugin'
DESCRIPTION = 'Gets the value that turns System Restore either on or off'
NAME = "disablesr_plugin"
DESCRIPTION = "Gets the value that turns System Restore either on or off"
COMPATIBLE_HIVE = SOFTWARE_HIVE_TYPE

def can_run(self):
# TODO: Choose the relevant condition - to determine if the plugin is relevant for the given hive
return self.registry_hive.hive_type == SOFTWARE_HIVE_TYPE

def run(self):
Expand All @@ -27,10 +26,16 @@ def run(self):
try:
key = self.registry_hive.get_key(SYS_RESTORE_PATH)
except RegistryKeyNotFoundException as ex:
logger.error(f'Could not find {self.NAME} subkey at {SYS_RESTORE_PATH}: {ex}')
logger.error(
f"Could not find {self.NAME} subkey at {SYS_RESTORE_PATH}: {ex}"
)
return None

self.entries = {SYS_RESTORE_PATH: {'last_write': convert_wintime(key.header.last_modified).isoformat()}}
self.entries = {
SYS_RESTORE_PATH: {
"last_write": convert_wintime(key.header.last_modified).isoformat()
}
}

for val in key.iter_values():
if val.name in value_list:
Expand Down
17 changes: 11 additions & 6 deletions regipy/plugins/software/spp_clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,15 @@


SPP_CLIENT_PATH = r"\Microsoft\Windows NT\CurrentVersion\SPP\Clients"
value_list = ("{09F7EDC5-294E-4180-AF6A-FB0E6A0E9513}")
value_list = "{09F7EDC5-294E-4180-AF6A-FB0E6A0E9513}"


class SppClientsPlugin(Plugin):
NAME = 'spp_clients_plugin'
DESCRIPTION = 'Determines volumes monitored by VSS'
NAME = "spp_clients_plugin"
DESCRIPTION = "Determines volumes monitored by VSS"
COMPATIBLE_HIVE = SOFTWARE_HIVE_TYPE

def can_run(self):
# TODO: Choose the relevant condition - to determine if the plugin is relevant for the given hive
return self.registry_hive.hive_type == SOFTWARE_HIVE_TYPE

def run(self):
Expand All @@ -27,10 +26,16 @@ def run(self):
try:
key = self.registry_hive.get_key(SPP_CLIENT_PATH)
except RegistryKeyNotFoundException as ex:
logger.error(f'Could not find {self.NAME} subkey at {SPP_CLIENT_PATH}: {ex}')
logger.error(
f"Could not find {self.NAME} subkey at {SPP_CLIENT_PATH}: {ex}"
)
return None

self.entries = {SPP_CLIENT_PATH: {'last_write': convert_wintime(key.header.last_modified).isoformat()}}
self.entries = {
SPP_CLIENT_PATH: {
"last_write": convert_wintime(key.header.last_modified).isoformat()
}
}

for val in key.iter_values():
if val.name in value_list:
Expand Down
15 changes: 9 additions & 6 deletions regipy/plugins/software/susclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@


class SusclientPlugin(Plugin):
NAME = 'susclient_plugin'
DESCRIPTION = 'Extracts SusClient* info, including HDD SN'
NAME = "susclient_plugin"
DESCRIPTION = "Extracts SusClient* info, including HDD SN"
COMPATIBLE_HIVE = SOFTWARE_HIVE_TYPE

def can_run(self):
# TODO: Choose the relevant condition - to determine if the plugin is relevant for the given hive
return self.registry_hive.hive_type == SOFTWARE_HIVE_TYPE

def run(self):
Expand All @@ -27,10 +26,14 @@ def run(self):
try:
key = self.registry_hive.get_key(WIN_VER_PATH)
except RegistryKeyNotFoundException as ex:
logger.error(f'Could not find {self.NAME} subkey at {WIN_VER_PATH}: {ex}')
logger.error(f"Could not find {self.NAME} subkey at {WIN_VER_PATH}: {ex}")
return None

self.entries = {WIN_VER_PATH: {'last_write': convert_wintime(key.header.last_modified).isoformat()}}
self.entries = {
WIN_VER_PATH: {
"last_write": convert_wintime(key.header.last_modified).isoformat()
}
}

for val in key.iter_values():
if val.name == "SusClientIdValidation":
Expand All @@ -42,4 +45,4 @@ def run(self):
def get_SN(data):
offset = int(data[:2], 16)
length = int(data[4:6], 16)
return bytes.fromhex(data[2 * offset:2 * (length + offset)]).decode('utf-16le')
return bytes.fromhex(data[2 * offset: 2 * (length + offset)]).decode("utf-16le")
39 changes: 30 additions & 9 deletions regipy/plugins/software/winver.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,32 @@


WIN_VER_PATH = r"\Microsoft\Windows NT\CurrentVersion"
os_list = ("ProductName", "ReleaseID", "CSDVersion", "CurrentVersion", "CurrentBuild", "CurrentBuildNumber", "InstallationType", "EditionID",
"ProductName", "ProductId", "BuildLab", "BuildLabEx", "CompositionEditionID", "RegisteredOrganization", "RegisteredOwner", "InstallDate")
os_list = (
"ProductName",
"ReleaseID",
"CSDVersion",
"CurrentVersion",
"CurrentBuild",
"CurrentBuildNumber",
"InstallationType",
"EditionID",
"ProductName",
"ProductId",
"BuildLab",
"BuildLabEx",
"CompositionEditionID",
"RegisteredOrganization",
"RegisteredOwner",
"InstallDate",
)


class WinVersionPlugin(Plugin):
NAME = 'winver_plugin'
DESCRIPTION = 'Get relevant OS information'
NAME = "winver_plugin"
DESCRIPTION = "Get relevant OS information"
COMPATIBLE_HIVE = SOFTWARE_HIVE_TYPE

def can_run(self):
# TODO: Choose the relevant condition - to determine if the plugin is relevant for the given hive
return self.registry_hive.hive_type == SOFTWARE_HIVE_TYPE

def run(self):
Expand All @@ -29,14 +44,20 @@ def run(self):
try:
key = self.registry_hive.get_key(WIN_VER_PATH)
except RegistryKeyNotFoundException as ex:
logger.error(f'Could not find {self.NAME} subkey at {WIN_VER_PATH}: {ex}')
logger.error(f"Could not find {self.NAME} subkey at {WIN_VER_PATH}: {ex}")
return None

self.entries = {WIN_VER_PATH: {'last_write': convert_wintime(key.header.last_modified).isoformat()}}
self.entries = {
WIN_VER_PATH: {
"last_write": convert_wintime(key.header.last_modified).isoformat()
}
}

for val in key.iter_values():
if val.name in os_list:
if val.name == "InstallDate":
self.entries[WIN_VER_PATH][val.name] = datetime.fromtimestamp(val.value, timezone.utc).strftime("%Y-%m-%d %H:%M:%S")
self.entries[WIN_VER_PATH][val.name] = datetime.fromtimestamp(
val.value, timezone.utc
).strftime("%Y-%m-%d %H:%M:%S")
else:
self.entries[WIN_VER_PATH][val.name] = val.value
self.entries[WIN_VER_PATH][val.name] = val.value
19 changes: 14 additions & 5 deletions regipy/plugins/system/backuprestore.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,19 @@
logger = logging.getLogger(__name__)


BACKUPRESTORE_PATH = [r"Control\BackupRestore\FilesNotToSnapshot", r"Control\BackupRestore\FilesNotToBackup", r"Control\BackupRestore\KeysNotToRestore"]
BACKUPRESTORE_PATH = [
r"Control\BackupRestore\FilesNotToSnapshot",
r"Control\BackupRestore\FilesNotToBackup",
r"Control\BackupRestore\KeysNotToRestore",
]


class BackupRestorePlugin(Plugin):
NAME = 'backuprestore_plugin'
NAME = "backuprestore_plugin"
DESCRIPTION = "Gets the contents of the FilesNotToSnapshot, KeysNotToRestore, and FilesNotToBackup keys"
COMPATIBLE_HIVE = SYSTEM_HIVE_TYPE

def can_run(self):
# TODO: Choose the relevant condition - to determine if the plugin is relevant for the given hive
return self.registry_hive.hive_type == SYSTEM_HIVE_TYPE

def run(self):
Expand All @@ -29,8 +32,14 @@ def run(self):
try:
backuprestore = self.registry_hive.get_key(br_subkey)
except RegistryKeyNotFoundException as ex:
logger.error(f'Could not find {self.NAME} subkey at {br_subkey}: {ex}')
logger.error(
f"Could not find {self.NAME} subkey at {br_subkey}: {ex}"
)
continue
self.entries[br_subkey] = {'last_write': convert_wintime(backuprestore.header.last_modified).isoformat()}
self.entries[br_subkey] = {
"last_write": convert_wintime(
backuprestore.header.last_modified
).isoformat()
}
for val in backuprestore.iter_values():
self.entries[br_subkey][val.name] = val.value
13 changes: 8 additions & 5 deletions regipy/plugins/system/codepage.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,15 @@


PROCESSOR_PATH = r"Control\Nls\CodePage"
crash_items = ("ACP")
crash_items = "ACP"


class CodepagePlugin(Plugin):
NAME = 'codepage'
NAME = "codepage"
DESCRIPTION = "Get codepage value"
COMPATIBLE_HIVE = SYSTEM_HIVE_TYPE

def can_run(self):
# TODO: Choose the relevant condition - to determine if the plugin is relevant for the given hive
return self.registry_hive.hive_type == SYSTEM_HIVE_TYPE

def run(self):
Expand All @@ -29,9 +28,13 @@ def run(self):
try:
codepage = self.registry_hive.get_key(codepage_subkey)
except RegistryKeyNotFoundException as ex:
logger.error(f'Could not find {self.NAME} subkey at {codepage_subkey}: {ex}')
logger.error(
f"Could not find {self.NAME} subkey at {codepage_subkey}: {ex}"
)
continue
self.entries[codepage_subkey] = {'last_write': convert_wintime(codepage.header.last_modified).isoformat()}
self.entries[codepage_subkey] = {
"last_write": convert_wintime(codepage.header.last_modified).isoformat()
}
for val in codepage.iter_values():
if val.name in crash_items:
self.entries[codepage_subkey][val.name] = val.value
29 changes: 19 additions & 10 deletions regipy/plugins/system/crash_dump.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,21 @@

PROCESSOR_PATH = r"Control\CrashControl"
crash_items = ("CrashDumpEnabled", "DumpFile", "MinidumpDir", "LogEvent")
dump_enabled = {"0": "None",
"1": "Complete memory dump",
"2": "Kernel memory dump",
"3": "Small memory dump (64 KB)",
"7": "Automatic memory dump"}
dump_enabled = {
"0": "None",
"1": "Complete memory dump",
"2": "Kernel memory dump",
"3": "Small memory dump (64 KB)",
"7": "Automatic memory dump",
}


class CrashDumpPlugin(Plugin):
NAME = 'crash_dump'
NAME = "crash_dump"
DESCRIPTION = "Get crash control information"
COMPATIBLE_HIVE = SYSTEM_HIVE_TYPE

def can_run(self):
# TODO: Choose the relevant condition - to determine if the plugin is relevant for the given hive
return self.registry_hive.hive_type == SYSTEM_HIVE_TYPE

def run(self):
Expand All @@ -33,11 +34,19 @@ def run(self):
try:
architecture = self.registry_hive.get_key(architecture_subkey)
except RegistryKeyNotFoundException as ex:
logger.error(f'Could not find {self.NAME} subkey at {architecture_subkey}: {ex}')
logger.error(
f"Could not find {self.NAME} subkey at {architecture_subkey}: {ex}"
)
continue
self.entries[architecture_subkey] = {'last_write': convert_wintime(architecture.header.last_modified).isoformat()}
self.entries[architecture_subkey] = {
"last_write": convert_wintime(
architecture.header.last_modified
).isoformat()
}
for val in architecture.iter_values():
if val.name in crash_items:
self.entries[architecture_subkey][val.name] = val.value
if val.name == "CrashDumpEnabled":
self.entries[architecture_subkey]["CrashDumpEnabledStr"] = dump_enabled.get(str(val.value), "")
self.entries[architecture_subkey]["CrashDumpEnabledStr"] = (
dump_enabled.get(str(val.value), "")
)
Loading

0 comments on commit a835040

Please sign in to comment.