在我們編程實現一些系統操作的時候,往往要求我們執行操作的進程擁有足夠的權限方可成功操作。比如,我們使用 ExitWindows 函數實現關機或重啟操作的時候,就要求我們的進程要有 SE_SHUTDOWN_NAME 的權限,否則,會忽視不執行操作。這時,我們唯一能夠做的,就是按照要求,提升我們進程的權限。
函數介紹
/*
打開與進程關聯的訪問令牌。
如果函數成功,則返回值不為零。
*/
BOOL WINAPI OpenProcessToken(
_In_ HANDLE ProcessHandle, // 打開與進程關聯的訪問令牌。
_In_ DWORD DesiredAccess, // 指定一個訪問掩碼,指定訪問令牌的請求類型。
_Out_ PHANDLE TokenHandle // 指向一個句柄的指針,用於標識當函數返回時新打開的訪問令牌。
);
/*
查看系統權限的特權值,返回信息到一個LUID結構體里。
如果函數成功,函數將返回非零值
*/
BOOL WINAPI LookupPrivilegeValue(
_In_opt_ LPCTSTR lpSystemName, // 指向以NULL結尾的字符串的指針,該字符串是指向要獲取特權值的系統名稱
_In_ LPCTSTR lpName, // 指向空終止字符串的指針,指定特權的名稱
_Out_ PLUID lpLuid // 指向LUID變量的指針,該變量接收由lpSystemName參數指定的系統上已知權限的LUID。
);
/*
啟用或禁用指定的訪問令牌中的權限
如果函數成功,則返回值不為零
*/
BOOL WINAPI AdjustTokenPrivileges(
_In_ HANDLE TokenHandle, // 訪問令牌的句柄,其中包含要修改的權限
_In_ BOOL DisableAllPrivileges, // 指定該功能是否禁用所有令牌的權限
_In_opt_ PTOKEN_PRIVILEGES NewState, // 指向TOKEN_PRIVILEGES結構的指針,該結構指定特權數組及其屬性
_In_ DWORD BufferLength, // 指定由PreviousState參數指向的緩沖區的大小
_Out_opt_ PTOKEN_PRIVILEGES PreviousState, // 接收修改權限的完整列表
_Out_opt_ PDWORD ReturnLength // 接收由PreviousState參數指向的緩沖區所需的大小
);
實現過程
首先,我們需要調用 OpenProcessToken 函數打開指定進程令牌,並獲取 TOKEN_ADJUST_PRIVILEGES 權限的令牌句柄。之所以要獲取進程令牌權限為 TOKEN_ADJUST_PRIVILEGES,是因為 AdjustTokenPrivileges 函數,要求要有此權限,方可修改進程令牌的訪問權限。
其中,第 1 個參數表示要打開進程令牌的進程句柄;第 2 個參數表示我們對進程令牌具有的權限,TOKEN_ADJUST_PRIVILEGES就表示,我們有修改進程令牌的權限;第 3 個參數表示返回的進程令牌句柄。
//打開進程令牌並獲取具有 TOKEN_ADJUST_PRIVILEGES 權限的進程令牌句柄
bRet = ::OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken);
if (FALSE == bRet)
{
ShowError("OpenProcessToken");
return FALSE;
}
然后,我們調用 LookupPrivilegeValue 函數,獲取本地系統指定特權名稱的LUID值,這個LUID值就相當於該特權的身份標號。
其中,第 1 個參數表示系統,NULL表示本地系統,即要獲取本地系統的指定特權的LUID值;第 2 個參數表示特權名稱;第 3 個參數表示獲取到的LUID返回值。
// 獲取本地系統的 pszPrivilegesName 特權的LUID值
bRet = ::LookupPrivilegeValue(NULL, pszPrivilegesName, &luidValue);
if (FALSE == bRet)
{
ShowError("LookupPrivilegeValue");
return FALSE;
}
接着,我們就開始對 TOKEN_PRIVILEGES 進程令牌特權結構體進行賦值設置,設置設置新特權的數量、特權對應的LUID值以及特權的屬性狀態。其中,tokenPrivileges.PrivilegeCount表示設置新特權的特權數量;tokenPrivileges.Privileges[i].Luid表示第 i 個特權對應的LUID值;tokenPrivileges.Privileges[0].Attributes表示特權的屬性;SE_PRIVILEGE_ENABLED就表示啟用該特權。
// 設置提升權限信息
tokenPrivileges.PrivilegeCount = 1;
tokenPrivileges.Privileges[0].Luid = luidValue;
tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
最后,我們調用 AdjustTokenPrivileges 函數對進程令牌的特權進行修改,將上面設置好的新特權設置到進程令牌中。
其中,第 1個參數表示進程令牌;第 2 個參數表示能是否禁用所有令牌的權限,FALSE則不禁用;第 3個參數是新設置的特權,指向設置好的令牌特權結構體;第 4 個參數表示返回上一個特權數據緩沖區的大小,不獲取,則可以設為 0;第 5 個參數表示返回上一個特權數據緩沖區,不接收返回數據,可以設為 NULL;第 6 個參數表示接收返回上一個特權數據緩沖區應該有的大小。
// 提升進程令牌訪問權限
bRet = ::AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, 0, NULL, NULL);
if (FALSE == bRet)
{
ShowError("AdjustTokenPrivileges");
return FALSE;
}
但是,需要注意的是,AdjustTokenPrivileges 返回 TRUE,並不代表特權就設置成功,還需要使用 GetLastError 來判斷錯誤嗎返回值。若錯誤碼返回值為ERROR_SUCCESS,則所有特權設置成功;若為 ERROR_NOT_ALL_ASSIGNED,則表示並不是所有特權都設置成功。
dwRet = ::GetLastError();
if (ERROR_SUCCESS == dwRet)
{
return TRUE;
}
else if (ERROR_NOT_ALL_ASSIGNED == dwRet)
{
ShowError("ERROR_NOT_ALL_ASSIGNED");
return FALSE;
}
換句話說,如果你只提升了一個特權,且錯誤碼為ERROR_NOT_ALL_ASSIGNED,那么這就是說明提升失敗了。如果程序運行在 Win7 或者 Win7 以上版本的操作系統,可以試着以管理員身份運行程序,這樣就可以成功提升進程令牌的訪問權限。
編碼實現
BOOL EnbalePrivileges(HANDLE hProcess, char *pszPrivilegesName)
{
HANDLE hToken = NULL;
LUID luidValue = {0};
TOKEN_PRIVILEGES tokenPrivileges = {0};
BOOL bRet = FALSE;
DWORD dwRet = 0;
// 打開進程令牌並獲取進程令牌句柄
bRet = ::OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken);
if (FALSE == bRet)
{
ShowError("OpenProcessToken");
return FALSE;
}
// 獲取本地系統的 pszPrivilegesName 特權的LUID值
bRet = ::LookupPrivilegeValue(NULL, pszPrivilegesName, &luidValue);
if (FALSE == bRet)
{
ShowError("LookupPrivilegeValue");
return FALSE;
}
// 設置提升權限信息
tokenPrivileges.PrivilegeCount = 1;
tokenPrivileges.Privileges[0].Luid = luidValue;
tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// 提升進程令牌訪問權限
bRet = ::AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, 0, NULL, NULL);
if (FALSE == bRet)
{
ShowError("AdjustTokenPrivileges");
return FALSE;
}
else
{
// 根據錯誤碼判斷是否特權都設置成功
dwRet = ::GetLastError();
if (ERROR_SUCCESS == dwRet)
{
return TRUE;
}
else if (ERROR_NOT_ALL_ASSIGNED == dwRet)
{
ShowError("ERROR_NOT_ALL_ASSIGNED");
return FALSE;
}
}
return FALSE;
}
提權
class CEnablePriv
{
public: //公有(對外開放的接口)
//
//設置當前進程優先級為最高(實時)
//
BOOL SetRealTimePriority();
//
//提升當前進程權限函數("SeShutdownPrivilege"關機權限)
//
BOOL EnableShutdownPriv();
//
//提升當前進程權限函數("SeDebugPrivilege"讀、寫控制權限)
//
BOOL EnableDebugPriv();
//
//提升當前進程權限函數("SeBackupPrivilege"注冊表備份權限)
//
BOOL EnableBackupPriv();
//
//提升當前進程權限函數("SeRestorePrivilege"恢復數據權限)
//
BOOL EnableRestorePriv();
private: //私有(內部使用的接口)
};
//
//設置當前進程優先級為最高(實時)
//
//返回值:“false”是失敗,“true”是成功。
BOOL CEnablePriv::SetRealTimePriority()
{
if (!SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS))
{
return false;
}
return true;
}
//
//提升當前進程權限函數("SeShutdownPrivilege"關機權限)
//
//返回值:“false”是失敗,“true”是成功。
BOOL CEnablePriv::EnableShutdownPriv()
{
HANDLE hToken;
LUID sedebugnameValue;
TOKEN_PRIVILEGES tkp;
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
return false;
if (!LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &sedebugnameValue))
{
CloseHandle(hToken);
return false;
}
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = sedebugnameValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof tkp, NULL, NULL))
CloseHandle(hToken);
return true;
}
//
//提升當前進程權限函數("SeDebugPrivilege"讀、寫控制權限)
//
//返回值:“false”是失敗,“true”是成功。
BOOL CEnablePriv::EnableDebugPriv()
{
HANDLE hToken;
LUID sedebugnameValue;
TOKEN_PRIVILEGES tkp;
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
return false;
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue))
{
CloseHandle(hToken);
return false;
}
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = sedebugnameValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof tkp, NULL, NULL))
CloseHandle(hToken);
return true;
}
//
//提升當前進程權限函數("SeBackupPrivilege"備份數據權限)
//
//返回值:“false”是失敗,“true”是成功。
BOOL CEnablePriv::EnableBackupPriv()
{
HANDLE hToken;
LUID sedebugnameValue;
TOKEN_PRIVILEGES tkp;
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
return false;
if (!LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &sedebugnameValue))
{
CloseHandle(hToken);
return false;
}
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = sedebugnameValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof tkp, NULL, NULL))
CloseHandle(hToken);
return true;
}
//
//提升當前進程權限函數("SeRestorePrivilege"恢復數據權限)
//
//返回值:“false”是失敗,“true”是成功。
BOOL CEnablePriv::EnableRestorePriv()
{
HANDLE hToken;
LUID sedebugnameValue;
TOKEN_PRIVILEGES tkp;
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
return false;
if (!LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &sedebugnameValue))
{
CloseHandle(hToken);
return false;
}
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = sedebugnameValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof tkp, NULL, NULL))
CloseHandle(hToken);
return true;
}
調用
CEnablePriv a;
if (a.EnableBackupPriv())
{
MessageBox(NULL, L"EnableBackupPriv success", L"", NULL);
}
if (a.EnableDebugPriv())
{
MessageBox(NULL, L"EnableDebugPriv success.", L"", NULL);
}
if (a.EnableRestorePriv())
{
MessageBox(NULL, L"EnableRestorePriv success .", L"", NULL);
}
if (a.EnableShutdownPriv())
{
MessageBox(NULL, L"EnableShutdownPriv success.", L"", NULL);
}
if (a.SetRealTimePriority())
{
MessageBox(NULL, L"SetRealTimePriority success.", L"", NULL);
}