From ccd08c3a63b4cbfa84ff96995edffa49952b6d0e Mon Sep 17 00:00:00 2001 From: Sinai Date: Thu, 13 May 2021 23:02:46 +1000 Subject: [PATCH] Add configurable reflection signature blacklist, extends to MCS as well --- src/Core/Config/ConfigManager.cs | 29 ++-- src/Core/Input/CursorUnlocker.cs | 2 +- src/Core/Reflection/Il2CppReflection.cs | 159 +++++++++++++++++++++- src/Core/Reflection/ReflectionUtility.cs | 47 ++++++- src/Loader/ML/MelonLoaderConfigHandler.cs | 4 +- src/UI/CacheObject/CacheMember.cs | 75 +++------- 6 files changed, 241 insertions(+), 75 deletions(-) diff --git a/src/Core/Config/ConfigManager.cs b/src/Core/Config/ConfigManager.cs index c7a90cd..ae55b89 100644 --- a/src/Core/Config/ConfigManager.cs +++ b/src/Core/Config/ConfigManager.cs @@ -18,20 +18,23 @@ namespace UnityExplorer.Core.Config public static ConfigElement Main_Menu_Toggle; public static ConfigElement Force_Unlock_Mouse; - public static ConfigElement Force_Unlock_Keybind; - public static ConfigElement Aggressive_Force_Unlock; - public static ConfigElement Default_Page_Limit; + public static ConfigElement Force_Unlock_Toggle; + public static ConfigElement Aggressive_Mouse_Unlock; public static ConfigElement Default_Output_Path; public static ConfigElement Log_Unity_Debug; public static ConfigElement Hide_On_Startup; public static ConfigElement Startup_Delay_Time; + public static ConfigElement Reflection_Signature_Blacklist; + // internal configs internal static InternalConfigHandler InternalHandler { get; private set; } public static ConfigElement ObjectExplorerData; public static ConfigElement InspectorData; public static ConfigElement CSConsoleData; + public static ConfigElement OptionsPanelData; + public static ConfigElement ConsoleLogData; internal static readonly Dictionary ConfigElements = new Dictionary(); internal static readonly Dictionary InternalConfigs = new Dictionary(); @@ -80,11 +83,11 @@ namespace UnityExplorer.Core.Config "Force the Cursor to be unlocked (visible) when the UnityExplorer menu is open.", true); - Force_Unlock_Keybind = new ConfigElement("Force Unlock Keybind", + Force_Unlock_Toggle = new ConfigElement("Force Unlock Toggle Key", "The keybind to toggle the 'Force Unlock Mouse' setting. Only usable when UnityExplorer is open.", KeyCode.None); - Aggressive_Force_Unlock = new ConfigElement("Aggressive Mouse Unlock", + Aggressive_Mouse_Unlock = new ConfigElement("Aggressive Mouse Unlock", "Use WaitForEndOfFrame to aggressively force the Mouse to be unlocked (requires game restart).", false); @@ -92,10 +95,6 @@ namespace UnityExplorer.Core.Config "Should UnityEngine.Debug.Log messages be printed to UnityExplorer's log?", false); - Default_Page_Limit = new ConfigElement("Default Page Limit", - "The default maximum number of elements per 'page' in UnityExplorer.", - 25); - Default_Output_Path = new ConfigElement("Default Output Path", "The default output path when exporting things from UnityExplorer.", Path.Combine(ExplorerCore.Loader.ExplorerFolder, "Output")); @@ -104,11 +103,21 @@ namespace UnityExplorer.Core.Config "The delay on startup before the UI is created.", 1f); - // Internal configs + Reflection_Signature_Blacklist = new ConfigElement("Reflection Signature Blacklist", + "Use this to blacklist certain member signatures if they are known to cause a crash or other issues." + + "\r\nSeperate signatures with a semicolon ';'.", + "DEFAULT"); + + Reflection_Signature_Blacklist.OnValueChanged += ReflectionUtility.LoadBlacklistString; + ReflectionUtility.LoadBlacklistString(Reflection_Signature_Blacklist.Value); + + // Internal configs (panel save data) ObjectExplorerData = new ConfigElement("ObjectExplorer", "", "", true); InspectorData = new ConfigElement("Inspector", "", "", true); CSConsoleData = new ConfigElement("CSConsole", "", "", true); + OptionsPanelData = new ConfigElement("OptionsPanel", "", "", true); + ConsoleLogData = new ConfigElement("ConsoleLog", "", "", true); } } } diff --git a/src/Core/Input/CursorUnlocker.cs b/src/Core/Input/CursorUnlocker.cs index f8f9e69..2e0f558 100644 --- a/src/Core/Input/CursorUnlocker.cs +++ b/src/Core/Input/CursorUnlocker.cs @@ -45,7 +45,7 @@ namespace UnityExplorer.Core.Input Unlock = ConfigManager.Force_Unlock_Mouse.Value; ConfigManager.Force_Unlock_Mouse.OnValueChanged += (bool val) => { Unlock = val; }; - if (ConfigManager.Aggressive_Force_Unlock.Value) + if (ConfigManager.Aggressive_Mouse_Unlock.Value) SetupAggressiveUnlock(); } diff --git a/src/Core/Reflection/Il2CppReflection.cs b/src/Core/Reflection/Il2CppReflection.cs index a897253..f8aeaf0 100644 --- a/src/Core/Reflection/Il2CppReflection.cs +++ b/src/Core/Reflection/Il2CppReflection.cs @@ -55,6 +55,7 @@ namespace UnityExplorer #endregion + #region Deobfuscation cache private static readonly Dictionary DeobfuscatedTypes = new Dictionary(); @@ -101,6 +102,7 @@ namespace UnityExplorer #endregion + // Get type by name internal override Type Internal_GetTypeByName(string fullName) @@ -172,6 +174,7 @@ namespace UnityExplorer #endregion + #region Casting private static readonly Dictionary cppClassPointers = new Dictionary(); @@ -183,6 +186,9 @@ namespace UnityExplorer var type = obj.GetType(); + if (type == castTo) + return obj; + // from structs if (type.IsValueType) { @@ -251,6 +257,7 @@ namespace UnityExplorer #endregion + #region Boxing and unboxing ValueTypes // cached il2cpp unbox methods @@ -356,7 +363,8 @@ namespace UnityExplorer #endregion - #region Strings + + #region String boxing/unboxing private const string IL2CPP_STRING_FULLNAME = "Il2CppSystem.String"; private const string STRING_FULLNAME = "System.String"; @@ -400,6 +408,7 @@ namespace UnityExplorer #endregion + #region Singleton finder internal override void Internal_FindSingleton(string[] possibleNames, Type type, BF flags, List instances) @@ -425,8 +434,7 @@ namespace UnityExplorer #endregion - - #region FORCE LOADING GAME MODULES + #region Force-loading game modules // Helper for IL2CPP to try to make sure the Unhollowed game assemblies are actually loaded. @@ -471,8 +479,153 @@ namespace UnityExplorer + #region Il2cpp reflection blacklist + public override string DefaultReflectionBlacklist => string.Join(";", defaultIl2CppBlacklist); + // These methods currently cause a crash in most il2cpp games, + // even from doing "GetParameters()" on the MemberInfo. + // Blacklisting until the issue is fixed in Unhollower. + public static HashSet defaultIl2CppBlacklist = new HashSet + { + // These were deprecated a long time ago, still show up in some IL2CPP games for some reason + "UnityEngine.MonoBehaviour.allowPrefabModeInPlayMode", + "UnityEngine.MonoBehaviour.runInEditMode", + "UnityEngine.Component.animation", + "UnityEngine.Component.audio", + "UnityEngine.Component.camera", + "UnityEngine.Component.collider", + "UnityEngine.Component.collider2D", + "UnityEngine.Component.constantForce", + "UnityEngine.Component.hingeJoint", + "UnityEngine.Component.light", + "UnityEngine.Component.networkView", + "UnityEngine.Component.particleSystem", + "UnityEngine.Component.renderer", + "UnityEngine.Component.rigidbody", + "UnityEngine.Component.rigidbody2D", + "UnityEngine.Light.flare", + // These can cause a crash in IL2CPP + "Il2CppSystem.Type.DeclaringMethod", + "Il2CppSystem.RuntimeType.DeclaringMethod", + "Unity.Jobs.LowLevel.Unsafe.JobsUtility.CreateJobReflectionData", + "Unity.Profiling.ProfilerRecorder.CopyTo", + "Unity.Profiling.ProfilerRecorder.StartNew", + "UnityEngine.Analytics.Analytics.RegisterEvent", + "UnityEngine.Analytics.Analytics.SendEvent", + "UnityEngine.Analytics.ContinuousEvent+ConfigureEventDelegate.Invoke", + "UnityEngine.Analytics.ContinuousEvent.ConfigureEvent", + "UnityEngine.Animations.AnimationLayerMixerPlayable.Create", + "UnityEngine.Animations.AnimationLayerMixerPlayable.CreateHandle", + "UnityEngine.Animations.AnimationMixerPlayable.Create", + "UnityEngine.Animations.AnimationMixerPlayable.CreateHandle", + "UnityEngine.AssetBundle.RecompressAssetBundleAsync", + "UnityEngine.Audio.AudioMixerPlayable.Create", + "UnityEngine.BoxcastCommand.ScheduleBatch", + "UnityEngine.Camera.CalculateProjectionMatrixFromPhysicalProperties", + "UnityEngine.CapsulecastCommand.ScheduleBatch", + "UnityEngine.Collider2D.Cast", + "UnityEngine.Collider2D.Raycast", + "UnityEngine.ComputeBuffer+BeginBufferWriteDelegate.Invoke", + "UnityEngine.ComputeBuffer+EndBufferWriteDelegate.Invoke", + "UnityEngine.ComputeBuffer.BeginBufferWrite", + "UnityEngine.ComputeBuffer.EndBufferWrite", + "UnityEngine.Cubemap+SetPixelDataImplArrayDelegate.Invoke", + "UnityEngine.Cubemap+SetPixelDataImplDelegate.Invoke", + "UnityEngine.Cubemap.SetPixelDataImpl", + "UnityEngine.Cubemap.SetPixelDataImplArray", + "UnityEngine.CubemapArray+SetPixelDataImplArrayDelegate.Invoke", + "UnityEngine.CubemapArray+SetPixelDataImplDelegate.Invoke", + "UnityEngine.CubemapArray.SetPixelDataImpl", + "UnityEngine.CubemapArray.SetPixelDataImplArray", + "UnityEngine.Experimental.Playables.MaterialEffectPlayable.Create", + "UnityEngine.Experimental.Rendering.RayTracingAccelerationStructure+AddInstanceDelegate.Invoke", + "UnityEngine.Experimental.Rendering.RayTracingAccelerationStructure+AddInstance_Procedural_InjectedDelegate.Invoke", + "UnityEngine.Experimental.Rendering.RayTracingAccelerationStructure.AddInstance", + "UnityEngine.Experimental.Rendering.RayTracingAccelerationStructure.AddInstance_Procedural", + "UnityEngine.Experimental.Rendering.RayTracingAccelerationStructure.AddInstance_Procedural_Injected", + "UnityEngine.Experimental.Rendering.RayTracingShader+DispatchDelegate.Invoke", + "UnityEngine.Experimental.Rendering.RayTracingShader.Dispatch", + "UnityEngine.Experimental.Rendering.RenderPassAttachment.Clear", + "UnityEngine.GUI.DoButtonGrid", + "UnityEngine.GUI.Slider", + "UnityEngine.GUI.Toolbar", + "UnityEngine.Graphics.DrawMeshInstancedIndirect", + "UnityEngine.Graphics.DrawMeshInstancedProcedural", + "UnityEngine.Graphics.DrawProcedural", + "UnityEngine.Graphics.DrawProceduralIndirect", + "UnityEngine.Graphics.DrawProceduralIndirectNow", + "UnityEngine.Graphics.DrawProceduralNow", + "UnityEngine.LineRenderer+BakeMeshDelegate.Invoke", + "UnityEngine.LineRenderer.BakeMesh", + "UnityEngine.Mesh.GetIndices", + "UnityEngine.Mesh.GetTriangles", + "UnityEngine.Mesh.SetIndices", + "UnityEngine.Mesh.SetTriangles", + "UnityEngine.Physics2D.BoxCast", + "UnityEngine.Physics2D.CapsuleCast", + "UnityEngine.Physics2D.CircleCast", + "UnityEngine.PhysicsScene.BoxCast", + "UnityEngine.PhysicsScene.CapsuleCast", + "UnityEngine.PhysicsScene.OverlapBox", + "UnityEngine.PhysicsScene.OverlapCapsule", + "UnityEngine.PhysicsScene.SphereCast", + "UnityEngine.PhysicsScene2D.BoxCast", + "UnityEngine.PhysicsScene2D.CapsuleCast", + "UnityEngine.PhysicsScene2D.CircleCast", + "UnityEngine.PhysicsScene2D.GetRayIntersection", + "UnityEngine.PhysicsScene2D.Linecast", + "UnityEngine.PhysicsScene2D.OverlapArea", + "UnityEngine.PhysicsScene2D.OverlapBox", + "UnityEngine.PhysicsScene2D.OverlapCapsule", + "UnityEngine.PhysicsScene2D.OverlapCircle", + "UnityEngine.PhysicsScene2D.OverlapCollider", + "UnityEngine.PhysicsScene2D.OverlapPoint", + "UnityEngine.PhysicsScene2D.Raycast", + "UnityEngine.Playables.Playable.Create", + "UnityEngine.Profiling.CustomSampler.Create", + "UnityEngine.RaycastCommand.ScheduleBatch", + "UnityEngine.RemoteConfigSettings+QueueConfigDelegate.Invoke", + "UnityEngine.RemoteConfigSettings.QueueConfig", + "UnityEngine.RenderTexture.GetTemporaryImpl", + "UnityEngine.Rendering.AsyncGPUReadback.Request", + "UnityEngine.Rendering.AttachmentDescriptor.ConfigureClear", + "UnityEngine.Rendering.BatchRendererGroup+AddBatch_InjectedDelegate.Invoke", + "UnityEngine.Rendering.BatchRendererGroup.AddBatch", + "UnityEngine.Rendering.BatchRendererGroup.AddBatch_Injected", + "UnityEngine.Rendering.CommandBuffer+Internal_DispatchRaysDelegate.Invoke", + "UnityEngine.Rendering.CommandBuffer.DispatchRays", + "UnityEngine.Rendering.CommandBuffer.DrawMeshInstancedProcedural", + "UnityEngine.Rendering.CommandBuffer.Internal_DispatchRays", + "UnityEngine.Rendering.CommandBuffer.ResolveAntiAliasedSurface", + "UnityEngine.Rendering.ScriptableRenderContext.BeginRenderPass", + "UnityEngine.Rendering.ScriptableRenderContext.BeginScopedRenderPass", + "UnityEngine.Rendering.ScriptableRenderContext.BeginScopedSubPass", + "UnityEngine.Rendering.ScriptableRenderContext.BeginSubPass", + "UnityEngine.Rendering.ScriptableRenderContext.SetupCameraProperties", + "UnityEngine.Rigidbody2D.Cast", + "UnityEngine.Scripting.GarbageCollector+CollectIncrementalDelegate.Invoke", + "UnityEngine.Scripting.GarbageCollector.CollectIncremental", + "UnityEngine.SpherecastCommand.ScheduleBatch", + "UnityEngine.Texture2D+SetPixelDataImplArrayDelegate.Invoke", + "UnityEngine.Texture2D+SetPixelDataImplDelegate.Invoke", + "UnityEngine.Texture2D.SetPixelDataImpl", + "UnityEngine.Texture2D.SetPixelDataImplArray", + "UnityEngine.Texture2DArray+SetPixelDataImplArrayDelegate.Invoke", + "UnityEngine.Texture2DArray+SetPixelDataImplDelegate.Invoke", + "UnityEngine.Texture2DArray.SetPixelDataImpl", + "UnityEngine.Texture2DArray.SetPixelDataImplArray", + "UnityEngine.Texture3D+SetPixelDataImplArrayDelegate.Invoke", + "UnityEngine.Texture3D+SetPixelDataImplDelegate.Invoke", + "UnityEngine.Texture3D.SetPixelDataImpl", + "UnityEngine.Texture3D.SetPixelDataImplArray", + "UnityEngine.TrailRenderer+BakeMeshDelegate.Invoke", + "UnityEngine.TrailRenderer.BakeMesh", + "UnityEngine.WWW.LoadFromCacheOrDownload", + "UnityEngine.XR.InputDevice.SendHapticImpulse", + }; + + #endregion } } diff --git a/src/Core/Reflection/ReflectionUtility.cs b/src/Core/Reflection/ReflectionUtility.cs index 915c59b..0ce235a 100644 --- a/src/Core/Reflection/ReflectionUtility.cs +++ b/src/Core/Reflection/ReflectionUtility.cs @@ -8,15 +8,13 @@ using BF = System.Reflection.BindingFlags; using UnityExplorer.Core.Runtime; using System.Text; using UnityEngine; +using UnityExplorer.Core.Config; namespace UnityExplorer { public class ReflectionUtility { - // The Instance and instance methods are not for public use, they're only so IL2CPP can override. - // This class and the Extensions class expose static methods to use instead. - public const BF FLAGS = BF.Public | BF.Instance | BF.NonPublic | BF.Static; internal static readonly ReflectionUtility Instance = @@ -37,10 +35,9 @@ namespace UnityExplorer /// Key: Type.FullName public static readonly SortedDictionary AllTypes = new SortedDictionary(StringComparer.OrdinalIgnoreCase); - //private static readonly SortedSet allTypeNames = new SortedSet(StringComparer.OrdinalIgnoreCase); private static string[] allTypesArray; - private static string[] GetTypeNameArray() + public static string[] GetTypeNameArray() { if (allTypesArray == null || allTypesArray.Length != AllTypes.Count) { @@ -403,7 +400,45 @@ namespace UnityExplorer } } -#endregion + #endregion + + #region Reflection Blacklist + + public virtual string DefaultReflectionBlacklist => string.Empty; + + public static void LoadBlacklistString(string blacklist) + { + if (string.Equals(blacklist, "DEFAULT", StringComparison.InvariantCultureIgnoreCase)) + blacklist = Instance.DefaultReflectionBlacklist; + + if (string.IsNullOrEmpty(blacklist)) + return; + + var sigs = blacklist.Split(';'); + foreach (var sig in sigs) + { + var s = sig.Trim(); + if (string.IsNullOrEmpty(s)) + continue; + if (!currentBlacklist.Contains(s)) + currentBlacklist.Add(s); + } + + Mono.CSharp.IL2CPP.Blacklist.SignatureBlacklist = currentBlacklist; + } + + public static bool IsBlacklisted(MemberInfo member) + { + if (string.IsNullOrEmpty(member.DeclaringType?.Namespace)) + return false; + + var sig = $"{member.DeclaringType.FullName}.{member.Name}"; + return currentBlacklist.Contains(sig); + } + + private static readonly HashSet currentBlacklist = new HashSet(); + + #endregion } } diff --git a/src/Loader/ML/MelonLoaderConfigHandler.cs b/src/Loader/ML/MelonLoaderConfigHandler.cs index 0fa3db2..21eeab4 100644 --- a/src/Loader/ML/MelonLoaderConfigHandler.cs +++ b/src/Loader/ML/MelonLoaderConfigHandler.cs @@ -18,7 +18,7 @@ namespace UnityExplorer.Loader.ML public override void Init() { - prefCategory = MelonPreferences.CreateCategory(CTG_NAME, $"{CTG_NAME} Settings"); + prefCategory = MelonPreferences.CreateCategory(CTG_NAME, $"{CTG_NAME} Settings", false, true); } public override void LoadConfig() @@ -36,7 +36,7 @@ namespace UnityExplorer.Loader.ML public override void RegisterConfigElement(ConfigElement config) { - var entry = prefCategory.CreateEntry(config.Name, config.Value, null, config.IsInternal) as MelonPreferences_Entry; + var entry = prefCategory.CreateEntry(config.Name, config.Value, null, config.Description, config.IsInternal, false); entry.OnValueChangedUntyped += () => { diff --git a/src/UI/CacheObject/CacheMember.cs b/src/UI/CacheObject/CacheMember.cs index 600a7ba..411c31f 100644 --- a/src/UI/CacheObject/CacheMember.cs +++ b/src/UI/CacheObject/CacheMember.cs @@ -237,15 +237,15 @@ namespace UnityExplorer.UI.CacheObject } private static void TryCacheMember(MemberInfo member, List list, HashSet cachedSigs, - Type declaringType, ReflectionInspector _inspector, bool ignoreMethodBlacklist = false) + Type declaringType, ReflectionInspector _inspector, bool ignorePropertyMethodInfos = true) { try { - var sig = GetSig(member); - - if (IsBlacklisted(sig)) + if (ReflectionUtility.IsBlacklisted(member)) return; + var sig = GetSig(member); + //ExplorerCore.Log($"Trying to cache member {sig}..."); //ExplorerCore.Log(member.DeclaringType.FullName + "." + member.Name); @@ -256,14 +256,15 @@ namespace UnityExplorer.UI.CacheObject case MemberTypes.Method: { var mi = member as MethodInfo; - if (!ignoreMethodBlacklist && IsBlacklisted(mi)) + if (!ignorePropertyMethodInfos + && (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_"))) return; var args = mi.GetParameters(); if (!CanParseArgs(args)) return; - sig += AppendArgsToSig(args); + sig += GetArgumentString(args); if (cachedSigs.Contains(sig)) return; @@ -285,11 +286,11 @@ namespace UnityExplorer.UI.CacheObject // write-only property, cache the set method instead. var setMethod = pi.GetSetMethod(true); if (setMethod != null) - TryCacheMember(setMethod, list, cachedSigs, declaringType, _inspector, true); + TryCacheMember(setMethod, list, cachedSigs, declaringType, _inspector, false); return; } - sig += AppendArgsToSig(args); + sig += GetArgumentString(args); if (cachedSigs.Contains(sig)) return; @@ -326,55 +327,23 @@ namespace UnityExplorer.UI.CacheObject internal static string GetSig(MemberInfo member) => $"{member.DeclaringType.Name}.{member.Name}"; - internal static string AppendArgsToSig(ParameterInfo[] args) + internal static string GetArgumentString(ParameterInfo[] args) { - string ret = " ("; + var sb = new StringBuilder(); + sb.Append(' '); + sb.Append('('); foreach (var param in args) - ret += $"{param.ParameterType.Name} {param.Name}, "; - ret += ")"; - return ret; + { + sb.Append(param.ParameterType.Name); + sb.Append(' '); + sb.Append(param.Name); + sb.Append(','); + sb.Append(' '); + } + sb.Append(')'); + return sb.ToString(); } - // Blacklists - private static readonly HashSet bl_typeAndMember = new HashSet - { - // these can cause a crash in IL2CPP -#if CPP - "Type.DeclaringMethod", - "Rigidbody2D.Cast", - "Collider2D.Cast", - "Collider2D.Raycast", - "Texture2D.SetPixelDataImpl", - "Camera.CalculateProjectionMatrixFromPhysicalProperties", -#endif - // These were deprecated a long time ago, still show up in some games for some reason - "MonoBehaviour.allowPrefabModeInPlayMode", - "MonoBehaviour.runInEditMode", - "Component.animation", - "Component.audio", - "Component.camera", - "Component.collider", - "Component.collider2D", - "Component.constantForce", - "Component.hingeJoint", - "Component.light", - "Component.networkView", - "Component.particleSystem", - "Component.renderer", - "Component.rigidbody", - "Component.rigidbody2D", - "Light.flare", - }; - private static readonly HashSet bl_methodNameStartsWith = new HashSet - { - // these are redundant - "get_", - "set_", - }; - - internal static bool IsBlacklisted(string sig) => bl_typeAndMember.Any(it => sig.Contains(it)); - internal static bool IsBlacklisted(MethodInfo method) => bl_methodNameStartsWith.Any(it => method.Name.StartsWith(it)); - #endregion