原文连接:http://bbs.pediy.com/showthread.php?t=173381
当一个线程使用Open*打开一个内核对象时,会发生什么?
有两种可能:
1. 打开成功,拿到句柄
2. 打开失败
这不是废话么?!为啥打开失败呢?有两种可能:
1. 当前线程不具有指定的特权
2. 权限不足(由dwDesiredAccess参数指定权限)
这个时候就引入了今天的主题:令牌(包含特权列表)和安全描述符(描述用户权限)。
Token
token是什么?对了,他就是一个令牌。就像原先钦差大臣手里面的上方宝剑一样,上可惩天子,下可斩贪官!所以千万不要小看它哦亲~。那Token中都有些神马东东呢?
先看一下他的数据结构:
代码:
lkd> dt nt!_token +0x000 TokenSource : _TOKEN_SOURCE +0x010 TokenId : _LUID +0x018 AuthenticationId : _LUID +0x020 ParentTokenId : _LUID +0x028 ExpirationTime : _LARGE_INTEGER +0x030 TokenLock : Ptr32 _ERESOURCE +0x038 AuditPolicy : _SEP_AUDIT_POLICY +0x040 ModifiedId : _LUID +0x048 SessionId : Uint4B +0x04c UserAndGroupCount : Uint4B +0x050 RestrictedSidCount : Uint4B +0x054 PrivilegeCount : Uint4B +0x058 VariableLength : Uint4B +0x05c DynamicCharged : Uint4B +0x060 DynamicAvailable : Uint4B +0x064 DefaultOwnerIndex : Uint4B +0x068 UserAndGroups : Ptr32 _SID_AND_ATTRIBUTES +0x06c RestrictedSids : Ptr32 _SID_AND_ATTRIBUTES +0x070 PrimaryGroup : Ptr32 Void +0x074 Privileges : Ptr32 _LUID_AND_ATTRIBUTES +0x078 DynamicPart : Ptr32 Uint4B +0x07c DefaultDacl : Ptr32 _ACL +0x080 TokenType : _TOKEN_TYPE +0x084 ImpersonationLevel : _SECURITY_IMPERSONATION_LEVEL +0x088 TokenFlags : Uint4B +0x08c TokenInUse : UChar +0x090 ProxyData : Ptr32 _SECURITY_TOKEN_PROXY_DATA +0x094 AuditData : Ptr32 _SECURITY_TOKEN_AUDIT_DATA +0x098 OriginatingLogonSession : _LUID +0x0a0 VariablePart : Uint4B
会话Id,用户和组列表这些告诉我们Token是属于哪个用户的。
特权列表,这个是表示该Token都可以干哪些坏事,拥有的权限越多越牛B哈哈
我们既然知道了Token是何方神圣了,那谁可以拥有Token呢?对了,只有进程和线程才能拥有!
每一个进程都拥有一个Token(这个是必须有的),该Token叫 主令牌 。可以从父进程处继承,也可以是后来设置的,使用NtSetInformationProcess,Class=ProcessAccessToken。
每个线程可以拥有一个ImpersonationToken( 模仿令牌 )。该令牌不是必须的,线程可以不拥有模仿令牌。
由此看来一个线程最多拥有2个令牌:主令牌和模仿令牌,最少拥有一个令牌:主令牌。
设置线程的模仿令牌的方法:NtSetInformationThread, calss=ThreadImpersonationToken。
既然有了主令牌,模仿令牌有什么用呢?
当一个线程要加载一个驱动,但发现主令牌没有加载驱动特权,它就可以模仿主令牌,然后在模仿令牌中加入加载驱动特权
当一个线程想要打开一个文件,但是该文件只有User2用户才能打开,主令牌却是属于User1用户。这时候可以模仿一个User2用户的令牌,这样就可以打开该文件了。
下面我们看一下都有哪些特权 :
代码:
SeAssignPrimaryTokenPrivilege SeAuditPrivilege SeBackupPrivilege SeChangeNotifyPrivilege SeCreateGlobalPrivilege SeCreatePagefilePrivilege SeCreatePermanentPrivilege SeCreateSymbolicLinkPrivilege SeCreateTokenPrivilege SeDebugPrivilege SeEnableDelegationPrivilege SeImpersonatePrivilege SeIncreaseBasePriorityPrivilege SeIncreaseQuotaPrivilege SeIncreaseWorkingSetPrivilege SeLoadDriverPrivilege SeLockMemoryPrivilege SeMachineAccountPrivilege SeManageVolumePrivilege SeProfileSingleProcessPrivilege SeRelabelPrivilege SeRemoteShutdownPrivilege SeRestorePrivilege SeSecurityPrivilege SeShutdownPrivilege SeSyncAgentPrivilege SeSystemEnvironmentPrivilege SeSystemProfilePrivilege SeSystemtimePrivilege SeTakeOwnershipPrivilege SeTcbPrivilege SeTimeZonePrivilege SeTrustedCredManAccessPrivilege SeUndockPrivilege SeUnsolicitedInputPrivilege
简单说明一下常见的。其他的看MSDN吧: http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx
SeCreateGlobalPrivilege
创建全局的特权。这个特权直接影响到你可不可以创建一个有名字的全局事件、互斥量、内存区等。
SeDebugPrivilege
调试特权。顾名思义,跟调试有关。该特权直接影响到你能否打开系统进程如system、winlogin.exe等,并调试他们。
SeLoadDriverPrivilege
加载驱动特权。想加载驱动?那就必须有改权限。
SeImpersonatePrivilege
模仿特权。要想设置线程的模仿令牌,就要有该权限
SeCreateTokenPrivilege
创建令牌特权。没有该特权,就无法创建一个新的令牌。
一个Token中的特权列表中,已经指明该Token可以拥有哪些特权。不过有些特权并不是置位有效的,如果要使用该特权,需要使用SetTokenInformation修改。但是,特权列表中没有的特权,是不能添加进去的(当然你非要添加的话还是有办法的)!
下面我们用windbg看一下windbg.exe的Token
代码:
lkd> !process 0 1 windbg.exe PROCESS 89031da0 SessionId: 0 Cid: 0c68 Peb: 7ffdb000 ParentCid: 0530 DirBase: 09f602e0 ObjectTable: e1f25ba0 HandleCount: 86. Image: windbg.exe VadRoot 88c553c0 Vads 87 Clone 0 Private 2296. Modified 3959. Locked 1. DeviceMap e20447c8 Token e108d258 ElapsedTime 02:26:28.813 UserTime 00:00:01.171 KernelTime 00:00:06.078 QuotaPoolUsage[PagedPool] 92740 QuotaPoolUsage[NonPagedPool] 3600 Working Set Sizes (now,min,max) (1803, 50, 345) (7212KB, 200KB, 1380KB) PeakWorkingSetSize 3848 VirtualSize 60 Mb PeakVirtualSize 61 Mb PageFaultCount 7861 MemoryPriority BACKGROUND BasePriority 8 CommitCharge 2704 lkd> !token e108d258 _TOKEN e108d258 TS Session ID: 0 User: S-1-5-21-2025429265-1682526488-1801674531-1005 Groups: 00 S-1-5-21-2025429265-1682526488-1801674531-513 Attributes - Mandatory Default Enabled 01 S-1-1-0 Attributes - Mandatory Default Enabled 02 S-1-5-32-544 Attributes - Mandatory Default Enabled Owner 03 S-1-5-32-545 Attributes - Mandatory Default Enabled 04 S-1-5-4 Attributes - Mandatory Default Enabled 05 S-1-5-11 Attributes - Mandatory Default Enabled 06 S-1-5-5-0-5125624 Attributes - Mandatory Default Enabled LogonId 07 S-1-2-0 Attributes - Mandatory Default Enabled Primary Group: S-1-5-21-2025429265-1682526488-1801674531-513 Privs: 00 0x000000017 SeChangeNotifyPrivilege Attributes - Enabled Default 01 0x000000008 SeSecurityPrivilege Attributes - 02 0x000000011 SeBackupPrivilege Attributes - 03 0x000000012 SeRestorePrivilege Attributes - 04 0x00000000c SeSystemtimePrivilege Attributes - 05 0x000000013 SeShutdownPrivilege Attributes - 06 0x000000018 SeRemoteShutdownPrivilege Attributes - 07 0x000000009 SeTakeOwnershipPrivilege Attributes - 08 0x000000014 SeDebugPrivilege Attributes - Enabled 09 0x000000016 SeSystemEnvironmentPrivilege Attributes - 10 0x00000000b SeSystemProfilePrivilege Attributes - 11 0x00000000d SeProfileSingleProcessPrivilege Attributes - 12 0x00000000e SeIncreaseBasePriorityPrivilege Attributes - 13 0x00000000a SeLoadDriverPrivilege Attributes - Enabled 14 0x00000000f SeCreatePagefilePrivilege Attributes - 15 0x000000005 SeIncreaseQuotaPrivilege Attributes - 16 0x000000019 SeUndockPrivilege Attributes - Enabled 17 0x00000001c SeManageVolumePrivilege Attributes - 18 0x00000001d SeImpersonatePrivilege Attributes - Enabled Default 19 0x00000001e SeCreateGlobalPrivilege Attributes - Enabled Default Authentication ID: (0,4e3f87) Impersonation Level: Anonymous TokenType: Primary Source: User32 TokenFlags: 0x89 ( Token in use ) Token ID: 4f3891 ParentToken ID: 0 Modified ID: (0, 4ffaa5) RestrictedSidCount: 0 RestrictedSids: 00000000
SecurityDescriptor
安全描述符(sd)是访问控制模型的灵魂。所有的内核对象都拥有安全描述符。包括Token内核对象。
先看一下安全描述符的结构:
代码:
lkd> dt nt!_Security_Descriptor
+0x000 Revision : UChar
+0x001 Sbz1 : UChar
+0x002 Control : Uint2B
+0x004 Owner : Ptr32 Void
+0x008 Group : Ptr32 Void
+0x00c Sacl : Ptr32 _ACL
+0x010 Dacl : Ptr32 _ACL
代码:
lkd> dt nt!_Object_Header +0x000 PointerCount : Int4B +0x004 HandleCount : Int4B +0x004 NextToFree : Ptr32 Void +0x008 Type : Ptr32 _OBJECT_TYPE +0x00c NameInfoOffset : UChar +0x00d HandleInfoOffset : UChar +0x00e QuotaInfoOffset : UChar +0x00f Flags : UChar +0x010 ObjectCreateInfo : Ptr32 _OBJECT_CREATE_INFORMATION +0x010 QuotaBlockCharged : Ptr32 Void +0x014 SecurityDescriptor : Ptr32 Void +0x018 Body : _QUAD
先简要介绍一下sd。 sd中描述了用户Sid,DACL、SACL。
SACL是系统访问控制列表,是用来做审计用的,一般不用关心。
DACL是自主访问控制列表,记录了哪些用户可以(/不可以)以何种方式访问该对象。
安全描述符查看方法:
1. 在对象上查看其属性。如文件,点击右键选择“属性”,找到“安全”选项卡,点击“高级”按钮。弹出的对话筐中,“权限”选项卡就是DACL,"审核"选项卡是SACL,“所有者”是Owner、Group。
2. 用windbg的!sd指令
代码:
lkd> !object \BaseNamedObjects\ShimSharedMemory Object: e1acd3a8 Type: (896732b0) Section ObjectHeader: e1acd390 (old version) HandleCount: 15 PointerCount: 16 Directory Object: e15289d0 Name: ShimSharedMemory lkd> !sd poi(e1acd3a8-4)&ffffffff8 ReadVirtual: e1a94680 not properly sign extended ReadVirtual: e1a94680 not properly sign extended ReadVirtual: e1a94690 not properly sign extended ReadVirtual: e1a94690 not properly sign extended ReadVirtual: e1a94664 not properly sign extended ReadVirtual: e1a94664 not properly sign extended ->Revision: 0x1 ->Sbz1 : 0x0 ->Control : 0x8004 SE_DACL_PRESENT SE_SELF_RELATIVE ->Owner : S-1-5-32-544 ->Group : S-1-5-18 ->Dacl : ->Dacl : ->AclRevision: 0x2 ->Dacl : ->Sbz1 : 0x0 ->Dacl : ->AclSize : 0x1c ->Dacl : ->AceCount : 0x1 ->Dacl : ->Sbz2 : 0x0 ->Dacl : ->Ace[0]: ->AceType: ACCESS_ALLOWED_ACE_TYPE ->Dacl : ->Ace[0]: ->AceFlags: 0x0 ->Dacl : ->Ace[0]: ->AceSize: 0x14 ->Dacl : ->Ace[0]: ->Mask : 0x0003001f ->Dacl : ->Ace[0]: ->SID: S-1-1-0 ->Sacl : is NULL
1. 对象的DACL==Null,则线程拥有完全的访问权限。
2. 对象的DACL不为Null,但是AceCount ==0(ACE,访问控制项),则拒绝任何线程访问。
3. 遍历DACL,找到跟令牌中用户或组一致的Ace,如果该Ace指明没有拥有制定的访问权限,则直接退出安全检查函数,并拒绝该线程访问。
4. 遍历DACL,没找到跟令牌中用户或组一致的Ace,并拒绝该线程访问。
5. 遍历DACL,找到跟令牌中用户或组一致的Ace,如果该Ace指明拥有制定的访问权限,则直接退出安全检查函数,并允许该线程访问。
为了更好的理解sd,给大家举个例子:
代码:
#include <windows.h> #include <sddl.h> BOOL InitSecurityDescriptor( SECURITY_ATTRIBUTES& sa, TCHAR* pszSD ) { sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = FALSE; return ConvertStringSecurityDescriptorToSecurityDescriptor( pszSD, SDDL_REVISION_1, &(sa.lpSecurityDescriptor), NULL); } void ReleaseSecurityDescriptor( SECURITY_ATTRIBUTES& sa ) { LocalFree(sa.lpSecurityDescriptor); } int _tmain(int argc, _TCHAR* argv[]) { SECURITY_ATTRIBUTES sa; //创建一个局部名字事件(由于是局部的,多会话时,每个会话都创建一个同名的事件) //第一个参数SECURITY_ATTRIBUTES传NULL,则使用进程TOKEN的默认DACL。 //默认DACL有两个ACE //ACE1: 当前用户,拥有全部权限(读、写) //ACE2: system用户,拥有全部权限(读、写) HANDLE hEvent1 = CreateEvent( NULL, FALSE, FALSE, TEXT("Event1")); //创建一个局部名字事件,与上面是等价的 //"D:(D;;GR;;;WD)(A;;GW;;;WD)" 的意思是: DACL添加Everyone用户,拒绝读,允许写权限 InitSecurityDescriptor( sa, TEXT("D:(D;;GR;;;WD)(A;;GW;;;WD)") ); HANDLE hEvent2 = CreateEvent( &sa, FALSE, FALSE, TEXT("Local\\Event2")); ReleaseSecurityDescriptor(sa); HANDLE hEventX = OpenEvent(EVENT_MODIFY_STATE, FALSE, TEXT("Local\\Event2")); if( hEventX != NULL ) { printf( "进入这里,有权限\n" ); CloseHandle(hEventX); } hEventX = OpenEvent(EVENT_ALL_ACCESS, FALSE, TEXT("Local\\Event2")); if( hEventX == NULL ) { printf( "进入这里,无权限。gle:%d\n", GetLastError() ); } //创建一个全局名字事件 //由于其只给当前用户和system分配了权限,所以虽然是全局事件,但是其他会话也是无法访问的 HANDLE hEvent3 = CreateEvent( NULL, FALSE, FALSE, TEXT("Global\\Event3")); //手动创建一个SECURITY_ATTRIBUTES,使用SDDL语法 //D:(A;;GA;;;WD) 的意思是: DACL添加Everyone用户,并让其拥有全部权限 InitSecurityDescriptor( sa, TEXT("D:(A;;GA;;;WD)") ); //创建一个全局名字事件,可以被其他会话访问 HANDLE hEvent4 = CreateEvent( &sa, FALSE, FALSE, TEXT("Global\\Event4")); ReleaseSecurityDescriptor(sa); printf( "%p, %p, %p, %p\n", hEvent1, hEvent2, hEvent3, hEvent4 ); getchar(); CloseHandle(hEvent1); CloseHandle(hEvent2); CloseHandle(hEvent3); CloseHandle(hEvent4); return 0; }
代码:
lkd> !object \Sessions\1\BaseNamedObjects\Event1 Object: 89584ac8 Type: (896762f8) Event ObjectHeader: 89584ab0 (old version) HandleCount: 1 PointerCount: 2 Directory Object: e19721e0 Name: Event1 lkd> !sd poi(89584ac8-4)&fffffff8 ->Revision: 0x1 ->Sbz1 : 0x0 ->Control : 0x8004 SE_DACL_PRESENT SE_SELF_RELATIVE ->Owner : S-1-5-21-2025429265-1682526488-1801674531-1006//当前用户Sid ->Group : S-1-5-21-2025429265-1682526488-1801674531-513//管理员组Sid ->Dacl : ->Dacl : ->AclRevision: 0x2 ->Dacl : ->Sbz1 : 0x0 ->Dacl : ->AclSize : 0x64 ->Dacl : ->AceCount : 0x3 ->Dacl : ->Sbz2 : 0x0 ->Dacl : ->Ace[0]: ->AceType: ACCESS_ALLOWED_ACE_TYPE//允许 ->Dacl : ->Ace[0]: ->AceFlags: 0x0 ->Dacl : ->Ace[0]: ->AceSize: 0x14 ->Dacl : ->Ace[0]: ->Mask : 0x001f0003//完全控制权限 ->Dacl : ->Ace[0]: ->SID: S-1-5-18//system Sid ->Dacl : ->Ace[1]: ->AceType: ACCESS_ALLOWED_ACE_TYPE ->Dacl : ->Ace[1]: ->AceFlags: 0x0 ->Dacl : ->Ace[1]: ->AceSize: 0x24 ->Dacl : ->Ace[1]: ->Mask : 0x001f0003 ->Dacl : ->Ace[1]: ->SID: S-1-5-21-2025429265-1682526488-1801674531-1006//会话1用户的Sid ->Dacl : ->Ace[2]: ->AceType: ACCESS_ALLOWED_ACE_TYPE ->Dacl : ->Ace[2]: ->AceFlags: 0x0 ->Dacl : ->Ace[2]: ->AceSize: 0x24 ->Dacl : ->Ace[2]: ->Mask : 0x001f0003 ->Dacl : ->Ace[2]: ->SID: S-1-5-21-2025429265-1682526488-1801674531-1006 ->Sacl : is NULL lkd> !object \Sessions\1\BaseNamedObjects\Event2 Object: 893db418 Type: (896762f8) Event ObjectHeader: 893db400 (old version) HandleCount: 1 PointerCount: 2 Directory Object: e19721e0 Name: Event2 lkd> !sd poi(893db418-4)&fffffff8 ->Revision: 0x1 ->Sbz1 : 0x0 ->Control : 0x8004 SE_DACL_PRESENT SE_SELF_RELATIVE ->Owner : S-1-5-21-2025429265-1682526488-1801674531-1006 ->Group : S-1-5-21-2025429265-1682526488-1801674531-513 ->Dacl : ->Dacl : ->AclRevision: 0x2 ->Dacl : ->Sbz1 : 0x0 ->Dacl : ->AclSize : 0x30 ->Dacl : ->AceCount : 0x2 ->Dacl : ->Sbz2 : 0x0 ->Dacl : ->Ace[0]: ->AceType: ACCESS_DENIED_ACE_TYPE//拒绝 ->Dacl : ->Ace[0]: ->AceFlags: 0x0 ->Dacl : ->Ace[0]: ->AceSize: 0x14 ->Dacl : ->Ace[0]: ->Mask : 0x00020001//读权限 ->Dacl : ->Ace[0]: ->SID: S-1-1-0//Everyone SID ->Dacl : ->Ace[1]: ->AceType: ACCESS_ALLOWED_ACE_TYPE ->Dacl : ->Ace[1]: ->AceFlags: 0x0 ->Dacl : ->Ace[1]: ->AceSize: 0x14 ->Dacl : ->Ace[1]: ->Mask : 0x00020002//写权限 ->Dacl : ->Ace[1]: ->SID: S-1-1-0 ->Sacl : is NULL lkd> !object \BaseNamedObjects\Event3 Object: 8945d7d0 Type: (896762f8) Event ObjectHeader: 8945d7b8 (old version) HandleCount: 1 PointerCount: 2 Directory Object: e15289d0 Name: Event3 lkd> !sd poi(8945d7d0-4)&fffffff8 ->Revision: 0x1 ->Sbz1 : 0x0 ->Control : 0x8004 SE_DACL_PRESENT SE_SELF_RELATIVE ->Owner : S-1-5-21-2025429265-1682526488-1801674531-1006 ->Group : S-1-5-21-2025429265-1682526488-1801674531-513 ->Dacl : ->Dacl : ->AclRevision: 0x2 ->Dacl : ->Sbz1 : 0x0 ->Dacl : ->AclSize : 0x40 ->Dacl : ->AceCount : 0x2 ->Dacl : ->Sbz2 : 0x0 ->Dacl : ->Ace[0]: ->AceType: ACCESS_ALLOWED_ACE_TYPE ->Dacl : ->Ace[0]: ->AceFlags: 0x0 ->Dacl : ->Ace[0]: ->AceSize: 0x24 ->Dacl : ->Ace[0]: ->Mask : 0x001f0003 ->Dacl : ->Ace[0]: ->SID: S-1-5-21-2025429265-1682526488-1801674531-1006 ->Dacl : ->Ace[1]: ->AceType: ACCESS_ALLOWED_ACE_TYPE ->Dacl : ->Ace[1]: ->AceFlags: 0x0 ->Dacl : ->Ace[1]: ->AceSize: 0x14 ->Dacl : ->Ace[1]: ->Mask : 0x001f0003 ->Dacl : ->Ace[1]: ->SID: S-1-5-18 ->Sacl : is NULL lkd> !object \BaseNamedObjects\Event4 Object: 8912ca78 Type: (896762f8) Event ObjectHeader: 8912ca60 (old version) HandleCount: 1 PointerCount: 2 Directory Object: e15289d0 Name: Event4 lkd> !sd poi(8912ca78-4)&fffffff8 ->Revision: 0x1 ->Sbz1 : 0x0 ->Control : 0x8004 SE_DACL_PRESENT SE_SELF_RELATIVE ->Owner : S-1-5-21-2025429265-1682526488-1801674531-1006 ->Group : S-1-5-21-2025429265-1682526488-1801674531-513 ->Dacl : ->Dacl : ->AclRevision: 0x2 ->Dacl : ->Sbz1 : 0x0 ->Dacl : ->AclSize : 0x1c ->Dacl : ->AceCount : 0x1 ->Dacl : ->Sbz2 : 0x0 ->Dacl : ->Ace[0]: ->AceType: ACCESS_ALLOWED_ACE_TYPE ->Dacl : ->Ace[0]: ->AceFlags: 0x0 ->Dacl : ->Ace[0]: ->AceSize: 0x14 ->Dacl : ->Ace[0]: ->Mask : 0x001f0003 ->Dacl : ->Ace[0]: ->SID: S-1-1-0 ->Sacl : is NULL