2017-01-29 20 views
0

C#でリモートコントロールアプリケーションを作成しています。 WinlogonデスクトップにSendInputを使用できないことを除いて、すべて正常に動作します。私は、デフォルトからWinlogonへの変更を正常に検出しています。私はそれを切り替えてスクリーンショットをキャプチャできます。 SendInput関数を受け入れないだけです。私はそれが可能であることを知っています。なぜなら、TeamViewerはそれを行い、マニフェストでuiAccess = trueを持っていないからです。彼らは私と同じプロセスを使用しているようです。C#/ pinvokeを使用してWinSta0 WinlogonデスクトップにSendInputを送る方法

ここで私が一言でやっていることは次のとおりです。サービスをインストールします。サービスは接続要求を待ち受けます。サービスは、CreateProcessAsUserを使用してユーザーのセッションで新しいプロセスを起動し、winlogon.exeから複製されたアクセストークンを起動します。ビューアは新しいプロセスに接続します。

新しいプロセスがSendInputにアクセスしてwinlogonにアクセスできるようにするには、何が欠けているのかを特定できますか?サービスから新しいプロセスを開始するために使用しているコードは次のとおりです。次はWinlogonデスクトップへの変更を検出してそれに切り替えるために使用しているコードです。

public static bool OpenProcessAsSystem(string applicationName, out PROCESS_INFORMATION procInfo) 
{ 
    try 
    { 

     uint winlogonPid = 0; 
     IntPtr hUserTokenDup = IntPtr.Zero, hPToken = IntPtr.Zero, hProcess = IntPtr.Zero; 
     procInfo = new PROCESS_INFORMATION(); 

     // Obtain session ID for active session. 
     uint dwSessionId = Kernel32.WTSGetActiveConsoleSessionId(); 

     // Check for RDP session. If active, use that session ID instead. 
     var rdpSessionID = GetRDPSession(); 
     if (rdpSessionID > 0) 
     { 
      dwSessionId = rdpSessionID; 
     } 

     // Obtain the process ID of the winlogon process that is running within the currently active session. 
     Process[] processes = Process.GetProcessesByName("winlogon"); 
     foreach (Process p in processes) 
     { 
      if ((uint)p.SessionId == dwSessionId) 
      { 
       winlogonPid = (uint)p.Id; 
      } 
     } 

     // Obtain a handle to the winlogon process. 
     hProcess = Kernel32.OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid); 

     // Obtain a handle to the access token of the winlogon process. 
     if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE, ref hPToken)) 
     { 
      Kernel32.CloseHandle(hProcess); 
      return false; 
     } 

     // Security attibute structure used in DuplicateTokenEx and CreateProcessAsUser. 
     SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES(); 
     sa.Length = Marshal.SizeOf(sa); 

     // Copy the access token of the winlogon process; the newly created token will be a primary token. 
     if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa, (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int)TOKEN_TYPE.TokenPrimary, ref hUserTokenDup)) 
     { 
      Kernel32.CloseHandle(hProcess); 
      Kernel32.CloseHandle(hPToken); 
      return false; 
     } 

     // By default, CreateProcessAsUser creates a process on a non-interactive window station, meaning 
     // the window station has a desktop that is invisible and the process is incapable of receiving 
     // user input. To remedy this we set the lpDesktop parameter to indicate we want to enable user 
     // interaction with the new process. 
     STARTUPINFO si = new STARTUPINFO(); 
     si.cb = (int)Marshal.SizeOf(si); 
     si.lpDesktop = @"winsta0\default"; // interactive window station parameter; basically this indicates that the process created can display a GUI on the desktop 

     // flags that specify the priority and creation method of the process 
     uint dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE; 

     // create a new process in the current user's logon session 
     bool result = CreateProcessAsUser(hUserTokenDup,  // client's access token 
             null,     // file to execute 
             applicationName,  // command line 
             ref sa,     // pointer to process SECURITY_ATTRIBUTES 
             ref sa,     // pointer to thread SECURITY_ATTRIBUTES 
             false,     // handles are not inheritable 
             dwCreationFlags,  // creation flags 
             IntPtr.Zero,   // pointer to new environment block 
             null,     // name of current directory 
             ref si,     // pointer to STARTUPINFO structure 
             out procInfo   // receives information about new process 
             ); 

     // invalidate the handles 
     Kernel32.CloseHandle(hProcess); 
     Kernel32.CloseHandle(hPToken); 
     Kernel32.CloseHandle(hUserTokenDup); 

     return result; 
    } 
    catch 
    { 
     procInfo = new PROCESS_INFORMATION() { }; 
     return false; 
    } 
} 
public static uint GetRDPSession() 
{ 
    IntPtr ppSessionInfo = IntPtr.Zero; 
    Int32 count = 0; 
    Int32 retval = WTSAPI32.WTSEnumerateSessions(WTSAPI32.WTS_CURRENT_SERVER_HANDLE, 0, 1, ref ppSessionInfo, ref count); 
    Int32 dataSize = Marshal.SizeOf(typeof(WTSAPI32.WTS_SESSION_INFO)); 
    var sessList = new List<WTSAPI32.WTS_SESSION_INFO>(); 
    Int64 current = (int)ppSessionInfo; 

    if (retval != 0) 
    { 
     for (int i = 0; i < count; i++) 
     { 
      WTSAPI32.WTS_SESSION_INFO sessInf = (WTSAPI32.WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)current, typeof(WTSAPI32.WTS_SESSION_INFO)); 
      current += dataSize; 
      sessList.Add(sessInf); 
     } 
    } 
    uint retVal = 0; 
    var rdpSession = sessList.Find(ses => ses.pWinStationName.ToLower().Contains("rdp") && ses.State == 0); 
    if (sessList.Exists(ses => ses.pWinStationName.ToLower().Contains("rdp") && ses.State == 0)) 
    { 
     retVal = (uint)rdpSession.SessionID; 
    } 
    return retVal; 
} 

