On an internal discussion group, the question can up as to whether you could compile or encode a CMD script to prevent casual viewing of the code, similar to what the Script Encoder does for Windows Script Host scripts.  So for fun I decided to see if I could do it.  Here is what I came up with.  (Important Note:  What is shown in this post is simple obfuscation of the code, not encryption.  Do not use any of the techniques in this post to protect sensitive information like user names and password.  They can be fairly easily decoded.)

There is no way built into the operating system to compile or encode command shell scripts.  My first thought was to embed the CMD code as a string or strings in a VBScript that when executed would write the CMD code out to a file in the %Temp% folder, execute it, then delete it.  You could then use the Script Encoder to encode the script.  However, putting the contents of a CMD script into VBScript strings would involve a lot of tedious string concatenation, escaping double quotes, etc.  The I remembered that if construct you script as a Windows Script Host file (.wsf file), you can place multiline strings without modification into a <resource> element.  You can then use the GetResource function to place the contents of the element into a variable.  Below is a sample WSF file that has two lines of CMD script code in the CmdScript <resource> element.  The VBScript code reads the contents of this element, writes it out to a temporary file, runs it, then deletes the temporary file.

<job id="EmbeddedCmd">

    <resource id="CmdScript">
        dir /s "c:\Test" > "C:\Temp\test.txt"
        wmic os get LocalDateTime /value >> "C:\Temp\test.txt"
    </resource>

    <script language="VBScript">
        ' intWindowStyle for WshShell.Run
        Const WINDOW_STYLE_HIDE = 0
        Const WINDOW_STYLE_ACTIVATE_DISPLAY = 1
        Const WINDOW_STYLE_ACTIVATE_MINIMIZE = 2
        Const WINDOW_STYLE_ACTIVATE_MAXIMIZE = 3
        Const WINDOW_STYLE_RECENT = 4
        Const WINDOW_STYLE_ACTIVATE_CURRENT = 5
        Const WINDOW_STYLE_MINIMIZE_ACTIVATE_NEXT = 6
        Const WINDOW_STYLE_MINIMIZE = 7
        Const WINDOW_STYLE_CURRENT = 8
        Const WINDOW_STYLE_ACTIVATE_RESTORE = 9
        Const WINDOW_STYLE_FROM_PARENT = 10

        Set fso = CreateObject("Scripting.FileSystemObject")
        set WshShell = WScript.CreateObject("WScript.Shell")

        tempDir = WshShell.ExpandEnvironmentStrings("%Temp%")
        cdmFilePath = tempDir & "\testfile.cmd"

        cmdScriptCode=getResource("CmdScript")

        Set f = fso.CreateTextFile(cdmFilePath, True)
        f.Write cmdScriptCode
        f.Close

        WshShell.Run "cmd /c """ & cdmFilePath & """", WINDOW_STYLE_ACTIVATE_DISPLAY, True

        fso.DeleteFile cdmFilePath
    </script>

</job>

So this seemed to be all that I needed and I would just encode this with the Script Encoder.  (The script would exist in unencoded form on the disk while it’s executing, but I could see no way to avoid this.)  Unfortunately, it wasn’t that simple.  The Script Encoder predates the introduction of WSF files and does not understand the XML format of the file.  The tool can only encode actual VBScript or JScript code.  So to get it to encode a WSF you have to tell the Script Encoder that the file is an HTML file and it will encode the <script> elements.  The <resource> elements will be left in clear text.

I thought I was stuck until I remembered a blog post by Lee Holmes of the Windows PowerShell team found here.  He Base64 encoded that part of the PowerShell script he wanted to hide it from casual viewing.  I figured I could do the same.  I could Base64 encode the CMD script text and place it in a <resource> element and place a function to decode it in the <script> element.  Then after using the Script Encoder both elements would be encoded.  And this has an advantage over Lee’s script in that the code used to decode the text we really wanted to hide would also be encoded.

Attached are the scripts I put together to try this out:

  • Base64Functions.vbs should contain the functions to encode and decode text to and from Base64.  I did not have my own functions for doing this so I used the ones found here and here to test this.  The copy of Base64Functions.vbs included in the Zip does not contain these functions, since I did not want to redistribute this code without permission.  You will have copy the Base64Encode and Base64Decode functions from the web pages and paste them into Base64Functions.vbs yourself.  You can use other functions that do Base64 conversions as well as long as the function names and signatures match those used in my scripts.  For example, here are functions that use MSXML to do the conversions.  You can also use a different encoding algorithm if you want, like ROT13 for example, but you will have to edit the scripts to use those functions instead.
  • CmdInWsfTemplate.wsf is the template that I use to construct the final script.  It has text markers that I replace to inject the encoded CMD script and the Base64 functions. 
  • EncodeCmdInWsf.cmd is the script the you run on the command line to start the encoding.  It runs EncodeCmdInWsf.wsf to create the target WSF file and then runs the Script Encoder to encode the <script> element.  The syntax is:

        EncodeCmdInWsf.cmd "<path to the CMD script>"
  • EncodeCmdInWsf.wsf is the script that does most of the work.  It read the contents of CmdInWsfTemplate.wsf, Base64Functions.vbs, and the CMD script into variables.  It then encodes the CMD script text, replaces the text markers with the encoded CMD script text and the Base64 functions, and writes out the resulting WSF file with the same base name as the CMD script (e.g. Test.cmd is tranformed into Test.wsf).
  • You will need to install the Script Encoder and copy screnc.exe from its installation folder into the folder with the other scripts.

The resulting script looks something like this (the <script> element is truncated due to column width).:

<job id="EmbeddedCmd">

<resource id="CmdResource">

ZGlyIC9zICJjOlxUZXN0IiA+ICJDOlxUZW1wXHRlc3QudHh0Ig0Kd21pYyBvcyBnZXQgTG9jYWxEYXRlVGltZSAvdmFsdWUgPj4gIkM6XFRlbXBcdGVzdC50eHQiDQo=

</resource>

<script language="VBScript.Encode">#@~^ChUAAA==@#@&P@#@&B,kxDbUNKhjYHVn~6WD,    /4?tssR"E    @#@&ZGUkY~&Hf}mj:5S3m_q92,x,!@#@&ZKx/DPq(1Gr    {UKeJA  .....</script>

</job>

I did this mainly as a fun intellectual exercise and to help a colleague.  I have not tested it beyond what you see here.  As with anything else posted to this blog site, use at your own risk.

Disclaimer: The information on this site is provided "AS IS" with no warranties, confers no rights, and is not supported by the authors or Microsoft Corporation. Use of included script samples are subject to the terms specified in the Terms of Use.

This post was contributed by Michael Murgolo, a Senior Consultant with Microsoft Services - U.S. East Region.