我正在嘗試在我使用 LogonUserExW 登錄的用戶的背景關系中啟動一個行程。
為此,我需要修改 Winstation "Winsta0" 和桌面 "Default" 的 DACL。我所做的是使用 GetSecurityInfo 獲取 DACL 和安全描述符(我實際上不需要也不需要使用)。然后我呼叫 GetAclInformation 兩次以獲取有關 ACL 大小和修訂的資訊。
ACL_SIZE_INFORMATION aclInfoSizeOut = new ACL_SIZE_INFORMATION();
GetAclInformation(daclWinsta, out aclInfoSizeOut, Marshal.SizeOf(aclInfoSizeOut), ACL_INFORMATION_CLASS.AclSizeInformation);
ACL_REVISION_INFORMATION aclInfoRevisionOut = new ACL_REVISION_INFORMATION();
GetAclInformation(daclWinsta, out aclInfoRevisionOut, Marshal.SizeOf(aclInfoRevisionOut), ACL_INFORMATION_CLASS.AclRevisionInformation);
獲得資料后,我會計算新 ACL 的所需大小:
int cbNewACL = Convert.ToInt32(aclInfoSizeOut.AclBytesInUse Marshal.SizeOf(allowedAce) GetLengthSid(userpSid) - sizeof(UInt32));
然后根據https://web.archive.org/web/20121231022835/http://support.microsoft.com/kb/102102我為新的 ACL 分配記憶體使用
IntPtr pNewAcl = LocalAlloc(LocalAllocFlags.LPTR, cbNewACL);
這就是我開始感到困惑的地方。我不明白這實際上是如何作業的。使用 LocalAlloc 我現在有一個指向我指定大小的記憶體的指標。然后我初始化應該在該記憶體塊中的新 ACL,但是如何?因為一旦我使用下面的函式,指標實際上就被重寫了。有趣的是,每次我呼叫該函式時它總是具有相同的值。而 Locallloc 回傳的指標總是不同的。
InitializeAcl(out pNewAcl, cbNewACL, aclInfoRevisionOut.AclRevision);
InitializeAcl 函式中的 pAcl 定義為“[out] pAcl 指向要由此函式初始化的 ACL 結構的指標。在呼叫此函式之前為 pAcl 分配記憶體。” 我會理解它是否作為 REF 傳遞給函式以知道在哪里實際初始化 ACL,但 REF 根本不會改變它。
另一件事是當我呼叫 InitializeAcl 時,cbNewAcl 被設定為 0。這完全超出了我的想象。它如何以及為什么會改變價值?
然后,當我呼叫 AddAccessAllowedAce 時,它??會在我從現有 ACL 獲取資料之前完全弄亂我設定的結構 - aclInfoSizeOut 和 aclInfoRevisionOut 設定為無意義的值。比如count 0,bytes in use 78548557(有些高的數字)。并且 cbNewAcl 被更改為 1。我不知道為什么會這樣。
AddAccessAllowedAce(ref pNewAcl, aclInfoRevisionOut.AclRevision, ACCESS_MASK.READ_CONTROL | ACCESS_MASK.WINSTA_ALL_ACCESS, userpSid);
下面是我“作業”的完整代碼——我獲取用戶的令牌和 psid,我查詢現有的 DACL,我查詢 DACL 的成員并獲取他們的字串 SID,但僅此而已。我無法創建新的 DACL。也將其與我的評論一起發布。我希望我沒有忘記任何簽名,因為 VS 中的代碼比我實際發布的要長一些。
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool CloseHandle(IntPtr hObject);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool LogonUserExW(
string lpszUsername,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
out IntPtr phToken,
out IntPtr ppLogonSid,
out IntPtr ppProfileBuffer,
out IntPtr pdwProfileLength,
out QUOTA_LIMITS pQuotaLimits
);
[DllImport("Advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern int ConvertSidToStringSidW(
IntPtr SidPtr,
out IntPtr SidString
);
[DllImport("Advapi32.dll", SetLastError = true)]
public static extern int GetSecurityInfo
(
IntPtr handle,
SE_OBJECT_TYPE ObjectType,
SECURITY_INFORMATION SecurityInfo,
out IntPtr ppsidOwner,
out IntPtr ppsidGroup,
out IntPtr ppDacl,
out IntPtr ppSacl,
out IntPtr ppSecurityDescriptor
);
[DllImport("User32.dll", SetLastError = true)]
public static extern IntPtr GetProcessWindowStation();
[DllImport("Advapi32.dll", SetLastError = true)]
public static extern bool GetAclInformation(
IntPtr pAcl,
out ACL_SIZE_INFORMATION pAclInformation,
int nAclInformationLength,
ACL_INFORMATION_CLASS aclInformationClass
);
public struct ACL_SIZE_INFORMATION
{
public uint AceCount;
public uint AclBytesInUse;
public uint AclBytesFree;
}
public struct ACL_REVISION_INFORMATION
{
public int AclRevision;
}
public enum ACL_INFORMATION_CLASS
{
AclRevisionInformation = 1,
AclSizeInformation
}
public struct QUOTA_LIMITS
{
public int PagedPoolLimit;
public int NonPagedPoolLimit;
public int MinimumWorkingSetSize;
public int MaximumWorkingSetSize;
public int PagefileLimit;
public Int64 TimeLimit;
}
ublic struct ACCESS_ALLOWED_ACE
{
public ACE_HEADER Header;
public ACCESS_MASK Mask;
public UInt16 SidStart;
}
ublic enum LocalAllocFlags : uint
{
LHND = 0x0042,
LMEM_FIXED = 0x0000,
LMEM_MOVEABLE = 0x0002,
LMEM_ZEROINIT = 0x0040,
LPTR = 0x0040
}
[DllImport("Kernel32.dll", SetLastError = true)]
public static extern IntPtr LocalAlloc(LocalAllocFlags flags, int bytes);
DllImport("Advapi32.dll", SetLastError = true)]
public static extern bool InitializeAcl(
out IntPtr pAcl,
int nAclLength,
int dwAclRevision
);
public static extern int AddAccessAllowedAce
(
ref IntPtr pAcl,
/* ACL_REVISION 2
ACL_REVISION_DS 4
*/
int dwAceRevision,
ACCESS_MASK AccessMask,
IntPtr pSid
);
LogonUserExW("XXXXX", "FFFFF", "BBBBB", 2, 0, out IntPtr userToken, out IntPtr userpSid, out IntPtr profileBuffer, out IntPtr profileLength, out QUOTA_LIMITS profQuotaLimits);
ConvertSidToStringSidW(userpSid, out IntPtr ptrSid);
string stringSid = Marshal.PtrToStringUni(ptrSid);
IntPtr winstaHandle = GetProcessWindowStation();
//get security descriptor for the station
int getDescriptorResult = GetSecurityInfo(winstaHandle, SE_OBJECT_TYPE.SE_WINDOW_OBJECT, SECURITY_INFORMATION.DACL_SECURITY_INFORMATION | SECURITY_INFORMATION.PROTECTED_DACL_SECURITY_INFORMATION, out IntPtr owner, out IntPtr group, out IntPtr daclWinsta, out IntPtr Sacl, out IntPtr secDescriptor);
//this actually works but shows different values than ACL_SIZE_INFORMATION and ACL_REVISION_INFORMATION
//maybe DACL is in different structure than ACL struct? could not find any other struct in the documentation
ACL aclStruct = (ACL)Marshal.PtrToStructure(daclWinsta, typeof(ACL));
ACL_SIZE_INFORMATION aclInfoSizeOut = new ACL_SIZE_INFORMATION();
GetAclInformation(daclWinsta, out aclInfoSizeOut, Marshal.SizeOf(aclInfoSizeOut), ACL_INFORMATION_CLASS.AclSizeInformation);
ACL_REVISION_INFORMATION aclInfoRevisionOut = new ACL_REVISION_INFORMATION();
GetAclInformation(daclWinsta, out aclInfoRevisionOut, Marshal.SizeOf(aclInfoRevisionOut), ACL_INFORMATION_CLASS.AclRevisionInformation);
//count:17 bytes:436
//create a new ACL just to get its size for the cbNewACL value
ACCESS_ALLOWED_ACE allowedAce = new ACCESS_ALLOWED_ACE();
int cbNewACL = Convert.ToInt32(aclInfoSizeOut.AclBytesInUse Marshal.SizeOf(allowedAce) GetLengthSid(userpSid) - sizeof(UInt32));
//??? for some reason cbNewACL gets cleared after calling InitializeAcl
IntPtr pNewAcl = LocalAlloc(LocalAllocFlags.LPTR, cbNewACL);
InitializeAcl(out pNewAcl, cbNewACL, aclInfoRevisionOut.AclRevision);
//just for test try to add it right away
//AddAccessAllowedAce(ref pNewAcl, aclInfoRevisionOut.AclRevision, ACCESS_MASK.READ_CONTROL | ACCESS_MASK.WINSTA_ALL_ACCESS, userpSid);
//after calling AddAccessAllowedAce the information is structs aclInfoSizeOut and aclInfoRevisionOut gets destroyed
//cbNewAcl gets set to 1
//???
bool newAceAdded = false;
bool userSidAlreadyExists = false;
//copy old ACEs into the new ACL
if (aclInfoSizeOut.AceCount != 0)
{
int indexInNewAcl = 0;
for (int i=0;i<aclInfoSizeOut.AceCount;i )
{
//get ace and add it to the new ACL
GetAce(daclWinsta, i, out IntPtr existingAce);
if (existingAce != IntPtr.Zero)
{
//get ace header from the ACE pointer to identify the ACE type
//get first 4 bytes from the pointer starting at 0 since ACE_HEADER struct is of size 4
//once we have it in the byte array, create a GC handle with the bytes - this allocates them in a managed memory
//then read it to the structure using Marshal.PtrToStructure
ACE_HEADER aceHeader = new ACE_HEADER();
byte[] aceHeaderBytes = new byte[Marshal.SizeOf(aceHeader)];
Marshal.Copy(existingAce, aceHeaderBytes, 0, aceHeaderBytes.Length);
GCHandle aceHeaderBytesHandle = GCHandle.Alloc(aceHeaderBytes, GCHandleType.Pinned);
try
{
aceHeader = (ACE_HEADER)Marshal.PtrToStructure(aceHeaderBytesHandle.AddrOfPinnedObject(), typeof(ACE_HEADER));
switch (aceHeader.AceType)
{
case 0:
case 1:
//0 allowed - ACCESS_ALLOWED_ACE, 1 denied - ACCESS_DENIED_ACE
//the two have the same members definied in their structure.
//the declaration below is not explicitly needed in our case, but might be useful for some other things
//ACCESS_ALLOWED_ACE existingDeniedAllowedACE = (ACCESS_ALLOWED_ACE)Marshal.PtrToStructure(existingAce, typeof(ACCESS_ALLOWED_ACE));
//0 and 1 in this context are together in one clause since they are definied by the same members in the same order, thus allowing us
//to get SidStart from the same offset from the ACE IntPtr
//GUESSING: SidStart starts at 8th byte in the struct, therefore an offset of 8 bytes - size of ACE_HEADER and ACCESS_MASK (Uint16)
//if for example the ACE type would be definied by more members with SidStart at the end, we would have to calculate the offset by
//the sum of all members except for SidStart
IntPtr sidPtrInAce = IntPtr.Zero;
try
{
sidPtrInAce = IntPtr.Add(existingAce, 8);
ConvertSidToStringSidW(sidPtrInAce, out IntPtr pSidStringInAce);
if (pSidStringInAce == IntPtr.Zero) { throw new Exception("failed to get pSidString"); }
string sidStringInAce = Marshal.PtrToStringUni(pSidStringInAce);
Console.WriteLine(sidStringInAce);
if (sidStringInAce == stringSid) { userSidAlreadyExists = true; }
} catch { }
break;
}
if (userSidAlreadyExists) { break; }
//if the ACE was not added AND the processing ACE is not NON-INHERITED DENIED and is not DENIED for Object AND is not Enable for Object
//then add our ADE to ensure it is in the correct order
//Windows 2000 and later ACE ordering in ACL:
//Non-Inherited -> Inherited
//withing each of the two groups the ACEs are also in the following order
//Disable for the object
//Disable for the subject of the object
//Enable for the object
//Enable for the subject of the object
//So basically if we hit anything but NON-Inherited disable for the object, NON-Inherited Disable for the subject of the object and NON-Inherited Enable for the object
//then we should add our ACE - this will ensure correct order
//^ could not figure out how to distinguish between Enable for the object and Enable for the subject of the object
//instead, add the ACE the moment we find an inherited ACE
if (!newAceAdded && isAceInherited(aceHeader.AceFlags))
{
//AddAccessAllowedAce(ref pNewAcl, aclInfoRevisionOut.AclRevision, ACCESS_MASK.READ_CONTROL | ACCESS_MASK.WINSTA_ALL_ACCESS, userpSid);
newAceAdded = true;
//indexInNewAcl ;
}
//AddAce(ref pNewAcl, aclInfoRevisionOut.AclRevision, indexInNewAcl, existingAce, aceHeader.AceSize);
indexInNewAcl ;
}
catch (Exception ex)
{
aceHeaderBytesHandle.Free();
}
} else
{
Console.WriteLine("failed obtaining existing ACE in ACL. quitting"); Console.Read(); return;
}
}
}
if (userSidAlreadyExists) { Console.WriteLine("user already exists in the ACL. quit"); Console.Read(); return; }
if (!newAceAdded && !userSidAlreadyExists)
{
//we did not hit an inherited ACE - probably none exists in the ACL OR AceCount was 0 - no going through ACEs
//therefore user was not added
//AddAccessAllowedAce(ref pNewAcl, aclInfoRevisionOut.AclRevision, ACCESS_MASK.READ_CONTROL | ACCESS_MASK.WINSTA_ALL_ACCESS, userpSid);
newAceAdded = true;
}
if (!newAceAdded)
{
Console.WriteLine("failed to add new ACE. quit"); Console.Read(); return;
}
CloseHandle(userToken);
LocalFree(userpSid);
Console.WriteLine("ready to quit");
Console.Read();
uj5u.com熱心網友回復:
根據塞爾文的評論:
問題出在 InitializeAcl 簽名上。它不是out,而是in,并且函式在IntPtr指向的地址中初始化了acl。除此之外,我還必須更改 AddAce 和 AddAccessAllowedAce 簽名——將 IntPtr pAcl 參考到 IntPtr 中。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/493355.html