は、ここで私は、画面をキャプチャデスクトップ変化を検出し、それに切り替えるために使用しているものです。

var hWnd = User32.GetDesktopWindow(); 
var hDC = User32.GetWindowDC(hWnd); 
var graphDC = graphic.GetHdc(); 
var copyResult = GDI32.BitBlt(graphDC, 0, 0, totalWidth, totalHeight, hDC, 0, 0, GDI32.TernaryRasterOperations.SRCCOPY | GDI32.TernaryRasterOperations.CAPTUREBLT); 
// Change to input desktop if copy fails. 
if (!copyResult) 
{ 
    var inputDesktop = User32.OpenInputDesktop(); 
    if (User32.SetThreadDesktop(inputDesktop) == false) 
    { 
     graphic.Clear(System.Drawing.Color.White); 
     var font = new Font(FontFamily.GenericSansSerif, 30, System.Drawing.FontStyle.Bold); 
     graphic.DrawString("Waiting for screen capture...", font, Brushes.Black, new PointF((totalWidth/2), totalHeight/2), new StringFormat() { Alignment = StringAlignment.Center }); 
     var error = Marshal.GetLastWin32Error(); 
     writeToErrorLog(new Exception("Failed to open input desktop. Error: " + error.ToString())); 
    } 
    var dw = User32.GetDesktopWindow(); 
    User32.SetActiveWindow(dw); 
    User32.SetForegroundWindow(dw); 
    User32.CloseDesktop(inputDesktop); 
} 
graphic.ReleaseHdc(graphDC); 
User32.ReleaseDC(hWnd, hDC); 

答えて

0

私はSendInputをログオンデスクトップ(およびそれが判明したように、UACセキュアデスクトップ)で動作するようにしました。 SetThreadDesktopは、最初にターゲットデスクトップでプロセスを開始した場合と同じ権限を与えるものであってはなりません。

SetThreadDesktopを呼び出すのではなく、デスクトップの変更を検出したときに、新しいデスクトップでCreateProcessAsUserを使用して別のプロセスを開始しました。それから私は視聴者が現在のプロセスを切り替えて終了するように指示しました。

関連する問題