淺析Windows的訪問權限檢查機制
0x00 簡介
在操作系統中,當我們提到安全的時候,意味著有一些資源需要被保護,在Windows操作系統中,這些被保護的資源大多以對象(Object)的形式存在,對象是對資源的一種抽象。每個對象都可以擁有自己的安全描述符(Security Descriptor),用來描述它能夠被誰、以何種方式而訪問。這些對象是客體,那么訪問這些對象的主體是什么呢?這些主體就是操作系統中的各個進程,更準確地說是這些進程中的每個線程。每個進程都有一個基本令牌 (Primary Token),可以被進程中的每個線程所共享,而有些線程比較特殊,它們正在模擬(Impersonating)某個客戶端的身份,因此擁有一個模擬令牌(Impersonation Token),對于這些線程來說,有效令牌就是模擬令牌,而其他線程的有效令牌則是其所屬進程的基本令牌。當主體嘗試去訪問客體時,操作系統會執行訪問權限檢查(Access Check),具體來說,是用主體的有效令牌與客體的安全描述符進行比對,從而確定該次訪問是否合法。為了提高效率,訪問權限檢查(Access Check)提供了一種緩存機制,即只有當一個對象被一個進程創建或者打開時,才會進行訪問權限檢查,訪問權限檢查的結果會被緩存到進程的句柄表(Handle Table)中,該進程對該對象的后續操作只需要查詢句柄表中內容即可確定訪問的合法性,而不必每次都執行開銷更大的訪問權限檢查(Access Check)。因為對象和進程都有各自的繼承層次,所以對象的安全描述符和進程的令牌從邏輯上講也是可以繼承的,比如文件系統中的文件可以繼承其所在目錄的安全描述符,子進程可以繼承父進程的令牌,這種繼承機制使讓對象和進程擁有了缺省的安全屬性,因此,在一個系統中的安全描述符和令牌的實例數目非常有限,大多數情況下是從父輩們繼承過來的缺省值。

0x01 訪問權限檢查
訪問權限檢查包含了多個維度上的檢查:
DACL檢查
DACL(自主訪問控制表,Discretionary Access Control List)檢查是最基本的訪問權限檢查。

