Prevent the CMD command interpreter from using the operators passed via parameter/argument?

Asked

Viewed 685 times

10

There is a way to prevent operators present in past arguments, via command line, from being used/interpreted by the interpreter CMD in C, C++ or C#?

I want to make use of characters present in the argument, regardless of whether they are operators or not, but I don’t want the interpreter to intercept and use them, specifically these characters/operators:

& && | || < > >> e <<

Since my executable, when receiving these characters, treat them as, in fact, characters. Not allowing the interpreter to manipulate the operation assigned to these characters, such as operators who are.

In other words...

How to pass parameters containing the special characters &, &&, |, ||, <, >, >>, e << via command line to the main function of a console application made in C#, but preventing CMD from using these parameters as operators.

Obs.: No prior use/editing/treatment of strings/operator for further use as argument/parameter

Is it possible? If it is, how to do it?

3 answers

4

Pass the arguments chained with "". This way, the Console will not interpret what is inside the character set.

echo olá >> mundo

The above command will create a "world" file with the contents "hello".

echo "olá >> mundo"

The above command will print olá >> mundo onscreen.

For this, when calling a process, use:

ProcessStartInfo CmdProcess = new ProcessStartInfo();
CmdProcess.Filename = "cmd.exe";
CmdProcess.UseShellExecute = true;
CmdProcess.Arguments = "\"" + "foo >> bar << & &&  |  ||  <  >  >>  <<" + "\"";

Process.Start(CmdProcess);

In this way, everything that is passed within "...", is interpreted literally.

Observing: you divide arguments passed to the CMD by a space. As within a "..." spaces are considered the same argument, separate arguments with other fields: cmd.exe "arg 1" "arg >> 2" "arg & 3"

3

The native Windows Command Prompt will operate all characters that are not escaped or encrypted in "...". What’s left to do is: create your own CMD.

I wrote a code that has the same function as the Windows Command Prompt and runs the same commands as it, but, does not treat operators. No need to escape or enclasurate characters, this small application will not handle them:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CustomCMD
{
    class Program
    {
        public const char ESCAPE_CHAR = '^';
        public const char ARGUMENT_DIVISOR = ' ';
        static string ContainsStringArray(string str, params string[] items)
        {
            foreach(string item in items)
            {
                if(str.Contains(item))
                {
                    return item;
                }
            }
            return null;
        }
        static string EvaluateCommand(string program, params string[] args)
        {
            System.Diagnostics.ProcessStartInfo inf = new System.Diagnostics.ProcessStartInfo();
            inf.FileName = "CMD";
            List<string> argsParsed = new List<string>();
            foreach(string arg in args)
            {
                argsParsed.Add(PrepareArgument(arg));
            }
            inf.Arguments = "/C " + program + ARGUMENT_DIVISOR + (args.Count() == 0 ? "" : string.Join(ARGUMENT_DIVISOR.ToString(), argsParsed).Trim());
            inf.UseShellExecute = false;
            inf.RedirectStandardOutput = true;

            try
            {
                System.Diagnostics.Process pro = System.Diagnostics.Process.Start(inf);
                pro.WaitForExit();
                Console.WriteLine(pro.StandardOutput.ReadToEnd());
            } catch (Exception ex)
            {
                return ex.Message;
            }
            return "";
        }
        static string PrepareArgument(string argument)
        {
            // Caracteres não aceitos: &  &&  |  ||  <  >  >> e <<
            string[] notAcceptedChars = { "&", "|", "<", ">" };
            StringBuilder newArgument = new StringBuilder();
            argument = argument.Trim('"'); // remove os "" dos argumentos
            for (int charIndex = 0; charIndex < argument.Length; charIndex++)
            {
                char ch = argument[charIndex];
                string extracted = ContainsStringArray(ch.ToString(), notAcceptedChars);
                if (extracted != null)
                {
                    // detectou um caractere não aceito
                    // verifica se já não está escapado
                    bool escaped = false;
                    try
                    {
                        escaped = argument[charIndex - 1] == ESCAPE_CHAR;
                    }
                    catch (Exception) { } finally
                    {
                        // início da string
                        if (!escaped) newArgument.Append(ESCAPE_CHAR);
                        newArgument.Append(ch);
                    }
                } else
                {
                    newArgument.Append(ch);
                }
            }
            string ret = argument.Trim().Contains(ARGUMENT_DIVISOR) ? '"' + newArgument.ToString() + '"' : newArgument.ToString();
            return ret ;
        }
        static void Main(string[] args)
        {
            if(args.Count() == 0)
            {
                Console.WriteLine("Console Não Operador");
                Console.WriteLine("Criado por Gabriel Scatolin");
                Console.WriteLine();
                while(true)
                {
                    Console.Write(System.IO.Directory.GetCurrentDirectory() + "> ");
                    string input = Console.ReadLine();
                    if (input == "") continue;

                    string exe = "";
                    string[] cmdArgs = { };

                    if(input.Contains(ARGUMENT_DIVISOR) == false)
                    {
                        exe = input;
                    } else
                    {
                        string[] inputData = input.Split(ARGUMENT_DIVISOR);
                        exe = inputData[0]; ;
                        cmdArgs = inputData.Skip(1).ToArray();
                    }

                    string ev = EvaluateCommand(exe, cmdArgs);
                    if(ev != "")
                    {
                        Console.WriteLine("Comando inválido ou arquivo não encontrado: " + ev);
                    }
                }
            } else
            {
                List<string> formattedArg = new List<string>();
                foreach(string arg in args.Skip(1))
                {
                    formattedArg.Add(arg);
                }

                string ev = EvaluateCommand(args[0], formattedArg.ToArray());
                if (ev != "")
                {
                    Console.WriteLine("Comando inválido ou arquivo não encontrado: " + ev);
                }
                Console.ReadLine();
            }
        }
    }
}

