A Guide to Signing Un-signed Drivers (Part 2)

This post continues from part 1 of the driver signing series which can be found here. Part 3 of the series can be found here.

It’s been a while since I looked at signing un-signed drivers I must admit. In fact, I’ve probably not signed one since my original post on AppDeploy (now ITNinja) back in 2010! I’ve been caught up in ‘other worlds’ since then, but I’m finally back to write a second part to this original post (which incidentally I’ve modified and re-written here and you may need to reference this throughout this post).

Anyway, this second part is a small piece on how driving signing works (high level) and an issue that I’ve stumbled upon and resolved recently.

Why Sign a Driver?

It’s a way of ensuring that a driver package has not been tampered with since it was tested and approved. In our self-signing example, you are testing and approving it! It also means that non-administrative users will not get any security prompts when installing drivers for Plug and Play devices (depending upon your driver signing policy).

What is a Catalog file used for?

A catalog file contains digital signatures that reference each file in your driver’s INF file (referenced by the CopyFiles directive…..read on), allowing the operating system to verify the integrity of every file. If any files referenced by the driver INF file are changed after digitally signing the catalog file (including the INF itself!), the signature will be broken.

How is the Catalog Used when we Install the Driver?

The inf file looks for the catalog file that it is referencing and verifies the digital signature. It also verifies the cryptographic checksum of every file that is recorded in the signed catalog file. If any of this does not comply, the driver install will fail.

Debugging a Driver Install (With an Example)

I’m working on an un-signed Smart Card printer by IDP called Smart 50s. Here is the original INF file content:

