diff --git a/Process.NET/Applied/AppliedManager.cs b/Process.NET/Applied/AppliedManager.cs index 009ce8b..47932d7 100644 --- a/Process.NET/Applied/AppliedManager.cs +++ b/Process.NET/Applied/AppliedManager.cs @@ -2,8 +2,9 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -namespace Process.NET.Applied +namespace ProcessNET.Applied { + /// [SuppressMessage("ReSharper", "LoopCanBePartlyConvertedToQuery")] public class AppliedManager : IAppliedManager where T : IApplied { diff --git a/Process.NET/Applied/AppliedUtilities.cs b/Process.NET/Applied/AppliedUtilities.cs new file mode 100644 index 0000000..87d02b3 --- /dev/null +++ b/Process.NET/Applied/AppliedUtilities.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +#if X64 +using ADDR = System.UInt64; +#else +using ADDR = System.UInt32; +#endif + +namespace ProcessNET.Applied +{ + public class AppliedUtilities + { + #region Delegate/Function Registration + + /// + /// Registers a function into a delegate. Note: The delegate must provide a proper function signature! + /// + /// + /// + /// + public static T RegisterDelegate(ADDR address) where T : class + { + return RegisterDelegate((IntPtr)address); + } + + /// + /// Registers a function into a delegate. Note: The delegate must provide a proper function signature! + /// + /// + /// + /// + public static T RegisterDelegate(IntPtr address) where T : class + { +#if !NOEXCEPTIONS + // Make sure delegates are attributed properly! + if (!HasUFPAttribute(typeof(T))) + { + throw new ArgumentException("The delegate does not have proper attributes! It must be adorned with" + + " the System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute, with proper calling conventions" + + " to work properly!"); + } +#endif + return Marshal.GetDelegateForFunctionPointer(address); + } + + #endregion + + internal static bool HasAttrib(Type item) + { + return item.GetCustomAttributes(typeof(T), true).Length != 0; + } + + internal static bool HasUFPAttribute(Delegate d) + { + return HasUFPAttribute(d.GetType()); + } + + internal static bool HasUFPAttribute(Type t) + { + return HasAttrib(t); + } + } +} diff --git a/Process.NET/Applied/ComplexAppliedManager.cs b/Process.NET/Applied/ComplexAppliedManager.cs index d83c5de..ca15851 100644 --- a/Process.NET/Applied/ComplexAppliedManager.cs +++ b/Process.NET/Applied/ComplexAppliedManager.cs @@ -1,4 +1,4 @@ -namespace Process.NET.Applied +namespace ProcessNET.Applied { public class ComplexAppliedManager : AppliedManager, IComplexAppliedManager where T : IComplexApplied { diff --git a/Process.NET/Applied/Detours/Detour.cs b/Process.NET/Applied/Detours/Detour.cs index dea5b45..142fca6 100644 --- a/Process.NET/Applied/Detours/Detour.cs +++ b/Process.NET/Applied/Detours/Detour.cs @@ -2,12 +2,18 @@ using System.Collections.Generic; using System.Runtime.InteropServices; -using Process.NET.Extensions; -using Process.NET.Memory; -using Process.NET.Utilities; +using ProcessNET.Extensions; +using ProcessNET.Memory; -namespace Process.NET.Applied.Detours +namespace ProcessNET.Applied.Detours { + public enum DetourCreateFlags + { + None, + IgnoreRules, + FastCall, + _x64, + } /// /// A manager class to handle function detours, and hooks. /// All credits to the GreyMagic library written by Apoc @ www.ownedcore.com @@ -19,112 +25,9 @@ public class Detour : IComplexApplied /// to keep a reference, to avoid the GC from collecting the delegate instance! /// // ReSharper disable once NotAccessedField.Local - private readonly Delegate _hookDelegate; - - public Detour(Delegate target, Delegate hook, string identifier, IMemory memory, - bool ignoreRules = false, bool fastCall = true, bool x64 = true) - { - ProcessMemory = memory; - Identifier = identifier; - IgnoreRules = ignoreRules; - - TargetDelegate = target; - Target = target.ToFunctionPtr(); - - _hookDelegate = hook; - HookPointer = hook.ToFunctionPtr(); //target - - //Store the original bytes - Original = new List(); - - //Setup the detour bytes - New = new List { 0x48, 0xb8 }; // movabs rax - var bytes = BitConverter.GetBytes(HookPointer.ToInt64()); // hookptr - New.AddRange(bytes); - New.AddRange(new byte[] { 0xff, 0xe0 }); // jmp rax - - Original.AddRange(memory.Read(Target, New.Count)); - } - - public Detour(Delegate target, Delegate hook, string identifier, IMemory memory, - bool ignoreRules = false, bool fastCall = true) - { - ProcessMemory = memory; - Identifier = identifier; - IgnoreRules = ignoreRules; - - TargetDelegate = target; - Target = target.ToFunctionPtr(); - - _hookDelegate = hook; - HookPointer = hook.ToFunctionPtr(); //target - - //Store the orginal bytes - Original = new List(); - Original.AddRange(memory.Read(Target, 6)); + private Delegate _hookDelegate; - //here the mess starts ... - //----------------------- - paramCount = target.Method.GetParameters().Length; - - //preparing the stack from fastcall to stdcall - first = new List(); - - first.Add(0x58); // pop eax - store the ret addr - - if (paramCount > 1) - first.Add(0x52); // push edx - - if (paramCount > 0) - first.Add(0x51); // push ecx - - first.Add(0x50); // push eax - retrieve ret addr - - //jump to the hook - first.Add(0x68); // push HookPointer - - var bytes = BitConverter.GetBytes(HookPointer.ToInt32()); - - first.AddRange(bytes); - first.Add(0xC3); // ret - jump to the detour handler - - firstPtr = Marshal.AllocHGlobal(first.Count); - //ProcessMemory.Write(firstPtr, first.ToArray()); - - //Setup the detour bytes - New = new List { 0x68 }; //push firstPtr - var bytes2 = IntPtr.Size == 4 ? BitConverter.GetBytes(firstPtr.ToInt32()) : - BitConverter.GetBytes(firstPtr.ToInt64()); - New.AddRange(bytes2); - New.Add(0xC3); //ret - jump to the first - - //preparing ecx, edx and the stack from stdcall to fastcall - last = new List(); - last.Add(0x58); // pop eax - store the ret addr - - if (paramCount > 0) - last.Add(0x59); // pop ecx - - if (paramCount > 1) - last.Add(0x5A); // pop edx - - last.Add(0x50); // push eax - retrieve ret addr - - //jump to the original function - last.Add(0x68); // push Target - - var bytes3 = BitConverter.GetBytes(HookPointer.ToInt32()); - - last.AddRange(bytes3); - last.Add(0xC3); // ret - - lastPtr = Marshal.AllocHGlobal(last.Count); - - //ProcessMemory.Write(lastPtr, last.ToArray()); - - //create the func called after the hook - lastDelegate = Marshal.GetDelegateForFunctionPointer(lastPtr, TargetDelegate.GetType()); - } + /// /// Initializes a new instance of the class. @@ -134,73 +37,52 @@ public Detour(Delegate target, Delegate hook, string identifier, IMemory memory, /// /// The instance. /// - public Detour(Delegate target, Delegate hook, string identifier, IMemory memory, - bool ignoreRules = false) + public Detour(Delegate target, Delegate hook, string identifier, IMemory memory, DetourCreateFlags flags) { - ProcessMemory = memory; - Identifier = identifier; - IgnoreRules = ignoreRules; - - TargetDelegate = target; - Target = target.ToFunctionPtr(); - - _hookDelegate = hook; - HookPointer = hook.ToFunctionPtr(); //target - - //Store the original bytes - Original = new List(); - Original.AddRange(memory.Read(Target, 6)); - - //Setup the detour bytes - New = new List { 0x68 }; - - var bytes = BitConverter.GetBytes(HookPointer.ToInt32()); - - New.AddRange(bytes); - New.Add(0xC3); + Construct(target, hook, identifier, memory, flags); } /// /// The reference of the object. /// - private IMemory ProcessMemory { get; } + private IMemory ProcessMemory { get; set; } /// /// Gets the pointer to be hooked/being hooked. /// /// The pointer to be hooked/being hooked. - public IntPtr HookPointer { get; } + public IntPtr HookPointer { get; protected set; } /// /// Gets the new bytes. /// /// The new bytes. - public List New { get; } + public List New { get; protected set; } /// /// Gets the original bytes. /// /// The original bytes. - public List Original { get; } + public List Original { get; protected set; } /// /// Gets the pointer of the target function. /// /// The pointer of the target function. - public IntPtr Target { get; } + public IntPtr Target { get; protected set; } /// /// Gets the targeted delegate instance. /// /// The targeted delegate instance. - public Delegate TargetDelegate { get; } + public Delegate TargetDelegate { get; protected set; } - public int paramCount { get; } - public List first { get; } - public IntPtr firstPtr { get; } - public List last { get; } - public IntPtr lastPtr { get; } - public Delegate lastDelegate { get; } + public int paramCount { get; protected set; } + public List first { get; protected set; } + public IntPtr firstPtr { get; protected set; } + public List last { get; protected set; } + public IntPtr lastPtr { get; protected set; } + public Delegate lastDelegate { get; protected set; } /// /// Get a value indicating if the detour has been disabled due to a running AntiCheat scan @@ -210,7 +92,7 @@ public Detour(Delegate target, Delegate hook, string identifier, IMemory memory, /// /// Geta s value indicating if the detour should never be disabled by the AntiCheat scan logic /// - public bool IgnoreRules { get; } + public bool IgnoreRules { get; protected set; } /// /// States if the detour is currently enabled. @@ -221,7 +103,7 @@ public Detour(Delegate target, Delegate hook, string identifier, IMemory memory, /// The name of the detour. /// /// The name of the detour. - public string Identifier { get; } + public string Identifier { get; protected set; } /// /// Gets a value indicating whether the is disposed. @@ -234,6 +116,117 @@ public Detour(Delegate target, Delegate hook, string identifier, IMemory memory, /// public bool MustBeDisposed { get; set; } = true; + protected void Construct(Delegate target, Delegate hook, string identifier, IMemory memory, DetourCreateFlags flags) + { + ProcessMemory = memory; + Identifier = identifier; + IgnoreRules = flags.HasFlag(DetourCreateFlags.IgnoreRules); + + TargetDelegate = target; + Target = target.ToFunctionPtr(); + + _hookDelegate = hook; + HookPointer = hook.ToFunctionPtr(); //target + + if (flags.HasFlag(DetourCreateFlags.FastCall)) + { + if (flags.HasFlag(DetourCreateFlags._x64)) + { + //Store the original bytes + Original = new List(); + + //Setup the detour bytes + New = new List { 0x48, 0xb8 }; // movabs rax + var bytes = BitConverter.GetBytes(HookPointer.ToInt64()); // hookptr + New.AddRange(bytes); + New.AddRange(new byte[] { 0xff, 0xe0 }); // jmp rax + + Original.AddRange(memory.Read(Target, New.Count)); + } + else + { + //Store the orginal bytes + Original = new List(); + Original.AddRange(memory.Read(Target, 6)); + + //here the mess starts ... + //----------------------- + paramCount = target.Method.GetParameters().Length; + + //preparing the stack from fastcall to stdcall + first = new List(); + + first.Add(0x58); // pop eax - store the ret addr + + if (paramCount > 1) + first.Add(0x52); // push edx + + if (paramCount > 0) + first.Add(0x51); // push ecx + + first.Add(0x50); // push eax - retrieve ret addr + + //jump to the hook + first.Add(0x68); // push HookPointer + + var bytes = BitConverter.GetBytes(HookPointer.ToInt32()); + + first.AddRange(bytes); + first.Add(0xC3); // ret - jump to the detour handler + + firstPtr = Marshal.AllocHGlobal(first.Count); + //ProcessMemory.Write(firstPtr, first.ToArray()); + + //Setup the detour bytes + New = new List { 0x68 }; //push firstPtr + var bytes2 = memory.Is32Bit ? BitConverter.GetBytes(firstPtr.ToInt32()) : + BitConverter.GetBytes(firstPtr.ToInt64()); + New.AddRange(bytes2); + New.Add(0xC3); //ret - jump to the first + + //preparing ecx, edx and the stack from stdcall to fastcall + last = new List(); + last.Add(0x58); // pop eax - store the ret addr + + if (paramCount > 0) + last.Add(0x59); // pop ecx + + if (paramCount > 1) + last.Add(0x5A); // pop edx + + last.Add(0x50); // push eax - retrieve ret addr + + //jump to the original function + last.Add(0x68); // push Target + + var bytes3 = BitConverter.GetBytes(HookPointer.ToInt32()); + + last.AddRange(bytes3); + last.Add(0xC3); // ret + + lastPtr = Marshal.AllocHGlobal(last.Count); + + //ProcessMemory.Write(lastPtr, last.ToArray()); + + //create the func called after the hook + lastDelegate = Marshal.GetDelegateForFunctionPointer(lastPtr, TargetDelegate.GetType()); + } + } + else + { + //Store the original bytes + Original = new List(); + Original.AddRange(memory.Read(Target, 6)); + + //Setup the detour bytes + New = new List { 0x68 }; + + var bytes = BitConverter.GetBytes(HookPointer.ToInt32()); + + New.AddRange(bytes); + New.Add(0xC3); + } + } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. In this /// case, it will disable the instance and suppress the finalizer. diff --git a/Process.NET/Applied/Detours/DetourManager.cs b/Process.NET/Applied/Detours/DetourManager.cs index d73fc05..771691d 100644 --- a/Process.NET/Applied/Detours/DetourManager.cs +++ b/Process.NET/Applied/Detours/DetourManager.cs @@ -1,9 +1,9 @@ using System; -using Process.NET.Extensions; -using Process.NET.Memory; -using Process.NET.Utilities; +using ProcessNET.Extensions; +using ProcessNET.Memory; +using ProcessNET.Utilities; -namespace Process.NET.Applied.Detours +namespace ProcessNET.Applied.Detours { /// /// A manager class to handle function detours, and hooks. @@ -62,12 +62,16 @@ public Detour Create(Delegate target, Delegate newTarget, string name, bool igno if (InternalItems.ContainsKey(name)) throw new ArgumentException($"The {name} detour already exists!", nameof(name)); - if (!fastCall) - InternalItems[name] = new Detour(target, newTarget, name, ProcessPlus, ignoreAntiCheatRules); - if (fastCall && !x64) - InternalItems[name] = new Detour(target, newTarget, name, ProcessPlus, ignoreAntiCheatRules, fastCall); - if (fastCall && x64) - InternalItems[name] = new Detour(target, newTarget, name, ProcessPlus, ignoreAntiCheatRules, fastCall, x64); + DetourCreateFlags flags = DetourCreateFlags.None; + if (ignoreAntiCheatRules) + flags |= DetourCreateFlags.IgnoreRules; + if (fastCall) + flags |= DetourCreateFlags.FastCall; + if (x64) + flags |= DetourCreateFlags._x64; + + InternalItems[name] = new Detour(target, newTarget, name, ProcessPlus, flags); + return InternalItems[name]; } diff --git a/Process.NET/Applied/IApplied.cs b/Process.NET/Applied/IApplied.cs index 2410890..6187872 100644 --- a/Process.NET/Applied/IApplied.cs +++ b/Process.NET/Applied/IApplied.cs @@ -1,6 +1,6 @@ -using Process.NET.Marshaling; +using ProcessNET.Marshaling; -namespace Process.NET.Applied +namespace ProcessNET.Applied { public interface IApplied : IDisposableState { diff --git a/Process.NET/Applied/IAppliedManager.cs b/Process.NET/Applied/IAppliedManager.cs index afc972e..5656994 100644 --- a/Process.NET/Applied/IAppliedManager.cs +++ b/Process.NET/Applied/IAppliedManager.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Process.NET.Applied +namespace ProcessNET.Applied { public interface IAppliedManager where T : IApplied { diff --git a/Process.NET/Applied/IComplexApplied.cs b/Process.NET/Applied/IComplexApplied.cs index 370b39e..bacbeab 100644 --- a/Process.NET/Applied/IComplexApplied.cs +++ b/Process.NET/Applied/IComplexApplied.cs @@ -1,4 +1,4 @@ -namespace Process.NET.Applied +namespace ProcessNET.Applied { public interface IComplexApplied : IApplied { diff --git a/Process.NET/Applied/IComplexAppliedManager.cs b/Process.NET/Applied/IComplexAppliedManager.cs index 77952ce..01e2fd7 100644 --- a/Process.NET/Applied/IComplexAppliedManager.cs +++ b/Process.NET/Applied/IComplexAppliedManager.cs @@ -1,4 +1,4 @@ -namespace Process.NET.Applied +namespace ProcessNET.Applied { public interface IComplexAppliedManager : IAppliedManager where T : IApplied { diff --git a/Process.NET/Applied/Patches/Patch.cs b/Process.NET/Applied/Patches/Patch.cs index b026863..83d47bc 100644 --- a/Process.NET/Applied/Patches/Patch.cs +++ b/Process.NET/Applied/Patches/Patch.cs @@ -1,8 +1,8 @@ using System; using System.Linq; -using Process.NET.Memory; +using ProcessNET.Memory; -namespace Process.NET.Applied.Patches +namespace ProcessNET.Applied.Patches { /// /// Class representing a Memory Patch object. diff --git a/Process.NET/Applied/Patches/PatchManager.cs b/Process.NET/Applied/Patches/PatchManager.cs index d94b037..f0e2aba 100644 --- a/Process.NET/Applied/Patches/PatchManager.cs +++ b/Process.NET/Applied/Patches/PatchManager.cs @@ -1,7 +1,7 @@ using System; -using Process.NET.Memory; +using ProcessNET.Memory; -namespace Process.NET.Applied.Patches +namespace ProcessNET.Applied.Patches { /// /// A manager class to handle memory patches. diff --git a/Process.NET/Assembly/Assemblers/Fasm32Assembler.cs b/Process.NET/Assembly/Assemblers/Fasm32Assembler.cs new file mode 100644 index 0000000..af51072 --- /dev/null +++ b/Process.NET/Assembly/Assemblers/Fasm32Assembler.cs @@ -0,0 +1,37 @@ +using Binarysharp.Assemblers.Fasm; +using System; + +namespace ProcessNET.Assembly.Assemblers +{ + /// + /// Implement Fasm.NET compiler for 32-bit development. + /// More info: https://github.com/ZenLulz/Fasm.NET + /// + public class Fasm32Assembler : IAssembler + { + /// + /// Assemble the specified assembly code. + /// + /// The assembly code. + /// An array of bytes containing the assembly code. + public byte[] Assemble(string asm) + { + // Assemble and return the code + return Assemble(asm, IntPtr.Zero); + } + + /// + /// Assemble the specified assembly code at a base address. + /// + /// The assembly code. + /// The address where the code is rebased. + /// An array of bytes containing the assembly code. + public byte[] Assemble(string asm, IntPtr baseAddress) + { + // Rebase the code + asm = String.Format("use32\norg 0x{0:X8}\n", baseAddress.ToInt64()) + asm; + // Assemble and return the code + return FasmNet.Assemble(asm); + } + } +} \ No newline at end of file diff --git a/Process.NET/Assembly/Assemblers/IAssembler.cs b/Process.NET/Assembly/Assemblers/IAssembler.cs index 0ee1758..c3ccb59 100644 --- a/Process.NET/Assembly/Assemblers/IAssembler.cs +++ b/Process.NET/Assembly/Assemblers/IAssembler.cs @@ -1,6 +1,6 @@ using System; -namespace Process.NET.Assembly.Assemblers +namespace ProcessNET.Assembly.Assemblers { /// /// Interface defining an assembler. diff --git a/Process.NET/Assembly/AssemblyFactory.cs b/Process.NET/Assembly/AssemblyFactory.cs index 02c0220..1189f5f 100644 --- a/Process.NET/Assembly/AssemblyFactory.cs +++ b/Process.NET/Assembly/AssemblyFactory.cs @@ -1,15 +1,15 @@ using System; using System.Linq; using System.Threading.Tasks; -using Process.NET.Assembly.Assemblers; -using Process.NET.Assembly.CallingConventions; -using Process.NET.Marshaling; -using Process.NET.Memory; -using Process.NET.Native.Types; -using Process.NET.Threads; -using Process.NET.Utilities; - -namespace Process.NET.Assembly +using ProcessNET.Assembly.Assemblers; +using ProcessNET.Assembly.CallingConventions; +using ProcessNET.Marshaling; +using ProcessNET.Memory; +using ProcessNET.Native.Types; +using ProcessNET.Threads; +using ProcessNET.Utilities; + +namespace ProcessNET.Assembly { public class AssemblyFactory : IAssemblyFactory { @@ -24,6 +24,17 @@ public AssemblyFactory(IProcess process, IAssembler assembler) Assembler = assembler; } + /// + /// Initializes a new instance of the class. + /// With the default assembler + /// + /// The process. + public AssemblyFactory(IProcess process) + : this(process, new Fasm32Assembler()) + { + + } + /// /// Gets or sets the assembler. /// @@ -264,9 +275,19 @@ public Task ExecuteAsync(IntPtr address, Native.Types.CallingConventions /// /// The mnemonics to inject. /// The address where the assembly code is injected. - public void Inject(string asm, IntPtr address) + public bool Inject(string asm, IntPtr address) { - Process.Memory.Write(address, Assembler.Assemble(asm, address)); + byte[] bytes = Assembler.Assemble(asm, address); + + try + { + int injectedCount = Process.Memory.Write(address, bytes); + return injectedCount == bytes.Length; + } + catch + { + return false; + } } /// @@ -274,9 +295,9 @@ public void Inject(string asm, IntPtr address) /// /// An array containing the mnemonics to inject. /// The address where the assembly code is injected. - public void Inject(string[] asm, IntPtr address) + public bool Inject(string[] asm, IntPtr address) { - Inject(string.Join("\n", asm), address); + return Inject(string.Join("\n", asm), address); } /// @@ -315,7 +336,9 @@ public IAllocatedMemory Inject(string[] asm) public T InjectAndExecute(string asm, IntPtr address) { // Inject the assembly code - Inject(asm, address); + bool injected = Inject(asm, address); + if (!injected) + return default; // Execute the code return Execute(address); } diff --git a/Process.NET/Assembly/AssemblyTransaction.cs b/Process.NET/Assembly/AssemblyTransaction.cs index 0adc34f..5302c14 100644 --- a/Process.NET/Assembly/AssemblyTransaction.cs +++ b/Process.NET/Assembly/AssemblyTransaction.cs @@ -1,8 +1,8 @@ using System; using System.Text; -using Process.NET.Marshaling; +using ProcessNET.Marshaling; -namespace Process.NET.Assembly +namespace ProcessNET.Assembly { /// /// Class representing a transaction where the user can insert mnemonics. @@ -110,12 +110,14 @@ public virtual void Dispose() { // If a pointer was specified if (Address != IntPtr.Zero) + { // If the assembly code must be executed if (IsAutoExecuted) ExitCode = _assemblyFactory.InjectAndExecute(Mnemonics.ToString(), Address); // Else the assembly code is just injected else _assemblyFactory.Inject(Mnemonics.ToString(), Address); + } // If no pointer was specified and the code assembly code must be executed if (Address == IntPtr.Zero && IsAutoExecuted) diff --git a/Process.NET/Assembly/CallingConventions/CallingConventionSelector.cs b/Process.NET/Assembly/CallingConventions/CallingConventionSelector.cs index 9d252f8..04f1565 100644 --- a/Process.NET/Assembly/CallingConventions/CallingConventionSelector.cs +++ b/Process.NET/Assembly/CallingConventions/CallingConventionSelector.cs @@ -1,7 +1,7 @@ using System; -using Process.NET.Utilities; +using ProcessNET.Utilities; -namespace Process.NET.Assembly.CallingConventions +namespace ProcessNET.Assembly.CallingConventions { /// /// Static class providing calling convention instances. diff --git a/Process.NET/Assembly/CallingConventions/CdeclCallingConvention.cs b/Process.NET/Assembly/CallingConventions/CdeclCallingConvention.cs index 754d4a3..f1d189d 100644 --- a/Process.NET/Assembly/CallingConventions/CdeclCallingConvention.cs +++ b/Process.NET/Assembly/CallingConventions/CdeclCallingConvention.cs @@ -1,9 +1,9 @@ using System; using System.Linq; using System.Text; -using Process.NET.Native.Types; +using ProcessNET.Native.Types; -namespace Process.NET.Assembly.CallingConventions +namespace ProcessNET.Assembly.CallingConventions { /// /// Define the C Declaration Calling Convention. diff --git a/Process.NET/Assembly/CallingConventions/FastcallCallingConvention.cs b/Process.NET/Assembly/CallingConventions/FastcallCallingConvention.cs index a140150..248fa0c 100644 --- a/Process.NET/Assembly/CallingConventions/FastcallCallingConvention.cs +++ b/Process.NET/Assembly/CallingConventions/FastcallCallingConvention.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Text; -using Process.NET.Native.Types; +using ProcessNET.Native.Types; -namespace Process.NET.Assembly.CallingConventions +namespace ProcessNET.Assembly.CallingConventions { /// /// Define the Fast Calling Convention (aka __msfastcall). diff --git a/Process.NET/Assembly/CallingConventions/ICallingConvention.cs b/Process.NET/Assembly/CallingConventions/ICallingConvention.cs index 6d58ad8..a649aed 100644 --- a/Process.NET/Assembly/CallingConventions/ICallingConvention.cs +++ b/Process.NET/Assembly/CallingConventions/ICallingConvention.cs @@ -1,7 +1,7 @@ using System; -using Process.NET.Native.Types; +using ProcessNET.Native.Types; -namespace Process.NET.Assembly.CallingConventions +namespace ProcessNET.Assembly.CallingConventions { /// /// Interface defining a calling convention. diff --git a/Process.NET/Assembly/CallingConventions/StdcallCallingConvention.cs b/Process.NET/Assembly/CallingConventions/StdcallCallingConvention.cs index 1f53098..234c8c3 100644 --- a/Process.NET/Assembly/CallingConventions/StdcallCallingConvention.cs +++ b/Process.NET/Assembly/CallingConventions/StdcallCallingConvention.cs @@ -1,9 +1,9 @@ using System; using System.Linq; using System.Text; -using Process.NET.Native.Types; +using ProcessNET.Native.Types; -namespace Process.NET.Assembly.CallingConventions +namespace ProcessNET.Assembly.CallingConventions { /// /// Define the Standard Calling Convention. diff --git a/Process.NET/Assembly/CallingConventions/ThiscallCallingConvention.cs b/Process.NET/Assembly/CallingConventions/ThiscallCallingConvention.cs index fb73b47..a230c8a 100644 --- a/Process.NET/Assembly/CallingConventions/ThiscallCallingConvention.cs +++ b/Process.NET/Assembly/CallingConventions/ThiscallCallingConvention.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Text; -using Process.NET.Native.Types; +using ProcessNET.Native.Types; -namespace Process.NET.Assembly.CallingConventions +namespace ProcessNET.Assembly.CallingConventions { /// /// Define the 'This' Calling Convention. diff --git a/Process.NET/Assembly/IAssemblyFactory.cs b/Process.NET/Assembly/IAssemblyFactory.cs index c123354..3d5ac1f 100644 --- a/Process.NET/Assembly/IAssemblyFactory.cs +++ b/Process.NET/Assembly/IAssemblyFactory.cs @@ -1,9 +1,9 @@ using System; using System.Threading.Tasks; -using Process.NET.Assembly.Assemblers; -using Process.NET.Memory; +using ProcessNET.Assembly.Assemblers; +using ProcessNET.Memory; -namespace Process.NET.Assembly +namespace ProcessNET.Assembly { public interface IAssemblyFactory : IDisposable { @@ -32,8 +32,8 @@ Task ExecuteAsync(IntPtr address, Native.Types.CallingConventions callingC IAllocatedMemory Inject(string[] asm); IAllocatedMemory Inject(string asm); - void Inject(string[] asm, IntPtr address); - void Inject(string asm, IntPtr address); + bool Inject(string[] asm, IntPtr address); + bool Inject(string asm, IntPtr address); IntPtr InjectAndExecute(string[] asm); IntPtr InjectAndExecute(string asm); IntPtr InjectAndExecute(string[] asm, IntPtr address); diff --git a/Process.NET/Assembly/IAssemblyTransaction.cs b/Process.NET/Assembly/IAssemblyTransaction.cs index 0f0367f..7fc3c2f 100644 --- a/Process.NET/Assembly/IAssemblyTransaction.cs +++ b/Process.NET/Assembly/IAssemblyTransaction.cs @@ -1,6 +1,6 @@ using System; -namespace Process.NET.Assembly +namespace ProcessNET.Assembly { public interface IAssemblyTransaction { diff --git a/Process.NET/Extensions/AttributeExtensions.cs b/Process.NET/Extensions/AttributeExtensions.cs index abb5c54..00e8dc4 100644 --- a/Process.NET/Extensions/AttributeExtensions.cs +++ b/Process.NET/Extensions/AttributeExtensions.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Runtime.InteropServices; -namespace Process.NET.Extensions +namespace ProcessNET.Extensions { public static class AttributeExtensions { diff --git a/Process.NET/Extensions/IntPtrExtensions.cs b/Process.NET/Extensions/IntPtrExtensions.cs new file mode 100644 index 0000000..aee6531 --- /dev/null +++ b/Process.NET/Extensions/IntPtrExtensions.cs @@ -0,0 +1,19 @@ +using System; + +namespace ProcessNET.Extensions +{ + public static class IntPtrExtensions + { + + public static bool MayBeValid(this IntPtr ptr) + { + return ptr.IsInRange((IntPtr)0x10000, (IntPtr)int.MaxValue); + } + + public static bool IsInRange(this IntPtr address, IntPtr start, IntPtr end) + { + var val = (uint)address.ToInt32(); + return (uint)start.ToInt32() <= val && val <= (uint)end.ToInt32(); + } + } +} diff --git a/Process.NET/Extensions/MemoryResourcesExtensions.cs b/Process.NET/Extensions/MemoryResourcesExtensions.cs new file mode 100644 index 0000000..3b8846a --- /dev/null +++ b/Process.NET/Extensions/MemoryResourcesExtensions.cs @@ -0,0 +1,33 @@ +using ProcessNET.Memory; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ProcessNET.Memory +{ + /// + /// Extension methods for safe handling of objects allocated in the remote/local memory. + /// + public static class MemoryResourcesExtensions + { + /// + /// Safely frees up the allocated memory region. + /// + /// The region to free up. + /// True if cleaned successfully, false otherwise. + public static bool SafeRelease(this IAllocatedMemory allocatedMemory) + { + try + { + allocatedMemory.Dispose(); + return true; + } + catch + { + return false; + } + } + } +} diff --git a/Process.NET/Extensions/NativeExtensions.cs b/Process.NET/Extensions/NativeExtensions.cs index ae5e473..738c8c2 100644 --- a/Process.NET/Extensions/NativeExtensions.cs +++ b/Process.NET/Extensions/NativeExtensions.cs @@ -1,4 +1,4 @@ -namespace Process.NET.Extensions +namespace ProcessNET.Extensions { public static class NativeExtensions { diff --git a/Process.NET/Extensions/ProcessExtensions.cs b/Process.NET/Extensions/ProcessExtensions.cs index 376a940..260d72b 100644 --- a/Process.NET/Extensions/ProcessExtensions.cs +++ b/Process.NET/Extensions/ProcessExtensions.cs @@ -3,10 +3,10 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -using Process.NET.Native.Types; -using Process.NET.Utilities; +using ProcessNET.Native.Types; +using ProcessNET.Utilities; -namespace Process.NET.Extensions +namespace ProcessNET.Extensions { public static class ProcessExtensions { diff --git a/Process.NET/Extensions/ProcessModuleExtensions.cs b/Process.NET/Extensions/ProcessModuleExtensions.cs index 70187a6..b2950b7 100644 --- a/Process.NET/Extensions/ProcessModuleExtensions.cs +++ b/Process.NET/Extensions/ProcessModuleExtensions.cs @@ -1,8 +1,8 @@ using System; using System.Diagnostics; -using Process.NET.Utilities; +using ProcessNET.Utilities; -namespace Process.NET.Extensions +namespace ProcessNET.Extensions { public static class ProcessModuleExtensions { diff --git a/Process.NET/Extensions/UnsafeMemoryExtensions.cs b/Process.NET/Extensions/UnsafeMemoryExtensions.cs index 5065390..a8fc796 100644 --- a/Process.NET/Extensions/UnsafeMemoryExtensions.cs +++ b/Process.NET/Extensions/UnsafeMemoryExtensions.cs @@ -1,10 +1,10 @@ using System; using System.Diagnostics; using System.Runtime.InteropServices; -using Process.NET.Marshaling; -using Process.NET.Native; +using ProcessNET.Marshaling; +using ProcessNET.Native; -namespace Process.NET.Extensions +namespace ProcessNET.Extensions { public static class FastCall { @@ -51,11 +51,11 @@ public static IntPtr WrapStdCallInFastCall(IntPtr stdCallPtr) public static class UnsafeMemoryExtensions { // Gets an address from a vtable index.Since it uses index * IntPtr, it should work for both x64 and x32. - public static IntPtr GetVtableIntPtr(this IntPtr intPtr, int functionIndex) - { - var vftable = intPtr.Read(); - return (vftable + functionIndex * IntPtr.Size).Read(); - } + // public static IntPtr GetVtableIntPtr(this IntPtr intPtr, int functionIndex) + // { + // var vftable = intPtr.Read(); + // return (vftable + functionIndex * IntPtr.Size).Read(); + // } //public static IntPtr GetVtableIntPtr2(this IntPtr intPtr, int functionIndex) //{ diff --git a/Process.NET/ExternalProcessMemory.cs b/Process.NET/ExternalProcessMemory.cs index 0666a64..f667157 100644 --- a/Process.NET/ExternalProcessMemory.cs +++ b/Process.NET/ExternalProcessMemory.cs @@ -1,9 +1,9 @@ using System; -using Process.NET.Marshaling; -using Process.NET.Native.Types; -using Process.NET.Utilities; +using ProcessNET.Marshaling; +using ProcessNET.Native.Types; +using ProcessNET.Utilities; -namespace Process.NET.Memory +namespace ProcessNET.Memory { /// /// Class for memory editing a process. @@ -31,7 +31,15 @@ public override byte[] Read(IntPtr intPtr, int length) { return MemoryHelper.ReadBytes(Handle, intPtr, length); } - + /// + /// Writes a set of bytes to memory. + /// + /// The address where the bytes start in memory. + /// The buffer to read data onto, it's size determines the count of bytes to read. + public override void Read(IntPtr intPtr, byte[] buffer) + { + MemoryHelper.ReadBytes(Handle, intPtr, buffer); + } /// /// Reads the value of a specified type from memory. /// @@ -40,7 +48,10 @@ public override byte[] Read(IntPtr intPtr, int length) /// A value. public override T Read(IntPtr intPtr) { - return MarshalType.ByteArrayToObject(Read(intPtr, MarshalType.Size)); + int size = MarshalCache.Size; + if (typeof(T) == typeof(IntPtr)) + size = Is32Bit ? 4 : 8; + return MarshalType.ByteArrayToObject(Read(intPtr, size)); } /// @@ -61,8 +72,7 @@ public override void Write(IntPtr intPtr, T value) /// The array of bytes to write. public override int Write(IntPtr intPtr, byte[] bytesToWrite) { - using (new MemoryProtection(Handle, intPtr, - MarshalType.Size*bytesToWrite.Length)) + using (new MemoryProtection(Handle, intPtr, MarshalType.Size * bytesToWrite.Length)) MemoryHelper.WriteBytes(Handle, intPtr, bytesToWrite); return bytesToWrite.Length; } diff --git a/Process.NET/IProcess.cs b/Process.NET/IProcess.cs index 1e83934..4cdb87a 100644 --- a/Process.NET/IProcess.cs +++ b/Process.NET/IProcess.cs @@ -1,11 +1,11 @@ using System; -using Process.NET.Memory; -using Process.NET.Modules; -using Process.NET.Native.Types; -using Process.NET.Threads; -using Process.NET.Windows; +using ProcessNET.Memory; +using ProcessNET.Modules; +using ProcessNET.Native.Types; +using ProcessNET.Threads; +using ProcessNET.Windows; -namespace Process.NET +namespace ProcessNET { /// /// A class that offers several tools to interact with a process. @@ -27,6 +27,10 @@ public interface IProcess : IDisposable /// Class for reading and writing memory. /// IMemory Memory { get; set; } + /// + /// Gets the name of the user this process belongs to. + /// + string OwnerUser { get; } /// /// Factory for manipulating threads. diff --git a/Process.NET/LocalProcessMemory.cs b/Process.NET/LocalProcessMemory.cs index 4f2a9bb..d328541 100644 --- a/Process.NET/LocalProcessMemory.cs +++ b/Process.NET/LocalProcessMemory.cs @@ -1,10 +1,10 @@ using System; using System.Runtime.InteropServices; -using Process.NET.Extensions; -using Process.NET.Native.Types; +using ProcessNET.Extensions; +using ProcessNET.Native.Types; -namespace Process.NET.Memory +namespace ProcessNET.Memory { /// /// Class for memory editing a process. @@ -31,15 +31,24 @@ public LocalProcessMemory(SafeMemoryHandle handle) : base(handle) public override byte[] Read(IntPtr intPtr, int length) { var readBytes = new byte[length]; + Read(intPtr, readBytes); + return readBytes; + } + /// + /// Writes a set of bytes to memory. + /// + /// The address where the bytes start in memory. + /// The buffer to read data onto, it's size determines the count of bytes to read. + public override void Read(IntPtr intPtr, byte[] buffer) + { + int length = buffer.Length; unsafe { - var bytes = (byte*) intPtr; + var bytes = (byte*)intPtr; for (var i = 0; i < length; i++) - readBytes[i] = bytes[i]; + buffer[i] = bytes[i]; } - return readBytes; } - /// /// Reads the value of a specified type from memory. /// diff --git a/Process.NET/Marshaling/IDisposableState.cs b/Process.NET/Marshaling/IDisposableState.cs index 0016dbe..f713259 100644 --- a/Process.NET/Marshaling/IDisposableState.cs +++ b/Process.NET/Marshaling/IDisposableState.cs @@ -1,6 +1,6 @@ using System; -namespace Process.NET.Marshaling +namespace ProcessNET.Marshaling { /// /// Defines an IDisposable interface with a known state. diff --git a/Process.NET/Marshaling/IMarshalledValue.cs b/Process.NET/Marshaling/IMarshalledValue.cs index 44b991f..b6ace94 100644 --- a/Process.NET/Marshaling/IMarshalledValue.cs +++ b/Process.NET/Marshaling/IMarshalledValue.cs @@ -1,7 +1,7 @@ using System; -using Process.NET.Memory; +using ProcessNET.Memory; -namespace Process.NET.Marshaling +namespace ProcessNET.Marshaling { /// /// Interface representing a value within the memory of a remote process. diff --git a/Process.NET/Marshaling/MarshalCache.cs b/Process.NET/Marshaling/MarshalCache.cs index 008af0f..0e56196 100644 --- a/Process.NET/Marshaling/MarshalCache.cs +++ b/Process.NET/Marshaling/MarshalCache.cs @@ -7,7 +7,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace Process.NET.Marshaling +namespace ProcessNET.Marshaling { [SuppressMessage("ReSharper", "StaticMemberInGenericType")] public static class MarshalCache diff --git a/Process.NET/Marshaling/MarshalType.cs b/Process.NET/Marshaling/MarshalType.cs index f2c019a..5b6d9a2 100644 --- a/Process.NET/Marshaling/MarshalType.cs +++ b/Process.NET/Marshaling/MarshalType.cs @@ -1,11 +1,11 @@ using System; using System.Runtime.InteropServices; using System.Text; -using Process.NET.Memory; +using ProcessNET.Memory; // ReSharper disable All -namespace Process.NET.Marshaling +namespace ProcessNET.Marshaling { /// /// Static class providing tools for extracting information related to types. diff --git a/Process.NET/Marshaling/MarshalValue.cs b/Process.NET/Marshaling/MarshalValue.cs index 1f376cd..fe0a6b8 100644 --- a/Process.NET/Marshaling/MarshalValue.cs +++ b/Process.NET/Marshaling/MarshalValue.cs @@ -7,7 +7,7 @@ * See the file LICENSE for more information. */ -namespace Process.NET.Marshaling +namespace ProcessNET.Marshaling { /// /// The factory to create instance of the class. diff --git a/Process.NET/Marshaling/MarshalledValue.cs b/Process.NET/Marshaling/MarshalledValue.cs index 582ab37..02f5af1 100644 --- a/Process.NET/Marshaling/MarshalledValue.cs +++ b/Process.NET/Marshaling/MarshalledValue.cs @@ -1,9 +1,9 @@ using System; using System.Text; -using Process.NET.Memory; -using Process.NET.Utilities; +using ProcessNET.Memory; +using ProcessNET.Utilities; -namespace Process.NET.Marshaling +namespace ProcessNET.Marshaling { /// /// Class marshalling a value into the remote process. @@ -75,7 +75,7 @@ private void Marshal() // Allocate memory in the remote process (string + '\0') Allocated = Process.MemoryFactory.Allocate(Randomizer.GenerateString(), text.Length + 1); // Write the value - Allocated.Write(0, text, Encoding.UTF8); + Allocated.WriteString(0, text, Encoding.UTF8); // Get the pointer Reference = Allocated.BaseAddress; } diff --git a/Process.NET/Memory/AllocatedMemory.cs b/Process.NET/Memory/AllocatedMemory.cs index 67d4627..731d184 100644 --- a/Process.NET/Memory/AllocatedMemory.cs +++ b/Process.NET/Memory/AllocatedMemory.cs @@ -1,14 +1,16 @@ using System; -using Process.NET.Native.Types; -using Process.NET.Utilities; +using System.ComponentModel; +using ProcessNET.Native.Types; +using ProcessNET.Utilities; -namespace Process.NET.Memory +namespace ProcessNET.Memory { /// /// Class representing an allocated memory in a remote process. /// public class AllocatedMemory : MemoryRegion, IAllocatedMemory { + private readonly System.Diagnostics.Process process = null; /// /// Initializes a new instance of the class. /// @@ -22,6 +24,7 @@ public AllocatedMemory(IProcess processPlus, string name, int size, bool mustBeDisposed = true) : base(processPlus, MemoryHelper.Allocate(processPlus.Handle, size, protection)) { + process = processPlus.Native; // Set local vars Identifier = name; MustBeDisposed = mustBeDisposed; @@ -39,30 +42,41 @@ public AllocatedMemory(IProcess processPlus, string name, int size, /// public bool MustBeDisposed { get; set; } + public bool IsAllocated => !IsDisposed; + public int Size { get; } + public string Identifier { get; } + /// /// Releases all resources used by the object. /// /// Don't use the IDisposable pattern because the class is sealed. public virtual void Dispose() { - if (!IsDisposed) + if (IsDisposed) + return; + // Set the flag to true + IsDisposed = true; + if (!process.HasExited) { - // Set the flag to true - IsDisposed = true; - // Release the allocated memory - Release(); - // Remove this object from the collection of allocated memory - Process.MemoryFactory.Deallocate(this); - // Remove the pointer - BaseAddress = IntPtr.Zero; - // Avoid the finalizer - GC.SuppressFinalize(this); + try + { + // Release the allocated memory + Release(); + } + catch (Win32Exception ex) + { + // Rethrow the exception but with the region's name. + throw new Win32Exception($"Allocated memory: {Identifier} in process id {process.Id}, {ex.Message}"); + } } + // Remove this object from the collection of allocated memory + Process.MemoryFactory.Deallocate(this); + // Remove the pointer + BaseAddress = IntPtr.Zero; + // Avoid the finalizer + GC.SuppressFinalize(this); } - public bool IsAllocated => IsDisposed; - public int Size { get; } - public string Identifier { get; } /// /// Frees resources and perform other cleanup operations before it is reclaimed by garbage collection. diff --git a/Process.NET/Memory/IAllocatedMemory.cs b/Process.NET/Memory/IAllocatedMemory.cs index 4482abd..0184739 100644 --- a/Process.NET/Memory/IAllocatedMemory.cs +++ b/Process.NET/Memory/IAllocatedMemory.cs @@ -1,6 +1,6 @@ -using Process.NET.Marshaling; +using ProcessNET.Marshaling; -namespace Process.NET.Memory +namespace ProcessNET.Memory { public interface IAllocatedMemory : IPointer, IDisposableState { diff --git a/Process.NET/Memory/IMemory.cs b/Process.NET/Memory/IMemory.cs index 40c6827..d694b96 100644 --- a/Process.NET/Memory/IMemory.cs +++ b/Process.NET/Memory/IMemory.cs @@ -1,10 +1,11 @@ using System; using System.Text; -namespace Process.NET.Memory +namespace ProcessNET.Memory { public interface IMemory { + bool Is32Bit { get; } /// /// Writes a set of bytes to memory. /// @@ -15,6 +16,13 @@ public interface IMemory /// byte[] Read(IntPtr intPtr, int length); + /// + /// Writes a set of bytes to memory. + /// + /// The address where the bytes start in memory. + /// The buffer to read data onto, it's size determines the count of bytes to read. + void Read(IntPtr intPtr, byte[] buffer); + /// /// Reads a string with a specified encoding from memory. /// @@ -25,7 +33,7 @@ public interface IMemory /// char). /// /// The string. - string Read(IntPtr intPtr, Encoding encoding, int maxLength); + string ReadString(IntPtr intPtr, Encoding encoding, int maxLength = 512); /// /// Reads the value of a specified type from memory. @@ -57,7 +65,7 @@ public interface IMemory /// The address where the string is written. /// The text to write. /// The encoding used. - void Write(IntPtr intPtr, string stringToWrite, Encoding encoding); + void WriteString(IntPtr intPtr, string stringToWrite, Encoding encoding); /// /// Writes an array of a specified type to memory, @@ -74,5 +82,12 @@ public interface IMemory /// The address where the value is written. /// The value to write. void Write(IntPtr intPtr, T value); + + /// + /// Reads the Runtime Type Information (RTTI) at certain address. + /// + /// Address to read the type from. + /// Runtime Type Information of the object at the passed address or null if it's not an object. + string ReadRemoteRuntimeTypeInformation(IntPtr address); } } \ No newline at end of file diff --git a/Process.NET/Memory/IMemoryFactory.cs b/Process.NET/Memory/IMemoryFactory.cs index 2dcea56..6573600 100644 --- a/Process.NET/Memory/IMemoryFactory.cs +++ b/Process.NET/Memory/IMemoryFactory.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; -using Process.NET.Native.Types; +using ProcessNET.Native.Types; -namespace Process.NET.Memory +namespace ProcessNET.Memory { public interface IMemoryFactory : IDisposable { diff --git a/Process.NET/Memory/IPointer.cs b/Process.NET/Memory/IPointer.cs index 52555cc..d43f8cc 100644 --- a/Process.NET/Memory/IPointer.cs +++ b/Process.NET/Memory/IPointer.cs @@ -1,18 +1,18 @@ using System; using System.Text; -namespace Process.NET.Memory +namespace ProcessNET.Memory { public interface IPointer { IntPtr BaseAddress { get; } bool IsValid { get; } byte[] Read(int offset, int length); - string Read(int offset, Encoding encoding, int maxLength); + string ReadString(int offset, Encoding encoding, int maxLength); T Read(int offset); T[] Read(int offset, int length); int Write(int offset, byte[] toWrite); - void Write(int offset, string stringToWrite, Encoding encoding); + void WriteString(int offset, string stringToWrite, Encoding encoding); void Write(int offset, T[] values); void Write(int offset, T value); } diff --git a/Process.NET/Memory/LocalUnmanagedMemory.cs b/Process.NET/Memory/LocalUnmanagedMemory.cs index 2bd6e89..b11dee5 100644 --- a/Process.NET/Memory/LocalUnmanagedMemory.cs +++ b/Process.NET/Memory/LocalUnmanagedMemory.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.InteropServices; -namespace Process.NET.Memory +namespace ProcessNET.Memory { /// /// Class representing a block of memory allocated in the local process. diff --git a/Process.NET/Memory/MemoryFactory.cs b/Process.NET/Memory/MemoryFactory.cs index 1bde4a4..0b6691e 100644 --- a/Process.NET/Memory/MemoryFactory.cs +++ b/Process.NET/Memory/MemoryFactory.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; -using Process.NET.Native.Types; -using Process.NET.Utilities; +using ProcessNET.Native.Types; +using ProcessNET.Utilities; -namespace Process.NET.Memory +namespace ProcessNET.Memory { /// /// Class providing tools for manipulating memory space. @@ -21,6 +21,7 @@ public class MemoryFactory : IMemoryFactory /// protected readonly IProcess Process; + protected bool isDisposed = false; /// /// Initializes a new instance of the class. /// @@ -58,7 +59,7 @@ public IEnumerable Regions { get { - var size = IntPtr.Size; + var size = Process.Memory.Is32Bit ? 4 : 8; var adresseTo = size == 8 ? new IntPtr(0x7fffffffffffffff) : new IntPtr(0x7fffffff); return MemoryHelper.Query(Process.Handle, IntPtr.Zero, adresseTo) @@ -102,6 +103,9 @@ public void Deallocate(IAllocatedMemory allocation) /// public virtual void Dispose() { + if (isDisposed) + return; + isDisposed = true; // Release all allocated memories which must be disposed foreach (var allocatedMemory in InternalRemoteAllocations.Where(m => m.MustBeDisposed).ToArray()) allocatedMemory.Dispose(); diff --git a/Process.NET/Memory/MemoryPointer.cs b/Process.NET/Memory/MemoryPointer.cs index 929d6b9..7aefc16 100644 --- a/Process.NET/Memory/MemoryPointer.cs +++ b/Process.NET/Memory/MemoryPointer.cs @@ -1,8 +1,8 @@ using System; using System.Text; -using Process.NET.Native.Types; +using ProcessNET.Native.Types; -namespace Process.NET.Memory +namespace ProcessNET.Memory { /// /// Class representing a pointer in the memory of the remote process. @@ -39,7 +39,7 @@ public bool Equals(MemoryPointer other) /// /// The address of the pointer in the remote process. /// - public IntPtr BaseAddress { get; protected set; } + public virtual IntPtr BaseAddress { get; protected set; } /// /// Gets if the is valid. @@ -89,9 +89,9 @@ public int Write(int offset, byte[] toWrite) /// ('\0' char). /// /// The string. - public string Read(int offset, Encoding encoding, int maxLength = 512) + public string ReadString(int offset, Encoding encoding, int maxLength = 512) { - return Process.Memory.Read(BaseAddress + offset, encoding, maxLength); + return Process.Memory.ReadString(BaseAddress + offset, encoding, maxLength); } /// @@ -122,9 +122,9 @@ public void Write(int offset, T[] array) /// The offset where the string is written from the pointer. /// The text to write. /// The encoding used. - public void Write(int offset, string text, Encoding encoding) + public void WriteString(int offset, string text, Encoding encoding) { - Process.Memory.Write(BaseAddress + offset, text, encoding); + Process.Memory.WriteString(BaseAddress + offset, text, encoding); } /// @@ -183,9 +183,9 @@ public T[] Read(Enum offset, int count) /// ('\0' char). /// /// The string. - public string Read(Enum offset, Encoding encoding, int maxLength = 512) + public string ReadString(Enum offset, Encoding encoding, int maxLength = 512) { - return Read(Convert.ToInt32(offset), encoding, maxLength); + return ReadString(Convert.ToInt32(offset), encoding, maxLength); } /// @@ -197,9 +197,9 @@ public string Read(Enum offset, Encoding encoding, int maxLength = 512) /// ('\0' char). /// /// The string. - public string Read(Encoding encoding, int maxLength = 512) + public string ReadString(Encoding encoding, int maxLength = 512) { - return Read(0, encoding, maxLength); + return ReadString(0, encoding, maxLength); } /// @@ -212,9 +212,9 @@ public string Read(Encoding encoding, int maxLength = 512) /// /// /// The string. - public string Read(int offset, int maxLength, Encoding encoding) + public string ReadString(int offset, int maxLength, Encoding encoding) { - return Process.Memory.Read(BaseAddress + offset, encoding, maxLength); + return Process.Memory.ReadString(BaseAddress + offset, encoding, maxLength); } /// @@ -226,9 +226,9 @@ public string Read(int offset, int maxLength, Encoding encoding) /// ('\0' char). /// /// The string. - public string Read(Enum offset, int maxLength = 512) + public string ReadStringUTF8(Enum offset, int maxLength = 512) { - return Read(Convert.ToInt32(offset), maxLength, Encoding.UTF8); + return ReadString(Convert.ToInt32(offset), maxLength, Encoding.UTF8); } /// @@ -287,9 +287,9 @@ public void Write(T[] array) /// The offset where the string is written from the pointer. /// The text to write. /// The encoding used. - public void Write(Enum offset, string text, Encoding encoding) + public void WriteString(Enum offset, string text, Encoding encoding) { - Write(Convert.ToInt32(offset), text, encoding); + WriteString(Convert.ToInt32(offset), text, encoding); } /// @@ -297,9 +297,9 @@ public void Write(Enum offset, string text, Encoding encoding) /// /// The text to write. /// The encoding used. - public void Write(string text, Encoding encoding) + public void WriteString(string text, Encoding encoding) { - Write(0, text, encoding); + WriteString(0, text, encoding); } /// @@ -307,7 +307,7 @@ public void Write(string text, Encoding encoding) /// /// The offset where the string is written from the pointer. /// The text to write. - public void Write(int offset, string text) + public void WriteString(int offset, string text) { Process.Memory.Write(BaseAddress + offset, text); } @@ -317,18 +317,18 @@ public void Write(int offset, string text) /// /// The offset where the string is written from the pointer. /// The text to write. - public void Write(Enum offset, string text) + public void WriteString(Enum offset, string text) { - Write(Convert.ToInt32(offset), text); + WriteString(Convert.ToInt32(offset), text); } /// /// Writes a string using the encoding UTF8 in the remote process. /// /// The text to write. - public void Write(string text) + public void WriteString(string text) { - Write(0, text); + WriteString(0, text); } /// diff --git a/Process.NET/Memory/MemoryProtection.cs b/Process.NET/Memory/MemoryProtection.cs index 602e8df..4a79778 100644 --- a/Process.NET/Memory/MemoryProtection.cs +++ b/Process.NET/Memory/MemoryProtection.cs @@ -1,8 +1,8 @@ using System; -using Process.NET.Native.Types; -using Process.NET.Utilities; +using ProcessNET.Native.Types; +using ProcessNET.Utilities; -namespace Process.NET.Memory +namespace ProcessNET.Memory { /// /// Class providing tools for manipulating memory protection. @@ -10,6 +10,7 @@ namespace Process.NET.Memory public class MemoryProtection : IDisposable { protected readonly SafeMemoryHandle Handle; + private bool isDisposed = false; /// /// Initializes a new instance of the class. @@ -33,7 +34,6 @@ public MemoryProtection(SafeMemoryHandle handle, IntPtr baseAddress, int size, // Change the memory protection OldProtection = MemoryHelper.ChangeProtection(Handle, baseAddress, size, protection); } - /// /// The base address of the altered memory. /// @@ -59,13 +59,18 @@ public MemoryProtection(SafeMemoryHandle handle, IntPtr baseAddress, int size, /// public int Size { get; } + /// /// Restores the initial protection of the memory. /// public virtual void Dispose() { + if (isDisposed) + return; + isDisposed = true; // Restore the memory protection MemoryHelper.ChangeProtection(Handle, BaseAddress, Size, OldProtection); + // Avoid the finalizer GC.SuppressFinalize(this); } diff --git a/Process.NET/Memory/MemoryProtectionOperation.cs b/Process.NET/Memory/MemoryProtectionOperation.cs index 547107b..21f1b99 100644 --- a/Process.NET/Memory/MemoryProtectionOperation.cs +++ b/Process.NET/Memory/MemoryProtectionOperation.cs @@ -3,7 +3,7 @@ // ReSharper disable All -namespace Process.NET.Memory +namespace ProcessNET.Memory { public enum MemoryProtectionType { diff --git a/Process.NET/Memory/MemoryRegion.cs b/Process.NET/Memory/MemoryRegion.cs index 65cfe3e..bf93bcb 100644 --- a/Process.NET/Memory/MemoryRegion.cs +++ b/Process.NET/Memory/MemoryRegion.cs @@ -1,8 +1,10 @@ using System; -using Process.NET.Native.Types; -using Process.NET.Utilities; +using System.ComponentModel; +using System.Security.Cryptography; +using ProcessNET.Native.Types; +using ProcessNET.Utilities; -namespace Process.NET.Memory +namespace ProcessNET.Memory { /// /// Represents a contiguous block of memory in the remote process. diff --git a/Process.NET/Memory/MemoryType.cs b/Process.NET/Memory/MemoryType.cs index 88291aa..70a6740 100644 --- a/Process.NET/Memory/MemoryType.cs +++ b/Process.NET/Memory/MemoryType.cs @@ -4,7 +4,7 @@ using System.Text; using System.Threading.Tasks; -namespace Process.NET.Memory +namespace ProcessNET.Memory { public enum MemoryType { diff --git a/Process.NET/Modules/IModuleFactory.cs b/Process.NET/Modules/IModuleFactory.cs index 5304a8a..23b334b 100644 --- a/Process.NET/Modules/IModuleFactory.cs +++ b/Process.NET/Modules/IModuleFactory.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; -namespace Process.NET.Modules +namespace ProcessNET.Modules { public interface IModuleFactory : IDisposable { diff --git a/Process.NET/Modules/IProcessFunction.cs b/Process.NET/Modules/IProcessFunction.cs index b322f09..462cb99 100644 --- a/Process.NET/Modules/IProcessFunction.cs +++ b/Process.NET/Modules/IProcessFunction.cs @@ -1,6 +1,6 @@ using System; -namespace Process.NET.Modules +namespace ProcessNET.Modules { public interface IProcessFunction { diff --git a/Process.NET/Modules/IProcessModule.cs b/Process.NET/Modules/IProcessModule.cs index 45cfde3..13b3209 100644 --- a/Process.NET/Modules/IProcessModule.cs +++ b/Process.NET/Modules/IProcessModule.cs @@ -1,7 +1,7 @@ using System.Diagnostics; -using Process.NET.Memory; +using ProcessNET.Memory; -namespace Process.NET.Modules +namespace ProcessNET.Modules { public interface IProcessModule : IPointer { diff --git a/Process.NET/Modules/InjectedModule.cs b/Process.NET/Modules/InjectedModule.cs index 6ffaa8e..9385f2e 100644 --- a/Process.NET/Modules/InjectedModule.cs +++ b/Process.NET/Modules/InjectedModule.cs @@ -1,9 +1,9 @@ using System; using System.Diagnostics; using System.Linq; -using Process.NET.Marshaling; +using ProcessNET.Marshaling; -namespace Process.NET.Modules +namespace ProcessNET.Modules { /// /// Class representing an injected module in a remote process. diff --git a/Process.NET/Modules/ModuleFactory.cs b/Process.NET/Modules/ModuleFactory.cs index 5d7ae71..aaa3e99 100644 --- a/Process.NET/Modules/ModuleFactory.cs +++ b/Process.NET/Modules/ModuleFactory.cs @@ -3,9 +3,9 @@ using System.Diagnostics; using System.IO; using System.Linq; -using Process.NET.Memory; +using ProcessNET.Memory; -namespace Process.NET.Modules +namespace ProcessNET.Modules { /// /// Class providing tools for manipulating modules and libraries. @@ -157,8 +157,9 @@ public IProcessModule FetchModule(string moduleName) if (!Path.HasExtension(moduleName)) moduleName += ".dll"; + var module = NativeModules.First(m => m.ModuleName.ToLower() == moduleName); // Fetch and return the module - return new RemoteModule(ProcessPlus, NativeModules.First(m => m.ModuleName.ToLower() == moduleName)); + return FetchModule(module); } /// @@ -168,7 +169,11 @@ public IProcessModule FetchModule(string moduleName) /// A new instance of a class. public IProcessModule FetchModule(ProcessModule module) { - return FetchModule(module.ModuleName); + if (module is null) + { + throw new ArgumentNullException(nameof(module)); + } + return new RemoteModule(ProcessPlus, module); } } } \ No newline at end of file diff --git a/Process.NET/Modules/RemoteFunction.cs b/Process.NET/Modules/RemoteFunction.cs index d186566..8be51bb 100644 --- a/Process.NET/Modules/RemoteFunction.cs +++ b/Process.NET/Modules/RemoteFunction.cs @@ -1,8 +1,8 @@ using System; using System.Runtime.InteropServices; -using Process.NET.Memory; +using ProcessNET.Memory; -namespace Process.NET.Modules +namespace ProcessNET.Modules { /// /// Class representing a function in the remote process. diff --git a/Process.NET/Modules/RemoteModule.cs b/Process.NET/Modules/RemoteModule.cs index bc8d7c8..1eaade8 100644 --- a/Process.NET/Modules/RemoteModule.cs +++ b/Process.NET/Modules/RemoteModule.cs @@ -2,12 +2,12 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using Process.NET.Extensions; -using Process.NET.Memory; -using Process.NET.Native.Types; -using Process.NET.Utilities; +using ProcessNET.Extensions; +using ProcessNET.Memory; +using ProcessNET.Native.Types; +using ProcessNET.Utilities; -namespace Process.NET.Modules +namespace ProcessNET.Modules { /// /// Class repesenting a module in the remote process. diff --git a/Process.NET/Native/Advapi32.cs b/Process.NET/Native/Advapi32.cs index 9bef8c0..f10fdb2 100644 --- a/Process.NET/Native/Advapi32.cs +++ b/Process.NET/Native/Advapi32.cs @@ -1,8 +1,8 @@ using System; using System.Runtime.InteropServices; -using Process.NET.Native.Types; +using ProcessNET.Native.Types; -namespace Process.NET.Native +namespace ProcessNET.Native { public static class Advapi32 { @@ -24,5 +24,9 @@ public static extern bool AdjustTokenPrivileges(IntPtr tokenHandle, int zero, IntPtr null1, IntPtr null2); + + [DllImport("advapi32.dll", SetLastError = true)] + public static extern bool OpenProcessToken(IntPtr ProcessHandle, uint DesiredAccess, out IntPtr TokenHandle); + } } \ No newline at end of file diff --git a/Process.NET/Native/Callbacks.cs b/Process.NET/Native/Callbacks.cs index 07bf8ba..c9a642b 100644 --- a/Process.NET/Native/Callbacks.cs +++ b/Process.NET/Native/Callbacks.cs @@ -1,6 +1,6 @@ using System; -namespace Process.NET.Native +namespace ProcessNET.Native { /// /// This delegate matches the type of parameter "lpfn" for the NativeMethods method "SetWindowsHookEx". diff --git a/Process.NET/Native/DbgHelp.cs b/Process.NET/Native/DbgHelp.cs new file mode 100644 index 0000000..7fef2f7 --- /dev/null +++ b/Process.NET/Native/DbgHelp.cs @@ -0,0 +1,25 @@ +using System.Runtime.InteropServices; +using System.Text; +using ProcessNET.Native.Types; + +namespace ProcessNET.Native +{ + public static class DbgHelp + { + private const string DLLName = "dbghelp.dll"; + [DllImport(DLLName, SetLastError = true, PreserveSig = true)] + private static extern int UnDecorateSymbolName( + [In] [MarshalAs(UnmanagedType.LPStr)] string DecoratedName, + [Out] StringBuilder UnDecoratedName, + [In] [MarshalAs(UnmanagedType.U4)] int UndecoratedLength, + [In] [MarshalAs(UnmanagedType.U4)] UnDecorateFlags Flags); + + + public static string UnDecorateSymbolName(string name, UnDecorateFlags flags) + { + StringBuilder undecorated = new StringBuilder(255); + UnDecorateSymbolName(name, undecorated, undecorated.Capacity, flags); + return undecorated.ToString(); + } + } +} \ No newline at end of file diff --git a/Process.NET/Native/Kernel32.cs b/Process.NET/Native/Kernel32.cs index 2278f63..9a7edc0 100644 --- a/Process.NET/Native/Kernel32.cs +++ b/Process.NET/Native/Kernel32.cs @@ -2,10 +2,10 @@ using System.Runtime.InteropServices; using System.Security; using System.Text; -using Process.NET.Native.Types; -using Process.NET.Windows; +using ProcessNET.Native.Types; +using ProcessNET.Windows; -namespace Process.NET.Native +namespace ProcessNET.Native { public static class Kernel32 { diff --git a/Process.NET/Native/Nt.cs b/Process.NET/Native/Nt.cs index 07f77c6..52570e1 100644 --- a/Process.NET/Native/Nt.cs +++ b/Process.NET/Native/Nt.cs @@ -1,11 +1,39 @@ using System; using System.Runtime.InteropServices; -using Process.NET.Native.Types; +using ProcessNET.Native.Types; -namespace Process.NET.Native +namespace ProcessNET.Native { public static class Nt { + [DllImport("ntdll.dll")] + public static extern int NtAllocateVirtualMemory(IntPtr processHandle, ref IntPtr baseAddress, uint zeroBits, ref uint regionSize, uint allocationType, uint protect); + + [DllImport("ntdll.dll")] + public static extern int NtWriteVirtualMemory(IntPtr processHandle, IntPtr baseAddress, byte[] buffer, uint bufferSize, out uint written); + + [DllImport("ntdll.dll")] + public static extern int RtlCreateUserThread(SafeMemoryHandle processHandle, IntPtr securityDescriptor, bool createSuspended, uint zeroBits, IntPtr zeroReserve, IntPtr zeroCommit, IntPtr startAddress, IntPtr startParameter, ref IntPtr threadHandle, ref NtClientId clientid); + + [DllImport("ntdll.dll")] + public static extern int NtWaitForSingleObject(IntPtr threadHandle, bool alertable, LARGE_INTEGER largeInt); + + [DllImport("ntdll.dll")] + public static extern int NtClose(IntPtr handle); + + [DllImport("ntdll.dll")] + public static extern int NtProtectVirtualMemory(IntPtr processHandle, ref IntPtr baseAddress, ref uint numberOfBytes, uint newProtect, ref uint oldProtect); + + + [DllImport("ntdll.dll")] + public static extern int NtFreeVirtualMemory(IntPtr processHandle, ref IntPtr baseAddress, uint regionSize, uint freeType); + + [DllImport("ntdll.dll", SetLastError = true)] + public static extern SafeMemoryHandle RtlCreateUserThread(SafeMemoryHandle processHandle, IntPtr threadSecurity, bool createSuspended, Int32 stackZeroBits, IntPtr stackReserved, + IntPtr stackCommit, IntPtr startAddress, IntPtr parameter, ref IntPtr threadHandle, ref IntPtr clientId); + + public const uint INFINITE = 0xFFFFFFFF; + /// /// Retrieves information about the specified process. /// @@ -63,5 +91,16 @@ public static extern int NtQueryInformationProcess(SafeMemoryHandle processHandl [DllImport("ntdll.dll")] public static extern int NtQueryInformationThread(SafeMemoryHandle hwnd, int infoclass, ref ThreadBasicInformation threadinfo, int length, IntPtr bytesread); + + + /// + /// Check if the returned flag from an Nt function call represents an operation success. + /// + /// The flag value to check. + /// True if the flag represents success. + public static bool IsNT_StatusSuccess(int value) + { + return (value >= 0 && value <= 0x3FFFFFFF) || (value >= 0x40000000 && value <= 0x7FFFFFFF); + } } } \ No newline at end of file diff --git a/Process.NET/Native/PSAPI.cs b/Process.NET/Native/PSAPI.cs new file mode 100644 index 0000000..d4f6214 --- /dev/null +++ b/Process.NET/Native/PSAPI.cs @@ -0,0 +1,25 @@ +using System; +using System.Runtime.InteropServices; +using System.Text; +using ProcessNET.Native.Types; + +namespace ProcessNET.Native +{ + public static class PSAPI + { + [DllImport("psapi.dll", SetLastError = true)] + public static extern bool EnumProcessModulesEx( + IntPtr hProcess, + [Out] IntPtr lphModule, + UInt32 cb, + [MarshalAs(UnmanagedType.U4)] out UInt32 lpcbNeeded, + DwModuleFilterFlag dwff); + + [DllImport("psapi.dll")] + public static extern uint GetModuleFileNameEx( + IntPtr hProcess, + IntPtr hModule, + [Out] StringBuilder lpBaseName, + [In][MarshalAs(UnmanagedType.U4)] int nSize); + } +} \ No newline at end of file diff --git a/Process.NET/Native/Types/NativeEnums.cs b/Process.NET/Native/Types/NativeEnums.cs index 2346425..486b93e 100644 --- a/Process.NET/Native/Types/NativeEnums.cs +++ b/Process.NET/Native/Types/NativeEnums.cs @@ -2,8 +2,83 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -namespace Process.NET.Native.Types +namespace ProcessNET.Native.Types { + [Flags] + public enum UnDecorateFlags + { + /// + /// Enable full undecoration + /// + UNDNAME_COMPLETE = (0x0000), + /// + /// Remove leading underscores from MS extended keywords + /// + UNDNAME_NO_LEADING_UNDERSCORES = (0x0001), + /// + /// Disable expansion of MS extended keywords + /// + UNDNAME_NO_MS_KEYWORDS = (0x0002), + /// + /// Disable expansion of return type for primary declaration + /// + UNDNAME_NO_FUNCTION_RETURNS = (0x0004), + /// + /// Disable expansion of the declaration model + /// + UNDNAME_NO_ALLOCATION_MODEL = (0x0008), + /// + /// Disable expansion of the declaration language specifier + /// + UNDNAME_NO_ALLOCATION_LANGUAGE = (0x0010), + /// + /// NYI Disable expansion of MS keywords on the 'this' type for primary declaration + /// + UNDNAME_NO_MS_THISTYPE = (0x0020), + /// + /// NYI Disable expansion of CV modifiers on the 'this' type for primary declaration + /// + UNDNAME_NO_CV_THISTYPE = (0x0040), + /// + /// Disable all modifiers on the 'this' type + /// + UNDNAME_NO_THISTYPE = (0x0060), + /// + /// Disable expansion of access specifiers for members + /// + UNDNAME_NO_ACCESS_SPECIFIERS = (0x0080), + /// + /// Disable expansion of 'throw-signatures' for functions and pointers to functions + /// + UNDNAME_NO_THROW_SIGNATURES = (0x0100), + /// + /// Disable expansion of 'static' or 'virtual'ness of members + /// + UNDNAME_NO_MEMBER_TYPE = (0x0200), + /// + /// Disable expansion of MS model for UDT returns + /// + UNDNAME_NO_RETURN_UDT_MODEL = (0x0400), + /// + /// Undecorate 32-bit decorated names + /// + UNDNAME_32_BIT_DECODE = (0x0800), + /// + /// Crack only the name for primary declaration; + /// + UNDNAME_NAME_ONLY = (0x1000), + + /// + /// return just [scope::]name. Does expand template params + /// Don't undecorate arguments to function + /// + UNDNAME_NO_ARGUMENTS = (0x2000), + /// + /// Don't undecorate special names (v-table, vcall, vector xxx, metatype, etc) + /// + UNDNAME_NO_SPECIAL_SYMS = (0x4000), + } + /// /// A list of calling conventions. /// @@ -906,6 +981,17 @@ public enum PebStructure MinimumStackCommit = 0x208 } + [Flags] + public enum NtProcessAccessFlags + { + DELETE = 0x00010000, + READ_CONTROL = 0x00020000, + WRITE_DAC = 0x00040000, + WRITE_OWNER = 0x00080000, + SYNCHRONIZE = 0x00100000, + END = 0xFFF, //if you have Windows XP or Windows Server 2003 you must change this to 0xFFFF + PROCESS_ALL_ACCESS = DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER | SYNCHRONIZE | END, + } /// /// Process access rights list. /// @@ -3056,4 +3142,14 @@ public enum SegmentRegisters /// Ss } + /// + /// Module filtering flag. + /// + public enum DwModuleFilterFlag : uint + { + LIST_MODULES_DEFAULT = 0x0, // This is the default one app would get without any flag. + LIST_MODULES_32BIT = 0x01, // list 32bit modules in the target process. + LIST_MODULES_64BIT = 0x02, // list all 64bit modules. 32bit exe will be stripped off. + LIST_MODULES_ALL = (LIST_MODULES_32BIT | LIST_MODULES_64BIT) // list all the modules + } } \ No newline at end of file diff --git a/Process.NET/Native/Types/NativeStructs.cs b/Process.NET/Native/Types/NativeStructs.cs index 31abd69..45f90e0 100644 --- a/Process.NET/Native/Types/NativeStructs.cs +++ b/Process.NET/Native/Types/NativeStructs.cs @@ -1,9 +1,9 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -using Process.NET.Marshaling; +using ProcessNET.Marshaling; -namespace Process.NET.Native.Types +namespace ProcessNET.Native.Types { [StructLayout(LayoutKind.Sequential)] public struct MSLLHOOKSTRUCT @@ -707,4 +707,40 @@ public struct WindowPlacement /// public Rectangle NormalPosition; } + + + [StructLayout(LayoutKind.Explicit, Size = 8)] + public struct LARGE_INTEGER + { + [FieldOffset(0)] public long QuadPart; + + [FieldOffset(0)] public uint LowPart; + [FieldOffset(4)] public int HighPart; + + [FieldOffset(0)] public int LowPartAsInt; + [FieldOffset(0)] public uint LowPartAsUInt; + + [FieldOffset(4)] public int HighPartAsInt; + [FieldOffset(4)] public uint HighPartAsUInt; + + public long ToInt64() + { + return ((long)this.HighPart << 32) | (uint)this.LowPartAsInt; + } + + public static LARGE_INTEGER FromInt64(long value) + { + return new LARGE_INTEGER + { + LowPartAsInt = (int)(value), + HighPartAsInt = (int)((value >> 32)) + }; + } + } + + public struct NtClientId + { + public IntPtr processHandle; + public IntPtr threadHandle; + } } \ No newline at end of file diff --git a/Process.NET/Native/Types/SafeLoadLibrary.cs b/Process.NET/Native/Types/SafeLoadLibrary.cs index 69752e5..ba6fd2a 100644 --- a/Process.NET/Native/Types/SafeLoadLibrary.cs +++ b/Process.NET/Native/Types/SafeLoadLibrary.cs @@ -1,6 +1,6 @@ using Microsoft.Win32.SafeHandles; -namespace Process.NET.Native.Types +namespace ProcessNET.Native.Types { /// /// Provides a safe handle for a library loaded via LoadLibraryEx. diff --git a/Process.NET/Native/Types/SafeMemoryHandle.cs b/Process.NET/Native/Types/SafeMemoryHandle.cs index 7738101..2a28395 100644 --- a/Process.NET/Native/Types/SafeMemoryHandle.cs +++ b/Process.NET/Native/Types/SafeMemoryHandle.cs @@ -3,7 +3,7 @@ using System.Security.Permissions; using Microsoft.Win32.SafeHandles; -namespace Process.NET.Native.Types +namespace ProcessNET.Native.Types { /// /// Represents a Win32 handle safely managed. diff --git a/Process.NET/Native/User32.cs b/Process.NET/Native/User32.cs index b9381df..361e748 100644 --- a/Process.NET/Native/User32.cs +++ b/Process.NET/Native/User32.cs @@ -2,9 +2,9 @@ using System.Runtime.InteropServices; using System.Security; using System.Text; -using Process.NET.Native.Types; +using ProcessNET.Native.Types; -namespace Process.NET.Native +namespace ProcessNET.Native { public static class User32 { @@ -108,8 +108,8 @@ public static class User32 /// to the variable; otherwise, it does not. /// /// The return value is the identifier of the thread that created the window. - [DllImport("user32.dll")] - public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId); + [DllImport("user32.dll", SetLastError =true)] + public static extern int GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); /// /// Enumerates the child windows that belong to the specified parent window by passing the handle to each child window, @@ -134,6 +134,42 @@ public static class User32 [return: MarshalAs(UnmanagedType.Bool)] public static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam); + /// + /// Get the handle of the parent window of the passed child. + /// + /// + /// The handle of the window whose parent to get. + /// + /// + /// The handle of the parent window. + /// + [DllImport("user32.dll")] + public static extern IntPtr GetParent(IntPtr hWnd); + + /// + /// Retrieves a handle to a window whose class name and window name match the specified strings. The function searches child windows, beginning with the one following the specified child window. + /// This function does not perform a case-sensitive search. + /// + /// + /// A handle to the parent window whose child windows are to be searched. + /// If hwndParent is NULL, the function uses the desktop window as the parent window. + /// The function searches among windows that are child windows of the desktop. + /// If hwndParent is HWND_MESSAGE, the function searches all message-only windows. + /// + /// + /// A handle to a child window. The search begins with the next child window in the Z order. + /// The child window must be a direct child window of hwndParent, not just a descendant window. + /// If hwndChildAfter is NULL, the search begins with the first child window of hwndParent. + /// Note that if both hwndParent and hwndChildAfter are NULL, the function searches all top-level and message-only windows. + /// + /// The class name or a class atom created by a previous call to the RegisterClass or RegisterClassEx function. + /// The atom must be placed in the low-order word of lpszClass; the high-order word must be zero. + /// + /// The window name (the window's title). If this parameter is NULL, all window names match. + /// + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle); + /// /// Flashes the specified window one time. It does not change the active state of the window. /// To flash the window a specified number of times, use the function. @@ -406,5 +442,10 @@ internal static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool UnhookWindowsHookEx(IntPtr hhk); + + + [DllImport("user32.dll")] + public static extern bool EnumThreadWindows(int dwThreadId, EnumWindowsProc lpfn, IntPtr lParam); + } } \ No newline at end of file diff --git a/Process.NET/Patterns/DwordPattern.cs b/Process.NET/Patterns/DwordPattern.cs index 77f9434..83bc789 100644 --- a/Process.NET/Patterns/DwordPattern.cs +++ b/Process.NET/Patterns/DwordPattern.cs @@ -2,7 +2,7 @@ using System.Globalization; using System.Linq; -namespace Process.NET.Patterns +namespace ProcessNET.Patterns { public class DwordPattern : IMemoryPattern { @@ -10,25 +10,30 @@ public class DwordPattern : IMemoryPattern private readonly string _mask; public readonly string PatternText; + public int SearchStartOffset { get; set; } + public MemoryPatternType PatternType { get; } + public PatternScannerAlgorithm Algorithm { get; } - public DwordPattern(string dwordPattern) + public DwordPattern(string dwordPattern, int startOffset = 0, PatternScannerAlgorithm algorithm = PatternScannerAlgorithm.Naive) { + this.PatternType = MemoryPatternType.Function; + this.Algorithm = algorithm; + this.SearchStartOffset = startOffset; PatternText = dwordPattern; - PatternType = MemoryPatternType.Function; - Offset = 0; _bytes = GetBytesFromDwordPattern(dwordPattern); _mask = GetMaskFromDwordPattern(dwordPattern); } - - public DwordPattern(string pattern, int offset) + public DwordPattern(byte[] pattern, int startOffset = 0, PatternScannerAlgorithm algorithm = PatternScannerAlgorithm.Naive) { - PatternText = pattern; - PatternType = MemoryPatternType.Data; - Offset = offset; - _bytes = GetBytesFromDwordPattern(pattern); - _mask = GetMaskFromDwordPattern(pattern); + this.PatternType = MemoryPatternType.Function; + this.Algorithm = algorithm; + this.SearchStartOffset = startOffset; + PatternText = string.Join(" ", pattern.Select(o => o.ToString("X2"))); + _bytes = new byte[pattern.Length]; + for (int i = 0; i < _bytes.Length; i++) + _bytes[i] = pattern[i]; + _mask = new string(Enumerable.Repeat('x', _bytes.Length).ToArray()); } - public IList GetBytes() { return _bytes; @@ -39,8 +44,6 @@ public string GetMask() return _mask; } - public int Offset { get; } - public MemoryPatternType PatternType { get; } private static string GetMaskFromDwordPattern(string pattern) { diff --git a/Process.NET/Patterns/IMemoryPattern.cs b/Process.NET/Patterns/IMemoryPattern.cs index e2cb229..631b716 100644 --- a/Process.NET/Patterns/IMemoryPattern.cs +++ b/Process.NET/Patterns/IMemoryPattern.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; -namespace Process.NET.Patterns +namespace ProcessNET.Patterns { public interface IMemoryPattern { - int Offset { get; } + int SearchStartOffset { get; } MemoryPatternType PatternType { get; } + PatternScannerAlgorithm Algorithm { get; } IList GetBytes(); string GetMask(); } diff --git a/Process.NET/Patterns/IPatternScanner.cs b/Process.NET/Patterns/IPatternScanner.cs index 6218795..5481bf8 100644 --- a/Process.NET/Patterns/IPatternScanner.cs +++ b/Process.NET/Patterns/IPatternScanner.cs @@ -1,7 +1,7 @@ -namespace Process.NET.Patterns +namespace ProcessNET.Patterns { public interface IPatternScanner { - PatternScanResult Find(IMemoryPattern pattern); + PatternScanResult Find(IMemoryPattern pattern, bool useCache = true); } } \ No newline at end of file diff --git a/Process.NET/Patterns/MemoryPatternType.cs b/Process.NET/Patterns/MemoryPatternType.cs index b112937..1fc8e7d 100644 --- a/Process.NET/Patterns/MemoryPatternType.cs +++ b/Process.NET/Patterns/MemoryPatternType.cs @@ -1,4 +1,4 @@ -namespace Process.NET.Patterns +namespace ProcessNET.Patterns { public enum MemoryPatternType { diff --git a/Process.NET/Patterns/PatternScanResult.cs b/Process.NET/Patterns/PatternScanResult.cs index 16e5a1a..f76c948 100644 --- a/Process.NET/Patterns/PatternScanResult.cs +++ b/Process.NET/Patterns/PatternScanResult.cs @@ -1,6 +1,6 @@ using System; -namespace Process.NET.Patterns +namespace ProcessNET.Patterns { public struct PatternScanResult { @@ -8,5 +8,8 @@ public struct PatternScanResult public IntPtr BaseAddress { get; set; } public int Offset { get; set; } public bool Found { get; set; } + + + public static PatternScanResult NotFound => new PatternScanResult(); } } \ No newline at end of file diff --git a/Process.NET/Patterns/PatternScanner.cs b/Process.NET/Patterns/PatternScanner.cs index 777ea15..00df858 100644 --- a/Process.NET/Patterns/PatternScanner.cs +++ b/Process.NET/Patterns/PatternScanner.cs @@ -1,83 +1,122 @@ using System; +using System.Collections.Concurrent; using System.Linq; -using Process.NET.Modules; +using ProcessNET.Memory; +using ProcessNET.Modules; +using ProcessNET.Utilities; -namespace Process.NET.Patterns +namespace ProcessNET.Patterns { public class PatternScanner : IPatternScanner { + private class CachedPatternScanResult + { + public int Offset { get; set; } + public string ModuleName { get; set; } + } + + static ConcurrentDictionary cache = null; + private readonly IProcessModule _module; + private readonly IProcess _process; + public byte[] Data { get; } - public PatternScanner(IProcessModule module) + public PatternScanner(IProcess process, IProcessModule module) { + this._process = process; _module = module; Data = module.Read(0, _module.Size); } + static PatternScanner() + { + cache = new ConcurrentDictionary(); + } - public byte[] Data { get; } - public PatternScanResult Find(IMemoryPattern pattern) + public PatternScanResult Find(IMemoryPattern pattern, bool useCache = true) { + if(useCache && cache.TryGetValue(GetPatternCacheKey(pattern), out CachedPatternScanResult cachedResult)) + { + return new PatternScanResult() + { + BaseAddress = _module.BaseAddress + cachedResult.Offset, + ReadAddress = _module.BaseAddress + cachedResult.Offset, + Offset = cachedResult.Offset, + Found = true, + }; + } + return pattern.PatternType == MemoryPatternType.Function ? FindFunctionPattern(pattern) : FindDataPattern(pattern); } - - private PatternScanResult FindFunctionPattern(IMemoryPattern pattern) + + + private int GetOffset(IMemoryPattern pattern) { - var patternData = Data; - var patternDataLength = patternData.Length; - - for (var offset = 0; offset < patternDataLength; offset++) + switch (pattern.Algorithm) { - if ( - pattern.GetMask() - .Where((m, b) => m == 'x' && pattern.GetBytes()[b] != patternData[b + offset]) - .Any()) - continue; - - return new PatternScanResult - { - BaseAddress = _module.BaseAddress + offset, - ReadAddress = _module.BaseAddress + offset, - Offset = offset, - Found = true - }; + case PatternScannerAlgorithm.Naive: + return StringSearching.Naive.GetIndexOf(pattern, Data, _module, pattern.SearchStartOffset); + case PatternScannerAlgorithm.BoyerMooreHorspool: + return StringSearching.BoyerMooreHorspool.IndexOf(Data, pattern.GetBytes().ToArray(), pattern.SearchStartOffset); + default: + throw new NotImplementedException($"Unknown search algorithm, please implement it, {pattern.Algorithm}."); } - return new PatternScanResult + } + private PatternScanResult FindFunctionPattern(IMemoryPattern pattern) + { + int offset = GetOffset(pattern); + if (offset < 0) + return PatternScanResult.NotFound; + PatternScanResult result = new PatternScanResult { - BaseAddress = IntPtr.Zero, - ReadAddress = IntPtr.Zero, - Offset = 0, - Found = false + BaseAddress = _module.BaseAddress + offset, + ReadAddress = _module.BaseAddress + offset, + Offset = offset, + Found = true }; + CachePatternScanResult(pattern, result); + return result; } - private PatternScanResult FindDataPattern(IMemoryPattern pattern) { - var patternData = Data; - var patternBytes = pattern.GetBytes(); - var patternMask = pattern.GetMask(); + int offset = GetOffset(pattern); + if (offset < 0) + return PatternScanResult.NotFound; var result = new PatternScanResult(); + // If this area is reached, the pattern has been found. + result.Found = true; + result.ReadAddress = _module.Read(offset); + result.BaseAddress = new IntPtr(result.ReadAddress.ToInt64() - _module.BaseAddress.ToInt64()); + result.Offset = offset; + CachePatternScanResult(pattern, result); + return result; + } - for (var offset = 0; offset < patternData.Length; offset++) + private string GetPatternCacheKey(IMemoryPattern pattern) + { + return $"{pattern.ToString()}_{_module.Name}_{_module.Path}_{pattern.SearchStartOffset}"; + } + private void CachePatternScanResult(IMemoryPattern pattern, PatternScanResult result) + { + if (!result.Found) + return; + string key = GetPatternCacheKey(pattern); + CachedPatternScanResult cachedPatternScanResult = new CachedPatternScanResult() { - if (patternMask.Where((m, b) => m == 'x' && patternBytes[b] != patternData[b + offset]).Any()) - continue; - // If this area is reached, the pattern has been found. - result.Found = true; - result.ReadAddress = _module.Read(offset + pattern.Offset); - result.BaseAddress = new IntPtr(result.ReadAddress.ToInt64() - _module.BaseAddress.ToInt64()); - result.Offset = offset; - return result; - } - // If this is reached, the pattern was not found. - result.Found = false; - result.Offset = 0; - result.ReadAddress = IntPtr.Zero; - result.BaseAddress = IntPtr.Zero; - return result; + ModuleName = _module.Name, + Offset = result.Offset, + }; + + cache.AddOrUpdate(key, oldKey => + { + return cachedPatternScanResult; + }, (oldKey, oldValue) => + { + return cachedPatternScanResult; + }); } } } \ No newline at end of file diff --git a/Process.NET/Patterns/PatternScannerAlgorithm.cs b/Process.NET/Patterns/PatternScannerAlgorithm.cs new file mode 100644 index 0000000..c7180d3 --- /dev/null +++ b/Process.NET/Patterns/PatternScannerAlgorithm.cs @@ -0,0 +1,8 @@ +namespace ProcessNET.Patterns +{ + public enum PatternScannerAlgorithm + { + Naive, + BoyerMooreHorspool, + } +} \ No newline at end of file diff --git a/Process.NET/Process.NET.csproj b/Process.NET/Process.NET.csproj index ba44703..8525d77 100644 --- a/Process.NET/Process.NET.csproj +++ b/Process.NET/Process.NET.csproj @@ -9,7 +9,7 @@ Properties Process.NET Process.NET - v4.7.2 + v4.5.2 512 @@ -80,25 +80,36 @@ MinimumRecommendedRules.ruleset + + ..\packages\Fasm.NET.1.70.03.2\lib\Fasm.NET.dll + + + + + + + + + @@ -188,6 +199,9 @@ + + +