Important remarks

  • Commands like CD, CLEAR or HELP will not work, since the Console is working in another session, different from each invoked process.
  • The Console is not fully compatible with all Command Prompt commands, as in each command a native CMD command is invoked in the following expression:

CMD /C <arg0> [<arg1 formatado> <arg2 formatado> <arg3 formatado> ...]

  • In view of this, the application behaves well when calling methods like ECHO Olá&Mundo, or applications with complex expressions.
  • To call executables directly by their names, without using CMD as a port, edit inf.FileName = "CMD"; for inf.FileName = program; and edit the following line:
// de:
inf.Arguments = "/C " + program + ARGUMENT_DIVISOR + (args.Count() == 0 ? "" : string.Join(ARGUMENT_DIVISOR.ToString(), argsParsed).Trim());

// para:
inf.Arguments = args.Count() == 0 ? "" : string.Join(ARGUMENT_DIVISOR.ToString(), argsParsed).Trim();

inserir a descrição da imagem aqui


Explanation of methods

  • PrepareArgument(string) - intelligently escapes characters and prepares a single argument for their use;
  • EvaluateCommand(string, string[]) - runs the executable, using a console instance, with the arguments. Note: arguments are formatted within the method, no need to format the input;
  • Main(string[]) - method of input of the original arguments.

This system can also handle initializations without arguments, functioning as an interactive terminal.

  • 1

    I’ll test it tomorrow, but I appreciate it, but did you pay for your other answer? Already scored (== useful p some operators), did not understand why not keep it.

  • @Itwasn'tme do not find ethical two answers from the same author, considering one of them being wrong to the question. But I will listen to her advice and I will restore her =)

  • 1

    good morning, n worked, using in bat he hangs, I keep giving n enter and he leaves without closing the window.

  • @Itwasn'tme you will need to implement a way to assign the Console session to batch. This code I put up is an example of what can be done.

  • 1

    I will insist on your code, thank you, including editing, in what I can.. thank you.

2