; SMART.INF
;
; INF file for SMART Card Printer
;
; Copyright 2013 IDP Corp., Ltd
[Version]
Signature="$Windows NT$"
Provider=%Provider%
ClassGUID={4D36E979-E325-11CE-BFC1-08002BE10318}
Class=Printer
CatalogFile=smart.cat
DriverVer=09/16/2014,2.14.09.16
;
; Manufacturer section.
;
; This section lists all of the manufacturers 
; that we will display in the Dialog box
;
[Manufacturer]
%Manufacturer%=INA, NTamd64, NTia64
;
; Model sections. 
; 
; Each section here corresponds with an entry listed in the
; [Manufacturer] section, above. The models will be displayed in the order
; that they appear in the INF file.
;
[INA]
%Product%=Product,USBPRINT\INASystemSMART_PrintDEAE
%Product30%=Product,USBPRINT\IDPSMART-30_Printer6622
[INA.NTamd64]
%Product%=Product,USBPRINT\INASystemSMART_PrintDEAE
%Product30%=Product,USBPRINT\IDPSMART-30_Printer6622
[INA.NTia64]
%Product%=Product,USBPRINT\INASystemSMART_PrintDEAE
%Product30%=Product,USBPRINT\IDPSMART-30_Printer6622
;
; Installer Sections
;
; These sections control file installation, and reference all files that
; need to be copied. The section name will be assumed to be the driver
; file, unless there is an explicit DriverFile section listed.
;
[Product]
CopyFiles=@smart.GPD
CopyFiles=@smart.INI
CopyFiles=@STDNAMES.GPD
CopyFiles=PRINTERUI
CopyFiles=PRINTERUNI
CopyFiles=PRINTERICM
CopyFiles=PRINTERMON
CopyFiles=PRINTERLIB
DataFile=smart.GPD
DataSection=UNIDRV_DATA
LanguageMonitor="IDP SMART Language Monitor,smartmon.dll"
Include=NTPRINT.INF
Needs=UNIDRV.OEM,UNIDRV_DATA
; Copy Sections
;
; Lists of files that are actually copied. These sections are referenced
; from the installer sections, above. Only create a section if it contains
; two or more files (if we only copy a single file, identify it in the
; installer section, using the @filename notation) or if it's a color
; profile (since the DestinationDirs can only handle sections, and not
; individual files).
;
[PRINTERUI]
smartUI.DLL
[PRINTERUNI]
smartUNI.DLL
[PRINTERICM]
cardprinter_d.icm
cardprinter_k.icm
cardprinter_k2.icm
cardprinter_k3.icm
[PRINTERLIB]
SmartComm2.dll
watch.dll
DrvPlugin.exe
CleanPrinter.exe
Watchman.exe
cputil.exe
cardprinter_mask_0.bmp
cardprinter_mask_1.bmp
cardprinter_mask_2.bmp
cardprinter_mask_3.bmp
cardprinter_mask_4.bmp
cardprinter_mask_5.bmp
cardprinter_mask_6.bmp
cardprinter_smart_p_0.prn
cardprinter_smart_p_1.prn
cardprinter_smart_p_2.prn
cardprinter_smart_p_3.prn
cardprinter_smart_p_4.prn
cardprinter_smart_p_5.prn
cardprinter_smart_p_6.prn
cardprinter_smart_p_7.prn
cardprinter_smart_p_8.prn
cardprinter_smart_p_9.prn
cardprinter_smart_s_0.prn
cardprinter_smart_s_1.prn
cardprinter_smart_s_2.prn
cardprinter_smart_s_3.prn
cardprinter_smart_s_4.prn
cardprinter_smart_s_5.prn
cardprinter_smart_s_6.prn
cardprinter_smart_s_7.prn
cardprinter_smart_s_8.prn
cardprinter_smart_s_9.prn
cardprinter_encoding.ini
[PRINTERMON]
smartmon.dll,,,0x00000020
;
; Data Sections
;
; These sections contain data that is shared between devices.
;
;
;  Location of source files not in Layout.inf.
;
[SourceDisksNames]
100 = %Product%
[SourceDisksFiles]
smart.GPD			= 100
smart.INI			= 100
STDNAMES.GPD			= 100
cardprinter_d.icm		= 100
cardprinter_k.icm		= 100
cardprinter_k2.icm		= 100
cardprinter_k3.icm		= 100
SmartComm2.dll			= 100
watch.dll			= 100
DrvPlugin.exe			= 100
CleanPrinter.exe		= 100
Watchman.exe			= 100
cputil.exe			= 100
cardprinter_mask_0.bmp		= 100
cardprinter_mask_1.bmp		= 100
cardprinter_mask_2.bmp		= 100
cardprinter_mask_3.bmp		= 100
cardprinter_mask_4.bmp		= 100
cardprinter_mask_5.bmp		= 100
cardprinter_mask_6.bmp		= 100
cardprinter_smart_p_0.prn	= 100
cardprinter_smart_p_1.prn	= 100
cardprinter_smart_p_2.prn	= 100
cardprinter_smart_p_3.prn	= 100
cardprinter_smart_p_4.prn	= 100
cardprinter_smart_p_5.prn	= 100
cardprinter_smart_p_6.prn	= 100
cardprinter_smart_p_7.prn	= 100
cardprinter_smart_p_8.prn	= 100
cardprinter_smart_p_9.prn	= 100
cardprinter_smart_s_0.prn	= 100
cardprinter_smart_s_1.prn	= 100
cardprinter_smart_s_2.prn	= 100
cardprinter_smart_s_3.prn	= 100
cardprinter_smart_s_4.prn	= 100
cardprinter_smart_s_5.prn	= 100
cardprinter_smart_s_6.prn	= 100
cardprinter_smart_s_7.prn	= 100
cardprinter_smart_s_8.prn	= 100
cardprinter_smart_s_9.prn	= 100
cardprinter_encoding.ini	= 100
[SourceDisksFiles.x86]
smartui.dll			= 100,i386
smartuni.dll			= 100,i386
smartmon.dll			= 100,i386
[SourceDisksFiles.amd64]
smartui.dll			= 100,amd64
smartuni.dll			= 100,amd64
smartmon.dll			= 100,amd64
[SourceDisksFiles.ia64]
smartui.dll			= 100,amd64
smartuni.dll			= 100,amd64
smartmon.dll			= 100,amd64
;
; Call SetupSetDirectoryId with 66000 to set the target directory at runtime
; (depending on which environment drivers are getting installed)
;
[DestinationDirs]
DefaultDestDir=66000
PRINTERICM=66003
PRINTERMON=66002
PRINTERLIB=11
[Strings]
Provider = "IDP Corp,.Ltd."
Manufacturer = "IDP Corp,.Ltd."
Product = "IDP SMART-50 Card Printer"
Product30 = "IDP SMART-30 Card Printer"

