Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(245)

Unified Diff: tools/win/ChromeDebug/ChromeDebug/ProcessDetail.cs

Issue 23480085: Initial submit of ChromeDebug extension (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Add README.txt Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: tools/win/ChromeDebug/ChromeDebug/ProcessDetail.cs
diff --git a/tools/win/ChromeDebug/ChromeDebug/ProcessDetail.cs b/tools/win/ChromeDebug/ChromeDebug/ProcessDetail.cs
new file mode 100644
index 0000000000000000000000000000000000000000..63dacd53709cbeaa4e8445886844974360546d0e
--- /dev/null
+++ b/tools/win/ChromeDebug/ChromeDebug/ProcessDetail.cs
@@ -0,0 +1,238 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+using Microsoft.Win32.SafeHandles;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using ChromeDebug.LowLevel;
+
+namespace ChromeDebug {
+ internal class ProcessDetail : IDisposable {
+ public ProcessDetail(int pid) {
+ // Initialize everything to null in case something fails.
+ this.processId = pid;
+ this.processHandleFlags = LowLevelTypes.ProcessAccessFlags.NONE;
+ this.cachedProcessBasicInfo = null;
+ this.machineTypeIsLoaded = false;
+ this.machineType = LowLevelTypes.MachineType.UNKNOWN;
+ this.cachedPeb = null;
+ this.cachedProcessParams = null;
+ this.cachedCommandLine = null;
+ this.processHandle = IntPtr.Zero;
+
+ OpenAndCacheProcessHandle();
+ }
+
+ // Returns the machine type (x86, x64, etc) of this process. Uses lazy evaluation and caches
+ // the result.
+ public LowLevelTypes.MachineType MachineType {
+ get {
+ if (machineTypeIsLoaded)
+ return machineType;
+ if (!CanQueryProcessInformation)
+ return LowLevelTypes.MachineType.UNKNOWN;
+
+ CacheMachineType();
+ return machineType;
+ }
+ }
+
+ // Returns the command line that this process was launched with. Uses lazy evaluation and
+ // caches the result. Reads the command line from the PEB of the running process.
+ public string CommandLine {
+ get {
+ if (!CanReadPeb)
+ throw new InvalidOperationException();
+ CacheProcessInformation();
+ CachePeb();
+ CacheProcessParams();
+ CacheCommandLine();
+ return cachedCommandLine;
+ }
+ }
+
+ // Determines if we have permission to read the process's PEB.
+ public bool CanReadPeb {
+ get {
+ LowLevelTypes.ProcessAccessFlags required_flags =
+ LowLevelTypes.ProcessAccessFlags.VM_READ
+ | LowLevelTypes.ProcessAccessFlags.QUERY_INFORMATION;
+
+ // In order to read the PEB, we must have *both* of these flags.
+ if ((processHandleFlags & required_flags) != required_flags)
+ return false;
+
+ // If we're on a 64-bit OS, in a 32-bit process, and the target process is not 32-bit,
+ // we can't read its PEB.
+ if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess
+ && (MachineType != LowLevelTypes.MachineType.X86))
+ return false;
+
+ return true;
+ }
+ }
+
+ // If we can't read the process's PEB, we may still be able to get other kinds of information
+ // from the process. This flag determines if we can get lesser information.
+ private bool CanQueryProcessInformation {
+ get {
+ LowLevelTypes.ProcessAccessFlags required_flags =
+ LowLevelTypes.ProcessAccessFlags.QUERY_LIMITED_INFORMATION
+ | LowLevelTypes.ProcessAccessFlags.QUERY_INFORMATION;
+
+ // In order to query the process, we need *either* of these flags.
+ return (processHandleFlags & required_flags) != LowLevelTypes.ProcessAccessFlags.NONE;
+ }
+ }
+
+ // Loads the top-level structure of the process's information block and caches it.
+ private void CacheProcessInformation() {
+ System.Diagnostics.Debug.Assert(CanReadPeb);
+
+ // Fetch the process info and set the fields.
+ LowLevelTypes.PROCESS_BASIC_INFORMATION temp = new LowLevelTypes.PROCESS_BASIC_INFORMATION();
+ int size;
+ LowLevelTypes.NTSTATUS status = NativeMethods.NtQueryInformationProcess(
+ processHandle,
+ LowLevelTypes.PROCESSINFOCLASS.PROCESS_BASIC_INFORMATION,
+ ref temp,
+ Utility.UnmanagedStructSize<LowLevelTypes.PROCESS_BASIC_INFORMATION>(),
+ out size);
+
+ if (status != LowLevelTypes.NTSTATUS.SUCCESS) {
+ throw new Win32Exception();
+ }
+
+ cachedProcessBasicInfo = temp;
+ }
+
+ // Follows a pointer from the PROCESS_BASIC_INFORMATION structure in the target process's
+ // address space to read the PEB.
+ private void CachePeb() {
+ System.Diagnostics.Debug.Assert(CanReadPeb);
+
+ if (cachedPeb == null) {
+ cachedPeb = Utility.ReadUnmanagedStructFromProcess<LowLevelTypes.PEB>(
+ processHandle,
+ cachedProcessBasicInfo.Value.PebBaseAddress);
+ }
+ }
+
+ // Follows a pointer from the PEB structure in the target process's address space to read the
+ // RTL_USER_PROCESS_PARAMETERS structure.
+ private void CacheProcessParams() {
+ System.Diagnostics.Debug.Assert(CanReadPeb);
+
+ if (cachedProcessParams == null) {
+ cachedProcessParams =
+ Utility.ReadUnmanagedStructFromProcess<LowLevelTypes.RTL_USER_PROCESS_PARAMETERS>(
+ processHandle, cachedPeb.Value.ProcessParameters);
+ }
+ }
+
+ private void CacheCommandLine() {
+ System.Diagnostics.Debug.Assert(CanReadPeb);
+
+ if (cachedCommandLine == null) {
+ cachedCommandLine = Utility.ReadStringUniFromProcess(
+ processHandle,
+ cachedProcessParams.Value.CommandLine.Buffer,
+ cachedProcessParams.Value.CommandLine.Length / 2);
+ }
+ }
+
+ private void CacheMachineType() {
+ System.Diagnostics.Debug.Assert(CanQueryProcessInformation);
+
+ StringBuilder moduleBuffer = new StringBuilder(1024);
+ int size = moduleBuffer.Capacity;
+
+ // If our extension is running in a 32-bit process (which it is), then attempts to access
+ // files in C:\windows\system (and a few other files) will redirect to C:\Windows\SysWOW64
+ // and we will mistakenly think that the image file is a 32-bit image. The way around this
+ // is to use a native system format path, of the form:
+ // \\?\GLOBALROOT\Device\HarddiskVolume0\Windows\System\foo.dat
+ // By using the NATIVE_SYSTEM_FORMAT flag to QueryFullProcessImageName, we can get the path
+ // in this format.
+ NativeMethods.QueryFullProcessImageName(
+ processHandle,
+ LowLevelTypes.ProcessQueryImageNameMode.NATIVE_SYSTEM_FORMAT,
+ moduleBuffer,
+ ref size);
+ moduleBuffer.Insert(0, "\\\\?\\GLOBALROOT");
+ string module = moduleBuffer.ToString();
+
+ // Open the PE File as a binary file, and parse just enough information to determine the
+ // machine type.
+ //http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
+ using (SafeFileHandle safeHandle = NativeMethods.CreateFile(
+ module,
+ LowLevelTypes.FileAccessFlags.GENERIC_READ,
+ LowLevelTypes.FileShareFlags.SHARE_READ,
+ IntPtr.Zero,
+ LowLevelTypes.FileCreationDisposition.OPEN_EXISTING,
+ LowLevelTypes.FileFlagsAndAttributes.NORMAL,
+ IntPtr.Zero)) {
+ FileStream fs = new FileStream(safeHandle, FileAccess.Read);
+ using (BinaryReader br = new BinaryReader(fs)) {
+ fs.Seek(0x3c, SeekOrigin.Begin);
+ Int32 peOffset = br.ReadInt32();
+ fs.Seek(peOffset, SeekOrigin.Begin);
+ UInt32 peHead = br.ReadUInt32();
+ if (peHead != 0x00004550) // "PE\0\0", little-endian
+ throw new Exception("Can't find PE header");
+ machineType = (LowLevelTypes.MachineType)br.ReadUInt16();
+ machineTypeIsLoaded = true;
+ }
+ }
+ }
+
+ private void OpenAndCacheProcessHandle() {
+ // Try to open a handle to the process with the highest level of privilege, but if we can't
+ // do that then fallback to requesting access with a lower privilege level.
+ processHandleFlags = LowLevelTypes.ProcessAccessFlags.QUERY_INFORMATION
+ | LowLevelTypes.ProcessAccessFlags.VM_READ;
+ processHandle = NativeMethods.OpenProcess(processHandleFlags, false, processId);
+ if (processHandle == IntPtr.Zero) {
+ processHandleFlags = LowLevelTypes.ProcessAccessFlags.QUERY_LIMITED_INFORMATION;
+ processHandle = NativeMethods.OpenProcess(processHandleFlags, false, processId);
+ if (processHandle == IntPtr.Zero) {
+ processHandleFlags = LowLevelTypes.ProcessAccessFlags.NONE;
+ throw new Win32Exception();
+ }
+ }
+ }
+
+ // An open handle to the process, along with the set of access flags that the handle was
+ // open with.
+ private int processId;
+ private IntPtr processHandle;
+ LowLevelTypes.ProcessAccessFlags processHandleFlags;
+
+ // The machine type is read by parsing the PE image file of the running process, so we cache
+ // its value since the operation expensive.
+ private bool machineTypeIsLoaded;
+ private LowLevelTypes.MachineType machineType;
+
+ // The following fields exist ultimately so that we can access the command line. However,
+ // each field must be read separately through a pointer into another process's address
+ // space so the access is expensive, hence we cache the values.
+ private Nullable<LowLevelTypes.PROCESS_BASIC_INFORMATION> cachedProcessBasicInfo;
+ private Nullable<LowLevelTypes.PEB> cachedPeb;
+ private Nullable<LowLevelTypes.RTL_USER_PROCESS_PARAMETERS> cachedProcessParams;
+ private string cachedCommandLine;
+
+ public void Dispose() {
+ if (processHandle != IntPtr.Zero)
+ NativeMethods.CloseHandle(processHandle);
+ processHandle = IntPtr.Zero;
+ }
+ }
+}
« no previous file with comments | « tools/win/ChromeDebug/ChromeDebug/ProcessCategory.cs ('k') | tools/win/ChromeDebug/ChromeDebug/Properties/AssemblyInfo.cs » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698