MyException - 我的异常网
当前位置:我的异常网» Windows » Windows下怎么创建低权限进程

Windows下怎么创建低权限进程

www.MyException.Cn  网友分享于:2013-10-16  浏览:0次
Windows下如何创建低权限进程

   1.  前言

在使用 Sysinternals 出品的 Process Explorer 过程中,对 “Run as Limited User” 功能的实现方式颇感兴趣,一番搜寻之下发现Mark大神在《Running as Limited User – the Easy Way》中对该功能的实现做了相关的阐述:

use the CreateRestrictedToken API to create a security context, called a token, that’s a stripped-down version of its own, removing administrative privileges and group membership. After generating a token that looks like one that Windows assigns to standard users Process Explorer calls CreateProcessAsUser to launch the target process with the new token.

使用 CreateRestrictedToken API来创建安全上下文,降低令牌(Token)的管理员权限和组成员资格,使其创建的令牌看起来像Windows赋予普通用户时一样,然后使用此令牌作为传入参数调用CreateProcessAsUser来创建新的子进程。

Process Explorer queries the privileges assigned to the Users group and strips out all other privileges, including powerful ones like SeDebugPrivilege, SeLoadDriverPrivilege and SeRestorePrivilege.

查询赋予用户组的特权并从当前进程权限中剔除这些权限比如SeDebugPrivilege、SeLoadDriverPrivilege和SeRestorePrivilege。

 

刚好最近有个项目需要实现降低进程权限的功能,在一翻折腾下就将其实现了,下面将谈谈实现的历程,如果纰漏之处,不吝指出。

   2.  知识背书

在列出代码前需要了解一下一些实现原理,下面是一些相关的知识点,如果无耐心往下看,可以直接点击这里跳到代码实现处。

 

安全对象

有资格拥有安全描述符的对象如文件、管道、进程、进程间同步对象等。所有已命名的Windows对象都是安全的,那些未被命名的对象比如线程或进程对象也可以拥有安全描述符。

对于大多数的安全对象,当创建该对象时可以指定它的安全描述符。当一个安全对象被创建时,系统会对其赋予一个安全描述符,安全描述符包含由其创建者指定的安全信息,或者缺省的安全信息(如果没有特意进行指定的话)。

  • ž   应用程序可以使特定的函数对已有的对象进行操作以来获取和设置安全信息。
  • ž   每种类型的安全对象定义了它自身的访问权限和自身映射的通用访问权限。

更详细内容见:https://msdn.microsoft.com/en-us/library/windows/desktop/aa379557(v=vs.85).aspx

 

安全描述符(security descriptor)

包含用于保护安全对象的安全信息。

安全描述符描述 对象的所有者(SIDs) 和 以下的访问控制列表:

  • 自由访问控制列表(DACL):指明特定用户或组对该对象的访问是允许或拒绝;
  • 安全访问控制列表(SACL):控制系统审计如何访问对象。

 

安全标识(Security Identifiers)

一定长度用来表示托管的唯一值。

安全标识主要运用于如下几个方面:

  • 在安全描述符中定义对象的所有者和基本组;
  • 在访问控制项中定义托管的权限是允许、拒绝或是审计;
  • 在访问令牌中定义用户和用户所在的组。

 

访问令牌

包含登录用户的信息。用来描述一个进程或线程的安全上下文的对象,令牌的信息包含关联到进程或线程的账号的标识和特权。

当一个用户登录时,系统对用户的账号和密码进行认证,如果登录成功,系统则创建一个访问令牌,每个进程运行时都有一个访问令牌代表当前的用户,访问令牌中的安全描述符指明当前用户所属的账号和所属的组账号,令牌也包含一系列由用户或用户所在组进行维护的权限,在一个进程试图进行访问安全对象或执行系统管理员任务过程中需要权限时,系统通过这个令牌来确认关联的用户。

 

访问控制列表及其访问控制项

自由访问控制列表(DACL)包含若干个访问控制项(ACEs)。

约定的执行规则如下:

  • ž   如果对象没有自由访问控制列表(DACL),则任何用户对其均有完全的访问权限;
  • ž   如果对象拥有DACL,那么系统仅允许那些在访问控制项(ACE)显式指明的访问权限;
  • ž   如果在访问控制列表(DACL)中不存在访问控制项(ACE),那么系统不允许任何用户对其进行访问;
  • ž   如果访问控制列表中的访问控制项对准许访问的用户或组数目有限,那么系统会隐式拒绝那些不在访问控制项中的其他托管的访问

需要注意的是访问控制项的排序很重要。因为系统按照队列的方式读取访问控制项,直到访问被拒绝或允许。用户的访问拒绝ACE必须放在访问允许ACE的前头,否则当系统读到对组的访问允许ACE时,它会给当前限制的用户赋予访问的权限。系统在检测到请求访问被允许或拒绝后就不再往下检查。

你可以通过标识允许访问的ACE来控制对对象的访问,你无需显式地拒绝一个对象的访问。

 

线程和安全对象间的交互

当一个线程想要使用一个安全对象时,系统在线程执行前会进行访问审核,在访问审核中,系统将线程访问令牌中的安全信息与对象安全描述符中的安全信息进行比对。

访问令牌中包含的安全标识(SIDs)可以指明与线程关联的用户,系统查看线程访问令牌中用户或组的SID,同时检查对象的自由访问控制列表(DACL),自由访问控制列表(DACL)中包含存储有指明对指定的用户或组的访问权限是允许或拒绝信息的访问控制项(ACE),系统检查每个访问控制项(ACE)直至出现指明针对此线程(的用户或组的SID)的访问权限是允许还是拒绝的ACE,或者到最终都没有对应的ACEs可以检查。

 