Since we’ve already generated a corporate certificate using my guide, all we really need to do here is sign the catalog file. Occasionally we need to delete the vendor’s cat file and generate our own cat file. I sometimes do this for peace of mind, to ensure that the cat file is up to date and contains all the correct file checksums referenced by the INF file.

So next I imported my certificate into the Trusted Root store (i need to do this before I can verify the digital signature), and verified the digital signature of my catalog file using SignTool.exe. Everything reported a success.

Next (and as a quick test) I lumped a copy of DPInst.exe into the driver folder on my dev machine, and ran it interactively by double clicking it. And then I gasped:

Unsigned driverHow can this be? Signtool.exe verified the digital signature successfully? So I delved into the driver log file which is located here:

C:\Windows\inf\setupapi.dev.log

And when I scrolled down to the bottom (incidentally deleting this file will regenerate a new one, which may be handy when debugging) I found this line:

!!!  sto:                Failed to verify file 'UNIDRVUI.DLL' against catalog. Catalog = smart.cat, Error = 0xE000024B
!!!  sto:                Catalog did not contain file hash. File is likely corrupt or a victim of tampering.
!!!  sto:           Driver package appears to be tampered. Filename = C:\windows\System32\DriverStore\Temp\{36d82f49-0898-19c5-54bf-8b142c4bcd4b}\smart.inf, Error = 0x800F024B
!!!  sto:           Driver package appears to be tampered and user does not want to install it.
!!!  ndv:           Driver package failed signature validation. Error = 0xE000024B

This was interesting. Take note of the exclamation marks at the very start – a single exclamation is a warning, and a triple exclamation mark is an error/failure So I did a little Googling and stumbled upon this. Specifically, it says:

PnP device installation considers the digital signature of a driver package to be invalid if any file in the driver package is altered after the driver package was signed. Such files include the INF file, the catalog file, and all files that are copied by INF CopyFiles directives.

I knew the INF and Catalog files hadn’t been modified since signing them, so it led me to the bit in bold. Was UNIDRVUI.DLL being copied to the driver store by a CopyFiles directive? It turns out it wasn’t!! So this meant fixing a vendor’s INF file which I can assure you, isn’t much fun! What’s important to note here is that even though Signtool.exe verified the digital signature of my catalog file, that does not mean to say that the catalog file contains all the required file checksums!

Here are the changes I made – unfortunately I won’t go in to how we modify INF files since it’s not a trivial task to explain. Anyway:
1. Added a CopyFiles directive, pointing to a section called UNIFILES.INF difference 1 2. Created the UNIFILES section, and added all the files that we missing (it wasn’t just UNIDRVUI.DLL)INF difference 2

3. Added the files to the SourceDisksFiles.x86 section, so that they could be located on disk (they were in a sub folder called i386)
INF difference 3

I then deleted the cat file so I could re-generate it with all the new file checksums. And I re-signed it with my certificate. I then ran DPInst.exe and successfully got the following prompt!

Signed successfully At this point I thought it was good to go. As you all probably know, Windows 7 and above come with some handy VBScript printing utilities located here:

C:\Windows\System32\Printing_Admin_Scripts\en-US

The scripts basically use WMI classes under the hood to add/configure printers and ports etc. I wanted to run quick test using the WMI approach instead of the PrintUI.dll approach since we can return exit codes, and I think there are issues returning exit codes via Rundll32 and PrintUI.dll (it tends to just print the error out to a dialog window as opposed to returning an exit code).

When we tried to install our driver, it returned an 2148467266 exit code. So I checked inside setupapi.dev.log and found the following:

!!!  sto:           Driver package signer is unknown. Assuming untrusted signer. Error = 0x800F0242
!!!  ndv:           Driver package failed signature validation. Error = 0xE0000242

Assuming an untrusted signer? That’s strange. So I MOVED the certificate to the Trusted Publishers store. And then I got this:

!!!  sto:           Catalog signer is untrusted. No error message will be displayed as client is running in non-interactive mode.
!!!  ndv:           Driver package failed signature validation. Error = 0xE0000247

Fair enough. I kind of expected that. So i decided to put my certificate in BOTH the Trusted Publisher AND the Trusted Root stores. And voila. No error messages and a return code of 0!

