Small WDM/WDF-style kernel driver that checks whether TPM 2.0 responses on a
machine have been tampered with, i.e. spoofed by a driver that hooks
IRP_MJ_DEVICE_CONTROL / DeviceIoControl on \Driver\TPM.
Some cheats/spoofers install a filter or hook on the TPM device stack so that
TPM2_ReadPublic (and similar commands) return a forged public key/EK
instead of the real one, letting them fake hardware identity to anti-cheat
or attestation checks. The forged data only shows up on the IOCTL path,
because the hook sits between the caller and the real TPM.
The driver gets the same TPM2_ReadPublic result two different ways and
compares them:
HashTpmIoctl— opens\??\TPMdirectly and sends a rawTPM2_ReadPubliccommand viaZwDeviceIoControlFile, exactly like a userland caller would throughDeviceIoControl. This is the path a hook would intercept.HashTpmCached— walks theTPM.sysdriver object, follows its WDF device context to the internalTPM_STACK/ resource manager structures, and reads the cached response buffer the TPM driver already stored for handle0x81010001(the EK). This bypasses the IOCTL path entirely, so a hook onDeviceIoControlhas no way to touch it.
Both responses get parsed (IsReadPublicRSA2048) and hashed with a small
FNV-1a implementation. If both hashes come back non-zero, the driver logs
whether they match. If a hook is spoofing the IOCTL response, the two hashes
will differ — the IOCTL path shows the fake key, the cached path still holds
the real one.
Note: the offsets into TPM_STACK / TPM_RESMGR are reverse engineered from
TPM.sys and will need to be re-checked against the target Windows build if
they stop matching. The WDF context type-info (_WDF_TpmStack_TYPE_INFO) is
located by walking TPM.sys's PE section table for .data and assuming it
sits at the start of that section — also hacky, same caveat applies.
src/Driver.cpp— the driver itself (entry point, IOCTL path, cached-buffer path, comparison).src/tpm12.h,src/tpm20.h— TCG TPM 1.2/2.0 spec structures and constants used to build/parse theReadPubliccommand.
Requires the WDK and Ninja/cl.exe (see CMakePresets.json).
cmake --preset x64-debug
cmake --build --preset x64-debug
Sign the resulting driver with scripts/sign.ps1 before loading it (test
signing / secure boot considerations apply as usual for a kernel driver).