惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Apple Machine Learning Research
Apple Machine Learning Research
M
MIT News - Artificial intelligence
罗磊的独立博客
博客园 - 【当耐特】
A
About on SuperTechFans
Last Week in AI
Last Week in AI
雷峰网
雷峰网
IT之家
IT之家
aimingoo的专栏
aimingoo的专栏
H
Hackread – Cybersecurity News, Data Breaches, AI and More
博客园_首页
博客园 - 叶小钗
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - Franky
J
Java Code Geeks
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
D
Docker
Engineering at Meta
Engineering at Meta
B
Blog RSS Feed
The Cloudflare Blog
大猫的无限游戏
大猫的无限游戏
阮一峰的网络日志
阮一峰的网络日志
S
SegmentFault 最新的问题
Recent Announcements
Recent Announcements

博客园 - Max Woods

Delphi XE8,C++ Builder XE8,RAD Studio XE8 官方 ISO 文件下载,附激活工具 PC 安装MAC InnoSetup能够实现“安装细节描述”界面吗? 在Unicode版Inno Setup中使用ISSkin Inno Setup使用技巧 用非管理员权限启动主程序,并用管理员权限启动子程序,导致WM_COPYDATA消息发送失败的问题 Inno setup 安装*.inf文件_示例 自动安装驱动程序代码 自动安装驱动程序 windows xp/7命令提示符强制结束指定进程 Delphi 如何让程序获取权限结束指定进程? Windows下如何枚举所有进程 使用MFC提供的Http类下载和上传文件 如何在安装程序中判断操作系统是否是64位 inno INNO setup安装卸载钱判断进程中是否在运行总结 innosetup安装之前关闭进程 INNO安装卸载自动结束进程插件使用 Inno Setup 卸载前关闭进程或服务 x86 x64 《Windows核心编程》学习笔记(9)– 在win7或者vista系统下提升一个进程的运行权限
Windows下提升进程权限
Max Woods · 2014-11-30 · via 博客园 - Max Woods

windows的每个用户登录系统后,系统会产生一个访问令牌(access token) ,其中关联了当前用户的权限信息,用户登录后创建的每一个进程都含有用户access token的拷贝,当进程试图执行某些需要特殊权限的操作或是访问受保护的内核对象时,系统会检查其acess token中的权限信息以决定是否授权操作。Administrator组成员的access token中会含有一些可以执行系统级操作的特权(privileges) ,如终止任意进程、关闭/重启系统、加载设备驱动和更改系统时间等,不过这些特权默认是被禁用的,当Administrator组成员创建的进程中包含一 些需要特权的操作时,进程必须首先打开这些禁用的特权以提升自己的权限,否则系统将拒绝进程的操作。注意,非Administrator组成员创建的进程 无法提升自身的权限,因此下面提到的进程均指Administrator组成员创建的进程。

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结构,其定义如下:

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

BOOL promoteProcessPrivileges(const TCHAR* newPrivileges)

{

    HANDLE tokenHandle;

    if(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &tokenHandle) == FALSE)

        return FALSE;

    TOKEN_PRIVILEGES structTkp;

    if(::LookupPrivilegeValue(NULL, newPrivileges, &structTkp.Privileges[0].Luid) == FALSE){

        CloseHandle(tokenHandle);

        return FASLE;

    }

    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;

}