├── README.md ├── gencert.ps1 ├── old_and_unsupported └── patcher.py └── patcher.ps1 /README.md: -------------------------------------------------------------------------------- 1 | # nvidia-kvm-patcher 2 | 3 | Generic fix to NVIDIA Code 43 on Virtual Machines 4 | 5 | ### Quick Instructions 6 | 7 | 1. Start NVIDIA Driver Setup, Exit Before Installing (Unpacks to C:/NVIDIA) 8 | 2. Install the appropriate WDK/DDK (W10: https://docs.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk) , See OS Support 9 | 3. If on Windows 7, See Windows 7 Workaround 10 | 4. Enable Test Mode (In admin cmd: bcdedit /set testsigning on) and Reboot 11 | 5. Open powershell and run patcher.ps1 C:\NVIDIA\DisplayDriver\372.54\Win10_64\International\Display.Driver 12 | 6. Install Driver Through Extracted Installer (In C:/NVIDIA/DisplayDriver/Version) 13 | 14 | ### Details 15 | 16 | So, the story so far: 17 | You have a VM that uses a passed-through NVIDIA graphics card 18 | 19 | However, the driver errored out with code 43, or outright blue-screened your VM 20 | 21 | This is because NVIDIA "Introduced a Bug" making their driver "Fail" on "Unsupported configurations", such as having a geforce, by "accidentally" detecting the prescence of a hypervisor 22 | 23 | ### Alternate Workaround (recent libvirt + qemu) 24 | ``` 25 | 26 | ... 27 | 28 | ... 29 | 30 | 31 | 32 | ... 33 | 34 | ... 35 | 36 | 37 | ... 38 | 39 | ... 40 | 41 | ``` 42 | 43 | ### Troubleshooting 44 | 45 | 1. If testsigning fails, make sure you are running Windows 10 x64, and have the WDK Installed 46 | 2. Windows will attempt to overwrite the driver using versions from windows update, you will want to blacklists these updates using Microsoft's tool: https://support.microsoft.com/en-us/kb/3073930 47 | 3. Having another problem? File an Issue. I would love some feedback on my crappy script. 48 | 4. Still getting error 43? Ensure the graphics card is using Message Signaled Interrupts before rebooting: http://forums.guru3d.com/showthread.php?t=378044 49 | 5. Still getting error 43 even with MSIs? Some system configurations (hypervisor/hardware) may not be capable of supporting a passed through nvidia card. I have listed some systems that I have personally tested below. 50 | 51 | ### OS Support 52 | 53 | * Windows 7 x64 is supported using the [Windows 7 DDK] (https://www.microsoft.com/en-us/download/details.aspx?id=11800) 54 | * Windows 10 x64 is supported using the [Windows 10 WDK] (https://developer.microsoft.com/en-us/windows/hardware/windows-driver-kit) 55 | 56 | ### Windows 7 Workaround 57 | 58 | For some reason, at least on the test system, signtool in the Windows 7 WDK Pre-Dates The Timestamp (possible reverse timezone compensation???). To get around this, remove all instances of the SKSoftware Certificate using mmc (if you have ran the script before), pre-date your clock by 2 days, and execute gencert.ps1 using powershell. 59 | 60 | ### Tested Working Host Platforms 61 | Tested with a Asus Z170-WS, i7-6700k, and kernel 4.7 62 | * libvirtd 2.3.0 running qemu 2.6.50 using OVMF UEFI, with PCIe ACS Override patch 63 | * xen 4.7 using bios 64 | * Tested with an ASRock Z170 Extreme7+, i7-6700k, GTX 980 Ti, and Windows Server 2016 Standard (v1607 build 14393.0) 65 | * Stock Hyper-V role (Host) running Windows Server 2016 Standard (Guest, same version), Gen2 VM config v8, using Discrete Device Assignment 66 | * Tested with HP Z400, Nvidia GTX 750 TI, Windows server 2016, kvm + BIOS (not UEFI), dumped ROM as per https://pve.proxmox.com/wiki/Pci_passthrough, proxmox 5.2-7, qemu 5.0-32, Nvidia Drivers 372.54 67 | 68 | ### Tested Non-Working Host Platforms 69 | * libvirtd 2.3.0 running qemu 2.6.50 using bios 70 | 71 | -------------------------------------------------------------------------------- /gencert.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | [string]$directory = $pwd 3 | ) 4 | $pwd = $directory 5 | 6 | #Adapted from https://blogs.msdn.microsoft.com/virtual_pc_guy/2010/09/23/a-self-elevating-powershell-script/ 7 | # Get the ID and security principal of the current user account 8 | $myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent() 9 | $myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID) 10 | 11 | # Get the security principal for the Administrator role 12 | $adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator 13 | 14 | # Check to see if we are currently running "as Administrator" 15 | if ($myWindowsPrincipal.IsInRole($adminRole)) 16 | { 17 | # We are running "as Administrator" - so change the title and background color to indicate this 18 | $Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + "(Elevated)" 19 | $Host.UI.RawUI.BackgroundColor = "DarkBlue" 20 | clear-host 21 | } 22 | else 23 | { 24 | # We are not running "as Administrator" - so relaunch as administrator 25 | 26 | # Create a new process object that starts PowerShell 27 | $newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell"; 28 | 29 | # Specify the current script path and name as a parameter 30 | [string[]]$argList = @('-ExecutionPolicy', 'Unrestricted') 31 | $argList += $myInvocation.MyCommand.Definition 32 | $argList += @('-directory', $pwd) 33 | $newProcess.Arguments = $argList 34 | 35 | # Indicate that the process should be elevated 36 | $newProcess.Verb = "runas"; 37 | 38 | # Start the new process 39 | $process = [System.Diagnostics.Process]::Start($newProcess); 40 | $process.WaitForExit() 41 | 42 | # Exit from the current, unelevated, process 43 | exit 44 | } 45 | 46 | $CertFile = Join-Path -Path $pwd -ChildPath "TestSign.cer" 47 | Write-Host "Certificate Path: " $CertFile 48 | 49 | #Check For Cert in PrivateCertStore and move it to My if it exists (compat with initial commit) 50 | $Cert = Get-ChildItem Cert:\CurrentUser\PrivateCertStore | Where {$_.subject -eq 'CN=SKSoftware'} 51 | if($Cert -ne $null) 52 | { 53 | Write-Host "Moving Cert in PrivateCertStore to My" 54 | $thumb = $Cert.Thumbprint 55 | Move-Item "Cert:\CurrentUser\PrivateCertStore\$thumb" "Cert:\CurrentUser\My\" 56 | } 57 | 58 | #Check For Existence of Certificate In Root 59 | $Certs = @(Get-ChildItem cert:\LocalMachine\Root | Where {$_.subject -eq 'CN=SKSoftware'}) 60 | if ($Certs.length -eq 0) { 61 | Write-Host "Cert Not Found in Root Store" 62 | 63 | #Not in Root, Check User 64 | $Certs = @(Get-ChildItem -recurse cert:\CurrentUser\ | Where {$_.subject -eq 'CN=SKSoftware'}) 65 | if ($Certs.length -eq 0) { 66 | #No Certificate, Create a New One 67 | Write-Host "Certificate not Found in User Store, Creating" 68 | if (Get-Command New-SelfSignedCertificate -errorAction SilentlyContinue) 69 | { 70 | $Cert = New-SelfSignedCertificate -Type CodeSigningCert -Subject CN=SKSoftware -CertStoreLocation "Cert:\CurrentUser\My" 71 | } 72 | else 73 | { 74 | Write-Host "New-SelfSignedCertificate Not Available, Falling Back To Makecert" 75 | 76 | $makecert = 'C:/WinDDK/7600.16385.1/bin/amd64/makecert.exe' 77 | if(-Not(Test-Path $makecert)) 78 | { 79 | Write-Host "[!] Failure: Unable to find $makecert" 80 | exit 81 | } 82 | 83 | & $makecert -r -pe -ss MY -n CN=SKSoftware -eku 1.3.6.1.5.5.7.3.3 $CertFile 84 | 85 | } 86 | } 87 | else 88 | { 89 | Write-Host "Certificate Found in User Store" 90 | $Cert = $Certs[0] 91 | } 92 | 93 | if (Get-Command Export-Certificate) 94 | { 95 | $output = Export-Certificate -Cert $Cert -FilePath $CertFile -Type CERT 96 | } 97 | 98 | Write-Host "Adding Certificate to Root Store" 99 | $pfx = new-object System.Security.Cryptography.X509Certificates.X509Certificate2 100 | $pfx.import($CertFile) 101 | $store = new-object System.Security.Cryptography.X509Certificates.X509Store( 102 | [System.Security.Cryptography.X509Certificates.StoreName]::Root, 103 | "localmachine" 104 | ) 105 | $store.open("MaxAllowed") 106 | $store.add($pfx) 107 | $store.close() 108 | 109 | } 110 | else 111 | { 112 | Write-Host "Certificate Found In Root Store" 113 | $Cert = $Certs[0] 114 | } 115 | 116 | if (Get-Command Export-Certificate -errorAction SilentlyContinue) 117 | { 118 | $output = Export-Certificate -Cert $Cert -FilePath $CertFile -Type CERT 119 | } -------------------------------------------------------------------------------- /old_and_unsupported/patcher.py: -------------------------------------------------------------------------------- 1 | # File: patcher.py 2 | # Author: SK1080 3 | # Date: 6/29/2016 4 | # License: Do whatever the fuck you want with this source code, at your own risk 5 | 6 | # Note: This Script is Fugly 7 | # Note: This Script Requires Python 3, Because You should be using it Anyway 8 | # Note: This Python Script is really a glorified Batch File 9 | # Note: Only Tested on versions 361.91 and 368.39 10 | # Note: Targeted at Windows 10 X64, Fix Tool Paths for Testsigning and /os: option in Inf2Cat to support other OS 11 | # Note: The test signing Script is BS and probably won't work on your system. 12 | 13 | import sys 14 | import subprocess 15 | import os 16 | 17 | # TODO: Wildcard Search 18 | PATCHES = { 19 | "41FF978804000085C0": "31C0909090909085C0", # 361.91 - 368.39 20 | "41FF97B804000085C0": "31C0909090909085C0", # 372.54 21 | } 22 | 23 | 24 | def testsign_failed(): 25 | print('[!] Testsigning Failed, Please Test Sign Driver Manually or Load via disabling enforcement') 26 | sys.exit(0) 27 | 28 | 29 | if len(sys.argv) != 2: 30 | print('Usage: patcher.py [/path/to/Display.Driver/Folder]') 31 | sys.exit(-1) 32 | 33 | print('[+] Unpacking nvlddmkm.sys') 34 | 35 | driver_path = sys.argv[1] 36 | 37 | sys_szdd_path = driver_path + '/nvlddmkm.sy_' 38 | sys_path = driver_path + '/nvlddmkm.sys' 39 | 40 | if not os.path.isfile(sys_szdd_path): 41 | print('Failed to find nvlddmkm.sy_') 42 | sys.exit(-1) 43 | 44 | subprocess.call(["expand", sys_szdd_path, sys_path]) 45 | 46 | if not os.path.isfile(sys_path): 47 | print('Failed to expand nvlddmkm.sy_') 48 | sys.exit(-1) 49 | 50 | #os.rename(sys_szdd_path, sys_szdd_path + ".orig") 51 | 52 | driver_data = open(sys_path, 'rb').read() 53 | 54 | print('[+] Patching KVM CPUID Check and unintentionally killing Vendor ID Check [FU NVIDIA]') 55 | for pattern, patch in PATCHES.items(): 56 | pattern = bytes.fromhex(pattern) 57 | patch = bytes.fromhex(patch) 58 | driver_data = driver_data.replace(pattern, patch) 59 | 60 | print('[+] Writing Patched Driver') 61 | open(sys_path, 'wb').write(driver_data) 62 | 63 | print ('[+] Modifying Installer Config to Use Extracted Driver') 64 | installer_config_path = driver_path + '/DisplayDriver.nvi' 65 | data = open(installer_config_path, 'rb').read() 66 | data = data.replace(b'nvlddmkm.sy_', b'nvlddmkm.sys') 67 | open(installer_config_path, 'wb').write(data) 68 | 69 | print('[+] Attempting to Test Sign Driver') 70 | 71 | print(' [+] Generating Security Catalog') 72 | inf2cat = 'C:/Program Files (x86)/Windows Kits/10/bin/x86/Inf2Cat.exe' 73 | if not os.path.isfile(inf2cat): 74 | print('[!] Failed to find Inf2Cat, Please Install Windows 10 WDK or Correct Path in Script') 75 | testsign_failed() 76 | 77 | subprocess.call([inf2cat, '/driver:' + driver_path, '/os:10_X64']) 78 | 79 | print(' [+] Generating and Installing TestSign Certificate') 80 | subprocess.call(["C:\\WINDOWS\\system32\\WindowsPowerShell\\v1.0\\powershell.exe", 81 | "-ExecutionPolicy", "Unrestricted", 82 | ". ./gencert.ps1", "-directory", os.path.dirname(os.path.realpath(__file__))], 83 | stdout=sys.stdout) 84 | 85 | 86 | print(' [+] Signing Catalog File') 87 | signtool = 'C:/Program Files (x86)/Windows Kits/10/Tools/bin/i386/signtool.exe' 88 | if not os.path.isfile(signtool): 89 | print('[!] Failed to find signtool, Please Install Windows 10 WDK or Correct Path in Script') 90 | testsign_failed() 91 | 92 | catalog_path = driver_path + './nv_disp.cat' 93 | subprocess.call([signtool, 'sign', '/v', '/n', 'SKSoftware', '/t', 'http://timestamp.verisign.com/scripts/timstamp.dll', catalog_path]) -------------------------------------------------------------------------------- /patcher.ps1: -------------------------------------------------------------------------------- 1 | # Nvidia KVM Patcher 2 | # SK1080 3 | # License: Do whatever you want with this code 4 | # Pardon my poor powershell skills 5 | 6 | # Note: Only Tested on Versions 361.91, 368.39, and 372.54 7 | # Note: Targeted at Windows 10 X64, Fix Tool Paths for Testsigning and /os: option in Inf2Cat to support other OS 8 | # Note: Testsigning Requires Windows 10 WDK 9 | 10 | param ( 11 | [Parameter(Position=1)][string]$directory = "" 12 | ) 13 | 14 | if($directory -eq "") 15 | { 16 | Write-Host "Usage: patcher.ps1 [/path/to/Display.Driver]" 17 | exit 18 | } 19 | 20 | $sys_packed = Join-Path $directory "nvlddmkm.sy_" 21 | $sys_packed_backup = Join-Path $directory "nvlddmkm.sy_.bak" 22 | if(Test-Path $sys_packed) 23 | { 24 | Move-Item $sys_packed $sys_packed_backup 25 | } 26 | $sys_packed = $sys_packed_backup 27 | 28 | $sys_unpacked = Join-Path $directory "nvlddmkm.sys" 29 | 30 | if(-Not(Test-Path $sys_packed)) 31 | { 32 | Write-Host "[!] Failure: Unable to find nvlddmkm.sy_ in $directory" 33 | exit 34 | } 35 | 36 | Write-Host '[+] Unpacking nvlddmkm.sys' 37 | iex "expand $sys_packed $sys_unpacked" | Out-Null 38 | 39 | if(-Not(Test-Path $sys_unpacked)) 40 | { 41 | Write-Host "[!] Failure: Unable to unpack nvlddmkm.sys" 42 | exit 43 | } 44 | 45 | Write-Host '[+] Patching CPUID Check' 46 | #Stupidity Incomming 47 | $stream = New-Object IO.FileStream -ArgumentList $sys_unpacked, 'Open', 'Read' 48 | $enc = [Text.Encoding]::GetEncoding(28591) 49 | $sr = New-Object IO.StreamReader -ArgumentList $stream, $enc 50 | $text = $sr.ReadToEnd() 51 | $sr.close() 52 | $stream.close() 53 | 54 | $pattern = '\xBB\x00\x00\x00\x40\x48\x8D\x44\x24\x50' # + 0x30 55 | $patch = [Byte[]]@(0x31, 0xC0, 0x90, 0x90, 0x90, 0x90, 0x90, 0x85, 0xC0) 56 | 57 | $regex = [Regex]$pattern 58 | $matches = $regex.Matches($text) 59 | $count = $matches.Count 60 | if($count -ne 1) 61 | { 62 | Write-Host "[!] Failure: $count occurrences of patch pattern found" 63 | exit 64 | } 65 | 66 | $offset = $matches[0].Index + 0x30 67 | 68 | $stream = New-Object IO.FileStream -ArgumentList $sys_unpacked, 'Open', 'Write' 69 | [void] $stream.Seek($offset, 'Begin') 70 | $stream.Write($patch, 0, $patch.Length) 71 | 72 | $stream.Close() 73 | 74 | Write-Host '[+] Modifying Installer Config to Use Extracted Driver' 75 | $installer_config = Join-Path $directory "DisplayDriver.nvi" 76 | if(-Not(Test-Path $installer_config)) 77 | { 78 | Write-Host "[!] Failure: Unable to find $installer_config" 79 | exit 80 | } 81 | 82 | (Get-Content $installer_config) -replace 'nvlddmkm.sy_', 'nvlddmkm.sys' | Set-Content $installer_config 83 | 84 | Write-Host '[+] Attempting to Test Sign Driver' 85 | 86 | $inf2cat = '' 87 | $signtool = '' 88 | 89 | $inf2cat_paths = 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.17134.0\x86\Inf2Cat.exe', 'C:/WinDDK/7600.16385.1/bin/selfsign/Inf2Cat.exe' 90 | $signtool_paths = 'C:/Program Files (x86)/Windows Kits/10/Tools/bin/i386/signtool.exe', 'C:/WinDDK/7600.16385.1/bin/amd64/signtool.exe' 91 | 92 | foreach($path in $inf2cat_paths) 93 | { 94 | if(Test-Path $path) 95 | { 96 | $inf2cat = $path 97 | break 98 | } 99 | } 100 | 101 | foreach($path in $signtool_paths) 102 | { 103 | if(Test-Path $path) 104 | { 105 | $signtool = $path 106 | break 107 | } 108 | } 109 | 110 | if(-Not(Test-Path $inf2cat)) 111 | { 112 | Write-Host "[!] Failure: Unable to locate inf2cat, see/edit script for expected paths" 113 | exit 114 | } 115 | if(-Not(Test-Path $signtool)) 116 | { 117 | Write-Host "[!] Failure: Unable to locate signtool, see/edit script for expected paths" 118 | exit 119 | } 120 | 121 | $ostype = '' 122 | $winver = [System.Environment]::OSVersion.Version 123 | if($winver.Major -eq 6) 124 | { 125 | if($winver.Minor -eq 1) 126 | { 127 | $ostype = '7_X64' 128 | } 129 | elseif($winver.Minor -eq 2 -Or $winver.Minor -eq 3) 130 | { 131 | $ostype = '8_X64' 132 | } 133 | } 134 | if($winver.Major -eq 10) 135 | { 136 | $ostype = '10_X64' 137 | } 138 | if($ostype -eq '') 139 | { 140 | Write-Host "[!] Failure: Unable to determine OS type, see script" 141 | exit 142 | } 143 | Write-Host " [+] Detected OS Type: $ostype" 144 | 145 | Write-Host ' [+] Generating Catalog File (this may take a while)' 146 | & $inf2cat /driver:`"$directory`" /os:$ostype | Out-Null 147 | 148 | Write-Host ' [+] Generating and Installing Certificate' 149 | $ScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Definition 150 | & (Join-Path $ScriptPath "gencert.ps1") -directory $ScriptPath | Out-Null 151 | 152 | Write-Host ' [+] Signing Catalog File' 153 | 154 | $catalog_path = $directory + './nv_disp.cat' 155 | & $signtool sign /v /n SKSoftware /t http://timestamp.verisign.com/scripts/timstamp.dll $catalog_path 156 | & $signtool sign /v /n SKSOftware /t http://timestamp.verisign.com/scripts/timstamp.dll $sys_unpacked --------------------------------------------------------------------------------