EAC - TPM
Hello UC,
I'm sharing some info on how EAC uses the TPM. Captured this while Rust was running.
based of this logs:
Code:
[0000000b7311c95b] pid=23672 EasyAntiCheat_EOS.sys tpm IN ioctl=0x0022c01c in_len=0x0 out_len=0x10 first=[]
[0000000b7311f078] pid=23672 EasyAntiCheat_EOS.sys tpm IN ioctl=0x0022c01c in_len=0x0 out_len=0x10 first=[]
[0000000b7311f078] pid=23672 EasyAntiCheat_EOS.sys tpm IN ioctl=0x0022c00c (TPM_TBS_SUBMIT_COMMAND) in_len=0xe out_len=0x1000 first=[02 01 00 00 00 0e 00 00 01 73 81 00 00 01] tpm{prio=200 tag=0x8001 size=14 cc=0x00000173(TPM2_ReadPublic) handle=0x81000001}
[0000000b7311f078] pid=23672 EasyAntiCheat_EOS.sys tpm IN ioctl=0x0022c00c (TPM_TBS_SUBMIT_COMMAND) in_len=0xe out_len=0x1000 first=[02 01 00 00 00 0e 00 00 01 73 81 0e ac 00] tpm{prio=200 tag=0x8001 size=14 cc=0x00000173(TPM2_ReadPublic) handle=0x810eac00}
[0000000b7311f078] pid=23672 EasyAntiCheat_EOS.sys tpm IN ioctl=0x0022c00c (TPM_TBS_SUBMIT_COMMAND) in_len=0xe out_len=0x1000 first=[02 01 00 00 00 0e 00 00 01 73 81 0e ac 00] tpm{prio=200 tag=0x8001 size=14 cc=0x00000173(TPM2_ReadPublic) handle=0x810eac00}
[0000000b7311f078] pid=23672 EasyAntiCheat_EOS.sys tpm IN ioctl=0x0022c00c (TPM_TBS_SUBMIT_COMMAND) in_len=0x16 out_len=0x1000 first=[02 01 00 00 00 16 00 00 01 7a 00 00 00 01 81 0e ac 00 00 00 00 01] tpm{prio=200 tag=0x8001 size=22 cc=0x0000017a(TPM2_GetCapability) cap=0x00000001(TPM_CAP_HANDLES) prop=0x810eac00 count=1}
The IOCTL 0x0022C00C wire format (important)
Every submit command starts with 02 01 00 00 00 <SIZE_BE> 00 00 01 <CC_LO>. Byte 0 being 0x02 confused me at first. I expected 0x80 for TPM_ST_NO_SESSIONS. After reversing tpm.sys and tbs.sys I got it. Windows uses a compact wrapper.
In tbs.sys it writes a priority class into byte 0. Then tpm.sys overwrites it with 0x80 before sending it to the TPM. So on the actual wire you always get 80 01...
EAC uses priority class 2 (NORMAL).
Byte level decode of what EAC sent
IOCTL 0x0022C01C Tbsi_GetDeviceInfo
in_len=0 out_len=0x10. Returns a 16 byte TPM_DEVICE_INFO struct. EAC calls this 2 times at startup as a quick probe.
TPM2_ReadPublic(0x81000001) SRK
This is the Storage Root Key. Microsoft provisions it during TPM setup. Its public modulus is unique per chip.
TPM2_ReadPublic(0x810EAC00) the interesting one
EAC creates this persistent handle on first launch with TPM2_CreatePrimary + TPM2_EvictControl. They read it twice back to back as a consistency check. If your hook returns different data each time it gets caught immediately.
TPM2_GetCapability(TPM_CAP_HANDLES 0x810EAC00 1)
This is the existence check. Bypasses ReadPublic completely. Harder to fake cleanly.
Full flow
Tbsi_GetDeviceInfo x2
TPM2_ReadPublic(0x81000001) SRK
TPM2_ReadPublic(0x810EAC00) first
TPM2_ReadPublic(0x810EAC00) second (consistency)
TPM2_GetCapability(TPM_CAP_HANDLES 0x810EAC00 1)
Three different checks that all have to match.
TL;DR
EAC binds machine identity to TPM persistent handle 0x810EAC00 provisioned at first install.
Verification is three part: SRK content + double ReadPublic consistency + GetCapability existence.
IOCTL 0x22C00C wire format has priority class in byte 0. tpm.sys rewrites byte 0 to 0x80 before parsing the TPM header.
Cheers.
This information provide from: xSquad.
|