OSRLogoOSRLogoOSRLogo x Seminar Ad

Everything Windows Driver Development

GoToHomePage xLoginx

    Thu, 14 Mar 2019     118020 members


  Online Dump Analyzer
OSR Dev Blog
The NT Insider
The Basics
File Systems
ListServer / Forum
  Express Links
  · The NT Insider Digital Edition - May-June 2016 Now Available!
  · Windows 8.1 Update: VS Express Now Supported
  · HCK Client install on Windows N versions
  · There's a WDFSTRING?
  · When CAN You Call WdfIoQueueP...ously

Keeping Secrets - Windows NT Security (Part II)


This article is part of a continuing series on Windows NT Security.  In the previous article, we covered terminology...below, we continue with a brief segment on basic construction.


Now that we have reviewed the basic terms, we will discuss how to construct these various data structures within your own kernel mode code.  Many of these routines are based upon the NT Native API, and several of these functions are present only in the IFS Kit.


Security Identifier


Because the Security Descriptor is used to identify security entities (users, groups, computers, etc.) it is essential that we start by describing how to build security descriptors.  In the remainder of this section we will discuss how to obtain an SID for use in your driver.


Using Standard SID Values


One possibility here is to use the set of pre-defined SIDs exported by the operating system.  This is part of the SE_EXPORTS structure (from ntifs.h) as shown in Figure 1.


