Skip to content

Commit

Permalink
Hook generator
Browse files Browse the repository at this point in the history
  • Loading branch information
ToniMacaroni committed Jan 1, 2024
1 parent e24fb98 commit f1d0cd0
Show file tree
Hide file tree
Showing 20 changed files with 1,677 additions and 43 deletions.
8 changes: 2 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,2 @@
- Added more events.
- Allow binding keybind config to panel visibility.
- Added `clearpickups` command (removes all pickups in a radius).
- Fixed some inputs not working sometimes.
- Various bug fixes.
- SUI improvements.
- Use custom fork of Il2CppInterop
-
4 changes: 3 additions & 1 deletion Dependencies/Il2CppAssemblyGenerator/Core.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.IO;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using RedLoader.Il2CppAssemblyGenerator.Packages;
Expand Down
39 changes: 38 additions & 1 deletion Dependencies/Il2CppAssemblyGenerator/Packages/Il2CppInterop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
using System.Text.RegularExpressions;
using Il2CppInterop.Common;
using Il2CppInterop.Generator;
using Il2CppInterop.Generator.Contexts;
using Il2CppInterop.Generator.Passes;
using Il2CppInterop.Generator.Runners;
using Il2CppInterop.Runtime.Startup;
using Microsoft.Extensions.Logging;
using Mono.Cecil;
using RedLoader.Utils;

namespace RedLoader.Il2CppAssemblyGenerator.Packages
{
Expand Down Expand Up @@ -47,9 +50,11 @@ internal override bool Execute()
UnityBaseLibsDir = Core.unitydependencies.Destination,
ObfuscatedNamesRegex = string.IsNullOrEmpty(Core.deobfuscationRegex.Regex) ? null : new Regex(Core.deobfuscationRegex.Regex),
Parallel = true,
Il2CppPrefixMode = GeneratorOptions.PrefixMode.OptIn
Il2CppPrefixMode = GeneratorOptions.PrefixMode.OptIn,
};

opts.AddPass<HookGenPass>();

//Inform cecil of the unity base libs
var trusted = (string) AppDomain.CurrentDomain.GetData("TRUSTED_PLATFORM_ASSEMBLIES");
var allUnityDlls = string.Join(Path.PathSeparator, Directory.GetFiles(Core.unitydependencies.Destination, "*.dll", SearchOption.TopDirectoryOnly));
Expand Down Expand Up @@ -90,6 +95,38 @@ internal override bool Execute()
}
}

internal class HookGenPass : ICustomPass
{
public void DoPass(RewriteGlobalContext context)
{
//var depsDirs = new List<string> { LoaderEnvironment.Il2CppAssembliesDirectory, Path.Combine(LoaderEnvironment.LoaderDirectory, "net6") };
// foreach (var assembly in assemblies)
// {
// GenerateHookAssembly(Path.Combine(LoaderEnvironment.Il2CppAssembliesDirectory, assembly + ".dll"),
// Path.Combine(LoaderEnvironment.HooksDirectory, "HK_" + assembly + ".dll"), depsDirs);
// }

foreach (var assembly in context.Assemblies)
{
var name = assembly.OriginalAssembly.Name.Name;
if(!name.Contains("Sons") && !name.Contains("Endnight"))
continue;

RLog.Msg($"Generating hooks for {assembly.OriginalAssembly.Name.Name}");
var gen = new HookGeneratorV2(assembly.OriginalAssembly.MainModule, assembly.NewAssembly.MainModule);

try
{
gen.Generate();
}
catch (Exception e)
{
Console.WriteLine($"Error while processing {name}: \n {e}");
}
}
}
}

internal class InteropLogger : ILogger
{
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
Expand Down
23 changes: 0 additions & 23 deletions Dependencies/SupportModules/Il2Cpp/HarmonyExceptionFix.cs

This file was deleted.

2 changes: 0 additions & 2 deletions Dependencies/SupportModules/Il2Cpp/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ private static ISupportModule_To Initialize(ISupportModule_From interface_from)
//HarmonyLib.Public.Patching.PatchManager.ResolvePatcher += HarmonyMethodPatcher.TryResolve;
runtime.Start();

HarmonyExceptionFix.Install();

return new SupportModule_To();
}

Expand Down
9 changes: 9 additions & 0 deletions RedLoader/Core.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ internal static class Core

