注销远程用户(会话)

本文介绍了如何在Delphi和C++中利用WtsApi32库来实现远程服务器上的用户会话注销功能,帮助管理员有效管理远程桌面连接。

Delphi实现:

uses

WtsApi32;

//通过会话ID得到会话名称
procedure GetSessionUserName(dwSessionId: dword; var UserName: string);
var
  bRes: boolean;
  dwBufferLen: dword;
  Ptr: Pointer;
begin
  bRes := WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, dwSessionId, WTSUserName, Ptr, dwBufferLen);
  if bRes = false then
  begin  
       UserName := '';  
        exit;
  end;
  UserName := PWideChar(Ptr);
end;
//通过会话名称结束目标会话
procedure ExitByUserName(str: string);
var
  i: integer;
  sessionInfo: PWTS_SESSION_INFOW;
  sessionInfoCount: dword;
  dwSessionId: dword;
  UserName: string;
  CommandLine: string;
begin
  if WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, sessionInfo, sessionInfoCount) then
  begin
    for i := 0 to sessionInfoCount - 1 do
    begin
      //遍历注销状态为“活动”或者“断开”的所有目标会话 
      if (sessionInfo.state = WTSActive) or (sessionInfo.state = WTSDisconnected) then
      begin
        dwSessionId := sessionInfo.SessionId;
        GetSessionUserName(dwSessionId, UserName);
        if UserName = str then
        begin
          CommandLine := Format('LOGOFF %d', [dwSessionId]);
          winexec(PAnsiChar(ansistring(CommandLine)), SW_HIDE);
          Sleep(2500);
        end;
      end;
      inc(sessionInfo);
    end;
  end;
end;


 

 

C++实现:

//在其它session中(如远程桌面的session)运行指定的程序,需要具有system权限,可以在任意的桌面里运行指定程序
#include <windows.h>
#include <stdio.h>
#include <process.h>
#include <Tlhelp32.h>
#include <tchar.h>
#include <psapi.h>
#include <stdio.h>
#include <STDLIB.H>
#include <tlhelp32.h>
#include <WtsApi32.h>
#pragma comment(lib, "WtsApi32.lib")
#pragma  comment (lib,"psapi")


// Get username from session id
bool GetSessionUserName(DWORD dwSessionId, char username[256])
{
	LPTSTR pBuffer = NULL;
	DWORD dwBufferLen;
	BOOL bRes = WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, dwSessionId, WTSUserName, &pBuffer, &dwBufferLen);
	if (bRes == FALSE)
		return false;
	lstrcpy(username ,pBuffer);
	WTSFreeMemory(pBuffer);
	return true;
}

// Get domain name from session id
bool GetSessionDomain(DWORD dwSessionId, char domain[256])
{
	LPTSTR pBuffer = NULL;
	DWORD dwBufferLen;	
	BOOL bRes = WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, dwSessionId, WTSDomainName, &pBuffer, &dwBufferLen);	
	if (bRes == FALSE)
	{
		printf("WTSQuerySessionInformation Fail!\n");
		return false;
	}	
	lstrcpy(domain,pBuffer);
	WTSFreeMemory(pBuffer);	
	return true;
}



HANDLE GetProcessHandle(LPSTR szExeName)  //遍历进程PID
{	
	PROCESSENTRY32 Pc = { sizeof(PROCESSENTRY32) };	
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);	
	if(Process32First(hSnapshot, &Pc))
	{		
		do
		{	
			if(!stricmp(Pc.szExeFile, szExeName)) 
			{   //返回explorer.exe进程的PID
				printf("explorer's PID=%d\n",Pc.th32ProcessID);
				return OpenProcess(PROCESS_ALL_ACCESS, TRUE, Pc.th32ProcessID);	
            }		
		}while(Process32Next(hSnapshot, &Pc));	
    }	
	return NULL;
}


//输出帮助的典型方法:
void Usage (void)
{
	fprintf(stderr,"===============================================================================\n"
		"\t名称:在任意的远程桌面的session中运行指定的程序,需要具有system权限\n"
		"\t环境:Win2003 + Visual C++ 6.0\n"
		"\t作者:***\n"
		"\t  QQ:***\n"
		"\t声明:***!\n"
		"\n"
		"\t使用方法:\n"
		"\tsession 1 c:\\win2003\\system32\\svchosts.exe //在会话1里面运行程序!\n"
		"===============================================================================\n");
}

