15

In Process Explorer I can view all the dlls (and dll details) loaded by a process selected. How can do this programmatically?

I can get a specific process details like this. But unsure where to go from here?

Process[] processlist = Process.GetProcesses();

foreach(Process theprocess in processlist){
Console.WriteLine(“Process: {0} ID: {1}”, theprocess.ProcessName, theprocess.Id);
}

enter image description here

nlstack01
  • 789
  • 2
  • 7
  • 30

5 Answers5

17

There exists the Process.Modules property which you can enumerate all Modules (exe and .dll's) loaded by the process.

foreach (ProcessModule module in proc.Modules)
{
   Console.WriteLine(string.Format("Module: {0}", module.FileName));
}

Per the ProcessModule class which gives you the properties of a specific module.

BullyWiiPlaza
  • 17,329
  • 10
  • 113
  • 185
Tony The Lion
  • 61,704
  • 67
  • 242
  • 415
  • I couldn't get `module` to be deducted to be a `ProcessModule` object, so I needed to specify it instead of using `var`. – Yves Calaci Mar 04 '21 at 20:46
16

The Process.Modules solution is NOT sufficient when running a 64-bit program and trying to collect all modules from a 32-bit process. By default, a 64-bit program will only work on a 64-bit process, and a 32-bit program will only work on a 32-bit processes.

For a solution that works for "AnyCPU", "x86", and "x64", see below. Simply call the CollectModules function with the target process. Note: A 32-bit program cannot collect modules from a 64-bit process.

public List<Module> CollectModules(Process process)
{
        List<Module> collectedModules = new List<Module>();

        IntPtr[] modulePointers = new IntPtr[0];
        int bytesNeeded = 0;

        // Determine number of modules
        if (!Native.EnumProcessModulesEx(process.Handle, modulePointers, 0, out bytesNeeded, (uint)Native.ModuleFilter.ListModulesAll))
        {
            return collectedModules;
        }

        int totalNumberofModules = bytesNeeded / IntPtr.Size;
        modulePointers = new IntPtr[totalNumberofModules];

        // Collect modules from the process
        if (Native.EnumProcessModulesEx(process.Handle, modulePointers, bytesNeeded, out bytesNeeded, (uint)Native.ModuleFilter.ListModulesAll))
        {
            for (int index = 0; index < totalNumberofModules; index++)
            {
                StringBuilder moduleFilePath = new StringBuilder(1024);
                Native.GetModuleFileNameEx(process.Handle, modulePointers[index], moduleFilePath, (uint)(moduleFilePath.Capacity));

                string moduleName = Path.GetFileName(moduleFilePath.ToString());
                Native.ModuleInformation moduleInformation = new Native.ModuleInformation();
                Native.GetModuleInformation(process.Handle, modulePointers[index], out moduleInformation, (uint)(IntPtr.Size * (modulePointers.Length)));

                // Convert to a normalized module and add it to our list
                Module module = new Module(moduleName, moduleInformation.lpBaseOfDll, moduleInformation.SizeOfImage);
                collectedModules.Add(module);
            }
        }

        return collectedModules;
    }
}

public class Native
{
    [StructLayout(LayoutKind.Sequential)]
    public struct ModuleInformation
    {
        public IntPtr lpBaseOfDll;
        public uint SizeOfImage;
        public IntPtr EntryPoint;
    }

    internal enum ModuleFilter
    {
        ListModulesDefault = 0x0,
        ListModules32Bit = 0x01,
        ListModules64Bit = 0x02,
        ListModulesAll = 0x03,
    }

    [DllImport("psapi.dll")]
    public static extern bool EnumProcessModulesEx(IntPtr hProcess, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U4)] [In][Out] IntPtr[] lphModule, int cb, [MarshalAs(UnmanagedType.U4)] out int lpcbNeeded, uint dwFilterFlag);

    [DllImport("psapi.dll")]
    public static extern uint GetModuleFileNameEx(IntPtr hProcess, IntPtr hModule, [Out] StringBuilder lpBaseName, [In] [MarshalAs(UnmanagedType.U4)] uint nSize);

    [DllImport("psapi.dll", SetLastError = true)]
    public static extern bool GetModuleInformation(IntPtr hProcess, IntPtr hModule, out ModuleInformation lpmodinfo, uint cb);
}

public class Module
{
    public Module(string moduleName, IntPtr baseAddress, uint size)
    {
        this.ModuleName = moduleName;
        this.BaseAddress = baseAddress;
        this.Size = size;
    }

    public string ModuleName { get; set; }
    public IntPtr BaseAddress { get; set; }
    public uint Size { get; set; }
}
Zachary Canann
  • 1,131
  • 2
  • 13
  • 23
  • 1
    This answer is only partially correct. Your code lists only native DLLs which have been loaded by LoadLibrary(). But .NET framework DLLs like System.Windows.Forms.dll are not listed on Framework 4.7 because the framework does not use LoadLibrary() to load them. See http://blogs.microsoft.co.il/sasha/2011/01/16/clr-4-does-not-use-loadlibrary-to-load-assemblies – Elmue Feb 15 '18 at 17:53
  • Is there a proposed solution to this? What methods exist for finding DLLs loaded outside of LoadLibrary – Zachary Canann May 17 '18 at 17:59
  • I think there is no solution because you can call Assembly.Load(Byte[]) which loads an Assembly into a process from a Byte[] array. This means that .NET applications can even load an assembly from internet directly into the process and you will never see this Assembly in the DLL's list. – Elmue May 17 '18 at 21:39
  • Hmm. I guess if one absolutely needed to find DLLs loaded this way, one could search memory for PE headers, or perhaps pages with the Execute flag set (although this is not very good on it's own) – Zachary Canann May 18 '18 at 01:50
  • But even if you find PE headers in memory you still don't know which was the original file path. You would have to hook CreateFile() API. But this is too complicated. – Elmue May 18 '18 at 15:21
4

This depends on what you want precisely.

Getting the list of .NET assemblies loaded into a specific app domain is easy (AppDomain.GetAssemblies).

But listing the App Domains in a process isn't so easy but can be done.

However if you want a list of dll's – native and .NET – as Tony the Lion answers, is just Process.Modules.

Community
  • 1
  • 1
Richard
  • 106,783
  • 21
  • 203
  • 265
4

After CLR v4, the accepted answer will show only unmanaged assemblies. If you want to get managed assemblies of external process you can refer to: Microsoft.Diagnostics.Runtime

Using the following code you can iterate app domains of external process and get loaded assemblies:

using var dt = DataTarget.AttachToProcess(clientProcess.Id, false);
assemblies = dt
      .ClrVersions
      .Select(dtClrVersion => dtClrVersion.CreateRuntime())
      .SelectMany(runtime => runtime.AppDomains.SelectMany(runtimeAppDomain => runtimeAppDomain.Modules))
      .Select(clrModule => clrModule.AssemblyName)
      .Distinct()
      .ToList();

Refer to their github for more details.

Roman Badiornyi
  • 1,509
  • 14
  • 28
  • I tested all solutions proposed in answers, but only this is working for me. It should be set as answer. Only this solution returns all dlls used by selected process – Paweł Iwaneczko Feb 21 '23 at 13:40
0

You can use ListDLLs app to view all the dlls for the given process, These are CLI commands

URL: https://learn.microsoft.com/en-us/sysinternals/downloads/listdlls

For example: listdlls -v outlook --> Will show all the DLL's running within outlook