AssemblyPatcher.cs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. using System.Collections.Generic;
  2. using System.IO;
  3. using System.Linq;
  4. using System.Reflection;
  5. using BepInEx.Common;
  6. using Mono.Cecil;
  7. namespace BepInEx.Bootstrap
  8. {
  9. public delegate void AssemblyPatcherDelegate(AssemblyDefinition assembly);
  10. public static class AssemblyPatcher
  11. {
  12. private static bool DumpingEnabled => bool.TryParse(Config.GetEntry("preloader-dumpassemblies", "false"), out bool result) ? result : false;
  13. public static void PatchAll(string directory, Dictionary<string, IList<AssemblyPatcherDelegate>> patcherMethodDictionary)
  14. {
  15. //load all the requested assemblies
  16. List<AssemblyDefinition> assemblies = new List<AssemblyDefinition>();
  17. Dictionary<AssemblyDefinition, string> assemblyFilenames = new Dictionary<AssemblyDefinition, string>();
  18. foreach (string assemblyPath in Directory.GetFiles(directory, "*.dll"))
  19. {
  20. var assembly = AssemblyDefinition.ReadAssembly(assemblyPath);
  21. //NOTE: this is special cased here because the dependency handling for System.dll is a bit wonky
  22. //System has an assembly reference to itself, and it also has a reference to Mono.Security causing a circular dependency
  23. //It's also generally dangerous to change system.dll since so many things rely on it,
  24. // and it's already loaded into the appdomain since this loader references it, so we might as well skip it
  25. if (assembly.Name.Name == "System"
  26. || assembly.Name.Name == "mscorlib") //mscorlib is already loaded into the appdomain so it can't be patched
  27. {
  28. #if CECIL_10
  29. assembly.Dispose();
  30. #endif
  31. continue;
  32. }
  33. assemblies.Add(assembly);
  34. assemblyFilenames[assembly] = Path.GetFileName(assemblyPath);
  35. }
  36. //generate a dictionary of each assembly's dependencies
  37. Dictionary<AssemblyDefinition, IList<AssemblyDefinition>> assemblyDependencyDict = new Dictionary<AssemblyDefinition, IList<AssemblyDefinition>>();
  38. foreach (AssemblyDefinition assembly in assemblies)
  39. {
  40. assemblyDependencyDict[assembly] = new List<AssemblyDefinition>();
  41. foreach (var dependencyRef in assembly.MainModule.AssemblyReferences)
  42. {
  43. var dependencyAssembly = assemblies.FirstOrDefault(x => x.FullName == dependencyRef.FullName);
  44. if (dependencyAssembly != null)
  45. assemblyDependencyDict[assembly].Add(dependencyAssembly);
  46. }
  47. }
  48. //sort the assemblies so load the assemblies that are dependant upon first
  49. IEnumerable<AssemblyDefinition> sortedAssemblies = Utility.TopologicalSort(assemblies, x => assemblyDependencyDict[x]);
  50. //call the patchers on the assemblies
  51. foreach (var assembly in sortedAssemblies)
  52. {
  53. #if CECIL_10
  54. using (assembly)
  55. #endif
  56. {
  57. string filename = Path.GetFileName(assemblyFilenames[assembly]);
  58. //skip if we aren't patching it
  59. if (!patcherMethodDictionary.TryGetValue(filename, out IList<AssemblyPatcherDelegate> patcherMethods))
  60. continue;
  61. Patch(assembly, patcherMethods);
  62. if (DumpingEnabled)
  63. {
  64. using (MemoryStream mem = new MemoryStream())
  65. {
  66. string dirPath = Path.Combine(Preloader.PluginPath, "DumpedAssemblies");
  67. if (!Directory.Exists(dirPath))
  68. Directory.CreateDirectory(dirPath);
  69. assembly.Write(mem);
  70. File.WriteAllBytes(Path.Combine(dirPath, filename), mem.ToArray());
  71. }
  72. }
  73. }
  74. }
  75. }
  76. public static void Patch(AssemblyDefinition assembly, IEnumerable<AssemblyPatcherDelegate> patcherMethods)
  77. {
  78. using (MemoryStream assemblyStream = new MemoryStream())
  79. {
  80. foreach (AssemblyPatcherDelegate method in patcherMethods)
  81. method.Invoke(assembly);
  82. assembly.Write(assemblyStream);
  83. Assembly.Load(assemblyStream.ToArray());
  84. }
  85. }
  86. }
  87. }