4

我的 C# 应用程序中嵌入了以下示例 Powershell 脚本。

Powershell 代码

    $MeasureProps = "AssociatedItemCount", "ItemCount", "TotalItemSize"

   $Databases = Get-MailboxDatabase -Status
    foreach($Database in $Databases) {

        $AllMBStats = Get-MailboxStatistics -Database $Database.Name    
     $MBItemAssocCount = $AllMBStats   |   %{$_.AssociatedItemCount.value} |  Measure-Object -Average   -Sum
     $MBItemCount =      $AllMBStats   |   %{$_.ItemCount.value} |  Measure-Object -Average  -Sum

        New-Object PSObject -Property @{
            Server = $Database.Server.Name
            DatabaseName = $Database.Name
            ItemCount = $MBItemCount.Sum
        }
    }

Visual Studio 为我提供了以下嵌入选项:

在此处输入图像描述

我见过的每个 PowerShell 示例(Exchange 上的 MSDN和 MSFT Dev Center)都要求我将 Powershell 命令分割成“位”并通过解析器发送。

我不想在我的应用程序中留下很多 PS1 文件,我需要一个没有其他“支持”PS1 文件的二进制文件。

我怎样才能使 myapp.exe 成为我的客户看到的唯一内容?

4

6 回答 6

6

许多客户不愿放弃受限执行策略,因为他们并不真正了解它。这不是一个安全边界——它只是一个额外的箍跳,这样你就不会在脚上开枪了。如果您想在自己的应用程序中运行 ps1 脚本,只需使用您自己的运行空间并使用基本授权管理器,它不理会系统执行策略:

InitialSessionState initial = InitialSessionState.CreateDefault();

// Replace PSAuthorizationManager with a null manager which ignores execution policy
initial.AuthorizationManager = new
      System.Management.Automation.AuthorizationManager("MyShellId");

// Extract psm1 from resource, save locally
// ...

// load my extracted module with my commands
initial.ImportPSModule(new[] { <path_to_psm1> });

// open runspace
Runspace runspace = RunspaceFactory.CreateRunspace(initial);
runspace.Open();

RunspaceInvoke invoker = new RunspaceInvoke(runspace);

// execute a command from my module
Collection<PSObject> results = invoker.Invoke("my-command");

// or run a ps1 script    
Collection<PSObject> results = invoker.Invoke("c:\temp\extracted\my.ps1");

通过使用空授权管理器,执行策略被完全忽略。请记住 - 这不是一些“黑客”,因为执行策略是为了保护用户免受自己的侵害。它不是为了防止恶意第三方。

http://www.nivot.org/nivot2/post/2012/02/10/Bypassing-Restricted-Execution-Policy-in-Code-or-in-Script.aspx

于 2012-02-11T04:14:18.763 回答
3

首先,您应该尝试消除客户对脚本的厌恶。阅读有关脚本签名、执行策略等的信息。

话虽如此,您可以将脚本作为 C# 代码本身中的多行字符串并执行它。由于您只有一个简单的脚本,因此这是最简单的方法。

您可以使用 AddScript ,将脚本作为字符串(不是脚本路径)

http://msdn.microsoft.com/en-us/library/dd182436(v=vs.85).aspx

于 2012-02-11T02:32:07.943 回答
1

您可以将其作为资源嵌入并在运行时通过反射检索它。这是来自MSDN的链接。文章是检索嵌入的图像,但原理是一样的。

于 2012-02-11T02:30:44.300 回答
1

您自己悬停了答案。通过将其添加为内容,您可以在运行时访问它(请参阅 Application.GetResourceStream)。然后,您可以将其存储为临时文件并执行,或者找出一种不使用文件来调用 powershell 的方法。

于 2012-02-11T02:30:52.173 回答
1

将您的 POSH 脚本存储为嵌入式资源,然后使用类似于此 MSDN 线程中的代码的内容根据需要运行它们:

public static Collection<PSObject> RunScript(string strScript)
{
  HttpContext.Current.Session["ScriptError"] = "";
  System.Uri serverUri = new Uri(String.Format("http://exchangsserver.contoso.com/powershell?serializationLevel=Full"));
  RunspaceConfiguration rc = RunspaceConfiguration.Create();
  WSManConnectionInfo wsManInfo = new WSManConnectionInfo(serverUri, SHELL_URI, (PSCredential)null);
  using (Runspace runSpace = RunspaceFactory.CreateRunspace(wsManInfo))
  {
    runSpace.Open();
    RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
        scriptInvoker.Invoke("Set-ExecutionPolicy Unrestricted");
    PowerShell posh = PowerShell.Create();
    posh.Runspace = runSpace;
    posh.AddScript(strScript);
    Collection<PSObject> results = posh.Invoke();
    if (posh.Streams.Error.Count > 0)
    {
      bool blTesting = false;
      string strType = HttpContext.Current.Session["Type"].ToString();
      ErrorRecord err = posh.Streams.Error[0];
      if (err.CategoryInfo.Reason == "ManagementObjectNotFoundException")
      {
    HttpContext.Current.Session["ScriptError"] = "Management Object Not Found Exception Error " + err + " running command " + strScript;
    runSpace.Close();
    return null;
      }
      else if (err.Exception.Message.ToString().ToLower().Contains("is of type usermailbox.") && (strType.ToLower() == "mailbox"))
      {
    HttpContext.Current.Session["ScriptError"] = "Mailbox already exists.";
    runSpace.Close();
    return null;
      }
      else
      {
    HttpContext.Current.Session["ScriptError"] = "Error " + err + "<br />Running command " + strScript;
    fnWriteLog(HttpContext.Current.Session["ScriptError"].ToString(), "error", strType, blTesting);
    runSpace.Close();
    return null;
      }
    }
    runSpace.Close();
    runSpace.Dispose();
    posh.Dispose();
    posh = null;
    rc = null;
    if (results.Count != 0)
    {
      return results;
    }
    else
    {
      return null;
    }
  }
}
于 2012-02-11T02:35:43.520 回答
0

客户只是看不到您部署的 PowerShell 脚本,对吗?你可以在运行时做任何你想做的事情。所以把它写到一个临时目录——甚至尝试一个命名管道,如果你想花哨并避免文件——然后简单地在那个目录上启动 PowerShell 进程。

您甚至可以尝试将其直接通过管道传输到标准输入。实际上,这可能是我首先要尝试的。然后,您没有任何记录表明它在计算机上的任何位置。Process 类用途广泛,可以在不直接接触 Windows API 的情况下执行此类操作。

于 2012-02-11T04:10:50.877 回答