Friday, May 17, 2013

Powershell to EXE : Please do not touch my code :)

We did a lots of Powershell to generate report or automatic task. It is powerful but it is less security. We found someone touched and changed our script because it is in clear txt file. so we developed some thing to encrypt the PS1 script.

Here is what the script can do:

we have a hello powershell script as below:

# Filename: Hello.ps1
Write-Host
Write-Host 'Hello World!'
Write-Host "Good-bye World! `n"
# end of script













Then, we ran the exe to encrypt the script, it will create a .bin file which contain the encrypted script:




From the hello.bin, we cannot see what the code it is. To run the script,  just run exe with the bin file. The powershell script to run in a new powershell console.



Now let's see how the script works:

Below is the key point of the script:
  • Encryption/decryption key: The script have to run everywhere in the company and ran by anyone, so we have to put the key in the executable file. There two string to combine the key, one part is the file name and another part is the substring from a key string pool. So it is not easy to guess the key :). And we put company information as Initialization vector. So the different company will have different IV :)

 private static string getkey (string filename)    {           
       
        string passwordpool = "HKPpCy5@9CM9ufu^4ZrHw2i*b3zq@KL&Lm%ixc3i5ELn8VTMNJVekzPetic!z^W!QsnLL3qxUmefsCXT#vYoeEUu@FCt%X^vXmHAKRQqz*^Fq*oWG!cvMtPfJ6AFRSc2kS9gsE$esYNbPBtx38bd^NJpQU675hqm8CSiBaxjW6x8W45$!BSDYL!!&XMMNpKgMe8pHBQMA#TfMz&bVDPVZ7#NF3G!7a!UCVWczFu23Q!c4owgGzr2^mfaSZaMB#Jz;
        int keylength=16-filename.Length;
            string subkey=passwordpool.Substring(123,keylength);
            return subkey+filename;
       
        }

  • Run script from C#:
Method One:  We tried Powershell  RunspaceFactory.CreateRunspace() first : it is works great but user can only see the running output after the script finishes and we have some difficult for "Add-PSSnapin" for exchange, Vmware or SCOM. so we changed to method two:


  private static void runPSScript(string scriptText)
        {
            // create Powershell runspace
            Runspace runspace = RunspaceFactory.CreateRunspace();
            // open it
            runspace.Open();
            // create a pipeline and feed it the script text
            Pipeline pipeline = runspace.CreatePipeline();
            pipeline.Commands.AddScript(scriptText);
            pipeline.Commands.Add("Out-String");

            // execute the script
            Collection results = pipeline.Invoke();

            // close the runspace
            runspace.Close();

            // convert the script result into a single string
            StringBuilder stringBuilder = new StringBuilder();
            foreach (PSObject obj in results)
            {
                stringBuilder.AppendLine(obj.ToString());
            }
            Console.WriteLine(stringBuilder.ToString());

        }



Method two:  we use ProcessStartInfo to call powershell and pass the script as command. So you can see the script output from new powershell window. To stream the PS script to Powershell console, we uses powershell to decrypted the script to pass the commands in powershell


 private static void runPSScript2(string scriptText,string key,string salt)
        {
            string codestring = "$fsCrypt = new-object io.FileStream($path, 'Open');$r = new-Object System.Security.Cryptography.RijndaelManaged ;$key = [Text.Encoding]::UTF8.GetBytes($skey);$IV = [Text.Encoding]::UTF8.GetBytes($salt);$decryptor = $r.CreateDecryptor($key,$iv);$cs = new-Object Security.Cryptography.CryptoStream $fsCrypt,$decryptor,'Read';$sr = new-Object IO.StreamReader $cs;$script=$sr.ReadToEnd();$sr.Close();$cs.Close();$decryptor.Clear();$r.Clear();cls;Invoke-Expression $script";
            string parameter="$path='"+scriptText+"';$skey='"+key+"';$salt='"+salt+"';";
            string fullpscode = parameter + codestring;

            string command = scriptText;
            ProcessStartInfo cmdsi = new ProcessStartInfo("powershell.exe");
            cmdsi.Arguments = " -command "+fullpscode;
            Process cmd = Process.Start(cmdsi);
          

        }



