【问题标题】:Is it possible to fake windows console api?是否可以伪造 Windows 控制台 API?
【发布时间】:2011-12-15 15:45:50
【问题描述】:

我已经用 c# 编写了一个 ssh 服务器,我认为将 powershell 连接为 shell 会很好。我已经尝试了 2 种方法来使其正常工作,但两者都远非完美。这是我尝试过的:

  1. 启动 powershell.exe 并将其重定向到 std(in/out)。这不 运行良好,因为 powershell.exe 检测到它被重定向,更改 这是行为。更重要的是,它期望在 stdid 上输入数据,而不是 命令。所以它使用控制台 api 来读取命令。
  2. 在“包装”应用程序中托管 powershell。这样做的好处是 能够为 powershell 提供“控制台”实现(通过 PSHostRawUserInterface)。这效果更好,但您仍然可以调用 命令(主要是真正的控制台应用程序),例如“... | more”,期望 能够使用控制台 api,然后尝试从 包装进程的控制台。

所以我想做的是用一组函数替换控制台应用程序使用的常规控制台输入/输出函数,这样我就可以处理它们。但这似乎是一个糟糕的设计理念 (imo)。

现在我的想法是通过使用 WriteConsoleInput 等原生/Pinvoke 函数发送相关键来操作控制台。我认为以这种方式伪造控制台是可能的。但我不知道我会如何“阅读”控制台上发生的事情。

另外请记住,它是一项服务,因此最好不要生成实际的控制台窗口,尽管可能在 Windows 会话 0 中不会出现并且无关紧要。

【问题讨论】:

    标签: c# powershell windows-console


    【解决方案1】:

    为此目的,您有 PSSession 和 Enter-PSSession CmdLet。你的 SSH 和 Powershell 会做什么 PSSession 没有做?

    但如果你想这样做,这里有一个不写任何东西的解决方案:Using PowerShell through SSH


    于 2011 年 2 月 11 日编辑

    PowerShell inside 提供另一种无需编写任何东西的方式(免费供个人使用)。

    Host03 sample,也许可以提供基本代码来做你想做的事情。

    【讨论】:

    • 好吧,我想从我的 android 移动设备访问我的 ssh。我还没有看到真正的powershell客户端。这个项目有一个明确的“很好玩”的部分。至于 Cygwin……我有(也许是不合理的)问题。
    • 你试试这个PowerShellInside。它存在一个单连接免费版本。
    • 嗯,一个有趣的发现,我去看看。让我想知道他们是如何解决这个问题的。
    • 我以为他们只是嵌入了 PowerShell 运行空间和管道,但我并没有问自己很多问题。
    【解决方案2】:

    我按照 JPBlanc 的建议安装了 PowerShellInside,但没有使用很长时间。一个连接的东西太局限了,我不喜欢被限制(特别是如果这种限制是基于利润的,但那是我不应该进入的另一个完整的讨论)。尽管是解决原始问题的方法,但感觉并不令人满意,因为它没有解决我遇到的编程问题。

    但是,我最终确实设法解决了上述问题,实际上是通过在包装进程中使用 windows api 调用。因为有很多陷阱,所以我决定回答我自己的问题,并给其他看到同样问题的人一些指示。基本结构如下:

    • 使用重定向的 stdin/-out(如果需要,还可以使用 stderr)启动包装程序。 (在我的情况下,stdin 和 out 将是 xterm 控制序列和数据流,因为这是 ssh 方式)
    • 使用 GetStdHandle() 检索重定向的输入和输出句柄。接下来 SetStdHandle() 到 "CONIN$" 和 "CONOUT$" 的 CreateFile(),这样子进程继承控制台并且没有包装进程的重定向。 (请注意,createfile 需要允许继承的安全描述符)
    • 设置控制台模式、大小、标题、Ctrl-C 处理程序等。注意:如果要支持 unicode,请务必设置字体,我使用 Lucida Console (.FontFamily = 54, .FaceName = "Lucida Console" )。如果没有这个,从控制台输出读取字符将返回代码页版本,这在托管代码中使用起来很糟糕。
    • 可以使用 SetWinEventHook() 读取输出,请务必使用上下文外通知,因为我很确定让您的托管应用程序突然在另一个进程上下文/地址空间中运行是一个坏主意™ (我很确定我什至没有尝试)。该事件将为每个控制台窗口触发,而不仅仅是您自己的。因此,通过窗口句柄过滤所有对回调的调用。使用 GetConsoleWindow() 检索当前控制台应用程序的窗口句柄。也不要忘记在应用程序完成后解除回调。
    • 注意,到目前为止,请确保不要使用(或做任何导致负载的事情)System.Console 类,否则很可能会出错。在此之后的使用将表现为子进程已写入输出。
    • 生成需要的子进程(注意,必须使用 .UseShellExecute = false 否则不会继承控制台)
    • 您可以开始使用 WriteConsoleInput() 向控制台提供输入
    • 此时(或在单独的线程上)您必须运行 Windows 消息循环,否则您将不会收到控制台事件通知回调。您可以简单地使用无参数 Application.Run() 来执行此操作。要打破消息循环,您必须在某个时候将退出消息发布到您的消息循环。我在子流程的 .Exited 事件中使用 Application.Exit() 执行此操作。 (注意使用 .EnableRaisingEvents 来实现)
    • 现在,当您的控制台上的某些内容发生变化时,将调用您的获胜事件回调。注意滚动事件,这可能有点出乎意料。也不要对同步交付做任何假设。如果子进程写入 3 行,那么在您处理第一个事件时,剩余的 3 行可能已经写入。公平地说,Windows 在编写事件方面做得很好,这样您就不会被单个字符的变化所淹没,并且可以跟上变化。
    • 如果所有 PInvoke 定义在输入或输出中的任何位置包含字符,请务必使用 CharSet=CharSet.Unicode 进行标记。 PInvoke.net 错过了其中不少。

    所有这一切的最终结果是:Windows 控制台 API 的包装应用程序。包装器可以读/写重定向的标准输入和标准输出以与世界通信。当然,如果你想变得花哨,你可以在这里使用任何流(命名管道、tcp/ip 等)。我实现了一些 xterm 控制序列,并设法获得了一个完全工作的终端包装器,它应该能够包装任何 Windows 控制台进程,将 xterm 输入转换为目标应用程序控制台输入上的输入,并将应用程序的输出处理为 xterm 控制序列。我什至让鼠标工作。将 powershell.exe 作为子进程启动现在解决了在 ssh 会话中运行 powershell 的原始问题。 Cmd.exe 也可以。如果有人感兴趣,我会在某处发布完整代码。

    【讨论】:

      猜你喜欢
      • 2018-08-18
      • 1970-01-01
      • 1970-01-01
      • 2023-04-02
      • 1970-01-01
      • 1970-01-01
      • 2011-08-15
      • 2011-08-26
      • 2014-07-13
      相关资源
      最近更新 更多