(图片出处:https://msdn.microsoft.com/en-us/library/windows/desktop/aa378890(v=vs.85).aspx

 

系统按照序列检查每个ACE,查询ACE中的托管与定义在线程中的托管(根据托管的SID)一致的ACE,直到如下的情况出现:

  • ž   表明访问拒绝的ACE显式拒绝在线程的访问令牌中的一个托管的任何访问权限;
  • ž   一个或多个表明访问允许的ACEs显式地为线程访问令牌中的托管提供所有访问权限;
  • ž   所有ACEs已经比对审核完但至少有一个请求访问权限没有显式允许,这种情况下该访问权限则被隐式拒绝。

一个访问控制列表(ACL)可以有多个的访问控制项(ACE)针对令牌的(同一个)SIDs,这种情况下每个ACE授予的访问权限是可以进行累积叠加,比如,如果一个ACE对一个组允许读的访问权限,而另一个ACE对该组内的一个用户允许写的访问权限,那么该用户对于当前对象就拥有了读和写的访问权限。

 

(图片出处:https://msdn.microsoft.com/en-us/library/windows/desktop/aa446597(v=vs.85).aspx

如上图所示,对于线程A,尽管在ACE@3中允许写权限,但因为在ACE@1中已经显式拒绝“Andrew”用户的访问权限,所以该安全对象对于线程A是不可访问的;对于线程B,在ACE@2中显式指明A组用户可以有写的权限,并且在ACE@3中允许任何用户读和执行的权限,所以线程B对这个安全对象拥有读、写以及执行的权限。

 

完整性级别

Windows完整性机制是对Windows安全架构的扩展,该完整性机制通过添加完整性等级到安全访问令牌和添加强制性标记访问控制项到安全描述符中的系统访问控制列表(SACL)

进程在安全访问令牌中定义完整性等级,IE在保护模式下的完整性等级为低,从开始菜单运行的应用程序的等级为中等,需要管理员权限并以管理员权限运行的应用程序的等级为高。

保护模式能够有效地减少IE进程附带的攻击行为如篡改和摧毁数据、安装恶意程序;相比其他程序,连接网络的程序更容易遭受网络的攻击因为它们更可能从未知源地址下载未受信任的内容,通过降低完整性等级或限制对其的允许权限,可以减少篡改系统或污染用户数据文件的风险。

在系统访问控制列表(SACL)中有一个称为强制标识的访问控制项(ACE),该控制项的安全描述符定义完整性等级或允许访问当前对象需要达到的等级,安全对象如果没有该控制项则默认拥有中等的完整性等级;

即使用户在自由访问控制列表(DACL)中已经明确授予相应的写权限,低等级的进程也不能获取比其高等级的安全对象的写权限,完整性等级检验在用户访问权限审查之前完成。

 

所有的文件和注册表键在缺省下的完整性等级为中,而由低等完整性进程创建的安全对象,系统会自动地赋予其低等完整性强制标志,同样,由低等完整性进程创建的子进程也是在低完整性等级下运行。

 

完整性访问等级(IL)

系统权限

安全标识

 

System

System

S-1-16-16384

 

High

Administrative

 

S-1-16-12288

可安装文件到Program Files文件夹、往敏感的注册表中如HKEY_LOCAL_MACHINE写数据

Medium

User

S-1-16-8192

创建和修改用户文档中的文件、往特定用户的注册表如HKEY_CURRENT_USER中写数据

Low

Untrusted

S-1-16-4096

仅能往低等级位置写数据如临时网络文件夹和注册表

HKEY_CURRENT_USER\Software\LowRegistry

 

低完整性进程可以往用户存档文件下写文件,通常为%USER PROFILE%\AppData\LocalLow,可以通过调用SHGetKnownFolderPath 函数并传入FOLDERID_LocalAppDataLow参数来获取完整的路径名称

SHGetKnownFolderPath(FOLDERID_LocalAppDataLow, 0, NULL, szPath, ARRAYSIZE(szPath));

同样低完整性进程可以往指定的注册表下创建和修改子键,该注册表路径通常为HKEY_CURRENT_USER\Software\AppDataLow

   3.  代码实现

实现思路

  1. 创建新的普通用户组和系统管理员的安全描述符标识;
  2. 获取当前进程的令牌,并根据令牌句柄获取当前进程所拥有的特权;
  3. 通过已创建的普通用户组的安全描述符标识获取普通用户组所拥有的特权;
  4. 给当前进程令牌中的管理员安全描述符添加Deny-only属性,以此达到避免新创建的进程以管理员作为其所有者;
  5. 从当前进程拥有的特权中剔除普通用户组所没有的特权;
  6. 从新的受限令牌中复制为模拟令牌;
  7. 将模拟令牌的完整性特权设为低级,以限制新创建的进程对普通文档、可执行程序的写、执行等访问权限;

代码实现

  1 void CreateRestrictedProcess() 
  2 {
  3     SECURITY_ATTRIBUTES sa;
  4     sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  5     sa.bInheritHandle = TRUE;
  6     sa.lpSecurityDescriptor = NULL;
  7 
  8     TCHAR szCmdLine[CMDLINE_SIZE] = {0};
  9     HANDLE hToken = NULL;
 10     HANDLE hNewToken = NULL;
 11     HANDLE hNewExToken = NULL;
 12 
 13     CHAR szIntegritySid[20] = "S-1-16-4096";
 14     PSID pIntegritySid = NULL;
 15     PSID pUserGroupSID = NULL;
 16     PSID pAdminSID = NULL;
 17 
 18     TOKEN_MANDATORY_LABEL tml = {0};
 19 
 20     PROCESS_INFORMATION pi;
 21     STARTUPINFO si;
 22 
 23     BOOL bSuc = FALSE;
 24     ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
 25 
 26     ZeroMemory(&si, sizeof(STARTUPINFO));
 27     si.cb = sizeof(STARTUPINFO);
 28     GetStartupInfo(&si);
 29     DWORD fdwCreate = 0;
 30 
 31     __try
 32     {
 33 
 34         if (!OpenProcessToken(GetCurrentProcess(),
 35             //MAXIMUM_ALLOWED,
 36             TOKEN_DUPLICATE |
 37             TOKEN_ADJUST_DEFAULT |
 38             TOKEN_QUERY |
 39             TOKEN_ASSIGN_PRIMARY,
 40             &hToken))
 41         {
 42             char szMsg[DEFAULT_MSG_SIZE] = {0};
 43             Dbg("OpenProcessToken failed, GLE = %u.", GetLastError());
 44             __leave;
 45         }
 46 
 47         Dbg("Using RestrictedTokens way !!!");
 48         DWORD dwSize = 0;
 49         DWORD dwTokenInfoLength = 0;
 50         SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
 51         SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
 52         if(!AllocateAndInitializeSid(
 53             &SIDAuthNT, 
 54             0x2, 
 55             SECURITY_BUILTIN_DOMAIN_RID/*0×20*/, 
 56             DOMAIN_ALIAS_RID_USERS, 
 57             0, 0, 0, 0, 0, 0,
 58             &pUserGroupSID))
 59         {
 60             Dbg("AllocateAndInitializeSid for UserGroup Error %u", GetLastError());
 61             __leave;
 62         }
 63 
 64         // Create a SID for the BUILTIN\Administrators group.
 65         if(! AllocateAndInitializeSid( &SIDAuth, 2,
 66             SECURITY_BUILTIN_DOMAIN_RID,
 67             DOMAIN_ALIAS_RID_ADMINS,
 68             0, 0, 0, 0, 0, 0,
 69             &pAdminSID) ) 
 70         {
 71             Dbg("AllocateAndInitializeSid for AdminGroup Error %u", GetLastError());
 72             __leave;
 73         }
 74 
 75         SID_AND_ATTRIBUTES SidToDisable[1] = {0};
 76         SidToDisable[0].Sid = pAdminSID;
 77         SidToDisable[0].Attributes = 0;
 78 
 79         PTOKEN_PRIVILEGES pTokenPrivileges = NULL;
 80         PTOKEN_PRIVILEGES pTokenPrivilegesToDel = NULL;
 81         if(!GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &dwSize))
 82         {
 83             if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
 84             {
 85                 pTokenPrivileges = (PTOKEN_PRIVILEGES)LocalAlloc(0, dwSize);
 86                 pTokenPrivilegesToDel = (PTOKEN_PRIVILEGES)LocalAlloc(0, dwSize);
 87                 if(pTokenPrivileges != NULL && pTokenPrivilegesToDel != NULL)
 88                 {
 89                     if(!GetTokenInformation(hToken, TokenPrivileges, pTokenPrivileges, dwSize, &dwSize))
 90                     {
 91                         Dbg("GetTokenInformation about TokenPrivileges failed GTE = %u.", GetLastError());
 92                         __leave;
 93                     }
 94                 }
 95                 else
 96                 {
 97                     char szMsg[DEFAULT_MSG_SIZE] = {0};
 98                     Dbg("LocalAlloc for pTokenPrivileges failed GTE = %u.", GetLastError());
 99                     __leave;
100                 }
101             }
102         }
103 
104         LUID_AND_ATTRIBUTES *pTokenLUID = pTokenPrivileges->Privileges;
105         Dbg("CurrentToken's TokenPrivileges Count: %u", pTokenPrivileges->PrivilegeCount);
106         DWORD dwLuidCount = 0;
107         PLUID pPrivilegeLuid = NULL;
108         if(!CTWProcHelper::GetPrivilegeLUIDWithSID(pUserGroupSID, &pPrivilegeLuid, &dwLuidCount))
109         {
110             Dbg("GetPrivilegeLUIDWithSID failed GTE = %u.", GetLastError());
111             if(pPrivilegeLuid)
112             {
113                 //HeapFree(GetProcessHeap(), 0, pPrivilegeLuid);
114                 LocalFree(pPrivilegeLuid);
115                 pPrivilegeLuid = NULL;
116             }
117             __leave;
118         }
119         Dbg("UserGroup's TokenPrivileges Count: %u", dwLuidCount);
120 
121         DWORD dwDelPrivilegeCount = 0;
122         for(DWORD dwIdx=0; dwIdx<(pTokenPrivileges->PrivilegeCount); dwIdx++)
123         {
124             BOOL bFound = FALSE;
125             DWORD dwJdx = 0;
126             for(; dwJdx<dwLuidCount; dwJdx++)
127             {
128                 //if(memcmp(&(pTokenLUID[dwIdx].Luid), &(pPrivilegeLuid[dwJdx]), sizeof(LUID)) == 0)
129                 if((pTokenLUID[dwIdx].Luid.HighPart == pPrivilegeLuid[dwJdx].HighPart)
130                     &&
131                     (pTokenLUID[dwIdx].Luid.LowPart == pPrivilegeLuid[dwJdx].LowPart))
132                 {
133                     bFound = TRUE;
134                     break;
135                 }
136             }
137             if(!bFound)
138             {
139                 char szPrivilegeName[MAX_PATH] = {0};
140                 DWORD dwNameSize = MAX_PATH;
141                 if(!LookupPrivilegeName(NULL, &(pTokenLUID[dwIdx].Luid), szPrivilegeName, &dwNameSize))
142                 {
143                     Dbg("LookupPrivilegeName failed GTE = %u.", GetLastError());
144                     //Dbg("NoFound[%u]: i=%u, j=%u", dwDelPrivilegeCount, dwIdx, dwJdx);
145                 }
146                 //else
147                 //{
148                 //    Dbg("NoFound[%u]: i=%u, j=%u -> %s", dwDelPrivilegeCount, dwIdx, dwJdx, szPrivilegeName);
149                 //}
150                 pTokenPrivilegesToDel->Privileges[dwDelPrivilegeCount++].Luid = pTokenLUID[dwIdx].Luid;
151             }
152         }
153         pTokenPrivilegesToDel->PrivilegeCount = dwDelPrivilegeCount;
154         Dbg("TokenPrivileges to delete Count: %u", dwDelPrivilegeCount);
155         if(pPrivilegeLuid)
156         {
157             //HeapFree(GetProcessHeap(), 0, pPrivilegeLuid);
158             LocalFree(pPrivilegeLuid);
159             pPrivilegeLuid = NULL;
160         }
161 
162         if(!CreateRestrictedToken(hToken,
163             0,
164             1, SidToDisable,
165             //0, NULL,
166             dwDelPrivilegeCount, pTokenPrivilegesToDel->Privileges,
167             0, NULL,
168             &hNewToken
169             ))
170         {
171             char szMsg[DEFAULT_MSG_SIZE] = {0};
172             Dbg("CreateRestrictedToken failed GTE = %u.", GetLastError());
173             __leave;
174         }
175 
176         // Duplicate the primary token of the current process.
177         if (!DuplicateTokenEx(hNewToken, MAXIMUM_ALLOWED, NULL, SecurityImpersonation, 
178             TokenPrimary, &hNewExToken))
179         {
180             Dbg("DuplicateTokenEx failed GTE = %u.", GetLastError());
181             hNewExToken = NULL;
182             //__leave;
183         }
184         else
185         {
186             if (ConvertStringSidToSid(szIntegritySid, &pIntegritySid))
187             {
188                 tml.Label.Attributes = SE_GROUP_INTEGRITY;
189                 tml.Label.Sid = pIntegritySid;
190 
191                 // Set the process integrity level
192                 if (!SetTokenInformation(hNewExToken, TokenIntegrityLevel, &tml,
193                     sizeof(TOKEN_MANDATORY_LABEL) + GetLengthSid(pIntegritySid)))
194                 {
195                     Dbg("SetTokenInformation failed GTE = %u.", GetLastError());
196                     //__leave;
197                 }
198                 else
199                 {
200                     CloseHandle(hNewToken);
201                     hNewToken = hNewExToken;
202                     hNewExToken = NULL;
203                     Dbg("Assign Low Mandatory Level to New Token which used to CreateProcessAsUser.");
204                 }
205             }
206 
207         }
208 
209         if(!(bSuc = CreateProcessAsUser(hNewToken, NULL,
210             szCmdLine,     // command line 
211             NULL,          // TODO: process security attributes 
212             NULL,          // TODO: primary thread security attributes 
213             TRUE,         // handles are inherited ?? 
214             fdwCreate,     // creation flags
215             NULL,          // use parent's environment 
216             NULL,          // use parent's current directory 
217             &si,           // STARTUPINFO pointer 
218             &pi)))         // receives PROCESS_INFORMATION 
219         {
220             Dbg("CreateProcessAsUser failed GTE = %u.", GetLastError());
221             __leave;
222         }
223 
224         if(pTokenPrivileges)
225         {
226             LocalFree(pTokenPrivileges);
227         }
228         if(pTokenPrivilegesToDel)
229         {
230             LocalFree(pTokenPrivilegesToDel);
231         }
232     }
233     __finally
234     {
235         if(pIntegritySid)
236         {
237             LocalFree(pIntegritySid);
238         }
239         if(pUserGroupSID)
240         {
241             LocalFree(pUserGroupSID);
242         }
243         if(pAdminSID)
244         {
245             LocalFree(pAdminSID);
246         }
247         // 
248         // Close the access token.
249         // 
250         if (hToken)
251         {
252             CloseHandle(hToken);
253         }
254         if(hNewToken)
255         {
256             CloseHandle(hNewToken);
257         }
258         if(hNewExToken)
259         {
260             CloseHandle(hNewExToken);
261         }
262         if(!bSuc)
263         {
264             Dbg("Retry to Create process in normal way.");
265             //Create process.
266             bSuc = CreateProcess(NULL, 
267                 szCmdLine,     // command line 
268                 NULL,          // TODO: process security attributes 
269                 NULL,          // TODO: primary thread security attributes 
270                 TRUE,          // handles are inherited ?? 
271                 fdwCreate,     // creation flags
272                 NULL,          // use parent's environment 
273                 NULL,          // use parent's current directory 
274                 &si,           // STARTUPINFO pointer 
275                 &pi);          // receives PROCESS_INFORMATION 
276         }
277     }
278 }


其中 GetPrivilegeLUIDWithSID 函数的实现如下:

 1 BOOL GetPrivilegeLUIDWithSID(PSID pSID, PLUID *pLUID, PDWORD pDwCount)
 2 {
 3     LSA_OBJECT_ATTRIBUTES ObjectAttributes;
 4     NTSTATUS ntsResult;
 5     LSA_HANDLE lsahPolicyHandle;
 6 
 7     // Object attributes are reserved, so initialize to zeros.
 8     ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));
 9 