Below is the full code:

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.IO;
using System.Security;
using System.Security.Cryptography;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {


        static void Main(string[] args)
        {

            if (args == null)
            {
                Console.WriteLine("Please add the bin file to run, for example: Runscript.exe srvreport.bin "); // Check for null array
            }
            else
            {
                string salt="XXXX Company";
                string filename = args[0];
                string[] Aextension = filename.Split(new string[] { "." }, 0);
                //Console.WriteLine(Aextension.Length.ToString());
                if (Aextension.Length > 1)
                {
                    string extension = Aextension[1];
                    if (extension == "ps1")
                    {
                        string outputfilename = filename.Replace(".ps1", ".bin");
                        string KeyedCollection=getkey(Aextension[0]);
                        EncryptFile(filename, outputfilename, KeyedCollection, salt);
                    }
                    else
                    {

                        if (extension == "bin")
                        {
                            Console.WriteLine(filename);
                            string KeyedCollection = getkey(Aextension[0]);
                            runPSScript2(filename, KeyedCollection, salt);
                        }
                    }
                }
                else {

                    filename = filename + ".bin";
                    string KeyedCollection = getkey(Aextension[0]);
                    runPSScript2(filename, KeyedCollection, salt);
                }






            }
        }

  

        private static void runPSScript2(string scriptText,string key,string salt)
        {
            string codestring = "$fsCrypt = new-object io.FileStream($path, 'Open');$r = new-Object System.Security.Cryptography.RijndaelManaged ;$key = [Text.Encoding]::UTF8.GetBytes($skey);$IV = [Text.Encoding]::UTF8.GetBytes($salt);$decryptor = $r.CreateDecryptor($key,$iv);$cs = new-Object Security.Cryptography.CryptoStream $fsCrypt,$decryptor,'Read';$sr = new-Object IO.StreamReader $cs;$script=$sr.ReadToEnd();$sr.Close();$cs.Close();$decryptor.Clear();$r.Clear();cls;Invoke-Expression $script";
            string parameter="$path='"+scriptText+"';$skey='"+key+"';$salt='"+salt+"';";
            string fullpscode = parameter + codestring;

            string command = scriptText;
            ProcessStartInfo cmdsi = new ProcessStartInfo("powershell.exe");
            cmdsi.Arguments = " -command "+fullpscode;
            Process cmd = Process.Start(cmdsi);
          

        }

   private static string getkey (string filename)    {          
      
        string passwordpool = "HKPpCy5@9CM9ufu^4ZrHw2i*b3zq@KL&Lm%ixc3i5ELn8VTMNJVekzPetic!z^W!QsnLL3qxUmefsCXT#vYoeEUu@FCt%X^vXmHAKRQqz*^Fq*oWG!cvMtPfJ6AFRSc2kS9gsE$esYNbPBtx38bd^NJpQU675hqm8CSiBaxjW6x8W45$!BSDYL!!&XMMNpKgMe8pHBQMA#TfMz&bVDPVZ7#NF3G!7a!UCVWczFu23Q!c4owgGzr2^mfaSZaMB#Jz;
        int keylength=16-filename.Length;
            string subkey=passwordpool.Substring(123,keylength);
            return filename+subkey;
      
        }


        private static string DecryptFile(string inputFile,string skey)
        {
             string result = "";
            var output = new MemoryStream();
            //FileStream fsOut = new FileStream("encrpout.txt", FileMode.Create);
            try
            {

                using (RijndaelManaged aes = new RijndaelManaged())
                {
                    byte[] key = ASCIIEncoding.UTF8.GetBytes(skey);

                    /* This is for demostrating purposes only.
                     * Ideally you will want the IV key to be different from your key and you should always generate a new one for each encryption in other to achieve maximum security*/
                    byte[] IV = ASCIIEncoding.UTF8.GetBytes(skey);

                    using (FileStream fsCrypt = new FileStream(inputFile, FileMode.Open))
                    {

                        using (ICryptoTransform decryptor = aes.CreateDecryptor(key, IV))
                        {
                            using (CryptoStream cs = new CryptoStream(fsCrypt, decryptor, CryptoStreamMode.Read))
                            {
                                int data;
                                while ((data = cs.ReadByte()) != -1)
                                {
                                    output.WriteByte((byte)data);
                                    // fsOut.WriteByte((byte)data);
                                }
                            }
                        }

                    }
                }
                output.Position = 0;
                StreamReader sr = new StreamReader(output);
                result = sr.ReadToEnd();
                //Console.Write(result);

            }
            catch (Exception ex)
            {
                // failed to decrypt file
            }

            return result;
        }

        private static void EncryptFile(string inputFile, string outputFile, string skey,string ssalt)
        {
            try
            {
                using (RijndaelManaged aes = new RijndaelManaged())
                {
                    byte[] key = ASCIIEncoding.UTF8.GetBytes(skey);

                    /* This is for demostrating purposes only.
                     * Ideally you will want the IV key to be different from your key and you should always generate a new one for each encryption in other to achieve maximum security*/
                    byte[] IV = ASCIIEncoding.UTF8.GetBytes(ssalt);

                    using (FileStream fsCrypt = new FileStream(outputFile, FileMode.Create))
                    {
                        using (ICryptoTransform encryptor = aes.CreateEncryptor(key, IV))
                        {
                            using (CryptoStream cs = new CryptoStream(fsCrypt, encryptor, CryptoStreamMode.Write))
                            {
                                using (FileStream fsIn = new FileStream(inputFile, FileMode.Open))
                                {
                                    int data;
                                    while ((data = fsIn.ReadByte()) != -1)
                                    {
                                        cs.WriteByte((byte)data);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                System.Console.WriteLine(ex.ToString());
                // failed to encrypt file
            }
        }
    }





}