在安全描述符中存在著一個DACL表(見安全描述符圖解),里面描述了擁有何種身份的主體申請的何種訪問權限會被允許或者拒絕。DACL表中的每個表項擁有如下的結構:
We'll define the structure of the predefined ACE types. Pictorally
the structure of the predefined ACE's is as follows:3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+---------------+-------+-------+---------------+---------------+
| AceFlags | Resd |Inherit| AceSize | AceType |
+---------------+-------+-------+---------------+---------------+
| Mask |
+---------------------------------------------------------------+
| |
+ +
| |
+ Sid +
| |
+ +
| |
+---------------------------------------------------------------+Mask is the access mask associated with the ACE. This is either the
access allowed, access denied, audit, or alarm mask.Sid is the Sid associated with the ACE.
其中,對于DACL來說,AceType可以有
#define ACCESS_ALLOWED_ACE_TYPE (0x0)
#define ACCESS_DENIED_ACE_TYPE (0x1)
兩種可能;Mask是一個代表相關權限集合的位圖;主體的身份使用Sid(Security Identifier)來表示,這是一個變長的數據結構:
kd> dt nt!_SID
+0x000 Revision : UChar
+0x001 SubAuthorityCount : UChar
+0x002 IdentifierAuthority : _SID_IDENTIFIER_AUTHORITY
+0x008 SubAuthority : [1] Uint4B
Sid是Windows安全系統中一個基本類型,可以用來唯一標識多種不同種類的實體,比如標識用戶身份與組身份,標識完整性級別、可信賴級別,AppContainer中的Capability等等。
DACL檢查過程是這樣的,如果對象的安全描述符為空,代表著這個對象不受任何保護,即可以被任何令牌代表的主體訪問;如果對象擁有一個非空的安全描述符,但是安全描述符中的Dacl成員為空,代表該對象沒有顯式地賦予任何主體任何訪問權限,即不允許被訪問;如果Dacl成員非空,就按照表中的順序逐一與當前主體的身份Sid進行比對,直到該主體所請求的權限被全部顯式允許,或者某一請求的權限被顯式地拒絕,檢查結束;如果直到Dacl表檢查結束,主體申請的權限仍未被全部顯式允許,那么仍然代表拒絕。
根據以上規則可知,DACL中各個表項的順序對訪問權限檢查的結果至關重要,比如說有一條有關某主體的顯式拒絕的表項位于表的最后,如果位于其之前的表項已經顯式允許了這一主體申請的權限,那么這條顯式拒絕的表項將起不到任何作用。基于此種原因,如果通過Windows右鍵安全選項菜單添加的DACL表項,顯式拒絕的表項永遠被置于顯式允許表項前面,以保證其有效性。直接通過API添加則不受此種限制。
特權(Privileges)以及超級特權(Super Privileges)檢查
所謂特權,是指那些不與某一具體對象相關的、影響整個系統的訪問權限。而超級特權是這樣的一些特權,你一旦擁有其中的一項超級特權,就擁有了取得所有其他特權和權限的潛質,比如SeDebugPrivilege,在一般情況下,一旦擁有該特權,就可以任意讀寫目標進程的內存,而不僅僅局限于打開該進程獲得句柄時得到的那些權限及特權。
完整性級別(Integrity Level)和強制策略(Mandatory Policy)檢查
完整性級別(IL)機制是Windows Vista引入的重要的安全機制,建立在其基礎上的沙盒機制十分簡單又異常強大。
受限令牌(Restricted Token)檢查
通過創建受限令牌,可以獲得一個普通令牌所有擁有的權限集合的一個子集,用來進行一些低權限操作,降低安全風險。
AppContainer's Capabilities檢查
LowBox令牌以及AppContainer's Capabilities組身份是從Windows 8開始引入的安全機制,它提供了另一種更加復雜的沙盒機制。Capabilities與Android系統中對于App權限限制非常類似。
可信級別(Trust Level)檢查
完整性級別檢查是一種粗粒度的、相對穩定的訪問權限控制手段;伴隨著Windows的不斷進化,對于簽名機制的依賴越來越明顯,簽名代表了一種可信賴程度。筆者認為,Windows正在逐漸建立一套完善的基于可信賴級別的權限檢查機制,受保護進程(Protected Process)就是這是這套機制中一部分。
上面的內容簡單介紹了Windows的權限檢查模型,以及權限檢查的維度;下面將介紹一些實現細節。
0x02 令牌
TOKEN結構體
Token結構體是訪問權限檢查中的代表主體身份的核心數據結構,下面展示的是Windows 10 x64平臺下的構成。
kd> dt nt!_TOKEN
+0x000 TokenSource : _TOKEN_SOURCE
+0x010 TokenId : _LUID
+0x018 AuthenticationId : _LUID
+0x020 ParentTokenId : _LUID
+0x028 ExpirationTime : _LARGE_INTEGER
+0x030 TokenLock : Ptr64 _ERESOURCE
+0x038 ModifiedId : _LUID
+0x040 Privileges : _SEP_TOKEN_PRIVILEGES
+0x058 AuditPolicy : _SEP_AUDIT_POLICY
+0x078 SessionId : Uint4B
+0x07c UserAndGroupCount : Uint4B
+0x080 RestrictedSidCount : Uint4B
+0x084 VariableLength : Uint4B
+0x088 DynamicCharged : Uint4B
+0x08c DynamicAvailable : Uint4B
+0x090 DefaultOwnerIndex : Uint4B
+0x098 UserAndGroups : Ptr64 _SID_AND_ATTRIBUTES
+0x0a0 RestrictedSids : Ptr64 _SID_AND_ATTRIBUTES
+0x0a8 PrimaryGroup : Ptr64 Void
+0x0b0 DynamicPart : Ptr64 Uint4B
+0x0b8 DefaultDacl : Ptr64 _ACL
+0x0c0 TokenType : _TOKEN_TYPE
+0x0c4 ImpersonationLevel : _SECURITY_IMPERSONATION_LEVEL
+0x0c8 TokenFlags : Uint4B
+0x0cc TokenInUse : UChar
+0x0d0 IntegrityLevelIndex : Uint4B
+0x0d4 MandatoryPolicy : Uint4B
+0x0d8 LogonSession : Ptr64 _SEP_LOGON_SESSION_REFERENCES
+0x0e0 OriginatingLogonSession : _LUID
+0x0e8 SidHash : _SID_AND_ATTRIBUTES_HASH
+0x1f8 RestrictedSidHash : _SID_AND_ATTRIBUTES_HASH
+0x308 pSecurityAttributes : Ptr64 _AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION
+0x310 Package : Ptr64 Void
+0x318 Capabilities : Ptr64 _SID_AND_ATTRIBUTES
+0x320 CapabilityCount : Uint4B
+0x328 CapabilitiesHash : _SID_AND_ATTRIBUTES_HASH
+0x438 LowboxNumberEntry : Ptr64 _SEP_LOWBOX_NUMBER_ENTRY
+0x440 LowboxHandlesEntry : Ptr64 _SEP_LOWBOX_HANDLES_ENTRY
+0x448 pClaimAttributes : Ptr64 _AUTHZBASEP_CLAIM_ATTRIBUTES_COLLECTION
+0x450 TrustLevelSid : Ptr64 Void
+0x458 TrustLinkedToken : Ptr64 _TOKEN
+0x460 IntegrityLevelSidValue : Ptr64 Void
+0x468 TokenSidValues : Ptr64 _SEP_SID_VALUES_BLOCK
+0x470 IndexEntry : Ptr64 _SEP_LUID_TO_INDEX_MAP_ENTRY
+0x478 VariablePart : Uint8B
我們比較關注其中的特權位圖和三個代表主體身份的Sid數組:UserAndGroups,RestrictedSids,Capabilities。

