2

I've been looking all over the internet for an answer to this, and it just doesn't seem to be directly answered, so I thought I would ask.

Case scenario: I want to take a screenshot of what is currently on the computer screen. If it's the Windows Logon screen, I want it to be that. If it's the active user's desktop, I want to to be that. If the user elevates their application, and the UAC prompt shows up, I want it to be that.

As per lots of reading and trial and error, my current setup is as follows:

  • Program runs as a windows service
  • Gets the active user's token
  • Runs CreateProcessAsUser with the user's token to generate another instance of itself
  • Takes a screenshot and transmits it back via pipes.

Right now this is working great for a logged on user, except that screenshot is black when a UAC prompt is enabled.

Also, this method obviously won't work for getting the logon screen.

Fundamentally I am wondering how exactly does TeamViewer go about achieving this sort of thing? It is able to switch between the logon screen and a user's session flawlessly, whilst also capturing UAC prompts. I am immensely curious as to how it achieves this.

Thanks everyone!

O. Jones
  • 103,626
  • 17
  • 118
  • 172
user10530103
  • 33
  • 1
  • 5
  • Team View uses [**`SetThreadDesktop`**](https://learn.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-setthreaddesktop) api to this. If you decide use, remember that you class of screenshot cannot have any window or make any reference to a class that have one (see [this question](https://stackoverflow.com/questions/41008676/how-do-i-make-setthreaddesktop-api-work-from-a-console-application)). –  Nov 16 '18 at 23:11
  • Hi Davison, I wanted to thank you for your comment. It appears you are correct; when I use SetDesktopThread, I can specify either WinSta0\Winlogon or WinSta0\Default. WinSta0\Winlogon lets me see the login screen and uac prompts; WinSta0\Default lets me see the desktop. Thus, I simply need a mechanism to tell which one is currently active, and swap my process to that. Do you have any suggestions? – user10530103 Nov 17 '18 at 05:15

1 Answers1

0

As per the advice of Davison, I have figured out how to do this, and it involves multiple steps.

Firstly, one must use CreateProcessAsUser to create a process inside the console session (obtained from WTSGetActiveConsoleSessionId). Something to note is that this process must have administrative privileges, which simply getting a handle to the user's token will not do. The way around this evidently, is to get a handle to a process running with administrative privileges, get this processes' token, duplicate it, and use that with CreateProcessAsUser. The process I used for this was Winlogon.

After this, the rest is quite simple; use OpenInputDesktop to get a handle to the desktop the user is currently seeing (it will be Default for actual desktop, and Winlogon for the UAC Prompt and login screen). After this, use SetThreadDesktop to set your processes' thread to the appropriate desktop, and capture the screen. Assuming that your Process has the privileges to create a handle to the Winlogon desktop, you will be able to capture the login screen/uac prompts and the regular user desktop.

Again, thanks to Davison, who pointed me in the right direction.

user10530103
  • 33
  • 1
  • 5
  • to complement your answer, see [**Create process by system with delphi**](https://stackoverflow.com/questions/37760239/create-process-by-system-with-delphi) **|** [**Winlogon screen capturing in Windows 7/10**](https://stackoverflow.com/questions/43520385/winlogon-screen-capturing-in-windows-7-10) **|** [**OpenInputDesktop**](https://www.cnblogs.com/findumars/p/5255339.html). PS: the last reference not works since Windows Vista, but see this useful implementation about `OpenInputDesktop/SetThreadDesktop`. –  Nov 19 '18 at 21:01
  • You might want to substitute "elevated privileges" or just "elevation" for "administrative privileges" because with UAC the two are different. – Spencer May 26 '22 at 16:43