windows 安全模型簡介

來源:互聯網
上載者:User

標籤:

作業系統中有些資源是不能由使用者代碼直接存取的,比如線程進程,檔案等等,這些資源必須由系統級代碼由RING3層進入到RING0層操作,並且返回一些標識供使用者程式使用,一般調用某個函數陷入到核心,這樣的函數叫做系統調用,而有些不直接陷入到核心,一般叫做系統API,linux中使用系統調用,而windows中封裝了一系列的API。

windows對象與控制代碼windows對象

作業系統為了安全,提供了一種保護機制,這種機制會禁止使用者操作某些資源,避免使用者過於關注細節,或者由於操作不當而造成系統崩潰。windows中將所有這些資源封裝成了一個個對象。對象就是作業系統為了維護這些資源而定義的一系列的資料結構。對象這個詞我們很容易聯想到物件導向程式設計語言中的對象,對象其實就是對一些資料以及操作這些資料的一個封裝,而windows是採用物件導向思想開發的,所以我們可以將windows中的對象想象成物件導向中的對象,而windows針對每種對象都提供了操作函數,這些函數一般都會以控制代碼作為第一個參數,這就有點像類函數中傳入的this指標。而這操作對象的函數就是類函數。
windows中總共有三種對象:GUI對象、GDI對象、核心對象。

windows中的控制代碼

windows中對象的操作是由系統提供的一系列的API函數來完成,這些函數有一個共同特點,就是以HANDLE 控制代碼作為第一個參數,windows中採用控制代碼來唯一標識每個核心對象。在windows中控制代碼的定義如下:

typedef void * HANDLE

從上面的定義可以看出,這個控制代碼應該是指向這個對象的結構體的指標。由於每次在程式啟動時記憶體都是隨機分配的,所以控制代碼不要使用硬式編碼方式,同時在複製核心對像的時候,並不是簡單的複製它的控制代碼,對象的複製有專門的函數,DuplicateHandle,該函數原型如下:

BOOL DuplicateHandle(  HANDLE hSourceProcessHandle, //來源物件所在進程控制代碼  HANDLE hSourceHandle, //來源物件控制代碼  HANDLE hTargetProcessHandle, //目標對象所在進程控制代碼  LPHANDLE lpTargetHandle, //返回目標對象的控制代碼  DWORD dwDesiredAccess, //以何種許可權複製  BOOL bInheritHandle, //複製的物件控點是否可繼承  DWORD dwOptions //標記一些特殊的動作);

下面是一個例子代碼:

HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);    HANDLE hMutexDup, hThread;    DWORD dwThreadId;    DuplicateHandle(GetCurrentProcess(),hMutex,GetCurrentProcess(),&hMutexDup,         0,FALSE,DUPLICATE_SAME_ACCESS);

上述代碼在本進程中複製了一個互斥對象對象的控制代碼。

windows 安全性實體模型


windows中的核心對象由進程和線程進行操作,而對象就好像一個被鎖上的房間,進程想要訪問對象,並對對象進程某種操作,就必須擷取這個對象的鑰匙,而線程就好像擁有鑰匙的人,只有鑰匙是對的,才可以訪問。這把鎖能夠由不同的鑰匙開啟,這些鑰匙資訊儲存在ACE中,而只有當ACE中的資訊與訪問字串這個鑰匙匹配才可以開啟。我們一般把這個鑰匙稱為訪問字串(Token)

訪問字串