Privileges位圖
特權在用戶態是通過LUID來表示,在內核結構體_TOKEN中是使用三個位圖來表示:
kd> dt _SEP_TOKEN_PRIVILEGES
nt!_SEP_TOKEN_PRIVILEGES
+0x000 Present : Uint8B
+0x008 Enabled : Uint8B
+0x010 EnabledByDefault : Uint8B
分別代表當前的主體可以選用的特權集合(Present)、已經打開的特權集合(Enabled)和默認打開的特權集合(EnabledByDefault),后兩個集合應該是Present集合的子集。
與代表主體身份的Sid數組不同,特權集合的表示簡單,而且沒有任何保護。從用戶態通過API只能打開或者關閉某一項已經存在的特權,而不能增加可選的特權,換句話說,用戶態只能修改Enabled特權集合,而不能修改Present特權集合;從內核態可以直接修改Present特權集合,比如給普通進程增加SeAssignPrimaryTokenPrivilege特權,以便為子進程顯式指定令牌,而不是繼承當前進程的令牌,可以達到擴大子進程權限的效果。
代表主體身份的Sid數組
三個代表主體身份的Sid數組分別是:
UserAndGroups:
代表著主體的普通用戶身份和組身份,是不可或缺的成員;
RestrictedSids:
可選成員,如果不為空,則代表著當前的令牌是受限令牌,受限令牌通過從普通令牌中過濾掉一些比較敏感的身份轉化而來,受限令牌中的UserAndGroups成員與普通令牌相同,但是RestriectedSids成員不為空,里面保存著過濾后的身份子集;由于在訪問權限檢查時,三個身份Sid數組要同時進行檢查,只有結果都通過才允許該次訪問,因此通過增加代表著受限制的權限集合的RestrictedSids成員,既達到了限制令牌權限的目的,又在UserAndGroups成員中保留了原有令牌的完整身份信息。
Capabilities:
可選成員,僅用于AppContainer,其中的Sid代表著與App相關的身份,比如擁有連接網絡、訪問當前位置等權限的身份。
這三個Sid數組都關聯了哈希信息,以保護其完整性,因此,即使從內核態直接修改,也會因為無法通過完整性驗證而失敗。不過好在哈希的算法非常簡單,下面展示的就是Windows 10 x64平臺下面該算法的C++演示代碼:
- static
- NTSTATUS
- RtlSidHashInitialize
- (
- __in PSID_AND_ATTRIBUTES Groups,
- __in size_t GroupsCount,
- __inout PSID_AND_ATTRIBUTES_HASH HashBuffer
- )
- {
- if (NULL == HashBuffer)
- return 0xC000000D;
- memset(HashBuffer, 0, 0x110);
- if (0 == GroupsCount || NULL == Groups)
- return 0;
- HashBuffer->SidCount = GroupsCount;
- HashBuffer->SidAttr = Groups;
- if (GroupsCount > 0x40)
- GroupsCount = 0x40;
- if (0 == GroupsCount)
- return 0;
- size_t bit_pos = 1;
- for (size_t i = 0; i < GroupsCount; i++)
- {
- PISID sid = reinterpret_cast<PISID>((Groups + i)->Sid);
- size_t sub_authority_count = sid->SubAuthorityCount;
- DWORD sub_authority = sid->SubAuthority[sub_authority_count - 1];
- *(size_t*)(&HashBuffer->Hash[(sub_authority & 0x0000000F)]) |= bit_pos;
- *(size_t*)(&HashBuffer->Hash[((sub_authority & 0x000000F0) >> 4) + 0x10]) |= bit_pos;
- bit_pos <<= 1;
- }
- return 0;
- }
該算法有兩處明顯的局限,只計算每個Sid的最后一個Sub Authority的最低位字節,以及最多只有前64個Sid參與計算。
UAC與關聯令牌
UAC是Vista版本引入的比較重要的安全機制,很多用戶抱怨它帶來的不便性,然而它就像Linux操作系統中的sudo一樣,是保護系統安全的一道重要屏障。當用戶登錄Windows時,操作系統會為用戶生成一對初始令牌,分別是代表著用戶所擁有的全部權限的完整版本令牌(即管理員權限令牌),以及被限制管理員權限后的普通令牌,二者互為關聯令牌;此后,代表用戶的進程所使用的令牌都是由普通令牌繼承而來,用來進行常規的、非敏感的操作;當用戶需要進行一些需要管理員權限的操作時,比如安裝軟件、修改重要的系統設置時,都會通過彈出提權對話框的形式提示用戶面臨的風險,征求用戶的同意,一旦用戶同意,將會切換到當前普通令牌關聯的管理員權限令牌,來進行敏感操作。通過這種與用戶交互的方式,避免一些惡意程序在后臺稍稍執行敏感操作。
關聯令牌是通過Logon Session來實現的,下圖展示了其大致原理:

并非所有的令牌都有關聯令牌,比如一直運行在較高權限下不需要進行提權操作的令牌。
0x03 對象
對象的結構
對象是對操作系統中需要保護和集中管理的資源的一種抽象,對象擁有統一的頭部,保存著抽象出來的信息。
kd> dt _OBJECT_HEADER
nt!_OBJECT_HEADER
+0x000 PointerCount : Int8B
+0x008 HandleCount : Int8B
+0x008 NextToFree : Ptr64 Void
+0x010 Lock : _EX_PUSH_LOCK
+0x018 TypeIndex : UChar
+0x019 TraceFlags : UChar
+0x019 DbgRefTrace : Pos 0, 1 Bit
+0x019 DbgTracePermanent : Pos 1, 1 Bit
+0x01a InfoMask : UChar
+0x01b Flags : UChar
+0x01b NewObject : Pos 0, 1 Bit
+0x01b KernelObject : Pos 1, 1 Bit
+0x01b KernelOnlyAccess : Pos 2, 1 Bit
+0x01b ExclusiveObject : Pos 3, 1 Bit
+0x01b PermanentObject : Pos 4, 1 Bit
+0x01b DefaultSecurityQuota : Pos 5, 1 Bit
+0x01b SingleHandleEntry : Pos 6, 1 Bit
+0x01b DeletedInline : Pos 7, 1 Bit
+0x01c Spare : Uint4B
+0x020 ObjectCreateInfo : Ptr64 _OBJECT_CREATE_INFORMATION
+0x020 QuotaBlockCharged : Ptr64 Void
+0x028 SecurityDescriptor : Ptr64 Void
+0x030 Body : _QUAD

