一. 背景介绍
在 Windows 系统管理中,注销用户会话是一个常见的需求。Windows 提供了命令行工具 logoff 来实现这一功能,其基本语法如下:
LOGOFF [sessionname | sessionid] [/SERVER:servername] [/V] [/VM]
sessionname 指定要注销的会话名称
sessionid 指定要注销的会话 ID
/SERVER:servername 指定远程终端服务器(默认为本地服务器)
/V 显示详细的操作信息
/VM 注销虚拟机中的会话(需要指定唯一会话 ID)
通过逆向分析 logoff 命令的实现原理,我们发现它主要依赖于 Windows Terminal Services API(也称为 WinSta API)中的两个关键函数:
WinStationOpenServerW- 用于获取服务器句柄WinStationReset- 用于执行会话注销操作

通过调用这些底层 API,我们可以实现与 logoff 命令相同的功能:

二.编写调用代码
1.导出函数定义
Winsta API微软并没有公布文档,但还是能找到泄露的函数定义
https://processhacker.sourceforge.io/doc/winsta_8h.html
typedef HANDLE(WINAPI* WinStationOpenServerW_t)(_In_ PWSTR ServerName);
typedef BOOL(WINAPI* WinStationReset_t)(_In_opt_ HANDLE hServer, _In_ ULONG SessionId, _In_ BOOLEAN bWait);
2.加载DLL
HMODULE hModule = LoadLibrary(L"winsta.dll");
if (hModule == NULL)
{
cout << "Error [-]: failed loading winsta.dll" << endl;
return 1;
}
3.获取函数地址
WinStationOpenServerW_t WinStationOpenServerW = (WinStationOpenServerW_t)GetProcAddress(hModule, "WinStationOpenServerW");
if (WinStationOpenServerW == NULL)
{
cout << "Error [-]: failed getting WinStationOpenServerW function address" << endl;
return 1;
}
WinStationReset_t WinStationReset = (WinStationReset_t)GetProcAddress(hModule, "WinStationReset");
if (WinStationReset == NULL)
{
cout << "Error [-]: failed getting WinStationReset function address" << endl;
return 1;
}
4.获取计算机名称
WinStationOpenServerW函数需传入servername,这里使用GetComputerName函数获取名称
wchar_t serverName[MAX_COMPUTERNAME_LENGTH + 1];
DWORD size = sizeof(serverName) / sizeof(serverName[0]);
GetComputerNameW(serverName, &size);
if (serverName[0] == L'\0')
{
cout << "Error [-]: failed getting server name" << endl;
return 1;
}
5.调用函数
HANDLE hServer = WinStationOpenServerW(serverName);
if (hServer == NULL)
{
cout << "Error [-]: failed opening server" << endl;
}
if (WinStationReset(hServer, sessionID, TRUE))
{
cout << "Success [+]: session " << sessionID << " reset" << endl;
}
三.完整代码
#include<Windows.h>
#include<iostream>
using namespace std;
int main(int argc, char* argv[])
{
if (argc < 2)
{
cout << "Usage: "<< argv[0] <<" <sessionID>" << endl;
return 1;
}
auto sessionID = atoi(argv[1]);
typedef HANDLE(WINAPI* WinStationOpenServerW_t)(_In_ PWSTR ServerName);
typedef BOOL(WINAPI* WinStationReset_t)(_In_opt_ HANDLE hServer, _In_ ULONG SessionId, _In_ BOOLEAN bWait);
HMODULE hModule = LoadLibrary(L"winsta.dll");
if (hModule == NULL)
{
cout << "Error [-]: failed loading winsta.dll" << endl;
return 1;
}
WinStationOpenServerW_t WinStationOpenServerW = (WinStationOpenServerW_t)GetProcAddress(hModule, "WinStationOpenServerW");
if (WinStationOpenServerW == NULL)
{
cout << "Error [-]: failed getting WinStationOpenServerW function address" << endl;
return 1;
}
wchar_t serverName[MAX_COMPUTERNAME_LENGTH + 1];
DWORD size = sizeof(serverName) / sizeof(serverName[0]);
GetComputerNameW(serverName, &size);
if (serverName[0] == L'\0')
{
cout << "Error [-]: failed getting server name" << endl;
return 1;
}
WinStationReset_t WinStationReset = (WinStationReset_t)GetProcAddress(hModule, "WinStationReset");
if (WinStationReset == NULL)
{
cout << "Error [-]: failed getting WinStationReset function address" << endl;
return 1;
}
HANDLE hServer = WinStationOpenServerW(serverName);
if (hServer == NULL)
{
cout << "Error [-]: failed opening server" << endl;
return 1;
}
if (WinStationReset(hServer, sessionID, TRUE))
{
cout << "Success [+]: session " << sessionID << " reset" << endl;
}
else
{
cout << "Error [-]: failed to reset session " << sessionID << endl;
}
return 0;
}