mirror of
				https://github.com/snltty/linker.git
				synced 2025-10-31 04:26:45 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			513 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			513 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using cmonitor.libs.winapis;
 | |
| using System;
 | |
| using System.Collections.Generic;
 | |
| using System.Diagnostics;
 | |
| using System.Linq;
 | |
| using System.Net;
 | |
| using System.Net.Sockets;
 | |
| using System.Runtime.InteropServices;
 | |
| using System.Runtime.Serialization;
 | |
| using System.Security.Principal;
 | |
| using System.Text;
 | |
| using System.Threading.Tasks;
 | |
| using static cmonitor.libs.winapis.WTSAPI32;
 | |
| using static common.libs.winapis.ADVAPI32;
 | |
| using static common.libs.winapis.Kernel32;
 | |
| using static common.libs.winapis.NetApi32;
 | |
| using static common.libs.winapis.User32;
 | |
| 
 | |
| namespace common.libs.winapis
 | |
| {
 | |
|     public class Win32Interop
 | |
|     {
 | |
|         private static nint lastInputDesktop;
 | |
| 
 | |
|         private static bool GetCurrentDesktop(out string desktopName)
 | |
|         {
 | |
|             var inputDesktop = OpenInputDesktop();
 | |
|             try
 | |
|             {
 | |
|                 byte[] deskBytes = new byte[256];
 | |
|                 if (!GetUserObjectInformationW(inputDesktop, UOI_NAME, deskBytes, 256, out uint lenNeeded))
 | |
|                 {
 | |
|                     desktopName = string.Empty;
 | |
|                     return false;
 | |
|                 }
 | |
| 
 | |
|                 desktopName = Encoding.Unicode.GetString(deskBytes.Take((int)lenNeeded).ToArray()).Replace("\0", "");
 | |
|                 return true;
 | |
|             }
 | |
|             finally
 | |
|             {
 | |
|                 CloseDesktop(inputDesktop);
 | |
|             }
 | |
|         }
 | |
|         private static List<WindowsSession> GetActiveSessions()
 | |
|         {
 | |
|             List<WindowsSession> sessions = new List<WindowsSession>();
 | |
|             uint consoleSessionId = Kernel32.WTSGetActiveConsoleSessionId();
 | |
|             sessions.Add(new WindowsSession()
 | |
|             {
 | |
|                 Id = consoleSessionId,
 | |
|                 Type = WindowsSessionType.Console,
 | |
|                 Name = "Console",
 | |
|                 Username = GetUsernameFromSessionId(consoleSessionId)
 | |
|             });
 | |
| 
 | |
|             nint ppSessionInfo = nint.Zero;
 | |
|             int count = 0;
 | |
|             int enumSessionResult = WTSAPI32.WTSEnumerateSessions(WTSAPI32.WTS_CURRENT_SERVER_HANDLE, 0, 1, ref ppSessionInfo, ref count);
 | |
|             int dataSize = Marshal.SizeOf(typeof(WTSAPI32.WTS_SESSION_INFO));
 | |
|             nint current = ppSessionInfo;
 | |
| 
 | |
|             if (enumSessionResult != 0)
 | |
|             {
 | |
|                 for (int i = 0; i < count; i++)
 | |
|                 {
 | |
|                     object wtsInfo = Marshal.PtrToStructure(current, typeof(WTSAPI32.WTS_SESSION_INFO));
 | |
|                     if (wtsInfo is null)
 | |
|                     {
 | |
|                         continue;
 | |
|                     }
 | |
|                     WTSAPI32.WTS_SESSION_INFO sessionInfo = (WTSAPI32.WTS_SESSION_INFO)wtsInfo;
 | |
|                     current += dataSize;
 | |
|                     if (sessionInfo.State == WTSAPI32.WTS_CONNECTSTATE_CLASS.WTSActive && sessionInfo.SessionID != consoleSessionId)
 | |
|                     {
 | |
| 
 | |
|                         sessions.Add(new WindowsSession()
 | |
|                         {
 | |
|                             Id = sessionInfo.SessionID,
 | |
|                             Name = sessionInfo.pWinStationName,
 | |
|                             Type = WindowsSessionType.RDP,
 | |
|                             Username = GetUsernameFromSessionId(sessionInfo.SessionID)
 | |
|                         });
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             return sessions;
 | |
|         }
 | |
|         private static string GetUsernameFromSessionId(uint sessionId)
 | |
|         {
 | |
|             var username = string.Empty;
 | |
| 
 | |
|             if (WTSAPI32.WTSQuerySessionInformation(nint.Zero, sessionId, WTSAPI32.WTS_INFO_CLASS.WTSUserName, out var buffer, out var strLen) && strLen > 1)
 | |
|             {
 | |
|                 username = Marshal.PtrToStringAnsi(buffer);
 | |
|                 WTSAPI32.WTSFreeMemory(buffer);
 | |
|             }
 | |
| 
 | |
|             return username ?? string.Empty;
 | |
|         }
 | |
| 
 | |
|         private static nint OpenInputDesktop()
 | |
|         {
 | |
|             return User32.OpenInputDesktop(0, true, User32.ACCESS_MASK.GENERIC_ALL);
 | |
|         }
 | |
|         public static bool SwitchToInputDesktop()
 | |
|         {
 | |
|             try
 | |
|             {
 | |
|                 CloseDesktop(lastInputDesktop);
 | |
| 
 | |
|                 nint inputDesktop = OpenInputDesktop();
 | |
|                 if (inputDesktop == nint.Zero)
 | |
|                 {
 | |
|                     if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG)
 | |
|                         Logger.Instance.Error($"OpenInputDesktop fail");
 | |
|                     return false;
 | |
|                 }
 | |
| 
 | |
|                 bool result = SetThreadDesktop(inputDesktop);
 | |
|                 if (result == false)
 | |
|                 {
 | |
|                     if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG)
 | |
|                         Logger.Instance.Error($"SetThreadDesktop fail");
 | |
|                 }
 | |
|                 result &= SwitchDesktop(inputDesktop);
 | |
|                 if (result == false)
 | |
|                 {
 | |
|                     if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG)
 | |
|                         Logger.Instance.Error($"SwitchDesktop fail");
 | |
|                 }
 | |
| 
 | |
|                 lastInputDesktop = inputDesktop;
 | |
|                 return result;
 | |
|             }
 | |
|             catch (Exception ex)
 | |
|             {
 | |
|                 if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG)
 | |
|                 {
 | |
|                     Logger.Instance.Error(ex);
 | |
|                 }
 | |
|                 return false;
 | |
|             }
 | |
|         }
 | |
| 
 | |
| 
 | |
|         private static uint GetWinLogonPid(uint dwSessionId)
 | |
|         {
 | |
|             uint winlogonPid = 0;
 | |
|             Process[] processes = Process.GetProcessesByName("winlogon");
 | |
|             foreach (Process p in processes)
 | |
|             {
 | |
|                 if ((uint)p.SessionId == dwSessionId)
 | |
|                 {
 | |
|                     winlogonPid = (uint)p.Id;
 | |
|                 }
 | |
|             }
 | |
|             return winlogonPid;
 | |
|         }
 | |
|         private static uint GetDwSessionId(int targetSessionId, bool forceConsoleSession)
 | |
|         {
 | |
|             uint dwSessionId = Kernel32.WTSGetActiveConsoleSessionId();
 | |
|             if (forceConsoleSession == false)
 | |
|             {
 | |
|                 List<WindowsSession> activeSessions = GetActiveSessions();
 | |
|                 if (activeSessions.Any(x => x.Id == targetSessionId))
 | |
|                 {
 | |
|                     dwSessionId = (uint)targetSessionId;
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     dwSessionId = activeSessions.Last().Id;
 | |
|                 }
 | |
|             }
 | |
|             return dwSessionId;
 | |
|         }
 | |
|         private static STARTUPINFO GetStartUpInfo(bool hiddenWindow, string desktopName, out uint dwCreationFlags)
 | |
|         {
 | |
|             STARTUPINFO si = new STARTUPINFO();
 | |
|             si.cb = Marshal.SizeOf(si);
 | |
|             si.lpDesktop = @"winsta0\" + desktopName;
 | |
|             if (hiddenWindow)
 | |
|             {
 | |
|                 dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT | CREATE_NO_WINDOW;
 | |
|                 si.dwFlags = STARTF_USESHOWWINDOW;
 | |
|                 si.wShowWindow = 0;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE;
 | |
|             }
 | |
|             return si;
 | |
|         }
 | |
|         private static bool CreateInteractiveSystemProcess(string commandLine, int targetSessionId, bool forceConsoleSession, string desktopName, bool hiddenWindow, out PROCESS_INFORMATION procInfo)
 | |
|         {
 | |
|             nint hPToken = nint.Zero;
 | |
|             procInfo = new PROCESS_INFORMATION();
 | |
| 
 | |
|             uint dwSessionId = GetDwSessionId(targetSessionId, forceConsoleSession);
 | |
|             uint winlogonPid = GetWinLogonPid(dwSessionId);
 | |
| 
 | |
|             nint hProcess = Kernel32.OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid);
 | |
|             if (OpenProcessToken(hProcess, TOKEN_DUPLICATE, ref hPToken) == false)
 | |
|             {
 | |
|                 Kernel32.CloseHandle(hProcess);
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
 | |
|             sa.Length = Marshal.SizeOf(sa);
 | |
|             if (DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa, SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, TOKEN_TYPE.TokenPrimary, out nint hUserTokenDup) == false)
 | |
|             {
 | |
|                 Kernel32.CloseHandle(hProcess);
 | |
|                 Kernel32.CloseHandle(hPToken);
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             STARTUPINFO si = GetStartUpInfo(hiddenWindow, desktopName, out uint dwCreationFlags);
 | |
|             bool result = CreateProcessAsUser(hUserTokenDup, null, commandLine, ref sa, ref sa, false, dwCreationFlags, nint.Zero, null, ref si, out procInfo);
 | |
| 
 | |
|             Kernel32.CloseHandle(hProcess);
 | |
|             Kernel32.CloseHandle(hPToken);
 | |
|             Kernel32.CloseHandle(hUserTokenDup);
 | |
| 
 | |
|             return result;
 | |
|         }
 | |
| 
 | |
|         private static string GetCommandLine()
 | |
|         {
 | |
|             nint commandLinePtr = Kernel32.GetCommandLine();
 | |
|             return Marshal.PtrToStringAuto(commandLinePtr) ?? string.Empty;
 | |
|         }
 | |
|         private static void AddTokenPrivilege()
 | |
|         {
 | |
|             if (OperatingSystem.IsWindows())
 | |
|             {
 | |
|                 WindowsIdentity windowsIdentity = WindowsIdentity.GetCurrent();
 | |
|                 CommandHelper.Windows(string.Empty, new string[] {
 | |
|                     $"ntrights +r SeAssignPrimaryTokenPrivilege -u {windowsIdentity.Name}"
 | |
|                 });
 | |
|             }
 | |
|         }
 | |
|         public static void RelaunchElevated()
 | |
|         {
 | |
|             if (OperatingSystem.IsWindows() == false) return;
 | |
| 
 | |
|             try
 | |
|             {
 | |
|                 AddTokenPrivilege();
 | |
|             }
 | |
|             catch(Exception ex)
 | |
|             {
 | |
|                 Logger.Instance.Debug($"AddTokenPrivilege {ex}");
 | |
|             }
 | |
|             try
 | |
|             {
 | |
|                 string commandLine = GetCommandLine();
 | |
|                 bool result = CreateInteractiveSystemProcess($"{commandLine} --elevated", -1, false, "default", true, out PROCESS_INFORMATION procInfo);
 | |
|                 uint code = Kernel32.GetLastError();
 | |
|                 if (result)
 | |
|                 {
 | |
|                     Environment.Exit(0);
 | |
|                 }
 | |
|             }
 | |
|             catch(Exception ex)
 | |
|             {
 | |
|                 Logger.Instance.Debug($"CreateInteractiveSystemProcess {ex}");
 | |
|             }
 | |
|         }
 | |
|         
 | |
| 
 | |
| 
 | |
|         private static string currentUsername = string.Empty;
 | |
|         public static string GetCurrentUserSid()
 | |
|         {
 | |
|             if (OperatingSystem.IsWindows() == false)
 | |
|             {
 | |
|                 return string.Empty;
 | |
|             }
 | |
|             if (OperatingSystem.IsWindows())
 | |
|             {
 | |
|                 WindowsIdentity currentIdentity = WindowsIdentity.GetCurrent();
 | |
|                 currentUsername = currentIdentity.Name;
 | |
|                 if (IsSystemUser() == false)
 | |
|                 {
 | |
|                     return currentIdentity.User.Value;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             IntPtr hToken;
 | |
|             int sessionId = (int)Kernel32.WTSGetActiveConsoleSessionId();
 | |
|             if (WTSAPI32.WTSQueryUserToken(sessionId, out hToken))
 | |
|             {
 | |
|                 try
 | |
|                 {
 | |
|                     IntPtr tokenInformation;
 | |
|                     int returnLength;
 | |
|                     if (GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenUser, IntPtr.Zero, 0, out returnLength) || returnLength == 0)
 | |
|                     {
 | |
|                         return string.Empty;
 | |
|                     }
 | |
| 
 | |
|                     tokenInformation = Marshal.AllocHGlobal(returnLength);
 | |
|                     try
 | |
|                     {
 | |
|                         if (GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenUser, tokenInformation, returnLength, out returnLength) == false)
 | |
|                         {
 | |
|                             return string.Empty;
 | |
|                         }
 | |
| 
 | |
|                         var user = (TOKEN_USER)Marshal.PtrToStructure(tokenInformation, typeof(TOKEN_USER));
 | |
|                         string stringSid;
 | |
|                         if (ConvertSidToStringSid(user.User.Sid, out stringSid))
 | |
|                         {
 | |
|                             return stringSid;
 | |
|                         }
 | |
|                     }
 | |
|                     finally
 | |
|                     {
 | |
|                         Marshal.FreeHGlobal(tokenInformation);
 | |
|                     }
 | |
|                 }
 | |
|                 finally
 | |
|                 {
 | |
|                     if (hToken != IntPtr.Zero)
 | |
|                     {
 | |
|                         Marshal.FreeHGlobal(hToken);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             return string.Empty;
 | |
|         }
 | |
|         public static string GetDefaultUserSid()
 | |
|         {
 | |
|             if (OperatingSystem.IsWindows() == false)
 | |
|             {
 | |
|                 return string.Empty;
 | |
|             }
 | |
| 
 | |
|             List<WindowUserInfo> users = new List<WindowUserInfo>();
 | |
|             int resumeHandle = 0;
 | |
|             int result = NetUserEnum(null, 0, 2, out IntPtr bufPtr, -1, out int entriesRead, out int totalEntries, ref resumeHandle);
 | |
|             if (result == 0)
 | |
|             {
 | |
|                 try
 | |
|                 {
 | |
|                     for (int i = 0; i < entriesRead; i++)
 | |
|                     {
 | |
|                         USER_INFO_0 userInfo = (USER_INFO_0)Marshal.PtrToStructure(bufPtr + (Marshal.SizeOf(typeof(USER_INFO_0)) * i), typeof(USER_INFO_0));
 | |
| 
 | |
|                         int cbSid = 0, cchReferencedDomainName = 0;
 | |
|                         StringBuilder referencedDomainName = new StringBuilder();
 | |
|                         IntPtr pSid = IntPtr.Zero;
 | |
|                         bool bSuccess = LookupAccountName(null, userInfo.usri0_name, pSid, ref cbSid, referencedDomainName, ref cchReferencedDomainName, out int peUse);
 | |
|                         if (bSuccess == false && cbSid > 0)
 | |
|                         {
 | |
|                             pSid = Marshal.AllocHGlobal(cbSid);
 | |
|                             referencedDomainName.EnsureCapacity(cchReferencedDomainName);
 | |
|                             bSuccess = LookupAccountName(null, userInfo.usri0_name, pSid, ref cbSid, referencedDomainName, ref cchReferencedDomainName, out peUse);
 | |
|                         }
 | |
|                         if (bSuccess == false || ConvertSidToStringSid(pSid, out string stringSid) == false) continue;
 | |
|                         if (pSid != IntPtr.Zero)
 | |
|                         {
 | |
|                             Marshal.FreeHGlobal(pSid);
 | |
|                         }
 | |
| 
 | |
| 
 | |
|                         if (NetUserGetInfo(null, userInfo.usri0_name, 3, out IntPtr bufptr) != NERR_Success)
 | |
|                         {
 | |
|                             continue;
 | |
|                         }
 | |
|                         USER_INFO_3 info = (USER_INFO_3)Marshal.PtrToStructure(bufptr, typeof(USER_INFO_3));
 | |
|                         if (info.LastLogon > 0)
 | |
|                         {
 | |
|                             users.Add(new WindowUserInfo { LastLogon = info.LastLogon, Sid = stringSid });
 | |
|                         }
 | |
|                         NetApiBufferFree(bufptr);
 | |
|                     }
 | |
|                 }
 | |
|                 catch (Exception)
 | |
|                 {
 | |
|                 }
 | |
|                 finally
 | |
|                 {
 | |
|                     NetApiBufferFree(bufPtr);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             if (users.Count > 0)
 | |
|             {
 | |
|                 return users.OrderByDescending(c => c.LastLogon).FirstOrDefault().Sid;
 | |
|             }
 | |
|             return string.Empty;
 | |
|         }
 | |
|         public static bool IsSystemUser()
 | |
|         {
 | |
|             return currentUsername == "NT AUTHORITY\\SYSTEM";
 | |
|         }
 | |
| 
 | |
|         public static string GetUserName()
 | |
|         {
 | |
|             // 获取活动的控制台会话 ID
 | |
|             uint sessionId = WTSGetActiveConsoleSessionId();
 | |
| 
 | |
|             IntPtr buffer;
 | |
|             uint bytesReturned;
 | |
|             string userName = "";
 | |
| 
 | |
|             // 获取用户名
 | |
|             if (WTSQuerySessionInformation(IntPtr.Zero, sessionId, WTS_INFO_CLASS.WTSUserName, out buffer, out bytesReturned))
 | |
|             {
 | |
|                 userName = Marshal.PtrToStringAnsi(buffer);
 | |
|                 WTSFreeMemory(buffer);
 | |
|             }
 | |
| 
 | |
|             return userName;
 | |
|         }
 | |
| 
 | |
|         public static void SetHandleBlockKill(IntPtr handle)
 | |
|         {
 | |
|             const int HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x1;
 | |
|             Kernel32.SetHandleInformation(handle, HANDLE_FLAG_PROTECT_FROM_CLOSE, HANDLE_FLAG_PROTECT_FROM_CLOSE);
 | |
|         }
 | |
| 
 | |
|         public static async Task<DateTime> GetNetworkTime()
 | |
|         {
 | |
|             // 定义 NTP 服务器地址
 | |
|             string ntpServer = "time.windows.com";
 | |
| 
 | |
|             // 创建 UDP 客户端
 | |
|             using UdpClient client = new UdpClient();
 | |
| 
 | |
|             // 连接到 NTP 服务器
 | |
|             IPAddress ntpServerAddress = Dns.GetHostEntry(ntpServer).AddressList[0];
 | |
|             IPEndPoint endPoint = new IPEndPoint(ntpServerAddress, 123);
 | |
|             client.Connect(endPoint);
 | |
| 
 | |
|             // 构造 NTP 请求包
 | |
|             byte[] ntpData = new byte[48];
 | |
|             ntpData[0] = 0x1B;
 | |
| 
 | |
|             // 发送请求包并接收响应
 | |
|             await client.SendAsync(ntpData, ntpData.Length);
 | |
|             byte[] responseData = client.Receive(ref endPoint);
 | |
| 
 | |
|             // 关闭 UDP 客户端
 | |
|             client.Close();
 | |
| 
 | |
|             // 解析 NTP 响应包,提取时间信息
 | |
|             ulong seconds = (ulong)responseData[40] << 24 | (ulong)responseData[41] << 16 | (ulong)responseData[42] << 8 | (ulong)responseData[43];
 | |
|             ulong fraction = (ulong)responseData[44] << 24 | (ulong)responseData[45] << 16 | (ulong)responseData[46] << 8 | (ulong)responseData[47];
 | |
| 
 | |
|             // NTP 时间戳的起始时间是 1900 年 1 月 1 日
 | |
|             DateTime ntpEpoch = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc);
 | |
| 
 | |
|             // 计算 NTP 响应时间
 | |
|             ulong milliseconds = (seconds * 1000 + (fraction * 1000) / 0x100000000L);
 | |
|             DateTime networkTime = ntpEpoch.AddMilliseconds((long)milliseconds);
 | |
| 
 | |
|             // 转换为本地时间
 | |
|             DateTime localTime = networkTime.ToLocalTime();
 | |
| 
 | |
|             return networkTime;
 | |
| 
 | |
|         }
 | |
|         public static void SetSystemTime(DateTime dateTime)
 | |
|         {
 | |
|             SYSTEMTIME st = new SYSTEMTIME
 | |
|             {
 | |
|                 wYear = (ushort)dateTime.Year,
 | |
|                 wMonth = (ushort)dateTime.Month,
 | |
|                 wDay = (ushort)dateTime.Day,
 | |
|                 wHour = (ushort)dateTime.Hour,
 | |
|                 wMinute = (ushort)dateTime.Minute,
 | |
|                 wSecond = (ushort)dateTime.Second,
 | |
|                 wMilliseconds = (ushort)dateTime.Millisecond
 | |
|             };
 | |
|             Kernel32.SetSystemTime(ref st);
 | |
|         }
 | |
| 
 | |
|     }
 | |
| 
 | |
| 
 | |
|     [DataContract]
 | |
|     public enum WindowsSessionType
 | |
|     {
 | |
|         Console = 1,
 | |
|         RDP = 2
 | |
|     }
 | |
| 
 | |
|     [DataContract]
 | |
|     public class WindowsSession
 | |
|     {
 | |
|         [DataMember(Name = "ID")]
 | |
|         public uint Id { get; set; }
 | |
|         [DataMember(Name = "Name")]
 | |
|         public string Name { get; set; } = string.Empty;
 | |
|         [DataMember(Name = "Type")]
 | |
|         public WindowsSessionType Type { get; set; }
 | |
|         [DataMember(Name = "Username")]
 | |
|         public string Username { get; set; } = string.Empty;
 | |
|     }
 | |
| 
 | |
|     public struct WindowUserInfo
 | |
|     {
 | |
|         public int LastLogon;
 | |
|         public string Sid;
 | |
|     }
 | |
| 
 | |
| 
 | |
| }
 | 
