LearnWordManage/AsposeHook/MethodHook.cs

238 lines
11 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ExtractResFile.AsposeHook
{
public class MethodHook
{
private byte[] _jmpCodeBytes;
private byte[] _originalMethodBytes;
private MethodMeta _sourceMethodMeta;
private MethodMeta _targetMethodMeta;
private bool _hookInitialized = false;
public bool IsEnabled { get; protected set; }
public MethodHook(MethodBase sourceMethod, MethodBase targetMethod)
{
if (sourceMethod == null)
{
throw new ArgumentException("create source method hook failed because of method is null.");
}
if (targetMethod == null)
{
throw new ArgumentException("create target method hook failed because of method is null.");
}
try
{
_sourceMethodMeta = new MethodMeta
{
Method = sourceMethod
};
_targetMethodMeta = new MethodMeta
{
Method = targetMethod
};
PrepareHook();
_hookInitialized = true;
Utils.LogWriteLine(_sourceMethodMeta.Method.DeclaringType.FullName + "." + _sourceMethodMeta.Method.Name + " is to be HOOKED by " + _targetMethodMeta.Method.Name + ".\n", ConsoleColor.DarkYellow);
}
catch (Exception)
{
_hookInitialized = false;
}
IsEnabled = false;
}
public void StartHook(bool showInfo = true)
{
try
{
if (!IsEnabled && _hookInitialized)
{
Kernel32.VirtualProtect(_sourceMethodMeta.FinalMethodAddress, _jmpCodeBytes.Length, 64u, out var oldProtectionType);
Marshal.Copy(_jmpCodeBytes, 0, _sourceMethodMeta.FinalMethodAddress, _jmpCodeBytes.Length);
Kernel32.VirtualProtect(_sourceMethodMeta.FinalMethodAddress, _jmpCodeBytes.Length, oldProtectionType, out var _);
IsEnabled = true;
if (showInfo)
{
Utils.LogWriteLine(_sourceMethodMeta.Method.Name + " HOOK STARTED.", ConsoleColor.Blue);
}
}
}
catch (Exception ex)
{
Utils.LogWriteLine(_sourceMethodMeta.Method.Name + " start hook error:" + ex.Message, ConsoleColor.DarkRed);
}
}
public void StopHook(bool showInfo = true)
{
try
{
if (IsEnabled && _hookInitialized)
{
Kernel32.VirtualProtect(_sourceMethodMeta.FinalMethodAddress, _originalMethodBytes.Length, 64u, out var oldProtectionType);
Marshal.Copy(_originalMethodBytes, 0, _sourceMethodMeta.FinalMethodAddress, _originalMethodBytes.Length);
Kernel32.VirtualProtect(_sourceMethodMeta.FinalMethodAddress, _originalMethodBytes.Length, oldProtectionType, out var _);
IsEnabled = false;
if (showInfo)
{
Utils.LogWriteLine(_sourceMethodMeta.Method.Name + " HOOK STOPPED.", ConsoleColor.Red);
}
}
}
catch (Exception ex)
{
Utils.LogWriteLine(_sourceMethodMeta.Method.Name + " stop hook error:" + ex.Message, ConsoleColor.DarkRed);
}
}
private void PrepareHook()
{
_sourceMethodMeta.MethodDescAddress = _sourceMethodMeta.Method.MethodHandle.Value;
_sourceMethodMeta.MethodDescValue = _sourceMethodMeta.Method.MethodHandle.Value.ReadIntPtr();
_sourceMethodMeta.FunctionPointerBeforePrepare = _sourceMethodMeta.Method.MethodHandle.GetFunctionPointer();
_sourceMethodMeta.Next1IntptrBeforePrepare = (_sourceMethodMeta.Method.MethodHandle.Value + IntPtr.Size).ReadIntPtr();
_sourceMethodMeta.Next2IntptrBeforePrepare = (_sourceMethodMeta.Method.MethodHandle.Value + IntPtr.Size * 2).ReadIntPtr();
RuntimeHelpers.PrepareMethod(_sourceMethodMeta.Method.MethodHandle);
_sourceMethodMeta.FunctionPointerAfterPrepare = _sourceMethodMeta.Method.MethodHandle.GetFunctionPointer();
_sourceMethodMeta.Next1IntptrAfterPrepare = (_sourceMethodMeta.Method.MethodHandle.Value + IntPtr.Size).ReadIntPtr();
_sourceMethodMeta.Next2IntptrAfterPrepare = (_sourceMethodMeta.Method.MethodHandle.Value + IntPtr.Size * 2).ReadIntPtr();
Utils.ShowMethodInfo(_sourceMethodMeta);
if (Environment.Is64BitProcess)
{
Utils.LogWriteLine("Because of Environment.Is64BitProcess, change method " + _sourceMethodMeta.Method.Name + " PREJIT to JIT through add 0x20.");
Marshal.WriteByte(_sourceMethodMeta.Method.MethodHandle.Value + 7, 32);
Utils.LogWriteLine("method " + _sourceMethodMeta.Method.Name + " MD value has changed to:" + _sourceMethodMeta.Method.MethodHandle.Value.ReadIntPtr().To16String());
}
_sourceMethodMeta.FinalMethodAddress = _sourceMethodMeta.FunctionPointerAfterPrepare;
Utils.LogWriteLine("Finally, wo choose method " + _sourceMethodMeta.Method.Name + " address: " + _sourceMethodMeta.FinalMethodAddress.To16String());
_targetMethodMeta.MethodDescAddress = _targetMethodMeta.Method.MethodHandle.Value;
_targetMethodMeta.MethodDescValue = _targetMethodMeta.Method.MethodHandle.Value.ReadIntPtr();
_targetMethodMeta.FunctionPointerBeforePrepare = _targetMethodMeta.Method.MethodHandle.GetFunctionPointer();
_targetMethodMeta.Next1IntptrBeforePrepare = (_targetMethodMeta.Method.MethodHandle.Value + IntPtr.Size).ReadIntPtr();
_targetMethodMeta.Next2IntptrBeforePrepare = (_targetMethodMeta.Method.MethodHandle.Value + IntPtr.Size * 2).ReadIntPtr();
RuntimeHelpers.PrepareMethod(_targetMethodMeta.Method.MethodHandle);
_targetMethodMeta.FunctionPointerAfterPrepare = _targetMethodMeta.Method.MethodHandle.GetFunctionPointer();
_targetMethodMeta.Next1IntptrAfterPrepare = (_targetMethodMeta.Method.MethodHandle.Value + IntPtr.Size).ReadIntPtr();
_targetMethodMeta.Next2IntptrAfterPrepare = (_targetMethodMeta.Method.MethodHandle.Value + IntPtr.Size * 2).ReadIntPtr();
Utils.ShowMethodInfo(_targetMethodMeta);
_targetMethodMeta.FinalMethodAddress = _targetMethodMeta.FunctionPointerAfterPrepare;
Utils.LogWriteLine("Finally, wo choose method " + _targetMethodMeta.Method.Name + " address: " + _targetMethodMeta.FinalMethodAddress.To16String());
Utils.LogWriteLine("Now change method " + _sourceMethodMeta.Method.Name + " body to jump to " + _targetMethodMeta.Method.Name + " through jmp instuction...");
List<byte> list = new List<byte>();
if (Environment.Is64BitProcess)
{
list.AddRange(new byte[2] { 72, 184 });
list.AddRange(BitConverter.GetBytes((long)_targetMethodMeta.FinalMethodAddress));
list.AddRange(new byte[2] { 255, 224 });
}
else
{
list.Add(184);
list.AddRange(BitConverter.GetBytes((int)_targetMethodMeta.FinalMethodAddress));
list.AddRange(new byte[2] { 255, 224 });
}
_jmpCodeBytes = list.ToArray();
_originalMethodBytes = new byte[_jmpCodeBytes.Length];
Kernel32.VirtualProtect(_sourceMethodMeta.FinalMethodAddress, _jmpCodeBytes.Length, 64u, out var oldProtectionType);
Marshal.Copy(_sourceMethodMeta.FinalMethodAddress, _originalMethodBytes, 0, _jmpCodeBytes.Length);
Kernel32.VirtualProtect(_sourceMethodMeta.FinalMethodAddress, _jmpCodeBytes.Length, oldProtectionType, out var _);
}
public T InvokeOriginal<T>(object instance, params object[] args)
{
try
{
if (IsEnabled)
{
if (instance is MethodBase)
{
object obj = (instance as MethodBase).Invoke(args[0], BindingFlags.Default, null, args[1] as object[], null);
if ((object)typeof(T) != null)
{
return (T)obj;
}
return (T)Convert.ChangeType(obj, typeof(T));
}
StopHook(showInfo: false);
object obj2 = _sourceMethodMeta.Method.Invoke(instance, args);
StartHook(showInfo: false);
if ((object)typeof(T) != null)
{
return (T)obj2;
}
return (T)Convert.ChangeType(obj2, typeof(T));
}
object value = _sourceMethodMeta.Method.Invoke(instance, args);
return (T)Convert.ChangeType(value, typeof(T));
}
catch (Exception ex)
{
Exception ex2 = ex;
while (ex2.InnerException != null)
{
ex2 = ex2.InnerException;
}
Utils.LogWriteLine("call InvokeOriginal error:" + ex2.Message, ConsoleColor.DarkRed);
}
return default(T);
}
public void InvokeOriginal(object instance, params object[] args)
{
try
{
if (IsEnabled)
{
if (instance is MethodBase)
{
(instance as MethodBase).Invoke(args[0], BindingFlags.Default, null, args[1] as object[], null);
return;
}
StopHook(showInfo: false);
_sourceMethodMeta.Method.Invoke(instance, args);
StartHook(showInfo: false);
}
else
{
_sourceMethodMeta.Method.Invoke(instance, args);
}
}
catch (Exception ex)
{
Exception ex2 = ex;
while (ex2.InnerException != null)
{
ex2 = ex2.InnerException;
}
Utils.LogWriteLine("call InvokeOriginal error:" + ex2.Message, ConsoleColor.DarkRed);
}
}
}
}