/home/josephspurrier

Execute Batch Scripts from C# Memory

Problem: I have a batch script that runs some awesome commands, but it contains sensitive information like username and passwords. How can I run the batch scripts using C#?

Solution: Use C# to decrypt the script and then execute from C# memory!

I love batch scripts because they are pretty easy to write. There are also a bunch of tools, like psexec from SysInternals, which make an Administrator’s job easier when utilized in batch scripts. The best way to hide passwords is to encrypt them. Unfortunately, batch scripts cannot be encrypted and then run. They can only be converted to EXEs which doesn’t really provide any protection since a simple text editor can still find the passwords. The best way is to encrypt your batch scripts to a text file, save them on a network drive, decrypt them into memory using C#, and then execute them. For the purposes of this guide, pretend usernames and network paths are used. Just roll with it.

Let’s jump right in. I want to automate the installation of Java on a client computer.

Here is the batch script:

@ECHO OFF
REM Set the user accounts
REM Only use the Domain Admin when accessing a network location, use the Local Admin for everything else
SET LOCAL_ADMIN=\\SERVER\ITTOOLS\psexec.exe -accepteula -u localadmin -p LOCAL_ADMIN_PASSWORD_HERE
SET DOMAIN_ADMIN=\\SERVER\ITTOOLS\psexec.exe -accepteula -u My-Domain\domainuser -p DOMAIN_NONADMIN_USER_PASSWORD_HERE
REM Set the Command Prompt options
SET CMD_OPT=CMD /Q /C
REM Add the domain nonadmin user as a local administrator
%LOCAL_ADMIN% %CMD_OPT% NET localgroup Administrators My-Domain\domainuser /add
REM Close Internet Explorer
%LOCAL_ADMIN% %CMD_OPT% taskkill /im iexplore.exe /f
REM Install new Java software
%DOMAIN_ADMIN% %CMD_OPT% \\SERVER\ITSOFTWARE\Java\jre-6u33-windows-i586.exe IEXPLORER=1
REM Import registry key to disable Java AutoUpdate
%DOMAIN_ADMIN% %CMD_OPT% REGEDIT /s \\SERVER\ITREGISTRY\Java\JavaDisableAutoUpdate.reg

Let’s step through each line for clarity.

@ECHO OFF

Suppress any of the commands from appearing in the Command Prompt window.

REM Set the user accounts
REM Only use the Domain Admin when accessing a network location, use the Local Admin for everything else

REM stands for remark. Each of these lines are simply comments to explain the logic for other developers. They are not executed.

SET LOCAL_ADMIN=\\SERVER\ITTOOLS\psexec.exe -accepteula -u localadmin -p LOCAL_ADMIN_PASSWORD_HERE
SET DOMAIN_ADMIN=\\SERVER\ITTOOLS\psexec.exe -accepteula -u My-Domain\domainuser -p DOMAIN_NONADMIN_USER_PASSWORD_HERE

The SET command creates variables which you can use in your script. LOCAL_ADMIN runs a command with local admin rights; it’s ideal for tasks that require administrative permissions like installing or uninstalling applications. DOMAIN_ADMIN runs a command with domain user rights; it’s ideal for tasks like accessing a file share.

SET CMD_OPT=CMD /Q /C

Ensures the command prompt with run with ECHO set to off and terminates once the command completes so you don’t have to close each command prompt window manually.

%LOCAL_ADMIN% %CMD_OPT% NET localgroup Administrators My-Domain\domainuser /add

Now to put the variables together: Using the local admin account, execute a new command which will add the domain user to the local Administrators group. Now the domain user is a local administrator. The domain user account can access applications on network drives AND install them with administrative permissions.

%LOCAL_ADMIN% %CMD_OPT% taskkill /im iexplore.exe /f

Close an running instances of Internet Explorer so any Java plugins install correctly.

%DOMAIN_ADMIN% %CMD_OPT% \\SERVER\ITSOFTWARE\Java\jre-6u33-windows-i586.exe IEXPLORER=1

Using the domain account (which now has local admin rights), execute a new command which will run the Java installer from a network location with the IEXPLORER=1 parameter.

%DOMAIN_ADMIN% %CMD_OPT% REGEDIT /s \\SERVER\ITREGISTRY\Java\JavaDisableAutoUpdate.reg

Using the domain account (which now has local admin rights), execute a new command which will merge a registry file that disables the Java auto update feature.

Then, travel over to StackOverflow and grab the SimpleAES encryption/decryption class. Make sure you change all the Keys at the top of the file. Add it to a C# project and then output the batch script as encrypted text to a file.

SimpleAES aes = new SimpleAES();
txtEncrypt.Text = aes.EncryptToString(txtPlain.Text);
File.WriteAllText("\\SERVER\ITENCRYPTEDSCRIPTS\Java.recipe",txtEncrypt.Text);

Your batch script is now encrypted, but you cannot run it. You can stored your batch scripts this way so even if someone finds the network location, they can’t see the passwords in the file.

When you want to run the batch script, you’ll need to decrypt the batch script into a string array called strLines. Of course, you should wrap try-catch blocks around all this code so your application don’t crash when a problem occurs.

string strDecrypt = aes.DecryptString(File.ReadAllText("\\SERVER\ITENCRYPTEDSCRIPTS\Java.recipe"));
string[] strLines = strDecrypt.Split(new string[]{Environment.NewLine},StringSplitOptions.RemoveEmptyEntries);

Now the C# code you really want. Multiple lines cannot be passed to the cmd.exe (command prompt), but we can string each one together with an ampersand.

// Create a process object
Process p = new Process();
try
{
    // Run the command line with a hidden window
    p.StartInfo.FileName = "cmd.exe";
    p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
    // Build a string of DOS commands
    string sFull = "";
    // For every line of code in the batch script
    foreach(string s in strLines)
    {
        // Ignore lines of comments
        if (s.ToUpper().StartsWith("REM")) continue;
        // If this is the first iteration, store the line of code to the string
        // Replace % with ! for delayed variable expansion
        if (sFull.Length==0) sFull = s.Replace("%","!");
        // If this is the second or later iteration, APPEND the line of code to the string (replace % with ! for delayed variable expansion)
        // Replace % with ! for delayed variable expansion
        else sFull = sFull + " & " + s.Replace("%","!");
    }
    // Set the arguments for the cmd.exe command so ECHO is off (/Q)
    // Enable delayed environment variable expansion using exclamation marks (/V:ON)
    // Set command prompt to close automatically after execution (/C)
    p.StartInfo.Arguments = "/Q /V:ON /C "+sFull;
    // Run the command
    p.Start();
    p.WaitForExit();
    MessageBox.Show("Installation complete","Software Installation");
}
// Catch any errors
catch
{
    MessageBox.Show("There was a problem installing");
}

All done! Now you can create custom scripts that normal users can run to install software on their laptops. It is also very useful for Administrators when a problem occurs on many different computers that requires a quick fix like a registry, file, or permissions change. Works great on Windows XP, Windows Vista, and Windows 7…even with UAC. The users just have to click OK when prompted. Now the weakest link is your C# application. Grab a copy of a good, free obfuscation program to protect your encryption key.

#microsoft #windows #csharp