Geoff Chappell - Software Analyst
A signed System Integrity policy file can have the side-effect that recent releases of Windows 10 allow self-signed kernel-mode drivers. Security is assured because the policy file must be signed with the Secure Boot Platform Key. The feature is active only in specially licensed editions, except for a contrived case that this paper demonstrates.
This paper is planned to have three parts: context; theory; demonstration. Only the first and last are yet published.
Imagine you have bought a computer and installed Windows on to it. You figure that computers have the general purpose of running whatever programs you want and you set about writing a program of your own. After some amount of debugging and testing, you’re satisfied that your code meets all the technical specifications for a Windows executable in general and for the particular Windows API functions that you call. This is a program you’re ready to start using for real, but first you must send it to Microsoft for a Microsoft signature that Windows on your computer will recognise as Microsoft’s permission for your program to run on your computer. Wait, you ask, what’s this about needing a Microsoft signature for my own code on my own computer? When did permission for this become Microsoft’s to give? How is running my own code on my own computer anyone’s business but my own?
Hold that thought for now. I will get back to how what you do just with your computer can be the reasonable interest of others and especially of Microsoft. First, though, I had better allay any slight panic you may have picked up. For Windows on computers, if not when locked to tablets and phones, what I just described for user-mode software is for now, for the most part, just a dystopian future that may never happen.
For kernel-mode Windows drivers, by contrast, this future has been the present for a while. As far back as 64-bit Windows Vista, you could run your own driver on your own computer without getting it signed by Microsoft, but you did have to involve Microsoft indirectly. You had to sign your driver with a so-called Software Publisher Certificate (SPC) from a third-party certification authority (CA) in combination with a publicly available cross-certificate that Microsoft issued to the CA so that Windows on your computers could know the CA had Microsoft’s permission. Less far back, Microsoft announced in April 2015 that this relatively easy driver-signing was to stop. Among the Driver Signing changes in Windows 10 would be that “all new Windows 10 kernel mode drivers must be submitted to and digitally signed by the Windows Hardware Developer Center Dashboard portal.” And perhaps out of caution that its readers who are programmers might not immediately register the change’s impact on them, it continued with a re-expression: “Windows 10 will not load new kernel mode drivers which are not signed by the portal.” After Windows 10 was formally released in July 2015, something seems to have gone wrong somehow, such that enforcement was relaxed for the 1511 release later that year. How much the inevitable confusion was settled by Microsoft’s assertion in a new announcement, of Driver Signing Changes in Windows 10, version 1607, that the original changes “remained only a policy statement” is doubtful, but enforcement was eventually restored and is plainly here to stay. If you write a kernel-mode driver and you want it load on Windows 10 in its 1607 release or later in arbitrary configurations and especially if Secure Boot is enabled, then you pretty much must send it to Microsoft to get a Microsoft signature—yes, even to load just on your own computers.
Now, please don’t take me as objecting to the very idea of needing Microsoft’s permission. If you write a kernel-mode driver to run on other people’s computers—and by far the greatest number are written for such distribution—then I think it’s not the slightest bit unreasonable if Windows on those other computers refuses to run your driver unless it comes with a badge of approval from some authority whose recognition by Windows is secure from being tampered with. A natural such authority is Microsoft and the natural recognition is to verify digital signatures on drivers against public keys that are, for instance, hard-coded into Windows executables and/or secured by a chain of trust that reaches back to the firmware.
Such protection is important because kernel-mode drivers are extremely powerful. In kernel mode, you are not just at another level, higher even than running on the SYSTEM account. It’s more like you are beyond levels, because your execution is not constrained at all by privileges or permissions. In kernel mode, you can see everything and change pretty much everything, including much of what user-mode software sees of the system. Such power is inevitably attractive, even irresistably so, and sadly not just to good programmers. The history of Windows is littered not just with kernel-mode malware but with commercially distributed kernel-mode code that is defective beyond any reasonable allowance for careless error in hand-crafted work. Indeed, some—by anti-virus vendors, no less—might better be regarded as abusive.
Thus have I got you back to that thought that I asked you to hold. It’s all very well if you say you want your kernel-mode driver to execute only on your own computer, which I mean as generalising to all the computers in your possibly very large enterprise, but how do I, Microsoft and the rest of the world know it will stay just on your computer? You could be a good person, with good and honest intent, and even be a good programmer. But it could also be that you are up to no good and your driver is malware. Perhaps you exploit some security vulnerability to gain administrative access to our computers and you install your driver without our even noticing (until the damage is done). Perhaps worse, you might induce us to run some program as an administrator, e.g., by persuading us that your software is a security tool for our benefit, and your program then installs your driver even though we didn’t mean for you to do any such thing. Either way, we naturally look to Microsoft to protect us from your being able to force your potentially unsafe or even mischievous driver onto our computers.
But this reasonable interest that I’ve just argued Microsoft has in what you run on your own computer isn’t obviously what Microsoft has acted from. If you’re genuine about running your driver only on your own computer, then from your perspective Microsoft’s particular method of protection is absurd overkill: you must get Microsoft’s signature so that other people can know that loading your driver has Microsoft’s blessing even though you don’t want that they can load it at all. From our perspective, too, what Microsoft has done is a good bit less than ideal. Even if you felt pressed enough to have got Microsoft’s signature, we would all be better served for our Windows security if Microsoft would leave your driver as some untrusted thing that Windows on our computers won’t load. Why has Microsoft not arranged for this?
One reason might be that the technology doesn’t exist, or would be too much trouble or too costly to implement, for having Windows on your computer know that your driver is indigenous and can be allowed to load but Windows on our computers can know that your driver is foreign and must be refused. If this were so, then I for one would be firmly with Microsoft for its solution of requiring ever stronger vetting. I could easily enough find other points to complain about driver-signing, e.g., that because Microsoft’s framework for driver certification isn’t even half-way through its evolution from testing hardware for compatibility with Windows, it has the curious effect of stifling innovation for some types of drivers while giving Microsoft’s signature far too readily to some others. But such debates can all be left for another time because suitable technology obviously does exist and can’t credibly be dismissed as too much trouble—well, not by Microsoft.
The technology’s called Secure Boot. The reason that Microsoft can’t credibly dismiss it is that Microsoft contributed substantially to its development and Microsoft’s marketing power was instrumental to its wide deployment. For a computer to get a Windows 10 logo—and how many computer manufacturers could stay in business without that?—it must not only implement Secure Boot in its firmware but have it set up with the Windows boot manager pre-approved for execution and with Microsoft pre-approved for subsequent reconfiguration of what else the firmware will and won’t execute.
At the heart of what makes Secure Boot secure is the Platform Key. This is a public key, typically in a certificate, that is enrolled with the firmware and thereafter acts as a root of trust. It can be read freely but can be written only from a firmware menu or if authenticated by a demonstrated ability to sign with it. Unless you leave the matching private key lying around for malware to abuse—the technological equivalent of leaving a key in a lock—the Platform Key is for all practical purposes a secure indication of your ownership of the computer. Signing anything with the Platform Key, including to issue certificates and to sign with those, is an expression of ownership that you might think should trump all other expressions of authority over your own computers. For instance, you might sign a kernel-mode driver with your Platform Key and not unreasonably think that Windows would then have the means of allowing your driver when it runs on your computers and denying it on everyone else’s computers. If only Windows would notice!
Of course Windows does notice! That Windows 10 requires Microsoft signatures on your own kernel-mode drivers just to load them only on your own computers is one way to protect other people’s computers but it certainly isn’t required just for the security of other people’s computers. Not only does Secure Boot provide Windows with the means in theory to distinguish that your driver is being loaded on your own computers rather than on someone else’s, but Windows 10 puts theory to practice—indeed, not just to the simplest possible practice but with elaboration. All releases of Windows 10 recognise when one sort or another of policy file is signed with the Platform Key and thus expresses the wishes of the computer’s owner, and among the expressions that Windows can recognise in these policy files is to specify non-Microsoft signatures for Windows to accept for kernel-mode drivers.
Since this runs so much against Microsoft’s own public descriptions of Microsoft’s driver-signing requirements for Windows 10, let’s have it again but with emphasis. All releases of Windows 10 have code for allowing your self-signed drivers to load on your computers in real-world configurations while protecting others from your drivers and you from theirs, but for the implementation in recent releases of Windows 10, Microsoft doesn’t license you to use this code.
To say it this way is perhaps to put words in Microsoft’s mouth, especially if you interpret me as saying why Microsoft has done it. Please be sure to understand that although I do mean to say what Microsoft has done and how Microsoft has done it, I can’t know why Microsoft has done it. I can’t even know if Microsoft’s own staff know why Microsoft has done it. I say that custom kernel-mode driver signing is a licensing issue because that’s how Microsoft’s programmers evidently thought of it when they wrote the relevant code. Whether the kernel-mode Code Integrity module (CI.DLL) allows a driver to have a custom root certificate in its signature instead of being required to have one or another well-known Microsoft root is retrieved from the registry by calling a function named ZwQueryLicenseValue from an internal procedure which Microsoft’s published symbol files name CipCheckLicensing. The boot loader (WINLOAD.EFI) decides its use of the feature a little differently. It reads from a registry value named Licensed. Its routine for making the assessment is named OslpCheckLicensedCodeIntegrityOptions. If this is not about licensing, then some manager failed badly at getting the message through to the programmers.
But is it wholly a question of licensing? The incontestable answer would come from naming an edition that is suitably licensed and giving directions for configuring it so that it loads a self-signed driver. But I don’t know for certain even of one edition that allows this. I can tell you it will be distinguished because the license value CodeIntegrity-AllowConfigurablePolicy-CustomKernelSigners will be non-zero. Searching the Internet for this gets a few matches, including two reports that this license value is 1 in something named EnterpriseG. Microsoft added PRODUCT_ENTERPRISEG to its published header files for Windows programming in advance of the 1703 release. Plausibly it’s the subject of Microsoft’s Announcing Windows 10 China Government Edition and the new Surface Pro on 23rd May 2017. Who’s to know? I have not yet found how to get a copy for inspection, certainly not just from a Visual Studio subscription, but perhaps I just have to look harder or smarter. There is, after all, some incentive: from what little Microsoft has described of this edition’s features, it sounds like the sort of Windows that many ordinary Windows users the world over would prefer, if only for being free of telemetry and other irritants. But if it has this particular feature of being configurable to load self-signed drivers under the protection of Secure Boot, I don’t expect Microsoft to say. Indeed, I know of nowhere that Microsoft says even obliquely that any of the numerous differently licensed editions of Windows 10 will load drivers that do not have at least a cross-signature except by being run in some Test Mode or under a debugger.
After so many words to allege that something’s going on in Windows but has escaped the attention of apparently everyone but me, you should want to see very soon that I can back up what I say and not much less soon that you can reproduce my evidence for your closer examination. Fortunately for my keeping your attention in this industry that likes to see a demonstrated proof of concept above all else, it turns out that I can do better than ask you to trust a wordy description of complicated Windows code that you can’t execute and can’t meaningfully inspect unless you’re at least a competent reverse engineer. Because the equivalent of CI’s licensing check is not solidly enforced in the earlier, simpler execution conditions of WINLOAD, it can be side-stepped for drivers that are loaded by WINLOAD rather than by the kernel. We can then glimpse experimentally what we could otherwise only deduce theoretically. The following snapshot shows a little exploration of a Windows 10 Professional installation on which I’ve loaded a small driver named selfsign.sys. Click on the picture if you’re interested enough to want it full-size and hi-fi:
The Command Prompt in the foreground shows first that we really are in Windows 10, specifically in its 1709 release. Next the sc query command confirms that the Service Control Manager knows that a service named SelfSign is in fact a kernel driver and that it’s running. Finally, a selfsign command, written as a companion to the driver, confirms that the driver is not just running but is functional (albeit for what little is expected of it).
The administrative Command Prompt on the right runs the PowerShell Confirm-SecureBootUEFI command to see that Secure Boot is enabled. This would be completely unremarkable if the driver has a Microsoft signature. It would be only slightly less unremarkable if the driver is cross-signed and has somehow sneaked through some semi-documented compatibility provision for upgraded systems. To confirm that neither case applies, the sc qc command has been run to show where the driver exists as a file and then the background shows the Properties for that file as found in the File Explorer.
The Digital Signatures tab of the “selfsign.sys Properties” shows that the driver has two signatures. That it has two isn’t relevant to the problem. It’s just what’s needed these days for a driver to load on multiple Windows versions. A primary signature uses SHA-1 for hashing so that the driver can load on older Windows versions that don’t recognise any stronger hash. A nested signature uses SHA-256 for newer Windows versions. The remaining dialogs in the background show the Digital Signature Details for this nested signature and then its Certification Path. See that all the certificates are My Own this and My Own that, because that’s how I named them when I made them myself (well, with Microsoft’s makecert tool).
The picture is not a mock-up. Stick with me and you will be able to produce it yourself. I haven't patched any Windows code as my way of getting Windows to do something it otherwise never would. I haven’t broken Secure Boot and neither would I want to. I’m not giving malware writers or even the many writers of rude commercial software some new technique for getting a driver loaded onto their targets’ computers. I can get my driver loaded onto my computer because I have configured Secure Boot to know that I own my computer and because Windows actually does have code for noticing. Because I cannot configure Secure Boot on your computer I cannot get my driver loaded onto your computer. There is no security issue here.
But there is more than a little contrivance. What I’ve done to produce this picture is certainly not a general solution for real-world use. It’s not even evidence, just experimental confirmation. If my understanding of the relevant Windows code is correct, then I predict this demonstration will work: and, look, it does! Moreover, as you will see soon enough if you stick with me through all the ingredients and steps, it’s not a demonstration that anyone’s likely just to stumble into—or even to see as something to try except if you already know it will work and why.
Indeed, the demonstration would be difficult even with a licensed edition. It arguably should be no small thing to set up your Windows for self-signed drivers. But the licensing is extra trouble. To get the picture without contrivance, you would need a license upgrade from Microsoft. The license data that would have to be upgraded is protected in various ways from being tampered with, and I certainly do not mean for anyone to try changing it, even for testing. If you want that this should work for you without contrivance, then pester Microsoft for an upgrade of the license data or at least for credible, detailed reasoning of why signing your own drivers for execution on your own computer is something you need to be specially licensed for.
Still, even with contrivance, how can it be that I—or you after following my directions if you want to test what I say—can get anywhere near to producing such a picture? After all, for what looks to be Microsoft’s definitive statement of Driver Signing Policy Microsoft states plainly that “Starting with Windows 10, version 1607, Windows will not load any new kernel mode drivers which are not signed by the Dev Portal.” Just as plainly, my driver is loaded without having any signature from Microsoft. The same page also presents some exceptions, but only for cross-signed drivers, which mine can’t be unless Microsoft has been so carefree as to issue cross-certificates for My Own signatures. What my driver is signed with is what the similar page Kernel-Mode Code Signing Requirements calls an in-house test certificate. It shouldn’t load on any 64-bit Windows all the way back to Windows Vista except if Windows is started with some such boot option as testsigning or is run under a kernel-mode debugger, both of which are disabled by Secure Boot. By any reckoning from anything that Microsoft documents about kernel-mode driver signing, the picture is impossible.
Put another way, if the picture’s not faked, then its mere existence shows that what Microsoft says about kernel-mode driver signing is not entirely truthful. That doesn’t mean that Microsoft must know its documentation is not the whole truth. The obvious alternative explanation is that I may have exploited a previously unknown coding error and thus exposed a security flaw, perhaps even a huge one, and am being reckless by not helping Microsoft to gloss over it via their Coordinated Disclosure programme. Yet I have said already, and will say again and again, that there is no security issue here. What, really, is the truth?
[The question is meant to introduce what must inevitably be a lengthy explanation of the many obscure, if not actually difficult, points of theory that explain why the demonstration is expected to work. Just as inevitable is that the theory will take time to write up. Meanwhile, I am tired, I have other work to do, and I recognise that most readers would rather have the demonstration first.]
Even without the need to work around Microsoft’s licensing constraints, configuring Microsoft’s support for custom kernel-mode driver signing would not be straightforward. Depending on how you have already regarded what Secure Boot means for the security of your computers, you may already be well set up for it. More likely, though, you are not.
Just for basic applicability, you will need administrative access to a computer that has Secure Boot enabled and which runs Windows 10 in at least its 1703 release.
How much later a release this demonstration continues to apply to depends on when Microsoft rethinks what Microsoft allows and how. Microsoft could rework the custom driver signing feature so completely that this demonstration becomes nothing but a historical curiosity. Even if Microsoft retains the feature’s present architecture, a future release could defeat the contrivance so that no demonstration is possible without patching Microsoft’s code (which I might entertain to make the point) or cracking the license (which I do not want even to investigate). The best outcome, of course, is that a future release removes the licensing constraint and thus allows the demonstration to be simplified because contrivance becomes unnecessary. We can all dream!
The first prerequisite beyond the basics is that your computer must have as its Platform Key a certificate that gives you some signing authority. You have the obvious such authority if you can sign with the Platform Key itself. Less obvious, but very welcome, is that it’s enough if you can sign with some derived certificate, i.e., one whose chain of issuers includes the Platform Key on the way towards the root certificate.
Beware, though, that a Platform Key can satisfy Secure Boot and suffice for booting Windows but not be regarded by Windows as suitable for enabling the custom driver signing feature. One additional requirement is plainly by design. Whether it’s the Platform Key itself that you sign with or some derived certificate, your signature must meet modern notions of strength: no certificate in the chain can have a signature algorithm that is weaker than SHA-256. Perhaps not by design, either the Platform Key must be a root certificate or its immediate issuer must be. (The cause is that the relevant code checks for the Platform Key only when its examination of the signature reaches the end of the certificate chain, not along the way. But who’s to know whether this is the intended coding or an oversight?)
If only for now, I do not plan to help you here with how to create a suitable certificate or enrol it as the Platform Key. Indeed, if you did not immediately grasp that changing the Platform Key must include preserving (or appending to) other Secure Boot variables, then I strongly suggest you do not proceed with this demonstration until you have acquired from elsewhere a better knowledge both of Secure Boot itself and of how Microsoft uses it to secure Windows.
For the signing, you will need a tool since none comes with Windows as standard. Though I will use SIGNTOOL.EXE such as Microsoft supplies with the Software Development Kit (SDK) and Windows Driver Kit (WDK), you can use any that provides for specifying the type of content. For SIGNTOOL, this means you need its -p7co switch, as supported by versions from the kits for Windows 8 and higher.
Note that these first prerequisites are architectural. They are what you would need even if you had Microsoft’s license and they will still be needed if a future release retains the feature’s present architecture but no longer seeks to require a license. Your assertion of ownership via the Platform Key is what makes Microsoft’s support for custom kernel-mode signers secure. You can run your self-signed driver on your computer because Windows recognises your assertion of ownership. You can’t force the custom kernel-mode signing feature, let alone your particular driver, onto any computer that has Secure Boot enabled but with a Platform Key that you can’t sign with. And nobody can force either the feature or their driver onto your computer unless you’re careless with the private part of your Platform Key.
Note also that I wave my hands over what security this ideal future would make available when Secure Boot is not enabled. Less, obviously! Yet also more than you might expect. Subject to the same licensing constraint, the custom driver signing feature can be enabled even if Secure Boot is disabled. Even without the license, this demonstration can be varied to work without Secure Boot to show 64-bit Windows load a self-signed driver even though it’s supposed to require at least cross-signed drivers. But if Secure Boot is disabled, then with or without the license, the feature does not have the authority of the Platform Key and although I am convinced that it can be configured securely, I prefer for now to leave it alone.
A second prerequisite is that your computer’s boot configuration must allow for booting Windows from an alternate source. Most obvious, and henceforth assumed, is that your computer retains a recovery option that boots Windows on a RAM disk loaded from a recovery partition. For a quick reminder of whether Windows still knows how to boot this way, run bcdedit /enum osloader and look for a Windows Boot Loader entry whose description is something like Windows Recovery Environment.
Note that this second prerequisite is only for our contrivance while Microsoft means to require a license that no ordinary Windows users are known to have. If instead you work for some entity whose relations with Microsoft are so special that you have the license, then whether you think this circumstance is your blessing or your curse, this second prerequisite does not apply to you and you can skip all of this demonstration’s contrivances: if you control the Platform Key of a computer you work at, then custom driver signing is yours to set up right now.
To demonstrate a feature that lets you load a self-signed driver, you will of course need a self-signed driver to test with. To enable the feature, you’ll need something that I shall persist in calling a System Integrity policy file. System Integrity appears to be the original name, but Microsoft also talks variously of Code Integrity policies, Device Guard policies, and even Windows Defender Application Control (WDAC) policies. All appear to name the same thing, though the last, despite being Microsoft’s current preference, is conspicuously unhelpful for disguising its applicability to device drivers.
Though you need a driver and a policy file, you do not need any particular driver or policy file. Download mine to start with, but please remember that getting you started really is all that I mean them for. Change to your own the moment you’re ready to develop the demonstration nearer to what might be real-world practice were your use of the feature ever to be licensed or to need a license no longer.
I provide you with a very simple driver that I have signed with a certificate chain of my own which you should, of course, not trust:
Feel free to re-sign my driver with any certificate of your own. Feel free to change to a completely different driver. That you should be able to load your own driver on your own computer without a signature from anyone else is most of the point. Of course, to write your own driver you will need a Windows Driver Kit (WDK), which is easy to obtain, and a lot of specialised knowledge which is not.
For simplicity, I henceforth leave this option of replacement as understood: my further description of the demonstration assumes that you use my driver. It is named selfsign.sys and comes with a simple console application, named selfsign.exe, for testing that the driver works. The whole of the driver’s work is to create a device that is exposed to user mode as \\.\SelfSign and which implements a very bare Device I/O Control interface. The companion application reports whether the device is present and whether its interface responds as expected. The companion application needs no privilege for this and you do not need the companion application for anything. There are other ways to confirm that a driver is loaded. The companion application is just extra help.
Source code is provided not just because I always provide the source code but so that you can satisfy yourself that the driver does nothing more than what I say. The executables have been built from this source code using the WDK for Windows 7 (because it doesn’t require that you get a compiler and linker separately, because it comes with an MSVCRT.LIB that doesn’t tie you to any particular version of Visual Studio, and because its makefiles allow for relatively easy commenting of the build process). Build the executables any other way you want. However you build them, I leave it entirely to you to specify what to sign them with and how. For completeness, though, here are the commands that would have signed the executables that are in the zipfiles, given a simplified setup in which my certificates are in my Personal Store:
signtool sign -v -fd sha1 -s my -n "My Own SHA1 Driver Signer" selfsign.sys signtool sign -v -as -fd sha256 -s my -n "My Own Driver Signer" selfsign.sys
I provide you with a very simple policy that allows all signed drivers much as does the testsigning boot option:
You should, of course, be unhappy about this policy’s laxity both in allowing all signed drivers and in not specifying an update signer. Its merit, and the only reason I tolerate it, is that you can try the demonstration without needing to change the policy file. I could, of course, give you a policy file that you must change, and give you directions for the change. That I don’t is not just for simplicity. It is instead because if you want to change the policy file, as you certainly would for real-world use, then there is a snag. Microsoft provides that these policy files are prepared in XML and then converted to binary form by the PowerShell command ConvertFrom-CIPolicy. The helpfulness of this, however, is greatly reduced because the command declines to run on most Windows editions, not that Microsoft’s documentation of ConvertFrom-CIPolicy troubles to warn you. Just to prepare your own policy file, not necessarily on the computer you use for the demonstration, you will need one of the acceptable editions. What makes an edition acceptable is that the license value CodeIntegrity-AllowConfigurablePolicy is non-zero. Among Windows 10 consumer editions, Professional is not acceptable but Education is. Quite why Microsoft has it that policy files affect all editions of Windows but can be edited (with Microsoft’s tools) only on some, I can’t begin to think. But it is what it is, and it means that I can’t count on your having a “right” Windows edition handy for editing the policy to make it more secure for you by being specific to your choices of driver and certificates.
Unless you want to edit the policy or satisfy yourself that the binary does indeed come from the XML, you need only the binary. It has been compiled by running the PowerShell command
ConvertFrom-CIPolicy -XmlFilePath selfsign.xml -BinaryFilePath selfsign.bin
If only for now, for the immediate purposes of this demonstration, the contents of the policy have to be left largely as magic. Since the source code is in XML, it is in some sense human-readable and I detect a strong suggestion that this is all the readability that Microsoft means anyone to have. For what’s allowed as the XML and with what syntax, read the XML schema in Microsoft.ConfigCI.Commands.dll, which is where the relevant PowerShell commands are handled. The magic, of course, is not in what elements are possible or in their syntax but in their meaning. I have not yet found formal documentation of this by Microsoft and I begin to expect none. After all, most of the applicable PowerShell commands edit the XML for you, as if what Microsoft wants you to know is not the meaning of the XML but the abstraction that is presented by the commands.
It’s not enough just to have a System Integrity policy file that’s syntactically valid and reads as if it might allow your signatures on your drivers. To enable the custom driver-signing feature, the file must first be signed with the Platform Key (or some derivation of it) and then installed with a particular name at a particular location.
As already noted, some flexibility is allowed about which certificate you sign the policy file with. Indeed, you can sign a syntactically correct policy file with anything and it can still be loaded as a policy file. I wave my hands over this because it’s not what we want. For Windows to recognise the policy file as enabling the custom driver signing feature when Secure Boot enabled, the policy must be signed by either the Platform Key or some certificate that has the Platform Key in its chain of issuers. Moreover, the last certificate in your signature—which, remember, will not ordinarily be the root of the certificate chain—must either be the Platform Key or be issued by the Platform Key. Suppose, for example, that My Own PK is installed as the Platform Key. Then custom driver signing will be enabled if you sign the policy file with My Own PK itself or with any certificate further to the right in either of the following chains
but not if you sign with any certificate in the chain
Whatever you sign the policy file with, you can do it with a single signtool sign command. I separate the command-line switches for clarity:
signtool sign -fd sha256 -p7co 188.8.131.52.4.1.311.79.1 -s my -n "My Own Policy Signer" -p7 . selfsign.bin
The -fd with its argument specifies the algorithm, which you know by now must be at least SHA-256.
The -p7co with its argument exactly as given is vital. We are not signing code but data, and even then some very particular content. The argument is an Object ID (OID) that goes into the signature such that whoever examines the signed file can know that what got signed, i.e., the content, is specifically a policy file. I know of no formal Microsoft documentation of this OID nor even of a programmatic declaration in a header from any development kit. Its first occurrence that I know of in formal documentation is almost incidental, as a magic argument in the recipe presented for Signing WDAC policies with SignTool.exe, apparently from 21st February 2018. Until then, documenting that it’s needed in the signature seems to have been left to the folklore of TechNet blogs:
Incidentally, where the first of these says you need “SignTool.exe, found in the Windows SDK (Windows 7 or later)”, do not take it literally: the version in the Windows 7 SDK does not know the -p7co switch and is therefore no good for signing System Integrity policies.
The -s and -n switches stand for whatever you devise for specifying your certificate and how to sign with it. My Own Policy Signer stands for the common name of your certificate that you sign with. For simplicity of demonstration, the switches I present assume that the certificate with its private key is in your Personal certificate store on the computer where you sign the policy. In real-world use you would certainly want to keep the private key much more securely than this. Again though, if that’s news to you, then you might better not be working through this demonstration!
The -p7 switch and its argument specify where to put the signed output, which gets named just by appending .p7 to the name of the unsigned input.
Put aside that Windows by now supports half a dozen types of policy file if you count by distinct values for the PolicyTypeID or a dozen if you count by distinct filenames. Settle just on the original policy type, which is the only one that Windows checks for a Platform Key signature. Whether signed or not, a policy of this type is recognised by Windows only if it is named SiPolicy.p7b. This too seems to be left to semi-official folklore (such as cited above). Ignore anything that any Microsoft blog tells you about putting it in the System32\CodeIntegrity subdirectory of the Windows installation that you want it to apply to. Now that the policy file is signed, it will be accepted by the boot loader only if the boot manager found it in the \EFI\Microsoft\Boot directory of the EFI system partition. This alternate location, though not its necessity, is at least hinted at by Microsoft in a design guide: Deploy Windows Defender Application Control policy rules and file rules.
The EFI system partition is ordinarily hidden. To have any access to it, you will need to mount a file system on it. To have convenient access, you will also want to assign a drive letter to it. Windows comes with a few suitable tools as standard. Perhaps the easiest is to run the following from an administrative Command Prompt:
mountvol x: /s
The /s switch both mounts the appropriate file system (which will be FAT32) and assigns it your choice x of drive letter. Ignore that what seems to be Microsoft’s documentation of this mountvol command, though dated 16th October 2017, says today, 17th June 2018, that /s is “Available on Itanium-based computers only.”
Now that you have access, simply copy the signed policy file into place, e.g.,
copy selfsign.bin.p7 x:\EFI\Microsoft\Boot\SiPolicy.p7b
In an ideal world, you might restart Windows now and then install your driver by any means you like. If the policy has been accepted as enabling custom kernel signing, then your non-Microsoft signature on your driver will be accepted. For us, however, the policy does not get accepted for this purpose except if we restart Windows in a very contrived way to work around Microsoft’s licensing constraints. Even with this contrivance, the policy will have been accepted only by the boot loader, not by the CI.DLL that handles code integrity once the kernel starts loading drivers. We can sneak past the licensing constraint only for a boot driver. To see it get loaded, we would have to restart yet again, repeating the contrivance. Instead, install now.
First, copy the driver to the usual directory for drivers, e.g., by running the following at an administrative Command Prompt:
copy selfsign.sys c:\windows\system32\drivers
A single sc create command will install the driver suitably. Again for clarity, I separate the arguments:
sc create SelfSign binPath= c:\windows\system32\drivers\selfsign.sys start= boot type= kernel group= "Boot Bus Extender"
The price for installing the driver with such ease is an objection by the Program Compatibility Assistant to tell you that “A digitally signed driver is required” and that “Windows has blocked the installation of a digitally unsigned driver.” The complaint is not only reasonable but desirable, even if it might more helpfully describe the driver not as unsigned but as having a signature that Windows disapproves of. If you prefer not to see the complaint, create the corresponding registry key and values manually. If I have to tell you what they are, then yet again it might be better if you don’t continue with the demonstration until you know more.
Restart Windows. Well, that’s what you’d do if you either had Microsoft’s license or (in some ideal future) didn’t need it. Instead, take a detour through a separate installation of Windows where you can run a Command Prompt. It doesn’t matter how you get there. That you’re working through this demonstration, presumably because you have an interest in deploying kernel-mode device drivers, if not in writing them, very likely means you have needed such a Command Prompt before as a recovery option and already have a favoured method. Modern Windows helpfully saves you from having to press some key at just the right time as Windows restarts. Go to Settings and Recovery and select to “Restart now” for an “Advanced startup”. At what looks like a boot menu, choose to Troubleshoot and then select “Advanced options” and Command Prompt. The computer will then reboot but start an alternate Windows on a drive x: that is in fact a RAM disk.
At the Command Prompt in this alternate Windows, run the Registry Editor. Select HKEY_LOCAL_MACHINE and then from the File menu ask to Load Hive. Browse to the SYSTEM hive of the Windows on which you want to load your self-signed driver. Most likely, this hive is C:\Windows\System32\config\SYSTEM. When you have loaded it, e.g., as system_c, browse to
Edit this value’s data to be 1. Close the Registry Editor, exit from the Command Prompt, and Continue with restarting your intended installation of Windows.
All being well, Windows restarts exactly as normal except that it loads your driver that you didn’t send to Microsoft for a signature. See that Windows is not in any sort of Test Mode. Run PowerShell’s Confirm-SecureBootUEFI command to see that Secure Boot has stayed on. Of course, what you most want to confirm is that your driver actually did get loaded. An easy way to check is through the Service Control Manager. If you have used my driver, the test will be that the command
sc query SelfSign
says the queried service is in fact a KERNEL_DRIVER that’s in the RUNNING state. Also if you used my driver, you can run its selfsign application to see if the driver is not just running but does what very little is expected of it:
Device \\.\SelfSign is present API version is 1.0
This happy circumstance of your having your own driver executing despite its having your own signature will persist through sleeps and hibernations until you next restart Windows. Remember first to disable your driver unless you plan to repeat the contrived detour into a separate Windows to edit the registry—or unless you want to check, and I do encourage you to check, that Windows otherwise would refuse to load the driver.
Of course, if something is wrong with your policy file or your signature of it or with your driver or your signature of it, then you will get nowhere near to any of this. The boot loader will itself have bounced you back to a recovery option, perhaps attempting to see what it might repair but then leaving you to fix things up at that same Command Prompt in a separate Windows installation. If you believe that you have followed my directions, then write to me and I will try to help as much as I can without getting into the business of writing policy files for your particular use. Please remember, though, that I can’t even begin to know what has happened on your computer without your sending me at least your signed policy, your signed driver and also the contents of the PK variable from Secure Boot.