如何保护我的私有函数不被反射执行?

时间:2011-12-02 14:00:53

标签: c# .net reflection

看到这个后:Do access modifiers affect reflection also?

我尝试过使用它,但它不起作用: enter image description here

如何阻止反射执行我的私有方法?

10 个答案:

答案 0 :(得分:25)

如果有人当前可以对您的私人方法使用反射,那么他们已经有足够的权限来回避您放置的任何其他方式。以较少的信任运行可能是一种选择,但这只是为了防止插件等访问过多 - 它不会阻止用户(例如)管理员访问该框,他只能提升访问权限。

如果您不希望代码运行,请不要将其置于恶意用户的物理范围内;保持网络服务或类似。用户可用的任何代码都可以直接使用,也可以通过反编译间接使用(如果需要,可以对其进行反混淆处理)。你可以使用一些技巧来阻止他们(通过堆栈跟踪检查调用者等),但这不会阻止某人确定。

答案 1 :(得分:24)

  

如何保护我的私有func免受反射执行?

您可以更改安全策略,以便代码在运行时无权执行“私人反射”。

当然,这只会影响您的计算机。如果您想影响其他人的计算机,向其计算机管理员发送电子邮件并要求管理员更改用户的安全策略,以使其无权进行“私人反映”。那是拥有机器及其运行的网络的人;显然,您无法更改您不拥有的网络上的安全设置。

当然注意,权利比私人反射更强大也必须受到限制。设置一个政策说没有好处,例如“私人反映权被拒绝,但授予改变安全政策的权利”。然后,用户可以只更改安全策略以重新授予对自己的私有反射。

您还必须限制访问磁盘的能力。可以访问磁盘的人可以简单地从程序集中读取代码,将私有元数据更改为public,并正常加载程序集。

因此,您的任务是说服全世界的所有机器管理员不允许其用户访问自己的磁盘。我怀疑你会不会成功;我建议您找到一种不同的方法来保护您的功能免受滥用。

答案 2 :(得分:18)

这是一个迟到的答案,但我认为这是一个更新,因为所有答案都是在2015年中期发布.NET 4.6之前编写的,它引入了一个名为DisablePrivateReflection的新程序集属性。

如果在AssemblyInfo.cs中标记了该属性,则可以禁用对该程序集的私有成员的反射。

示例:

namespace DisablingPrivateReflection
{
    public class Singleton
    {
        private Singleton()
        {

        }
    }
}

AssemblyInfo.cs添加此行:

[assembly: DisablePrivateReflection]

然后在引用上述程序集的客户端程序集中,此代码将在运行时失败:

var singleton = Activator.CreateInstance(typeof(Singleton), true);

抛出的异常是MethodAccessException类型,带有消息:

  

尝试通过方法'Program.Main(System.String [])'来访问方法   'DisablingPrivateReflection.Singleton..ctor()'失败。

答案 3 :(得分:8)

访问修饰符不是安全机制。

如果 if ,你可能会阻止通过反射调用你的函数,用户可以只反编译程序,提取该函数,将它放入一个新的程序集中并执行它。

答案 4 :(得分:1)

没有办法做到这一点。为什么要阻止执行私有函数?通常,如果有人使用反射,他就知道自己在做什么。

答案 5 :(得分:1)

您不能,修饰符仅供开发人员进行适当的封装。在运行时,一切都处于同一水平可能会让人感到难过。

反射机制通常由需要调用某些预配置方法(旧ORM)或显示它们(IDE)的应用程序使用。如果这种机制无法做到这一点,那将非常困难。

答案 6 :(得分:1)

虽然我完全同意访问修饰符不是安全功能的想法,但仅仅是为了编程我一直在考虑这个问题,而且我有一个简单的,没有多少用处的机制,反思反思用反思: - )

请注意,这只是一个愚蠢的概念证明,我没有考虑通用方法,它需要改变...

这个想法是,在你要保护的每个私有方法的开头被“非法”调用时,你只需通过Reflection检查你是从该类中的另一个方法调用的,而不是从外部调用的。所以你会用: new StackTrace()。GetFrame(1)。GetMethod(); 获取调用者的MethodBase,并将其与您的类的MethodInfos列表进行比较。

