Windows下的关机函数是ExitWindowEx, 函数原型为:
BOOL WINAPI ExitWindowsEx(
__in UINT uFlags,
__in DWORD dwReason
)
功能上有“关闭系统”,“注销用户”,“锁定工作站”3中操作。在Windows NT内核的操作系统中,进程需要拥有SE_SHUTDOWN_NAME权限才能成功调用此函数。
进程的权限记录在进程的令牌当中,进程的令牌是一种核心对象,它保存了用户的安全配置信息,例如用户的SID、组的UID以及各种权限等,令牌也记录一个已登录会话的引用和内核使用的默认安全设置。进程的令牌在进程被创建时产生,继承于父进程。下面来看如何是区别当前操作系统版本,以及使本进程拥有SE_SHUTDOWN_NAME权限。
Windows的系统的版本信息使用一个结构体OSVERSIONINFO表示,定义如下:
typedef struct _OSVERSIONINFO {
DWORD dwOSVersionInfoSize; //本结构体的大小
DWORD dwMajorVersion; //主版本号
DWORD dwMinorVersion; //副版本号
DWORD dwBuildNumber; //创建号
DWORD dwPlatformId; //平台ID,用于区分是否NT内核
TCHAR szCSDVersion[128]; //记录补丁之类的信息
} OSVERSIONINFO;
获取系统的版本信息的函数是GetVersionEx, 函数原型为:
BOOL GetVersionEx(
LPOSVERSIONINFO lpVersionInformation // 指向存储获得信息的结构体
)
现在,我们已经知道关机程序的大体流程如下:
...
OSVERSIONINFO verinfo;
verinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
if(GetVersionEx(&verinfo) == 0) { //获得版本信息
printf("error in GetVersionEx \n");
return 0;
}
if(verinfo.dwPlatformId == VER_PLATFORM_WIN32_NT) {
... //获得SE_SHUTDOWN_NAME权限
}
ExitWindowsEx(EWX_FORCE, 0); //关机
下面来看获得权限一部分。要使进程的权限改变,就是改变他的令牌的属性。首先,得获取现有进程的令牌,使用函数OpenProcessToken, 他的原型如下:
BOOL WINAPI OpenProcessToken(
__in HANDLE ProcessHandle, //进程的句柄
__in DWORD DesiredAccess, //令牌的访问权限
__out PHANDLE TokenHandle //存储获得的令牌的指针
)
本进程的句柄可以用函数GetCurrentProcess()函数得到。要使进程拥有SE_SHUTDOWN_NAME权限,实际上就是使该进程的令牌拥有该权限对应的LUID值。获得某个权限对应的LUID值可以使用函数LookupPrivilegeValue,原型如下:
BOOL WINAPI LookupPrivilegeValue(
__in_opt LPCTSTR lpSystemName, //系统名,本系统为NULL
__in LPCTSTR lpName, //权限名
__out PLUID lpLuid //获得的LUID
)
知道了这些之后,将进程的令牌对照进行调整,进程便拥有我们想要的权限了,这里用到函数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
)
现在我们也知道了获得SE_SHUTDOWN_NAME权限的大体流程:
HANDLE token;
TOKEN_PRIVILEGES tp;
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token) ){
LookupPrivilegeValue(NULL,SE_SHUTDOWN_NAME, &(tp.Privileges[0].Luid));
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(token, FALSE, &tp, 0, NULL, 0);
}
总程序如下:
#include <windows.h>
#include <stdio.h>
int main()
{
OSVERSIONINFO verinfo;
HANDLE token;
TOKEN_PRIVILEGES tp;
verinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
if(GetVersionEx(&verinfo) == 0)
{
printf("error in GetVersionEx \n");
return 0;
}
if(verinfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
{
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token) )
{
LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &(tp.Privileges[0].Luid));
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(token, FALSE, &tp, 0, NULL, 0);
}
}
ExitWindowsEx(EWX_FORCE, 0);
return 0;
}