10     // Get a handle to the Policy object.
11     ntsResult = LsaOpenPolicy(
12         NULL,    //Name of the target system.
13         &ObjectAttributes, //Object attributes.
14         POLICY_ALL_ACCESS, //Desired access permissions.
15         &lsahPolicyHandle  //Receives the policy handle.
16         );
17 
18     if (ntsResult != STATUS_SUCCESS)
19     {
20         printf("OpenPolicy failed returned %lu", LsaNtStatusToWinError(ntsResult));
21         return FALSE;
22     } 
23 
24     PLSA_UNICODE_STRING UserRights = NULL;
25     ULONG uRightCount;
26     ntsResult = LsaEnumerateAccountRights(lsahPolicyHandle, pSID, &UserRights, &uRightCount);
27     if (ntsResult != STATUS_SUCCESS)
28     {
29         printf("LsaEnumerateAccountRights failed returned %lu", LsaNtStatusToWinError(ntsResult));
30         LsaClose(lsahPolicyHandle);
31         return FALSE;
32     }
33     
34     printf("LsaEnumerateAccountRights returned Right count: %lu", uRightCount);
35 
36     (*pDwCount) = 0;
37     //pLUID = (PLUID)HeapAlloc(GetProcessHeap(), 0, uRightCount*sizeof(LUID));
38     (*pLUID) = (PLUID)LocalAlloc(LPTR, uRightCount*sizeof(LUID));
39     if((*pLUID) == NULL)
40     {
41         printf("HeapAlloc for PLUID failed returned %u", GetLastError());
42         LsaClose(lsahPolicyHandle);
43         return FALSE;
44     }
45 
46     for(ULONG uIdx=0; UserRights != NULL && uIdx<uRightCount; uIdx++)
47     {
48         int nLenOfMultiChars = WideCharToMultiByte(CP_ACP, 0, UserRights[uIdx].Buffer, UserRights[uIdx].Length,
49             NULL, 0, NULL, NULL);
50         PTSTR pMultiCharStr = (PTSTR)HeapAlloc(GetProcessHeap(), 0, nLenOfMultiChars*sizeof(char));
51         if(pMultiCharStr != NULL)
52         {
53             WideCharToMultiByte(CP_ACP, 0, UserRights[uIdx].Buffer, UserRights[uIdx].Length,
54                 pMultiCharStr, nLenOfMultiChars, NULL, NULL);
55             LUID luid;
56             if(!LookupPrivilegeValue(NULL, pMultiCharStr, &luid))
57             {
58                 printf("LookupPrivilegeValue about %s failed, GLE=%u.", pMultiCharStr, GetLastError());
59                 HeapFree(GetProcessHeap(), 0, pMultiCharStr);
60                 continue;
61             }
62             (*pLUID)[(*pDwCount)++] = luid;
63             HeapFree(GetProcessHeap(), 0, pMultiCharStr);
64         }
65     }
66     if((ntsResult = LsaFreeMemory(UserRights)) != STATUS_SUCCESS)
67     {
68         printf("LsaFreeMemory failed returned %lu", LsaNtStatusToWinError(ntsResult));
69     }
70     LsaClose(lsahPolicyHandle);
71     return TRUE;
72 }

 

 下图是普通创建子进程效果(使用 Process Explorer 获取的进程信息,下同),可以看到:

  • 该进程的所有者为 Administrators;
  • 强制性标识等级为:高
  • 部分特权为 Enabled

 

 通过执行上方的代码对新进程的创建令牌进行一系列的限制后,可以看到:

  • 进程的所有者:添加了Deny属性;
  • 强制性标识等级:低;
  • 部分特权被删除。

 

 

  4.  扩展延伸