對象的頭部包含著指向安全描述符的指針。
安全描述符
安全描述符保存著作為訪問權限檢查客體的對象的主要信息。它包含著4個關鍵成員,根據這4個成員是嵌入在結構體的尾部,還是引用自外部緩存位置的差異,可以分為兩種不同的結構形式:
kd> dt _SECURITY_DESCRIPTOR
ntdll!_SECURITY_DESCRIPTOR
+0x000 Revision : UChar
+0x001 Sbz1 : UChar
+0x002 Control : Uint2B
+0x008 Owner : Ptr64 Void
+0x010 Group : Ptr64 Void
+0x018 Sacl : Ptr64 _ACL
+0x020 Dacl : Ptr64 _ACL
kd> dt _SECURITY_DESCRIPTOR_RELATIVE
nt!_SECURITY_DESCRIPTOR_RELATIVE
+0x000 Revision : UChar
+0x001 Sbz1 : UChar
+0x002 Control : Uint2B
+0x004 Owner : Uint4B
+0x008 Group : Uint4B
+0x00c Sacl : Uint4B
+0x010 Dacl : Uint4B
Owner和Group代表了該安全描述符的屬主Sid,Sacl是系統訪問控制表(System Access Control List)的簡稱,里面可以包含當前對象的審計(Audit)、完整性級別(Integrity Level)以及可信賴級別(Trust Level)等信息,與前面提到的Dacl共用同一結構體。
可選頭部
對象除了擁有共同的固定頭部外,還可以有可選的頭部,保存著名稱等可選信息。通過固定頭部的InfoMask成員查表可以得到可選頭部的位置,如圖所示:

對象目錄
只有部分對象擁有名稱信息,它們被稱為命名對象。命名對象的主要作用是方便對象在不同的進程中共享,它們被按類別編纂成對象目錄,因此可以通過在對象目錄中的路徑信息找到該對象。對象目錄的實現如下圖所示:

對象類型
對象類型是對象中非常重要的信息,Windows將對象的類型信息從同一類對象中抽象出來,保存成一個單獨的類型對象。系統中全部的類型對象被集中放置在一個表中,對象通過維護一個指向該表的索引(TypeIndex)來表明當前對象的類型。這個索引值直接保存在對象的頭部,而對象體與對象頭部直接相鄰,如果對象體被損壞,有可能導致頭部的索引值被改變,使所謂的類型混淆(Type Confusion)利用成為可能。為了緩解這一問題,Windows 10對該索引值做了特殊保護,如下圖所示:

這種保護方式簡單而強大,新的索引值由3部分經過異或操作得到:類型對象在類型對象表中的真實索引值,對象頭部地址的第二個字節(即第8到第15位),保存在ObHeaderCookie全局變量中的因每次系統啟動而異的Cookie字節。其中,ObHeaderCookie的引入,使同一類型的對象在不同機器上,甚至是同一機器上兩次啟動之間的索引值不同,然而這樣并不足以緩解類型混淆利用,我們還可以利用信息泄露(Info Leak)來繞過(Bypass)該保護,因此還引入了對象頭部地址,使得在同一時刻、同一系統中的兩個相同類型對象的不同實例間的索引值也不相同,從而有效地緩解類型混淆利用。
0x04 完整性級別檢查
完整性級別(IL)檢查是沙盒的最簡單實現方式,通過完整性級別在對象和進程層次之間的繼承關系,就如同在操作系統中建立起了“世襲制度”。通過嚴格控制完整性級別的繼承規則,以及設置嚴格的完整性級別檢查制度,可以保證“出身低微”的主體無法訪問到“出身高貴”的客體資源。
完整性擁有以下幾個級別:
SeUntrustedMandatorySid
SeLowMandatorySid
SeMediumMandatorySid
SeHighMandatorySid
SeSystemMandatorySid
主體的缺省完整性級別是SeUntrustedMandatorySid,而客體的缺省完整性級別是SeMediumMandatorySid,這種差異也進一步地強化了這種“世襲制度”。
0x05 保護進程
保護進程(Protected Process)是Windows操作系統為了保護某些關鍵進程,防止其被普通進程調試、注入、甚至是讀取內存信息而建立起來的一種安全機制。
保護進程與其他普通進程的區別在于,保護進程的Protection成員不為0。
kd> dt nt!_EPROCESS Protection
+0x6b2 Protection : _PS_PROTECTION
kd> dt nt!_PS_PROTECTION
+0x000 Level : UChar
+0x000 Type : Pos 0, 3 Bits
+0x000 Audit : Pos 3, 1 Bit
+0x000 Signer : Pos 4, 4 Bits
保護進程的Type成員可以代表兩種保護類型:Protected Process(PP),Protected Process Lite(PPL),兩者的區別在于PP保護進程擁有更高的權限。保護進程的Signer成員代表該進程的簽名發布者的類別。對于Signer為PsProtectedSignerWindows(5)和PsProtectedSignerTcb(6)的保護進程,其Type和Signer信息會被抽取出來,組裝成一個Sid,代表著該進程的可信賴級別,保存到基本令牌中的TrustLevelSid成員中。當一個令牌中的TrustLevelSid被使用時,需要保證與當前進程的Protection信息保持同步,這主要是為了應對令牌在不同進程間傳遞時(比如子進程繼承父進程的令牌)導致的TrustLevelSid成員過時的情形。
當調試或者創建一個進程時,會調用內核函數PspCheckForInvalidAccessByProtection進行權限檢查,該函數根據當前進程以及目標進程的Protection來判定當前操作是否需要遵守保護進程規定的權限限制,具體判定規則如下:
如果操作來自于內核態代碼,不需要遵守限制;
如果目標進程的Protection成員為空,表示目標進程不是保護進程,不需要遵守限制;
如果當前進程是PP類型保護進程,該類型保護進程擁有最高權限,不需要遵守限制;
如果當前進程與目標進程都是PPL類型保護進程,需要根據RtlProtectedAccess表來判斷當前進程的Signer是否優先于(dominate)目標進程的Signer,如果是,不需要遵守限制;
其他情況,需要遵守限制。
RtlProtectedAccess表如下所示:
RTL_PROTECTED_ACCESS RtlProtectedAccess[] =
{
// Domination, Process, Thread,
// Mask, Restrictions, Restrictions,
{ 0, 0, 0}, //PsProtectedSignerNone Subject To Restriction Type
{ 2, 0x000fc7fe, 0x000fe3fd}, //PsProtectedSignerAuthenticode 0y00000010
{ 4, 0x000fc7fe, 0x000fe3fd}, //PsProtectedSignerCodeGen 0y00000100
{ 8, 0x000fc7ff, 0x000fe3ff}, //PsProtectedSignerAntimalware 0y00001000
{ 0x10, 0x000fc7ff, 0x000fe3ff}, //PsProtectedSignerLsa 0y00010000
{ 0x3e, 0x000fc7fe, 0x000fe3fd}, //PsProtectedSignerWindows 0y00111110
{ 0x7e, 0x000fc7ff, 0x000fe3ff}, //PsProtectedSignerTcb 0y01111110
};
其中,Process Restrictions以及Thread Restrictions分別代表著進程和線程的權限限制,詳解如下圖:

