Enumerating Registry Key Permissions
Posted: Fri Mar 30, 2012 8:05 pm
Nothing special, just a snippet of C++ code from work for a larger project that I quickly converted to PureBasic and stuffed it in a procedure. Thought it might help someone. Should be easy to adapt to most needs. Gets the account permissions for a registry key (actually a sub key of a main hive key). This is raw code, not refined in any way, there will be clean up and changes in my final code (see some of the notes in the code, they might give you some ideas too so i left some of the notes in). Only been tested on Windows 7 x86, didn't test on 64 bit yet.
Note: Just changed the post code to remove the extra calls to the procedure in the enum loop, left 'em in by accident while i was checking something else, sorry 'bout that
Code: Select all
Structure SID
Revision.b
SubAuthorityCount.b
*IdentifierAuthority.SID_IDENTIFIER_AUTHORITY
SubAuthority.l[#ANYSIZE_ARRAY]
EndStructure
Enumeration ;ACL_INFORMATION_CLASS
#AclRevisionInformation = 1
#AclSizeInformation
EndEnumeration
Enumeration ; SID_NAME_USE
#SidTypeUser = 1
#SidTypeGroup
#SidTypeDomain
#SidTypeAlias
#SidTypeWellKnownGroup
#SidTypeDeletedAccount
#SidTypeInvalid
#SidTypeUnknown
#SidTypeComputer
#SidTypeLabel
EndEnumeration
#DACL_SECURITY_INFORMATION = $00000004 ; Include the discretionary access control List (DACL).
Procedure.q TopHiveKey(HiveKeyConvert$)
Protected HiveKeyx.q
If Right(HiveKeyConvert$, 1) = "\"
HiveKeyConvert$ = RemoveString(HiveKeyConvert$, "\", #PB_String_NoCase, Len(HiveKeyConvert$) - 1, 1)
EndIf
HiveKeya$=StringField(HiveKeyConvert$,1,"\")
HiveKeyTop$=UCase(HiveKeya$)
Select HiveKeyTop$
Case "HKEY_CLASSES_ROOT"
HiveKeyx = #HKEY_CLASSES_ROOT
Case "HKEY_CURRENT_USER"
HiveKeyx = #HKEY_CURRENT_USER
Case "HKEY_LOCAL_MACHINE"
HiveKeyx = #HKEY_LOCAL_MACHINE
Case "HKEY_USERS"
HiveKeyx = #HKEY_USERS
Case "HKEY_CURRENT_CONFIG"
HiveKeyx = #HKEY_CURRENT_CONFIG
Default
HiveKeyx = #Null
EndSelect
ProcedureReturn HiveKeyx
EndProcedure
Procedure.s KeyConvert(KeyToConvert$)
If Right(KeyToConvert$, 1) = "\"
KeyToConvert$ = RemoveString(KeyToConvert$, "\", #PB_String_NoCase, Len(KeyToConvert$) - 1, 1)
EndIf
GetBackSlash=FindString(KeyToConvert$,"\",1)
KeyNameX$=Right(KeyToConvert$,(Len(KeyToConvert$)-GetBackSlash))
If Left(KeyNameX$, 1) = "\"
KeyNameX$ = Right(KeyNameX$, Len(KeyNameX$) - 1)
EndIf
ProcedureReturn KeyNameX$
EndProcedure
Procedure.s Reg_EnumAccountKeyAccessPermission(Key.s, sel_out.i, sid_count.i)
Protected HiveKey.q, KeyName$, bRtnBool.b, FuncRet.i, retfunc.i, hKey.i
Protected *pSecDesc.SECURITY_DESCRIPTOR, *pDacl.ACL, *pAce.ACCESS_ALLOWED_ACE, aclSize.ACL_SIZE_INFORMATION
Protected szAccountName.s, szDomainName.s, dwAccountNameSize.i, dwDomainNameSize.i, snu.i
Protected bDaclDefault.b, bDaclPresent.b, nLengthNeeded.l = 0, aclAceCount.i, acl_ACECount.i
HiveKey = TopHiveKey(Key)
KeyName$ = KeyConvert(Key)
FuncRet = RegOpenKeyEx_(HiveKey, @KeyName$, 0, #KEY_ALL_ACCESS, @hKey)
If FuncRet = #ERROR_SUCCESS
retfunc = RegGetKeySecurity_(hKey, #DACL_SECURITY_INFORMATION, #Null, @nLengthNeeded)
If retfunc = #ERROR_INSUFFICIENT_BUFFER
*pSecDesc = LocalAlloc_(#LPTR, nLengthNeeded) ; can't use LocalAlloc in final - swap for heap function in final
If *pSecDesc = #Null
ProcedureReturn ""
EndIf
retfunc = RegGetKeySecurity_(hKey, #DACL_SECURITY_INFORMATION, *pSecDesc, @nLengthNeeded)
EndIf
If retfunc = #ERROR_SUCCESS
If *pSecDesc
GetSecurityDescriptorDacl_(*pSecDesc, @bDaclPresent, @*pDacl, @bDaclDefault)
If bDaclPresent = 0
RegCloseKey_(hKey)
ProcedureReturn "No DACL"
Else
GetAclInformation_(*pDacl, @aclSize, SizeOf(aclSize), #AclSizeInformation)
If sel_out = 0
acl_ACECount = aclSize\AceCount
LocalFree_(*pSecDesc); can't use LocalFree in final - swap for heap function in final
RegCloseKey_(hKey)
EndIf
If sel_out > 0 And sel_out < 4
;For aclAceCount = 0 To aclSize\AceCount
GetAce_(*pDacl, sid_count, @*pAce)
bRtnBool = LookupAccountSid_(#Null, @*pAce\SidStart, 0, @dwAccountNameSize, 0, @dwDomainNameSize, @snu)
szAccountName = Space(dwAccountNameSize)
szDomainName = Space(dwDomainNameSize)
bRtnBool = LookupAccountSid_(#Null, @*pAce\SidStart, @szAccountName, @dwAccountNameSize, @szDomainName, @dwDomainNameSize, @snu)
; If-EndIf section below to be replaced by array later
; Specific Access Rights
If *pAce\Mask & #KEY_QUERY_VALUE = #KEY_QUERY_VALUE
SpecificAcess$ + "KEY_QUERY_VALUE - "
If *pAce\Mask & #KEY_SET_VALUE = #KEY_SET_VALUE
SpecificAcess$ + "KEY_SET_VALUE - "
If *pAce\Mask & #KEY_CREATE_SUB_KEY = #KEY_CREATE_SUB_KEY
SpecificAcess$ + "KEY_CREATE_SUB_KEY - "
If *pAce\Mask & #KEY_ENUMERATE_SUB_KEYS = #KEY_ENUMERATE_SUB_KEYS
SpecificAcess$ + "KEY_ENUMERATE_SUB_KEYS - "
If *pAce\Mask & #KEY_NOTIFY = #KEY_NOTIFY
SpecificAcess$ + "KEY_NOTIFY - "
If *pAce\Mask & #KEY_CREATE_LINK = #KEY_CREATE_LINK
SpecificAcess$ + "KEY_CREATE_LINK - "
If *pAce\Mask & #KEY_READ = #KEY_READ
SpecificAcess$ + "KEY_READ - " ; ;KEY_EXECUTE (0x20019)
If *pAce\Mask & #KEY_WRITE = #KEY_WRITE
SpecificAcess$ + "KEY_WRITE - "
If *pAce\Mask & #KEY_EXECUTE = #KEY_EXECUTE
SpecificAcess$ + "KEY_EXECUTE - " ; same As KEY_READ (0x20019)
If *pAce\Mask & #KEY_ALL_ACCESS = #KEY_ALL_ACCESS
SpecificAcess$ + "KEY_ALL_ACCESS - "
EndIf
EndIf
EndIf
EndIf
EndIf
EndIf
EndIf
EndIf
EndIf
EndIf
SpecificAcess$ = RemoveString(SpecificAcess$, " - ", #PB_String_NoCase, Len(SpecificAcess$) - 3, 1) ; goes away later with string formatting
;Standard Access Rights
If *pAce\Mask & #DELETE = #DELETE
StandardAcess$ + "DELETE - "
If *pAce\Mask & #READ_CONTROL = #READ_CONTROL
StandardAcess$ + "READ_CONTROL - "
If *pAce\Mask & #WRITE_DAC = #WRITE_DAC
StandardAcess$ + "WRITE_DAC - "
If *pAce\Mask & #WRITE_OWNER = #WRITE_OWNER
StandardAcess$ + "WRITE_OWNER - "
If *pAce\Mask & #SYNCHRONIZE = #SYNCHRONIZE
StandardAcess$ + "SYNCHRONIZE - "
If *pAce\Mask & #STANDARD_RIGHTS_REQUIRED = #STANDARD_RIGHTS_REQUIRED
StandardAcess$ + "STANDARD_RIGHTS_REQUIRED - "
If *pAce\Mask & #STANDARD_RIGHTS_READ = #STANDARD_RIGHTS_READ
StandardAcess$ + "STANDARD_RIGHTS_READ - "
If *pAce\Mask & #STANDARD_RIGHTS_WRITE = #STANDARD_RIGHTS_WRITE
StandardAcess$ + "STANDARD_RIGHTS_WRITE - "
If *pAce\Mask & #STANDARD_RIGHTS_EXECUTE = #STANDARD_RIGHTS_EXECUTE
StandardAcess$ + "STANDARD_RIGHTS_EXECUTE - "
If *pAce\Mask & #STANDARD_RIGHTS_ALL = #STANDARD_RIGHTS_ALL
StandardAcess$ + "STANDARD_RIGHTS_ALL - "
If *pAce\Mask & #SPECIFIC_RIGHTS_ALL = #SPECIFIC_RIGHTS_ALL
StandardAcess$ + "SPECIFIC_RIGHTS_ALL - "
EndIf
EndIf
EndIf
EndIf
EndIf
EndIf
EndIf
EndIf
EndIf
EndIf
EndIf
StandardAcess$ = RemoveString(StandardAcess$, " - ", #PB_String_NoCase, Len(StandardAcess$) - 3, 1); goes away later with string formatting
; ************* If-EndIf section above to be replaced by array later ********
; ************below not used this implementation - later use in framework
;Select snu
;Case #SidTypeUser
;sidtypeuse$ = "User SID"
;Case #SidTypeGroup
;sidtypeuse$ = "Group SID"
;Case #SidTypeDomain
;sidtypeuse$ = "Domain SID"
;Case #SidTypeAlias
;sidtypeuse$ = "Alias SID"
;Case #SidTypeWellKnownGroup
;sidtypeuse$ = "SID for a well-known group"
;Case #SidTypeDeletedAccount
;sidtypeuse$ = "SID for a deleted account"
;Case #SidTypeInvalid
;sidtypeuse$ = "SID that is not valid"
;Case #SidTypeUnknown
;sidtypeuse$ = "SID of unknown type"
;Case #SidTypeComputer
;sidtypeuse$ = "SID for a computer"
;Case #SidTypeLabel
;sidtypeuse$ = "Mandatory integrity label SID"
;Default
;sidtypeuse$ = "SID of unknown type"
;EndSelect
;************** end section not used this implementation
Select *pAce\Header\AceType
Case #ACCESS_ALLOWED_ACE_TYPE
AceType$ = "ACCESS_ALLOWED_ACE_TYPE"
;Break
Case #ACCESS_DENIED_ACE_TYPE
AceType$ = "ACCESS_DENIED_ACE_TYPE"
;Break
Case #SYSTEM_AUDIT_ACE_TYPE
AceType$ = "SYSTEM_AUDIT_ACE_TYPE"
;Break
Default
AceType$ = "Unknown ACE type"
;Break
EndSelect
;Next aclAceCount
LocalFree_(*pSecDesc); can't use LocalFree in final - swap for heap function in final
RegCloseKey_(hKey)
EndIf
EndIf
EndIf
EndIf
EndIf
Select sel_out
Case 0
procret$ = Str(acl_ACECount)
Case 1
procret$ = "Account = " + szAccountName + " Domain = " + szDomainName + " Specific Access Rights = " + SpecificAcess$ ; specific access rights
Case 2
procret$ = "Account = " + szAccountName + " Domain = " + szDomainName + " Standard Access Rights = " + StandardAcess$ ; standard access rights
Case 3
procret$ = "Account = " + szAccountName + " Domain = " + szDomainName + " ACE Type = " + AceType$; ACE Type
Default ; just for form and in case
procret$ = "Invalid output selection."
EndSelect
ProcedureReturn procret$
EndProcedure
; Reg_EnumAccountKeyAccessPermission(Key.s, sel_out.i, sid_count.i)
; Where: Key.s is the registry key we want permissions for - sel_out is the return we want (see last select -endselect in code) - sid_count is the account we are getting information for (0 to max available)
; first, get the acl ace count to find the max number of accounts with DACL we can query
acecnt_max_available.i = Val(Reg_EnumAccountKeyAccessPermission("HKEY_CURRENT_USER\Control Panel\TestKey", 0, 0))
Debug "Maximum available accounts with DACL = " + Str(acecnt_max_available) ; the number of accounts - the first account starts at 0 and goes to the max 'acecnt' (max available)
Debug ""
; now the loop to enum
For i = 0 To acecnt_max_available - 1
Debug Reg_EnumAccountKeyAccessPermission("HKEY_CURRENT_USER\Control Panel", 1, i)
Debug ""
Debug Reg_EnumAccountKeyAccessPermission("HKEY_CURRENT_USER\Control Panel", 2, i)
Debug ""
Debug Reg_EnumAccountKeyAccessPermission("HKEY_CURRENT_USER\Control Panel", 3, i)
Debug ""
Next
; notice the rem'd out loop in the code, thats for putting the stuff in an array later - and removing the 'sid_count' parameter - not implemented yet