又看到一個(gè)好DD.轉(zhuǎn)過(guò)來(lái)先! (這個(gè)在windowsXP sp3中進(jìn)行測(cè)試并沒(méi)有通過(guò),根據(jù)msdn也進(jìn)行了些顯而易見(jiàn)錯(cuò)誤的修改,:( )
ACL即訪問(wèn)控制表,由一個(gè)ACL頭和零到多個(gè)ACE(Access_control entry 訪問(wèn)控制實(shí)例)構(gòu)成。
ACL的應(yīng)用平臺(tái)是WindowsNT/2000/XP/2003,實(shí)際上WindowsNT3.1之后的使用NT內(nèi)核的操作系統(tǒng)都支持這個(gè)結(jié)構(gòu)。ACL標(biāo)志了第三方對(duì)某一個(gè)對(duì)象的訪問(wèn)權(quán)限,這個(gè)對(duì)象可以是任何類(lèi)的實(shí)例,當(dāng)然也包括了進(jìn)程(Process)對(duì)象。
1 概述
每一個(gè)ACE包含一個(gè)授權(quán)對(duì)象(Trustee)和一組權(quán)限,一個(gè)有效的SecurityDescriptor(安全標(biāo)志)包含兩個(gè)ACL,即DACL和SACL。 在WindowsNT下,使用OpenProcess打開(kāi)進(jìn)程的時(shí)候,系統(tǒng)會(huì)根據(jù)相應(yīng)進(jìn)程的DACL結(jié)構(gòu)確定對(duì)象的訪問(wèn)權(quán)限,假如DACL不存在,那么將開(kāi)放所有權(quán)限;假如DACL存在但是為空,那么將拒絕一切形式的訪問(wèn)。 實(shí)際上,在不同的操作系統(tǒng)下,對(duì)DACL為空的處理是不盡相同的,有可能不拒絕系統(tǒng)管理員權(quán)限進(jìn)程的任意形式訪問(wèn)。 SACL的作用是允許系統(tǒng)管理員紀(jì)錄所有對(duì)安全對(duì)象的訪問(wèn)情況,SACL中ACE即是需要被記錄的項(xiàng)目。 DACL的工作原理如下圖:
(圖略)
2 DACL and CreateProcess
2.1 SECURITY_ATTRIBUTES
SECURITY_ATTRIBUTES結(jié)構(gòu)用于在創(chuàng)建進(jìn)程時(shí)指定訪問(wèn)安全級(jí)別。
typedef struct _SECURITY_ATTRIBUTES { DWORD nLength; LPVOID lpSecurityDescriptor; BOOL bInheritHandle; } SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES; 其中l(wèi)pSecurityDescriptor指向一個(gè)SECURITY_DESCRIPTOR結(jié)構(gòu)。
2.2 SECURITY_DESCRIPTOR
SECURITY_DESCRIPTOR結(jié)構(gòu)不能直接修改,只能通過(guò)調(diào)用API函數(shù)來(lái)修改,它的內(nèi)容包括進(jìn)程的所有者(SID Owner)、所屬工作組(SID PrimaryGroup)、DACL(一個(gè))、SACL(一個(gè))、對(duì)前面項(xiàng)目的修改等級(jí)。 主要的API函數(shù)包括:
(1) InitializeSecurityDescriptor
初始化一個(gè)SECURITY_DESCRIPTOR結(jié)構(gòu),除SECURITY_DESCRIPTOR_REVISION是默認(rèn)的修改等級(jí),Owner等項(xiàng)目全部為空或者為NULL值。
(2) SetSecurityDescriptorDacl
本函數(shù)設(shè)置SECURITY_DESCRIPTOR結(jié)構(gòu)中的DACL,參數(shù)中包含了指向ACL結(jié)構(gòu)的指針。
(3) SetSecurityDescriptorGroup
本函數(shù)設(shè)置SECURITY_DESCRIPTOR結(jié)構(gòu)中的主工作組(primary group)信息,原先如果存在主工作組信息,執(zhí)行本函數(shù)時(shí)原信息將被覆蓋。
(4) SetSecurityDescriptorOwner
本函數(shù)設(shè)置SECURITY_DESCRIPTOR結(jié)構(gòu)中的所有者(owner)信息,并覆蓋原有信息。
(5) SetSecurityDescriptorRMControl
本函數(shù)設(shè)置SECURITY_DESCRIPTOR中對(duì)應(yīng)資源管理程序的64位信息,資源管理程序通過(guò)訪問(wèn)這8個(gè)字節(jié)的信息,獲取進(jìn)程對(duì)象的相關(guān)資料。
(6) SetSecurityDescriptorSacl
本函數(shù)設(shè)置SECURITY_DESCRIPTOR結(jié)構(gòu)中的SACL,參數(shù)中包含了指向ACL結(jié)構(gòu)的指針。
通過(guò)以上六個(gè)函數(shù),我們可以完成對(duì)SECURITY_DESCRIPTOR結(jié)構(gòu)的訪問(wèn)。
2.3 CreateProcess
函數(shù)聲明:
BOOL CreateProcess( LPCTSTR lpApplicationName, // name of executable module LPTSTR lpCommandLine, // command line string LPSECURITY_ATTRIBUTES lpProcessAttributes, // SD LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD BOOL bInheritHandles, // handle inheritance option DWORD dwCreationFlags, // creation flags LPVOID lpEnvironment, // new environment block LPCTSTR lpCurrentDirectory, // current directory name LPSTARTUPINFO lpStartupInfo, // startup information LPPROCESS_INFORMATION lpProcessInformation // process information ); 部分參數(shù)說(shuō)明: lpCommandLine 指向字符串的指針,是新進(jìn)程的命令行。命令行字符串以空字符結(jié)束,同樣的'\0'也代表字符的結(jié)束。當(dāng)lpApplicationName為NULL的時(shí)候,本字符串將被看成是新進(jìn)程的可執(zhí)行文件路徑和文件名,這時(shí)要注意空格的處理;LpProcessAttributes,lpThreadAttributes 這兩個(gè)指針指向的內(nèi)容代表了對(duì)新進(jìn)程和其線程的可繼承性的設(shè)定。特別的,在NT內(nèi)核情況下,指向一個(gè)SECURITY_ATTRIBUTES結(jié)構(gòu)。如前所述,它包含了對(duì)象安全級(jí)別的信息;DwCreationFlags 這個(gè)DWORD值用于設(shè)置進(jìn)程的創(chuàng)建模式和優(yōu)先權(quán),大多flag值是可以組合的。
3 ACL
3.1 ACL頭定義
typedef struct _ACL { BYTE AclRevision; BYTE Sbz1; WORD AclSize; WORD AceCount; WORD Sbz2; } ACL,*PACL; 參數(shù)說(shuō)明:
AclRevision 內(nèi)容修正等級(jí),一般是ACL_REVISION,在NT內(nèi)核下,當(dāng)結(jié)構(gòu)中存在指定對(duì)象ACE時(shí),應(yīng)該設(shè)置為ACL_REVISION_DS;
Sbz1, Sbz2 填料;
AceCount 結(jié)構(gòu)中包含ACE的數(shù)量;
AclSize ACL頭和所有ACE的總字節(jié)數(shù)。
結(jié)構(gòu)說(shuō)明:
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 +----------------------------------+----------------+---------------+ | AclSize | Sbz1 | AclRevision | +----------------------------------+----------------+---------------+ | Sbz2 | AceCount | +----------------------------------+---------------------------------+
ACL是由ACL頭(即ACL結(jié)構(gòu))和不定數(shù)量的ACE構(gòu)成的,ACL中每個(gè)ACE從0到n-1編號(hào),因此,n就是ACL中ACE的數(shù)量,即n = AceCount。編輯ACL的時(shí)候,應(yīng)用程序通過(guò)ACE編號(hào)來(lái)訪問(wèn)每個(gè)單獨(dú)的ACE。
如前所述,ACL有兩種,即DACL和SACL。
DACL由對(duì)象所有者(Owner)進(jìn)程和擁有該對(duì)象WRITE_DAC權(quán)限的任何進(jìn)程維護(hù)。這種機(jī)制類(lèi)似于某一個(gè)文件的主人或者管理員有權(quán)決定能讓誰(shuí)看到這份文件。
一個(gè)對(duì)象同樣擁有系統(tǒng)級(jí)的安全設(shè)定,這就是SACL。SACL由系統(tǒng)管理員(administrator)維護(hù),它決定了對(duì)于某個(gè)對(duì)象,系統(tǒng)管理員能夠紀(jì)錄并審核哪些訪問(wèn)。
3.2 ACE for DACL
ACE是ACL中標(biāo)志訪問(wèn)權(quán)限的單元,以下是已經(jīng)定義的能夠在DACL中使用的ACE類(lèi)型:
ACCESS_ALLOWED_ACE
這個(gè)結(jié)構(gòu)允許指定的用戶(hù)或群組訪問(wèn)某一個(gè)對(duì)象;
ACCESS_ALLOWED_OBJECT_ACE
這個(gè)結(jié)構(gòu)允許指定的用戶(hù)或群組訪問(wèn)某一個(gè)特定的對(duì)象,應(yīng)用在Windows2000/XP。當(dāng)ACL中包含這個(gè)類(lèi)型的ACE時(shí),AclRevision應(yīng)設(shè)置為ACL_REVISION_DS;
ACCESS_DENIED_ACE
這個(gè)結(jié)構(gòu)拒絕指定的用戶(hù)或群組訪問(wèn)某一個(gè)對(duì)象;
ACCESS_DENIED_OBJECT_ACE
這個(gè)結(jié)構(gòu)拒絕指定的用戶(hù)或群組訪問(wèn)某一個(gè)特定的對(duì)象,應(yīng)用在Windows2000/XP。當(dāng)ACL中包含這個(gè)類(lèi)型的ACE時(shí),AclRevision應(yīng)設(shè)置為ACL_REVISION_DS。
為了進(jìn)一步說(shuō)明ACE,以下介紹ACE的數(shù)據(jù)結(jié)構(gòu)。
typedef struct _ACE_HEADER { BYTE AceType; BYTE AceFlags; WORD AceSize; } ACE_HEADER; typedef ACE_HEADER *PACE_HEADER; 參數(shù)說(shuō)明: AceType 指定ACE類(lèi)型; AceFlags ACE的控制標(biāo)志,可以是幾個(gè)標(biāo)志的組合; AceSize ACE的字節(jié)長(zhǎng)度。 每個(gè)ACE都以ACE_HEADER開(kāi)頭,并包含了其全長(zhǎng)(按字節(jié))。 3.3 如何應(yīng)用ACL
本節(jié)將介紹如何往一個(gè)DACL中添加ACCESS_DENIED_ ACE元素,并實(shí)現(xiàn)對(duì)一個(gè)進(jìn)程的訪問(wèn)保護(hù)。
3.3.1 AddAccessDeniedAce函數(shù)
BOOL AddAccessDeniedAce( PACL pAcl, // access control list DWORD dwAceRevision, // ACL revision level DWORD AccessMask, // access mask PSID pSid // security identifier ); 部分參數(shù)說(shuō)明: dwAceRevision 如果ACL中已經(jīng)包含了一個(gè)以上ACCESS_ALLOWED_OBJECT_ACE或者ACCESS_DENIED_OBJECT_ACE類(lèi)型的ACE對(duì)象,應(yīng)設(shè)置為ACL_REVISION_DS,否則應(yīng)設(shè)置為ACL_REVISION; AccessMask 訪問(wèn)類(lèi)型掩碼,它可以標(biāo)志一種訪問(wèn)類(lèi)型或者多種訪問(wèn)類(lèi)型的組合,本函數(shù)中這些被標(biāo)志的訪問(wèn)類(lèi)型將被禁用; pSid Trutee的ID信息。這個(gè)參數(shù)指向一個(gè)SID結(jié)構(gòu),它指定了那些用戶(hù)將被剝奪訪問(wèn)權(quán)限,這些用戶(hù)可以是普通用戶(hù)、群組以及共享用戶(hù)。 另外,在ACL中,應(yīng)該按照先設(shè)ACCESS_DENIED_ACE再設(shè)ACCESS_ALLOWED_ACE的順序來(lái)添加ACE,否則將會(huì)導(dǎo)致錯(cuò)誤。這一點(diǎn)十分重要,也是初學(xué)者容易犯的錯(cuò)誤。在這里添加的只是ACCESS_DENIED_ACE,不需要考慮順序問(wèn)題。
3.3.2 ACCESS_MASK and SID
typedef DWORD ACCESS_MASK; 這個(gè)類(lèi)型組合標(biāo)志了一個(gè)或者多個(gè)訪問(wèn)類(lèi)型。 SID(The security identifier) 這是一個(gè)變長(zhǎng)的數(shù)據(jù)結(jié)構(gòu),用于唯一的標(biāo)志用戶(hù)或者群組。這個(gè)結(jié)構(gòu)不能被直接編輯,需要用系統(tǒng)API函數(shù)來(lái)創(chuàng)建、修改,具體應(yīng)用如下:
前一陣子,有網(wǎng)友向我抱怨。他說(shuō)自己在本機(jī)建立了一個(gè)共享內(nèi)存,然后用B/S架構(gòu)的瀏覽器端來(lái)訪問(wèn)這塊內(nèi)存,結(jié)果總是訪問(wèn)成功,返回錯(cuò)誤是沒(méi)有權(quán)限。
的確,B/S程序,其插件運(yùn)行在瀏覽器中,賬戶(hù)是guest,當(dāng)然沒(méi)有權(quán)限訪問(wèn)身為內(nèi)核對(duì)象的本機(jī)共享內(nèi)存。不過(guò),如果你在創(chuàng)建內(nèi)核對(duì)象時(shí)重新設(shè)置了它的訪問(wèn)控制列表(DACL),那么就能讓它被你所允許的任何賬戶(hù)訪問(wèn)。很神奇,不是嗎?
下面我通過(guò)創(chuàng)建一個(gè)在Windows2000下不會(huì)被殺死的計(jì)事本程序,向各位展示這個(gè)技術(shù):
void Test() { printf("本程序?qū)?chuàng)建一個(gè)不可被殺死的記事本!\n"); PSID pEveryoneSID = NULL; // everyone群組SID PSID pAdminSID = NULL; // 本機(jī)系統(tǒng)管理員群組SID EXPLICIT_ACCESS ea[3]; // ACE內(nèi)容 PACL pProcessACL = NULL; // 進(jìn)程的DACL PSECURITY_DESCRIPTOR pSD = NULL; // 進(jìn)程的SD SECURITY_ATTRIBUTES saProcess; // 進(jìn)程SA
DWORD dwRet; // 以下創(chuàng)建SID // S-1-1-0 SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY; SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY; if (!AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pEveryoneSID)) { printf("AllocateAndInitializeSid failed! EveryoneSID\n"); goto Err; }
// S-1-5-32-0x220 if (!AllocateAndInitializeSid(&SIDAuthNT, 1, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdminSID)) { printf("AllocateAndInitializeSid failed! AdminSID\n"); goto Err; }
// S-1-5-32-0x1F4 PSID pAdminuUserSID; if (!AllocateAndInitializeSid(&SIDAuthNT, 1, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_USER_RID_ADMIN, 0, 0, 0, 0, 0, 0, &pAdminuUserSID)) { printf("AllocateAndInitializeSid failed! pAdminuUserSID\n"); goto Err; }
// 填充外部訪問(wèn)組 ZeroMemory(&ea, 2 * sizeof(EXPLICIT_ACCESS));
// S-1-1-0,禁止關(guān)閉進(jìn)程和修改參數(shù) ea[0].grfAccessPermissions = PROCESS_TERMINATE|PROCESS_SET_INFORMATION; ea[0].grfAccessMode = GRANT_ACCESS; ea[0].grfInheritance= NO_INHERITANCE; ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; ea[0].Trustee.ptstrName? = (LPTSTR) pEveryoneSID;
// S-1-5-32-0x220,禁止關(guān)閉進(jìn)程和修改參數(shù) ea[1].grfAccessPermissions = PROCESS_TERMINATE|PROCESS_SET_INFORMATION; ea[1].grfAccessMode = GRANT_ACCESS; ea[1].grfInheritance= NO_INHERITANCE; ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP; ea[1].Trustee.ptstrName? = (LPTSTR) pAdminSID;
// S-1-5-32-0x1F4,禁止關(guān)閉進(jìn)程和修改參數(shù) ea[2].grfAccessPermissions = PROCESS_TERMINATE|PROCESS_SET_INFORMATION; ea[2].grfAccessMode = GRANT_ACCESS; ea[2].grfInheritance= NO_INHERITANCE; ea[2].Trustee.TrusteeForm = TRUSTEE_IS_SID; ea[2].Trustee.TrusteeType = TRUSTEE_IS_USER; ea[2].Trustee.ptstrName? = (LPTSTR) pAdminuUserSID;
// 創(chuàng)建并填充ACL dwRet = SetEntriesInAcl(3, ea, NULL, &pProcessACL); if (dwRet != ERROR_SUCCESS) { printf("SetEntriesInAcl failed!\nCode = %d", dwRet); goto CleanUp; }
// 創(chuàng)建并初始化SD pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); if (pSD == NULL) { printf( "LocalAlloc failed!\n"); goto Err; } if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) { printf( "InitializeSecurityDescriptor failed!\n"); goto Err; }
// 添加ACL到SD中去 if (!SetSecurityDescriptorDacl(pSD, TRUE, // fDaclPresent flag pProcessACL, FALSE)) // not a default DACL { printf( "SetSecurityDescriptorDacl failed!"); goto Err; }
// 設(shè)置SA saProcess.nLength = sizeof(SECURITY_ATTRIBUTES); saProcess.lpSecurityDescriptor = pSD; saProcess.bInheritHandle = FALSE;
// 運(yùn)行進(jìn)程并等待其正常結(jié)束 PROCESS_INFORMATION ProcessInfo; STARTUPINFO StartupInfo; ZeroMemory(&StartupInfo, sizeof(StartupInfo)); StartupInfo.cb = sizeof(StartupInfo); if (CreateProcess("c:\\winnt\\notepad.exe", NULL, &saProcess, NULL, FALSE, 0, NULL, NULL, &StartupInfo, &ProcessInfo)) { WaitForSingleObject(ProcessInfo.hProcess, INFINITE); CloseHandle(ProcessInfo.hThread); CloseHandle(ProcessInfo.hProcess); } else { printf("CreateProcess failed!\n"); goto Err; }
// Clean up CleanUp: printf("Exiting...\n");
if (pEveryoneSID != NULL) { FreeSid(pEveryoneSID); } if (pAdminSID != NULL) { FreeSid(pAdminSID); } if (pProcessACL != NULL) { LocalFree(pProcessACL); } if (pSD != NULL) { LocalFree(pSD); }
printf("Success!\n"); return;
// Error process Err: DWORD dwErr; dwErr = GetLastError(); printf("Code = %d \n", dwErr);
goto CleanUp; }
|