1 實例
GetCurrentProcess獲取當前進程的句柄,OpenProcessToken函數用來打開與進程相關聯的訪問令牌,LookupPrivilegeValueW查看系統權限的特權值,返回信息到一個LUID結構體里(第三個參數)。NewState是一個TOKEN_PRIVILEGES結構體:
typedef struct _TOKEN_PRIVILEGES
{
ULONG PrivilegeCount; //數組元素的個數
LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY]; //數組.類型為LUID_AND_ATTRIBUTES
} TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES;
Attributes=2,即SE_PRIVILEGE_ENABLED時將打開Luid對應的特權,而禁用特權是設置Attributes=0。設置完成后,我們需要調用AdjustTokenPrivileges函數通知操作系統將指定的access token權限中的特權置為打開狀態
2 原理
來源:http://blog.csdn.net/zacklin/article/details/7663129
windows的每個用戶登錄系統后,系統會產生一個訪問令牌(access token),其中關聯了當前用戶的權限信息,用戶登錄后創建的每一個進程都含有用戶access token的拷貝,當進程試圖執行某些需要特殊權限的操作或是訪問受保護的內核對象時,系統會檢查其acess token中的權限信息以決定是否授權操作。Administrator組成員的access token中會含有一些可以執行系統級操作的特權(privileges),如終止任意進程、關閉/重啟系統、加載設備驅動和更改系統時間等(這里的特權和UAC中的不一樣,UAC主要是讀寫一些敏感的文件等,安裝程序等,這些是管理員權限,繞過UAC主要是不彈出那個提示框;這里的這些特權即使是管理員權限也需要調用代碼獲取(或者叫開啟),像一些注入里,經常會用到這些特權提升代碼),不過這些特權默認是被禁用的,當Administrator組成員創建的進程中包含一些需要特權的操作時,進程必須首先打開這些禁用的特權以提升自己的權限,否則系統將拒絕進程的操作。注意,(貌似win7以后)非Administrator組成員創建的進程無法提升自身的權限,因此下面提到的進程均指Administrator組成員創建的進程。
Windows以字符串的形式表示系統特權,如“SeCreatePagefilePrivilege”表示該特權用于創建頁面文件,“SeDebugPrivilege”表示該特權可用于調試及更改其它進程的內存,為了便于在代碼中引用這些字符串,微軟在winnt.h中定義了一組宏,如 #define SE_DEBUG_NAME TEXT("SeDebugPrivilege")。完整的特權列表可以查閱msdn的security一章。
下列只是一部分,完整的:https://msdn.microsoft.com/en-us/library/windows/desktop/bb530716(v=vs.85).aspx; https://msdn.microsoft.com/en-us/library/windows/desktop/aa375728(v=vs.85).aspx#privilege_constants
SeAssignPrimaryTokenPrivilege ? ? 替換經常級令牌
SeBackupPrivilege? ? ? ? ? ? ? ? ? 備份文件和目錄
SeDebugPrivilege? ? ? ? ? ? ? ? ? ? 調試程序
SeIncreaseQuotaPrivilege? ? ? ? ? 調整進程的內存配額
SeTcbPrivilege? ? ? ? ? ? ? ? ? ? ? 作為操作系統的一部分
? ? ? ? ? ?
雖然Windows使用字符串表示特權,但查詢或更改特權的API需要LUID來引用相應的特權,LUID表示local unique identifier,它是一個64位值,在當前系統中是唯一的。為了提升進程權限到指定的特權,我們必須先找到該特權對應的LUID,這時要調用LookupPrivilegeValue函數。
獲得特權對應的LUID之后,我們要打開該特權。此時要用到LUID_AND_ATTRIBUTES結構,其定義如下:
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結構的指針,定義如下:
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權限的提升了。
下面是一個實際的例子,用來將執行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;
}
測試結果不再列出,有興趣的朋友可以自己試一試。