1 Star 0 Fork 1

SesameTechGroup / NearAop

forked from New佳佳 / NearAop 
Create your Gitee Account
Explore and code with more than 6 million developers,Free private repositories !:)
Sign up
This repository doesn't specify license. Without author's permission, this code is only for learning and cannot be used for other purposes.
Clone or download
Cancel
Notice: Creating folder will generate an empty file .keep, because not support in Git
Loading...
README.md

NearAop

简介

自己实现的简单AOP及代码混淆。支持 .NET Framework 4.X , .NET Core 3.0。目前处于测试阶段,请勿在生产环境下使用! 交流群:438789123 ,51492982

Image

安装

Install-Package NearAop
VS菜单: 项目 > 管理 NuGet 程序包 > 输入NearAop
Image

卸载

卸载时需要同时卸载NearAop.Handler,否则编译时出现 AopTask”任务意外失败。

目录

1、 属性变更通知
2 、 属性代理

3 、方法代理

4、接口代理
5、SetProxySourceAttribute特性
6、SetCancelAopAttribute特性
7、AOP调试信息输出

8、代码混淆


1、 属性变更通知

编译时自动实现INotifyPropertyChanged接口。当属性值变更时触发事件来通知处理程序。

类型 用途
AddNotifyPropertyChangedAttribute(特性) 标记要处理的类或者成员属性。
IAopHandlerNotifyPropertyChanged(接口) 用于对外提供触发事件的函数接口。

c#

编译前:
[AddNotifyPropertyChanged]
class NotifyPropertyChangedDemo
{
    public int MyProperty { get; set; }
}

编译后:
[AddNotifyPropertyChanged]
internal class NotifyPropertyChangedDemo : INotifyPropertyChanged, IAopHandlerNotifyPropertyChanged
{
	private int _MyProperty;
	public int MyProperty
	{
		get
		{
			return _MyProperty;
		}
		set
		{
            // 赋值比较可用 AddNotifyPropertyChanged(IsTestEquals = false) 关闭。
			if (_MyProperty != value)
			{                 
				_MyProperty = value;
				IAopHandlerNotifyPropertyChanged.RaiseEvent(this, "MyProperty");
			}
		}
	}
	 //自动添加事件
	public event PropertyChangedEventHandler _nearAop_PropertyChanged;
    //自动添加的RaiseEvent方法。
	void IAopHandlerNotifyPropertyChanged.RaiseEvent(object sender, string propertyName)
	{
		PropertyChanged?.Invoke(sender, new PropertyChangedEventArgs(propertyName));
	}
}

1、由于自动生成了PropertyChanged事件和IAopHandlerNotifyPropertyChanging.RaiseEvent方法,因此在编译前无法使用。如需调用可以自行实现IAopHandlerNotifyPropertyChanging接口,代码实现参考上面的例子,或使用NearAop.AopHelper.RaiseEventPropertyChanged方法。

2、静态成员需要自行添加 PropertyChangedEventHandler静态事件字段。

返回目录

vb.net

编译前:
Imports NearAop
<AddNotifyPropertyChanged>
Public Class NotifyPropertyChangedDemo
    Property MyProperty As Integer
End Class

编译后:
<AddNotifyPropertyChanged>
Public Class NotifyPropertyChangedDemo
    Implements INotifyPropertyChanged, IAopHandlerNotifyPropertyChanged

    Private _MyProperty As Integer
    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    Public Property MyProperty As Integer
        Get
            Return _MyProperty
        End Get
        Set(value As Integer)
                ’赋值比较可用 AddNotifyPropertyChanged(IsTestEquals:=False) 关闭
            If _MyProperty <> value Then
                _MyProperty = value
                [RaiseEvent](Me, "MyProperty")
            End If
        End Set
    End Property

    Sub [RaiseEvent](sender As Object, propertyName As String) Implements IAopHandlerNotifyPropertyChanged.[RaiseEvent]
        RaiseEvent PropertyChanged(sender, New PropertyChangedEventArgs(propertyName))
    End Sub

End Class

返回目录


2、属性代理

编译时自动为属性Get\Set方法植入调用代码。与属性变更通知的差异是可以获取到属性变更前后的值。

特性 对应接口 用途
AddProxyPropertyAttribute IAopHandlerProperty 用于标记要处理的类或属性,等效于同时处理Set\Get方法
AddProxyPropertySetAttribute IAopHandlerPropertySet 用于标记要处理的类或属性,处理Set方法
AddProxyPropertyGetAttribute IAopHandlerPropertyGet 用于标记要处理的类或属性,处理Get方法

返回目录

本地代理

本地代理可以直接实现IAopHandlerPropertyLocal接口,等效下面的代码。

