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; }