使用某些Windows API的时候需要提升进程的默认权限,例如RegRestoreKey需要SE_RESTORE_NAME 和SE_BACKUP_NAME 权限。在这种情况下,我们需要使用到一组Windows API提升进程权限。需要的函数有:

1.OpenProcessToken
2.LookupPrivilegeValue
3.AdjustTokenPrivileges

使用这组函数提升权限的前提是进程具备该权限,只是访问令牌中没有启用该权限。如果进程的访问令牌中本身就没有关联该权限,这AdjustTokenPrivileges函数调用将会返回ERROR_NOT_ALL_ASSIGNED(值为1300L)的错误码。如何让进程具有该权限?可以通过“控制面板”—“管理工具”—“本地安全策略”—“本地策略”—“用户权利指派”设置将该权限关联到指定的用户分组或用户上。具体设置如下图所示:

win32process 设置进程名称 进程权限的设置_win32process 设置进程名称

win7之本地安全策略启动:

1.单击“控制面板”--“系统和安全”--“管理工具”--“本地安全策略”,会进入“本地安全策略”界面。 

2..win+R--secpol.msc,也可以。



利用AdjustTokenPrivileges提升权限,准确的说不是提升,而是将访问令牌中禁用的权限启用。


BOOL SetPrivilege(
		HANDLE hToken,          // access token handle
		LPCTSTR lpszPrivilege,  // name of privilege to enable/disable
		BOOL bEnablePrivilege   // to enable or disable privilege
		) 
{
	TOKEN_PRIVILEGES tp;
	LUID luid;

	if ( !LookupPrivilegeValue( 
		NULL,            // lookup privilege on local system
		lpszPrivilege,   // privilege to lookup 
		&luid ) )        // receives LUID of privilege
	{
		printf("LookupPrivilegeValue error: %u\n", GetLastError() ); 
		return FALSE; 
	}

	tp.PrivilegeCount = 1;
	tp.Privileges[0].Luid = luid;
	if (bEnablePrivilege)
		tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	else
		tp.Privileges[0].Attributes = 0;

	// Enable the privilege or disable all privileges.

	if ( !AdjustTokenPrivileges(
		hToken, 
		FALSE, 
		&tp, 
		sizeof(TOKEN_PRIVILEGES), 
		(PTOKEN_PRIVILEGES) NULL, 
		(PDWORD) NULL) )
	{ 
		printf("AdjustTokenPrivileges error: %u\n", GetLastError() ); 
		return FALSE; 
	} 

	if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)

	{
		printf("The token does not have the specified privilege. \n");
		return FALSE;
	} 

	return TRUE;
}

void main( )
{

	HANDLE hToken;
	BOOL bRet = OpenProcessToken(GetCurrentProcess(),TOKEN_ALL_ACCESS,&hToken);
	SetPrivilege(hToken,SE_DEBUG_NAME,TRUE);
}



这段代码在xp上没有问题,但

如果在windows 7 或者vista上,如果程序以标准用户启动,AdjustTokenPrivileges将会调用失败,以管理员身份启动没有问题。



这是因为在Windows 7上,标准用户权限很少,没有Debug权限,更无从谈起启用Debug权限,用户可以以管理员和标准用户两种方式启用控制台,输入命令whoami /ALL

来查看两种权限下权限的不同。

PS:即使提升调试权限,也不意味着对其它进程调用OpenProcess会成功(例如win7系统下的system和audiodg进程)


下面解释这三个函数:

首先需要获取进程的令牌句柄
OpenProcessToken的原型.


BOOL WINAPI OpenProcessToken( 
  __in          HANDLE ProcessHandle, 
  __in          DWORD DesiredAccess, 
  __out         PHANDLE TokenHandle 
);



第一个参数 进程句柄(当前进程为GetCurrentProcess()为参数)


第二个参数 访问令牌特权


第三个参数 返回的参数 就是AdjustTokenPrivileges的第一个参数


例子:



HANDLE hToken; 
    
bool retn = OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&hToken); 
    
if(!retn) 
    
{ 
    
      return; //获取令牌失败。。 
    
}



注:第二个参数 是令牌的权限,这个权限是要有修改权限的特权,意思就是要把你程序的权限修改得更高。