I’ll probably end up using C:\Windows\System32\PnPutil.exe to install my driver above. It has a handy command line switch (/i) that installs the driver on matching devices that are connected to the system (the WMI approach doesn’t seem to do this – although perhaps a little more research is needed surrounding this!). In other words, when a user plugs in this device (without the drivers installed – they need to plug it in first to mount the Virtual Printer port) it will create an entry under Devices and Printers in the ‘Unspecified’ section. Installing the driver and adding a printer via WMI works, but also leaves the ‘Unspecified’ device until the user logs off and logs back in (even after a print spooler stop and start). PnPutil.exe seems to install the driver and then match it up to this unspecified device and moves it to the Printers and Faxes section. That’s all….for now.

A Guide to Signing Un-signed Drivers (Part 1)

This is a cleaned-up version of a post I made a while ago on ITNinja (http://www.itninja.com/question/guide-to-signing-unsigned-drivers). I’ve also incorporated a few changes for Windows 7 etc (using a later version of the WDK) and thanks goes to Rowan Heuvel over at ITNinja (Rheuvel) for these additions. I’ve not actually gone through it all from start to finish for a good while, so feel free to offer any feedback. Be careful of the quotes when copying and pasting (specifically for the custom actions) – quotes must be straight quotes!

Part 2 of this driver signing series can be found here, and part 3 can be found here.

Tools you need (most are from the Windows Driver Kit – I placed all of these in the same folder):

Inf2Cat.exe (To generate the unsigned catalog file from our INF)

WindowsProtectedFiles.xml

Microsoft.Whos.Shared.IO.Cabinets.dll

Microsoft.Whos.Shared.IO.Catalogs.dll

Microsoft.Whos.Shared.Xml.InfReader.dll

Microsoft.Whos.Winqual.Submissions.SubmissionBuilder.dll

Microsoft.Whos.Xml.NonXmlDataReader.dll

Makecert.exe (Used to create our certificate)

Cert2spc.exe (Create Software Publisher’s Certificate (SPC) from our certificate)

Signcode.exe (Sign our catalog file with an Authenticode digital signature. Note – this has been replaced with Signtool.exe in later versions of the WDK)

pvk2pfx.exe (Copies public key and private key information contained in .spc, .cer, and .pvk files to a Personal Information Exchange (.pfx) file. Note – this is present in later versions of the WDK and must be used before Signtool.exe.)

Certmgr.exe (Used to add and delete our certificate to the system root)

DifxApp.msm (Difx Merge Module to install the driver)

Introduction

Now we have our toolset in place, let’s package the unsigned Captain Planet printer driver. We’re packaging this driver for a customer called ‘Planeteers Ltd’. Let’s assume we’ve captured the Captain Planet printer driver using a snapshot tool. We can see from the resultant snapshot that there is a file in [WindowsFolder]\inf called ‘captainplanet.inf’. There are also files called ‘captainplanet.sys’ and ‘captainplanet.dll’ in [SystemFolder]spool\drivers\w32x86. All of these files make up the Captain Planet printer driver so let’s create a folder anywhere on your work machine (say, “c:\cpdriver”) and copy the three files to it. We should now have our unsigned driver in a temporary folder called “C:\cpdriver” which contains:

captainplanet.inf

captainplanet.sys

captainplanet.dll

Now let’s generate a customer certificate, so we can sign this (and many more) driver(s).

Step 1: Signing the Driver and Verifying the Signature

Step 1a: Create .cat (catalog) file for driver.

We notice that the Captain Planet driver doesn’t contain a cat file, so we’ll need to generate one. Open the .INF file in a text editor. Ensure that under the [version] section that you have an entry specifying a .cat file. If it’s not there, add the line at the end of the section. For example:

[version] Signature=xxxxxx

Provider=xxxxxx

CatalogFile.NTx86=captainplanet.cat

“captainplanet.cat” is the name of the cat file that we want to generate. Not having a line specifying this will result in an “error 22.9.4 – Missing 32-bit catalog file entry” when we run Inf2Cat.exe. “NTx86” represents the Windows 2000 x86-based platforms.

Command line:

(Windows XP)

Inf2Cat.exe /driver:"C:\cpdriver" /os:XP_X86

(Windows 7)

Inf2Cat.exe /driver:"C:\cpdriver" /os:7_X86

Running this successfully will generate captainplanet.cat in the ‘C:\cpdriver’ folder.

Step 1b: Create authenticode certificate and set private key

Create another folder called ‘c:\PlaneteersLtd_certificate’. It is here where we’ll create our customer-specific certificate and private key. Remember that this certificate can be reused multiple times for the customer (Planeteers Ltd) to sign different drivers, so keep naming conventions generic to your customer.

Command line:

MakeCert.exe -r -pe -n "CN=PlaneteersLtd" -sv "c:\PlaneteersLtd_certificate\PlaneteersLtd.pvk" -len 2048  "c:\PlaneteersLtd_certificate\PlaneteersLtd.cer"

Running this will ask you to set a private key. Make a note of this key! Running this command will generate:

c:\PlaneteersLtd_certificate\PlaneteersLtd.cer and c:\PlaneteersLtd_certificate\PlaneteersLtd.pvk (I think certificates of this kind are actually supposed to be used for development/testing as opposed to a live environment. It’s probably advisable to purchase a certificate from Verisign/Comodo/whoever if you have the budget.)

Step 1c: Create Software Publisher’s Certificate (SPC) from our certificate

Command Line:

cert2spc.exe "c:\PlaneteersLtd_certificate\PlaneteersLtd.cer" "c:\PlaneteersLtd_certificate\PlaneteersLtd.spc"

This will generate c:\PlaneteersLtd_certificate\PlaneteersLtd.spc

Step 1d: Sign the catalog file

Command line:

SignCode.exe -spc "c:\PlaneteersLtd_certificate\PlaneteersLtd.spc" -v "c:\PlaneteersLtd_certificate\PlaneteersLtd.pvk" -t http://timestamp.verisign.com/scripts/timstamp.dll "C:\cpdriver\captainplanet.cat"

This will prompt you for the private key you set earlier. Enter it when prompted.

If you are using a later version of the WDK, you will need to use SignTool.exe instead. Because SignTool.exe requires a .pfx file (Personal Information Exchange), there is an additional step involved.

Command line:

pvk2pfx.exe -pvk "c:\PlaneteersLtd_certificate\PlaneteersLtd.pvk" -spc "c:\PlaneteersLtd_certificate\PlaneteersLtd.spc" -pfx "c:\PlaneteersLtd_certificate\PlaneteersLtd.pfx" -pi "<private key password>"

Command line:

signtool.exe sign /f "c:\PlaneteersLtd_certificate\PlaneteersLtd.pfx" /p "<private key password>" /t http://timestamp.verisign.com/scripts.timstamp.dll "C:\cpdriver\captainplanet.cat"

It’s important to timestamp the signature. Timestamping means that even when the code signing certificate expires, the signature will remain valid indefinitely.

Step 1e: Verifying the signature

You can verify the catalog is signed by adding your certificate to the trusted root and then running the following command:

signtool.exe verify /pa /v /c "C:\cpdriver\captainplanet.cat" "C:\cpdriver\captainplanet.inf"

If the catalog is signed correctly you should see the following at the end of the signtool output:

Successfully verified: C:\cpdriver\captainplanet.inf
Number of files successfully Verified: 1
Number of warnings: 0
Number of errors: 0

Step 2: Adding the Customer Certificate

The catalog for our driver is now signed. Every time we install this driver using DifxApp, we need to ensure the customer certificate is installed on the machine before DifxApp installs the driver. To do this via a windows installer (alternatively we could just deploy the certificates out to all machines using group policy and skip this step), we use CertMgr.exe in two Custom Actions (CA) – one to add the certificate on install, and one to remove it on uninstall. We can do this in two ways:

a) Create the two CAs in every single driver package you do