int main(int argc, char **argv)
{
	if(argc==1) //遍历所有的session		
	{// 函数的句柄
		HMODULE hInstKernel32    = NULL;	
		HMODULE hInstWtsapi32    = NULL;
		// 这里的代码用的是VC6,新版的SDK已经包括此函数,无需LoadLibrary了。
		typedef DWORD (WINAPI *WTSGetActiveConsoleSessionIdPROC)();	
		WTSGetActiveConsoleSessionIdPROC WTSGetActiveConsoleSessionId = NULL;	
		hInstKernel32 = LoadLibrary("Kernel32.dll");
		if (!hInstKernel32)		
		{		
			return FALSE;		
		}	
		WTSGetActiveConsoleSessionId = (WTSGetActiveConsoleSessionIdPROC)GetProcAddress(hInstKernel32,"WTSGetActiveConsoleSessionId");	
		if (!WTSGetActiveConsoleSessionId)			
		{
			return FALSE;
		}		
		// WTSQueryUserToken 函数,通过会话ID得到令牌		
		typedef BOOL (WINAPI *WTSQueryUserTokenPROC)(ULONG SessionId, PHANDLE phToken );
		WTSQueryUserTokenPROC WTSQueryUserToken = NULL;
		hInstWtsapi32 = LoadLibrary("Wtsapi32.dll");	
		if (!hInstWtsapi32)		
		{		
			return FALSE;		
		}
		WTSQueryUserToken = (WTSQueryUserTokenPROC)GetProcAddress(hInstWtsapi32,"WTSQueryUserToken");	
		if (!WTSQueryUserToken)	
		{	
			return FALSE;	
		}
		
			
		//遍历3389登录的session:
		/*
		typedef struct _WTS_SESSION_INFO {
                                  DWORD      SessionId;
                                  LPTSTR     pWinStationName;
                                  WTS_CONNECTSTATE_CLASS State;
		}WTS_SESSION_INFO, *PWTS_SESSION_INFO;
		*/
		WTS_SESSION_INFO *sessionInfo = NULL;
		DWORD sessionInfoCount;
		char domain1[256];
		char username1[256];
		BOOL result = WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &sessionInfo, &sessionInfoCount);
		unsigned int userCount(0);
		int num=0;
		for(unsigned int i = 0; i < sessionInfoCount; ++i)
		{
			if( (sessionInfo[i].State == WTSActive) || (sessionInfo[i].State == WTSDisconnected) )		
			{
                printf("session %d information:\n",num++);
                printf("\tsessionInfo.SessionId=%d\n",sessionInfo[i].SessionId);
                GetSessionDomain(sessionInfo[i].SessionId, domain1); //获得Session Domain
                printf("\tSession Domain = %s\n",domain1);
				
				GetSessionUserName(sessionInfo[i].SessionId,username1);
                printf("\tSession user's name = %s\n",username1);		
                userCount++;
			}
		}
		printf("session's number:%d\n\n",userCount);
		Usage();
		//printf("example:\n\tsession 1 c:\\win2003\\system32\\svchosts.exe //在会话1里面运行程序!\n");
		//printf("程序说明:在其它session中(如任意的远程桌面的session中)运行指定的程序,需要具有system权限\n");
		WTSFreeMemory(sessionInfo); //释放	
  }
  else if(argc==3) //session 1 c:\win2003\temp\klog.exe
  {  
	  // 得到当前登录用户的令
	  
	  /*  HANDLE hTokenDup = NULL;
	      bRes = WTSQueryUserToken(dwSessionId, &hTokenDup);
		  if (!bRes)
		  {
		    printf("WTSQueryUserToken Failed!%d\n",GetLastError()); 
			return FALSE;			
          }
	  */
	  
	  	  
      /*  bRes = ImpersonateLoggedOnUser(hTokenDup);
  
		  if (!bRes)
		  {
		    printf("ImpersonateLoggedOnUser!%d\n",GetLastError());
			return FALSE;
		  }
	  */
	  //MessageBox(NULL,"test2","test1",MB_OK);
	  //system("winver.exe");
	  
	  HANDLE hThisProcess = GetCurrentProcess(); // 获取当前进程句柄
	  
	  //HANDLE   hThisProcess   = GetProcessHandle("EXPLORER.EXE");
	  //if(hThisProcess   ==   NULL)
	  // return   0;
	  
	  // 打开当前进程令牌
	  
	  HANDLE hTokenThis = NULL;
	  HANDLE hTokenDup = NULL;
	  
	  OpenProcessToken(hThisProcess, TOKEN_ALL_ACCESS, &hTokenThis); 
	  // 复制一个进程令牌,目的是为了修改session id属性,以便在其它session中创建进程  
	  DuplicateTokenEx(hTokenThis, MAXIMUM_ALLOWED,NULL, SecurityIdentification, TokenPrimary, &hTokenDup);
	  //获取活动session id,这里要注意,如果服务器还没有被登录而使用了远程桌面,这样用是可以的,如果有多个session存在,
	  //不能简单使用此函数,需要枚举所有session并确定你需要的一个,或者干脆使用循环,针对每个session都执行后面的代码
	  //SetTokenInformation(hTokenDup, TokenSessionId, &dwSessionId, sizeof(DWORD)); //把session id设置到备份的令牌中
	  DWORD dwSessionId=atoi(argv[1]); //与会话进行连接
	  bool bRes;
	  bRes=SetTokenInformation(hTokenDup, TokenSessionId, &dwSessionId, sizeof(DWORD));
	  if (!bRes)	  
	  {
		  printf("SetTokenInformation!%d\n",GetLastError());
		  return FALSE;
	  } 
	  // 好了,现在要用新的令牌来创建一个服务进程。注意:是“服务”进程!如果需要以用户身份运行,必须在前面执行LogonUser来获取用户令牌
	  STARTUPINFO si; 
	  PROCESS_INFORMATION pi;
	  ZeroMemory(&si, sizeof(STARTUPINFO));
	  ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
	  si.cb = sizeof(STARTUPINFO); 
	  si.lpDesktop = "WinSta0\\Default";
	  LPVOID pEnv = NULL;
	  DWORD dwCreationFlag = NORMAL_PRIORITY_CLASS | Create_NEW_CONSOLE; // 注意标志
	  //CreateEnvironmentBlock(&pEnv, hTokenDup, FALSE); // 创建环境块
	  // 创建新的进程,这个进程就是你要弹出窗口的进程,它将工作在新的session中
	  char path[MAX_PATH];
	  lstrcpy(path,argv[2]);
	  CreateProcessAsUser(hTokenDup, NULL, (char *)path, NULL, NULL, FALSE, dwCreationFlag, pEnv, NULL, &si, &pi);
  }
  return 0;
}


 

评论 8
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值