关于其他权限可以查MSDN.. 所有权限可以写TOKEN_ALL_ACCESS ,去查看一个令牌特权可以用TOKEN_QUERY


这个下面函数有写。


--------------------------------------------------------------


首先先说一下使用

AdjustTokenPrivileges需要的。


在这个函数中的第3和第5个参数中需要用到一个TOKEN_PRIVILEGES的结构体,在这个结构体中还有个LUID_AND_ATTRIBUTES结构体


TOKEN_PRIVILEGES结构体



typedef struct _TOKEN_PRIVILEGES 
    
{ 
       DWORD PrivilegeCount;  
       LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY]; 
}TOKEN_PRIVILEGES;



下面的参数是个特权数组。


上面的参数是要修改的特权数目


LUID_AND_ATTRIBUTES 结构体



typedef struct _LUID_AND_ATTRIBUTES  
{   
       LUID Luid;   
       DWORD Attributes; 
} LUID_AND_ATTRIBUTES;



第一个参数是Luid是一个标志,不同的Luid代表着各种不同的特权类型


第二个参数是要这个特权干嘛,如启用这个特权(SE_PRIVILEGE_ENABLED)


这里的Luid的值需要用

LookupPrivilegeValue来获取。


------------------------------------------------------------------------------------------


LookupPrivilegeValue的原型



BOOL WINAPI LookupPrivilegeValue( 
  __in_opt      LPCTSTR lpSystemName, 
  __in          LPCTSTR lpName, 
  __out         PLUID lpLuid 
);



第一个参数是系统的名字,如果为NULL,就是本地名字(这里就填NULL)


第二个参数是特权的名字,要查看详细特权,看我的博客里翻译分类里的 包含特权 的文章。(在这里写SE_DEBUG_NAME)


第三个参数就可以通过指针返回一个LUID类型的Luid的标识了。 通过这个值就可以填入刚才的结构体里了。


----------------------------------------------------------------------------------------------------------------------------


最后就可以介绍

AdjustTokenPrivilege就应该没什么问题了。


它的原型为:



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 
);

第一个参数为OpenProcessToken第三个指针参数传出的句柄值


第二个参数为是否禁用所有所有的特权(这里填false)


第三个参数为新的TOKEN_PRIVILEGES的特权结构体指针


第四个参数是上面结构体的字节长度(sizeof)


第五个参数是 接受原先的特权的结构体


第六个参数也是这个结构体的字节长度的指针


在这里后两个参数不用管。



MSDN里说


如果第五个参数不是NULL,在OpenProcessToken加特权时除了需要指定TOKEN_ADJUST_PRIVILEGES还必须指定TOKEN_QUERY


如果第五个参数是NULL,你不接受原先的结构体(第六个当然也是NULL), 就不用再指定附加的TOKEN_QUERY的特权了。


还要注意:


就算这个函数返回为真,还要调用GetLastError()来检验是否完全成功。如果

GetLastError()==ERROR_SUCCESS就代表修改非常成功 ,这个非常重要!!


还有就是Vista和Window7 里 一定要开管理员模式 才能获取成功


----------------------------------------------------


完整的例子


可以直接复制运行



#include <windows.h>    
#include <iostream>    
using namespace std;    
     
     
void main()    
{    
        BOOL retn;    
        HANDLE hToken;    
        retn = OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&hToken);    
        if(retn != TRUE)    
        {    
                    cout<<"获取令牌句柄失败!"<<endl;    
                    return;    
        }    
       
        TOKEN_PRIVILEGES tp; //新特权结构体    
        LUID Luid;    
        retn = LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&Luid);    
     
        if(retn != TRUE)    
        {    
                    cout<<"获取Luid失败"<<endl;    
                    return;    
        }    
                //给TP和TP里的LUID结构体赋值    
        tp.PrivilegeCount = 1;    
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;    
        tp.Privileges[0].Luid = Luid;    
          
        AdjustTokenPrivileges(hToken,FALSE,&tp,sizeof(TOKEN_PRIVILEGES),NULL,NULL);    
        if(GetLastError() != ERROR_SUCCESS)    
        {    
                    cout<<"修改特权不完全或失败!"<<endl;    
        }    
       else  
       {    
                    cout<<"修改成功!"<<endl;    
       }    
}