PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/tools/win/ChromeDebug/ChromeDebug/ProcessDetail.cs

https://github.com/chromium/chromium
C# | 286 lines | 211 code | 36 blank | 39 comment | 32 complexity | 1ca1ca9d9f5b98a6c4a043413f731818 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, Apache-2.0, BSD-3-Clause
  1. // Copyright 2013 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. using Microsoft.Win32.SafeHandles;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.ComponentModel;
  8. using System.IO;
  9. using System.Linq;
  10. using System.Text;
  11. using System.Threading.Tasks;
  12. using ChromeDebug.LowLevel;
  13. using System.Runtime.InteropServices;
  14. using System.Drawing;
  15. namespace ChromeDebug {
  16. internal class ProcessDetail : IDisposable {
  17. public ProcessDetail(int pid) {
  18. // Initialize everything to null in case something fails.
  19. this.processId = pid;
  20. this.processHandleFlags = LowLevelTypes.ProcessAccessFlags.NONE;
  21. this.cachedProcessBasicInfo = null;
  22. this.machineTypeIsLoaded = false;
  23. this.machineType = LowLevelTypes.MachineType.UNKNOWN;
  24. this.cachedPeb = null;
  25. this.cachedProcessParams = null;
  26. this.cachedCommandLine = null;
  27. this.processHandle = IntPtr.Zero;
  28. OpenAndCacheProcessHandle();
  29. }
  30. // Returns the machine type (x86, x64, etc) of this process. Uses lazy evaluation and caches
  31. // the result.
  32. public LowLevelTypes.MachineType MachineType {
  33. get {
  34. if (machineTypeIsLoaded)
  35. return machineType;
  36. if (!CanQueryProcessInformation)
  37. return LowLevelTypes.MachineType.UNKNOWN;
  38. CacheMachineType();
  39. return machineType;
  40. }
  41. }
  42. public string NativeProcessImagePath {
  43. get {
  44. if (nativeProcessImagePath == null) {
  45. nativeProcessImagePath = QueryProcessImageName(
  46. LowLevelTypes.ProcessQueryImageNameMode.NATIVE_SYSTEM_FORMAT);
  47. }
  48. return nativeProcessImagePath;
  49. }
  50. }
  51. public string Win32ProcessImagePath {
  52. get {
  53. if (win32ProcessImagePath == null) {
  54. win32ProcessImagePath = QueryProcessImageName(
  55. LowLevelTypes.ProcessQueryImageNameMode.WIN32_FORMAT);
  56. }
  57. return win32ProcessImagePath;
  58. }
  59. }
  60. public Icon SmallIcon {
  61. get {
  62. LowLevel.LowLevelTypes.SHFILEINFO info = new LowLevelTypes.SHFILEINFO(true);
  63. LowLevel.LowLevelTypes.SHGFI flags = LowLevel.LowLevelTypes.SHGFI.Icon
  64. | LowLevelTypes.SHGFI.SmallIcon
  65. | LowLevelTypes.SHGFI.OpenIcon
  66. | LowLevelTypes.SHGFI.UseFileAttributes;
  67. int cbFileInfo = Marshal.SizeOf(info);
  68. LowLevel.NativeMethods.SHGetFileInfo(Win32ProcessImagePath,
  69. 256,
  70. ref info,
  71. (uint)cbFileInfo,
  72. (uint)flags);
  73. return Icon.FromHandle(info.hIcon);
  74. }
  75. }
  76. // Returns the command line that this process was launched with. Uses lazy evaluation and
  77. // caches the result. Reads the command line from the PEB of the running process.
  78. public string CommandLine {
  79. get {
  80. if (!CanReadPeb)
  81. throw new InvalidOperationException();
  82. CacheProcessInformation();
  83. CachePeb();
  84. CacheProcessParams();
  85. CacheCommandLine();
  86. return cachedCommandLine;
  87. }
  88. }
  89. // Determines if we have permission to read the process's PEB.
  90. public bool CanReadPeb {
  91. get {
  92. LowLevelTypes.ProcessAccessFlags required_flags =
  93. LowLevelTypes.ProcessAccessFlags.VM_READ
  94. | LowLevelTypes.ProcessAccessFlags.QUERY_INFORMATION;
  95. // In order to read the PEB, we must have *both* of these flags.
  96. if ((processHandleFlags & required_flags) != required_flags)
  97. return false;
  98. // If we're on a 64-bit OS, in a 32-bit process, and the target process is not 32-bit,
  99. // we can't read its PEB.
  100. if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess
  101. && (MachineType != LowLevelTypes.MachineType.X86))
  102. return false;
  103. return true;
  104. }
  105. }
  106. // If we can't read the process's PEB, we may still be able to get other kinds of information
  107. // from the process. This flag determines if we can get lesser information.
  108. private bool CanQueryProcessInformation {
  109. get {
  110. LowLevelTypes.ProcessAccessFlags required_flags =
  111. LowLevelTypes.ProcessAccessFlags.QUERY_LIMITED_INFORMATION
  112. | LowLevelTypes.ProcessAccessFlags.QUERY_INFORMATION;
  113. // In order to query the process, we need *either* of these flags.
  114. return (processHandleFlags & required_flags) != LowLevelTypes.ProcessAccessFlags.NONE;
  115. }
  116. }
  117. private string QueryProcessImageName(LowLevelTypes.ProcessQueryImageNameMode mode) {
  118. StringBuilder moduleBuffer = new StringBuilder(1024);
  119. int size = moduleBuffer.Capacity;
  120. NativeMethods.QueryFullProcessImageName(
  121. processHandle,
  122. mode,
  123. moduleBuffer,
  124. ref size);
  125. if (mode == LowLevelTypes.ProcessQueryImageNameMode.NATIVE_SYSTEM_FORMAT)
  126. moduleBuffer.Insert(0, "\\\\?\\GLOBALROOT");
  127. return moduleBuffer.ToString();
  128. }
  129. // Loads the top-level structure of the process's information block and caches it.
  130. private void CacheProcessInformation() {
  131. System.Diagnostics.Debug.Assert(CanReadPeb);
  132. // Fetch the process info and set the fields.
  133. LowLevelTypes.PROCESS_BASIC_INFORMATION temp = new LowLevelTypes.PROCESS_BASIC_INFORMATION();
  134. int size;
  135. LowLevelTypes.NTSTATUS status = NativeMethods.NtQueryInformationProcess(
  136. processHandle,
  137. LowLevelTypes.PROCESSINFOCLASS.PROCESS_BASIC_INFORMATION,
  138. ref temp,
  139. Utility.UnmanagedStructSize<LowLevelTypes.PROCESS_BASIC_INFORMATION>(),
  140. out size);
  141. if (status != LowLevelTypes.NTSTATUS.SUCCESS) {
  142. throw new Win32Exception();
  143. }
  144. cachedProcessBasicInfo = temp;
  145. }
  146. // Follows a pointer from the PROCESS_BASIC_INFORMATION structure in the target process's
  147. // address space to read the PEB.
  148. private void CachePeb() {
  149. System.Diagnostics.Debug.Assert(CanReadPeb);
  150. if (cachedPeb == null) {
  151. cachedPeb = Utility.ReadUnmanagedStructFromProcess<LowLevelTypes.PEB>(
  152. processHandle,
  153. cachedProcessBasicInfo.Value.PebBaseAddress);
  154. }
  155. }
  156. // Follows a pointer from the PEB structure in the target process's address space to read the
  157. // RTL_USER_PROCESS_PARAMETERS structure.
  158. private void CacheProcessParams() {
  159. System.Diagnostics.Debug.Assert(CanReadPeb);
  160. if (cachedProcessParams == null) {
  161. cachedProcessParams =
  162. Utility.ReadUnmanagedStructFromProcess<LowLevelTypes.RTL_USER_PROCESS_PARAMETERS>(
  163. processHandle, cachedPeb.Value.ProcessParameters);
  164. }
  165. }
  166. private void CacheCommandLine() {
  167. System.Diagnostics.Debug.Assert(CanReadPeb);
  168. if (cachedCommandLine == null) {
  169. cachedCommandLine = Utility.ReadStringUniFromProcess(
  170. processHandle,
  171. cachedProcessParams.Value.CommandLine.Buffer,
  172. cachedProcessParams.Value.CommandLine.Length / 2);
  173. }
  174. }
  175. private void CacheMachineType() {
  176. System.Diagnostics.Debug.Assert(CanQueryProcessInformation);
  177. // If our extension is running in a 32-bit process (which it is), then attempts to access
  178. // files in C:\windows\system (and a few other files) will redirect to C:\Windows\SysWOW64
  179. // and we will mistakenly think that the image file is a 32-bit image. The way around this
  180. // is to use a native system format path, of the form:
  181. // \\?\GLOBALROOT\Device\HarddiskVolume0\Windows\System\foo.dat
  182. // NativeProcessImagePath gives us the full process image path in the desired format.
  183. string path = NativeProcessImagePath;
  184. // Open the PE File as a binary file, and parse just enough information to determine the
  185. // machine type.
  186. //http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
  187. using (SafeFileHandle safeHandle = NativeMethods.CreateFile(
  188. path,
  189. LowLevelTypes.FileAccessFlags.GENERIC_READ,
  190. LowLevelTypes.FileShareFlags.SHARE_READ,
  191. IntPtr.Zero,
  192. LowLevelTypes.FileCreationDisposition.OPEN_EXISTING,
  193. LowLevelTypes.FileFlagsAndAttributes.NORMAL,
  194. IntPtr.Zero)) {
  195. FileStream fs = new FileStream(safeHandle, FileAccess.Read);
  196. using (BinaryReader br = new BinaryReader(fs)) {
  197. fs.Seek(0x3c, SeekOrigin.Begin);
  198. Int32 peOffset = br.ReadInt32();
  199. fs.Seek(peOffset, SeekOrigin.Begin);
  200. UInt32 peHead = br.ReadUInt32();
  201. if (peHead != 0x00004550) // "PE\0\0", little-endian
  202. throw new Exception("Can't find PE header");
  203. machineType = (LowLevelTypes.MachineType)br.ReadUInt16();
  204. machineTypeIsLoaded = true;
  205. }
  206. }
  207. }
  208. private void OpenAndCacheProcessHandle() {
  209. // Try to open a handle to the process with the highest level of privilege, but if we can't
  210. // do that then fallback to requesting access with a lower privilege level.
  211. processHandleFlags = LowLevelTypes.ProcessAccessFlags.QUERY_INFORMATION
  212. | LowLevelTypes.ProcessAccessFlags.VM_READ;
  213. processHandle = NativeMethods.OpenProcess(processHandleFlags, false, processId);
  214. if (processHandle == IntPtr.Zero) {
  215. processHandleFlags = LowLevelTypes.ProcessAccessFlags.QUERY_LIMITED_INFORMATION;
  216. processHandle = NativeMethods.OpenProcess(processHandleFlags, false, processId);
  217. if (processHandle == IntPtr.Zero) {
  218. processHandleFlags = LowLevelTypes.ProcessAccessFlags.NONE;
  219. throw new Win32Exception();
  220. }
  221. }
  222. }
  223. // An open handle to the process, along with the set of access flags that the handle was
  224. // open with.
  225. private int processId;
  226. private IntPtr processHandle;
  227. private LowLevelTypes.ProcessAccessFlags processHandleFlags;
  228. private string nativeProcessImagePath;
  229. private string win32ProcessImagePath;
  230. // The machine type is read by parsing the PE image file of the running process, so we cache
  231. // its value since the operation expensive.
  232. private bool machineTypeIsLoaded;
  233. private LowLevelTypes.MachineType machineType;
  234. // The following fields exist ultimately so that we can access the command line. However,
  235. // each field must be read separately through a pointer into another process's address
  236. // space so the access is expensive, hence we cache the values.
  237. private Nullable<LowLevelTypes.PROCESS_BASIC_INFORMATION> cachedProcessBasicInfo;
  238. private Nullable<LowLevelTypes.PEB> cachedPeb;
  239. private Nullable<LowLevelTypes.RTL_USER_PROCESS_PARAMETERS> cachedProcessParams;
  240. private string cachedCommandLine;
  241. ~ProcessDetail() {
  242. Dispose();
  243. }
  244. public void Dispose() {
  245. if (processHandle != IntPtr.Zero)
  246. NativeMethods.CloseHandle(processHandle);
  247. processHandle = IntPtr.Zero;
  248. }
  249. }
  250. }