我們可以通過NtDebugActiveProcess以及NtCreateUserProcess的演示代碼來理解保護進程及其限制的邏輯:
- static
- NTSTATUS
- NtDebugActiveProcess(
- __in HANDLE ProcessHandle,
- __in HANDLE DebugObjectHandle
- )
- {
- PEPROCESS target_process = nullptr;
- NTSTATUS result = ObReferenceObjectByHandleWithTag( ProcessHandle, &target_process);
- if (! NT_SUCCESS(result))
- return result;
- PEPROCESS host_process = PsGetCurrentProcess();
- if (host_process == target_process)
- return 0xC0000022;
- if (PsInitialSystemProcess == target_process)
- return 0xC0000022;
- if (PspCheckForInvalidAccessByProtection(PreviousMode, host_process->Protection, target_process->Protection))
- return 0xC0000712;
- // ......
- }
- static
- NTSTATUS
- NtCreateUserProcess(
- __out PHANDLE ProcessHandle,
- __out PHANDLE ThreadHandle,
- __in ACCESS_MASK ProcessDesiredAccess ,
- __in ACCESS_MASK ThreadDesiredAccess ,
- __in POBJECT_ATTRIBUTES ProcessObjectAttributes OPTIONAL ,
- __in POBJECT_ATTRIBUTES ThreadObjectAttributes OPTIONAL ,
- __in ULONG CreateProcessFlags,
- __in ULONG CreateThreadFlags,
- __in PRTL_USER_PROCESS_PARAMETERS ProcessParameters ,
- __in PVOID Parameter9,
- __in PNT_PROC_THREAD_ATTRIBUTE_LIST AttributeList
- )
- {
- ACCESS_MASK allowed_process_access = ProcessDesiredAccess ;
- ACCESS_MASK allowed_thread_access = ThreadDesiredAccess ;
- PS_PROTECTION protection = ProcessContext.member_at_0x20;
- if (PspCheckForInvalidAccessByProtection(PreviousMode, host_process->Protection, target_process->Protection))
- {
- // 1 << 0x19 = 0x80000, WRITE_OWNER
- if ( MAXIMUM_ALLOWED == ProcessDesiredAccess )
- allowed_process_access = (((~RtlProtectedAccess[protection.Signer].DeniedProcessAccess) & 0x1FFFFF) | ProcessDesiredAccess) & (~(1 << 0x19));
- if ( MAXIMUM_ALLOWED == ThreadDesiredAccess )
- allowed_thread_access = (((~RtlProtectedAccess[protection.Signer].DeniedThreadAccess) & 0x1FFFFF) | ThreadDesiredAccess) & (~(1 << 0x19));
- }
- //PspInsertProcess(..., allowed_process_access, ...);
- //PspInsertThread(..., allowed_thread_access, ...);
- }
0x06 結語
Windows操作系統中的權限檢查機制以及安全系統一直都在不斷地進化著,探索其實現的細節以及推測其背后的設計理念是一件非常有趣有益的事情,本文僅僅是作者在探索中的小結,難免有疏漏甚至錯誤,更加詳細的內容可以參考下列材料,或者直接分析和調試Windows內核,以了解最新最真實的Windows。
James Forshaw: The Windows Sandbox Paradox
James Forshaw: A Link to the Past: Abusing Symbolic Links on Windows
Alex Ionescu: The Evolution of Protected Processes Part 1: Pass-the-Hash Mitigations in Windows 8.1