̶N̶ã̶o̶ ̶s̶e̶i̶ ̶s̶e̶ ̶v̶a̶i̶ ̶l̶h̶e̶ ̶s̶e̶r̶v̶i̶r̶ ̶m̶a̶s̶ ̶e̶m̶ ̶c #̶ ̶o̶ ̶q̶u̶e̶ ̶d̶á̶ ̶p̶a̶r̶a̶ ̶f̶a̶z̶e̶r̶ ̶é̶ ̶c̶r̶i̶a̶r̶ ̶u̶m̶ ̶s̶h̶e̶l̶l̶ ̶f̶a̶l̶s̶o̶(̶F̶a̶k̶e̶ ̶S̶h̶e̶l̶l̶)̶ ̶o̶n̶d̶e̶ ̶o̶ ̶u̶s̶u̶á̶r̶i̶o̶ ̶d̶i̶g̶i̶t̶a̶ ̶a̶ ̶l̶i̶n̶h̶a̶ ̶d̶e̶ ̶c̶o̶m̶a̶n̶d̶o̶ ̶e̶n̶q̶u̶a̶n̶t̶o̶ ̶o̶ ̶f̶a̶k̶e̶ ̶s̶h̶e̶l̶l̶ ̶p̶r̶o̶c̶e̶s̶s̶a̶ ̶e̶s̶s̶a̶ ̶e̶n̶t̶r̶a̶d̶a̶ ̶e̶ ̶r̶e̶t̶r̶a̶n̶s̶m̶i̶t̶e̶ ̶p̶a̶r̶a̶ ̶o̶ ̶̶c̶m̶d̶̶ ̶o̶ ̶q̶u̶e̶ ̶v̶o̶c̶ê̶ ̶q̶u̶i̶s̶e̶r̶.̶ ̶ ̶N̶e̶s̶s̶e̶ ̶e̶x̶e̶m̶p̶l̶o̶ ̶e̶u̶ ̶a̶p̶e̶n̶a̶s̶ ̶r̶e̶t̶r̶a̶n̶s̶m̶i̶t̶o̶ ̶p̶a̶r̶a̶ ̶o̶ ̶̶c̶m̶d̶̶ ̶e̶x̶a̶t̶a̶m̶e̶n̶t̶e̶ ̶t̶u̶d̶o̶ ̶q̶u̶e̶ ̶u̶s̶u̶á̶r̶i̶o̶ ̶d̶i̶g̶i̶t̶a̶r̶,̶ ̶ú̶n̶i̶c̶a̶ ̶c̶o̶i̶s̶a̶ ̶q̶u̶e̶ ̶f̶a̶ç̶o̶ ̶e̶n̶t̶r̶e̶ ̶o̶ ̶d̶i̶g̶i̶t̶a̶r̶ ̶d̶a̶ ̶l̶i̶n̶h̶a̶ ̶d̶e̶ ̶c̶o̶m̶a̶n̶d̶o̶ ̶e̶ ̶r̶e̶c̶o̶n̶h̶e̶c̶i̶m̶e̶n̶t̶o̶ ̶p̶e̶l̶o̶ ̶̶c̶m̶d̶̶ ̶é̶ ̶q̶u̶e̶b̶r̶a̶r̶ ̶a̶ ̶l̶i̶n̶h̶a̶ ̶d̶e̶ ̶c̶o̶m̶a̶n̶d̶o̶ ̶e̶m̶ ̶e̶s̶p̶a̶ç̶o̶s̶ ̶e̶ ̶i̶m̶p̶r̶i̶m̶i̶r̶ ̶s̶e̶u̶s̶ ̶c̶o̶m̶p̶o̶n̶e̶n̶t̶e̶s̶ ̶e̶n̶t̶r̶e̶ ̶c̶o̶l̶c̶h̶e̶t̶e̶s̶:̶ ̶

To solve your problem I created a Fakeshell that acts in two modes. DOS Mode and Fakeshell Mode.

What are these two ways?

In DOS mode the program works exactly the same as the CDM the characters of operators & && | || < > >> e << are processed in a traditional way by performing their respective functions.

In Fakeshell mode the command line is intercepted and broken by the function CommandLineToArgvW staying in the dll Shell32.dllthat makes the lexical analysis of the command line according to the DOS standard, so that there are no problems of fractures or inconsistencies with the arguments. The command line break is passed to a string array, string[] args = SplitArgs(cmd);, where the first element is the name of the program you want to call and the rest of the vector are the arguments.

So instead of using the CMD, the program creates a parallel process that calls the program passed in args[0] using the rest of the vector args as arguments. The result is that the characters of operators & && | || < > >> e << are not processed but are understood as commands and are understood as simple strings.

Why two ways?

Because Fakeshell Mode does not work under CMD environment then the DOS commands are disabled and the only thing you can do is call executables, batch files or files associated with applications. Then the DOS mode serves to navigate the system and the Fakeshell mode serves to ignore DOS commands.

How to change operation modes? Press [CTRL] + P and then enter.

The program starts in DOS mode.

The code:

EDITED

using System;
using System.Text;
using System.IO;
using System.Diagnostics;

