238 lines
11 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
}
|