Access Tokens
The MSDN describes an access token as follows:
"An access token contains the security information for a logon session. The system creates an access token when a user logs on, and every process executed on behalf of the user has a copy of the token. The token identifies the user, the user's groups, and the user's privileges. The system uses the token to control access to securable objects and to control the ability of the user to perform various system-related operations on the local computer. There are two kinds of access token, primary and impersonation."
So, in other words if we want our applications to be able to discover a load of information about the permissions of the current process (that would be our application) we need to be able to get an access token and decipher all of the information that it contains or provides us access to. This allows us to ascertain whether we have the proper permissions to do certain things that might require a particular right/elevated permissions. If we were able to easily access this information, it would be a huge step forward, but it is only the first step. The real power will come when we can create tokens and properly modify the tokens so that we can elevate and/or restrict permissions. For now, let's take that first step and see what is provided by a process token when using the GetTokenInformation function and a handful of other API calls.
Running the Code
Cut-N-Paste the following code into a prg file and run it from within Visual FoxPro. A temporary log file will be generated and displayed showing you the types of information available via GetTokenInformation. Time should be taken to step through the code and understand the different parts of it for at least three reasons:
- The information that it generates is useful to Visual FoxPro applications moving forward into the world of Vista and UAC
- In addition to working with Tokens, many of the fundamental building blocks (structures) to Windows security are used
- There are a number of advanced techniques in the code for dealing with pointers and structures that can prove useful to any Visual FoxPro developer that wants to make use of the Windows API.
The next steps are to show how to set token information using the SetTokenInformation API call and then to start massaging the code provided into some workable/reuseable classes for Visual FoxPro developers. These are the types of things I will be showing (among others) in the upcoming blog entries for this series. I hope you've being enjoying these blog entries and if you have any questions or things you'd like me to show more information on, please don't hesitate to leave a comment here or contact me directly via email.
As for everything I've been going over in the last 5 blog entries, eventually an FLL can, and probably will be created, that provides VFP with the ability to access and modify token information using a series of functions that will negate the need for extensive VFP code such as is being provided here. It's a natural progression in my mind. First understand it and work with it in Visual FoxPro and then expand the Visual FoxPro language base by providing Visual FoxPro developers with FLLs.
In any event, here's the VFP code and have fun!
Get Process Token Information
#Define TokenUser 1 &&
#Define TokenGroups 2 &&
#Define TokenPrivileges 3 &&
#Define TokenOwner 4 &&
#Define TokenPrimaryGroup 5 &&
#Define TokenDefaultDacl 6 &&
#Define TokenSource 7 &&
#Define TokenType 8 &&
#Define TokenImpersonationLevel 9 &&
#Define TokenStatistics 10 &&
#Define TokenRestrictedSids 11 &&
#Define TokenSessionId 12 &&
#Define TokenGroupsAndPrivileges 13 &&
#Define TokenSessionReference 14
#Define TokenSandBoxInert 15
#Define TokenAuditPolicy 16
#Define TokenOrigin 17
#Define TokenElevationType 18
#Define TokenLinkedToken 19
#Define TokenElevation 20
#Define TokenHasRestrictions 21
#Define TokenAccessInformation 22
#Define TokenVirtualizationAllowed 23
#Define TokenVirtualizationEnabled 24
#Define TokenIntegrityLevel 25
#Define TokenUIAccess 26
#Define TokenMandatoryPolicy 27
#Define TokenLogonSid 28
#Define TokenPrimary 1
#Define TokenImpersonation 2
#Define SecurityAnonymous = 0
#Define SecurityIdentification = 1
#Define SecurityImpersonation = 2
#Define SecurityDelegation = 3
#Define SidTypeUser 1
#Define SidTypeGroup 2
#Define SidTypeDomain 3
#Define SidTypeAlias 4
#Define SidTypeWellKnownGroup 5
#Define SidTypeDeletedAccount 6
#Define SidTypeInvalid 7
#Define SidTypeUnknown 8
#Define SidTypeComputer 9
#Define SidTypeLabel 10
**********************************************
*!* The SID is enabled for access checks.
*!* When the system performs an access check, it checks for access-allowed
*!* and access-denied access control entries (ACEs) that apply to the SID.
*!* A SID without this attribute is ignored during an access check unless
*!* the #DEFINE SE_GROUP_USE_FOR_DENY_ONLY attribute is set.
#Define SE_GROUP_ENABLED 0x00000004
#Define SE_GROUP_ENABLED_BY_DEFAULT 0x00000002 && The SID is enabled by default.
#Define SE_GROUP_INTEGRITY 0x00000020 && Yet To Be Determined
#Define SE_GROUP_INTEGRITY_ENABLED 0x00000040 && Yet To Be Determined
#Define SE_GROUP_LOGON_ID 0xC0000000 && The SID is a logon SID that identifies the logon session associated with an access token.
**********************************************
*!* The SID cannot have the #DEFINE SE_GROUP_ENABLED attribute cleared
*!* by a call to the AdjustTokenGroups function. However, you can use
*!* the CreateRestrictedToken function to convert a mandatory SID to a deny-only SID.
#Define SE_GROUP_MANDATORY 0x00000001
**********************************************
*!* The SID identifies a group account for which the user of the token is the owner of the group,
*!* or the SID can be assigned as the owner of the token or objects.
#Define SE_GROUP_OWNER 0x00000008
#Define SE_GROUP_RESOURCE 0x20000000 && The SID identifies a domain-local group. Windows NT: This value is not supported.
**********************************************
*!* The SID is a deny-only SID in a restricted token. When the system performs an access check,
*!* it checks for access-denied ACEs that apply to the SID; it ignores access-allowed ACEs for the SID.
*!* If this attribute is set, SE_GROUP_ENABLED is not set, and the SID cannot be reenabled.
*!* Windows NT: This value is not supported.
#Define SE_GROUP_USE_FOR_DENY_ONLY 0x00000010
#Define SE_PRIVILEGE_ENABLED_BY_DEFAULT 0x00000001
#Define SE_PRIVILEGE_ENABLED 0x00000002
#Define SE_PRIVILEGE_REMOVED 0x00000004
#Define SE_PRIVILEGE_USED_FOR_ACCESS 0x80000000
#Define NO_MULTIPLE_TRUSTEE 0
#Define TRUSTEE_IS_IMPERSONATE 1
#Define TRUSTEE_IS_SID 0
#Define TRUSTEE_IS_NAME 1
#Define TRUSTEE_BAD_FORM 2
#Define TRUSTEE_IS_OBJECTS_AND_SID 3
#Define TRUSTEE_IS_OBJECTS_AND_NAME 4
#Define TRUSTEE_IS_UNKNOWN 0
#Define TRUSTEE_IS_USER 1
#Define TRUSTEE_IS_GROUP 2
#Define TRUSTEE_IS_DOMAIN 3
#Define TRUSTEE_IS_ALIAS 4
#Define TRUSTEE_IS_WELL_KNOWN_GROUP 5
#Define TRUSTEE_IS_DELETED 6
#Define TRUSTEE_IS_INVALID 7
#Define TRUSTEE_IS_COMPUTER 8
#Define NOT_USED_ACCESS 0
#Define GRANT_ACCESS 1
#Define SET_ACCESS 2
#Define DENY_ACCESS 3
#Define REVOKE_ACCESS 4
#Define SET_AUDIT_SUCCESS 5
#Define SET_AUDIT_FAILURE 6
#Define MaxTokenInfoClass 29
#Define MAXIMUM_ALLOWED 33554432
#Define TOKEN_SOURCE_LENGTH 8
#Define GPTR 0x0040
#Define ERROR_SUCCESS 0
#define RIGHTS_DELETE 0x00010000 && Was DELETE
#define READ_CONTROL 0x00020000
#define WRITE_DAC 0x00040000
#define WRITE_OWNER 0x00080000
#define SYNCHRONIZE 0x00100000
#define STANDARD_RIGHTS_REQUIRED 0x000f0000
#define STANDARD_RIGHTS_READ READ_CONTROL
#define STANDARD_RIGHTS_WRITE READ_CONTROL
#define STANDARD_RIGHTS_EXECUTE READ_CONTROL
#define STANDARD_RIGHTS_ALL 0x001f0000
#define SPECIFIC_RIGHTS_ALL 0x0000ffff
#define GENERIC_READ 0x80000000
#define GENERIC_WRITE 0x40000000
#define GENERIC_EXECUTE 0x20000000
#define GENERIC_ALL 0x10000000
#define NO_INHERITANCE 0x0
#define SUB_OBJECTS_ONLY_INHERIT 0x1
#define SUB_CONTAINERS_ONLY_INHERIT 0x2
#define SUB_CONTAINERS_AND_OBJECTS_INHERIT 0x3
#define INHERIT_NO_PROPAGATE 0x4
#define INHERIT_ONLY 0x8
#define INHERITED_ACCESS_ENTRY 0x10
#define INHERITED_PARENT 0x10000000
#define INHERITED_GRANDPARENT 0x20000000
#define ACE_OBJECT_TYPE_PRESENT 0x1
#define ACE_INHERITED_OBJECT_TYPE_PRESENT 0x2
#define SE_UNKNOWN_OBJECT_TYPE 0
#define SE_FILE_OBJECT 1
#define SE_SERVICE 2
#define SE_PRINTER 3
#define SE_REGISTRY_KEY 4
#define SE_LMSHARE 5
#define SE_KERNEL_OBJECT 6
#define SE_WINDOW_OBJECT 7
#define SE_DS_OBJECT 8
#define SE_DS_OBJECT_ALL 9
#define SE_PROVIDER_DEFINED_OBJECT 10
#define SE_WMIGUID_OBJECT 11
#define SE_REGISTRY_WOW64_32KEY 12
#define OBJECT_INHERIT_ACE 0x1
#define CONTAINER_INHERIT_ACE 0x02
#define NO_PROPAGATE_INHERIT_ACE 0x04
#define INHERIT_ONLY_ACE 0x08
#define INHERITED_ACE 0x10
#define VALID_INHERIT_FLAGS 0x0F
#define VALID_INHERIT_FLAGS 0x1F
#DEFINE CRLF CHR(13) + CHR(10)
Declare Integer GetTokenInformation In "Advapi32" As GetTokenInformationC ;
LONG nTokenHandle, ;
LONG nTokenInformationClass, ;
STRING @cTokenInformation, ;
LONG nTokenInformationSize, ;
LONG @nReturnLength
Declare Integer GetTokenInformation In "Advapi32" As GetTokenInformationN ;
LONG nTokenHandle, ;
LONG nTokenInformationClass, ;
LONG @nTokenInformation, ;
LONG nTokenInformationSize, ;
LONG @nReturnLength
Declare Integer GetTokenInformation In "Advapi32" As GetTokenInformationP ;
LONG nTokenHandle, ;
LONG nTokenInformationClass, ;
LONG nTokenInformation, ;
LONG nTokenInformationSize, ;
LONG @nReturnLength
Declare Integer OpenProcessToken In "Advapi32" ;
LONG nProcessHandle, ;
LONG nDesiredAccess, ;
LONG @nTokenHandle
Declare Integer IsTokenRestricted In "Advapi32" ;
LONG nTokenHandle
Declare Long GetLengthSid In "Advapi32" As GetLengthSidC ;
STRING pSID
Declare Long GetLengthSid In "Advapi32" As GetLengthSidN ;
LONG pSID
Declare Integer CopySid In "Advapi32" ;
LONG nDestinationSidLength, ;
STRING @pDestinationSid, ;
STRING @pSourceSid
Declare Integer LookupAccountSid In "Advapi32" ;
STRING lpSystemName, ;
STRING lpSid, ;
STRING @lpName, ;
LONG @cchName, ;
STRING @lpReferencedDomainName, ;
LONG @cchReferencedDomainName, ;
LONG @peUse
Declare Integer IsValidSid In advapi32 ;
STRING pSid
Declare FreeSid In "Advapi32" ;
STRING pSid
Declare Integer LookupPrivilegeName In "advapi32" ;
STRING lpSystemName, ;
LONG lpLuid, ;
STRING @lpName, ;
LONG @chName
Declare Long GetExplicitEntriesFromAcl In advapi32 ;
LONG pacl, ;
LONG @pcCountOfExplicitEntries, ;
LONG @pListOfExplicitEntries
DECLARE String strcpy IN "msvcr71" String@, Long
Declare Long GlobalAlloc In "kernel32" ;
LONG wFlags, ;
LONG dwBytes
Declare Long GlobalFree In "kernel32" ;
LONG Hmem
Declare Long LocalFree In "kernel32" ;
LONG Hmem
Declare Integer CloseHandle In "Kernel32" ;
LONG nHandle
Declare Long GetCurrentProcess In "Kernel32"
Declare Integer GetLastError In win32api
Local lnToken, lnResult, lcType, lnSize, lnImpersonationLevel, lcStatistics, lnSessionID, lcOwnerSID, lcLogFile
m.lcLogFile = ADDBS(SYS(2023)) + SYS(2015) + ".log"
m.lnToken = 0
m.lnType = 0
m.lnResult = OpenProcessToken(GetCurrentProcess(), MAXIMUM_ALLOWED, @m.lnToken)
If m.lnResult != 0
SET CONSOLE OFF
SET ALTERNATE TO (m.lcLogFile)
SET ALTERNATE ON
?"Restricted:" + Transform(IsTokenRestricted(m.lnToken))
m.lnSize = 0
If GetTokenInformationN(m.lnToken, TokenType, @m.lnType, 4, @m.lnSize) != 0
?"Type: " + Iif(m.lnType = TokenPrimary, "Primary", "Impersonation")
If m.lnType = TokenImpersonation
m.lnSize = 0
m.lnImpersonationLevel = 0
If GetTokenInformationN(m.lnToken, TokenImpersonationLevel, @m.lnImpersonationLevel, 4, @m.lnSize) != 0
Do Case
Case m.lnImpersonationLevel = SecurityAnonymous
?"Impersonation Level: Anonymous"
Case m.lnImpersonationLevel = SecurityIdentification
?"Impersonation Level: Identification"
Case m.lnImpersonationLevel = SecurityImpersonation
?"Impersonation Level: Impersonation"
Case m.lnImpersonationLevel = SecurityDelegation
?"Impersonation Level: Delegation"
Endcase
Else
? "GetTokenInformation Failed: TokenImpersonationLevel" + CRLF + GetLastErrorMessage(GetLastError())
Endif
Endif
Else
? "GetTokenInformation Failed: TokenType" + CRLF + GetLastErrorMessage(GetLastError())
Endif
m.lnSize = 0
GetTokenInformationC(m.lnToken, TokenStatistics, Null, 0, @m.lnSize)
If m.lnSize > 0
m.lcStatistics = Replicate(Chr(0), m.lnSize)
If GetTokenInformationC(m.lnToken, TokenStatistics, @m.lcStatistics, m.lnSize, @m.lnSize) != 0
? "TokenId (low part): " + Transform(CToBin(Substr(m.lcStatistics,1,4), "4RS"))
? "TokenId (high part): " + Transform(CToBin(Substr(m.lcStatistics,5,4), "4RS"))
? "AuthenticationId (low part): " + Transform(CToBin(Substr(m.lcStatistics,9,4), "4RS"))
? "AuthenticationId (high part): " + Transform(CToBin(Substr(m.lcStatistics,13,4), "4RS"))
? "ExpirationTime (low part):" + Transform(CToBin(Substr(m.lcStatistics,17,4), "4RS"))
? "ExpirationTime (high part):" + Transform(CToBin(Substr(m.lcStatistics,21,4), "4RS"))
GetFileTime(Substr(m.lcStatistics,17,8))
? "TokenType: " + Transform(CToBin(Substr(m.lcStatistics,25,4), "4RS"))
? "ImpersonationLevel: " + Transform(CToBin(Substr(m.lcStatistics,29,4), "4RS"))
? "DynamicCharged: " + Transform(CToBin(Substr(m.lcStatistics,33,4), "4RS"))
? "DynamicAvailable: " + Transform(CToBin(Substr(m.lcStatistics,37,4), "4RS"))
? "GroupCount: " + Transform(CToBin(Substr(m.lcStatistics,41,4), "4RS"))
? "PrivilegeCount: " + Transform(CToBin(Substr(m.lcStatistics,45,4), "4RS"))
? "ModifiedId (low part): " + Transform(CToBin(Substr(m.lcStatistics,49,4), "4RS"))
? "ModifiedId (high part): " + Transform(CToBin(Substr(m.lcStatistics,53,4), "4RS"))
Else
? "GetTokenInformation Failed: TokenStatistics" + CRLF + GetLastErrorMessage(GetLastError())
Endif
Endif
m.lnSize = 0
m.lnSessionID = 0
If GetTokenInformationN(m.lnToken, TokenSessionId, @m.lnSessionID, 4, @m.lnSize) != 0
? "Terminal Services Session ID: " + Transform(m.lnSessionID)
Else
? "GetTokenInformation Failed: TokenSessionId" + CRLF + GetLastErrorMessage(GetLastError())
Endif
m.lnSize = 0
?GetTokenInformationN(m.lnToken, TokenOwner, 0, 0, @m.lnSize)
If m.lnSize > 0
m.lnOwner = 0
m.lnOwner = GlobalAlloc(GPTR,m.lnSize)
If GetTokenInformationP(m.lnToken, TokenOwner, m.lnOwner, m.lnSize, @m.lnSize) != 0
m.lnPSID = CToBin(Sys(2600,lnOwner,4),"4rs")
m.lcOwner = Sys(2600,m.lnPSID,m.lnSize-4)
GetSidInformation(m.lcOwner)
FreeSid(m.lcOwner)
Else
? "GetTokenInformation Failed: TokenOwner" + CRLF + GetLastErrorMessage(GetLastError())
Endif
GlobalFree(m.lnOwner)
Endif
m.lnSize = 0
GetTokenInformationP(m.lnToken, TokenUser, 0, 0, @m.lnSize)
If m.lnSize > 0
m.lnUser = 0
m.lnUser = GlobalAlloc(GPTR, m.lnSize)
If GetTokenInformationP(m.lnToken, TokenUser, m.lnUser, m.lnSize, @m.lnSize) != 0
m.lnPSID = CToBin(Sys(2600,lnUser,4),"4rs")
m.lcUser = Sys(2600,m.lnPSID,m.lnSize-8)
GetSidInformation(m.lcUser)
m.lnAttributes = CToBin(Sys(2600,m.lnUser+4,4),"4rs")
GetBitFlags(m.lnAttributes, "SID Attributes: ")
FreeSid(m.lcUser)
Else
? "GetTokenInformation Failed: TokenUser" + CRLF + GetLastErrorMessage(GetLastError())
Endif
GlobalFree(m.lnUser)
Endif
m.lnSize = 0
GetTokenInformationN(m.lnToken, TokenPrimaryGroup, 0, 0, @m.lnSize)
If m.lnSize > 0
m.lnPrimaryGroup = 0
m.lnPrimaryGroup = GlobalAlloc(GPTR,m.lnSize)
If GetTokenInformationP(m.lnToken, TokenPrimaryGroup, m.lnPrimaryGroup, m.lnSize, @m.lnSize) != 0
m.lnPSID = CToBin(Sys(2600,lnPrimaryGroup,4),"4rs")
m.lcPrimaryGroup = Sys(2600,m.lnPSID,m.lnSize-4)
GetSidInformation(m.lcPrimaryGroup)
FreeSid(m.lcPrimaryGroup)
Else
? "GetTokenInformation Failed: TokenPrimaryGroup" + CRLF + GetLastErrorMessage(GetLastError())
Endif
GlobalFree(m.lnPrimaryGroup)
Endif
m.lnSize = 0
GetTokenInformationC(m.lnToken, TokenSource, Null, 0, @m.lnSize)
If m.lnSize > 0
m.lcSource = Replicate(Chr(0), m.lnSize)
If GetTokenInformationC(m.lnToken, TokenSource, @m.lcSource, m.lnSize, @m.lnSize) != 0
? "SourceName: " + Substr(m.lcSource,1,TOKEN_SOURCE_LENGTH)
? "SourceIdentifier (low part): " + Transform(CToBin(Substr(m.lcSource,1+TOKEN_SOURCE_LENGTH,4), "4RS"))
? "SourceIdentifier (high part): " + Transform(CToBin(Substr(m.lcSource,5+TOKEN_SOURCE_LENGTH,4), "4RS"))
Else
? "GetTokenInformation Failed: TokenSource" + CRLF + GetLastErrorMessage(GetLastError())
Endif
Endif
m.lnSize = 0
GetTokenInformationP(m.lnToken, TokenRestrictedSids, 0, 0, @m.lnSize)
If m.lnSize > 0
m.lnRestrictedSids = 0
m.lnRestrictedSids = GlobalAlloc(GPTR, m.lnSize)
If GetTokenInformationP(m.lnToken, TokenRestrictedSids, m.lnRestrictedSids, m.lnSize, @m.lnSize) != 0
m.lnGroupCount = CToBin(Sys(2600,lnRestrictedSids,4),"4rs")
? "Group Count: " + Transform(m.lnGroupCount)
If m.lnGroupCount > 0
For m.lnCounter = 1 To m.lnGroupCount
m.lnRestrictedSids = m.lnRestrictedSids + 4
m.lnPSID = CToBin(Sys(2600,lnRestrictedSids,4),"4rs")
m.lnSize = GetLengthSidN(m.lnPSID)
m.lcRestrictedSids = Sys(2600,m.lnPSID,m.lnSize)
GetSidInformation(m.lcRestrictedSids)
m.lnRestrictedSids = m.lnRestrictedSids + 4
m.lnAttributes = CToBin(Sys(2600,lnRestrictedSids,4),"4rs")
GetBitFlags(m.lnAttributes, "SID Attributes: ")
m.lcAttributeCaption = "Group" + Transform(m.lnCounter) + ": "
If m.lnAttributes = 0
? m.lcAttributeCaption + "Disabled"
Else
If Bitand(m.lnAttributes, SE_GROUP_ENABLED) != 0
If Bitand(m.lnAttributes, SE_GROUP_ENABLED_BY_DEFAULT) != 0
? m.lcAttributeCaption + "Enabled By Default"
Else
? m.lcAttributeCaption + "Enabled"
Endif
Endif
If Bitand(m.lnAttributes, SE_GROUP_LOGON_ID) != 0
? m.lcAttributeCaption + "Logon ID"
Endif
If Bitand(m.lnAttributes, SE_GROUP_MANDATORY) != 0
? m.lcAttributeCaption + "Mandatory"
Endif
If Bitand(m.lnAttributes, SE_GROUP_USE_FOR_DENY_ONLY) != 0
? m.lcAttributeCaption + "Use For Deny Only"
Endif
If Bitand(m.lnAttributes, SE_GROUP_INTEGRITY) != 0
? m.lcAttributeCaption + "Integrity"
Endif
If Bitand(m.lnAttributes, SE_GROUP_INTEGRITY_ENABLED) != 0
? m.lcAttributeCaption + "Integrity Enabled"
Endif
If Bitand(m.lnAttributes, SE_GROUP_OWNER) != 0
? m.lcAttributeCaption + "Owner"
Endif
If Bitand(m.lnAttributes, SE_GROUP_RESOURCE) != 0
? m.lcAttributeCaption + "Resource"
Endif
Endif
FreeSid(m.lcRestrictedSids)
Endfor
Endif
Else
? "GetTokenInformation Failed: TokenRestrictedSids" + CRLF + GetLastErrorMessage(GetLastError())
Endif
GlobalFree(m.lnRestrictedSids)
Endif
m.lnSize = 0
GetTokenInformationN(m.lnToken, TokenPrivileges, 0, 0, @m.lnSize)
If m.lnSize > 0
m.lnPrivileges = 0
m.lnPrivileges = GlobalAlloc(GPTR, m.lnSize)
If GetTokenInformationP(m.lnToken, TokenPrivileges, m.lnPrivileges, m.lnSize, @m.lnSize) != 0
m.lnPrivilegeCount = CToBin(Sys(2600, m.lnPrivileges, 4),"4rs")
? "Privilege Count: " + Transform(m.lnPrivilegeCount)
If m.lnPrivilegeCount > 0
For m.lnCounter = 1 To m.lnPrivilegeCount
m.lnPrivileges = m.lnPrivileges + 4
m.lnSize = 0
LookupPrivilegeName(Null, m.lnPrivileges, Null, @m.lnSize)
If m.lnSize > 0
m.lcPrivilegeName = Replicate(Chr(0), m.lnSize)
LookupPrivilegeName(Null, m.lnPrivileges, @m.lcPrivilegeName, @m.lnSize)
?"Privilege Name: " + Left(m.lcPrivilegeName, m.lnSize)
Endif
? "Privilege LUID (low part): " + Transform(CToBin(Sys(2600, m.lnPrivileges, 4),"4rs"))
m.lnPrivileges = m.lnPrivileges + 4
? "Privilege LUID (high part): " + Transform(CToBin(Sys(2600, m.lnPrivileges, 4),"4rs"))
m.lnPrivileges = m.lnPrivileges + 4
m.lnAttributes = CToBin(Sys(2600, m.lnPrivileges, 4),"4rs")
GetBitFlags(m.lnAttributes, "SID Attributes: ")
m.lcAttributeCaption = "Privilege" + Transform(m.lnCounter) + ": "
If m.lnAttributes = 0
? m.lcAttributeCaption + "Disabled"
Else
If Bitand(m.lnAttributes, SE_PRIVILEGE_ENABLED) != 0
If Bitand(m.lnAttributes, SE_PRIVILEGE_ENABLED_BY_DEFAULT ) != 0
? m.lcAttributeCaption + "Enabled By Default"
Else
? m.lcAttributeCaption + "Enabled"
Endif
Endif
If Bitand(m.lnAttributes, SE_PRIVILEGE_REMOVED) != 0
? m.lcAttributeCaption + "Removed"
Endif
If Bitand(m.lnAttributes, SE_PRIVILEGE_USED_FOR_ACCESS) != 0
? m.lcAttributeCaption + "Used For Access"
Endif
Endif
Endfor
Endif
Else
? "GetTokenInformation Failed: TokenPrivileges" + CRLF + GetLastErrorMessage(GetLastError())
Endif
ENDIF
m.lnSize = 0
GetTokenInformationN(m.lnToken, TokenDefaultDacl, 0, 0, @m.lnSize)
If m.lnSize > 0
m.lnDefaultDacl = 0
m.lnDefaultDacl = GlobalAlloc(GPTR, m.lnSize)
If GetTokenInformationP(m.lnToken, TokenDefaultDacl, m.lnDefaultDacl, m.lnSize, @m.lnSize) != 0
m.lnPACL = CToBin(Sys(2600, m.lnDefaultDacl, 4),"4rs")
? "Acl Revision: " + Transform(CToBin(Sys(2600, m.lnPACL, 1), "1RS"))
? "Sbz1: " + Transform(CToBin(Sys(2600, m.lnPACL + 1, 1), "1RS")) && padding to 16-bit boundary
m.lnSize = CToBin(Sys(2600, m.lnPACL + 2, 2), "2RS")
? "Acl Size: " + Transform(m.lnSize)
m.lnAceCount = CToBin(Sys(2600, m.lnPACL + 4, 2), "2RS")
? "Ace Count: " + Transform(m.lnAceCount)
? "Sbz2: " + Transform(CToBin(Sys(2600, m.lnPACL + 4, 1), "1RS")) && padding to 32-bit boundary
m.lnEntryCount = 0
m.lnPExplicitEntries = 0
If GetExplicitEntriesFromAcl(m.lnPACL, @m.lnEntryCount, @m.lnPExplicitEntries) = ERROR_SUCCESS
For m.lnCounter = 1 To m.lnEntryCount
m.lcExplicitEntry = Sys(2600, m.