using System.Runtime.InteropServices;
using System.Linq;

namespace FakeShell
{
    class FakeShell
    {
        private static StringBuilder fakeOutput = null;




        //*********************************************************************************************************************
        //                                       EDIÇÃO
        //*********************************************************************************************************************
        private static bool prompt = false;

        /// <summary>
        /// Usa a função Win32 CommandLineToArgvW para analisar e quebrar a string de entrada segundo o formato pedido pelos DOS
        /// </summary>
        /// <param name="entrada">String a ser analizada</param>
        /// <returns>Retorna a quebra da enrada em vetor de strings segundo a sintaxe do dos</returns>
        static string[] SplitArgs(string entrada)
        {
            int contador;
            IntPtr ptrArgumentos;
            string[] argumentos;

            ptrArgumentos = CommandLineToArgvW(entrada, out contador);
            if (ptrArgumentos == IntPtr.Zero)
            {
                return new string[] {"Erro a analisar o argumento"};                
            }
            try
            {
                argumentos = new string[contador];
                for (int i = 0; i < contador; i++)
                    argumentos[i] = Marshal.PtrToStringUni(
                        Marshal.ReadIntPtr(ptrArgumentos, i * IntPtr.Size));
                return argumentos;
            }
            finally
            {
                LocalFree(ptrArgumentos);
            }
        }

        [DllImport("shell32.dll", SetLastError = true)]
        static extern IntPtr CommandLineToArgvW(
            [MarshalAs(UnmanagedType.LPWStr)] string lpCmdLine,
            out int pNumArgs);

        [DllImport("kernel32.dll")]
        static extern IntPtr LocalFree(IntPtr hMem);



        private static void OutputHandler(object sendingProcess,
            DataReceivedEventArgs outLine)
        {

            //Modifiquei o código despejo do buffer mas tem ainda um bug
            //Não sei o porque de em alguns momentos ele adiciona um \n na frente do prompt 
            if (!String.IsNullOrWhiteSpace(outLine.Data))
            {
                if (prompt)
                {
                    Console.Write(outLine.Data);
                    prompt = false;
                } else Console.WriteLine(outLine.Data);
            }
        }


        //*********************************************************************************************************************
        //                                       FIM - EDIÇÃO
        //*********************************************************************************************************************


        static void Main()
        {
            //****************************************************************************
            //                                       EDIÇÃO
            //****************************************************************************

            //Adicionei essa variável para controlar o processamento ou não do CMD. 
            //True é modo DOS
            //False é modo FakeShell
            bool processarCMD = true; //Começa em modo DOS
            //Adicionei essa variável para controlar e exibição ou não do prompt
            prompt = false;


            //*********************************************************************************************************************
            //                                       FIM - EDIÇÃO
            //*********************************************************************************************************************

            try
            {
                Process fakeShell = new Process();
                fakeShell.StartInfo.FileName = "cmd.exe";
                fakeShell.StartInfo.UseShellExecute = false;
                fakeShell.StartInfo.RedirectStandardOutput = true;
                fakeShell.OutputDataReceived += OutputHandler;
                fakeShell.StartInfo.RedirectStandardInput = true;
                fakeShell.Start();
                StreamWriter streamWriter = fakeShell.StandardInput;
                fakeShell.BeginOutputReadLine();
                String cmd;
                Console.WriteLine("-Fake Shell- digite um comando ou pressione enter para saír:\n");
                Console.WriteLine("pressione [CTRL] + P para ativar/desativar o modo de processameto CDM");
                streamWriter.Flush();
                do
                {
                    //para pegar o prompt
                    prompt = true;
                    streamWriter.WriteLine("\x0D"); 

                    cmd = Console.ReadLine();

                    if (!String.IsNullOrEmpty(cmd))
                    {
                        //****************************************************************************
                        //                                       EDIÇÃO
                        //****************************************************************************

                        if (cmd == "\u0010")
                        {
                            processarCMD = !processarCMD;
                            Console.WriteLine("<<Modo " + ((processarCMD)? "DOS" : "FakeShell") + ">>\n");
                            continue;
                        }

                        //SplitArgs quebra a linha de comando inserida segundo o formato DOS
                        string[] args = SplitArgs(cmd);

                        //Caso queira comutar entre modo DOS e modo FakeShell pressione [CTRL] + P
                        if (processarCMD) streamWriter.WriteLine(cmd);
                        else
                        {
                            //Esse foreach é só um exemplo ele pega a quebra da linha de comando e imprime
                            //verticalmente entre colchetes só para mostrar sequencia de quebra.
                            foreach (var arg in args)
                            {
                                Console.WriteLine("[" + arg + "]");
                            }

                            //Cria um novo processo paralelo para executar seu Aplicativo
                            Process newProcess = new Process();


                            newProcess.StartInfo.FileName = args[0];





                            //Junta os argumentos  
                            newProcess.StartInfo.Arguments = String.Join(" ", args.Skip(1).ToArray());


                            newProcess.StartInfo.UseShellExecute = true;




                            //Inicializa o processo chamando o executável em outra janela
                            try
                            {
                                newProcess.Start();

                            }
                            catch (Exception e)
                            {
                                streamWriter.WriteLine(e.Message);

                                continue;
                            }
                            //Aguarda até que o applicativo esteja ecerrado
                            newProcess.WaitForExit();



                            //Fecha o processo paralelo
                            newProcess.Close();


                        }                     


                        //****************************************************************************
                        //                                   FIM - EDIÇÃO
                        //****************************************************************************
                    }
                } while (!String.IsNullOrEmpty(cmd));
                streamWriter.Close();
                fakeShell.WaitForExit();
                fakeShell.Close();
            }
            catch (InvalidOperationException e)
            {
                Console.WriteLine(e);
            }
        }
    }
}