internal static int Initialize()
{
try
{
ReshadeManager.LoadUnity();
}
catch (Exception e)
{
Console.WriteLine("Reshade not found");
}

var runtimeFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
var runtimeDirInfo = new DirectoryInfo(runtimeFolder);
LoaderEnvironment.LoaderDirectory = runtimeDirInfo.Parent!.FullName;
Expand Down
10 changes: 5 additions & 5 deletions RedLoader/Fixes/DotnetLoadFromManagedFolderFix.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ private static Assembly OnResolve(AssemblyLoadContext alc, AssemblyName name)
var gameRootPath = Path.Combine(LoaderEnvironment.GameRootDirectory, filename);

var ret = TryLoad(alc, osSpecificPath)
?? TryLoad(alc, il2cppPath)
//?? TryLoad(alc, managedPath)
?? TryLoad(alc, modsPath)
?? TryLoad(alc, userlibsPath)
?? TryLoad(alc, gameRootPath);
?? TryLoad(alc, il2cppPath)
//?? TryLoad(alc, managedPath)
?? TryLoad(alc, modsPath)
?? TryLoad(alc, userlibsPath)
?? TryLoad(alc, gameRootPath);

if (ret == null)
MelonDebug.Error($"[DotnetManagedFolder] Failed to find {filename} in any of the known search directories");
Expand Down
3 changes: 2 additions & 1 deletion RedLoader/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@
[assembly: InternalsVisibleTo("MelonStartScreen")]
[assembly: InternalsVisibleTo("SonsLoaderPlugin")]
[assembly: InternalsVisibleTo("SonsGameManager")]
[assembly: InternalsVisibleTo("SonsSdk")]
[assembly: InternalsVisibleTo("SonsSdk")]
[assembly: InternalsVisibleTo("HookGen")]
2 changes: 2 additions & 0 deletions RedLoader/RedLoader.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<PackageReference Include="AssetRipper.VersionUtilities" Version="1.4.0" />
<PackageReference Include="AssetsTools.NET" Version="3.0.0-preview3" />
<PackageReference Include="HarmonyX" Version="2.10.1" />
<PackageReference Include="MonoMod" Version="22.7.31.1" />
<PackageReference Include="MonoMod.RuntimeDetour" Version="22.7.31.1" />
<PackageReference Include="Samboy063.Tomlet" Version="5.1.2" />
<PackageReference Include="bHapticsLib" Version="1.0.6" />
Expand Down Expand Up @@ -69,6 +70,7 @@
<ItemGroup>
<ProjectReference Include="..\Dependencies\Il2CppInterop\Il2CppInterop.Common\Il2CppInterop.Common.csproj" />
<ProjectReference Include="..\Dependencies\Il2CppInterop\Il2CppInterop.Generator\Il2CppInterop.Generator.csproj" />
<ProjectReference Include="..\Dependencies\Il2CppInterop\Il2CppInterop.HarmonySupport\Il2CppInterop.HarmonySupport.csproj" />
<ProjectReference Include="..\Dependencies\Il2CppInterop\Il2CppInterop.Runtime\Il2CppInterop.Runtime.csproj" />
</ItemGroup>
</Project>
9 changes: 9 additions & 0 deletions RedLoader/ReshadeManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Runtime.InteropServices;

namespace RedLoader;

internal class ReshadeManager
{
[DllImport("ReShade64.dll")]
public static extern bool LoadUnity();
}
175 changes: 175 additions & 0 deletions RedLoader/Utils/Hooking/HookDetourPatcher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using HarmonyLib;
using Il2CppInterop.Common;
using Il2CppInterop.HarmonySupport;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.Startup;
using Mono.Cecil.Cil;
using MonoMod.Cil;
using MonoMod.Utils;

namespace RedLoader.Utils.Hooking;

public class HookDetourPatcher : Il2CppDetourMethodPatcher
{
public static readonly Dictionary<long, HookPatchInstance> HarmonyPatchInstances = new();

private readonly HookPatchInstance _patchInstance;

public HookDetourPatcher(MethodBase original, HookPatchInstance patchInstance) : base(original)
{
_patchInstance = patchInstance;
}

public void Apply()
{
DetourTo(null);
}

public override MethodBase DetourTo(MethodBase _)
{
// // Unpatch an existing detour if it exists
if (nativeDetour != null)
{
// Point back to the original method before we unpatch
modifiedNativeMethodInfo.MethodPointer = originalNativeMethodInfo.MethodPointer;
nativeDetour.Dispose();
}

// Generate a new DMD of the modified unhollowed method, and apply harmony patches to it
var copiedDmd = CopyOriginal();

// Generate the MethodInfo instances
var managedHookedMethod = copiedDmd.Generate();
var unmanagedTrampolineMethod = GenerateNativeToManagedTrampoline(managedHookedMethod).Generate();

// Apply a detour from the unmanaged implementation to the patched harmony method
var unmanagedDelegateType = DelegateTypeFactory.instance.CreateDelegateType(unmanagedTrampolineMethod,
CallingConvention.Cdecl);

var unmanagedDelegate = unmanagedTrampolineMethod.CreateDelegate(unmanagedDelegateType);
DelegateCache.Add(unmanagedDelegate);

nativeDetour =
Il2CppInteropRuntime.Instance.DetourProvider.Create(originalNativeMethodInfo.MethodPointer, unmanagedDelegate);
nativeDetour.Apply();
modifiedNativeMethodInfo.MethodPointer = nativeDetour.OriginalTrampoline;

// TODO: Add an ILHook for the original unhollowed method to go directly to managedHookedMethod
// Right now it goes through three times as much interop conversion as it needs to, when being called from managed side
return managedHookedMethod;
}

public static void PostfixCaller(long ptr, object[] args)
{
var instance = HarmonyPatchInstances[ptr];
// for (var i = 0; i < args.Length; i++)
// {
// var arg = args[i];
// RLog.Msg(Color.CadetBlue, $"[{i}] PAR: {arg.ToString()}");
// }

foreach (var del in instance.PostfixDelegates)
{
//RLog.Msg(Color.CadetBlue, "Calling: " + instance.HookDelegates.First().Method.Name);
del.DynamicInvoke(args);
}
}

public static bool PrefixCaller(long ptr, object[] args)
{
var instance = HarmonyPatchInstances[ptr];

foreach (var del in instance.PrefixDelegates)
{
//RLog.Msg(Color.CadetBlue, "Calling: " + del.Method.Name);
var result = del.DynamicInvoke(args);
if (result is false)
{
return false;
}
}

return true;
}

public override DynamicMethodDefinition CopyOriginal()
{
var ptr = modifiedNativeMethodInfo.Pointer.ToInt64();
HarmonyPatchInstances[ptr] = _patchInstance;

var dmd = new DynamicMethodDefinition(Original);
dmd.Definition.Name = "UnhollowedWrapper_" + dmd.Definition.Name;
var cursor = new ILCursor(new ILContext(dmd.Definition));

CreateDelegateCall(dmd, cursor, ptr, typeof(HookDetourPatcher).GetMethod(nameof(PrefixCaller), BindingFlags.Static | BindingFlags.Public));
cursor.Emit(OpCodes.Brfalse, dmd.Definition.Body.Instructions.Last());

// Remove il2cpp_object_get_virtual_method
if (cursor.TryGotoNext(x => x.MatchLdarg(0),
x => x.MatchCall(typeof(IL2CPP),
nameof(IL2CPP.Il2CppObjectBaseToPtr)),
x => x.MatchLdsfld(out _),
x => x.MatchCall(typeof(IL2CPP),
nameof(IL2CPP.il2cpp_object_get_virtual_method))))
{
cursor.RemoveRange(4);
}
else
{
cursor.Goto(0)
.GotoNext(x =>
x.MatchLdsfld(Il2CppInteropUtils
.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(Original)))
.Remove();
}

// Replace original IL2CPPMethodInfo pointer with a modified one that points to the trampoline
cursor
.Emit(OpCodes.Ldc_I8, ptr)
.Emit(OpCodes.Conv_I);

CreateDelegateCall(dmd, cursor, ptr, typeof(HookDetourPatcher).GetMethod(nameof(PostfixCaller), BindingFlags.Static | BindingFlags.Public));

//RLog.Msg(Color.Orange, $"IL: {String.Join("\n", dmd.Definition.Body.Instructions.Select(x => x.ToString()))}");

return dmd;
}

private static void CreateDelegateCall(DynamicMethodDefinition dmd, ILCursor cursor, long ptr, MethodBase method)
{
var pars = dmd.Definition.Parameters;

// load pointer parameter
cursor.Emit(OpCodes.Ldc_I8, ptr)
.Emit(OpCodes.Ldc_I4, pars.Count);

// create array of objects
cursor.Emit(OpCodes.Newarr, typeof(object));

// load original method parameters into array
for (var i = 0; i < pars.Count; i++)
{
//RLog.Msg(Color.Orange, $"[{i}] Loading in parameter {pars[i].Name}");

cursor.Emit(OpCodes.Dup)
.Emit(OpCodes.Ldc_I4, i)
.Emit(OpCodes.Ldarg, pars[i]);

if (pars[i].ParameterType.IsValueType)
{
cursor.Emit(OpCodes.Box, pars[i].ParameterType);
}

cursor.Emit(OpCodes.Stelem_Ref);
}

// Call method
cursor.Emit(OpCodes.Call, method);
}
}
Loading

0 comments on commit f1d0cd0

Please sign in to comment.