You can link binaries with /integritycheck. This will tell the loader to check the signature of the binary at load time. Supposedly signing with page hash (/ph) has a substantial impact on load time. Omitting /ph will require the entire binary to be loaded in order to calculate the hash and verify the signature. I assume /ph does it over the pages as the name implies.

From "Kernel Data and Filtering Support for Windows Server 2008":

  • "All ISV kernel and user mode modules must be linked with the /integritycheck flag set at link time.  This will cause the memory manager to enforce a signature check at load time for the referenced code.  This link.exe setting is present in recent versions of the Microsoft Visual Studio® linker.  If the ISV is using alternate tools to link or edit their produced binaries, this flag setting has the effect of setting (ON) IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY    (0x0800)   in the image PE header OptionalHeader.DllCharacteristics field.
  • Digitally signed user mode modules must have cryptographic per-page hash catalogs.  These are generated using the /PH command-line setting to the signtool utility."

You add /integritycheck in the Visual Studio linker options in advanced. You can verify in a binary editor that the actual flag is set. I used CFF Explorer.

In order to sign, I create a demo certificate:

Makecert -r -pe -sr localMachine -ss demostore -n "CN=DemoCert" democert.cer

Then you can use signtool.exe to sign the binary with that certificate.

Signtool Sign /v /s demostore /n DemoCert /t <mydlltosign>.dll

I created to binaries: one signed with /ph and another without. On trying to load the /ph binary I got an error saying the binary was invalid. I checked the signature and all was good. I signed an executable with the same certificate, UAC elevated it and the consent dialog had the signed flavor. Hmm. Cert was added also to the trusted store.

From "AppInit DLLs in Windows 7 and Windows Server 2008 R2":

"The DLL must be signed with the /ph (page-hash) option. Page-hash enforcement verifies the signature on each page as it is loaded into memory. Because of this requirement, the DLL must be signed on a system that is running Windows Vista or a later version of Windows. The DLL should also be signed with the /t (timestamp) option to prevent the digital signature from expiring when the code-signing certificate expires. When you release sign the DLL, you must use the /ac option to reference a cross-certificate. This is required because the signing code path exists in the Windows kernel, which has a hard-coded root of trust. The cross-certificate is required to chain to the issuing certificate authority."

It appears that integrity checking bypasses the trusted store. Most likely for performance reasons but that is my guess. You need to cross link with a Microsoft certificate. Since I didn't have one handy (and I have no idea how one would do it), I rebooted and turned off driver sign checking. Low and behold my binary loaded.

The actual performance part was not as easy as hoped. I used x-perf. But difference between the page-hash signed binary and the no page hash signed binary was inconclusive. Most likely because my binaries were too small.

Next time I have a free morning, I'll dive into it.