Life
我一直坚信,在计算机的世界里没有秘密,只要用心寻找,很多东西就躺在那里,等着我们去慢慢的发掘。
调研
因为项目需求,需要获取当前登录的所有的用户,于是顺手就google了下。
https://blog.csdn.net/kingfox/article/details/116353723
这篇文章总结了几种获取用户名的方式,
1. GetUserName
2. GetEnvironmentVariable
3. Windows Terminal Session API
4. 通过获取当前Shell的创建者来间接获取当前登录的用户名
5. quser 工具获取
第一种有一个缺点就是只能获取到当前进程的用户名。第二种的缺点是获取的结果不是很理想。第三种最低支持的系统是vista,这让从xp开始的系统没法搞了。第四种确实一种可行的方案,每一个登陆的用户都会创建explorer.exe,如果同时两个用户登录该机器,则会出现两个不同的用户名的explorer.exe,但是实现的代码着实让人觉得复杂。
最后的重点就是微软提供的这个工具了
C:\Users\twin7>quser
用户名 会话名 ID 状态 空闲时间 登录时间
>twin7 console 1 运行中 . 2022/2/15 9:33
administrator 2 断开 . 2022/2/15 10:25
这不就是我心目中的那个它么!!!
原理
那就探究quser到底使用了哪个api,拖入ida后,发现了关键的函数过程
...
if ( !(unsigned __int8)WinStationEnumerateW(hServerName, (int)&v12, (int)&v13) )
{
v10 = GetLastError();
ErrorPrintf(0x66u, v10);
PutStdErr(v10, 0);
return 1;
}
v14 = 0;
if ( v13 > 0 )
{
v9 = 0;
do
{
DisplayUserInfo(0, hServerName, (int *)(v9 + v12), &user_string);
++v14;
v9 += 76;
}
while ( v14 < v13 );
}
WinStationFreeMemory(v12);
...
DisplayUserInfo的关键代码如下:
char __userpurge DisplayUserInfo@<al>(char a1@<bl>, int a2, int *a3, wchar_t *user_string)
{
...
result = WinStationQueryInformationW(a2, v4, 8, (int)&v13, 1216, (int)&v12);
if ( !result )
return result;
v10 = a1;
if ( !Str )
return result;
v6 = __wcslwr(&Str);
TruncateString((int)v6, 0x14u);
v7 = __wcslwr(&WideCharStr);
TruncateString((int)v7, 0xFu);
if ( !MatchedOne )
{
Message(0x70u, a1);
MatchedOne = 1;
}
if ( a2 || v15 != CurrentLogonId )
ANSI2OEM_Wprintf(L" ", v10);
else
ANSI2OEM_Wprintf(L">", v10);
WideCharToMultiByte(1u, 0, &Str, -1, &MultiByteStr, 1024, 0, 0);
WideCharToMultiByte(1u, 0, &WideCharStr, -1, &v21, 1024, 0, 0);
v8 = &byte_100141B;
if ( v13 != 4 )
v8 = &v21;
_fprintf(&__iob[1], "%-20s %-15s ", &MultiByteStr, v8);
v9 = (const WCHAR *)StrConnectState(v13, 1);
if ( v9 )
{
WideCharToMultiByte(1u, 0, v9, -1, &v20, 1024, 0, 0);
_fprintf(&__iob[1], "%4u %-6s ", v15, &v20);
}
else
{
_fprintf(&__iob[1], "%4u %-6s ", v15, &byte_100141B);
}
DisplayLastInputTime(&v19, &v16);
DisplayTime(&v17);
result = ANSI2OEM_Wprintf(L"\n", v11);
return result;
}
而在这个过程中,发现了两个关键的函数 WinStationEnumerateW 和 WinStationQueryInformationW 从这两个函数的名字,就会发现,这很微软的作风。通过搜索发现,这两个函数来自于 winsta.dll,通过进一步分析,发现最终涉及到一本协议。[MS-TSTS]: Terminal Services Terminal Server Runtime Interface Protocol。
关键细节,可以看最后的源码。
完整代码
最后整理后的代码如下,可以达到quser相同的效果:
#define MAX_THINWIRECACHE 4
#define DOMAIN_LENGTH 17
#define USERNAME_LENGTH 20
#define WINSTATIONNAME_LENGTH 32
typedef WCHAR WINSTATIONNAME[WINSTATIONNAME_LENGTH + 1];
typedef enum _WINSTATIONSTATECLASS
{
State_Active = 0,
State_Connected = 1,
State_ConnectQuery = 2,
State_Shadow = 3,
State_Disconnected = 4,
State_Idle = 5,
State_Listen = 6,
State_Reset = 7,
State_Down = 8,
State_Init = 9
} WINSTATIONSTATECLASS;
typedef enum _WINSTATIONINFOCLASS
{
WinStationCreateData,
WinStationConfiguration,
WinStationPdParams,
WinStationWd,
WinStationPd,
WinStationPrinter,
WinStationClient,
WinStationModules,
WinStationInformation,
WinStationTrace,
WinStationBeep,
WinStationEncryptionOff,
WinStationEncryptionPerm,
WinStationNtSecurity,
WinStationUserToken,
WinStationUnused1,
WinStationVideoData,
WinStationInitialProgram,
WinStationCd,
WinStationSystemTrace,
WinStationVirtualData,
WinStationClientData,
WinStationSecureDesktopEnter,
WinStationSecureDesktopExit,
WinStationLoadBalanceSessionTarget,
WinStationLoadIndicator,
WinStationShadowInfo,
WinStationDigProductId,
WinStationLockedState,
WinStationRemoteAddress,
WinStationIdleTime,
WinStationLastReconnectType,
WinStationDisallowAutoReconnect,
WinStationUnused2,
WinStationUnused3,
WinStationUnused4,
WinStationUnused5,
WinStationReconnectedFromId,
WinStationEffectsPolicy,
WinStationType,
WinStationInformationEx
} WINSTATIONINFOCLASS;
typedef struct _SESSIONIDW {
union {
ULONG SessionId;
ULONG LogonId;
} _SessionId_LogonId_union;
WINSTATIONNAME WinStationName;
WINSTATIONSTATECLASS State;
} SESSIONIDW,
*PSESSIONIDW;
typedef struct _TSHARE_COUNTERS {
ULONG Reserved;
} TSHARE_COUNTERS,
*PTSHARE_COUNTERS;
typedef struct _PROTOCOLCOUNTERS {
ULONG WdBytes;
ULONG WdFrames;
ULONG WaitForOutBuf;
ULONG Frames;
ULONG Bytes;
ULONG CompressedBytes;
ULONG CompressFlushes;
ULONG Errors;
ULONG Timeouts;
ULONG AsyncFramingError;
ULONG AsyncOverrunError;
ULONG AsyncOverflowError;
ULONG AsyncParityError;
ULONG TdErrors;
USHORT ProtocolType;
USHORT Length;
union {
TSHARE_COUNTERS TShareCounters;
ULONG Reserved[100];
} Specific;
} PROTOCOLCOUNTERS,
*PPROTOCOLCOUNTERS;
typedef struct _THINWIRECACHE {
ULONG CacheReads;
ULONG CacheHits;
} THINWIRECACHE,
*PTHINWIRECACHE;
typedef struct _RESERVED_CACHE {
THINWIRECACHE ThinWireCache[MAX_THINWIRECACHE];
} RESERVED_CACHE,
*PRESERVED_CACHE;
typedef struct _TSHARE_CACHE {
ULONG Reserved;
} TSHARE_CACHE,
*PTSHARE_CACHE;
typedef struct CACHE_STATISTICS {
USHORT ProtocolType;
USHORT Length;
union {
RESERVED_CACHE ReservedCacheStats;
TSHARE_CACHE TShareCacheStats;
ULONG Reserved[20];
} Specific;
} CACHE_STATISTICS,
*PCACHE_STATISTICS;
typedef struct _PROTOCOLSTATUS {
PROTOCOLCOUNTERS Output;
PROTOCOLCOUNTERS Input;
CACHE_STATISTICS Cache;
ULONG AsyncSignal;
ULONG AsyncSignalMask;
} PROTOCOLSTATUS,
*PPROTOCOLSTATUS;
typedef struct _WINSTATIONINFORMATIONW {
WINSTATIONSTATECLASS ConnectState;
WINSTATIONNAME WinStationName;
ULONG LogonId;
LARGE_INTEGER ConnectTime;
LARGE_INTEGER DisconnectTime;
LARGE_INTEGER LastInputTime;
LARGE_INTEGER LogonTime;
PROTOCOLSTATUS Status;
WCHAR Domain[DOMAIN_LENGTH + 1];
WCHAR UserName[USERNAME_LENGTH + 1];
LARGE_INTEGER CurrentTime;
} WINSTATIONINFORMATIONW,
*PWINSTATIONINFORMATIONW;
typedef BOOL(WINAPI *pfn_WinStationEnumerateW)(_In_opt_ HANDLE hServer, _Out_ PSESSIONIDW *SessionIds, _Out_ PULONG Count);
typedef BOOL(WINAPI *pfn_WinStationQueryInformationW)(_In_opt_ HANDLE hServer, _In_ ULONG SessionId,
_In_ WINSTATIONINFOCLASS WinStationInformationClass,
_Out_writes_bytes_(WinStationInformationLength) PVOID pWinStationInformation, _In_ ULONG WinStationInformationLength,
_Out_ PULONG pReturnLength);
typedef BOOL(WINAPI *pfn_WinStationFreeMemory)(_In_ PVOID Buffer);
int main(int argc, const char * argv[])
{
BOOL bRet = FALSE;
pfn_WinStationEnumerateW pWinStationEnumerateW = NULL;
pfn_WinStationFreeMemory pWinStationFreeMemory = NULL;
pfn_WinStationQueryInformationW pWinStationQueryInformationW = NULL;
HMODULE hDll = LoadLibrary(L"winsta.dll");
if (hDll)
{
pWinStationEnumerateW = (pfn_WinStationEnumerateW)GetProcAddress(hDll, "WinStationEnumerateW");
pWinStationFreeMemory = (pfn_WinStationFreeMemory)GetProcAddress(hDll, "WinStationFreeMemory");
pWinStationQueryInformationW = (pfn_WinStationQueryInformationW)GetProcAddress(hDll, "WinStationQueryInformationW");
}
PSESSIONIDW pTerm = NULL;
ULONG uTermCount = 0;
/*
DWORD dwSessionID = -1;
BOOL bRet = ProcessIdToSessionId(GetCurrentProcessId(), &dwSessionID);
if (bRet == FALSE)
{
return 0;
}
*/
do
{
DWORD dwSize = sizeof(SESSIONIDW);
bRet = pWinStationEnumerateW(NULL, &pTerm, &uTermCount);
if (bRet == FALSE)
{
break;
}
for (ULONG i = 0; i < uTermCount; ++i)
{
WINSTATIONINFORMATIONW pStationInfo;
ULONG uInfoLen = sizeof(WINSTATIONINFORMATIONW);
ULONG uRetLen = 0;
if (FALSE == pWinStationQueryInformationW(NULL, pTerm->_SessionId_LogonId_union.SessionId, WinStationInformation, &pStationInfo, uInfoLen, &uRetLen))
{
break;
}
if (pStationInfo.UserName[0])
{
wprintf(L"domain : %s, user : %s \n", pStationInfo.Domain, pStationInfo.UserName);
}
pTerm++;
}
} while (FALSE);
if (pTerm)
{
pWinStationFreeMemory(pTerm);
}
system("pause");
return 0;
}
测试结果
最终的测试结果如下,且支持xp。
domain : twin7-PC, user : twin7
domain : twin7-PC, user : Administrator
请按任意键继续. . .