托管

一个托管可以是用户账号、组账号或者登陆会话。是由访问控制项(ACE)赋予的,每个访问控制项(ACE)中都有一个安全标识(SID)用来表明特定的托管。

 

特权(Privilege)

特权用于对一个对象或服务的访问控制,比自由访问控制更为严格,一个系统管理员通过使用特权控制那些用户可以操纵系统资源,一个应用程序在修改系统层级的资源需要使用到特权,比如修改系统时间和关闭系统。

 

更多内容请查看如下链接:

Windows Integrity Mechanism Design

Designing Applications to Run at a Low Integrity Level

Understanding and Working in Protected Mode Internet Explorer

Browsing the Web and Reading E-mail Safely as an Administrator(DropMyRight.exe的实现)

 

文章评论

程序员应该关注的一些事儿
程序员应该关注的一些事儿
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
漫画:程序员的工作
漫画:程序员的工作
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
中美印日四国程序员比较
中美印日四国程序员比较
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
我的丈夫是个程序员
我的丈夫是个程序员
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
那些争议最大的编程观点
那些争议最大的编程观点
鲜为人知的编程真相
鲜为人知的编程真相
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
程序员必看的十大电影
程序员必看的十大电影
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
程序员和编码员之间的区别
程序员和编码员之间的区别
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
程序员都该阅读的书
程序员都该阅读的书
代码女神横空出世
代码女神横空出世
总结2014中国互联网十大段子
总结2014中国互联网十大段子
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
 程序员的样子
程序员的样子
一个程序员的时间管理
一个程序员的时间管理
每天工作4小时的程序员
每天工作4小时的程序员
Java程序员必看电影
Java程序员必看电影
为什么程序员都是夜猫子
为什么程序员都是夜猫子
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
程序员的鄙视链
程序员的鄙视链
10个调试和排错的小建议
10个调试和排错的小建议
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
我是如何打败拖延症的
我是如何打败拖延症的
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
如何成为一名黑客
如何成为一名黑客
编程语言是女人
编程语言是女人
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
老程序员的下场
老程序员的下场
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有