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 list = new List(); 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(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); } } } }