├── .gitignore ├── LICENSE ├── README.md ├── Resources ├── UefiVarMonitor.png └── uefi-var-monitor-on-linux.png ├── UefiVarMonitorExClient ├── .gitignore ├── UefiVarMonitorExClient.sln └── UefiVarMonitorExClient │ ├── UefiVarMonitorExClient.c │ ├── UefiVarMonitorExClient.vcxproj │ └── UefiVarMonitorExClient.vcxproj.filters ├── UefiVarMonitorPkg ├── Drivers │ ├── UefiVarMonitorDxe │ │ ├── UefiVarMonitorDxe.c │ │ └── UefiVarMonitorDxe.inf │ └── UefiVarMonitorExDxe │ │ ├── UefiVarMonitorExDxe.c │ │ ├── UefiVarMonitorExDxe.h │ │ └── UefiVarMonitorExDxe.inf ├── UefiVarMonitorPkg.dec └── UefiVarMonitorPkg.dsc └── uefi-var-monitor ├── .cargo └── config.toml ├── .gitignore ├── .vscode └── tasks.json ├── Cargo.toml ├── OVMF_CODE.fd ├── OVMF_VARS.fd ├── RunQemu.bat └── src ├── main.rs └── serial.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Satoshi Tanda 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | UefiVarMonitor 2 | =============== 3 | 4 | The sample runtime DXE driver (UEFI driver) monitoring access to the UEFI variables by hooking the runtime service table in C and Rust. 5 | 6 | This project was developed to provide a small sample of a runtime driver. 7 | 8 | ![UefiVarMonitor](Resources/UefiVarMonitor.png) 9 | ![uefi-var-monitor](Resources/uefi-var-monitor-on-linux.png) 10 | 11 | Rust implementation was made solely for author's learning. 12 | 13 | Projects Overview 14 | ------------------ 15 | 16 | * UefiVarMonitorDxe 17 | 18 | The UEFI runtime driver that hooks `GetVariable` and `SetVariable` runtime services, and logs the use of them into serial output. Written in less than 300 lines of C code. 19 | 20 | * uefi-var-monitor 21 | 22 | Nearly equivalent implementation of `UefiVarMonitorDxe` in Rust. Unsafe, unsafe everywhere. 23 | 24 | * UefiVarMonitorExDxe 25 | 26 | The enhanced version of `UefiVarMonitorDxe` allowing a Windows driver to register an inline callback of the above runtime services. This can also be used to alter parameters and block those calls. 27 | 28 | * UefiVarMonitorExClient 29 | 30 | The sample Windows driver registering a callback with `UefiVarMonitorExDxe`. 31 | 32 | Building 33 | --------- 34 | 35 | * UefiVarMonitorDxe and UefiVarMonitorExDxe 36 | 37 | 1. Set up edk2 build environment 38 | 2. Copy `UefiVarMonitorPkg` as `edk2\UefiVarMonitorPkg` 39 | 3. On the edk2 build command prompt, run the below command: 40 | ``` 41 | > edksetup.bat 42 | > build -t VS2019 -a X64 -b NOOPT -p UefiVarMonitorPkg\UefiVarMonitorPkg.dsc -D DEBUG_ON_SERIAL_PORT 43 | ``` 44 | Or on Linux or WSL, 45 | ``` 46 | $ . edksetup.sh 47 | $ build -t GCC5 -a X64 -b NOOPT -p UefiVarMonitorPkg/UefiVarMonitorPkg.dsc -D DEBUG_ON_SERIAL_PORT 48 | ``` 49 | 50 | * uefi-var-monitor 51 | 52 | 1. Install the nightly rust compiler. Below is an example on Linux, but it is largely the same on Windows. 53 | ``` 54 | $ sudo snap install rustup --classic 55 | $ rustup default nightly 56 | $ rustup component add rust-src 57 | ``` 58 | 2. Build the project. 59 | ``` 60 | $ cd uefi-var-monitor 61 | $ cargo build 62 | ``` 63 | 64 | * UefiVarMonitorExClient 65 | 66 | This is a standard Windows driver. VS2019 and WDK 10.0.18362 or later are required. 67 | 68 | Credits 69 | --------- 70 | 71 | - Thank you [@x1tan](https://twitter.com/x1tan) for modernalized xcargo-less build. 72 | -------------------------------------------------------------------------------- /Resources/UefiVarMonitor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tandasat/UefiVarMonitor/8d47580023482c87d65d08213cf17c74308adefb/Resources/UefiVarMonitor.png -------------------------------------------------------------------------------- /Resources/uefi-var-monitor-on-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tandasat/UefiVarMonitor/8d47580023482c87d65d08213cf17c74308adefb/Resources/uefi-var-monitor-on-linux.png -------------------------------------------------------------------------------- /UefiVarMonitorExClient/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc 262 | 263 | # WPP 264 | *.mof 265 | 266 | # CppCheck 267 | *.cppcheck 268 | **/*-cppcheck-build-dir 269 | 270 | # Doxygen 271 | Doxygen/ 272 | 273 | # .lib files collected by the Pre-Link script 274 | Libs/ 275 | -------------------------------------------------------------------------------- /UefiVarMonitorExClient/UefiVarMonitorExClient.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30011.22 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UefiVarMonitorExClient", "UefiVarMonitorExClient\UefiVarMonitorExClient.vcxproj", "{3E8F9F2C-043B-4047-B7D3-7D7CAF610C7E}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Release|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {3E8F9F2C-043B-4047-B7D3-7D7CAF610C7E}.Debug|x64.ActiveCfg = Debug|x64 15 | {3E8F9F2C-043B-4047-B7D3-7D7CAF610C7E}.Debug|x64.Build.0 = Debug|x64 16 | {3E8F9F2C-043B-4047-B7D3-7D7CAF610C7E}.Debug|x64.Deploy.0 = Debug|x64 17 | {3E8F9F2C-043B-4047-B7D3-7D7CAF610C7E}.Release|x64.ActiveCfg = Release|x64 18 | {3E8F9F2C-043B-4047-B7D3-7D7CAF610C7E}.Release|x64.Build.0 = Release|x64 19 | {3E8F9F2C-043B-4047-B7D3-7D7CAF610C7E}.Release|x64.Deploy.0 = Release|x64 20 | EndGlobalSection 21 | GlobalSection(SolutionProperties) = preSolution 22 | HideSolutionNode = FALSE 23 | EndGlobalSection 24 | GlobalSection(ExtensibilityGlobals) = postSolution 25 | SolutionGuid = {2FACD59B-4D1D-4F99-9205-5CF39D0B0F6D} 26 | EndGlobalSection 27 | EndGlobal 28 | -------------------------------------------------------------------------------- /UefiVarMonitorExClient/UefiVarMonitorExClient/UefiVarMonitorExClient.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /** 6 | * @brief Handles GetVariable and SetVariable runtime service calls. 7 | */ 8 | static 9 | BOOLEAN 10 | EFIAPI 11 | HandleGetOrSetVariable ( 12 | _Inout_ VARIABLE_CALLBACK_PARAMETERS* Parameters 13 | ) 14 | { 15 | NTSTATUS status; 16 | CHAR guidStr[RTL_GUID_STRING_SIZE - 2 + 1]; // -2 for {}, +1 for NULL 17 | CONST GUID* guid; 18 | SIZE_T dataSize; 19 | CONST WCHAR* variableName; 20 | CONST CHAR* message; 21 | 22 | // 23 | // This callback is always called at DISPATCH_LEVEL (to prevent recursive call). 24 | // 25 | NT_ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); 26 | 27 | // 28 | // This sample does not do anything with the pre-callback. 29 | // 30 | if (Parameters->OperationType == OperationPre) 31 | { 32 | goto Exit; 33 | } 34 | 35 | // 36 | // Gather parameters and results to print them out. 37 | // 38 | if (Parameters->CallbackType == VariableCallbackGet) 39 | { 40 | guid = *Parameters->Parameters.Get.VendorGuid; 41 | dataSize = **Parameters->Parameters.Get.DataSize; 42 | variableName = *Parameters->Parameters.Get.VariableName; 43 | message = Parameters->Parameters.Get.StatusMessage; 44 | } 45 | else 46 | { 47 | guid = *Parameters->Parameters.Set.VendorGuid; 48 | dataSize = *Parameters->Parameters.Set.DataSize; 49 | variableName = *Parameters->Parameters.Set.VariableName; 50 | message = Parameters->Parameters.Set.StatusMessage; 51 | } 52 | 53 | status = RtlStringCchPrintfA( 54 | guidStr, 55 | RTL_NUMBER_OF(guidStr), 56 | "%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX", 57 | guid->Data1, 58 | guid->Data2, 59 | guid->Data3, 60 | guid->Data4[0], 61 | guid->Data4[1], 62 | guid->Data4[2], 63 | guid->Data4[3], 64 | guid->Data4[4], 65 | guid->Data4[5], 66 | guid->Data4[6], 67 | guid->Data4[7]); 68 | NT_VERIFY(NT_SUCCESS(status)); 69 | 70 | DbgPrintEx(DPFLTR_IHVDRIVER_ID, 71 | DPFLTR_ERROR_LEVEL, 72 | "%c: %s Size=%08X %S: %s\n", 73 | (Parameters->CallbackType == VariableCallbackGet) ? 'G' : 'S', 74 | guidStr, 75 | dataSize, 76 | variableName, 77 | message); 78 | 79 | Exit: 80 | // 81 | // This callback always allows the original function to be called (returns FALSE) . 82 | // 83 | return FALSE; 84 | } 85 | 86 | /** 87 | * @brief Prints out log entries by parsing log buffer. 88 | */ 89 | static 90 | VOID 91 | ProcessBuffer ( 92 | _In_ CONST UINT8* Buffer, 93 | _In_ ULONG EndOffset 94 | ) 95 | { 96 | PAGED_CODE(); 97 | 98 | for (ULONG offset = 0; offset < EndOffset; ) 99 | { 100 | NTSTATUS status; 101 | CONST VARIABLE_LOG_ENTRY* entry; 102 | CHAR guidStr[RTL_GUID_STRING_SIZE - 2 + 1]; // -2 for {}, +1 for NULL 103 | 104 | entry = (CONST VARIABLE_LOG_ENTRY*)&Buffer[offset]; 105 | 106 | status = RtlStringCchPrintfA( 107 | guidStr, 108 | RTL_NUMBER_OF(guidStr), 109 | "%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX", 110 | entry->VendorGuid.Data1, 111 | entry->VendorGuid.Data2, 112 | entry->VendorGuid.Data3, 113 | entry->VendorGuid.Data4[0], 114 | entry->VendorGuid.Data4[1], 115 | entry->VendorGuid.Data4[2], 116 | entry->VendorGuid.Data4[3], 117 | entry->VendorGuid.Data4[4], 118 | entry->VendorGuid.Data4[5], 119 | entry->VendorGuid.Data4[6], 120 | entry->VendorGuid.Data4[7]); 121 | NT_VERIFY(NT_SUCCESS(status)); 122 | 123 | DbgPrintEx(DPFLTR_IHVDRIVER_ID, 124 | DPFLTR_ERROR_LEVEL, 125 | "%c: %s Size=%08X %S: %s\n", 126 | (entry->CallbackType == VariableCallbackGet) ? 'G' : 'S', 127 | guidStr, 128 | entry->DataSize, 129 | entry->VariableName, 130 | entry->StatusMessage); 131 | 132 | offset += ALIGN_UP_BY(sizeof(*entry) + entry->DataSize, 0x10); 133 | } 134 | } 135 | 136 | /** 137 | * @brief Unloading entry point. Unregisters the registered callback. 138 | */ 139 | static 140 | VOID 141 | DriverUnload ( 142 | _In_ PDRIVER_OBJECT DriverObject 143 | ) 144 | { 145 | NTSTATUS status; 146 | VOID* data; 147 | ULONG size; 148 | UNICODE_STRING unregisterCallbacks = RTL_CONSTANT_STRING(L"UnregisterCallbacks"); 149 | 150 | UNREFERENCED_PARAMETER(DriverObject); 151 | 152 | PAGED_CODE(); 153 | 154 | // 155 | // Unregister the callback. 156 | // 157 | data = (VOID*)&HandleGetOrSetVariable; 158 | size = sizeof(data); 159 | status = ExGetFirmwareEnvironmentVariable(&unregisterCallbacks, 160 | (GUID*)&g_BackdoorGuid, 161 | &data, 162 | &size, 163 | NULL); 164 | NT_ASSERT(NT_SUCCESS(status)); 165 | } 166 | 167 | /** 168 | * @brief The module entry point. Prints out all buffered logs and registers a callback. 169 | */ 170 | NTSTATUS 171 | DriverEntry ( 172 | _In_ PDRIVER_OBJECT DriverObject, 173 | _In_ PUNICODE_STRING RegistryPath 174 | ) 175 | { 176 | NTSTATUS status; 177 | ULONG size; 178 | VOID* data; 179 | UINT8* buffer; 180 | UNICODE_STRING drainBuffer = RTL_CONSTANT_STRING(L"DrainBuffer"); 181 | UNICODE_STRING registerCallbacks = RTL_CONSTANT_STRING(L"RegisterCallbacks"); 182 | 183 | UNREFERENCED_PARAMETER(RegistryPath); 184 | 185 | buffer = NULL; 186 | 187 | DriverObject->DriverUnload = DriverUnload; 188 | 189 | // 190 | // Get a size of buffer required to drain contents of saved logs. 191 | // 192 | size = 0; 193 | status = ExGetFirmwareEnvironmentVariable(&drainBuffer, 194 | (GUID*)&g_BackdoorGuid, 195 | NULL, 196 | &size, 197 | NULL); 198 | if (status != STATUS_BUFFER_TOO_SMALL) 199 | { 200 | DbgPrintEx(DPFLTR_IHVDRIVER_ID, 201 | DPFLTR_ERROR_LEVEL, 202 | "ExGetFirmwareEnvironmentVariable returned unexpected value: %08x\n", 203 | status); 204 | status = STATUS_UNSUCCESSFUL; 205 | goto Exit; 206 | } 207 | 208 | // 209 | // Allocate the buffer, get the contents, and parse it if there is any log. 210 | // 211 | if (size != 0) 212 | { 213 | buffer = ExAllocatePoolWithTag(PagedPool, size, 'CMVU'); 214 | if (buffer == NULL) 215 | { 216 | DbgPrintEx(DPFLTR_IHVDRIVER_ID, 217 | DPFLTR_ERROR_LEVEL, 218 | "ExAllocatePoolWithTag failed : %08x\n", size); 219 | status = STATUS_INSUFFICIENT_RESOURCES; 220 | goto Exit; 221 | } 222 | 223 | status = ExGetFirmwareEnvironmentVariable(&drainBuffer, 224 | (GUID*)&g_BackdoorGuid, 225 | buffer, 226 | &size, 227 | NULL); 228 | if (!NT_SUCCESS(status)) 229 | { 230 | DbgPrintEx(DPFLTR_IHVDRIVER_ID, 231 | DPFLTR_ERROR_LEVEL, 232 | "ExGetFirmwareEnvironmentVariable(DrainBuffer) failed : %08x\n", 233 | status); 234 | goto Exit; 235 | } 236 | 237 | ProcessBuffer(buffer, size); 238 | } 239 | 240 | // 241 | // Register the callback. 242 | // 243 | data = (VOID*)&HandleGetOrSetVariable; 244 | size = sizeof(data); 245 | status = ExGetFirmwareEnvironmentVariable(®isterCallbacks, 246 | (GUID*)&g_BackdoorGuid, 247 | &data, 248 | &size, 249 | NULL); 250 | if (!NT_SUCCESS(status)) 251 | { 252 | DbgPrintEx(DPFLTR_IHVDRIVER_ID, 253 | DPFLTR_ERROR_LEVEL, 254 | "ExGetFirmwareEnvironmentVariable(RegisterCallbacks) failed : %08x\n", 255 | status); 256 | goto Exit; 257 | } 258 | 259 | Exit: 260 | if (buffer != NULL) 261 | { 262 | ExFreePoolWithTag(buffer, 'CMVU'); 263 | } 264 | return status; 265 | } 266 | -------------------------------------------------------------------------------- /UefiVarMonitorExClient/UefiVarMonitorExClient/UefiVarMonitorExClient.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | {3E8F9F2C-043B-4047-B7D3-7D7CAF610C7E} 15 | {1bc93793-694f-48fe-9372-81e2b05556fd} 16 | v4.5 17 | 12.0 18 | Debug 19 | Win32 20 | UefiVarMonitorClient 21 | 22 | 23 | 24 | Windows10 25 | true 26 | WindowsKernelModeDriver10.0 27 | Driver 28 | KMDF 29 | Universal 30 | 31 | 32 | Windows10 33 | false 34 | WindowsKernelModeDriver10.0 35 | Driver 36 | KMDF 37 | Universal 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | DbgengKernelDebugger 49 | $(SolutionDir)..\UefiVarMonitorPkg\Drivers\UefiVarMonitorExDxe;$(IncludePath) 50 | 51 | 52 | DbgengKernelDebugger 53 | $(SolutionDir)..\UefiVarMonitorPkg\Drivers\UefiVarMonitorExDxe;$(IncludePath) 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /UefiVarMonitorExClient/UefiVarMonitorExClient/UefiVarMonitorExClient.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {8E41214B-6785-4CFE-B992-037D68949A14} 18 | inf;inv;inx;mof;mc; 19 | 20 | 21 | 22 | 23 | Source Files 24 | 25 | 26 | -------------------------------------------------------------------------------- /UefiVarMonitorPkg/Drivers/UefiVarMonitorDxe/UefiVarMonitorDxe.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static EFI_EVENT g_SetVaMapEvent; 9 | static EFI_GET_VARIABLE g_GetVariable; 10 | static EFI_SET_VARIABLE g_SetVariable; 11 | 12 | /** 13 | * @brief Handles GetVariable runtime service calls. 14 | */ 15 | static 16 | EFI_STATUS 17 | EFIAPI 18 | HandleGetVariable ( 19 | IN CHAR16* VariableName, 20 | IN EFI_GUID* VendorGuid, 21 | OUT UINT32* Attributes OPTIONAL, 22 | IN OUT UINTN* DataSize, 23 | OUT VOID* Data OPTIONAL 24 | ) 25 | { 26 | EFI_STATUS status; 27 | UINTN effectiveDataSize; 28 | 29 | // 30 | // Invoke the original GetVariable service, and log this service invocation. 31 | // 32 | status = g_GetVariable(VariableName, VendorGuid, Attributes, DataSize, Data); 33 | effectiveDataSize = EFI_ERROR(status) ? 0 : *DataSize; 34 | DebugPrint(DEBUG_VERBOSE, 35 | "G: %g Size=%08x %s: %r\n", 36 | VendorGuid, 37 | effectiveDataSize, 38 | VariableName, 39 | status); 40 | 41 | return status; 42 | } 43 | 44 | /** 45 | * @brief Handles SetVariable runtime service calls. 46 | */ 47 | static 48 | EFI_STATUS 49 | EFIAPI 50 | HandleSetVariable ( 51 | IN CHAR16* VariableName, 52 | IN EFI_GUID* VendorGuid, 53 | IN UINT32 Attributes, 54 | IN UINTN DataSize, 55 | IN VOID* Data 56 | ) 57 | { 58 | EFI_STATUS status; 59 | 60 | // 61 | // Invoke the original SetVariable service, and log this service invocation. 62 | // 63 | status = g_SetVariable(VariableName, VendorGuid, Attributes, DataSize, Data); 64 | DebugPrint(DEBUG_VERBOSE, 65 | "S: %g Size=%08x %s: %r\n", 66 | VendorGuid, 67 | DataSize, 68 | VariableName, 69 | status); 70 | 71 | return status; 72 | } 73 | 74 | /** 75 | * @brief Converts global pointers from physical-mode ones to virtual-mode ones. 76 | */ 77 | static 78 | VOID 79 | EFIAPI 80 | HandleSetVirtualAddressMap ( 81 | IN EFI_EVENT Event, 82 | IN VOID* Context 83 | ) 84 | { 85 | EFI_STATUS status; 86 | VOID* currentAddress; 87 | 88 | currentAddress = (VOID*)g_GetVariable; 89 | status = gRT->ConvertPointer(0, (VOID**)&g_GetVariable); 90 | ASSERT_EFI_ERROR(status); 91 | DEBUG((DEBUG_ERROR, 92 | "GetVariable relocated from %p to %p\n", 93 | currentAddress, 94 | g_GetVariable)); 95 | 96 | currentAddress = (VOID*)g_SetVariable; 97 | status = gRT->ConvertPointer(0, (VOID**)&g_SetVariable); 98 | ASSERT_EFI_ERROR(status); 99 | DEBUG((DEBUG_ERROR, 100 | "SetVariable relocated from %p to %p\n", 101 | currentAddress, 102 | g_SetVariable)); 103 | } 104 | 105 | /** 106 | * @brief Exchanges a pointer in the EFI System Table. 107 | */ 108 | static 109 | EFI_STATUS 110 | ExchangePointerInServiceTable ( 111 | IN OUT VOID** AddressToUpdate, 112 | IN VOID* NewPointer, 113 | OUT VOID** OriginalPointer OPTIONAL 114 | ) 115 | { 116 | EFI_STATUS status; 117 | EFI_TPL tpl; 118 | 119 | ASSERT(*AddressToUpdate != NewPointer); 120 | 121 | // 122 | // Disable interrupt. 123 | // 124 | tpl = gBS->RaiseTPL(TPL_HIGH_LEVEL); 125 | 126 | // 127 | // Save the current value if needed and update the pointer. 128 | // 129 | if (OriginalPointer != NULL) 130 | { 131 | *OriginalPointer = *AddressToUpdate; 132 | } 133 | *AddressToUpdate = NewPointer; 134 | 135 | // 136 | // Update the CRC32 in the EFI System Table header. 137 | // 138 | gST->Hdr.CRC32 = 0; 139 | status = gBS->CalculateCrc32(&gST->Hdr, gST->Hdr.HeaderSize, &gST->Hdr.CRC32); 140 | ASSERT_EFI_ERROR(status); 141 | 142 | gBS->RestoreTPL(tpl); 143 | return status; 144 | } 145 | 146 | /** 147 | * @brief Cleans up changes made by this module and release resources. 148 | * 149 | * @details This function handles pertical clean up and is safe for multiple calls. 150 | */ 151 | static 152 | VOID 153 | Cleanup ( 154 | VOID 155 | ) 156 | { 157 | EFI_STATUS status; 158 | 159 | ASSERT(EfiAtRuntime() == FALSE); 160 | 161 | if (gST->RuntimeServices->SetVariable == HandleSetVariable) 162 | { 163 | status = ExchangePointerInServiceTable( 164 | (VOID**)&gST->RuntimeServices->SetVariable, 165 | (VOID*)g_SetVariable, 166 | NULL); 167 | ASSERT_EFI_ERROR(status); 168 | } 169 | 170 | if (gST->RuntimeServices->GetVariable == HandleGetVariable) 171 | { 172 | status = ExchangePointerInServiceTable( 173 | (VOID**)&gST->RuntimeServices->GetVariable, 174 | (VOID*)g_GetVariable, 175 | NULL); 176 | ASSERT_EFI_ERROR(status); 177 | } 178 | 179 | if (g_SetVaMapEvent != NULL) 180 | { 181 | status = gBS->CloseEvent(&g_SetVaMapEvent); 182 | ASSERT_EFI_ERROR(status); 183 | g_SetVaMapEvent = NULL; 184 | } 185 | } 186 | 187 | /** 188 | * @brief The module entry point. 189 | */ 190 | EFI_STATUS 191 | EFIAPI 192 | UefiVarMonitorDxeInitialize ( 193 | IN EFI_HANDLE ImageHandle, 194 | IN EFI_SYSTEM_TABLE* SystemTable 195 | ) 196 | { 197 | EFI_STATUS status; 198 | 199 | DEBUG((DEBUG_ERROR, "Driver being loaded\n")); 200 | 201 | // 202 | // Register a notification for SetVirtualAddressMap call. 203 | // 204 | status = gBS->CreateEventEx(EVT_NOTIFY_SIGNAL, 205 | TPL_CALLBACK, 206 | HandleSetVirtualAddressMap, 207 | NULL, 208 | &gEfiEventVirtualAddressChangeGuid, 209 | &g_SetVaMapEvent); 210 | if (EFI_ERROR(status)) 211 | { 212 | DEBUG((DEBUG_ERROR, "CreateEventEx failed : %r\n", status)); 213 | goto Exit; 214 | } 215 | 216 | // 217 | // Install hooks. 218 | // 219 | status = ExchangePointerInServiceTable((VOID**)&gST->RuntimeServices->GetVariable, 220 | (VOID*)HandleGetVariable, 221 | (VOID**)&g_GetVariable); 222 | if (EFI_ERROR(status)) 223 | { 224 | DEBUG((DEBUG_ERROR, "ExchangeTablePointer(GetVariable) failed : %r\n", status)); 225 | goto Exit; 226 | } 227 | status = ExchangePointerInServiceTable((VOID**)&gST->RuntimeServices->SetVariable, 228 | (VOID*)HandleSetVariable, 229 | (VOID**)&g_SetVariable); 230 | if (EFI_ERROR(status)) 231 | { 232 | DEBUG((DEBUG_ERROR, "ExchangeTablePointer(SetVariable) failed : %r\n", status)); 233 | goto Exit; 234 | } 235 | 236 | Exit: 237 | if (EFI_ERROR(status)) 238 | { 239 | Cleanup(); 240 | } 241 | return status; 242 | } 243 | 244 | /** 245 | * @brief Handles unload request of this module. 246 | */ 247 | EFI_STATUS 248 | EFIAPI 249 | UefiVarMonitorDxeUnload ( 250 | IN EFI_HANDLE ImageHandle 251 | ) 252 | { 253 | Cleanup(); 254 | return EFI_SUCCESS; 255 | } 256 | -------------------------------------------------------------------------------- /UefiVarMonitorPkg/Drivers/UefiVarMonitorDxe/UefiVarMonitorDxe.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 1.27 3 | BASE_NAME = UefiVarMonitorDxe 4 | FILE_GUID = d7e9ffa5-f4ce-41ab-a320-4cd7881ef4a3 5 | MODULE_TYPE = DXE_RUNTIME_DRIVER 6 | VERSION_STRING = 1.0 7 | ENTRY_POINT = UefiVarMonitorDxeInitialize 8 | UNLOAD_IMAGE = UefiVarMonitorDxeUnload 9 | 10 | [Sources] 11 | UefiVarMonitorDxe.c 12 | 13 | [Packages] 14 | MdePkg/MdePkg.dec 15 | 16 | [LibraryClasses] 17 | UefiDriverEntryPoint 18 | UefiLib 19 | UefiRuntimeLib 20 | 21 | [Guids] 22 | gEfiEventVirtualAddressChangeGuid 23 | 24 | [Depex] 25 | TRUE 26 | 27 | [BuildOptions.common.DXE_RUNTIME_DRIVER] 28 | # Detect use of deprecated interfaces if any. 29 | MSFT:*_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES 30 | 31 | # Remove DebugLib library instances (ASSERT and such) from the RELEASE binary. 32 | # https://github.com/tianocore-docs/edk2-UefiDriverWritersGuide/blob/master/31_testing_and_debugging_uefi_drivers/314_debugging_code_statements/3141_configuring_debuglib_with_edk_ii.md 33 | MSFT:RELEASE_*_*_CC_FLAGS = -D MDEPKG_NDEBUG 34 | 35 | # By default, certain meta-data in the PE header is zeroed out to increase 36 | # compression ratio. Some of those information can be helpful for a debugger, 37 | # for example, to reconstruct stack trace. Leave it for such cases. See also, 38 | # https://edk2-docs.gitbooks.io/edk-ii-basetools-user-guides/content/GenFw.html 39 | MSFT:*_*_X64_GENFW_FLAGS = --keepexceptiontable --keepzeropending --keepoptionalheader 40 | -------------------------------------------------------------------------------- /UefiVarMonitorPkg/Drivers/UefiVarMonitorExDxe/UefiVarMonitorExDxe.c: -------------------------------------------------------------------------------- 1 | #include "UefiVarMonitorExDxe.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | // 16 | // 256KB should be enough for every one, eh? 17 | // 18 | #define RUNTIME_BUFFER_SIZE_IN_PAGES ((UINTN)64) 19 | #define RUNTIME_BUFFER_SIZE_IN_BYTES (RUNTIME_BUFFER_SIZE_IN_PAGES * EFI_PAGE_SIZE) 20 | 21 | static EFI_EVENT g_SetVaMapEvent; 22 | static EFI_GET_VARIABLE g_GetVariable; 23 | static EFI_SET_VARIABLE g_SetVariable; 24 | 25 | // 26 | // Log buffer related. 27 | // 28 | static SPIN_LOCK g_LogBufferSpinLock; 29 | static UINT8* g_LogBuffer; 30 | static UINTN g_CurrentPoision; 31 | 32 | // 33 | // Callbacks. 34 | // 35 | static SPIN_LOCK g_VariableCallbacksLock; 36 | static VARIABLE_CALLBACK g_VariableCallbacks[8]; 37 | 38 | 39 | #if defined(_MSC_VER) 40 | // 41 | // MSVC compiler intrinsics for CR8 access. 42 | // 43 | UINTN __readcr8(VOID); 44 | VOID __writecr8(UINTN Data); 45 | 46 | #elif defined(__GNUC__) 47 | // 48 | // Inline assemblies for CR8 access. 49 | // 50 | static 51 | __inline__ 52 | __attribute__((always_inline)) 53 | UINTN 54 | __readcr8 ( 55 | VOID 56 | ) 57 | { 58 | UINTN value; 59 | 60 | __asm__ __volatile__ ( 61 | "mov %%cr8, %[value]" 62 | : [value] "=a" (value) 63 | ); 64 | return value; 65 | } 66 | 67 | static 68 | __inline__ 69 | __attribute__((always_inline)) 70 | VOID 71 | __writecr8 ( 72 | UINTN Data 73 | ) 74 | { 75 | __asm__ __volatile__ ( 76 | "mov %%eax, %%cr8" 77 | : 78 | : "a" (Data) 79 | ); 80 | } 81 | #endif 82 | 83 | /** 84 | * @brief Disables low-priority interrupts and acquires the spin lock, 85 | */ 86 | static 87 | VOID 88 | AcquireSpinLockForNt ( 89 | IN OUT SPIN_LOCK* SpinLock, 90 | OUT UINTN* OldInterruptState 91 | ) 92 | { 93 | static CONST UINTN dispatchLevel = 2; 94 | 95 | *OldInterruptState = __readcr8(); 96 | ASSERT(*OldInterruptState <= dispatchLevel); 97 | 98 | __writecr8(dispatchLevel); 99 | AcquireSpinLock(SpinLock); 100 | } 101 | 102 | /** 103 | * @brief Enables low-level interrupts and releases the spin lock. 104 | */ 105 | static 106 | VOID 107 | ReleaseSpinLockForNt ( 108 | IN OUT SPIN_LOCK* SpinLock, 109 | IN UINTN NewInterruptState 110 | ) 111 | { 112 | ReleaseSpinLock(SpinLock); 113 | __writecr8(NewInterruptState); 114 | } 115 | 116 | /** 117 | * @brief Adds the new log entry to the global log buffer. 118 | */ 119 | static 120 | VOID 121 | AddLogEntryVariable( 122 | VARIABLE_CALLBACK_TYPE CallbackType, 123 | CONST CHAR16* VariableName, 124 | CONST EFI_GUID* VendorGuid, 125 | UINT32 Attributes, 126 | UINTN DataSize, 127 | CONST VOID* Data OPTIONAL, 128 | EFI_STATUS Status 129 | ) 130 | { 131 | UINTN interruptState; 132 | UINTN entrySize; 133 | VARIABLE_LOG_ENTRY* entry; 134 | 135 | AcquireSpinLockForNt(&g_LogBufferSpinLock, &interruptState); 136 | 137 | entrySize = sizeof(*entry) + DataSize; 138 | if ((g_CurrentPoision + entrySize) <= RUNTIME_BUFFER_SIZE_IN_BYTES) 139 | { 140 | // 141 | // Copy parameters to the log buffer. 142 | // 143 | entry = (VARIABLE_LOG_ENTRY*)&g_LogBuffer[g_CurrentPoision]; 144 | StrnCpyS(entry->VariableName, 145 | ARRAY_SIZE(entry->VariableName), 146 | VariableName, 147 | ARRAY_SIZE(entry->VariableName) - 1); 148 | entry->VendorGuid = *VendorGuid; 149 | entry->CallbackType = CallbackType; 150 | entry->Attributes = Attributes; 151 | entry->Status = Status; 152 | AsciiSPrint(entry->StatusMessage, sizeof(entry->StatusMessage), "%r", Status); 153 | entry->DataSize = DataSize; 154 | CopyMem(entry->Data, Data, DataSize); 155 | 156 | // 157 | // Log entries must start at 16 byte alignment. 158 | // 159 | g_CurrentPoision += ALIGN_VALUE(entrySize, 0x10); 160 | ASSERT((g_CurrentPoision % 0x10) == 0); 161 | } 162 | 163 | ReleaseSpinLockForNt(&g_LogBufferSpinLock, interruptState); 164 | 165 | DebugPrint(DEBUG_VERBOSE, 166 | "%c: %g Size=%08x %s: %r\n", 167 | (CallbackType == VariableCallbackGet) ? L'G' : L'S', 168 | VendorGuid, 169 | DataSize, 170 | VariableName, 171 | Status); 172 | } 173 | 174 | /** 175 | * @brief Moves the contents of the log buffer to the provided buffer. 176 | */ 177 | static 178 | EFI_STATUS 179 | HandleDrainBufferCommand ( 180 | OUT VOID* Buffer OPTIONAL, 181 | IN OUT UINTN* BufferSize 182 | ) 183 | { 184 | EFI_STATUS status; 185 | UINTN interruptState; 186 | 187 | ASSERT((Buffer != NULL) || (*BufferSize == 0)); 188 | 189 | // 190 | // Return the log buffer size if the provided buffer size is smaller than that. 191 | // 192 | if (*BufferSize < RUNTIME_BUFFER_SIZE_IN_BYTES) 193 | { 194 | *BufferSize = RUNTIME_BUFFER_SIZE_IN_BYTES; 195 | status = EFI_BUFFER_TOO_SMALL; 196 | goto Exit; 197 | } 198 | 199 | // 200 | // Copy the contents of log buffer to provided buffer and update the 201 | // buffer size with the current size. Then, reset the log buffer and the 202 | // current size. 203 | // 204 | AcquireSpinLockForNt(&g_LogBufferSpinLock, &interruptState); 205 | 206 | CopyMem(Buffer, g_LogBuffer, g_CurrentPoision); 207 | *BufferSize = g_CurrentPoision; 208 | ZeroMem(g_LogBuffer, g_CurrentPoision); 209 | g_CurrentPoision = 0; 210 | 211 | ReleaseSpinLockForNt(&g_LogBufferSpinLock, interruptState); 212 | 213 | status = EFI_SUCCESS; 214 | 215 | Exit: 216 | return status; 217 | } 218 | 219 | /** 220 | * @brief Registers the callbacks of Get/SetVariable. 221 | */ 222 | static 223 | EFI_STATUS 224 | HandleRegisterCallbacksCommand ( 225 | OUT VOID* Buffer OPTIONAL, 226 | IN OUT UINTN* BufferSize 227 | ) 228 | { 229 | EFI_STATUS status; 230 | UINTN interruptState; 231 | VARIABLE_CALLBACK callback; 232 | 233 | if ((Buffer == NULL) || 234 | (*BufferSize != sizeof(VARIABLE_CALLBACK*))) 235 | { 236 | status = EFI_INVALID_PARAMETER; 237 | goto Exit; 238 | } 239 | 240 | // 241 | // Return this error when no slot is available. 242 | // 243 | status = EFI_OUT_OF_RESOURCES; 244 | callback = *(VARIABLE_CALLBACK*)Buffer; 245 | 246 | AcquireSpinLockForNt(&g_VariableCallbacksLock, &interruptState); 247 | 248 | for (UINTN i = 0; i < ARRAY_SIZE(g_VariableCallbacks); i++) 249 | { 250 | // 251 | // Register the callback if an empty slot is found. 252 | // 253 | if (g_VariableCallbacks[i] == NULL) 254 | { 255 | g_VariableCallbacks[i] = callback; 256 | status = EFI_SUCCESS; 257 | break; 258 | } 259 | 260 | // 261 | // Return error if the same callback is already registered. 262 | // 263 | if (g_VariableCallbacks[i] == callback) 264 | { 265 | status = EFI_INVALID_PARAMETER; 266 | break; 267 | } 268 | } 269 | 270 | ReleaseSpinLockForNt(&g_VariableCallbacksLock, interruptState); 271 | 272 | status = EFI_SUCCESS; 273 | 274 | Exit: 275 | return status; 276 | } 277 | 278 | /** 279 | * @brief Unregisters the callback of Get/SetVariable. 280 | */ 281 | static 282 | EFI_STATUS 283 | HandleUnregisterCallbacksCommand ( 284 | OUT VOID* Buffer OPTIONAL, 285 | IN OUT UINTN* BufferSize 286 | ) 287 | { 288 | EFI_STATUS status; 289 | UINTN interruptState; 290 | VARIABLE_CALLBACK callback; 291 | 292 | if ((Buffer == NULL) || 293 | (*BufferSize != sizeof(VARIABLE_CALLBACK*))) 294 | { 295 | status = EFI_INVALID_PARAMETER; 296 | goto Exit; 297 | } 298 | 299 | // 300 | // Return this error when no slot is available. 301 | // 302 | status = EFI_INVALID_PARAMETER; 303 | callback = *(VARIABLE_CALLBACK*)Buffer; 304 | 305 | AcquireSpinLockForNt(&g_VariableCallbacksLock, &interruptState); 306 | 307 | for (UINTN i = 0; i < ARRAY_SIZE(g_VariableCallbacks); i++) 308 | { 309 | // 310 | // Clear the callback if the same ones as specified are found. 311 | // 312 | if (g_VariableCallbacks[i] == callback) 313 | { 314 | g_VariableCallbacks[i] = NULL; 315 | status = EFI_SUCCESS; 316 | break; 317 | } 318 | } 319 | 320 | ReleaseSpinLockForNt(&g_VariableCallbacksLock, interruptState); 321 | 322 | 323 | Exit: 324 | return status; 325 | } 326 | 327 | /** 328 | * @brief Handles the backdoor command. 329 | */ 330 | static 331 | EFI_STATUS 332 | HandleBackdoorRequest ( 333 | IN CONST CHAR16* VariableName, 334 | IN OUT VOID* Data OPTIONAL, 335 | IN OUT UINTN* DataSize 336 | ) 337 | { 338 | EFI_STATUS status; 339 | 340 | if (StrCmp(VariableName, L"RegisterCallbacks") == 0) 341 | { 342 | status = HandleRegisterCallbacksCommand(Data, DataSize); 343 | } 344 | else if (StrCmp(VariableName, L"UnregisterCallbacks") == 0) 345 | { 346 | status = HandleUnregisterCallbacksCommand(Data, DataSize); 347 | } 348 | else if (StrCmp(VariableName, L"DrainBuffer") == 0) 349 | { 350 | status = HandleDrainBufferCommand(Data, DataSize); 351 | } 352 | else 353 | { 354 | status = EFI_INVALID_PARAMETER; 355 | } 356 | 357 | return status; 358 | } 359 | 360 | /** 361 | * @brief Invokes all registered callbacks. 362 | */ 363 | static 364 | EFI_STATUS 365 | InvokeCallbacks ( 366 | IN OUT VARIABLE_CALLBACK_PARAMETERS* Parameters 367 | ) 368 | { 369 | UINTN interruptState; 370 | BOOLEAN blocked; 371 | 372 | blocked = FALSE; 373 | 374 | AcquireSpinLockForNt(&g_VariableCallbacksLock, &interruptState); 375 | 376 | for (UINTN i = 0; i < ARRAY_SIZE(g_VariableCallbacks); i++) 377 | { 378 | if (g_VariableCallbacks[i] == NULL) 379 | { 380 | continue; 381 | } 382 | 383 | // 384 | // Invoke a callback. The blocked status cannot be override if any of 385 | // callbacks returned TRUE. 386 | // 387 | blocked |= g_VariableCallbacks[i](Parameters); 388 | } 389 | 390 | ReleaseSpinLockForNt(&g_VariableCallbacksLock, interruptState); 391 | 392 | // 393 | // Return EFI_ACCESS_DENIED if any of callbacks returned TRUE. 394 | // 395 | return (blocked == FALSE) ? EFI_SUCCESS : EFI_ACCESS_DENIED; 396 | } 397 | 398 | /** 399 | * @brief Invokes all registered Get callbacks. 400 | */ 401 | static 402 | EFI_STATUS 403 | InvokeGetCallbacks ( 404 | IN OPERATION_TYPE OperationType, 405 | IN OUT CHAR16** VariableName, 406 | IN OUT EFI_GUID** VendorGuid, 407 | IN OUT UINT32** Attributes OPTIONAL, 408 | IN OUT UINTN** DataSize, 409 | IN OUT VOID** Data OPTIONAL, 410 | IN CONST EFI_STATUS* ResultStatus OPTIONAL 411 | ) 412 | { 413 | BOOLEAN succeeded; 414 | CHAR8 statusMessage[32]; 415 | CHAR8* statusMessagePtr; 416 | VARIABLE_CALLBACK_PARAMETERS parameters; 417 | 418 | // 419 | // Convert the resulted status to a human readable string if it is given. 420 | // 421 | if (ResultStatus != NULL) 422 | { 423 | AsciiSPrint(statusMessage, sizeof(statusMessage), "%r", *ResultStatus); 424 | statusMessagePtr = statusMessage; 425 | succeeded = (EFI_ERROR(*ResultStatus) == FALSE); 426 | } 427 | else 428 | { 429 | statusMessagePtr = NULL; 430 | succeeded = FALSE; 431 | } 432 | 433 | parameters.CallbackType = VariableCallbackGet; 434 | parameters.OperationType = OperationType; 435 | parameters.Parameters.Get.VariableName = VariableName; 436 | parameters.Parameters.Get.VendorGuid = VendorGuid; 437 | parameters.Parameters.Get.Attributes = Attributes; 438 | parameters.Parameters.Get.DataSize = DataSize; 439 | parameters.Parameters.Get.Data = Data; 440 | parameters.Parameters.Get.Succeeded = succeeded; 441 | parameters.Parameters.Get.StatusMessage = statusMessagePtr; 442 | 443 | return InvokeCallbacks(¶meters); 444 | } 445 | 446 | /** 447 | * @brief Invokes all registered Set callbacks. 448 | */ 449 | static 450 | EFI_STATUS 451 | ProcessSetCallbacks ( 452 | IN OPERATION_TYPE OperationType, 453 | IN OUT CHAR16** VariableName, 454 | IN OUT EFI_GUID** VendorGuid, 455 | IN OUT UINT32* Attributes, 456 | IN OUT UINTN* DataSize, 457 | IN OUT VOID** Data, 458 | IN CONST EFI_STATUS* ResultStatus OPTIONAL 459 | ) 460 | { 461 | BOOLEAN succeeded; 462 | CHAR8 statusMessage[32]; 463 | CHAR8* statusMessagePtr; 464 | VARIABLE_CALLBACK_PARAMETERS parameters; 465 | 466 | // 467 | // Convert the resulted status to a human readable string if it is given. 468 | // 469 | if (ResultStatus != NULL) 470 | { 471 | AsciiSPrint(statusMessage, sizeof(statusMessage), "%r", *ResultStatus); 472 | statusMessagePtr = statusMessage; 473 | succeeded = (EFI_ERROR(*ResultStatus) == FALSE); 474 | } 475 | else 476 | { 477 | statusMessagePtr = NULL; 478 | succeeded = FALSE; 479 | } 480 | 481 | parameters.CallbackType = VariableCallbackSet; 482 | parameters.OperationType = OperationType; 483 | parameters.Parameters.Set.VariableName = VariableName; 484 | parameters.Parameters.Set.VendorGuid = VendorGuid; 485 | parameters.Parameters.Set.Attributes = Attributes; 486 | parameters.Parameters.Set.DataSize = DataSize; 487 | parameters.Parameters.Set.Data = Data; 488 | parameters.Parameters.Set.Succeeded = succeeded; 489 | parameters.Parameters.Set.StatusMessage = statusMessagePtr; 490 | 491 | return InvokeCallbacks(¶meters); 492 | } 493 | 494 | /** 495 | * @brief Handles GetVariable runtime service calls. 496 | */ 497 | static 498 | EFI_STATUS 499 | EFIAPI 500 | HandleGetVariable ( 501 | IN CHAR16* VariableName, 502 | IN EFI_GUID* VendorGuid, 503 | OUT UINT32* Attributes OPTIONAL, 504 | IN OUT UINTN* DataSize, 505 | OUT VOID* Data OPTIONAL 506 | ) 507 | { 508 | EFI_STATUS status; 509 | UINTN effectiveDataSize; 510 | UINT32 effectiveAttributes; 511 | 512 | // 513 | // Only execute a backdoor command if the certain GUID is specified. 514 | // 515 | if (CompareGuid(VendorGuid, &g_BackdoorGuid) != FALSE) 516 | { 517 | status = HandleBackdoorRequest(VariableName, Data, DataSize); 518 | goto Exit; 519 | } 520 | 521 | // 522 | // Invoke Pre- Get callbacks. Callbacks can make the service call fail. 523 | // 524 | status = InvokeGetCallbacks(OperationPre, 525 | &VariableName, 526 | &VendorGuid, 527 | &Attributes, 528 | &DataSize, 529 | &Data, 530 | NULL); 531 | if (EFI_ERROR(status)) 532 | { 533 | goto Exit; 534 | } 535 | 536 | // 537 | // Invoke the original GetVariable service, and log this service invocation. 538 | // 539 | status = g_GetVariable(VariableName, VendorGuid, Attributes, DataSize, Data); 540 | effectiveDataSize = EFI_ERROR(status) ? 0 : *DataSize; 541 | effectiveAttributes = (EFI_ERROR(status) || (Attributes == NULL)) ? 0 : *Attributes; 542 | AddLogEntryVariable(VariableCallbackGet, 543 | VariableName, 544 | VendorGuid, 545 | effectiveAttributes, 546 | effectiveDataSize, 547 | Data, 548 | status); 549 | 550 | // 551 | // Invoke Post- Get callbacks. Post callbacks cannot make the service fail. 552 | // 553 | InvokeGetCallbacks(OperationPost, 554 | &VariableName, 555 | &VendorGuid, 556 | &Attributes, 557 | &DataSize, 558 | &Data, 559 | &status); 560 | 561 | Exit: 562 | return status; 563 | } 564 | 565 | /** 566 | * @brief Handles SetVariable runtime service calls. 567 | */ 568 | static 569 | EFI_STATUS 570 | EFIAPI 571 | HandleSetVariable ( 572 | IN CHAR16* VariableName, 573 | IN EFI_GUID* VendorGuid, 574 | IN UINT32 Attributes, 575 | IN UINTN DataSize, 576 | IN VOID* Data 577 | ) 578 | { 579 | EFI_STATUS status; 580 | 581 | // 582 | // Invoke Pre- Set callbacks. Callbacks can make the service call fail. 583 | // 584 | status = ProcessSetCallbacks(OperationPre, 585 | &VariableName, 586 | &VendorGuid, 587 | &Attributes, 588 | &DataSize, 589 | &Data, 590 | NULL); 591 | if (EFI_ERROR(status)) 592 | { 593 | goto Exit; 594 | } 595 | 596 | // 597 | // Invoke the original SetVariable service, and log this service invocation. 598 | // 599 | status = g_SetVariable(VariableName, VendorGuid, Attributes, DataSize, Data); 600 | AddLogEntryVariable(VariableCallbackSet, 601 | VariableName, 602 | VendorGuid, 603 | Attributes, 604 | DataSize, 605 | Data, 606 | status); 607 | 608 | // 609 | // Invoke Post- Set callbacks. Post callbacks cannot make the service fail. 610 | // 611 | ProcessSetCallbacks(OperationPost, 612 | &VariableName, 613 | &VendorGuid, 614 | &Attributes, 615 | &DataSize, 616 | &Data, 617 | &status); 618 | 619 | Exit: 620 | return status; 621 | } 622 | 623 | /** 624 | * @brief Converts global pointers from physical-mode ones to virtual-mode ones. 625 | */ 626 | static 627 | VOID 628 | EFIAPI 629 | HandleSetVirtualAddressMap ( 630 | IN EFI_EVENT Event, 631 | IN VOID* Context 632 | ) 633 | { 634 | EFI_STATUS status; 635 | VOID* currentAddress; 636 | 637 | currentAddress = (VOID*)g_GetVariable; 638 | status = gRT->ConvertPointer(0, (VOID**)&g_GetVariable); 639 | ASSERT_EFI_ERROR(status); 640 | DEBUG((DEBUG_ERROR, 641 | "GetVariable relocated from %p to %p\n", 642 | currentAddress, 643 | g_GetVariable)); 644 | 645 | currentAddress = (VOID*)g_SetVariable; 646 | status = gRT->ConvertPointer(0, (VOID**)&g_SetVariable); 647 | ASSERT_EFI_ERROR(status); 648 | DEBUG((DEBUG_ERROR, 649 | "SetVariable relocated from %p to %p\n", 650 | currentAddress, 651 | g_SetVariable)); 652 | 653 | currentAddress = (VOID*)g_LogBuffer; 654 | status = gRT->ConvertPointer(0, (VOID**)&g_LogBuffer); 655 | ASSERT_EFI_ERROR(status); 656 | DEBUG((DEBUG_ERROR, 657 | "RuntimeBuffer relocated from %p to %p\n", 658 | currentAddress, 659 | g_LogBuffer)); 660 | } 661 | 662 | /** 663 | * @brief Exchanges a pointer in the EFI System Table. 664 | */ 665 | static 666 | EFI_STATUS 667 | ExchangePointerInServiceTable ( 668 | IN OUT VOID** AddressToUpdate, 669 | IN VOID* NewPointer, 670 | OUT VOID** OriginalPointer OPTIONAL 671 | ) 672 | { 673 | EFI_STATUS status; 674 | EFI_TPL tpl; 675 | 676 | ASSERT(*AddressToUpdate != NewPointer); 677 | 678 | tpl = gBS->RaiseTPL(TPL_HIGH_LEVEL); 679 | 680 | // 681 | // Save the current value if needed. 682 | // 683 | if (OriginalPointer != NULL) 684 | { 685 | *OriginalPointer = *AddressToUpdate; 686 | } 687 | *AddressToUpdate = NewPointer; 688 | 689 | // 690 | // Update the CRC32 in the EFI System Table header. 691 | // 692 | gST->Hdr.CRC32 = 0; 693 | status = gBS->CalculateCrc32(&gST->Hdr, gST->Hdr.HeaderSize, &gST->Hdr.CRC32); 694 | ASSERT_EFI_ERROR(status); 695 | 696 | gBS->RestoreTPL(tpl); 697 | return status; 698 | } 699 | 700 | /** 701 | * @brief Cleans up changes made by this module and release resources. 702 | * 703 | * @details This function handles pertical clean up and is safe for multiple calls. 704 | */ 705 | static 706 | VOID 707 | Cleanup ( 708 | VOID 709 | ) 710 | { 711 | EFI_STATUS status; 712 | 713 | ASSERT(EfiAtRuntime() == FALSE); 714 | 715 | if (gST->RuntimeServices->SetVariable == HandleSetVariable) 716 | { 717 | status = ExchangePointerInServiceTable( 718 | (VOID**)&gST->RuntimeServices->SetVariable, 719 | (VOID*)g_SetVariable, 720 | NULL); 721 | ASSERT_EFI_ERROR(status); 722 | } 723 | 724 | if (gST->RuntimeServices->GetVariable == HandleGetVariable) 725 | { 726 | status = ExchangePointerInServiceTable( 727 | (VOID**)&gST->RuntimeServices->GetVariable, 728 | (VOID*)g_GetVariable, 729 | NULL); 730 | ASSERT_EFI_ERROR(status); 731 | } 732 | 733 | if (g_SetVaMapEvent != NULL) 734 | { 735 | status = gBS->CloseEvent(&g_SetVaMapEvent); 736 | g_SetVaMapEvent = NULL; 737 | ASSERT_EFI_ERROR(status); 738 | } 739 | 740 | if (g_LogBuffer != NULL) 741 | { 742 | FreePages(g_LogBuffer, RUNTIME_BUFFER_SIZE_IN_PAGES); 743 | g_LogBuffer = NULL; 744 | } 745 | } 746 | 747 | /** 748 | * @brief The module entry point. 749 | */ 750 | EFI_STATUS 751 | EFIAPI 752 | UefiVarMonitorDxeInitialize ( 753 | IN EFI_HANDLE ImageHandle, 754 | IN EFI_SYSTEM_TABLE* SystemTable 755 | ) 756 | { 757 | EFI_STATUS status; 758 | 759 | InitializeSpinLock(&g_LogBufferSpinLock); 760 | InitializeSpinLock(&g_VariableCallbacksLock); 761 | 762 | DEBUG((DEBUG_ERROR, "Driver being loaded\n")); 763 | 764 | // 765 | // Allocate the memory that is availabe for use even at the runtime phase. 766 | // 767 | g_LogBuffer = AllocateRuntimePages(RUNTIME_BUFFER_SIZE_IN_PAGES); 768 | if (g_LogBuffer == NULL) 769 | { 770 | status = EFI_OUT_OF_RESOURCES; 771 | DEBUG((DEBUG_ERROR, "AllocateRuntimePages failed\n")); 772 | goto Exit; 773 | } 774 | ZeroMem(g_LogBuffer, RUNTIME_BUFFER_SIZE_IN_PAGES * EFI_PAGE_SIZE); 775 | 776 | // 777 | // Register a notification for SetVirtualAddressMap call. 778 | // 779 | status = gBS->CreateEventEx(EVT_NOTIFY_SIGNAL, 780 | TPL_CALLBACK, 781 | HandleSetVirtualAddressMap, 782 | NULL, 783 | &gEfiEventVirtualAddressChangeGuid, 784 | &g_SetVaMapEvent); 785 | if (EFI_ERROR(status)) 786 | { 787 | DEBUG((DEBUG_ERROR, "CreateEventEx failed : %r\n", status)); 788 | goto Exit; 789 | } 790 | 791 | // 792 | // Install hooks. At this point, everything that is used in the hook handlers 793 | // must be initialized. 794 | // 795 | status = ExchangePointerInServiceTable((VOID**)&gST->RuntimeServices->GetVariable, 796 | (VOID*)HandleGetVariable, 797 | (VOID**)&g_GetVariable); 798 | if (EFI_ERROR(status)) 799 | { 800 | DEBUG((DEBUG_ERROR, "ExchangeTablePointer(GetVariable) failed : %r\n", status)); 801 | goto Exit; 802 | } 803 | status = ExchangePointerInServiceTable((VOID**)&gST->RuntimeServices->SetVariable, 804 | (VOID*)HandleSetVariable, 805 | (VOID**)&g_SetVariable); 806 | if (EFI_ERROR(status)) 807 | { 808 | DEBUG((DEBUG_ERROR, "ExchangeTablePointer(SetVariable) failed : %r\n", status)); 809 | goto Exit; 810 | } 811 | 812 | Exit: 813 | if (EFI_ERROR(status)) 814 | { 815 | Cleanup(); 816 | } 817 | return status; 818 | } 819 | 820 | /** 821 | * @brief Handles unload request of this module. 822 | */ 823 | EFI_STATUS 824 | EFIAPI 825 | UefiVarMonitorDxeUnload ( 826 | IN EFI_HANDLE ImageHandle 827 | ) 828 | { 829 | Cleanup(); 830 | return EFI_SUCCESS; 831 | } 832 | -------------------------------------------------------------------------------- /UefiVarMonitorPkg/Drivers/UefiVarMonitorExDxe/UefiVarMonitorExDxe.h: -------------------------------------------------------------------------------- 1 | #ifndef __UEFI_VAR_MONITOR_EX_DXE_H__ 2 | #define __UEFI_VAR_MONITOR_EX_DXE_H__ 3 | 4 | #if defined(NTDDI_VERSION) 5 | 6 | // 7 | // For NT drivers. 8 | // 9 | #include 10 | #define EFIAPI __cdecl 11 | typedef WCHAR CHAR16; 12 | typedef CHAR CHAR8; 13 | typedef SIZE_T EFI_STATUS; 14 | typedef SIZE_T UINTN; 15 | 16 | #else 17 | 18 | // 19 | // For UEFI drivers. 20 | // 21 | #include 22 | 23 | #endif 24 | 25 | // 26 | // {3DEC99FB-86B4-4EED-B4D8-4E6ADDE56F95} 27 | // 28 | CONST GUID g_BackdoorGuid = 29 | { 0x3dec99fb, 0x86b4, 0x4eed, { 0xb4, 0xd8, 0x4e, 0x6a, 0xdd, 0xe5, 0x6f, 0x95 } }; 30 | 31 | typedef enum _VARIABLE_CALLBACK_TYPE 32 | { 33 | VariableCallbackGet, 34 | VariableCallbackSet, 35 | } VARIABLE_CALLBACK_TYPE; 36 | 37 | typedef enum _OPERATION_TYPE 38 | { 39 | OperationPre, 40 | OperationPost, 41 | } OPERATION_TYPE; 42 | 43 | // 44 | // The single log entry type in the log buffer. 45 | // 46 | #if defined(_MSC_VER) 47 | #pragma warning(push) 48 | #pragma warning(disable: 4200) 49 | #endif 50 | typedef struct _VARIABLE_LOG_ENTRY 51 | { 52 | CHAR16 VariableName[64]; 53 | GUID VendorGuid; 54 | VARIABLE_CALLBACK_TYPE CallbackType; 55 | UINT32 Attributes; 56 | EFI_STATUS Status; 57 | CHAR8 StatusMessage[32]; 58 | UINTN DataSize; 59 | UINT8 Data[0]; 60 | } VARIABLE_LOG_ENTRY; 61 | #if defined(_MSC_VER) 62 | #pragma warning(pop) 63 | #endif 64 | 65 | // 66 | // The parameter type of the Get/SetVariable service callback. 67 | // 68 | typedef struct _VARIABLE_CALLBACK_PARAMETERS 69 | { 70 | VARIABLE_CALLBACK_TYPE CallbackType; 71 | OPERATION_TYPE OperationType; 72 | union 73 | { 74 | struct 75 | { 76 | CHAR16** VariableName; // Mutable 77 | GUID** VendorGuid; // Mutable 78 | UINT32** Attributes; // Mutable; (*Attributes) may be NULL 79 | UINTN** DataSize; // Mutable 80 | VOID** Data; // Mutable; (*Data) may be NULL 81 | BOOLEAN Succeeded; // Immutable 82 | CHAR8* StatusMessage; // Immutable 83 | } Get; 84 | 85 | struct 86 | { 87 | CHAR16** VariableName; // Mutable 88 | GUID** VendorGuid; // Mutable 89 | UINT32* Attributes; // Mutable 90 | UINTN* DataSize; // Mutable 91 | VOID** Data; // Mutable 92 | BOOLEAN Succeeded; // Immutable 93 | CHAR8* StatusMessage; // Immutable 94 | } Set; 95 | } Parameters; 96 | } VARIABLE_CALLBACK_PARAMETERS; 97 | 98 | // 99 | // Get/SetVariable service callback type. 100 | // 101 | // The callbacks are allowed to modify values pointed from Parameters if it is 102 | // indicated as mutable. 103 | // 104 | // Returning TRUE on Pre-callback prevent the service to be executed. 105 | // On Post-callback, the return value is ignored. 106 | // 107 | typedef 108 | BOOLEAN 109 | (EFIAPI*VARIABLE_CALLBACK) ( 110 | IN OUT VARIABLE_CALLBACK_PARAMETERS* Parameters 111 | ); 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /UefiVarMonitorPkg/Drivers/UefiVarMonitorExDxe/UefiVarMonitorExDxe.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 1.27 3 | BASE_NAME = UefiVarMonitorExDxe 4 | FILE_GUID = e6bb14be-2668-44d2-97bc-f229994eaa91 5 | MODULE_TYPE = DXE_RUNTIME_DRIVER 6 | VERSION_STRING = 1.0 7 | ENTRY_POINT = UefiVarMonitorDxeInitialize 8 | UNLOAD_IMAGE = UefiVarMonitorDxeUnload 9 | 10 | [Sources] 11 | UefiVarMonitorExDxe.c 12 | 13 | [Packages] 14 | MdePkg/MdePkg.dec 15 | 16 | [LibraryClasses] 17 | MemoryAllocationLib 18 | PrintLib 19 | SynchronizationLib 20 | UefiDriverEntryPoint 21 | UefiLib 22 | UefiRuntimeLib 23 | 24 | [Guids] 25 | gEfiEventVirtualAddressChangeGuid 26 | 27 | [Depex] 28 | TRUE 29 | 30 | [BuildOptions.common.DXE_RUNTIME_DRIVER] 31 | # Detect use of deprecated interfaces if any. 32 | MSFT:*_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES 33 | 34 | # Remove DebugLib library instances (ASSERT and such) from the RELEASE binary. 35 | # https://github.com/tianocore-docs/edk2-UefiDriverWritersGuide/blob/master/31_testing_and_debugging_uefi_drivers/314_debugging_code_statements/3141_configuring_debuglib_with_edk_ii.md 36 | MSFT:RELEASE_*_*_CC_FLAGS = -D MDEPKG_NDEBUG 37 | 38 | # By default, certain meta-data in the PE header is zeroed out to increase 39 | # compression ratio. Some of those information can be helpful for a debugger, 40 | # for example, to reconstruct stack trace. Leave it for such cases. See also, 41 | # https://edk2-docs.gitbooks.io/edk-ii-basetools-user-guides/content/GenFw.html 42 | MSFT:*_*_X64_GENFW_FLAGS = --keepexceptiontable --keepzeropending --keepoptionalheader 43 | -------------------------------------------------------------------------------- /UefiVarMonitorPkg/UefiVarMonitorPkg.dec: -------------------------------------------------------------------------------- 1 | [Defines] 2 | DEC_SPECIFICATION = 1.27 3 | PACKAGE_NAME = UefiVarMonitorPkg 4 | PACKAGE_GUID = 44df64b6-d629-48c9-9827-2072f1c0d376 5 | PACKAGE_VERSION = 1.00 6 | -------------------------------------------------------------------------------- /UefiVarMonitorPkg/UefiVarMonitorPkg.dsc: -------------------------------------------------------------------------------- 1 | [Defines] 2 | DSC_SPECIFICATION = 1.28 3 | PLATFORM_NAME = UefiVarMonitorPkg 4 | PLATFORM_GUID = 0b79e762-783e-4448-b8e1-ca9659e9371a 5 | PLATFORM_VERSION = 1.00 6 | OUTPUT_DIRECTORY = Build/UefiVarMonitorPkg 7 | SUPPORTED_ARCHITECTURES = X64 8 | BUILD_TARGETS = DEBUG|RELEASE|NOOPT 9 | SKUID_IDENTIFIER = DEFAULT 10 | 11 | [Components] 12 | UefiVarMonitorPkg/Drivers/UefiVarMonitorDxe/UefiVarMonitorDxe.inf 13 | UefiVarMonitorPkg/Drivers/UefiVarMonitorExDxe/UefiVarMonitorExDxe.inf 14 | 15 | [LibraryClasses] 16 | BaseLib|MdePkg/Library/BaseLib/BaseLib.inf 17 | BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf 18 | DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf 19 | DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf 20 | DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf 21 | DxeServicesTableLib|MdePkg/Library/DxeServicesTableLib/DxeServicesTableLib.inf 22 | MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf 23 | PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf 24 | PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf 25 | SynchronizationLib|MdePkg/Library/BaseSynchronizationLib/BaseSynchronizationLib.inf 26 | TimerLib|MdePkg/Library/BaseTimerLibNullTemplate/BaseTimerLibNullTemplate.inf 27 | UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf 28 | UefiCpuLib|UefiCpuPkg/Library/BaseUefiCpuLib/BaseUefiCpuLib.inf 29 | UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf 30 | UefiLib|MdePkg/Library/UefiLib/UefiLib.inf 31 | UefiRuntimeLib|MdePkg/Library/UefiRuntimeLib/UefiRuntimeLib.inf 32 | UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf 33 | !if $(TARGET) == RELEASE 34 | DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf 35 | !else 36 | !ifdef $(DEBUG_ON_SERIAL_PORT) 37 | IoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicSev.inf 38 | SerialPortLib|PcAtChipsetPkg/Library/SerialIoLib/SerialIoLib.inf 39 | DebugLib|MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf 40 | !else 41 | DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf 42 | !endif 43 | !endif 44 | 45 | [PcdsFixedAtBuild] 46 | # Define DEBUG_ERROR | DEBUG_VERBOSE | DEBUG_INFO | DEBUG_WARN to enable 47 | # logging at those levels. Also, define DEBUG_PROPERTY_ASSERT_DEADLOOP_ENABLED 48 | # and such. Assertion failure will call CpuDeadLoop. 49 | # https://github.com/tianocore/tianocore.github.io/wiki/EDK-II-Debugging 50 | gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80400042 51 | gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x2f 52 | -------------------------------------------------------------------------------- /uefi-var-monitor/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "x86_64-unknown-uefi" 3 | rustflags = ["-Z", "pre-link-args=/subsystem:efi_runtime_driver"] 4 | 5 | [unstable] 6 | build-std = ["core", "compiler_builtins", "alloc"] 7 | build-std-features = ["compiler-builtins-mem"] 8 | -------------------------------------------------------------------------------- /uefi-var-monitor/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /uefi-var-monitor/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Rust: cargo xbuild - uefi-var-monitor", 6 | "type": "shell", 7 | "command": "cargo", 8 | "args": [ 9 | "xbuild", 10 | "--target", 11 | "x86_64-unknown-uefi" 12 | ], 13 | "group": { 14 | "kind": "build", 15 | "isDefault": true 16 | }, 17 | "problemMatcher": [ 18 | "$rustc" 19 | ], 20 | "presentation": { 21 | "clear": true 22 | }, 23 | "options": { 24 | "env": { 25 | "RUSTFLAGS": "-Z pre-link-args=/subsystem:efi_runtime_driver" 26 | } 27 | } 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /uefi-var-monitor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uefi-var-monitor" 3 | version = "0.1.1" 4 | authors = ["Satoshi Tanda "] 5 | edition = "2018" 6 | 7 | [features] 8 | default = ["log-serial", "log-panic"] 9 | # Have the log! macro write to serial output. Disabling this significantly 10 | # reduces code size, but makes debugging essentially impossible 11 | log-serial = [] 12 | # Log panics to serial output. Disabling this (without disabling log-serial) 13 | # gets you most of the code size reduction, without losing _all_ debugging. 14 | log-panic = ["log-serial"] 15 | 16 | [dependencies] 17 | r-efi = "3.1.0" 18 | x86_64 = "0.12.2" 19 | atomic_refcell = "0.1.6" 20 | -------------------------------------------------------------------------------- /uefi-var-monitor/OVMF_CODE.fd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tandasat/UefiVarMonitor/8d47580023482c87d65d08213cf17c74308adefb/uefi-var-monitor/OVMF_CODE.fd -------------------------------------------------------------------------------- /uefi-var-monitor/OVMF_VARS.fd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tandasat/UefiVarMonitor/8d47580023482c87d65d08213cf17c74308adefb/uefi-var-monitor/OVMF_VARS.fd -------------------------------------------------------------------------------- /uefi-var-monitor/RunQemu.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | qemu-system-x86_64.exe ^ 3 | -nodefaults ^ 4 | -vga std ^ 5 | -machine q35 ^ 6 | -m 128M ^ 7 | -drive if=pflash,format=raw,readonly,file=OVMF_CODE.fd ^ 8 | -drive if=pflash,format=raw,file=OVMF_VARS.fd ^ 9 | -drive format=raw,file=fat:rw:.\target\x86_64-unknown-uefi ^ 10 | -serial mon:stdio 11 | 12 | :: The former puts serial output to the console of the host. 13 | :: The latter opens the port for telnet. 14 | :: -serial mon:stdio 15 | :: -serial telnet:localhost:12345,server,nowait 16 | -------------------------------------------------------------------------------- /uefi-var-monitor/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use r_efi::efi; 5 | 6 | #[macro_use] 7 | mod serial; 8 | 9 | type GetVariableType = extern "win64" fn( 10 | *mut r_efi::base::Char16, 11 | *mut r_efi::base::Guid, 12 | *mut u32, 13 | *mut usize, 14 | *mut core::ffi::c_void, 15 | ) -> r_efi::base::Status; 16 | 17 | static mut GET_VARIABLE: GetVariableType = handle_get_variable; 18 | 19 | /** 20 | * @brief Handles GetVariable runtime service calls. 21 | */ 22 | extern "win64" fn handle_get_variable( 23 | variable_name: *mut r_efi::base::Char16, 24 | vendor_guid: *mut r_efi::base::Guid, 25 | attributes: *mut u32, 26 | data_size: *mut usize, 27 | data: *mut core::ffi::c_void, 28 | ) -> efi::Status { 29 | // 30 | // Invoke the original GetVariable service, and log this service invocation. 31 | // 32 | let efi_status = 33 | unsafe { GET_VARIABLE(variable_name, vendor_guid, attributes, data_size, data) }; 34 | 35 | // 36 | // Convert to UTF-8 form USC-2 up to 64 characters. 37 | // 38 | let mut name; 39 | let name = unsafe { 40 | let variable_name = core::slice::from_raw_parts(variable_name, 64); 41 | name = core::mem::MaybeUninit::<[u8; 64]>::uninit(); 42 | let name_ptr = name.as_mut_ptr() as *mut u8; 43 | let mut offset = 0; 44 | for c in variable_name 45 | .iter() 46 | .take_while(|&&c| c != 0) 47 | .map(|&c| c as u8) 48 | { 49 | name_ptr.add(offset).write(c); 50 | offset = offset + 1; 51 | } 52 | core::str::from_utf8_unchecked(core::slice::from_raw_parts(name_ptr, offset)) 53 | }; 54 | 55 | let effective_size = if data_size.is_null() { 56 | 0 57 | } else { 58 | unsafe { *data_size } 59 | }; 60 | let data = unsafe { (*vendor_guid).as_fields() }; 61 | log!( 62 | "G: {:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X} Size={:08x} {}: {:#x}", 63 | data.0, 64 | data.1, 65 | data.2, 66 | data.3, 67 | data.4, 68 | data.5[0], 69 | data.5[1], 70 | data.5[2], 71 | data.5[3], 72 | data.5[4], 73 | data.5[5], 74 | effective_size, 75 | name, 76 | efi_status.as_usize(), 77 | ); 78 | 79 | return efi_status; 80 | } 81 | 82 | /** 83 | * @brief Converts global pointers from physical-mode ones to virtual-mode ones. 84 | */ 85 | extern "win64" fn handle_set_virtual_address_map( 86 | _event: r_efi::base::Event, 87 | context: *mut core::ffi::c_void, 88 | ) { 89 | assert!(!context.is_null()); 90 | 91 | let runtime_services = unsafe { &mut *(context as *mut r_efi::efi::RuntimeServices) }; 92 | let curr_addr = unsafe { GET_VARIABLE as u64 }; 93 | let efi_status = (runtime_services.convert_pointer)(0, unsafe { 94 | &mut GET_VARIABLE as *mut _ as *mut *mut core::ffi::c_void 95 | }); 96 | log!( 97 | "GetVariable relocated from {:#08x} to {:#08x}", 98 | curr_addr, 99 | unsafe { GET_VARIABLE as u64 }, 100 | ); 101 | 102 | assert!(!efi_status.is_error()); 103 | } 104 | 105 | /** 106 | * @brief Exchanges a pointer in the EFI System Table. 107 | */ 108 | fn exchange_pointer_in_service_table( 109 | system_table: *mut efi::SystemTable, 110 | address_to_update: *mut *mut core::ffi::c_void, 111 | new_function_pointer: *mut core::ffi::c_void, 112 | original_function_pointer: *mut *mut core::ffi::c_void, 113 | ) -> efi::Status { 114 | unsafe { 115 | assert!(!system_table.is_null()); 116 | assert!(*address_to_update != new_function_pointer); 117 | }; 118 | let system_table = unsafe { &mut *system_table }; 119 | let boot_services = unsafe { &mut *system_table.boot_services }; 120 | 121 | // 122 | // Disable interrupt. 123 | // 124 | let tpl = (boot_services.raise_tpl)(efi::TPL_HIGH_LEVEL); 125 | 126 | unsafe { 127 | *original_function_pointer = *address_to_update; 128 | *address_to_update = new_function_pointer; 129 | }; 130 | 131 | // 132 | // Update the CRC32 in the EFI System Table header. 133 | // 134 | system_table.hdr.crc32 = 0; 135 | let efi_status = (boot_services.calculate_crc32)( 136 | &mut system_table.hdr as *mut _ as *mut core::ffi::c_void, 137 | system_table.hdr.header_size as usize, 138 | &mut system_table.hdr.crc32, 139 | ); 140 | assert!(!efi_status.is_error()); 141 | 142 | (boot_services.restore_tpl)(tpl); 143 | return efi_status; 144 | } 145 | 146 | /** 147 | * @brief The module entry point. 148 | */ 149 | #[no_mangle] 150 | fn efi_main(_image_handle: efi::Handle, system_table: *mut efi::SystemTable) -> efi::Status { 151 | assert!(!system_table.is_null()); 152 | let system_table = unsafe { &mut *system_table }; 153 | 154 | assert!(!system_table.boot_services.is_null()); 155 | let boot_services = unsafe { &mut *system_table.boot_services }; 156 | 157 | log!("Driver being loaded"); 158 | 159 | // 160 | // Register a notification for SetVirtualAddressMap call. 161 | // 162 | let mut event: r_efi::base::Event = core::ptr::null_mut(); 163 | let mut efi_status = (boot_services.create_event_ex)( 164 | r_efi::efi::EVT_NOTIFY_SIGNAL, 165 | r_efi::efi::TPL_CALLBACK, 166 | handle_set_virtual_address_map, 167 | system_table.runtime_services as *mut core::ffi::c_void, 168 | &mut r_efi::efi::EVENT_GROUP_VIRTUAL_ADDRESS_CHANGE, 169 | &mut event, 170 | ); 171 | if efi_status.is_error() { 172 | log!("create_event_ex failed : {:#x}", efi_status.as_usize()); 173 | return efi_status; 174 | } 175 | 176 | // 177 | // Install hooks. 178 | // 179 | efi_status = unsafe { 180 | exchange_pointer_in_service_table( 181 | system_table, 182 | &mut (*system_table.runtime_services).get_variable as *mut _ 183 | as *mut *mut core::ffi::c_void, 184 | handle_get_variable as *mut core::ffi::c_void, 185 | &mut GET_VARIABLE as *mut _ as *mut *mut core::ffi::c_void, 186 | ) 187 | }; 188 | if efi_status.is_error() { 189 | log!( 190 | "exchange_table_pointer failed : {:#x}", 191 | efi_status.as_usize() 192 | ); 193 | (boot_services.close_event)(event); 194 | return efi_status; 195 | } 196 | 197 | return efi_status; 198 | } 199 | 200 | #[panic_handler] 201 | fn panic_handler(_info: &core::panic::PanicInfo) -> ! { 202 | loop {} 203 | } 204 | -------------------------------------------------------------------------------- /uefi-var-monitor/src/serial.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2019 Intel Corporation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Inspired by https://github.com/phil-opp/blog_os/blob/post-03/src/vga_buffer.rs 16 | // from Philipp Oppermann 17 | 18 | use core::fmt; 19 | 20 | use atomic_refcell::AtomicRefCell; 21 | use x86_64::instructions::port::PortWriteOnly; 22 | 23 | // We use COM1 as it is the standard first serial port. 24 | static PORT: AtomicRefCell> = AtomicRefCell::new(PortWriteOnly::new(0x3f8)); 25 | 26 | pub struct Serial; 27 | 28 | impl fmt::Write for Serial { 29 | fn write_str(&mut self, s: &str) -> fmt::Result { 30 | let mut port = PORT.borrow_mut(); 31 | for b in s.bytes() { 32 | unsafe { port.write(b) } 33 | } 34 | Ok(()) 35 | } 36 | } 37 | 38 | #[macro_export] 39 | macro_rules! log { 40 | ($($arg:tt)*) => {{ 41 | use core::fmt::Write; 42 | #[cfg(all(feature = "log-serial", not(test)))] 43 | writeln!(crate::serial::Serial, $($arg)*).unwrap(); 44 | #[cfg(all(feature = "log-serial", test))] 45 | println!($($arg)*); 46 | }}; 47 | } 48 | --------------------------------------------------------------------------------