几个小时以来我一直在反对这个问题,所以我觉得是时候问了.我将从对情况的高级描述开始.您可以在https://github.com/Jay-Rad/InstaTech_Client找到完整的源代码.此问题仅适用于"/ InstaTech_Service /"中的项目.
概观
InstaTech客户端是一个远程控制应用程序,它使用websockets并与ASP.NET服务器建立出站连接,以便与查看器进行中继.我有不同的版本,但它们的功能大致相同(Electron版本在使用原始websockets之前首先尝试WebRTC).该应用程序的查看器部分是基于Web的,可以在此处找到演示:https://instatech.org/Demo/Remote_Control
WPF(C#)和Electron版本提供了一个带有随机ID的GUI,它们必须提供给远程进入其计算机的人(类似于TeamViewer).会话开始后,他们会以不同的方式捕获屏幕.对于C#,我使用pinvoke来将BitBlt复制到内存中的图形,然后通过websocket发送.将后续的屏幕捕获与前一个屏幕捕获进行比较以创建包含更改的像素的框,然后发送该裁剪的部分.鼠标和键盘输入由客户端接收,并通过pinvoke执行到keybd_event和mouse_event.这些都很有效.
我创建的服务以类似的方式工作,但这里有不同之处.服务本身在系统帐户下的会话0中运行.它连接到服务器并侦听websocket.当建立连接并请求屏幕查看时,它会在WinSta0\Default中的用户会话中启动单独的交互式进程.一旦连接了新进程的websocket,服务器就开始在它和查看器之间中继消息而不是服务和查看器.
虽然新进程是在用户会话中以交互方式启动的,但它在系统帐户下运行.这是通过对CreateProcessAsUser的pinvoke和复制winlogon.exe访问令牌来实现的.
问题
如果有人已登录,即使通过RDP,此解决方案也能正常工作.但是,如果没有人登录或计算机被锁定,我无法与登录屏幕进行交互.在进行屏幕捕获时,我正在检测捕获是否失败,这意味着WinSta0\Default桌面不再处于活动状态.由于我正在使用CreateProcessAsUser,我可以将桌面切换到WinSta0\Winlogon就好了.我仍然可以看到它(即使没有人登录),但它不会接受任何输入.我理解这是出于安全原因而设计的.嗯,奇怪的是,如果我移动它并导致光标重新定位,一些鼠标移动会"滑过",但其余部分会被发送到默认桌面并在重新登录后执行.
所以问题是我无法通过此设置将帐户登录到计算机.如果重要,如果其他人已经登录并锁定了计算机,我不关心与Winlogon桌面交互.我只希望能够登录,如果没有其他人正在使用它,或者它是我的帐户登录并在锁定屏幕.
试图解决方案
我假设没有办法绕过无法将模拟输入发送到Winlogon桌面.(更正:也就是说,使用mouse_event和keybd_event函数.我见过其他应用程序,比如TeamViewer和Microsoft SCCM远程控制.我不知道他们是怎么做的.)如果它在某种程度上可行,我认为那是最直接的路线.但是这里有一些我已经研究过的重点是开始新的登录会话.
Pinvoke到LsaLogonUser.我不确定这是否能完成我所追求的目标,但无论如何我都试过了.但是,即使对LsaLogonUser的调用报告成功,我从LsaRegisterLogonProcess(输出到lsaHan)获得的句柄为0.我不确定我做错了什么.我不太熟悉Win32的电话,并试图随便拿起它.也许呼叫过程没有必要的权利.我已尝试从会话0中的服务和交互式会话中运行的进程中调用此方法.我正在做的一个例子如下.
Microsoft终端服务活动客户端COM库.我没有深入研究这个,但我想知道是否可以使用它来启动RDP登录会话.进行RDP登录会话后,在该会话中生成一个新的InstaTech进程并连接到该进程.我怀疑如果从同一台计算机尝试RDP连接,这将会起作用.
凭证提供者.我在研究时遇到了证书提供者.我不确定创建一个是否可以解决问题,但听起来这是一项非常复杂的任务.
有没有人有什么建议?或者我完全错过了什么?
如果您想重新编译服务和测试,我在服务器上创建了一个临时管理员帐户.安装了该服务的任何计算机都将显示在那里,您可以使用此帐户登录.请记住,阅读此帖子的任何人都可以访问运行该服务的任何计算机,因此请确保它处于隔离的环境中.
用户名:admin
密码:plzh @ lpm3purdyplz
该服务是自行安装的.将-install开关传递给install,-uninstall以卸载.EXE被复制到%programdata%\ InstaTech,服务从那里启动它.
谢谢!
参考代码
public static void CreateNewSession()
{
var kli = new SECUR32.KERB_INTERACTIVE_LOGON()
{
MessageType = SECUR32.KERB_LOGON_SUBMIT_TYPE.KerbInteractiveLogon,
UserName = "myusername@someplace.com",
Password = "superencryptedstring"
};
IntPtr pluid;
IntPtr lsaHan;
ulong secMode;
uint authPackID;
IntPtr kerbLogInfo;
SECUR32.LSA_STRING logonProc = new SECUR32.LSA_STRING()
{
Buffer = Marshal.StringToHGlobalAuto("InstaLogon"),
Length = (ushort)Marshal.SizeOf(Marshal.StringToHGlobalAuto("InstaLogon")),
MaximumLength = (ushort)Marshal.SizeOf(Marshal.StringToHGlobalAuto("InstaLogon"))
};
SECUR32.LSA_STRING …Run Code Online (Sandbox Code Playgroud) 正如标题所说,有没有办法在 .NET Standard 2.0 中获得总(已安装)系统 RAM?我所有的搜索都只出现在 .NET Framework 解决方案中。
我正在用 C# 为一套技术支持工具编写远程控制应用程序。一切工作正常,除了我无法使用 SendInput 到 Winlogon 桌面。我成功检测到从默认到 Winlogon 的更改,并且我能够切换到它并捕获屏幕截图。它只是不接受 SendInput 函数。我知道这是可能的,因为 TeamViewer 这样做了,而且他们的清单中没有 uiAccess=true 。他们似乎正在使用与我相同的流程。
简而言之,这就是我正在做的事情:安装服务。服务监听连接请求。服务使用 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 …Run Code Online (Sandbox Code Playgroud)