diff --git a/src/CSharpExtension.cs b/src/CSharpExtension.cs index 171c29c..e63e57c 100644 --- a/src/CSharpExtension.cs +++ b/src/CSharpExtension.cs @@ -43,9 +43,6 @@ public class CSharpExtension : Extension // The .cs plugin loader private CSharpPluginLoader loader; - // Is the sandbox enabled? (always default to true) - public static bool SandboxEnabled { get; private set; } = true; - /// /// Initializes a new instance of the CSharpExtension class /// @@ -75,12 +72,6 @@ public override void Load() // Register engine frame callback Interface.Oxide.OnFrame(OnFrame); - - if (File.Exists(Path.Combine(Interface.Oxide.ExtensionDirectory, "oxide.disable-sandbox"))) - { - Interface.Oxide.LogWarning($"{Path.Combine(Interface.Oxide.ExtensionDirectory, "oxide.disable-sandbox")} found, disabling security sandbox. Potentially dangerous APIs and methods are now allowed inside plugins."); - CSharpExtension.SandboxEnabled = false; - } } /// diff --git a/src/CSharpPlugin.cs b/src/CSharpPlugin.cs index da9d559..df23e19 100644 --- a/src/CSharpPlugin.cs +++ b/src/CSharpPlugin.cs @@ -385,7 +385,7 @@ protected override object InvokeMethod(HookMethod method, object[] args) CompilablePlugin compilablePlugin = CSharpPluginLoader.GetCompilablePlugin(Interface.Oxide.PluginDirectory, Name); if (compilablePlugin?.CompiledAssembly != null) { - File.WriteAllBytes(Interface.Oxide.PluginDirectory + "\\" + Name + ".dump", compilablePlugin.CompiledAssembly.PatchedAssembly); + File.WriteAllBytes(Interface.Oxide.PluginDirectory + "\\" + Name + ".dump", compilablePlugin.CompiledAssembly.RawAssembly); Interface.Oxide.LogWarning($"The invalid raw assembly has been dumped to Plugins/{Name}.dump"); } hookDispatchFallback = true; @@ -475,9 +475,9 @@ protected void LogToFile(string filename, string text, Plugin plugin, bool dated lock (_logFileLock) { using (FileStream file = new FileStream(Path.Combine(path, Utility.CleanPath(filename)), FileMode.Append, FileAccess.Write, FileShare.Read)) - using (StreamWriter writer = new StreamWriter(file, Encoding.Unicode)) + using (StreamWriter writer = new StreamWriter(file, Encoding.UTF8)) { - writer.Write(timestampPrefix ? $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {text}" : text); + writer.WriteLine(timestampPrefix ? $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {text}" : text); } } } diff --git a/src/Compilation.cs b/src/Compilation.cs index a53637b..2a95b50 100644 --- a/src/Compilation.cs +++ b/src/Compilation.cs @@ -272,8 +272,7 @@ private void PreparseScript(CompilablePlugin plugin) if (match.Success) { string result = match.Groups[1].Value; - if (!result.StartsWith("Oxide.") && !result.Contains("Newtonsoft.Json")&& !result.Contains("protobuf-net") - || !CSharpExtension.SandboxEnabled && !result.Contains("Harmony")) + if (!result.StartsWith("Oxide.") && !result.Contains("Newtonsoft.Json") && !result.Contains("protobuf-net")) { AddReference(plugin, result); Interface.Oxide.LogInfo("Added '// Reference: {0}' in plugin '{1}'", result, plugin.Name); diff --git a/src/CompiledAssembly.cs b/src/CompiledAssembly.cs index 3bf5588..ab1cf91 100644 --- a/src/CompiledAssembly.cs +++ b/src/CompiledAssembly.cs @@ -3,8 +3,6 @@ using Oxide.Core; using Oxide.Core.CSharp; using References::Mono.Cecil; -using References::Mono.Cecil.Cil; -using References::Mono.Cecil.Rocks; using System; using System.Collections.Generic; using System.IO; @@ -12,9 +10,6 @@ using System.Reflection; using System.Threading; -using MethodAttributes = References::Mono.Cecil.MethodAttributes; -using MethodBody = References::Mono.Cecil.Cil.MethodBody; - namespace Oxide.Plugins { public class CompiledAssembly @@ -34,17 +29,6 @@ public class CompiledAssembly private bool isPatching; private bool isLoaded; - private static IEnumerable BlacklistedNamespaces => new[] { - "Harmony", "Mono.CSharp", "Mono.Cecil", "Oxide.Core.ServerConsole", "ServerFileSystem", "System.IO", "System.Net", "System.Xml", "System.Reflection.Assembly", - "System.Reflection.Emit", "System.Threading", "System.Runtime.InteropServices", "System.Diagnostics", "System.Security", "System.Timers" - }; - - private static IEnumerable WhitelistedNamespaces => new[] { - "System.Diagnostics.Stopwatch", "System.IO.MemoryStream", "System.IO.Stream", "System.IO.BinaryReader", "System.IO.BinaryWriter", "System.IO.StringReader", - "System.IO.StringWriter", "System.Net.Dns", "System.Net.Dns.GetHostEntry", "System.Net.IPAddress", "System.Net.IPEndPoint", "System.Net.NetworkInformation", - "System.Net.Sockets.SocketFlags", "System.Security.Cryptography", "System.Threading.Interlocked", "System.Threading.Monitor" - }; - public CompiledAssembly(string name, CompilablePlugin[] plugins, byte[] rawAssembly, float duration) { Name = name; @@ -69,7 +53,7 @@ public void LoadAssembly(Action callback) return; } - PatchAssembly(rawAssembly => + ValidateAssembly(rawAssembly => { if (rawAssembly == null) { @@ -97,17 +81,14 @@ public void LoadAssembly(Action callback) }); } - private void PatchAssembly(Action callback) + private void ValidateAssembly(Action callback) { if (isPatching) { Interface.Oxide.LogWarning("Already patching plugin assembly: {0} (ignoring)", PluginNames.ToSentence()); - //RemoteLogger.Warning($"Already patching plugin assembly: {PluginNames.ToSentence()}"); return; } - float startedAt = Interface.Oxide.Now; - isPatching = true; ThreadPool.QueueUserWorkItem(_ => { @@ -119,129 +100,8 @@ private void PatchAssembly(Action callback) definition = AssemblyDefinition.ReadAssembly(stream); } - ConstructorInfo exceptionConstructor = typeof(UnauthorizedAccessException).GetConstructor(new[] { typeof(string) }); - MethodReference securityException = definition.MainModule.Import(exceptionConstructor); - - Action patchModuleType = null; - patchModuleType = type => - { - foreach (MethodDefinition method in type.Methods) - { - bool changedMethod = false; - - if (method.Body == null) - { - if (method.HasPInvokeInfo) - { - method.Attributes &= ~MethodAttributes.PInvokeImpl; - MethodBody body = new MethodBody(method); - body.Instructions.Add(Instruction.Create(OpCodes.Ldstr, "PInvoke access is restricted, you are not allowed to use PInvoke")); - body.Instructions.Add(Instruction.Create(OpCodes.Newobj, securityException)); - body.Instructions.Add(Instruction.Create(OpCodes.Throw)); - method.Body = body; - } - } - else - { - bool replacedMethod = false; - foreach (VariableDefinition variable in method.Body.Variables) - { - if (!IsNamespaceBlacklisted(variable.VariableType.FullName)) - { - continue; - } - - MethodBody body = new MethodBody(method); - body.Instructions.Add(Instruction.Create(OpCodes.Ldstr, $"System access is restricted, you are not allowed to use {variable.VariableType.FullName}")); - body.Instructions.Add(Instruction.Create(OpCodes.Newobj, securityException)); - body.Instructions.Add(Instruction.Create(OpCodes.Throw)); - method.Body = body; - replacedMethod = true; - break; - } - if (replacedMethod) - { - continue; - } - - References::Mono.Collections.Generic.Collection instructions = method.Body.Instructions; - ILProcessor ilProcessor = method.Body.GetILProcessor(); - Instruction first = instructions.First(); - - int i = 0; - while (i < instructions.Count && !changedMethod) - { - Instruction instruction = instructions[i]; - if (instruction.OpCode == OpCodes.Ldtoken) - { - IMetadataTokenProvider operand = instruction.Operand as IMetadataTokenProvider; - string token = operand?.ToString(); - if (IsNamespaceBlacklisted(token)) - { - ilProcessor.InsertBefore(first, Instruction.Create(OpCodes.Ldstr, $"System access is restricted, you are not allowed to use {token}")); - ilProcessor.InsertBefore(first, Instruction.Create(OpCodes.Newobj, securityException)); - ilProcessor.InsertBefore(first, Instruction.Create(OpCodes.Throw)); - changedMethod = true; - } - } - else if (instruction.OpCode == OpCodes.Call || instruction.OpCode == OpCodes.Calli || instruction.OpCode == OpCodes.Callvirt || instruction.OpCode == OpCodes.Ldftn || instruction.OpCode == OpCodes.Newobj) - { - MethodReference methodCall = instruction.Operand as MethodReference; - string fullNamespace = methodCall?.DeclaringType.FullName; - - if ((fullNamespace == "System.Type" && methodCall.Name == "GetType") || IsNamespaceBlacklisted(fullNamespace)) - { - ilProcessor.InsertBefore(first, Instruction.Create(OpCodes.Ldstr, $"System access is restricted, you are not allowed to use {fullNamespace}")); - ilProcessor.InsertBefore(first, Instruction.Create(OpCodes.Newobj, securityException)); - ilProcessor.InsertBefore(first, Instruction.Create(OpCodes.Throw)); - changedMethod = true; - } - } - else if (instruction.OpCode == OpCodes.Ldfld) - { - FieldReference fieldType = instruction.Operand as FieldReference; - string fullNamespace = fieldType?.FieldType.FullName; - if (IsNamespaceBlacklisted(fullNamespace)) - { - ilProcessor.InsertBefore(first, Instruction.Create(OpCodes.Ldstr, $"System access is restricted, you are not allowed to use {fullNamespace}")); - ilProcessor.InsertBefore(first, Instruction.Create(OpCodes.Newobj, securityException)); - ilProcessor.InsertBefore(first, Instruction.Create(OpCodes.Throw)); - changedMethod = true; - } - } - i++; - } - } - - if (changedMethod) - { - method.Body?.OptimizeMacros(); - /*//Interface.Oxide.LogDebug("Updating {0} instruction offsets: {1}", instructions.Count, method.FullName); - int curoffset = 0; - for (var i = 0; i < instructions.Count; i++) - { - var instruction = instructions[i]; - instruction.Previous = (i == 0) ? null : instructions[i - 1]; - instruction.Next = (i == instructions.Count - 1) ? null : instructions[i + 1]; - instruction.Offset = curoffset; - curoffset += instruction.GetSize(); - //Interface.Oxide.LogDebug(" {0}", instruction.ToString()); - }*/ - } - } - foreach (TypeDefinition nestedType in type.NestedTypes) - { - patchModuleType(nestedType); - } - }; - foreach (TypeDefinition type in definition.MainModule.Types) { - if (CSharpExtension.SandboxEnabled) - { - patchModuleType(type); - } - if (IsCompilerGenerated(type)) { continue; @@ -270,9 +130,8 @@ private void PatchAssembly(Action callback) else { Interface.Oxide.LogWarning(PluginNames.Length == 1 - ? $"{PluginNames[0]} has polluted the global namespace by defining {type.Name}" - : $"A plugin has polluted the global namespace by defining {type.Name}"); - //RemoteLogger.Info($"A plugin has polluted the global namespace by defining {type.Name}: {PluginNames.ToSentence()}"); + ? $"{PluginNames[0]} has polluted the global namespace by defining {type.Name}" + : $"A plugin has polluted the global namespace by defining {type.Name}"); } } else if (type.FullName != "") @@ -280,28 +139,8 @@ private void PatchAssembly(Action callback) if (!PluginNames.Any(plugin => type.FullName.StartsWith($"Oxide.Plugins.{plugin}"))) { Interface.Oxide.LogWarning(PluginNames.Length == 1 - ? $"{PluginNames[0]} has polluted the global namespace by defining {type.FullName}" - : $"A plugin has polluted the global namespace by defining {type.FullName}"); - } - } - } - - // TODO: Why is there no error on boot using this? - foreach (TypeDefinition type in definition.MainModule.Types) - { - if (type.Namespace != "Oxide.Plugins" || !PluginNames.Contains(type.Name)) - { - continue; - } - - foreach (MethodDefinition m in type.Methods.Where(m => !m.IsStatic && !m.HasGenericParameters && !m.ReturnType.IsGenericParameter && !m.IsSetter && !m.IsGetter)) - { - foreach (ParameterDefinition parameter in m.Parameters) - { - foreach (CustomAttribute attribute in parameter.CustomAttributes) - { - //Interface.Oxide.LogInfo($"{m.FullName} - {parameter.Name} - {attribute.Constructor.FullName}"); - } + ? $"{PluginNames[0]} has polluted the global namespace by defining {type.FullName}" + : $"A plugin has polluted the global namespace by defining {type.FullName}"); } } } @@ -315,7 +154,6 @@ private void PatchAssembly(Action callback) Interface.Oxide.NextTick(() => { isPatching = false; - //Interface.Oxide.LogDebug("Patching {0} assembly took {1:0.00} ms", ScriptName, Interface.Oxide.Now - startedAt); callback(PatchedAssembly); }); } @@ -325,7 +163,6 @@ private void PatchAssembly(Action callback) { isPatching = false; Interface.Oxide.LogException($"Exception while patching: {PluginNames.ToSentence()}", ex); - //RemoteLogger.Exception($"Exception while patching: {PluginNames.ToSentence()}", ex); callback(null); }); } @@ -335,24 +172,5 @@ private void PatchAssembly(Action callback) public bool IsOutdated() => CompilablePlugins.Any(pl => pl.GetLastModificationTime() != CompiledAt); private bool IsCompilerGenerated(TypeDefinition type) => type.CustomAttributes.Any(attr => attr.Constructor.DeclaringType.ToString().Contains("CompilerGeneratedAttribute")); - - private static bool IsNamespaceBlacklisted(string fullNamespace) - { - foreach (string namespaceName in BlacklistedNamespaces) - { - if (!fullNamespace.StartsWith(namespaceName)) - { - continue; - } - - if (WhitelistedNamespaces.Any(fullNamespace.StartsWith)) - { - continue; - } - - return true; - } - return false; - } } }