Bang adds to Windows programs the ability to execute interpreter scripts. Such scripts start with the #!
character sequence, sometimes referred to as "hash-bang" or "she-bang".
A demonstration follows:
-
Create a simple Python script:
billziss@xps ⟩ ~ ⟩ Set-Content -Encoding ascii prargs.test cmdlet Set-Content at command pipeline position 1 Supply values for the following parameters: Value[0]: #!/usr/bin/env python Value[1]: import sys; print(sys.argv) Value[2]:
-
Attempt to execute:
billziss@xps ⟩ ~ ⟩ Start-Process .\prargs.test "10 20" -NoNewWindow -Wait Start-Process : This command cannot be run due to the error: %1 is not a valid Win32 application. At line:1 char:1 + Start-Process .\prargs.test "10 20" -NoNewWindow -Wait + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [Start-Process], InvalidOperationException + FullyQualifiedErrorId : InvalidOperationException,Microsoft.PowerShell.Commands.StartProcess Command
-
Start Bang and now it works:
billziss@xps ⟩ ~ ⟩ .\Projects\bang\build\VStudio\build\Release\bang64.exe -p -1 billziss@xps ⟩ ~ ⟩ Start-Process .\prargs.test "10 20" -NoNewWindow -Wait ['C:\\Users\\billziss\\prargs.test', '10', '20']
-
Works in child processes as well:
billziss@xps ⟩ ~ ⟩ cmd Microsoft Windows [Version 10.0.22621.963] (c) Microsoft Corporation. All rights reserved. C:\Users\billziss>.\prargs.test 10 20 ['.\\prargs.test', '10', '20']
From a shell prompt execute the command bang64 -p -1
(or bang32
on a 32-bit system). Your shell (and its children) will now have the "Bang" ability to execute interpreter scripts!
Bang is a command line utility with the following usage:
usage:
bang [-p -1|PID]
bang COMMAND [ARG ...]
-
The
-p PID
option is used to add the Bang ability to an existing process with IDPID
.PID
can be-1
in which case the ability is added to Bang's parent process and any new processes started from that process. -
Bang can also be used to start a new process
COMMAND
with argumentsARG ...
. The new process and any processes started from that process will have the Bang ability.
Bang supports two different character sequences for interpreter scripts:
-
#!/
: This is the standard hash-bang sequence from Unix. For example:#!/usr/bin/env python import sys; print(sys.argv)
A command line
.\script arguments-from-command-line
for a script with an interpreter line of#!/interpreter optional-arguments
, results in executing/interpreter
with the command line.\script optional-arguments arguments-from-command-line
. -
///
: Languages such as C use//
for line comments. Furthermore Unix shells will often attempt to interpret a file as a shell script if the file does not contain an executable header or the#!
sequence. Sometimes this is used to make a C/C++/etc. program executable:///usr/bin/env gcc "$0"; exit int main() { return 0; }
(The 3-slash convention originated on Unix-like environments (like Cygwin) that treat 2 slashes specially.)
Bang supports a very limited form of this syntax. It does not understand any of the Unix shell syntax although it knows to strip the terminating
; exit
. It also supports variables$0
-$9
and the$@
and"$@"
incantations with a meaning similar (but not the same) to the one in the Unix shell. (In particular note that Bang treats quotes and backslashes as normal characters, except for the special case of"$@"
.)
Bang uses a "Pathmap" to translate Unix-like interpreter paths to Windows-like interpreter paths. This Pathmap is configurable (see the Configuration section) but there is an internal default "Pathmap" that provides reasonable defaults:
-
/usr/bin/env COMMAND
: Instructs Bang to look forCOMMAND
in thePATH
. -
/usr/bin/COMMAND
: Instructs Bang to look forCOMMAND
in the%SYSTEMROOT%\System32
directory (usuallyC:\Windows\system32
).
Bang can be configured via the registry key HKEY_CURRENT_USER\Software\Bang
. (The HKEY_CURRENT_USER
key is specific to a particular user.) The following settings are available. All settings are of type "String" (REG_SZ
) or "Expandable String" (REG_EXPAND_SZ
).
-
Pathmap
: Controls the mapping of Unix-like paths to Windows-like paths. The syntax isUnixPath*[WindowsPath][;...]
whereUnixPath
is a Unix-like path (e.g./usr/bin/
) andWindowsPath
is a Windows-like path (e.g.C:\Windows\System32\
).-
The mappings specified in the Pathmap are attempted in order.
-
If
WindowsPath
is missing then searches are performed according to thePATH
environment variable. -
If
UnixPath
ends in slash/
then it refers to a directory and matches against it are performed using prefix-matching. In this caseWindowsPath
must end in a backslash\
and theUnixPath
will be replaced by theWindowsPath
. For example, the Pathmap/usr/bin/*C:\Windows\System32\
will map/usr/bin/cmd
toC:\Windows\System32\cmd.exe
and the Pathmap/usr/bin/*
will map/usr/bin/cmd
to thecmd.exe
file found by aPATH
search. -
If
UnixPath
does not end in slash then behavior depends: - IfWindowsPath
is missing then Bang will use the first optional argument in the intepreter line as the program to find by aPATH
search. For example, the Pathmap/usr/bin/env*
will map the interpreter line#!/usr/bin/env cmd
to thecmd.exe
file found by aPATH
search. - IfWindowsPath
is present then Bang will simply substitute theWindowsPath
in place of theUnixPath
. For example, the Pathmap/usr/bin/cmd*C:\Windows\System32\cmd.exe
will perform the obvious substitution. -
The internal default Pathmap is the following:
/usr/bin/env*;/usr/bin/*%SYSTEMROOT%\System32\;/bin/*%SYSTEMROOT%\System32\
-
-
Directories
: Controls the directory trees within which interpreter scripts must reside in order to be executable by Bang. The syntax is:WindowsPath[;...]
and any specifiedWindowsPath
must end in a backslash\
. If theDirectories
setting is missing (the default) all scripts are executable by Bang regardless of location.-
For example, the following setting will allow execution of scripts from the
.bin
andProjects
directory subtrees only:%USERPROFILE%\.bin\;%USERPROFILE%\Projects\
-
-
Programs
: Controls the programs that can inherit the Bang ability. The syntax is:WindowsPath[;...]
. The specifiedWindowsPath
may be a fully qualified path such asC:\Windows\System32\cmd.exe
or it may be a base file name such ascmd.exe
. If thePrograms
setting is missing (the default) all programs can inherit the Bang ability.-
For example, the following setting will allow only
cmd.exe
andpowershell.exe
to inherit the Bang ability:C:\Windows\System32\cmd.exe;powershell.exe
-
Bang consists of a command line utility (EXE) and a dynamic link library (DLL). When Bang "injects" its DLL into a process, that process acquires the Bang ability. Any new process started from a process that has the Bang ability is also bestowed the Bang ability (but see the Configuration section for how to control which processes inherit the Bang ability).
The Bang DLL uses the Microsoft Detours library to intercept calls to the CreateProcess
and ShellExecute
API's, which are used to create new processes on Windows. When Bang receives an intercepted CreateProcess
call, it examines the executed file to see if it starts with one of the character sequences #!/
or ///
. If either sequence is found, then the file is treated as an interpreter script and the CreateProcess
call is altered accordingly.
There are two main security concerns:
-
Bang enables any text file to become executable by simply adding the
#!/
or///
script magic. To mitigate this risk use theDirectories
configuration setting to control the directory trees where scripts can reside. -
Bang injects its DLL into other processes. This DLL represents foreign code to these processes and has an associated risk. To mitigate this risk use the
Programs
configuration setting to control which processes inherit the Bang ability.
Although a process with the Bang ability can execute interpreter scripts, some processes do not always know what to do with their newfound ability.
For example, Powershell with the Bang ability does not know that it is able to execute non-EXE files. Consequently it treats them as document files and attempts to open them via ShellExecute
. This still works because Bang also intercepts ShellExecute
, but the experience is not quite the same as with native programs.
billziss@xps ⟩ ~ ⟩ .\prargs.test | sort
Cannot run a document in the middle of a pipeline: C:\Users\billziss\prargs.test.
At line:1 char:1
+ .\prargs.test | sort
+ ~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (C:\Users\billziss\prargs.test:String) [], Runtime
Exception
+ FullyQualifiedErrorId : CantActivateDocumentInPipeline
billziss@xps ⟩ ~ ⟩ Start-Process .\prargs.test -NoNewWindow -Wait | sort
['C:\\Users\\billziss\\prargs.test']
There is work-in-progress to improve this experience. One trick that works is to place your script's extension in the PATHEXT
environment variable or to simply name your script with a .exe
extension!
billziss@xps ⟩ ~ ⟩ mv .\prargs.test .\prargs.exe
billziss@xps ⟩ ~ ⟩ .\prargs.exe | sort
['C:\\Users\\billziss\\prargs.exe']