编译前:
[AddProxyProperty]
class ProxyPropertyDemo : IAopHandlerProperty
{
    public int MyProperty { get; set; }

    public void PropertySetHandler<T>(Lazy<AopParamInfo> aopParamInfo, object sender, T value, Func<T> getValue, Action<T> setValue)
    {
        var propName = aopParamInfo.GetMemberName();
        Console.WriteLine($"{sender}.{propName}被修改, 修改前:{getValue()}, 新值:{value}");
        setValue(value);//执行修改
    }

    public T PropertyGetHandler<T>(Lazy<AopParamInfo> aopParamInfo, object sender, Func<T> getValue)
    {
        var propName = aopParamInfo.GetMemberName();
        var result = getValue();
        var msg = $"{sender}.{propName}被读取,值:{result}";
        Console.WriteLine(msg);
        return result;
    }
}

编译后:
[AddProxyProperty]
internal class ProxyPropertyDemo : IAopHandlerProperty, IAopHandlerPropertySet, IAopHandlerPropertyGet
{
	private int _MyPropert;
	private static Lazy<AopParamInfo> _nearAop_Info = AopParamInfo.CreateFactory<ProxyPropertyDemo>(385875971);

	public int MyProperty
	{
		get
		{
			return ((IAopHandlerPropertyGet)this).PropertyGetHandler(_nearAop_Info, this, new _nearAop_get_MyProperty);
		}
		set
		{
            //赋值比较可用 AddProxyProperty(IsTestEquals = false) 关闭
			if (_MyPropert != value)
			{
				((IAopHandlerPropertySet)this).PropertySetHandler(_nearAop_Info, this, value,  _nearAop_get_MyProperty, _nearAop_set_MyProperty));
			}
		}
	}

	public void PropertySetHandler<T>(Lazy<AopParamInfo> aopParamInfo, object sender, T value, Func<T> getValue, Action<T> setValue)
	{
		//---代码同上
	}

	public T PropertyGetHandler<T>(Lazy<AopParamInfo> aopParamInfo, object sender, Func<T> getValue)
	{
		//---代码同上
	}

	private int _nearAop_get_MyProperty() => _MyPropert;

	private void _nearAop_set_MyProperty(int value) => _MyPropert = value;
}

返回目录

外部代理

多个类使用同一个实现了IAopHandlerProperty接口的代理类处理。如果代理类(MyProxy)构造函数中有参数,将不会自动初始化.请自行初始化(参考SetProxySourceAttribute特性).

[AddProxyProperty(typeof(MyProxy))]
class ProxyPropertyDemo1
{
    public int MyProperty { get; set; }
}

[AddProxyProperty(typeof(MyProxy))]
class ProxyPropertyDemo2
{
    public int MyProperty { get; set; }
}

class MyProxy : IAopHandlerProperty
{
    public void PropertySetHandler<T>(Lazy<AopParamInfo> aopParamInfo, object sender, T value, Func<T> getValue, Action<T> setValue)
    {
       //---代码同上
    }

    public T PropertyGetHandler<T>(Lazy<AopParamInfo> aopParamInfo, object sender, Func<T> getValue)
    {
       //---代码同上
    }
}

返回目录


3、方法代理

编译时在原函数中植入代码,在进入函数和退出函数时调用接口实现代码植入。(进入函数时可以修改和读取函数中的参数值)

特性 对应接口 用途
AddProxyMethodAttribute IAopHandlerMethod 用于标记类或成员函数,包含(OnEntry和OnExit)
AddProxyMethodEntryAttribute IAopHandlerMethod 标记进入函数时执行。
AddProxyMethodExitAttribute IAopHandlerMethod 标记退出函数时执行。

基础用法

编译前:

[AddProxyMethod(typeof(Proxy2), IsUpdateValue = true, IsSetValueOnExit = true)]
class ProxyMethodDemo
{
    private int value1;
    private int value2;

    //要植入代码的方法
    public void SetValue(int value1, int value2)
    {
        this.value1 = value1;
        this.value2 = value2;
    }

    //要植入代码的方法
    public string GetValue(int value1, int value2)
    {
        return $"{this.value1} . {this.value2} . {value1} . {value2}";
    }   
}

//代理类,用于处理进入函数和退出函数时的逻辑
class Proxy2 : IAopHandlerMethod
{
	//进入函数时执行
    public void OnEntry(MethodArgs methodArgs)
    {
        //---------------------------获取参数的值
        //通过索引获取指定位置的参数值
        var value1 = methodArgs.TryGetValue<int>(0);
        //通过参数名称获取参数值
        var value2 = methodArgs.TryGetValue<int>("value2");

        //---------------------------修改参数值
        //通过索引修改指定位置的的参数值
        methodArgs.TrySetValue(0, 192);
        //通过参数名称修改参数值
        methodArgs.TrySetValue("value2", 168);

        //获取全部的参数值
        var values = string.Join(",", methodArgs.ParamValues);
        //获取方法名
        var methodName = methodArgs.MethodInfo.Name;            
        Console.WriteLine($"进入[{methodName}]方法时, value1 = {value1} , value2 = {value2}, 全部参数值 = {values}");
    }
    