b) Create a merge module which can easily be incorporated in every driver package you do In my opinion option b) would provide less hassle for me because after all, we want our certificate to be easily re-usable for multiple driver packages. So for now, we’ll do option b).

Step 2a: Create a merge module and add certificate.

Create a new merge module and add captainplanet.cer to the SystemFolder of the MSM (or wherever you deem appropriate)

Step 2b: Add Custom Actions to add and remove the certificate

Go to the Custom Action (CA) table and create 2 CAs, both are an ‘Exe stored in binary table’ and ‘Deferred in a System Context’. If you use the CA wizard, you should stream ‘certmgr.exe’ into your binary table. Here’s what (roughly) your CustomAction table should look like after you’ve made them:

Name: addCertificate

Type: 3074

Source: certmgr

Target: -add “[SystemFolder]PlaneteersLtd.cer” -s -r localMachine ROOT

 

Name: removeCertificate

Type: 3074

Source: certmgr

Target: -del “[SystemFolder]PlaneteersLtd.cer” -s -r localMachine ROOT

 

And here is the Binary table:

 

Name: certmgr

Data: <binary data>

Of course, using the wizard to do both CAs may result in two entries in your binary table containing the same exe (certmgr.exe). Obviously this is a waste, so remove one of them and set the ‘Source’ column in your CustomAction table appropriately.

