private static ModuleInfo[] GetModuleInfos(int processId, bool firstModuleOnly) { Contract.Ensures(Contract.Result <ModuleInfo[]>().Length >= 1); // preserving Everett behavior. if (processId == SystemProcessID || processId == IdleProcessID) { // system process and idle process doesn't have any modules throw new Win32Exception(Interop.EFail, SR.EnumProcessModuleFailed); } SafeProcessHandle processHandle = SafeProcessHandle.InvalidHandle; try { processHandle = ProcessManager.OpenProcess(processId, Interop.PROCESS_QUERY_INFORMATION | Interop.PROCESS_VM_READ, true); IntPtr[] moduleHandles = new IntPtr[64]; GCHandle moduleHandlesArrayHandle = new GCHandle(); int moduleCount = 0; for (; ;) { bool enumResult = false; try { moduleHandlesArrayHandle = GCHandle.Alloc(moduleHandles, GCHandleType.Pinned); enumResult = Interop.mincore.EnumProcessModules(processHandle, moduleHandlesArrayHandle.AddrOfPinnedObject(), moduleHandles.Length * IntPtr.Size, ref moduleCount); // The API we need to use to enumerate process modules differs on two factors: // 1) If our process is running in WOW64. // 2) The bitness of the process we wish to introspect. // // If we are not running in WOW64 or we ARE in WOW64 but want to inspect a 32 bit process // we can call psapi!EnumProcessModules. // // If we are running in WOW64 and we want to inspect the modules of a 64 bit process then // psapi!EnumProcessModules will return false with ERROR_PARTIAL_COPY (299). In this case we can't // do the enumeration at all. So we'll detect this case and bail out. // // Also, EnumProcessModules is not a reliable method to get the modules for a process. // If OS loader is touching module information, this method might fail and copy part of the data. // This is no easy solution to this problem. The only reliable way to fix this is to // suspend all the threads in target process. Of course we don't want to do this in Process class. // So we just to try avoid the race by calling the same method 50 (an arbitary number) times. // if (!enumResult) { bool sourceProcessIsWow64 = false; bool targetProcessIsWow64 = false; SafeProcessHandle hCurProcess = SafeProcessHandle.InvalidHandle; try { hCurProcess = ProcessManager.OpenProcess(unchecked ((int)Interop.mincore.GetCurrentProcessId()), Interop.PROCESS_QUERY_INFORMATION, true); bool wow64Ret; wow64Ret = Interop.mincore.IsWow64Process(hCurProcess, ref sourceProcessIsWow64); if (!wow64Ret) { throw new Win32Exception(); } wow64Ret = Interop.mincore.IsWow64Process(processHandle, ref targetProcessIsWow64); if (!wow64Ret) { throw new Win32Exception(); } if (sourceProcessIsWow64 && !targetProcessIsWow64) { // Wow64 isn't going to allow this to happen, the best we can do is give a descriptive error to the user. throw new Win32Exception(Interop.ERROR_PARTIAL_COPY, SR.EnumProcessModuleFailedDueToWow); } } finally { if (hCurProcess != SafeProcessHandle.InvalidHandle) { hCurProcess.Dispose(); } } // If the failure wasn't due to Wow64, try again. for (int i = 0; i < 50; i++) { enumResult = Interop.mincore.EnumProcessModules(processHandle, moduleHandlesArrayHandle.AddrOfPinnedObject(), moduleHandles.Length * IntPtr.Size, ref moduleCount); if (enumResult) { break; } Thread.Sleep(1); } } } finally { moduleHandlesArrayHandle.Free(); } if (!enumResult) { throw new Win32Exception(); } moduleCount /= IntPtr.Size; if (moduleCount <= moduleHandles.Length) { break; } moduleHandles = new IntPtr[moduleHandles.Length * 2]; } List <ModuleInfo> moduleInfos = new List <ModuleInfo>(); int ret; for (int i = 0; i < moduleCount; i++) { try { ModuleInfo moduleInfo = new ModuleInfo(); IntPtr moduleHandle = moduleHandles[i]; Interop.NtModuleInfo ntModuleInfo = new Interop.NtModuleInfo(); if (!Interop.mincore.GetModuleInformation(processHandle, moduleHandle, ntModuleInfo, Marshal.SizeOf(ntModuleInfo))) { throw new Win32Exception(); } moduleInfo._sizeOfImage = ntModuleInfo.SizeOfImage; moduleInfo._entryPoint = ntModuleInfo.EntryPoint; moduleInfo._baseOfDll = ntModuleInfo.BaseOfDll; StringBuilder baseName = new StringBuilder(1024); ret = Interop.mincore.GetModuleBaseName(processHandle, moduleHandle, baseName, baseName.Capacity * 2); if (ret == 0) { throw new Win32Exception(); } moduleInfo._baseName = baseName.ToString(); StringBuilder fileName = new StringBuilder(1024); ret = Interop.mincore.GetModuleFileNameEx(processHandle, moduleHandle, fileName, fileName.Capacity * 2); if (ret == 0) { throw new Win32Exception(); } moduleInfo._fileName = fileName.ToString(); if (moduleInfo._fileName != null && moduleInfo._fileName.Length >= 4 && moduleInfo._fileName.StartsWith(@"\\?\", StringComparison.Ordinal)) { moduleInfo._fileName = moduleInfo._fileName.Substring(4); } moduleInfos.Add(moduleInfo); } catch (Win32Exception e) { if (e.NativeErrorCode == Interop.ERROR_INVALID_HANDLE || e.NativeErrorCode == Interop.ERROR_PARTIAL_COPY) { // It's possible that another thread casued this module to become // unloaded (e.g FreeLibrary was called on the module). Ignore it and // move on. } else { throw; } } // // If the user is only interested in the main module, break now. // This avoid some waste of time. In addition, if the application unloads a DLL // we will not get an exception. // if (firstModuleOnly) { break; } } ModuleInfo[] temp = new ModuleInfo[moduleInfos.Count]; moduleInfos.CopyTo(temp, 0); return(temp); } finally { #if FEATURE_TRACESWITCH Debug.WriteLineIf(Process._processTracing.TraceVerbose, "Process - CloseHandle(process)"); #endif if (!processHandle.IsInvalid) { processHandle.Dispose(); } } }
private static ModuleInfo[] GetModuleInfos(int processId, bool firstModuleOnly) { Contract.Ensures(Contract.Result<ModuleInfo[]>().Length >= 1); // preserving Everett behavior. if (processId == SystemProcessID || processId == IdleProcessID) { // system process and idle process doesn't have any modules throw new Win32Exception(Interop.EFail, SR.EnumProcessModuleFailed); } SafeProcessHandle processHandle = SafeProcessHandle.InvalidHandle; try { processHandle = ProcessManager.OpenProcess(processId, Interop.PROCESS_QUERY_INFORMATION | Interop.PROCESS_VM_READ, true); IntPtr[] moduleHandles = new IntPtr[64]; GCHandle moduleHandlesArrayHandle = new GCHandle(); int moduleCount = 0; for (; ; ) { bool enumResult = false; try { moduleHandlesArrayHandle = GCHandle.Alloc(moduleHandles, GCHandleType.Pinned); enumResult = Interop.mincore.EnumProcessModules(processHandle, moduleHandlesArrayHandle.AddrOfPinnedObject(), moduleHandles.Length * IntPtr.Size, ref moduleCount); // The API we need to use to enumerate process modules differs on two factors: // 1) If our process is running in WOW64. // 2) The bitness of the process we wish to introspect. // // If we are not running in WOW64 or we ARE in WOW64 but want to inspect a 32 bit process // we can call psapi!EnumProcessModules. // // If we are running in WOW64 and we want to inspect the modules of a 64 bit process then // psapi!EnumProcessModules will return false with ERROR_PARTIAL_COPY (299). In this case we can't // do the enumeration at all. So we'll detect this case and bail out. // // Also, EnumProcessModules is not a reliable method to get the modules for a process. // If OS loader is touching module information, this method might fail and copy part of the data. // This is no easy solution to this problem. The only reliable way to fix this is to // suspend all the threads in target process. Of course we don't want to do this in Process class. // So we just to try avoid the race by calling the same method 50 (an arbitary number) times. // if (!enumResult) { bool sourceProcessIsWow64 = false; bool targetProcessIsWow64 = false; SafeProcessHandle hCurProcess = SafeProcessHandle.InvalidHandle; try { hCurProcess = ProcessManager.OpenProcess(unchecked((int)Interop.mincore.GetCurrentProcessId()), Interop.PROCESS_QUERY_INFORMATION, true); bool wow64Ret; wow64Ret = Interop.mincore.IsWow64Process(hCurProcess, ref sourceProcessIsWow64); if (!wow64Ret) { throw new Win32Exception(); } wow64Ret = Interop.mincore.IsWow64Process(processHandle, ref targetProcessIsWow64); if (!wow64Ret) { throw new Win32Exception(); } if (sourceProcessIsWow64 && !targetProcessIsWow64) { // Wow64 isn't going to allow this to happen, the best we can do is give a descriptive error to the user. throw new Win32Exception(Interop.ERROR_PARTIAL_COPY, SR.EnumProcessModuleFailedDueToWow); } } finally { if (hCurProcess != SafeProcessHandle.InvalidHandle) { hCurProcess.Dispose(); } } // If the failure wasn't due to Wow64, try again. for (int i = 0; i < 50; i++) { enumResult = Interop.mincore.EnumProcessModules(processHandle, moduleHandlesArrayHandle.AddrOfPinnedObject(), moduleHandles.Length * IntPtr.Size, ref moduleCount); if (enumResult) { break; } Thread.Sleep(1); } } } finally { moduleHandlesArrayHandle.Free(); } if (!enumResult) { throw new Win32Exception(); } moduleCount /= IntPtr.Size; if (moduleCount <= moduleHandles.Length) break; moduleHandles = new IntPtr[moduleHandles.Length * 2]; } List<ModuleInfo> moduleInfos = new List<ModuleInfo>(); int ret; for (int i = 0; i < moduleCount; i++) { try { ModuleInfo moduleInfo = new ModuleInfo(); IntPtr moduleHandle = moduleHandles[i]; Interop.NtModuleInfo ntModuleInfo = new Interop.NtModuleInfo(); if (!Interop.mincore.GetModuleInformation(processHandle, moduleHandle, ntModuleInfo, Marshal.SizeOf(ntModuleInfo))) throw new Win32Exception(); moduleInfo.sizeOfImage = ntModuleInfo.SizeOfImage; moduleInfo.entryPoint = ntModuleInfo.EntryPoint; moduleInfo.baseOfDll = ntModuleInfo.BaseOfDll; StringBuilder baseName = new StringBuilder(1024); ret = Interop.mincore.GetModuleBaseName(processHandle, moduleHandle, baseName, baseName.Capacity * 2); if (ret == 0) throw new Win32Exception(); moduleInfo.baseName = baseName.ToString(); StringBuilder fileName = new StringBuilder(1024); ret = Interop.mincore.GetModuleFileNameEx(processHandle, moduleHandle, fileName, fileName.Capacity * 2); if (ret == 0) throw new Win32Exception(); moduleInfo.fileName = fileName.ToString(); if (moduleInfo.fileName != null && moduleInfo.fileName.Length >= 4 && moduleInfo.fileName.StartsWith(@"\\?\", StringComparison.Ordinal)) { moduleInfo.fileName = moduleInfo.fileName.Substring(4); } moduleInfos.Add(moduleInfo); } catch (Win32Exception e) { if (e.NativeErrorCode == Interop.ERROR_INVALID_HANDLE || e.NativeErrorCode == Interop.ERROR_PARTIAL_COPY) { // It's possible that another thread casued this module to become // unloaded (e.g FreeLibrary was called on the module). Ignore it and // move on. } else { throw; } } // // If the user is only interested in the main module, break now. // This avoid some waste of time. In addition, if the application unloads a DLL // we will not get an exception. // if (firstModuleOnly) { break; } } ModuleInfo[] temp = new ModuleInfo[moduleInfos.Count]; moduleInfos.CopyTo(temp, 0); return temp; } finally { #if FEATURE_TRACESWITCH Debug.WriteLineIf(Process.processTracing.TraceVerbose, "Process - CloseHandle(process)"); #endif if (!processHandle.IsInvalid) { processHandle.Dispose(); } } }