	//退出函数时执行
    public TResult OnExit<TResult>(MethodArgs methodArgs, TResult result)
    {
        var methodName = methodArgs.MethodInfo.Name;
         return (TResult)(object)$"{methodName}方法返回 : {result}";
    }
}

返回目录

编译后:

[AddProxyMethod(typeof(Proxy2), IsUpdateValue = true, IsSetValueOnExit = true)]
internal class ProxyMethodDemo
{
	private int value1;
	private int value2;
	private static Lazy<AopParamInfo> _nearAop_Info1 = AopParamInfo.CreateFactory<ProxyMethodDemo>(100663328);
	
    //自动添加的代理类型字段。如果代理类构造函数有参数,将不会自动初始化.请自行初始化(参考SetProxySourceAttribute特性)
	private Proxy2 _nearAop_Porxy = new Proxy2();

	public void SetValue(int value1, int value2)
	{        
		var methodArgs = new MethodArgs<int, int>(_nearAop_Info1, this);
        //第一次获取参数
		methodArgs.SetValue(value1, value2);
		var aopHandlerMethod = AopHelper.TestNull<IAopHandlerMethod>("_nearAop_Porxy", _nearAop_Porxy);
        //调用OnEntry方法
		aopHandlerMethod.OnEntry(methodArgs);
        //!!!!!!执行参数值修改(默认关闭,特性 IsUpdateValue = true 时开启)
		methodArgs.OutValue(out value1, out value2);
        //执行原方法代码
		this.value1 = value1;
		this.value2 = value2;
        //!!!!!!第二次获取参数(默认关闭,特性 IsSetValueOnExit = true 时开启)
		methodArgs.SetValue(value1, value2);
        //调用OnExit方法
		aopHandlerMethod.OnExit<object>(methodArgs, null);
	}

	public string GetValue(int value1, int value2)
	{
		//同上
	}
}

执行结果:

var test = new ProxyMethodDemo();
test.SetValue(1, 2);
var result = GetValue(3, 4);
// result ==  "GetValue方法返回 : 192 . 168 . 192 . 168"

返回目录

自定义处理函数

使用特性的HandlerName属性指定处理函数名称。

[AddProxyMethod(typeof(Proxy3), HandlerName = "自定义函数")]
class ProxyMethodDemo
{
    /// <summary>
    /// 实例函数
    /// </summary>
    public void SetValue1(int value) { }

    /// <summary>
    /// 静态函数
    /// </summary>
    public static void SetValue2(int value) { }
}


class Proxy3
{
    //注意:函数签名与IAopHandlerMethod.OnEntry 一致
    public void 自定义函数(MethodArgs methodArgs)
    {
        var info = methodArgs.MethodInfo;
        var staticStr = info.IsStatic ? "静态" : "";
        Console.WriteLine($"进入{staticStr}函数[{info.DeclaringType}.{info.Name}]");
    }

    //注意:函数签名与IAopHandlerMethod.OnExit 一致
    public TResult 自定义函数<TResult>(MethodArgs methodArgs, TResult result)
    {
        var info = methodArgs.MethodInfo;
        var staticStr = info.IsStatic ? "静态" : "";
        Console.WriteLine($"退出{staticStr}函数[{info.DeclaringType}.{info.Name}]");
        return result;
    }
}

返回目录


4、接口代理

由于编译前代理字段可能为自动生成,用于配合代理模式使用。执行时会在类中实现目标接口,并将接口实现指向代理字段。如果代理类构造函数有参数,将不会自动初始化.请自行初始化(参考SetProxySourceAttribute特性)。如果代理类构造函数有参数,将不会自动初始化.请自行初始化(参考SetProxySourceAttribute特性)

特性 参数1 参数2
AddProxyInterface Type:代理类型 Type[]:目标接口

编译前:

[AddProxyInterface(typeof(Proxy4),typeof(IMyInterface1),typeof(IMyInterface2))]
class ProxyInterfaceDemo
{
}   

interface IMyInterface1
{
    void ShowMsg();
}

interface IMyInterface2
{
}

//代理类要求实现目标接口
class Proxy4: IMyInterface
{
    public void ShowMsg()
    {
        Console.WriteLine("接口代理测试成功!");
    }
}

编译后:

[AddProxyInterface(typeof(Proxy4), typeof(IMyInterface))]
internal class ProxyInterfaceDemo : IMyInterface, IMyInterface2
{
	//如果代理类构造函数有参数,将不会自动初始化.请自行初始化(参考SetProxySourceAttribute特性)
	private Proxy4 _nearAop_Porxy = new Proxy4();

	void IMyInterface.ShowMsg()
	{
		AopHelper.TestNull<IMyInterface>("_nearAop_Porxy", _nearAop_Porxy).ShowMsg();
	}
}

返回目录


5、SetProxySourceAttribute特性

用于标记代理类字段,通常情况下代理类字段由AOP自动生成,但在代理类构造函数中有参数的情况下不会自动初始化实例,需用户显式初始化。

!如自动初始化失败,并且用户没有手动初始化,运行时会抛出异常:

_nearAop_Porxy..... is empty,未将对象引用设置到对象的实例.

[AddProxyInterface(typeof(Proxy5), typeof(IMyInterface1))]
[AddProxyInterface(typeof(Proxy6), typeof(IMyInterface2))]
class ProxySourceDemo
{
    //(用户显式初始化)直接使用字段类型
    [SetProxySource]
    readonly Proxy5 proxy5 = new Proxy5("接口1");

    //(用户显式初始化)使用type
    [SetProxySource(typeof(Proxy6))]
    readonly object proxy6 = new Proxy6("接口2");
}


class Proxy5 : IMyInterface1
{
    private readonly string p1;
    public Proxy5(string p1) => this.p1 = p1;
}

class Proxy6 : IMyInterface2
{
    private readonly string p1;
    public Proxy6(string p1) => this.p1 = p1;

}

返回目录


6、SetCancelAopAttribute特性

标记SetCancelAopAttribute特性的成员或类型,AOP将忽略代码植入操作。
返回目录


7、AOP调试信息输出

在项目的输出文件目录obj\Debugobj\Release目录下新建文件NearAop.Debug
返回目录


8、代码混淆

在编译时对输出的(Dll、Exe)文件,进行混淆,不会影响源码。

启用混淆

项目中新建任意名称的Class并继承NearAop.ObfuscatedOption
C#:

class ObfuscatedOption : NearAop.ObfuscatedOption
{
    //没有自定义需求的情况下可忽略构造函数!
    public ObfuscatedOption()
    {
       //短名称文本,启用后成员将被修改为 "O火星文O";
       TargetName = "火星文";
       //混淆符号,启用后会替代O,要求大于16个字符,并且不能相同;
       Key = "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ";         
       //注:某些字符无法用在成员变量上;
    }
}

编译后:
Image

VB.NET

Public Class ObfuscatedOption
    Inherits NearAop.ObfuscatedOption
    '没有自定义需求的情况下可忽略构造函数!
    Sub New()
       '短名称文本,启用后成员将被修改为 "O火星文O"
       TargetName = "火星文"
       '混淆符号,启用后会替代O,要求大于16个字符,并且不能相同;
       Key = "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ"      
       '注:某些字符无法用在成员变量上;
    End Sub
End Class

编译后:
Image
返回目录

取消成员混淆

某些情况下需要反射成员,使用SetCancelObfuscatedAttribute特性标记指定的成员,可以取消混淆行为。

//如果没有使用构造函数,将自动判断标记的成员类型

//等同于 [SetCancelObfuscated(MemberType.Type)]
[SetCancelObfuscated]
class Test
{
    //等同于 [SetCancelObfuscated(MemberType.Method)]
    [SetCancelObfuscated]
    public void SetValue() { }

    //同时取消“函数”和“参数”的命名混淆
    [SetCancelObfuscated(MemberType.Method | MemberType.Parameter)]
    public void SetValue2(string param) { }
}

返回目录

混淆覆盖率

如果类型被定义为Public 其中被定义为Public的成员不会被混淆。否则外部引用当前DLL时将无法使用。合理使用访问级别关键字控制混淆覆盖率!

类型被定义为Public 且成员被定义为Public 时,成员及类型不会被混淆。

类型被定义为非Public 时成员会被混淆。例外:定义一个 internal级别的接口,但实现类是public 的情况也会取消成员的混淆。

c# Vb 是否混淆
public Public no
internal Friend yes
protected Protected yes
protected internal Protected Friend yes
private Private yes

Comments ( 0 )

Sign in for post a comment

About

自用简单AOP和代码混淆 spread retract
Cancel

Releases

No release

Contributors

All

Activities

load more
can not load any more
1
https://gitee.com/sesametechgroup/NearAop.git
git@gitee.com:sesametechgroup/NearAop.git
sesametechgroup
NearAop
NearAop
master

Search