UPDATE 1: In windows 7 environments we may also use certutil which is part of the operating system:

Too add: certutil -addStore “Root” “[SystemFolder]PlaneteersLtd.cer”

Too remove: certutil -delStore “Root” xxyyzz

‘xxyyzz’ is the Serial Number of the certificate, which can be obtained (after importing the certificate) by: certutil -store “Root”

UPDATE 2: We should also add/remove the certificate to/from the TrustedPublisher store. See the end of part 2 of this driver series for more information.

Step 2c: Add Custom Actions to the installation sequence

Go to the ModuleInstallExecuteSequence table and add the following:

Action: addCertificate

Sequence: <null>

BaseAction: InstallFiles

After: 1

Condition: Not Installed

 

Action: removeCertificate

Sequence: <null>

BaseAction: InstallInitialize

After: 1

Condition: REMOVE~=”ALL”

 

Action: InstallFiles

Sequence: 4000

BaseAction: <null>

After: <null>

Condition: <null>

 

Action: InstallInitialize

Sequence: 1500

BaseAction: <null>

After: <null>

Condition: <null>

The sequencing above is important as the addCertificate CA needs to run after the certificate is put down on the machine (after InstallFiles) and the removeCertificate CA needs to run before the certificate is removed from the machine. It also needs to run before DifxApp installs the driver.

STEP 3: Create the Driver Package

Great. At this point we should now have a signed driver, and a merge module which installs our customer certificate. Now we can create the MSI package which installs our signed driver:

Step 3a: Create a new ISM/WSI project and make a folder somewhere for your driver (one folder per driver remember).

Let’s say: “[ProgramFilesFolder]CaptainPlanetDriver” for this example. This folder will contain all the files in ‘c:\cpdriver’ on our work machine: captainplanet.inf captainplanet.sys captainplanet.dll captainplanet.cat (the one we’ve just generated!)Remember that all these files should be in the SAME COMPONENT as well as the same folder (one .inf per folder), and the keypath of the component needs to be the .inf file! Let’s call the component: “CaptainPlanet_DRIVER”

Step 3b. Install the driver using DifxApp.

When we add the latest version of the DifxApp.msm merge module, it will create a table in the project called “MsiDriverPackages”. Installshield and Wise have slightly different wizards to install a driver, so I’ll just show you roughly how the MsiDriverPackages table should be populated:

Component: CaptainPlanet_DRIVER

Flags: 7

Sequence: 1

ReferenceComponents: <null>

Once this is done, compile your .MSI. Ensure in your .MSI that your ‘addCertificate’ CA runs after ‘installFiles’ and before ‘MsiProcessDrivers’ in the installExecuteSequence. At last, we’re done!

Step 4. Final Thoughts

Now I’d highly recommend testing your MSI install with verbose logging enabled, as the DifX merge module does write some half-decent logging which can assist you. For example, just recently DifxApp verbose logging warned me that some DLLs (part of the driver) were not being copied to the driver store. This was the reason why my driver wasn’t working, and so I had to edit the .INF file [SourceDisksFiles] section (Add the relevant files which weren’t being copied to the driver store), re-generate the cat file, re-sign it and then re-add these updated files to my MSI (Note there was obviously no need to go fiddling with my MSM as the certificate remains the same). It’s also always best to test your driver with the appropriate hardware. Anyway. I hope this guide can be of some help to others. If you break your machine, or yourself, during implementing this guide then don’t blame myself. It’s only meant as a guide.