typedef struct _SE_EXPORTS {


    // Privilege values



    LUID    SeCreateTokenPrivilege;

    LUID    SeAssignPrimaryTokenPrivilege;

    LUID    SeLockMemoryPrivilege;

    LUID    SeIncreaseQuotaPrivilege;

    LUID    SeUnsolicitedInputPrivilege;

    LUID    SeTcbPrivilege;

    LUID    SeSecurityPrivilege;

    LUID    SeTakeOwnershipPrivilege;

    LUID    SeLoadDriverPrivilege;

    LUID    SeCreatePagefilePrivilege;

    LUID    SeIncreaseBasePriorityPrivilege;

    LUID    SeSystemProfilePrivilege;

    LUID    SeSystemtimePrivilege;

    LUID    SeProfileSingleProcessPrivilege;

    LUID    SeCreatePermanentPrivilege;

    LUID    SeBackupPrivilege;

    LUID    SeRestorePrivilege;

    LUID    SeShutdownPrivilege;

    LUID    SeDebugPrivilege;

    LUID    SeAuditPrivilege;

    LUID    SeSystemEnvironmentPrivilege;

    LUID    SeChangeNotifyPrivilege;

    LUID    SeRemoteShutdownPrivilege;


    // Universally defined Sids



    PSID  SeNullSid;

    PSID  SeWorldSid;

    PSID  SeLocalSid;

    PSID  SeCreatorOwnerSid;

    PSID  SeCreatorGroupSid;


    // Nt defined Sids



    PSID  SeNtAuthoritySid;

    PSID  SeDialupSid;

    PSID  SeNetworkSid;

    PSID  SeBatchSid;

    PSID  SeInteractiveSid;

    PSID  SeLocalSystemSid;

    PSID  SeAliasAdminsSid;

    PSID  SeAliasUsersSid;

    PSID  SeAliasGuestsSid;

    PSID  SeAliasPowerUsersSid;

    PSID  SeAliasAccountOpsSid;

    PSID  SeAliasSystemOpsSid;

    PSID  SeAliasPrintOpsSid;

    PSID  SeAliasBackupOpsSid;




Figure 1 — Pre-defined SIDs from SE_EXPORTS


Access to these must be “activated” by ensuring that your driver calls the SeEnableAccessToExports macro because a failure to call this function will yield incorrect results.  Once enabled, you can use the SeExports variable to access these well-known values.


Constructing an SID


An alternative to using the standard SID values is to construct an SID directly.  Typically, this would only be done for the pre-defined values as an alternative to using the exported types.  For example, the code snippet in Figure 2 demonstrates how to construct an SID for “local system”.




  // Temporary stack based storage for an SID.


  UCHAR sidBuffer[64];

  PISID localSid = (PISID) sidBuffer;




  // Build the local system SID


  RtlZeroMemory(sidBuffer, sizeof(sidBuffer));


  localSid->Revision = SID_REVISION;

  localSid->SubAuthorityCount = 1;

  localSid->IdentifierAuthority = localSidAuthority;

  localSid->SubAuthority[0] = SECURITY_LOCAL_SYSTEM_RID;



  // Make sure it is valid


  if (!RtlValidSid(localSid)) {


      DbgPrint("no dice - SID is invalid\n");









Figure 2 — Constructing a SID for a local system



In this code sample, we used the well-known “local system” authority value (SECURITY_NT_AUTHORITY) and then associated with it a single “Relative Identifier” or RID.  This RID value (SECURITY_LOCAL_SYSTEM_RID) is used to indicate that the calling process is part of the operating system.


Lastly, we used RtlValidSid to confirm that we built the SID correctly.  While we might not use this in production code, such crosschecks are valuable in debugging new security code.


Extracting the SID from the Token


Another technique, and one that can prove to be quite valuable, is to extract the SID from the token of the current thread or process (See Figure 3).  This is often used to determine the caller performing a particular I/O operation.  For example, when the CIFS File Server (srv.sys) is performing an operation that requires authentication it impersonates the client.  A file system (or filter) can determine the SID of the remote client and act appropriately based upon that information, rather than using the local system SID, which is normally used by SRV because it runs as a kernel mode driver.


NTSTATUS GetCallerSid(PUCHAR SidBuffer, PULONG SidBufferLength)



    UCHAR       buffer[256];

    PISID       sid = (PISID)&buffer[sizeof(TOKEN_USER)];

    NTSTATUS    status;

    HANDLE      handle;

    ULONG       tokenInfoLength;

    LONG        length;


    // sanity check





    if (*SidBufferLength == 0) {






    // open the thread token


    status = ZwOpenThreadToken(NtCurrentThread(), TOKEN_READ, TRUE, &handle);


    if (status == STATUS_NO_TOKEN) {


        // No thread level token, so use the process

        // level token.  This is the common case since the only

        // time a thread has a token is when it is impersonating.


        status = ZwOpenProcessToken(NtCurrentProcess(), TOKEN_READ, &handle);





    // This should have succeeded.  In this example, we

    // crash if it didn't work.


    if (!NT_SUCCESS(status)) {


        return status;




    //  Retrieve the user information from the token. 


    status = ZwQueryInformationToken( handle,






    // This call should always work.


    if (!NT_SUCCESS(status)) {


        DbgPrint("ZwQueryInformationToken failure - status %x\n",status);


        return status;




    length = tokenInfoLength - sizeof(TOKEN_USER);


    ASSERT(length > 0);


    if ((ULONG)length > *SidBufferLength) {


        DbgPrint("SidBufferLength too small - expected %d. got %d.\n", length,







    // copy the sid


    *SidBufferLength = (ULONG)length;


    RtlCopyMemory(SidBuffer, sid, *SidBufferLength);



    // for debug, let's see it


    DbgPrint("SID (Revision %u, SubAuthorityCount %u):\n",




    DbgPrint("PsclUtilsGetSid:\tIdentifierAuthority = %u-%u-%u-%u-%u-%u\n",








    if (sid->SubAuthorityCount) {

        ULONG   index;


        for (index = 0; index < sid->SubAuthorityCount;index++) {


            DbgPrint("PsclUtilsGetSid:\tSubAuthority = index %d value %u\n",








    return STATUS_SUCCESS;



Figure 3 — Technique For Extracting SID From Token



Note the technique used here was specifically designed to work, even in the face of impersonation.   This is accomplished by first attempting to open the token for the calling thread.  If that fails, the process token is used.  This works because normally threads rely upon their process credentials, rather than specific thread credentials.  In the case where the thread has credentials, it is probably because the thread is impersonating.  A process must have a token.


The routine ZwOpenThreadToken() is not presently included in ntddk.h or ntifs.h but has been discussed and published in a number of support forums.  The prototype for this function is shown in Figure 4.





ZwOpenThreadToken(HANDLE ThreadHandle,

                                     ACCESS_MASK AccessMask,

                                     BOOLEAN OpenAsSelf,

                                     PHANDLE TokenHandle);



Figure 4 — ZwOpenThreadToken()



The prototypes for  NtProcessToken() is included in ntifs.h.  Note that ZwProcessToken() has the same prototype, although it uses the normal system call mechanism to invoke NtProcessToken().










Related Articles
Keeping Secrets - Windows Security (Part III)
Keeping Secrets - Windows NT Security (Part I)
You've Gotta Use Protection -- Inside Driver & Device Security
Still Feeling Insecure? - IoCreateDeviceSecure( ) for Windows 2K/XP/.NET
Securing Device Interfaces - A Better Approach than Sending an SD
Security During Create Operations
Locking Down Drivers - A Survey of Techniques
What is Coming with Vista - Limited User Access

User Comments
Rate this article and give us feedback. Do you find anything missing? Share your opinion with the community!
Post Your Comment

"Use Ex version"
Microsoft has added Ex versions for ZwOpenThreadToken and ZwOpenProcessToken - the only difference is that you can specify the OBJ_KERNEL_HANDLE flag (which is done to ensure the correct behavior of the code. Otherwise there is a potential attack.)

24-Jan-10, Tony Mason

Post Your Comments.
Print this article.
Email this article.
bottom nav links