訪問字串主要包括:使用者標識、組標識、優先權資訊、以及其他訪問資訊。
使用者標識:用於唯一標識每個使用者,就好像為每個使用者都分配了一個唯一的使用者ID
組標識:使用者所屬組的唯一標識ID
優先權:一般系統對每個使用者以及它所屬組分配了一些許可權,而有的時候這些許可權並不夠,這個時候需要通過這個優先權資訊額外新增一些許可權
當使用者登入windows系統時,系統就為這個使用者指派了一個帶有該使用者資訊的訪問字串,該使用者建立的每個安全性實體都有這個訪問字串的拷貝,當使用者開啟的進程試圖訪問某個安全性實體時系統就在對象的ACL中尋找該使用者是否有某項許可權。有這個許可權才能對對象進行這項操作。
子進程的訪問字串一般繼承與父進程,但是子進程也可以自行建立訪問字串來覆蓋原來的訪問字串。
操作訪問字串所使用的API主要有以下幾個:
OpenProcessToken //開啟進程的存取權杖
OpenThreadToken //開啟線程的存取權杖
AdjustTokenGroups //改變使用者組的存取權杖
AdjustTokenPrivileges //改變令牌的存取權限
GetTokenInformation //擷取存取權杖的資訊
SetTokenInformation //設定存取權杖資訊

下面主要介紹一下函數GetTokenInformation 的用法:

