windows的每一個用戶登陸系統後,系統會產生一個訪問令牌(access token) ,其中關聯了當前用戶的權限信息,用戶登陸後建立的每個進程都含有用戶access token的拷貝,當進程試圖執行某些須要特殊權限的操做或是訪問受保護的內核對象時,系統會檢查其acess token中的權限信息以決定是否受權操做。Administrator組成員的access token中會含有一些能夠執行系統級操做的特權(privileges) ,如終止任意進程、關閉/重啓系統、加載設備驅動和更改系統時間等,不過這些特權默認是被禁用的,當Administrator組成員建立的進程中包含一 些須要特權的操做時,進程必須首先打開這些禁用的特權以提高本身的權限,不然系統將拒絕進程的操做。注意,非Administrator組成員建立的進程 沒法提高自身的權限,所以下面提到的進程均指Administrator組成員建立的進程。windows
Windows以字符串的形式表示系統特權,如「SeCreatePagefilePrivilege」表示該特權用於建立頁面文 件,「SeDebugPrivilege」表示該特權可用於調試及更改其它進程的內存,爲了便於在代碼中引用這些字符串,微軟在winnt.h中定義了一 組宏,如 #define SE_DEBUG_NAME TEXT("SeDebugPrivilege")。完整的特權列表能夠查閱msdn的security一章。雖然Windows使用字符串表示特權,但 查詢或更改特權的API須要LUID來引用相應的特權,LUID表示local unique identifier,它是一個64位值,在當前系統中是惟一的。爲了提高進程權限到指定的特權,咱們必須先找到該特權對應的LUID,這時要調用 LookupPrivilegeValue函數。數組
得到特權對應的LUID以後,咱們要打開該特權。此時要用到LUID_AND_ATTRIBUTES結構,其定義以下:ide
typedef struct _LUID_AND_ATTRIBUTES { LUID Luid; DWORD Attributes; } LUID_AND_ATTRIBUTES, * PLUID_AND_ATTRIBUTES;
Attributes取SE_PRIVILEGE_ENABLED時將打開Luid對應的特權。設置完成後,咱們須要調用 AdjustTokenPrivileges函數通知操做系統將指定的access token權限中的特權置爲打開狀態,前面咱們說過,進程執行須要特列權限的操做時系統將檢查其access token,所以更改了進程的access token特權設置,也就是更改了所屬進程的特權設置。AdjustTokenPrivileges函數的原型以下:函數
BOOL WINAPI AdjustTokenPrivileges( __in HANDLE TokenHandle, __in BOOL DisableAllPrivileges, __in_opt PTOKEN_PRIVILEGES NewState, __in DWORD BufferLength, __out_opt PTOKEN_PRIVILEGES PreviousState, __out_opt PDWORD ReturnLength );
TokenHandle是要更改特權設置的acess token的句柄,DisableAllPrivileges表示是否禁用該access token的全部特權,NewState用來傳遞要新的特權設置,注意它的類型是 PTOKEN_PRIVILEGES,PTOKEN_PRIVILEGES是TOKEN_PRIVILEGES結構的指針,定義以下:ui
typedef struct _TOKEN_PRIVILEGES { DWORD PrivilegeCount; LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY]; } TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES;
其中ANYSIZE_ARRAY被定義爲1,能夠看到TOKEN_PRIVILEGES中包含了用於設置特權信息的 LUID_AND_ATTRIBUTES結構,在使用時,只須要將PrivilegeCount賦爲1,而後把Privileges數組的第1個元素 (Privileges[0])的Luid域設置爲指定特權的Luid,再將其Attributes域設置爲SE_PRIVILEGE_ENABLED, 就能夠完成TokenHandle表示的access token權限的提高了。spa
下面是一個實際的例子,用來將執行promoteProcessPrivilege的當前進程的指定特權打開,函數參數爲指定的特權名,能夠傳遞其宏定義,也能夠是完整的字符串表示:操作系統
BOOL promoteProcessPrivileges(const TCHAR* newPrivileges) { HANDLE tokenHandle; //得到當前進程的access token句柄 if(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &tokenHandle) == FALSE) return FALSE; TOKEN_PRIVILEGES structTkp; //查找newPrivileges參數對應的Luid,並將結果寫入structTkp.Privileges[0]的Luid域中 if(::LookupPrivilegeValue(NULL, newPrivileges, &structTkp.Privileges[0].Luid) == FALSE){ CloseHandle(tokenHandle); return FASLE; } //設置structTkp結構 structTkp.PrivilegeCount = 1; structTkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; //通知操做系統更改權限 if(::AdjustTokenPrivileges(tokenHandle, FALSE, &structTkp, sizeof(structTkp), NULL, NULL) == FALSE){ CloseHandle(tokenHandle); return FALSE; } CloseHandle(tokenHandle); return TRUE; }
咱們來用一個簡單的例子驗證promoteProcessPrivileges函數。下面的traceSystemProcess用於遍歷當前系統進程, 若是調用traceSystemProcess函數的進程以默認權限運行,對於如csrss.exe之類的進程,函數將沒有足夠的權限得到其模塊名。若是 在traceSystemProcess以前調用了promoteProcessPrivileges將進程權限提高至SE_DEBUG_NAME級 別,traceSystemProcess函數將能正確打印出如csrss.exe等關鍵進程的路徑:指針
BOOL traceSystemProcess() { PROCESSENTRY32 processEntry; processEntry.dwSize = sizeof(processEntry); HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); if(hProcessSnap == INVALID_HANDLE_VALUE){ _tprintf(_T("CreateToolhelp32Snapshot 調用失敗!/n")); return FALSE; } BOOL bMore = ::Process32First(hProcessSnap, &processEntry); while(bMore){ _tprintf(_T("進程名稱:%s /n"), processEntry.szExeFile); _tprintf(_T("進程ID號:%u /n"), processEntry.th32ProcessID); HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processEntry.th32ProcessID); if(hProcess != NULL){ TCHAR szBuffer[MAX_PATH] = {_T('/0')}; if(::GetModuleFileNameEx(hProcess, NULL, szBuffer, MAX_PATH)){ _tprintf(_T("進程路徑:%s /n"), szBuffer); } ::CloseHandle(hProcess); } _tprintf(_T("/n")); bMore = ::Process32Next(hProcessSnap,&processEntry); } ::CloseHandle(hProcessSnap); return TRUE; }
BOOL
promoteProcessPrivileges(
const
TCHAR
* newPrivileges)
{
HANDLE
tokenHandle;
//得到當前進程的access token句柄
if
(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &tokenHandle) == FALSE)
return
FALSE;
TOKEN_PRIVILEGES structTkp;
//查找newPrivileges參數對應的Luid,並將結果寫入structTkp.Privileges[0]的Luid域中
if
(::LookupPrivilegeValue(NULL, newPrivileges, &structTkp.Privileges[0].Luid) == FALSE){
CloseHandle(tokenHandle);
return
FASLE;
}
//設置structTkp結構
structTkp.PrivilegeCount = 1;
structTkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
//通知操做系統更改權限
if
(::AdjustTokenPrivileges(tokenHandle, FALSE, &structTkp,
sizeof
(structTkp), NULL, NULL) == FALSE){
CloseHandle(tokenHandle);
return
FALSE;
}
CloseHandle(tokenHandle);
return
TRUE;
}
BOOL
traceSystemProcess()
{
PROCESSENTRY32 processEntry;
processEntry.dwSize =
sizeof
(processEntry);
HANDLE
hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
if
(hProcessSnap == INVALID_HANDLE_VALUE){
_tprintf(_T(
"CreateToolhelp32Snapshot 調用失敗!/n"
));
return
FALSE;
}
BOOL
bMore = ::Process32First(hProcessSnap, &processEntry);
while
(bMore){
_tprintf(_T(
"進程名稱:%s /n"
), processEntry.szExeFile);
_tprintf(_T(
"進程ID號:%u /n"
), processEntry.th32ProcessID);
HANDLE
hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
FALSE, processEntry.th32ProcessID);
if
(hProcess != NULL){
TCHAR
szBuffer[MAX_PATH] = {_T(
'/0'
)};
if
(::GetModuleFileNameEx(hProcess, NULL, szBuffer, MAX_PATH)){
_tprintf(_T(
"進程路徑:%s /n"
), szBuffer);
}
::CloseHandle(hProcess);
}
_tprintf(_T(
"/n"
));
bMore = ::Process32Next(hProcessSnap,&processEntry);
}
::CloseHandle(hProcessSnap);
return
TRUE;
}