To do the test I created a directory and in this directory I gave the following in both modes:

C: Test> CMD.exe /? >> Test.txt

This command calls CMD help, which is extensive, and saves in a file called Teste.txt in DOS mode it creates the file normally in fake shell mode it opens a new window with CMD and consumes the srings >> and Teste.txt as if they were parameters and ignores the string already /? is also consumed as parameter and in the window that was opened displays the help of CMD without creating the file Teste.txt.

OBS: For your C# program to consume command line the method Main must have the following signature:

static void Main(string[] args)

  • Uniting the C# with the cmd: I have what meets me using only one bat/cmd in the call with the argumentos thus: 2>nul call_Redirect_Operator.cmd

  • Contents of bat/cmd = line calling Redirect_operator.exe: @echo off && 2>nul Redirect_Operator.exe <nul & && > >> < << | || <nul

  • Sadida returns this way: @echo off && 2>nul Redirect_Operator.exe <nul & && > >> < << | || <nul Hence, how to make him ignore what comes before the name and remove the first <nul (simulates fake/enter in the fakeshell) and the last <nul ?

  • For you to come back from this form:

  • Let me think about it 'cause then input analysis is selective.

  • This code I wrote doesn’t support this.

  • It is important that you understand the commentaries, also, as it was called the bat, which command inside the bat and the observations of the commentaries in the image.

  • It is very close, it n can give a "Crop" in the line <nul. or everything before the name, type, n use/write, before the name, and before the name remove 1st and last <nul ..

  • I understand you want something like the $ of linux

  • Yeah, it’s very close..

  • Really I’ll have to think about it. Wait for me...

  • Trankilo, your answer is the most complete present, and very close to precise, thank you.. Note, the first 2>null pre name, serves to have no exit in the "immersion" of the fake shell.. type, delete any screen/message of cmd...

  • The problem I’m encountering is this, in that image from the left the command interpreter interprets the symbol && as a command. Already on the right side of the image the command interpreter cannot interpret && as a command. For this to happen there has to be a tag indicating when to interpret or not the symbols as a command what you incur in double quotes.

  • Tranquil, && in this c 1 bat, @echo off &

  • Another thing, the last can go to the line below, it worked here.. In calling via . cmd, it works when it is part of a second command on the line: @echo off & 2>nul Redirect_Operator.exe <nul & && > >> < << | || line below the <nul

  • Produces the output @echo off & 2>nul Redirect_operator.exe <nul & && > >> < << | ||`` , just remove everything that comes before the name and after the name and the <nul on his way out, forgive you "rent"!

  • I don’t know if it goes according to your expectations, but I thought I’d create a $command for the cmd. It would have roughly the syntax **@echo off & 2>nul $(" Redirect_operator.exe <nul & && > >> < << | || ") *. The logic is following what was outside the $(" ") would be sued by the CMD what was inside would use the fakeshell argument break.

Show 14 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.