1d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
2d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)// found in the LICENSE file.
4d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
5d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)using Microsoft.Win32.SafeHandles;
6d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)using System;
7d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)using System.Collections.Generic;
8d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)using System.ComponentModel;
9d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)using System.IO;
10d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)using System.Linq;
11d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)using System.Text;
12d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)using System.Threading.Tasks;
13d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
14d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)using ChromeDebug.LowLevel;
158bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)using System.Runtime.InteropServices;
168bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)using System.Drawing;
17d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
18d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)namespace ChromeDebug {
19d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  internal class ProcessDetail : IDisposable {
20d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    public ProcessDetail(int pid) {
21d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // Initialize everything to null in case something fails.
22d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      this.processId = pid;
23d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      this.processHandleFlags = LowLevelTypes.ProcessAccessFlags.NONE;
24d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      this.cachedProcessBasicInfo = null;
25d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      this.machineTypeIsLoaded = false;
26d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      this.machineType = LowLevelTypes.MachineType.UNKNOWN;
27d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      this.cachedPeb = null;
28d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      this.cachedProcessParams = null;
29d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      this.cachedCommandLine = null;
30d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      this.processHandle = IntPtr.Zero;
31d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
32d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      OpenAndCacheProcessHandle();
33d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    }
34d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
35d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // Returns the machine type (x86, x64, etc) of this process.  Uses lazy evaluation and caches
36d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // the result.
37d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    public LowLevelTypes.MachineType MachineType {
38d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      get {
39d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        if (machineTypeIsLoaded)
40d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          return machineType;
41d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        if (!CanQueryProcessInformation)
42d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          return LowLevelTypes.MachineType.UNKNOWN;
43d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
44d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        CacheMachineType();
45d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        return machineType;
46d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      }
47d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    }
48d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
498bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    public string NativeProcessImagePath {
508bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      get {
518bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        if (nativeProcessImagePath == null) {
528bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)          nativeProcessImagePath = QueryProcessImageName(
538bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)              LowLevelTypes.ProcessQueryImageNameMode.NATIVE_SYSTEM_FORMAT);
548bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        }
558bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        return nativeProcessImagePath;
568bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      }
578bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    }
588bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
598bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    public string Win32ProcessImagePath {
608bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      get {
618bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        if (win32ProcessImagePath == null) {
628bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)          win32ProcessImagePath = QueryProcessImageName(
638bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)              LowLevelTypes.ProcessQueryImageNameMode.WIN32_FORMAT);
648bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        }
658bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        return win32ProcessImagePath;
668bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      }
678bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    }
688bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
698bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    public Icon SmallIcon {
708bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      get {
718bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        LowLevel.LowLevelTypes.SHFILEINFO info = new LowLevelTypes.SHFILEINFO(true);
728bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        LowLevel.LowLevelTypes.SHGFI flags = LowLevel.LowLevelTypes.SHGFI.Icon
738bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)                                             | LowLevelTypes.SHGFI.SmallIcon
748bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)                                             | LowLevelTypes.SHGFI.OpenIcon
758bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)                                             | LowLevelTypes.SHGFI.UseFileAttributes;
768bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        int cbFileInfo = Marshal.SizeOf(info);
778bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        LowLevel.NativeMethods.SHGetFileInfo(Win32ProcessImagePath,
788bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)                                             256,
798bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)                                             ref info,
808bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)                                             (uint)cbFileInfo,
818bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)                                             (uint)flags);
828bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        return Icon.FromHandle(info.hIcon);
838bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      }
848bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    }
858bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
86d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // Returns the command line that this process was launched with.  Uses lazy evaluation and
87d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // caches the result.  Reads the command line from the PEB of the running process.
88d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    public string CommandLine {
89d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      get {
90d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        if (!CanReadPeb)
91d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          throw new InvalidOperationException();
92d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        CacheProcessInformation();
93d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        CachePeb();
94d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        CacheProcessParams();
95d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        CacheCommandLine();
96d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        return cachedCommandLine;
97d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      }
98d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    }
99d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
100d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // Determines if we have permission to read the process's PEB.
101d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    public bool CanReadPeb {
102d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      get {
103d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        LowLevelTypes.ProcessAccessFlags required_flags =
104d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)            LowLevelTypes.ProcessAccessFlags.VM_READ
105d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          | LowLevelTypes.ProcessAccessFlags.QUERY_INFORMATION;
106d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
107d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        // In order to read the PEB, we must have *both* of these flags.
108d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        if ((processHandleFlags & required_flags) != required_flags)
109d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          return false;
110d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
111d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        // If we're on a 64-bit OS, in a 32-bit process, and the target process is not 32-bit,
112d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        // we can't read its PEB.
113d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess
114d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)            && (MachineType != LowLevelTypes.MachineType.X86))
115d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          return false;
116d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
117d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        return true;
118d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      }
119d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    }
120d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
121d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // If we can't read the process's PEB, we may still be able to get other kinds of information
122d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // from the process.  This flag determines if we can get lesser information.
123d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    private bool CanQueryProcessInformation {
124d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      get {
125d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        LowLevelTypes.ProcessAccessFlags required_flags =
126d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)            LowLevelTypes.ProcessAccessFlags.QUERY_LIMITED_INFORMATION
127d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          | LowLevelTypes.ProcessAccessFlags.QUERY_INFORMATION;
128d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
129d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        // In order to query the process, we need *either* of these flags.
130d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        return (processHandleFlags & required_flags) != LowLevelTypes.ProcessAccessFlags.NONE;
131d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      }
132d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    }
133d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
1348bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    private string QueryProcessImageName(LowLevelTypes.ProcessQueryImageNameMode mode) {
1358bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      StringBuilder moduleBuffer = new StringBuilder(1024);
1368bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      int size = moduleBuffer.Capacity;
1378bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      NativeMethods.QueryFullProcessImageName(
1388bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)          processHandle,
1398bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)          mode,
1408bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)          moduleBuffer,
1418bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)          ref size);
1428bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      if (mode == LowLevelTypes.ProcessQueryImageNameMode.NATIVE_SYSTEM_FORMAT)
1438bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        moduleBuffer.Insert(0, "\\\\?\\GLOBALROOT");
1448bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      return moduleBuffer.ToString();
1458bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    }
1468bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
147d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // Loads the top-level structure of the process's information block and caches it.
148d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    private void CacheProcessInformation() {
149d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      System.Diagnostics.Debug.Assert(CanReadPeb);
150d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
151d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // Fetch the process info and set the fields.
152d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      LowLevelTypes.PROCESS_BASIC_INFORMATION temp = new LowLevelTypes.PROCESS_BASIC_INFORMATION();
153d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      int size;
154d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      LowLevelTypes.NTSTATUS status = NativeMethods.NtQueryInformationProcess(
155d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          processHandle,
156d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          LowLevelTypes.PROCESSINFOCLASS.PROCESS_BASIC_INFORMATION,
157d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          ref temp,
158d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          Utility.UnmanagedStructSize<LowLevelTypes.PROCESS_BASIC_INFORMATION>(),
159d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          out size);
160d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
161d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      if (status != LowLevelTypes.NTSTATUS.SUCCESS) {
162d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        throw new Win32Exception();
163d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      }
164d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
165d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      cachedProcessBasicInfo = temp;
166d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    }
167d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
168d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // Follows a pointer from the PROCESS_BASIC_INFORMATION structure in the target process's
169d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // address space to read the PEB.
170d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    private void CachePeb() {
171d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      System.Diagnostics.Debug.Assert(CanReadPeb);
172d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
173d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      if (cachedPeb == null) {
174d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        cachedPeb = Utility.ReadUnmanagedStructFromProcess<LowLevelTypes.PEB>(
175d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)            processHandle,
176d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)            cachedProcessBasicInfo.Value.PebBaseAddress);
177d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      }
178d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    }
179d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
180d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // Follows a pointer from the PEB structure in the target process's address space to read the
181d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // RTL_USER_PROCESS_PARAMETERS structure.
182d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    private void CacheProcessParams() {
183d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      System.Diagnostics.Debug.Assert(CanReadPeb);
184d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
185d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      if (cachedProcessParams == null) {
186d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        cachedProcessParams =
187d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)            Utility.ReadUnmanagedStructFromProcess<LowLevelTypes.RTL_USER_PROCESS_PARAMETERS>(
188d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                processHandle, cachedPeb.Value.ProcessParameters);
189d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      }
190d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    }
191d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
192d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    private void CacheCommandLine() {
193d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      System.Diagnostics.Debug.Assert(CanReadPeb);
194d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
195d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      if (cachedCommandLine == null) {
196d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        cachedCommandLine = Utility.ReadStringUniFromProcess(
197d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)            processHandle,
198d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)            cachedProcessParams.Value.CommandLine.Buffer,
199d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)            cachedProcessParams.Value.CommandLine.Length / 2);
200d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      }
201d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    }
202d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
203d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    private void CacheMachineType() {
204d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      System.Diagnostics.Debug.Assert(CanQueryProcessInformation);
205d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
206d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // If our extension is running in a 32-bit process (which it is), then attempts to access
207d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // files in C:\windows\system (and a few other files) will redirect to C:\Windows\SysWOW64
208d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // and we will mistakenly think that the image file is a 32-bit image.  The way around this
209d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // is to use a native system format path, of the form:
210d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      //    \\?\GLOBALROOT\Device\HarddiskVolume0\Windows\System\foo.dat
2118bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      // NativeProcessImagePath gives us the full process image path in the desired format.
2128bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      string path = NativeProcessImagePath;
213d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
214d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // Open the PE File as a binary file, and parse just enough information to determine the
215d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // machine type.
216d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      //http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
217d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      using (SafeFileHandle safeHandle = NativeMethods.CreateFile(
2188bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)                 path,
219d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                 LowLevelTypes.FileAccessFlags.GENERIC_READ,
220d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                 LowLevelTypes.FileShareFlags.SHARE_READ,
221d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                 IntPtr.Zero,
222d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                 LowLevelTypes.FileCreationDisposition.OPEN_EXISTING,
223d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                 LowLevelTypes.FileFlagsAndAttributes.NORMAL,
224d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                 IntPtr.Zero)) {
225d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        FileStream fs = new FileStream(safeHandle, FileAccess.Read);
226d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        using (BinaryReader br = new BinaryReader(fs)) {
227d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          fs.Seek(0x3c, SeekOrigin.Begin);
228d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          Int32 peOffset = br.ReadInt32();
229d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          fs.Seek(peOffset, SeekOrigin.Begin);
230d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          UInt32 peHead = br.ReadUInt32();
231d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          if (peHead != 0x00004550) // "PE\0\0", little-endian
232d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)            throw new Exception("Can't find PE header");
233d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          machineType = (LowLevelTypes.MachineType)br.ReadUInt16();
234d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          machineTypeIsLoaded = true;
235d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        }
236d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      }
237d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    }
238d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
239d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    private void OpenAndCacheProcessHandle() {
240d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // Try to open a handle to the process with the highest level of privilege, but if we can't
241d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      // do that then fallback to requesting access with a lower privilege level.
242d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      processHandleFlags = LowLevelTypes.ProcessAccessFlags.QUERY_INFORMATION
243d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                         | LowLevelTypes.ProcessAccessFlags.VM_READ;
244d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      processHandle = NativeMethods.OpenProcess(processHandleFlags, false, processId);
245d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      if (processHandle == IntPtr.Zero) {
246d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        processHandleFlags = LowLevelTypes.ProcessAccessFlags.QUERY_LIMITED_INFORMATION;
247d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        processHandle = NativeMethods.OpenProcess(processHandleFlags, false, processId);
248d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        if (processHandle == IntPtr.Zero) {
249d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          processHandleFlags = LowLevelTypes.ProcessAccessFlags.NONE;
250d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          throw new Win32Exception();
251d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        }
252d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      }
253d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    }
254d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
255d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // An open handle to the process, along with the set of access flags that the handle was
256d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // open with.
257d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    private int processId;
258d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    private IntPtr processHandle;
2598bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    private LowLevelTypes.ProcessAccessFlags processHandleFlags;
2608bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    private string nativeProcessImagePath;
2618bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    private string win32ProcessImagePath;
262d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
263d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // The machine type is read by parsing the PE image file of the running process, so we cache
264d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // its value since the operation expensive.
265d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    private bool machineTypeIsLoaded;
266d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    private LowLevelTypes.MachineType machineType;
267d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
268d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // The following fields exist ultimately so that we can access the command line.  However,
269d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // each field must be read separately through a pointer into another process's address
270d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // space so the access is expensive, hence we cache the values.
271d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    private Nullable<LowLevelTypes.PROCESS_BASIC_INFORMATION> cachedProcessBasicInfo;
272d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    private Nullable<LowLevelTypes.PEB> cachedPeb;
273d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    private Nullable<LowLevelTypes.RTL_USER_PROCESS_PARAMETERS> cachedProcessParams;
274d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    private string cachedCommandLine;
275d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
2768bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    ~ProcessDetail() {
2778bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      Dispose();
2788bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    }
2798bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
280d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    public void Dispose() {
281d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      if (processHandle != IntPtr.Zero)
282d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        NativeMethods.CloseHandle(processHandle);
283d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      processHandle = IntPtr.Zero;
284d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    }
285d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  }
286d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
287