概述:windows 操作系统获取系统当前登录的用户

0x01 API查询结果总结

以域名为domain,系统当前登录用户名为 test 为例举例说明,设备名为 WIN-FEKV0FJ89UQ

API用户态进程服务进程
GetUserNametestSYSTEM
GetEnvironmentVariable(获取环境变量 %USERNAME%)testWIN-FEKV0FJ89UQ$
WTSQuerySessionInformation(不指定sessionid)test服务进程中会返回空
WTSQuerySessionInformation(指定sessionid为WTSGetActiveConsoleSessionId())testtest
GetUserNameExAtest(返回形式依据传入的参数1)domain\WIN-FEKV0FJ89UQ

总结下来,如果要获取系统当前登录的用户名,那么不管是在用户进程还是服务进程,使用 WTSQuerySessionInformation 指定 Sessionid 的方法比较靠谱。

0x02 获取用户的API

GetUserName

GetUserName以其运行的exe为主,用户态进程下获取的系统当前登录的用户名,服务进程中获取的为 system 用户。

std::string GetLoginUsernameByApi()
{
	char username[1024];
	DWORD usernameLength = sizeof username;
	GetUserName(username, &usernameLength);
	return username;
}

通过环境变量获取

%USERNAME% 和 GetUserName 本质上一致,不同的是,服务进程中,%USERNAME% 字段保存的是主机名+&,只有用户态进程下才是当前系统登录用户名

std::string getLoginUsernameByEnv()
{
	char username[1024];
	GetEnvironmentVariable("USERNAME", username, sizeof username);
	return username;
}

WTSQuerySessionInformation 通过会话查询用户名

std::string getLoginUsernameBySession()
{
	LPWSTR usernameBuffer = NULL;
	DWORD infoSize = 0;
    
	WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTS_INFO_CLASS::WTSUserName, &usernameBuffer, &infoSize);
	string username(usernameBuffer);
    
	return username;
}

GetUserNameExA

	BOOL GetNameEx()
{
	// 获取当前登录用户的域信息  
    PCHAR cpuName = new CHAR[256];
    PCHAR domainName = new CHAR[256];
    DWORD domainNameSize = 0;
 
    ZeroMemory(domainName, 256);
 
    GetComputerObjectNameA(NameSamCompatible, NULL, &domainNameSize);
    if (GetComputerObjectNameA(NameSamCompatible, cpuName, &domainNameSize)) 
    {
        std::wcout << "当前登录的域机器: " << cpuName << std::endl;
    }
    else 
    {
        std::cerr << "无法获取域信息" << std::endl;
    }
 
    domainNameSize = 0;
    GetUserNameExA(NameDisplay, NULL, &domainNameSize);
    if (GetUserNameExA(NameDisplay, domainName, &domainNameSize))
    {
        std::wcout << "当前登录的域用户: " << domainName << std::endl;
    }
    else {
        std::cerr << "无法获取域信息" << std::endl;
    }
}

0x03 如何在服务进程中获取系统当前登录的用户

获取当前控制台会话ID,然后再调用 WTSQuerySessionInformationW 查询用户名。

BOOL GetLocalCurUserName(std::wstring& strUserName)
{
	BOOL bRet = FALSE;
	DWORD sessionId = WTSGetActiveConsoleSessionId();
	LPWSTR ppBuffer[100];
	DWORD bufferSize;
	bRet = WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, sessionId, WTSUserName, ppBuffer, &bufferSize);
 
	printf(" GetLocal1CurUserName --> %s", *ppBuffer);
 
	if (bRet == TRUE)
	{
		strUserName = *ppBuffer;
		WTSFreeMemory(ppBuffer);
	}
 
	return bRet;
}

0x04 其他方式

上述指定 Sessionid 的方法也可以通过 ProcessIdToSessionId 来获取 Sessionid。思路就是获取进程 Explorer.exe 的进程id,然后通过 PID 获取 Sessionid,最后通过 WTSQuerySessionInformationW 获取到对应的用户名。

DWORD GetPorceeIdByName(const std::string& procName)
{
	const char* FindExe = procName.c_str();
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (INVALID_HANDLE_VALUE == hSnapshot) {
		return NULL;
	}
	PROCESSENTRY32 pe = { sizeof(pe) };
	for (BOOL ret = Process32First(hSnapshot, &pe); ret; ret = Process32Next(hSnapshot, &pe)) {
		if (strcmp(pe.szExeFile, FindExe) == 0) {
			CloseHandle(hSnapshot);
			return pe.th32ProcessID;
		}
		//printf("%-6d %s\n", pe.th32ProcessID, pe.szExeFile);
	}
	CloseHandle(hSnapshot);
}
 
bool GetUserNameByPid(DWORD pid, std::wstring& username)
{
	DWORD SessionId;
	ProcessIdToSessionId(pid, &SessionId);
 
	LPWSTR usernameBuffer = NULL;
	DWORD infoSize = 0;
	if (FALSE == WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, SessionId, WTS_INFO_CLASS::WTSUserName, &usernameBuffer, &infoSize))
	{
		return false;
	}
	
	username = usernameBuffer;
 
	return true;
}

测试

void test()
{
    DWORD dwPid;
	wstring wsUserName;
	if (dwPid = GetPorceeIdByName("explorer.exe"))
	{
		if (GetUserNameByPid(dwPid, wsUserName))
		{
			std::wcout << "explorer.exe:" << wsUserName << endl;
		}
	}
}