Skip to content

Instantly share code, notes, and snippets.

@mwulftange
Last active February 5, 2016 14:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mwulftange/f747b71c2ad926ddc262 to your computer and use it in GitHub Desktop.
Save mwulftange/f747b71c2ad926ddc262 to your computer and use it in GitHub Desktop.
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
public class CommandLine {
public static void main(String[] args) throws IOException {
args = new String[] {
"C:\\Temp\\PrintArguments.exe",
"arg 1",
"arg 2",
"arg 3"
};
start(args);
}
public static void start(String[] args) throws IOException {
InputStream in = new ProcessBuilder(Arrays.asList(args)).start().getInputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
System.out.write(buffer, 0, len);
}
}
}
Arguments in Java:
0: 'C:\Temp\PrintArguments.exe'
1: '"""""'
CreateProcess command line:
"C:\Temp\PrintArguments.exe" """""
argv arguments:
0: 'C:\Temp\PrintArguments.exe'
1: '""'
CommandLineToArgvW arguments:
0: 'C:\Temp\PrintArguments.exe'
1: '"'
Arguments in Java:
0: 'C:\Temp\PrintArguments.exe'
1: '" "" "'
CreateProcess command line:
"C:\Temp\PrintArguments.exe" " "" "
argv arguments:
0: 'C:\Temp\PrintArguments.exe'
1: ' " '
CommandLineToArgvW arguments:
0: 'C:\Temp\PrintArguments.exe'
1: ' "'
2: ''
Arguments in Java:
0: 'C:\Temp\PrintArguments.exe'
1: '"" '
CreateProcess command line:
C:\Temp\PrintArguments.exe """ "
argv arguments:
0: 'C:\Temp\PrintArguments.exe'
1: '" '
CommandLineToArgvW arguments:
0: 'C:\Temp\PrintArguments.exe'
1: '"'
2: ''
Arguments in Java:
0: 'C:\Temp\PrintArguments.exe'
1: '""" '
CreateProcess command line:
C:\Temp\PrintArguments.exe """" "
argv arguments:
0: 'C:\Temp\PrintArguments.exe'
1: '"'
2: ''
CommandLineToArgvW arguments:
0: 'C:\Temp\PrintArguments.exe'
1: '" '
#include <Windows.h>
int main(int argc, char *argv[], char **envp)
{
int nArgs;
LPWSTR * szArgs;
printf("argv arguments:\n");
for (int i = 0; i < argc; ++i) printf("%u: '%s'\n", i, argv[i]);
printf("CommandLineToArgvW arguments:\n");
szArgs = CommandLineToArgvW(GetCommandLineW(), &nArgs);
if (szArgs == NULL) return -1;
for (int i = 0; i < nArgs; ++i) wprintf(L"%u: '%ws'\n", i, szArgs[i]);
LocalFree(szArgs);
return 0;
}
private ProcessImpl(String cmd[],
final String envblock,
final String path,
final long[] stdHandles,
final boolean redirectErrorStream)
throws IOException
{
String cmdstr;
SecurityManager security = System.getSecurityManager();
boolean allowAmbiguousCommands = false;
if (security == null) {
allowAmbiguousCommands = true;
String value = System.getProperty("jdk.lang.Process.allowAmbiguousCommands");
if (value != null)
allowAmbiguousCommands = !"false".equalsIgnoreCase(value);
}
if (allowAmbiguousCommands) {
/* 331-344 skipped; legacy mode */
} else {
/* 346-383 skipped; strict mode */
}
handle = create(cmdstr, envblock, path,
stdHandles, redirectErrorStream);
/* 389-418 skipped */
}
private static String createCommandLine(int verificationType,
final String executablePath,
final String cmd[])
{
StringBuilder cmdbuf = new StringBuilder(80);
cmdbuf.append(executablePath);
for (int i = 1; i < cmd.length; ++i) {
cmdbuf.append(' ');
String s = cmd[i];
if (needsEscaping(verificationType, s)) {
cmdbuf.append('"').append(s);
// The code protects the [java.exe] and console command line
// parser, that interprets the [\"] combination as an escape
// sequence for the ["] char.
// http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
//
// If the argument is an FS path, doubling of the tail [\]
// char is not a problem for non-console applications.
//
// The [\"] sequence is not an escape sequence for the [cmd.exe]
// command line parser. The case of the [""] tail escape
// sequence could not be realized due to the argument validation
// procedure.
if ((verificationType != VERIFICATION_CMD_BAT) && s.endsWith("\\")) {
cmdbuf.append('\\');
}
cmdbuf.append('"');
} else {
cmdbuf.append(s);
}
}
return cmdbuf.toString();
}
private static boolean isQuoted(boolean noQuotesInside, String arg,
String errorMessage) {
int lastPos = arg.length() - 1;
if (lastPos >=1 && arg.charAt(0) == '"' && arg.charAt(lastPos) == '"') {
// The argument has already been quoted.
if (noQuotesInside) {
if (arg.indexOf('"', 1) != lastPos) {
// There is ["] inside.
throw new IllegalArgumentException(errorMessage);
}
}
return true;
}
if (noQuotesInside) {
if (arg.indexOf('"') >= 0) {
// There is ["] inside.
throw new IllegalArgumentException(errorMessage);
}
}
return false;
}
private boolean isShellFile(String executablePath) {
String upPath = executablePath.toUpperCase();
return (upPath.endsWith(".CMD") || upPath.endsWith(".BAT"));
}
// Legacy mode.
// Normalize path if possible.
String executablePath = new File(cmd[0]).getPath();
// No worry about internal, unpaired ["], and redirection/piping.
if (needsEscaping(VERIFICATION_LEGACY, executablePath) )
executablePath = quoteString(executablePath);
cmdstr = createCommandLine(
//legacy mode doesn't worry about extended verification
VERIFICATION_LEGACY,
executablePath,
cmd);
private static boolean needsEscaping(int verificationType, String arg) {
// Switch off MS heuristic for internal ["].
// Please, use the explicit [cmd.exe] call
// if you need the internal ["].
// Example: "cmd.exe", "/C", "Extended_MS_Syntax"
// For [.exe] or [.com] file the unpaired/internal ["]
// in the argument is not a problem.
boolean argIsQuoted = isQuoted(
(verificationType == VERIFICATION_CMD_BAT),
arg, "Argument has embedded quote, use the explicit CMD.EXE call.");
if (!argIsQuoted) {
char testEscape[] = ESCAPE_VERIFICATION[verificationType];
for (int i = 0; i < testEscape.length; ++i) {
if (arg.indexOf(testEscape[i]) >= 0) {
return true;
}
}
}
return false;
}
String executablePath;
try {
executablePath = getExecutablePath(cmd[0]);
} catch (IllegalArgumentException e) {
// Workaround for the calls like
// Runtime.getRuntime().exec("\"C:\\Program Files\\foo\" bar")
// No chance to avoid CMD/BAT injection, except to do the work
// right from the beginning. Otherwise we have too many corner
// cases from
// Runtime.getRuntime().exec(String[] cmd [, ...])
// calls with internal ["] and escape sequences.
// Restore original command line.
StringBuilder join = new StringBuilder();
// terminal space in command line is ok
for (String s : cmd)
join.append(s).append(' ');
// Parse the command line again.
cmd = getTokensFromCommand(join.toString());
executablePath = getExecutablePath(cmd[0]);
// Check new executable name once more
if (security != null)
security.checkExec(executablePath);
}
// Quotation protects from interpretation of the [path] argument as
// start of longer path with spaces. Quotation has no influence to
// [.exe] extension heuristic.
cmdstr = createCommandLine(
// We need the extended verification procedure for CMD files.
isShellFile(executablePath)
? VERIFICATION_CMD_BAT
: VERIFICATION_WIN32,
quoteString(executablePath),
cmd);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment