Sunshine
我们对计算机锁屏、注销、关机通常都是点击鼠标去操作,但是,大家有没有试过编程去实现。
或许对命令行熟悉的读者,会想到可以使用CMD命令去实现注销、关机、重启等操作。是的,这个可以实现,而且使用命令行实现,是本文讲解的一种实现方法之一。另一种方法,就是使用Win32 API函数 ExitWindowsEx 去实现注销、关机等操作,使用 LockWorkStation 函数实现锁屏操作。
现在,本文就把实现过程和原理写成文档,分享给大家。
可以锁定工作站保护其免受未经授权者使用。
函数声明
BOOL WINAPI LockWorkStation(void);
参数
- 无参数
返回值
- 如果函数成功,则返回值为非零值。由于该函数以异步方式执行,返回非零值指示操作已启动。它并不表示是否已成功锁定工作站。
用来退出、重启或注销系统。
函数声明
BOOL ExitWindowsEx(
UINT uFlags, // 关闭参数
DWORD dwReserved // 系统保留,一般取0
)
参数
- uFlags
指定关闭的类型。此参数必须有下列值的组合:
VALUE MEANING EWX_FORCE 强制终止进程。当此标志设置,Windows不会发送消息WM_QUERYENDSESSION和WM_ENDSESSION的消息给目前在系统中运行的程序。这可能会导致应用程序丢失数据。因此,你应该只在紧急情况下使用此标志。 EWX_LOGOFF 关闭所有进程,然后注销用户。 EWX_POWEROFF 关闭系统并关闭电源。该系统必须支持断电。Windows NT中调用进程必须有 SE_SHUTDOWN_NAME 特权。 EWX_REBOOT 关闭系统,然后重新启动系统。Windows NT中:调用进程必须有SE_SHUTDOWN_NAME特权。 EWX_SHUTDOWN 关闭系统,安全地关闭电源。所有文件缓冲区已经刷新到磁盘上,所有正在运行的进程已经停止。Windows NT中:调用进程必须有SE_SHUTDOWN_NAME特权。
- dwReserved
系统保留,这参数被忽略。一般取0。返回值
- 如果函数成功,返回值为非零。
- 如果函数失败,返回值是零。想获得更多错误信息,请调用GetLastError函数。
我们可以使用命令行 shutdown 指令来实现注销、关机和重启等功能,但这个指令支持实现锁屏功能。
Win32 API 函数 LockWorkStation 支持实现锁屏功能。
Win32 API 函数 ExitWindowsEx 支持实现注销、关机、重启、关电源等功能。而且,实现这些操作需要有权限才行。
所以,对于使用命令行方式,我们可以使用函数 system 去实现CMD命令。
关机的CMD指令是:
system("shutdown -p");
// 或者
system("shutdown -s");
注销的CMD命令是:
system("shutdown -l");
重启的CMD命令是:
system("shutdown -r");
那么对于使用API函数来实现的话,我们直接调用相应的API。
锁屏实现的核心代码是:
::LockWorkStation();
系统注销实现的核心代码是:
::ExitWindowsEx(EWX_LOGOFF, 0)
系统关机实现的核心代码是:
::ExitWindowsEx(EWX_SHUTDOWN, 0)
系统关机实现的核心代码是:
::ExitWindowsEx(EWX_REBOOT, 0)
需要注意的是,如果使用 ExitWindowsEx 函数来实现关机和重启的操作,需要进程必须要有 SE_SHUTDOWN_NAME 权限,如果没有,则必须先提权,才可以成功使用此函数。因为进程提权的知识,超出了本文讲解的内容范畴,所以在本文就不详细讲解进程提权的操作了,只给出实现代码。
// 进程提权
BOOL EnableProcessPrivilege()
{
HANDLE hToken;
if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
{
ShowError("OpenProcessToken");
return FALSE;
}
TOKEN_PRIVILEGES tkp;
// 修改进程权限
if (!::LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid))
{
ShowError("LookupPrivilegeValue");
return FALSE;
}
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// 通知系统修改进程权限
if (!::AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL))
{
ShowError("AdjustTokenPrivileges");
return FALSE;
}
return TRUE;
}
// CMD方式实现 注销
BOOL CMD_LogOff()
{
system("shutdown -l");
return TRUE;
}
// CMD方式实现 关机
BOOL CMD_Shutdown()
{
system("shutdown -p");
return TRUE;
}
// CMD方式实现 重启
BOOL CMD_Reboot()
{
system("shutdown -r");
return TRUE;
}
// API方式实现 锁屏
BOOL API_Lock()
{
BOOL bRet = ::LockWorkStation();
return bRet;
}
// API方式实现 注销
BOOL API_LogOff()
{
BOOL bRet = ::ExitWindowsEx(EWX_LOGOFF, 0);
return bRet;
}
// API方式实现 关机
BOOL API_Shutdown()
{
BOOL bRet = ::ExitWindowsEx(EWX_SHUTDOWN, 0);
return bRet;
}
// API方式实现 重启
BOOL API_Reboot()
{
BOOL bRet = ::ExitWindowsEx(EWX_REBOOT, 0);
return bRet;
}
需要注意的是,如果使用 ExitWindowsEx 函数来实现关机和重启的操作,需要进程必须要有 SE_SHUTDOWN_NAME 权限,如果没有,则必须先提权,才可以成功使用此函数。
注意,此程序的功能实现不需要管理员权限就可以实现,上面提到的提升进程权限,并不是指获取管理员权限,而是指将修改进程令牌,实现进程权限的提升。而本文,进程提升到 SE_SHUTDOWN_NAME 权限不需要程序以管理员权限运行就可以成功提升。而对于其它的进程权限提升,例如进程提升到 SE_DEBUG_NAME 权限,则需要程序以管理员权限运行方可成功提权。
参考自《Windows黑客编程技术详解》一书
keyboard_arrow_left上一篇 : 内存快速搜索遍历 磁盘盘符隐藏并访问隐藏磁盘的文件数据 : 下一篇keyboard_arrow_right