Inside GetOfflineDeviceUniqueID
How Windows Derives Its Offline Device Identifier
Abstract
Modern Windows systems can derive a stable, salt-scoped identifier for the physical machine on which they are running. This identifier is produced through the undocumented Windows function clipc!GetOfflineDeviceUniqueID, exposed by clipc.dll, and is implemented internally by the Client Licensing Platform service, ClipSVC. The identifier is used by Windows licensing components, device-identity components, and some third-party anti-cheat systems because it is designed to persist across operating-system reinstallation.
This paper reconstructs the call flow from the client-side API into the RPC service, examines the command dispatcher inside clipsvc.dll, and analyzes the observed identifier-derivation methods: TPM endorsement-key, a cached UEFI copy of that key, a UEFI-persisted random seed, and a registry-persisted random seed. The note also identifies inconsistencies in the optional CRC integrity outputs and discusses the security and privacy implications of the mechanism.
Keywords: Windows, ClipSVC, GetOfflineDeviceUniqueID, TPM, UEFI, device identifier, reverse engineering, privacy, licensing.
Introduction
This note was inspired by recent news coverage concerning Microsoft’s ability to recognize devices across installations [1], [2]. That reporting prompted a closer examination of the internal Windows components that produce stable device identifiers. The present analysis focuses on one such mechanism: the undocumented function GetOfflineDeviceUniqueID, exported by clipc.dll, the Client Licensing Platform Client library.
The function returns a 32-byte identifier derived from a caller-provided salt and a machine-bound source value. The source value may come from the TPM endorsement key, from UEFI variables, or from a registry-persisted random seed. Because the resulting identifier is salt-scoped, different callers can receive different identifiers for the same machine when they use different salts. This property is visible in the WinRT SystemIdentification API, where the publisher identity of a calling application is used as the salt.
This note expands upon earlier public reverse-engineering work by Wack0 [3]. Because Microsoft does not provide public PDB symbols for clipc.dll or clipsvc.dll, the function names, type names, and prototypes in this note are reconstructed from observed behavior and binary analysis. They should therefore be treated as analytical labels rather than official Microsoft symbols.
Scope and Method
The analysis is limited to understanding how Windows derives the offline device identifier and how the result is consumed by selected in-box components. The note does not provide instructions for evading security products, bypassing licensing, or attacking systems. Security considerations are presented at an architectural level to support defensive evaluation and privacy analysis.
The observations described here apply to all Windows 10 and higher systems. Although the public enumeration associated with prior research contains additional method identifiers, only four implemented retrieval methods are explained to keep things simple:
- Method 1: TPM endorsement-key public value.
- Method 2: cached TPM endorsement-key public value stored in a UEFI variable.
- Method 3: random seed stored in a UEFI variable.
- Method 6: random seed stored in the registry.
Client Side Interface in clipc.dll
A program that wants the device ID doesn’t compute it itself. It calls a function named GetOfflineDeviceUniqueID inside a Windows file called clipc.dll, which quietly forwards the request to a background Windows service and hands back whatever that service returns.
The exported function clipc!GetOfflineDeviceUniqueID is a thin client side wrapper around an RPC call to ClipSVC. The caller supplies a salt, a requested retrieval method, and an output buffer capable of holding at least 32 bytes. The function validates basic arguments, acquires a service session, serializes the request into a property list, and invokes the service through RPC.
clipc!GetOfflineDeviceUniqueID client wrapper, based on prior research by
Wack0.
GetOfflineDeviceUniqueID is therefore an RPC wrapper over clipsvc.dll. It communicates with the service through NdrClientCall3. The second argument of NdrClientCall3 is the procedure number. In the analyzed ClipSVC interface, procedure 0 appears to acquire a session, procedure 1 releases it, and procedure 2 is a generic command channel. ClipSvcCall passes an additional operation identifier; for GetOfflineDeviceUniqueID, that operation identifier is 13.
clipc!ClipSvcCall RPC worker.
Analysis Inside ClipSVC
The background service that actually generates the ID has an internal switchboard: each request carries a numbered “command,” and the service looks up which internal function should handle that number. Requesting the device ID uses command number 13.
The service-side implementation resides in clipsvc.dll. The RPC server is registered during ServiceMain using RpcServerInterfaceGroupCreateW. The endpoint uses ncalrpc and is named ClipServiceTransportEndpoint-00001. The security descriptor grants access to normal callers and also permits Application Container callers to read from the interface.
Following the global ClipRpcInterfaceSpec structure, at offset 0x50 there is a MIDL_SERVER_INFO pointer, and at offset 0x8 of that structure there is a manager function array. Inside srv_ClipSvcDispatch, specific command identifiers are dispatched to final handlers through a lookup in gCommandDispatchTable. The table contains 16 entries.
Handler pointers are encoded with EncodePointer and decoded with DecodePointer before invocation. Table initialization occurs during DLL initialization through the C++ runtime initialization mechanism, so the table appears to be a freestanding global variable with a constructor that performs the encoding.
The target handler for GetOfflineDeviceUniqueID is at index 13 in the table. In this note, the handler is named ClipSvcHandlerCmd13.
The actual computation of the device identifier is performed by ComputeOfflineDeviceID. This function accepts either a specific method identifier or the default value 0. When the default is requested, the function probes supported methods in priority order: TPM endorsement key, cached UEFI EK public value, UEFI random seed, and finally registry random seed.
ComputeOfflineDeviceID, originally observed as sub_1800B450C.
Three observations follow from this dispatcher:
- If an input method is not specified, the function tries TPM first and then the other methods until one succeeds.
- The
pCrcOkoutput appears intended as a consistency signal, but it is broken across the implemented methods and is not a reliable tamper indicator. - Although the public enumeration contains additional methods, only four implemented methods were observed on the analyzed Windows system:
ODUID_TPM_EK,ODUID_UEFI_VARIABLE_TPM,ODUID_UEFI_VARIABLE_RANDOMSEED, andODUID_REGISTRY_ENTRY.
Method 1: DeriveID_TpmEk
The preferred source is the machine’s TPM security chip. Windows reads a fixed, hardware-rooted value out of the chip, mixes it with the caller’s salt, and hashes the result. Because the value comes from the chip itself, it survives a Windows reinstall.
ID = SHA-256(salt || TPM_EK_public).
A specific registry key is checked to detect the Windows PE recovery environment. GetTpmEkPublic behaves differently depending on that result. TPM EK public retrieval is performed by opening a handle to the TPM device, whose commands are implemented by tbs.sys. Command submission occurs through NtDeviceIoControlFile.
PersistEkPubToUefi stores the retrieved TPM EK public value using SetFirmwareEnvironmentVariableExW. It writes the value to OfflineUniqueIDEKPub and stores the CRC32 of its hash in OfflineUniqueIDEKPubCRC. This cached copy is later used by method 2.
Method 2: DeriveID_UefiEkPub
When Windows successfully reads the TPM value with Method 1, it also saves a copy in a small piece of persistent firmware storage. Method 2 is the fallback that reads that saved copy instead of talking to the TPM chip directly, which helps if the TPM isn’t reachable at that moment.
ID = SHA-256(salt || cached_EK_public).
IsUefiFirmware checks SystemBootEnvironmentInformation using NtQuerySystemInformation and verifies that the firmware type is at least FirmwareTypeUefi. The most likely reason this method exists in addition to the direct TPM method is to obtain the same EK public value when the TPM is inaccessible, for example in environments where the TPM driver path is unavailable. This method returns the stored CRC from OfflineUniqueIDEKPubCRC.
Method 3: DeriveID_UefiRandomSeed
If there’s no usable TPM at all, Windows falls back to a random 32-byte value that it generates once and saves in the same kind of persistent firmware storage as Method 2. Because it’s saved in firmware rather than on the hard drive, it can still survive a Windows reinstall.
ID = SHA-256(salt || random_seed).
This method uses the OfflineUniqueIDRandomSeed and OfflineUniqueIDRandomSeedCRC UEFI firmware environment variables. If they are not set, a randomly generated seed is created and persisted. The global mutex OFFLINEDEVICEID-CREATERANDOMSEED prevents corruption if two requests attempt to create the seed simultaneously.
Method 6: DeriveID_RegistryRandomSeed
The last-resort fallback, for machines old enough to lack UEFI firmware storage entirely. It works the same way as Method 3 (a random value generated once and reused), except the value is saved in the Windows registry instead of firmware. Because the registry lives on the same drive as Windows itself, this copy does not survive a reinstall.
ID = SHA-256(salt || random_seed).
This method follows the structure of DeriveID_UefiRandomSeed closely, with the difference that the identifier seed is stored in the registry instead of UEFI environment variables. It is most likely the last fallback for legacy systems that do not have UEFI.
The use of RtlGetPersistedStateLocation is interesting. If the key HKLM\SYSTEM\CurrentControlSet\Control\StateSeparation\RedirectionMap\Keys\OfflineDeviceID exists, it could be used to override the location of the ODUID state through its TargetPath subkey. This method also sets no CRC cookie, and unlike the TPM path, that omission is not harmless.
Summary of Retrieval Methods
The method numbers below are not contiguous. Two additional values defined in the RETRIEVAL_METHOD enumeration, a UEFI device lock/unlock method and an Xbox console ID path, had no corresponding implementation in clipsvc.dll on the analyzed system and are omitted from the table.
| Method | Source | Storage location | Stored cookie | pCrcOk behavior |
|---|---|---|---|---|
| 1: TPM_EK | TPM public EK | \??\TPM through PCP/TBS; mirrored to UEFI |
None; method writes pCookie = 0 |
Comparison skipped; output may remain uninitialized |
| 2: UEFI_TPM | Cached TPM EK public value | UEFI OfflineUniqueIDEKPub |
CRC32(SHA-256(EKPub)) |
Matches only for an empty salt; normally false for salted IDs |
| 3: UEFI_RANDOMSEED | 32-byte random seed | UEFI OfflineUniqueIDRandomSeed |
CRC32(SHA-256(seed)) |
Matches only for an empty salt; normally false for salted IDs |
| 6: REGISTRY_ENTRY | 32-byte random seed | HKLM\...\TPM\ODUID\RandomSeed |
None; method writes pCookie = 0 |
Still compared against zero; almost always false |
Broken pCrcOk Semantics
Alongside the ID itself, Windows returns a flag that is supposed to say “yes, this checks out” or “no, something looks tampered with.” In every retrieval method examined, that flag is computed incorrectly and almost never means what it appears to mean. It should not be treated as proof that the ID is genuine or unmodified.
The two auxiliary outputs returned by command 13 appear to have been designed as a consistency check. The selected derivation method returns a stored CRC32 cookie through pCookie, after which ComputeOfflineDeviceID recomputes CRC32 over the final 32-byte device ID.
For methods 2 and 3, the stored cookie covers the unsalted identifier. When the caller supplies a nonempty salt, the two SHA-256 outputs differ. Their CRC32 values will therefore differ with overwhelming probability, causing pCrcOk to be set to false on a completely unmodified system.
This is particularly notable for AppContainer and low-integrity callers. The client wrapper explicitly rejects an empty salt for those callers, meaning that methods 2 and 3 cannot normally reach the one case in which the stored cookie corresponds to the returned ID.
For method 6, the registry-backed method stores no CRC cookie. DeriveID_RegistryRandomSeed writes zero to pCookie, but the final guard checks whether the pCookie pointer is non-null, not whether the cookie has a meaningful value. Command handler 13 always passes a valid pointer, so method 6 compares the final salted identifier CRC against zero. That expression will almost always be false.
For method 1, the TPM EK path is explicitly excluded from the CRC comparison. It writes zero to pCookie, but it does not write pCrcOk. In the reconstructed command handler, the two outputs may occupy adjacent DWORDs of the same stack storage. For method 1, one DWORD is written while the other may remain uninitialized, yet both values are serialized into the reply.
The in-box callers examined later in this note pass null client-side pointers for both optional outputs. ClipSVC still calculates and serializes the fields, but clipc!GetOfflineDeviceUniqueID discards them after parsing the reply. The bugs therefore have no visible effect on those particular call paths.
Third-party consumers should not treat pCrcOk as a generic tamper indicator:
- For method 1, it may be undefined or uninitialized.
- For methods 2 and 3, it is only potentially meaningful when the salt is empty.
- For method 6, it has no meaningful interpretation because no cookie exists.
The CRC itself is also unkeyed. Even in the empty-salt UEFI case where the comparison works as intended, a party capable of modifying both the persisted value and its CRC can generate a matching pair.
Security and Privacy Considerations
This ID proves a request came from some machine, but it does not cryptographically prove that a sufficiently privileged attacker could not have faked it, so it should not be relied on as unforgeable proof of identity. It also has privacy implications, because the same ID (or a predictably related one) can be handed to multiple apps: those that share a salt, or use no salt at all, can potentially recognize the same PC across different pieces of software.
GetOfflineDeviceUniqueID provides a stable, salt-scoped identifier. It does not provide attestation. The result is not signed, bound to a caller-selected challenge, or cryptographically authenticated to the caller. Consequently, a sufficiently privileged attacker can replace the result without reproducing the underlying TPM, UEFI, or registry source.
At an architectural level, interception or spoofing may occur at several boundaries: the exported client API, the RPC transport, the service implementation, the TPM driver path, or the persisted UEFI and registry values. The service host is protected as PPL-Light, which raises the bar for direct user-mode tampering, but this protection does not make the mechanism tamper-proof against kernel-level or physical-memory adversaries.
Replacing ClipSVC with a fake local RPC server is another possible interception boundary, but this note does not evaluate whether service configuration, endpoint ownership, RPC security, or automatic service recovery makes that practical. Similarly, direct modification of persisted UEFI or registry values could globally affect seed-based methods, especially if associated cookies are also updated.
From a privacy perspective, the identifier is stable across operating-system reinstallation when the same source value remains available. The salt-scoping mechanism limits cross-application correlation when different salts are used, as in the WinRT publisher-scoped API. However, callers that use the same salt, or no salt where permitted, can receive the same identifier and may therefore correlate activity across installations.
Observed Windows Consumers
Which parts of Windows actually use this ID? Scanning the system files for references to clipc.dll narrowed it down to two: a device-management helper library, and the WinRT API that Windows Store apps use to identify the device they’re running on.
To understand how Windows itself uses this identifier, the Windows system directory was scanned for portable executable files that reference clipc.dll. A narrower search for direct references to GetOfflineDeviceUniqueID identified two modules: dmcmnutils.dll and Windows.System.Profile.SystemId.dll.
clipc.dll and direct GetOfflineDeviceUniqueID usage.
dmcmnutils!GetOfflineHardwareUID
Windows’ device-management components (used for things like enterprise device enrollment) call this ID as their first choice for identifying the machine, using their own fixed salt. If it isn’t available, they fall back to other identifiers, and as a last resort generate and save a random ID of their own.
The device-management common utilities module contains a function named GetOfflineHardwareUID. This function dynamically resolves clipc.dll and calls GetOfflineDeviceUniqueID with a static 16-byte salt embedded in the module. The resulting 32-byte identifier is converted to a 64-character uppercase hexadecimal string.
dmcmnutils!GetOfflineHardwareUID.
A higher-level exported function, DMGetClientHardwareUID, uses GetOfflineHardwareUID as the first choice in a fallback chain. If the offline device identifier is unavailable or explicitly skipped, the function attempts other identity sources, eventually falling back to a persisted random GUID.
dmcmnutils!DMGetClientHardwareUID fallback chain.
Windows.System.Profile.SystemId!GetSystemId
This is the officially documented, app-facing route to this ID. A Windows Store app can call SystemIdentification.GetSystemIdForPublisher() to get a device ID scoped to that app’s publisher. The publisher’s identity is used as the salt, so different publishers’ apps see different-looking IDs from the same PC.
The WinRT module Windows.System.Profile.SystemId.dll implements the public Windows.System.Profile.SystemIdentification API. UWP applications call GetSystemIdForPublisher to obtain a per-publisher device identifier. Internally, the module calls GetOfflineDeviceUniqueID on normal PC systems. On Xbox systems, a separate console-identifier path is used.
GetSystemId implementation.
GetSystemIdForPublisher salt selection.
The publisher-ID salt is privacy-relevant: it causes the same physical device to produce different identifiers for different publishers. The Xbox path is the only observed practical use of method 5, the Xbox console identifier. The console identifier is hardware-rooted and is mapped to the public source value Tpm. The precise purpose of quirk 1114137 was not determined; it appears to preserve compatibility in the mapping of internal method identifiers to public source categories.
Call Flowchart
GetOfflineDeviceUniqueID.
Conclusion
The Windows offline device identifier is a stable, salt-scoped SHA-256 value derived from machine-bound source material. The preferred source is the TPM endorsement-key public value, with UEFI and registry fallbacks for environments where direct TPM access or UEFI storage is unavailable. The mechanism is implemented through an undocumented RPC interface in ClipSVC and is consumed by Windows licensing, device-management, and WinRT system-identification components.
The identifier is designed to persist across operating-system reinstallation, which makes it valuable for licensing and anti-fraud scenarios but also raises privacy considerations. Salt-scoping mitigates naive cross-application tracking, but the identifier remains stable for callers that share the same salt. The optional CRC integrity outputs are not reliable and should not be used as tamper evidence.
Glossary
Short, non-technical definitions for terms used throughout this paper. Technical readers can skip this section.
- TPM (Trusted Platform Module)
- A small security chip (or a firmware equivalent) built into most modern PCs. It can store cryptographic keys that are difficult to extract, separately from the operating system and hard drive.
- Endorsement key (EK)
- A cryptographic key pair burned into a TPM at manufacturing time. Its public half is effectively a fixed, hardware-rooted fingerprint for that specific chip.
- UEFI
- The modern replacement for the traditional PC BIOS. Among other things, it provides a small amount of persistent storage, called “UEFI variables,” that can hold data across reboots and, in some cases, across a Windows reinstall.
- Firmware variable
- A named piece of data stored by the UEFI firmware itself rather than by Windows. Windows can read and write these through dedicated system calls.
- Registry
- The hierarchical settings database built into Windows, used here as a fallback storage location on older systems without UEFI.
- Salt
- An extra piece of data mixed in before hashing so that the same underlying secret produces a different-looking result for each caller. It is why the same PC can hand out a different ID to each app that asks.
- Hash / SHA-256
- A one-way mathematical function that turns any input into a fixed-length string of bytes. The same input always produces the same output, but the output cannot practically be reversed back into the input. SHA-256 is one specific, widely used hash function that always produces 32 bytes (256 bits).
- CRC32 / checksum
- A short numeric fingerprint used to detect accidental data corruption. Unlike a cryptographic hash, it offers no protection against deliberate tampering: anyone who can change the protected data can also recompute a matching CRC32.
- DLL (Dynamic Link Library)
- A file containing reusable code and functions that other Windows programs can load and call into at runtime, such as
clipc.dllin this paper. - RPC (Remote Procedure Call) /
ncalrpc - A mechanism that lets one piece of software call a function that actually runs inside a different, separate process, in this case a background Windows service.
ncalrpcis the specific transport used here, restricted to communication within the same local machine. - Windows service /
svchost.exe - A background program that Windows keeps running independently of any signed-in user. Many built-in services, including the one examined here, run hosted inside a shared
svchost.exeprocess. - PPL (Protected Process Light)
- A Windows protection level that shields a process from being freely inspected or modified by other software running as a normal administrator, even though it is not a full kernel-mode component.
- WinRT / UWP
- The application platform and API layer used by Windows Store-style apps, as opposed to traditional desktop applications.
- HRESULT
- The standard numeric result code Windows functions return to indicate success or a specific kind of failure.
- Reverse engineering
- The process of analyzing compiled software to understand its behavior when the original source code and documentation are not available.
Sources
- “A Hacker’s Arrest Reveals Microsoft Can Track Users via a Windows Device,” PCMag. https://www.pcmag.com/news/a-hackers-arrest-reveals-microsoft-can-track-users-via-a-windows-device.
- “You can’t fully disable Microsoft’s GDID Windows 11 tracker, but these settings limit what it captures,” Windows Latest, July 10, 2026. https://www.windowslatest.com/2026/07/10/you-cant-fully-disable-microsofts-gdid-windows-11-tracker-but-these-settings-limit-what-it-captures/.
-
Wack0, prior research notes on
GetOfflineDeviceUniqueID, published approximately eleven months before this writeup. https://gist.github.com/Wack0/663dc0a91056cf4431365f77036f3bf3.