Ejemplo n.º 1
0
        public int DocumentState(String applicationName, String commandLine, PROCESS_INFORMATION pi, STARTUPINFOW si, string lpDesktop, string lpTitle, int originalParentProcessId, bool isConsole)
        {
            var state = new State {
                ApplicationName = applicationName,
                CommandLine = commandLine,
                ProcessInformation = pi,
                StartupInformation = si,
                Desktop = lpDesktop,
                Title = lpTitle,
                OriginalParentProcessId = originalParentProcessId,
                IsConsole = isConsole
            };

            _states.Add(_states.Count + 1, state);

            return _states.Count;
        }
        public int Run(string[] args)
        {
            bool bouncingRequest = false;
            var options = args.Switches();
            var parameters = args.Parameters();

            foreach (var arg in options.Keys)
            {
                var argumentParameters = options[arg];

                switch (arg)
                {
                    case "load-config":
                        // all ready done, but don't get too picky.
                        break;

                    case "nologo":
                        this.Assembly().SetLogo(string.Empty);
                        break;

                    case "help":
                        return Help();

                    case "output-file":
                        _traceFile = Path.GetFullPath(argumentParameters.LastOrDefault());
                        break;

                    case "bounce":
                        // whoa there, this is an internal bounce request
                        bouncingRequest = true;
                        _ticket = int.Parse(argumentParameters.LastOrDefault());
                        break;

                    default:
                        return Fail("Unknown parameter [--{0}]", arg);
                }
            }

            if (parameters.Count() < 1 && !bouncingRequest)
            {
                return Fail("Missing Command. \r\n\r\n    Use --help for command line help.");
            }

            //
            // Get the rest of the command line
            //
            var application = string.Empty;
            var cmdline = string.Empty;

            if (!bouncingRequest)
            {
                application = Helpers.FindInPath(parameters.First());

                if( !File.Exists(application)) {
                    return Fail("Unable to start given command line. \r\n\r\n    Can not find executable [{0}]", application);
                }

                cmdline = parameters.Skip(1).Aggregate("\"" + application + "\" ", (current, i) => current + (i.Contains(" ") ? "\"" + i + "\" " : i + " ")).Trim();
                Logo();
            }

            //
            // Register our .NET remoting "scribe" and related comm channel
            //
            if (!bouncingRequest)
            {
                _channel = new IpcChannel("CoAppTraceIpc");
                WellKnownServiceTypeEntry wkst = new WellKnownServiceTypeEntry(typeof(Scribe),
                    "Scribe", WellKnownObjectMode.Singleton);

                RemotingConfiguration.RegisterWellKnownServiceType(wkst);
                ChannelServices.RegisterChannel(_channel, false);
            }

            //
            // Create the first instance
            //
            var scribe = (IScribe)Activator.GetObject(typeof(IScribe), "ipc://CoAppTraceIpc/Scribe");
            scribe.Ping();

            //
            // Detour and launch the target process.
            //
            // If we're bouncing a request, we need to ensure we restore all the
            // original parameters before continuing.
            //
            IntPtr ppi = Marshal.AllocHGlobal(Constants.SizeOfProcessInformation);
            Helpers.RtlZeroMemory(ppi, Constants.SizeOfProcessInformation);

            IntPtr psi = Marshal.AllocHGlobal(Constants.SizeOfStartupInformation);
            Helpers.RtlZeroMemory(psi, Constants.SizeOfStartupInformation);
            Marshal.WriteInt32(psi, 0, Constants.SizeOfStartupInformation);

            var state = new Scribe.State();

            if (bouncingRequest)
            {
                state = scribe.RetrieveState(_ticket);

                var processInfo = new PROCESS_INFORMATION {
                    hProcess = state.pi_hProcess.ToCorrectIntPtr(),
                    hThread = state.pi_hThread.ToCorrectIntPtr(),
                    dwProcessId = state.pi_dwProcessId,
                    dwThreadId = state.pi_dwThreadId
                };

                var startupInfo = new STARTUPINFO {
                    cb = (uint) Constants.SizeOfStartupInformation,
                    lpReserved = IntPtr.Zero,
                    lpDesktop =
                        string.IsNullOrEmpty(state.Desktop)
                            ? IntPtr.Zero
                            : state.IsWide ? Marshal.StringToHGlobalUni(state.Desktop) : Marshal.StringToHGlobalAnsi(state.Desktop),
                    lpTitle =
                        string.IsNullOrEmpty(state.Title)
                            ? IntPtr.Zero
                            : state.IsWide ? Marshal.StringToHGlobalUni(state.Title) : Marshal.StringToHGlobalAnsi(state.Title),
                    dwX = state.si_dwX,
                    dwY = state.si_dwY,
                    dwXSize = state.si_dwXSize,
                    dwYSize = state.si_dwYSize,
                    dwXCountChars = state.si_dwXCountChars,
                    dwYCountChars = state.si_dwYCountChars,
                    dwFillAttribute = state.si_dwFillAttribute,
                    dwFlags = state.si_dwFlags,
                    wShowWindow = state.si_wShowWindow,
                    cbReserved2 = 0,
                    lpReserved2 = IntPtr.Zero,
                    hStdInput = state.si_hStdInput.ToCorrectIntPtr(),
                    hStdOutput =state.si_hStdOutput.ToCorrectIntPtr(),
                    hStdError = state.si_hStdError.ToCorrectIntPtr()
                };

                application = state.ApplicationName;
                cmdline = state.CommandLine;

                // Leaky. But not really a problem.
                Marshal.StructureToPtr(processInfo, ppi, false);
                Marshal.StructureToPtr(startupInfo, psi, false);
                //
                // The bounce process has a console of its own (console subsystem).
                // We need to destroy it and latch onto our parent's existing one.
                //
                // Kernel32.FreeConsole();
                // Kernel32.AttachConsole(-1);
            }

            var lpApplication = Marshal.StringToHGlobalUni(application);
            var lpCmdLine = Marshal.StringToHGlobalUni(cmdline);

            bool bounced;

            Detours.DetourCreateProcessWithDllW(lpApplication, lpCmdLine, IntPtr.Zero, IntPtr.Zero, Constants.DetoursDllName,
                Constants.CoAppTraceHost, 1, 0, IntPtr.Zero, IntPtr.Zero, psi, ppi, application, cmdline , out bounced);

            var pi = (PROCESS_INFORMATION)Marshal.PtrToStructure(ppi, typeof(PROCESS_INFORMATION));

            Debug.Assert(pi.dwProcessId != 0, "Process ID 0 detected.");

            if (bouncingRequest) {
                scribe.DocumentNewProcess(state.OriginalParentProcessId, (int)pi.dwProcessId, cmdline, application, Environment.CurrentDirectory);

                // we need to give the child app time enough to get going before doing anything...
                // Thread.Sleep(1000);

                if (state.IsConsole) {
                    try {
                        Scribe.GetStartedProcessById((int) pi.dwProcessId).WaitForExit();
                    }
                    catch {
                        /// meh. it dies, it dies.
                        // Console.WriteLine("Exception'd. {0}", e.Message);
                    }
                }
            }
            else {
                if (!bounced) {
                    scribe.DocumentNewProcess((int) pi.dwProcessId, cmdline, application, Environment.CurrentDirectory);
                }

                //
                // Wait for the spawned process (and its children) to terminate completely (waiting 30 seconds tops).
                //
                while(!scribe.Finished.WaitOne(30000) ) {
                    scribe.CheckForLingeringProcesses();
                }

                //
                // Save the trace output to disk.
                //
                scribe.Write(_traceFile.format((int) pi.dwProcessId));
                using (new ConsoleColors(ConsoleColor.White, ConsoleColor.Black)) {
                    Console.WriteLine("TRACE COMPLETE");
                    Console.WriteLine("TRACE File: {0} ", _traceFile.format((int) pi.dwProcessId).GetFullPath());
                }
            }

            return 0;
        }