BOOL WINAPI GetTokenInformation(  __in          HANDLE TokenHandle, //訪問字串的控制代碼  __in          TOKEN_INFORMATION_CLASS TokenInformationClass, //需要返回訪問字串哪方面的資訊  __out_opt     LPVOID TokenInformation, //用於接收返回資訊的緩衝  __in          DWORD TokenInformationLength, //緩衝的長度  __out         PDWORD ReturnLength //實際所需的緩衝的長度);

這個函數可以返回存取權杖多方面的資訊,具體返回哪個方面的資訊,需要通過第二個參數來指定,這個參數是一個枚舉類型,表示需要擷取的資訊,每個方面的資訊都定義了對應的結構體,這些資訊可以由MSDN中查到。另外這個函數支援兩次調用,第一次傳入NULL指標和0長度,這樣通過最後一個參數可以得到具體所需要的緩衝的大小。

SID

訪問字串中使用者與使用者組採用安全性識別碼的方式唯一標識(Security Indentifer SID),系統中的SID是唯一的。它主要用來標識下面的這些內容:
1. 安全性描述元中的所有者和使用者組
2. 被ACE認可的訪問者
3. 訪問字串的使用者和組
SID的長度是可變的,在使用時不應該使用SID這個資料類型,因為這個時候還不知道需要的長度是多少,應該由系統來建立並返回它的指標,所以在使用時需要使用SID的指標。下面是一些操作SID的API
AllocateAndInitializeSid //初始化一個SID
FreeSid //釋放一個SID
CopySid //拷貝一個SID
EqualSid //判斷兩個SID是否相等
GetLengthSid //擷取SID的長度
IsValidSid //是否是有效SID
ConvertSidToStringSid //將SID轉化為字串的方式
下面是一個利用這些API擷取系統使用者組的SID和當前登入使用者的SID的例子:

BOOL GetLoginSid(HANDLE hToken, PSID *ppsid);//SID本身大小是不可知的,所以在傳入參數時應該傳入2級指標,由函數自己決定大小void FreeSid(PSID *ppsid);int _tmain(int argc, TCHAR* argv[]){    setlocale(LC_ALL, "chs");    HANDLE hProcess = GetCurrentProcess();    HANDLE hToken = NULL;    OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, &hToken);    PSID pSid = NULL;    GetLoginSid(hToken, &pSid);    LPTSTR pStringSid = NULL;    ConvertSidToStringSid(pSid, &pStringSid);    _tprintf(_T("當前登入的使用者sid = %s\n"), pStringSid);    FreeSid(&pSid);    return 0;}BOOL GetLoginSid(HANDLE hToken, PSID *ppsid){    TOKEN_GROUPS *ptg = NULL;    BOOL bSuccess = FALSE;    DWORD dwLength = 0;    if(!GetTokenInformation(hToken, TokenGroups, ptg, 0, &dwLength))    {        if (ERROR_INSUFFICIENT_BUFFER != GetLastError())        {            goto __CLEAN_UP;        }        ptg = (TOKEN_GROUPS*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength);        if (!GetTokenInformation(hToken, TokenGroups, ptg, dwLength, &dwLength))        {            goto __CLEAN_UP;        }    }    _tprintf(_T("共找到%d個組SID\n"), ptg->GroupCount);    LPTSTR pStringSid = NULL;    for (int i = 0; i < ptg->GroupCount; i++)    {        ConvertSidToStringSid(ptg->Groups[i].Sid, &pStringSid);        _tprintf(_T("\t id = %d sid = %s"), i, pStringSid);        if ((ptg->Groups[i].Attributes & SE_GROUP_LOGON_ID) ==  SE_GROUP_LOGON_ID)        {            _tprintf(_T("此使用者為當前登入的使用者"));            *ppsid = (PSID)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength);             CopySid(dwLength, *ppsid, ptg->Groups[i].Sid);        }        _tprintf(_T("\n"));    }__CLEAN_UP:    if (ptg != NULL)    {        HeapFree(GetProcessHeap(), 0, ptg);    }    return bSuccess;}void FreeSid(PSID *ppsid){    HeapFree(GetProcessHeap(), 0, *ppsid);    *ppsid = NULL;}

這個例子是MSDN中的一個例子,上述代碼中首先擷取進程的存取權杖,然後通過函數GetTokenInformation 擷取存取權杖的資訊。通過傳入TokenGroups這個值擷取目前使用者所在使用者組的訪問字串。並將資訊儲存到結構體TOKEN_GROUPS中最後通過(ptg->Groups[i].Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID這樣一個運算式來判斷當前的SID是否是登入使用者的。

優先權

優先權是由字串標識的局部唯一的標識符(LUID)
優先權是由系統管理員分配給對應的使用者,一般不能通過編程的方式提升使用者的優先權,但是有時候即使使用者具有某個優先權,但是它啟動的程式並不具有相關的優先權。這個時候可以通過編程的方式提升使用者進程特權。
系統有3個值代表一個優先權:
字串名,整個系統上都有意義,稱為全域名,這個字串並不是一個可讀的字串,顯示出來的資訊不一定能看得懂。
顯示給使用者的可讀名稱;如:改變系統時間(可以在組策略中查看)
每個電腦都不同的局部值;
下面有幾個常用的優先權:

#define SE_DEBUG_NAME  TEXT("SeDebugPrivilege") //調試進程#define SE_LOAD_DRIVER_NAME TEXT("SeLoadDriverPrivilege") //裝載驅動#define SE_LOCK_MEMORY_NAME TEXT("SeLockMemoryPrivilege") //鎖定記憶體頁面#define SE_SHUTDOWN_NAME TEXT("SeShutdownPrivilege") //關機

下面是幾個優先權函數
LookupPrivilegeValue //查詢優先權的值
LookupPrivilegeDisplayName //查詢優先權的輸出名
LookupPrivilegeName //查詢優先權的名稱
PrivilegeCheck //優先權資訊檢查
下面是一個擷取使用者特權資訊的代碼:

int _tmain(int argc, TCHAR *argv[]){    setlocale(LC_ALL, "chs");    HANDLE hToken = NULL;    HANDLE hProcess = GetCurrentProcess();    OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);    TOKEN_PRIVILEGES* ptg = NULL;    DWORD dwTgSize = 0;    TCHAR szName[255] = _T("");    TCHAR szDisplay[255] = _T("");    DWORD languageID = GetUserDefaultLangID();    if (!GetTokenInformation(hToken, TokenPrivileges, ptg, 0, &dwTgSize))    {        if (ERROR_INSUFFICIENT_BUFFER != GetLastError())        {            _tprintf(_T("擷取令牌失敗\n"));            return 0;        }        ptg = (TOKEN_PRIVILEGES*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwTgSize);        if (!GetTokenInformation(hToken, TokenPrivileges, ptg, dwTgSize, &dwTgSize))        {            _tprintf(_T("讀取令牌資訊失敗\n"));            return 0;        }    }    _tprintf(_T("使用者的特權資訊:\n"));    for(int i = 0; i < ptg->PrivilegeCount; i++)    {        DWORD dwName = sizeof(szName) / sizeof(TCHAR);        DWORD dwDisplay = sizeof(szDisplay) / sizeof(TCHAR);        LookupPrivilegeName(NULL, &ptg->Privileges[i].Luid, szName, &dwName);        LookupPrivilegeDisplayName(NULL, szName, szDisplay, &dwDisplay, &languageID);        _tprintf(_T("\t 特權名%s %s"), szName, szDisplay);        if (ptg->Privileges[i].Attributes & ((SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_ENABLED_BY_DEFAULT)))        {            _tprintf(_T("特權開放\n"));        }else        {            _tprintf(_T("特權關閉\n"));        }    }    HeapFree(GetProcessHeap(), 0, ptg);    return 0;}

下面是進程提權的代碼:

int _tmain(int argc, TCHAR *argv[]){    HANDLE hToken = NULL;    HANDLE hProcess = GetCurrentProcess();    SetPrivileges(hToken, SE_TCB_NAME , TRUE);    return 0;}BOOL SetPrivileges(HANDLE hToken, LPTSTR lpPrivilegesName, BOOL bEnablePrivilege){    //擷取Token的特權資訊    LUID uid = {0};    LookupPrivilegeValue(NULL, lpPrivilegesName, &uid);    TOKEN_PRIVILEGES tp = {0};    tp.PrivilegeCount = 1;    tp.Privileges[0].Luid = uid;    if (bEnablePrivilege)    {        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;    }else    {        tp.Privileges[0].Attributes = 0;    }    AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL);    if ( GetLastError() == ERROR_NOT_ALL_ASSIGNED )    {        _tprintf(_T("分配指定的特權(%s)失敗. \n"),lpPrivilegesName);        return FALSE;    }     return TRUE;}
安全性描述元

安全性描述元中通常包含資訊:所有者、主組、任選存取控制清單(DACL)、系統存取控制清單(SACL)
安全性描述元是以SECURITE_DESCRIPTOR結構開始,後面連續跟著安全性描述元的其它資訊

存取控制清單

存取控制清單(Access Control List ACL)主要由多個訪問存取控制入口(Access Control Entries ACE)組成。ACE用於標識一個使用者、組或局部組以及它們中每一個允許的訪問權;

安全性描述元的建立

在建立安全訪問對象的函數中一般都需要填入一個SECURITY_ATTRIBUTES結構體的指標,我們要麼給定一個NULL值使其具有預設的安全屬性,或者自己建立一個安全性描述元並將他的指標傳入。
建立一個安全性描述元主要有下面幾步:
1. 用函數 AllocateAndInitializeSid 建立使用者的SID。函數的定義如下:

BOOL WINAPI AllocateAndInitializeSid(  __in          PSID_IDENTIFIER_AUTHORITY pIdentifierAuthority, //用於表示該SID標識的頒發機構  __in          BYTE nSubAuthorityCount,  //SID有多少個子部分  __in          DWORD dwSubAuthority0, //第0個子部分  __in          DWORD dwSubAuthority1,  __in          DWORD dwSubAuthority2,  __in          DWORD dwSubAuthority3,  __in          DWORD dwSubAuthority4,  __in          DWORD dwSubAuthority5,  __in          DWORD dwSubAuthority6,  __in          DWORD dwSubAuthority7,  __out         PSID* pSid //返回一個PSID的指標);

SID主要由一個頒發機構以及一個或者多個32位的唯一的RID組成這些RID通過參數dwSubAuthority0到dwSubAuthority7產生。對應的使用者SID都有固定的組合。這個我還沒找到具體的使用者與SID是如何定義的。
2. 為該使用者SID分配存取控制許可權,主要通過填充結構體EXPLICIT_ACCESS成員來實現,這個結構體的定義如下:

typedef struct _EXPLICIT_ACCESS {      DWORD grfAccessPermissions; //制定使用者權限    ACCESS_MODE grfAccessMode; //用於表示允許、拒絕、審查特定使用者的許可權    DWORD grfInheritance; //當前的許可權是否可以繼承    TRUSTEE Trustee; //這是一個訪問託管} EXPLICIT_ACCESS,  *PEXPLICIT_ACCESS;

3.用API SetEntriesInAcl將上述結構體放入到ACL中
4. 分配並初始化SECURITY_DESCRIPTOR結構體,初始化該結構體所用到的API 是InitializeSecurityDescriptor
5. 將SECURITY_DESCRIPTOR結構加入到安全性描述元中,安全性描述元的結構體是:SECURITY_ATTRIBUTES
下面是一個具體的例子(這個例子來自MSDN):

    SID_IDENTIFIER_AUTHORITY SIDAuthorityNT = SECURITY_WORLD_SID_AUTHORITY;    SID_IDENTIFIER_AUTHORITY SIDAuthorityWord = SECURITY_NT_AUTHORITY;    PSID pEveryOneSid = NULL;    PSID pAdminSid = NULL;    EXPLICIT_ACCESS ea[2] = {0};    PACL pAcl = NULL;    //建立everyone使用者的SID    AllocateAndInitializeSid(&SIDAuthorityWord, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pEveryOneSid);    PSECURITY_DESCRIPTOR pSD = NULL;    //定義everyone 的存取控制資訊    ea[0].grfAccessMode = SET_ACCESS; //用於表示允許、拒絕、審查特定使用者的許可權    ea[0].grfAccessPermissions = KEY_READ; //制定使用者權限    ea[0].grfInheritance = FALSE;    ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;    ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;    ea[0].Trustee.ptstrName = (LPTSTR)pEveryOneSid;    //建立administor使用者組的SID    AllocateAndInitializeSid(&SIDAuthorityNT, 2, SECURITY_BUILTIN_DOMAIN_RID,DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdminSid);    ea[1].grfAccessMode = SET_ACCESS;    ea[1].grfAccessPermissions = KEY_ALL_ACCESS;    ea[1].grfInheritance = FALSE;    ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;    ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP;    ea[1].Trustee.ptstrName = (LPTSTR)pAdminSid;    //將以上兩個SID加入到ACL中    SetEntriesInAcl(2, ea, NULL, &pAcl);    pSD = (PSECURITY_DESCRIPTOR) HeapAlloc(GetProcessHeap(), 0, SECURITY_DESCRIPTOR_MIN_LENGTH);     //初始化一個SECURITY_DESCRIPTOR結構    InitializeSecurityDescriptor(&pSD, SECURITY_DESCRIPTOR_REVISION);    //將SECURITY_DESCRIPTOR結構加入到安全性描述元中    SECURITY_ATTRIBUTES sa = {0};    sa.bInheritHandle = FALSE;    sa.lpSecurityDescriptor = pSD;    sa.nLength = sizeof(SECURITY_ATTRIBUTES);    HKEY hkSub = NULL;    DWORD dwDisposition = 0;    RegCreateKeyEx(HKEY_CURRENT_USER, _T("mykey"), 0, _T(""), 0,    KEY_READ | KEY_WRITE, &sa, &hkSub, &dwDisposition);    if (pEveryOneSid)     {        FreeSid(pEveryOneSid);    }    if (pAdminSid)     {        FreeSid(pAdminSid);    }    if (pAcl)     {        LocalFree(pAcl);    }    HeapFree(GetProcessHeap(), 0, pSD);    if (hkSub)    {        RegCloseKey(hkSub);    }

windows 安全模型簡介

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.