using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using ProcessNET.Extensions;
using ProcessNET.Memory;
using ProcessNET.Modules;
using ProcessNET.Native.Types;
using ProcessNET.Threads;
using ProcessNET.Utilities;
using ProcessNET.Windows;
namespace ProcessNET
{
///
/// A class that offsers several tools to interact with a process.
///
///
public class ProcessSharp : IProcess
{
///
/// Initializes a new instance of the class.
///
/// The native process.
/// The type of memory being manipulated.
public ProcessSharp(System.Diagnostics.Process native, MemoryType type)
{
native.EnableRaisingEvents = true;
native.Exited += (s, e) =>
{
ProcessExited?.Invoke(s, e);
HandleProcessExiting();
};
Native = native;
Handle = MemoryHelper.OpenProcess(ProcessAccessFlags.AllAccess, Native.Id);
switch (type)
{
case MemoryType.Local:
Memory = new LocalProcessMemory(Handle);
break;
case MemoryType.Remote:
Memory = new ExternalProcessMemory(Handle);
break;
default:
throw new ArgumentOutOfRangeException(nameof(type), type, null);
}
native.ErrorDataReceived += OutputDataReceived;
native.OutputDataReceived += OutputDataReceived;
ThreadFactory = new ThreadFactory(this);
ModuleFactory = new ModuleFactory(this);
MemoryFactory = new MemoryFactory(this);
WindowFactory = new WindowFactory(this);
}
///
/// Initializes a new instance of the class.
///
/// Name of the process.
/// The type of memory being manipulated.
public ProcessSharp(string processName, MemoryType type) : this(ProcessHelper.FromName(processName), type)
{
}
///
/// Initializes a new instance of the class.
///
/// The process id of the process to open with all rights.
/// The type of memory being manipulated.
public ProcessSharp(int processId, MemoryType type) : this(ProcessHelper.FromProcessId(processId), type)
{
}
///
/// Raises when the object is disposed.
///
public event EventHandler OnDispose;
///
/// Class for reading and writing memory.
///
public IMemory Memory { get; set; }
///
/// Provide access to the opened process.
///
public System.Diagnostics.Process Native { get; set; }
///
/// The process handle opened with all rights.
///
public SafeMemoryHandle Handle { get; set; }
///
/// Factory for manipulating threads.
///
public IThreadFactory ThreadFactory { get; set; }
///
/// Factory for manipulating modules and libraries.
///
public IModuleFactory ModuleFactory { get; set; }
///
/// Factory for manipulating memory space.
///
public IMemoryFactory MemoryFactory { get; set; }
///
/// Factory for manipulating windows.
///
public IWindowFactory WindowFactory { get; set; }
protected string ownerUser = null;
///
/// Gets the name of the user this process belongs to.
///
public string OwnerUser
{
get
{
if(string.IsNullOrWhiteSpace(ownerUser))
{
ownerUser = ProcessHelper.GetProcessUser(Native.Id);
}
return ownerUser;
}
}
///
/// Gets the with the specified module name.
///
/// Name of the module.
/// IProcessModule.
public IProcessModule this[string moduleName] => ModuleFactory[moduleName];
///
/// Gets the with the specified address.
///
/// The address the pointer is located at in memory.
/// IPointer.
public IPointer this[IntPtr intPtr] => new MemoryPointer(this, intPtr);
protected bool IsDisposed { get; set; }
protected bool MustBeDisposed { get; set; } = true;
///
/// Releases unmanaged and - optionally - managed resources.
///
public virtual void Dispose()
{
//TODO Consider adding a null check here, or a nasty crash deadlock can make it in here occasionally.
//TODO followup: did the invoke threadsafety characterstics fix the deadlock?
if (!IsDisposed)
{
IsDisposed = true;
OnDispose?.Invoke(this, EventArgs.Empty);
ThreadFactory?.Dispose();
ModuleFactory?.Dispose();
MemoryFactory?.Dispose();
WindowFactory?.Dispose();
Handle?.Close();
GC.SuppressFinalize(this);
}
}
///
/// Handles the process exiting.
///
/// Created 2012-02-15
protected virtual void HandleProcessExiting()
{
}
#region Pointers
///
/// Calculates the final address a pointer is pointing at in a module, knowing it's base address and offsets.
///
/// The module name to calculate the pointer at.
/// The base address of the pointer.
/// The offsets of the pointer.
/// The final address the pointer is pointing at.
public IntPtr GetPointerAddress(string moduleName, IntPtr baseAddress, params int[] offsets)
{
if (string.IsNullOrWhiteSpace(moduleName))
{
throw new ArgumentException($"'{nameof(moduleName)}' cannot be null or whitespace.", nameof(moduleName));
}
return GetPointerAddress(this[moduleName], baseAddress, offsets);
}
///
/// Calculates the final address a pointer is pointing at in a module, knowing it's base address and offsets.
///
/// The module to calculate the pointer at.
/// The base address of the pointer.
/// The offsets of the pointer.
/// The final address the pointer is pointing at.
public IntPtr GetPointerAddress(IProcessModule module, IntPtr baseAddress, params int[] offsets)
{
if (module is null)
{
throw new ArgumentNullException(nameof(module));
}
IntPtr finalBaseAddress;
if (Memory.Is32Bit)
finalBaseAddress = module.BaseAddress + baseAddress.ToInt32();
else
finalBaseAddress = new IntPtr(module.BaseAddress.ToInt64() + baseAddress.ToInt64());
return GetPointerAddress(finalBaseAddress, offsets);
}
///
/// Calculates the final address a pointer is pointing at, knowing it's base address and offsets.
///
/// The base address of the pointer.
/// The offsets of the pointer.
/// The final address the pointer is pointing at.
public IntPtr GetPointerAddress(IntPtr baseAddress, params int[] offsets)
{
if (baseAddress.MayBeValid() == false)
return IntPtr.Zero;
IntPtr address = baseAddress;
if (offsets != null && offsets.Length > 0)
{
for(int i = 0; i < offsets.Length; i++)
{
try
{
address = Memory.Read(address + offsets[i]);
}
catch(Exception ex)
{
return IntPtr.Zero;
}
}
}
return address;
}
#endregion
///
/// Event queue for all listeners interested in ProcessExited events.
///
public event EventHandler ProcessExited;
private static void OutputDataReceived(object sender, DataReceivedEventArgs e)
{
Trace.WriteLine(e.Data);
}
~ProcessSharp()
{
if (MustBeDisposed)
{
Dispose();
}
}
}
}