mirror of
https://github.com/BepInEx/BepInEx.git
synced 2026-10-20 01:46:13 +00:00
Implement automatic assembly unhollower
This commit is contained in:
10 files changed
+236
-28
No files matched your search
+33
-13
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Text;
|
||||
using Mono.Cecil;
|
||||
|
||||
namespace BepInEx
|
||||
@@ -162,21 +163,30 @@ namespace BepInEx
|
||||
|
||||
foreach (string subDirectory in potentialDirectories)
|
||||
{
|
||||
string path = Path.Combine(subDirectory, $"{assemblyName.Name}.dll");
|
||||
|
||||
if (!File.Exists(path))
|
||||
continue;
|
||||
|
||||
try
|
||||
string[] potentialPaths = new[]
|
||||
{
|
||||
assembly = loader(path);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$"{assemblyName.Name}.dll",
|
||||
$"{assemblyName.Name}.exe"
|
||||
};
|
||||
|
||||
return true;
|
||||
foreach (var potentialPath in potentialPaths)
|
||||
{
|
||||
string path = Path.Combine(subDirectory, potentialPath);
|
||||
|
||||
if (!File.Exists(path))
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
assembly = loader(path);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -250,5 +260,15 @@ namespace BepInEx
|
||||
currentType = currentType.BaseType?.Resolve();
|
||||
}
|
||||
}
|
||||
|
||||
public static string ByteArrayToString(byte[] data)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder(data.Length * 2);
|
||||
|
||||
foreach (byte b in data)
|
||||
builder.AppendFormat("{0:x2}", b);
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<LangVersion>8</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>portable</DebugType>
|
||||
@@ -33,8 +34,15 @@
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<LangVersion>8</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="AssemblyUnhollower">
|
||||
<HintPath>..\lib\AssemblyUnhollower.exe</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Il2CppDumper">
|
||||
<HintPath>..\lib\Il2CppDumper.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Il2Cppmscorlib">
|
||||
<HintPath>..\lib\Il2Cppmscorlib.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
@@ -44,7 +52,7 @@
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="UnhollowerBaseLib">
|
||||
<HintPath>..\lib\UnhollowerBaseLib.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine-IL2CPP">
|
||||
<HintPath>..\lib\UnityEngine-IL2CPP.dll</HintPath>
|
||||
@@ -66,6 +74,7 @@
|
||||
<Compile Include="Preloader.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Hook\DetourGenerator.cs" />
|
||||
<Compile Include="ProxyAssemblyGenerator.cs" />
|
||||
<Compile Include="UnityEngine\Input.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -17,10 +17,17 @@ namespace BepInEx.IL2CPP
|
||||
AppDomain.CurrentDomain.AssemblyResolve += LocalResolve;
|
||||
AppDomain.CurrentDomain.AssemblyResolve -= DoorstopEntrypoint.ResolveCurrentDirectory;
|
||||
|
||||
File.WriteAllText("B:\\a.txt", "a");
|
||||
|
||||
//AppDomain.CurrentDomain.TypeResolve += (sender, eventArgs) =>
|
||||
//{
|
||||
// eventArgs.
|
||||
//}
|
||||
|
||||
Preloader.Run();
|
||||
}
|
||||
|
||||
private static Assembly LocalResolve(object sender, ResolveEventArgs args)
|
||||
internal static Assembly LocalResolve(object sender, ResolveEventArgs args)
|
||||
{
|
||||
var assemblyName = new AssemblyName(args.Name);
|
||||
|
||||
|
||||
@@ -34,8 +34,6 @@ namespace BepInEx.IL2CPP
|
||||
|
||||
private static RuntimeInvokeDetourDelegate originalInvoke;
|
||||
|
||||
private static readonly ManualLogSource unhollowerLogSource = Logger.CreateLogSource("Unhollower");
|
||||
|
||||
private static FastNativeDetour RuntimeInvokeDetour { get; set; }
|
||||
|
||||
private static IL2CPPChainloader Instance { get; set; }
|
||||
@@ -132,12 +130,6 @@ namespace BepInEx.IL2CPP
|
||||
|
||||
Logger.Sources.Add(UnityLogSource);
|
||||
|
||||
|
||||
UnhollowerBaseLib.LogSupport.InfoHandler += unhollowerLogSource.LogInfo;
|
||||
UnhollowerBaseLib.LogSupport.WarningHandler += unhollowerLogSource.LogWarning;
|
||||
UnhollowerBaseLib.LogSupport.TraceHandler += unhollowerLogSource.LogDebug;
|
||||
UnhollowerBaseLib.LogSupport.ErrorHandler += unhollowerLogSource.LogError;
|
||||
|
||||
base.InitializeLoggers();
|
||||
|
||||
|
||||
@@ -147,11 +139,23 @@ namespace BepInEx.IL2CPP
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
// Temporarily disable the console log listener as we replay the preloader logs
|
||||
|
||||
var logListener = Logger.Listeners.FirstOrDefault(logger => logger is ConsoleLogListener);
|
||||
|
||||
if (logListener != null)
|
||||
Logger.Listeners.Remove(logListener);
|
||||
|
||||
foreach (var preloaderLogEvent in PreloaderConsoleListener.LogEvents)
|
||||
{
|
||||
PreloaderLogger.Log.Log(preloaderLogEvent.Level, preloaderLogEvent.Data);
|
||||
}
|
||||
|
||||
if (logListener != null)
|
||||
Logger.Listeners.Add(logListener);
|
||||
|
||||
|
||||
//UnityEngine.Application.s_LogCallbackHandler = DelegateSupport.ConvertDelegate<Application.LogCallback>(new Action<string>(UnityLogCallback));
|
||||
//UnityEngine.Application.s_LogCallbackHandler = (Application.LogCallback)new Action<string>(UnityLogCallback);
|
||||
|
||||
@@ -12,7 +12,8 @@ namespace BepInEx.IL2CPP
|
||||
|
||||
private static PreloaderConsoleListener PreloaderLog { get; set; }
|
||||
|
||||
private static ManualLogSource Log => PreloaderLogger.Log;
|
||||
internal static ManualLogSource Log => PreloaderLogger.Log;
|
||||
internal static ManualLogSource UnhollowerLog { get; set; }
|
||||
|
||||
public static IL2CPPChainloader Chainloader { get; private set; }
|
||||
|
||||
@@ -20,24 +21,42 @@ namespace BepInEx.IL2CPP
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
PreloaderLog = new PreloaderConsoleListener(false);
|
||||
PreloaderLog = new PreloaderConsoleListener(true);
|
||||
Logger.Listeners.Add(PreloaderLog);
|
||||
|
||||
|
||||
|
||||
if (ConsoleManager.ConfigConsoleEnabled.Value && !ConsoleManager.ConsoleActive)
|
||||
{
|
||||
ConsoleManager.CreateConsole();
|
||||
Logger.Listeners.Add(new ConsoleLogListener());
|
||||
}
|
||||
|
||||
BasicLogInfo.PrintLogInfo(Log);
|
||||
|
||||
|
||||
|
||||
Log.LogInfo($"Running under Unity v{FileVersionInfo.GetVersionInfo(Paths.ExecutablePath).FileVersion}");
|
||||
|
||||
Log.LogDebug($"Game executable path: {Paths.ExecutablePath}");
|
||||
Log.LogDebug($"Unhollowed assembly directory: {IL2CPPUnhollowedPath}");
|
||||
Log.LogDebug($"BepInEx root path: {Paths.BepInExRootPath}");
|
||||
|
||||
|
||||
|
||||
UnhollowerLog = Logger.CreateLogSource("Unhollower");
|
||||
UnhollowerBaseLib.LogSupport.InfoHandler += UnhollowerLog.LogInfo;
|
||||
UnhollowerBaseLib.LogSupport.WarningHandler += UnhollowerLog.LogWarning;
|
||||
UnhollowerBaseLib.LogSupport.TraceHandler += UnhollowerLog.LogDebug;
|
||||
UnhollowerBaseLib.LogSupport.ErrorHandler += UnhollowerLog.LogError;
|
||||
|
||||
|
||||
if (ProxyAssemblyGenerator.CheckIfGenerationRequired())
|
||||
ProxyAssemblyGenerator.GenerateAssemblies();
|
||||
|
||||
|
||||
Logger.Listeners.Remove(PreloaderLog);
|
||||
|
||||
|
||||
|
||||
Chainloader = new IL2CPPChainloader();
|
||||
|
||||
Chainloader.Initialize();
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using AssemblyUnhollower;
|
||||
using BepInEx.Logging;
|
||||
using Il2CppDumper;
|
||||
|
||||
namespace BepInEx.IL2CPP
|
||||
{
|
||||
internal static class ProxyAssemblyGenerator
|
||||
{
|
||||
public static string GameAssemblyPath => Path.Combine(Paths.GameRootPath, "GameAssembly.dll");
|
||||
|
||||
private static string HashPath => Path.Combine(Preloader.IL2CPPUnhollowedPath, "assembly-hash.txt");
|
||||
|
||||
private static string tempDumperDirectory => Path.Combine(Preloader.IL2CPPUnhollowedPath, "temp");
|
||||
|
||||
private static ManualLogSource Il2cppDumperLogger = Logger.CreateLogSource("Il2CppDumper");
|
||||
|
||||
|
||||
private static string ComputeGameAssemblyHash()
|
||||
{
|
||||
using var md5 = MD5.Create();
|
||||
using var assemblyStream = File.OpenRead(GameAssemblyPath);
|
||||
|
||||
var hash = md5.ComputeHash(assemblyStream);
|
||||
|
||||
return Utility.ByteArrayToString(hash);
|
||||
}
|
||||
|
||||
public static bool CheckIfGenerationRequired()
|
||||
{
|
||||
if (!Directory.Exists(Preloader.IL2CPPUnhollowedPath))
|
||||
return true;
|
||||
|
||||
if (!File.Exists(HashPath))
|
||||
return true;
|
||||
|
||||
if (ComputeGameAssemblyHash() != File.ReadAllText(HashPath))
|
||||
{
|
||||
Preloader.Log.LogInfo("Detected a game update, will regenerate proxy assemblies");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void GenerateAssemblies()
|
||||
{
|
||||
var domain = AppDomain.CreateDomain("GeneratorDomain", null, new AppDomainSetup
|
||||
{
|
||||
ApplicationBase = Paths.BepInExAssemblyDirectory
|
||||
});
|
||||
|
||||
var runner = (AppDomainRunner)domain.CreateInstanceAndUnwrap(typeof(AppDomainRunner).Assembly.FullName, typeof(AppDomainRunner).FullName);
|
||||
|
||||
runner.Setup(Paths.ExecutablePath, Preloader.IL2CPPUnhollowedPath);
|
||||
runner.GenerateAssembliesInternal(new AppDomainListener());
|
||||
|
||||
AppDomain.Unload(domain);
|
||||
|
||||
Directory.Delete(tempDumperDirectory, true);
|
||||
|
||||
File.WriteAllText(HashPath, ComputeGameAssemblyHash());
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
private class AppDomainListener : MarshalByRefObject
|
||||
{
|
||||
public void DoPreloaderLog(object data, LogLevel level)
|
||||
{
|
||||
Preloader.Log.Log(level, data);
|
||||
}
|
||||
|
||||
public void DoDumperLog(object data, LogLevel level)
|
||||
{
|
||||
Il2cppDumperLogger.Log(level, data);
|
||||
}
|
||||
|
||||
public void DoUnhollowerLog(object data, LogLevel level)
|
||||
{
|
||||
Preloader.UnhollowerLog.Log(level, data);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
private class AppDomainRunner : MarshalByRefObject
|
||||
{
|
||||
public void Setup(string executablePath, string unhollowedPath)
|
||||
{
|
||||
Paths.SetExecutablePath(executablePath);
|
||||
Preloader.IL2CPPUnhollowedPath = unhollowedPath;
|
||||
}
|
||||
|
||||
public void GenerateAssembliesInternal(AppDomainListener listener)
|
||||
{
|
||||
listener.DoPreloaderLog("Generating Il2CppUnhollower assemblies", LogLevel.Message);
|
||||
|
||||
System.Threading.Thread.Sleep(5000);
|
||||
|
||||
if (Directory.Exists(Preloader.IL2CPPUnhollowedPath))
|
||||
Directory.Delete(Preloader.IL2CPPUnhollowedPath, true);
|
||||
|
||||
Directory.CreateDirectory(Preloader.IL2CPPUnhollowedPath);
|
||||
|
||||
string tempDumperDirectory = Path.Combine(Preloader.IL2CPPUnhollowedPath, "temp");
|
||||
Directory.CreateDirectory(tempDumperDirectory);
|
||||
|
||||
|
||||
|
||||
|
||||
var dumperConfig = new Config
|
||||
{
|
||||
GenerateScript = false,
|
||||
GenerateDummyDll = true
|
||||
};
|
||||
|
||||
listener.DoPreloaderLog("Generating Il2CppDumper intermediate assemblies", LogLevel.Info);
|
||||
|
||||
Il2CppDumper.Il2CppDumper.PerformDump(GameAssemblyPath,
|
||||
Path.Combine(Paths.GameRootPath, $"{Paths.ProcessName}_Data", "il2cpp_data", "Metadata", "global-metadata.dat"),
|
||||
tempDumperDirectory,
|
||||
dumperConfig,
|
||||
s => listener.DoDumperLog(s, LogLevel.Debug));
|
||||
|
||||
|
||||
|
||||
listener.DoPreloaderLog("Executing Il2CppUnhollower generator", LogLevel.Info);
|
||||
|
||||
System.Threading.Thread.Sleep(5000);
|
||||
|
||||
UnhollowerBaseLib.LogSupport.InfoHandler += s => listener.DoUnhollowerLog(s, LogLevel.Info);
|
||||
UnhollowerBaseLib.LogSupport.WarningHandler += s => listener.DoUnhollowerLog(s, LogLevel.Warning);
|
||||
UnhollowerBaseLib.LogSupport.TraceHandler += s => listener.DoUnhollowerLog(s, LogLevel.Debug);
|
||||
UnhollowerBaseLib.LogSupport.ErrorHandler += s => listener.DoUnhollowerLog(s, LogLevel.Error);
|
||||
|
||||
var unhollowerOptions = new UnhollowerOptions
|
||||
{
|
||||
GameAssemblyPath = GameAssemblyPath,
|
||||
MscorlibPath = Path.Combine(Paths.GameRootPath, "mono", "Managed", "mscorlib.dll"),
|
||||
SourceDir = Path.Combine(tempDumperDirectory, "DummyDll"),
|
||||
OutputDir = Preloader.IL2CPPUnhollowedPath
|
||||
};
|
||||
|
||||
AssemblyUnhollower.Program.Main(unhollowerOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in new issue
Block a user