你可以把它添加到一个帮助类中(无论如何你需要一个IEqualityComparer来比较MethodBases ......

一个问题是你也会阻止一些正确的调用,例如通过委托调用它或通过另一个方法调用反射......所以你在使用时应该小心。

您可以执行here

答案 7 :(得分:1)

using System;
using System.Reflection;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;

class Person
{
    private string SayInternalSecure()
    {
         if (!PrivacyHelper.IsInvocationAllowed<Person>())
            throw new Exception("you can't invoke this private method");
         return "Internal Secure";
    }

    private string SayInternal()
    {
         return "Internal";
    }

    public string SaySomething()
    {
         return "Hi " + this.SayInternal();
    }

    public string SaySomethingSecure()
    {
        return "Hi " + this.SayInternalSecure();
    }

    public void BeingCalledBy()
    {
            Console.WriteLine("I'm being called by: " + new StackTrace().GetFrame(1).GetMethod().Name);
    }
}

public class MethodBaseComparer : IEqualityComparer<MethodBase> 
{
     private string GetMethodIdentifier(MethodBase mb)
     {
      return mb.Name + ":" + String.Join(";", mb.GetParameters().Select(paramInfo=>paramInfo.Name).ToArray());
     }

     public bool Equals(MethodBase m1, MethodBase m2) 
     {
        //we need something more here, comparing just by name is not enough, need to take parameters into account
        return this.GetMethodIdentifier(m1) == this.GetMethodIdentifier(m2);
     }

     public int GetHashCode(MethodBase mb) 
     {
             return this.GetMethodIdentifier(mb).GetHashCode();
     }
}

class PrivacyHelper
{
static Dictionary<Type, MethodBase[]> cache = new Dictionary<Type, MethodBase[]>();
public static bool IsInvocationAllowed<T>()
{
    Type curType = typeof(T);
    if (!cache.ContainsKey(curType))
    {
        cache[curType] = curType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).ToArray();
    }
    //StackTrace.GetFrame returns a MethodBase, not a MethodInfo, that's why we're falling back to MethodBody
    MethodBase invoker = new StackTrace().GetFrame(2).GetMethod();
    return cache[curType].Contains(invoker, new MethodBaseComparer());
}
}

public class App
{
public static void CheckCaller()
{
    Person p = new Person();

    Console.WriteLine("- calling via delegate");
    Action action = p.BeingCalledBy;
    action();

    Console.WriteLine("- calling via reflection");
    MethodInfo method = typeof(Person).GetMethod("BeingCalledBy", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
    Console.WriteLine(method.Invoke(p, null));

    Console.WriteLine("- calling via delegate again");
    action = (Action)(Delegate.CreateDelegate(typeof(Action), p, method));
    action();
}

public static void Main()
{
    Console.WriteLine("Press key to run");
    Console.ReadLine();

    CheckCaller();

    Person p = new Person();
    Console.WriteLine(p.SaySomething());
    Console.WriteLine(p.SaySomethingSecure());

    MethodInfo privateMethod = typeof(Person).GetMethod("SayInternal", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
    Console.WriteLine("invoking private method via Reflection:");
    Console.WriteLine(privateMethod.Invoke(p, null));

    Console.WriteLine("----------------------");

    privateMethod = typeof(Person).GetMethod("SayInternalSecure", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
    Console.WriteLine("invoking secured private method via Reflection:");
    try
    {
        Console.WriteLine(privateMethod.Invoke(p, null));
    }
    catch(Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}
}

答案 8 :(得分:0)

我使用 eazfuscator 并且它使方法虚拟似乎在隐藏方法方面做得很好。

然后在方法中,我喜欢保护我将在释放模式下更改代码以提供空指针异常以启动将生成空指针的任务并在没有调用堆栈的代码的情况下执行它。

没有多少开发人员可以在不调试的情况下使用您的生产代码...

#if !DEBUG
    #pragma warning disable CS8618 
    
    [System.Diagnostics.DebuggerStepThrough, System.Diagnostics.DebuggerNonUserCode]
    [System.ComponentModel.Browsable(false)]
    [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
#endif

namespace MyApp.Infrastructure.LicenseManager
{
    [Obfuscation(Feature = "apply to member * when method or constructor: virtualization", Exclude = false)]
    internal sealed class LicenseManager(ISomeClass parameter, ILoggerFactory? factory = null)
    {
       //hide the name space for reverse engineering the naming scheme
       _logger = factory?.CreateLogger("MyApp.LicenseManager") 

    #if !DEBUG
        if (System.Diagnostics.Debugger.IsAttached)
        {
            //send computer information for license violation and prosecution then crash
            System.Threading.ThreadPool.UnsafeQueueUserWorkItem(SystemCrasher.SendAbuseAndCrash);
            
            //send a log entry, of don't if you wish to give no warning
           _logger?.LogWarning(Walter.BOM.LogEvents.FireWall_Warning, "Debugging will cause the developer to generate null pointer exceptions in administration context");
           return;
           
           //all class initiolizers will stay null
         }
    #endif

        _myClass= parameter;

}

#if !DEBUG
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
#endif

如果你的代码被共享 DLL 中的其他代码使用(比如在 NuGet 包中)那么上面的方法会阻止你在同事中流行,你需要一个“调试”友好的方法,使用方法代理可能会与混淆相结合的技巧。

[Obfuscation(Feature = "virtualization", Exclude = false)]
private void MyMethod(int parameter)
{
    MyMethodProxy();
    //do some code
}

private void MyMethodProxy([CallerMemberName] string? calledBy = null)
{
    if (!string.Equals(nameof(MyMethod), calledBy, StringComparison.Ordinal))
    {
        throw new LordOfTheRingsException("You shall not pass!!");
    }
}

通过使用 NameOf 方法,您可以从混淆器的重命名方案中保留不可打印的字符。

我使用了一些其他选项,但这会限制您自动化并使用面向方面的框架,例如 postsharp

希望对您有所帮助enter code here

答案 9 :(得分:-1)