你好,这里是 Dotnet 工具箱,定期分享 Dotnet 有趣,实用的工具和组件,希望对您有用!

【JIEJIE.NET - 强大的 .NET 代码混淆工具】JIEJIE.NET

JIEJIE.NET 是一个使用 C# 开发的开源 .NET 代码加密工具。

很多 .NET 开发人员担心他们的软件被破解,版权受到侵犯,所以他们使用一些工具来混淆 IL 代码。比如 PreEmptive dotfuscator, 但有些场景的需求,是这些工具不能满足的。


【资料图】

所以作者写了 JieJie.NET,它可以深度加密.NET程序集,帮助大家保护版权。重要的是,这个工具是开源的。

界面预览功能特性

1.类型和成员重命名

旧代码:

public abstract class XTextDocumentContentElement : XTextContentElement{    public override void AfterLoad(ElementLoadEventArgs args);    public override void Clear();    [Obfuscation(Exclude = true, ApplyToMembers = true)]    public override XTextElement Clone(bool Deeply);    [Obfuscation(Exclude = true, ApplyToMembers = true)]    public override XTextDocument CreateContentDocument(bool includeThis);    public XTextSelection CreateSelection(int startIndex, int length);    public override void Dispose();    public override void DrawContent(InnerDocumentPaintEventArgs args);    [Obfuscation(Exclude = true, ApplyToMembers = true)]    public override void EditorRefreshViewExt(bool fastMode);    public float FixPageLinePosition(int pos);    public override void Focus();    [Obfuscation(Exclude = true, ApplyToMembers = true)]    public XTextLineList GetAllLines();    [Obfuscation(Exclude = true, ApplyToMembers = true)]    public virtual XTextRange GetRange(int StartIndex, int EndIndex);    public void InnerGetSelectionBorderElement(ref XTextElement startElement, ref XTextElement endElement);    public void InvalidateSpecifyLayoutElements();    [Obfuscation(Exclude = true, ApplyToMembers = true)]    public virtual bool IsSelected(XTextElement element);    public void RefreshParagraphListState(bool checkFlag, bool updateListIndex);    public XTextParagraphFlagElement RootParagraphFlag();    [Obfuscation(Exclude = true, ApplyToMembers = true)]    public bool SetSelection(int startIndex, int length);    [Obfuscation(Exclude = true, ApplyToMembers = true)]    public bool SetSelectionRange(int firstIndex, int lastIndex);}

加密后:

public abstract class XTextDocumentContentElement : XTextContentElement{    public override void Clear();    public override XTextElement Clone(bool Deeply);    public override XTextDocument CreateContentDocument(bool includeThis);    public override void Dispose();    public override void EditorRefreshViewExt(bool fastMode);    public override void Focus();    public XTextLineList GetAllLines();    public virtual XTextRange GetRange(int StartIndex, int EndIndex);    public virtual bool IsSelected(XTextElement element);    public bool SetSelection(int startIndex, int length);    public bool SetSelectionRange(int firstIndex, int lastIndex);    public XTextParagraphFlagElement z0ZzZzbmm1mO001();    public XTextSelection z0ZzZzbmm1mO011(int startIndex, int length);    public void z0ZzZzbmm1mO01O();    public float z0ZzZzbmm1mOOm1(int pos);    public void z0ZzZzbmm1mOOmn(ref XTextElement startElement, ref XTextElement endElement);    public void z0ZzZzbmm1mOOmO(bool checkFlag, bool updateListIndex);    public override void z0ZzZzbmmOO11nn(z0ZzZzbm0mmlm1O args);    public override void z0ZzZzbmmOOl0nO(ElementLoadEventArgs args);}

可以看到,一些 API 的名称被混淆了。

2. 混淆代码流程

JieJie.NET 可以分析 IL 代码,并且在不丢失任何特性的情况下随机混淆代码流程。它可以破坏 foreach/lock/using, 让代码很难阅读,有时候还会导致破解工具错误。

旧代码:

public int RemoveByControl(object control){    if (control == null)    {        throw new ArgumentNullException("control");    }    if (CheckOwner() == false)    {        return -1;    }    int result = 0;    lock (this)    {        for (int iCount = _Tasks.Count - 1; iCount >= 0; iCount--)        {            if (_Tasks[iCount].Control == control)            {                _Tasks.RemoveAt(iCount);                result++;            }        }        if (_CurrentTask != null && _CurrentTask.Control == control)        {            _CurrentTask = null;        }    }    return result;}

使用 JieJie.NET 后,在 ILSpy 中显示的代码如下:

public int RemoveByControl(object control){//Discarded unreachable code: IL_000b, IL_0073//IL_000b: Incompatible stack heights: 1 vs 0//IL_0073: Incompatible stack heights: 1 vs 0int num = z0ZzZzgw.z0kh;bool flag = default(bool);int num4 = default(int);int result = default(int);while (true){switch (num){default:{if (control == null){throw new ArgumentNullException(z0ZzZzow.z0rj);}if (!z0rk()){goto IL_0049;}int num2 = 0;z0ZzZzjw.z0uk(this);try{int num3 = z0ZzZzgw.z0ah;while (true){switch (num3){default:num2++;goto IL_0097;case 3:if (flag){z0ik = null;}break;case 4:case 5:{num4 = z0bk.Count - 1;goto IL_009e;}IL_009e:if (num4 < 0){flag = z0ik != null && z0ik.Control == control;num3 = z0ZzZzgw.z0wj;continue;}if (z0bk[num4].Control == control){z0bk.RemoveAt(num4);num3 = z0ZzZzgw.z0sh;continue;}goto IL_0097;IL_0097:num4--;goto IL_009e;}break;}}finally{Monitor.Exit(this);}result = num2;break;}case 0:case 1:case 3:break;}break;IL_0049:result = -1;num = z0ZzZzgw.z0wj;}return result;}

现在代码流程已经被破坏了。

3. 加密所有字符串值

JieJie.NET 可以收集程序集中定义的所有字符串值,然后把它们转换为新类中的静态只读字段,并对它们的值进行加密。

旧代码:

private string GetLicenseMessage(){    return "这是一个密钥 :" + Environment.UserName;}

加密后:

private string GetLicenseMessage(){    string text = _0._6 + Environment.UserName;    return text;}//  also create a new class, contains all string value in assembly in random order.internal static class _0{    public static readonly string _0;    public static readonly string _1;    public static readonly string _2;    public static readonly string _3;    public static readonly string _4;    public static readonly string _5;    public static readonly string _6;    public static readonly string _7;    public static readonly string _8;    public static readonly string _9;    public static readonly string _10;    public static readonly string _11;    public static readonly string _12;    public static readonly string _13;    public static readonly string _14;    public static readonly string _15;    public static readonly string _16;    public static readonly string _17;    public static readonly string _18;    public static readonly string _19;    public static readonly string _20;    public static readonly string _21;    static _0()    {        byte[] datas = _BytesContainer__._0();        _11 = GetStringByLong(datas, 151732605047602L);        _20 = GetStringByLong(datas, 450799767951810L);        _7 = GetStringByLong(datas, 101155071172227L);        _4 = GetStringByLong(datas, 47279000500949L);        _15 = GetStringByLong(datas, 415615395474299L);        _5 = GetStringByLong(datas, 54975582493063L);        _2 = GetStringByLong(datas, 17592187197342L);        _14 = GetStringByLong(datas, 206708198516324L);        _8 = GetStringByLong(datas, 124244814685054L);        _21 = GetStringByLong(datas, 459595860893446L);        _6 = GetStringByLong(datas, 72567769190975L);        _13 = GetStringByLong(datas, 182518931688172L);        _18 = GetStringByLong(datas, 433207581847376L);        _16 = GetStringByLong(datas, 417814419099513L);        _3 = GetStringByLong(datas, 36283884381871L);        _1 = GetStringByLong(datas, 9895605165436L);        _9 = GetStringByLong(datas, 136339442622330L);        _19 = GetStringByLong(datas, 440904163377248L);        _17 = GetStringByLong(datas, 426610511995160L);        _0 = GetStringByLong(datas, 598562L);        _10 = GetStringByLong(datas, 148434069970387L);        _12 = GetStringByLong(datas, 158329675868829L);    }    private static string GetStringByLong(byte[] datas, long key)    {        int num = (int)(key & 0xFFFF) ^ 0xEF83;        key >>= 16;        int num2 = (int)(key & 0xFFFFF);        key >>= 24;        int num3 = (int)key;        char[] array = new char[num2];        int num4 = 0;        while (num4 < num2)        {            int num5 = num4 + num3 << 1;            array[num4] = (char)(((datas[num5] << 8) + datas[num5 + 1]) ^ num);            num4++;            num++;        }        return new string(array);    }}

项目地址: https://github.com/dcsoft-yyf/JIEJIE.NET

【Dots - 更友好的 .NET SDK 管理器】什么是 Dots?

Dots 是一个用于管理 .NET SDK 的 GUI 工具,它使用 .NET MAUI 开发的,可用于 Windows 和 macOS(对不住了,Linux 用户)。

为什么会开发 Dots?

总所周知, .NET 的小版本更新很快,而我经常会试用 SDK 的最新预览版。甚至尝试自定义构建。我在我的机器上安装了几个不同版本的 SDK,只是方便能够在它们之间进行切换。通常我会尝试保留当前的稳定版本、最新的预览版和 LTS 版本。除此之外,我可能需要特定项目的特定 SDK 版本。

当然有 dotnet cli 允许我检查安装的版本,dotnet --list-sdks 可以输出安装的版本信息。

但我想更好地了解所有已安装版本的一些细节,并能够快速卸载它们。

于是,Dots - 更友好的 .NET SDK 管理器来了!欢迎大家尝试使用!

项目地址: https://github.com/nor0x/Dots

【DotNetCorePlugins- 动态加载和卸载 .NET 程序插件】

DotNetCorePlugins 是一个 .NET 的开源插件项目,它提供了能够动态加载程序集的 API,然后把它们作为 .NET 主程序的扩展程序执行。

这个库主要用到了 AssemblyLoadContext技术, System.Runtime.Loader.AssemblyLoadContext,又名 ALC,提供了一些用于定义动态程序集加载行为的基本 API。这是 .NET Core 中我最喜欢但鲜为人知的 API 之一。

如何使用?

安装 McMaster.NETCore.PluginsNuGet 包。

dotnet add package McMaster.NETCore.Plugins

主要使用的 API 是 PluginLoader.CreateFromAssemblyFile, 它允许从文件中读取并加载程序集。

PluginLoader.CreateFromAssemblyFile(    assemblyFile: "./plugins/MyPlugin/MyPlugin1.dll",    sharedTypes: new [] { typeof(IPlugin), typeof(IServiceCollection), typeof(ILogger) },    isUnloadable: true)
assemblyFile = 插件 .dll 的文件路径sharedTypes = 加载程序的统一的类型列表isUnloadable = 允许这个插件在将来的某个时候从内存中卸载。

定义接口

这是一个示例,我们定义了一个接口,里面包含了 GetName, 如下

public interface IPlugin{    string GetName();}

对于插件,我们直接使用这个接口并进行实现,如下

internal class MyPlugin1 : IPlugin{    public string GetName() => "My plugin v1";}

对于主程序,我们可以使用 PluginLoaderAPI 来加载插件,程序需要使用查找磁盘中的插件程序集。一种方式是基于约定的,比如

plugins/    $PluginName1/        $PluginName1.dll        (additional plugin files)    $PluginName2/        $PluginName2.dll

每个插件都发布到一个单独的目录中,这样可以避免插件之间的争用和重复的依赖问题。

以通过运行下面的命令,输出插件到文件夹中。

dotnet publish MyPlugin1.csproj --output plugins/MyPlugin1/

接下来,我们可以通过反射获取所有的插件,并进行加载, 代码如下

using McMaster.NETCore.Plugins;var loaders = new List();// create plugin loadersvar pluginsDir = Path.Combine(AppContext.BaseDirectory, "plugins");foreach (var dir in Directory.GetDirectories(pluginsDir)){    var dirName = Path.GetFileName(dir);    var pluginDll = Path.Combine(dir, dirName + ".dll");    if (File.Exists(pluginDll))    {        var loader = PluginLoader.CreateFromAssemblyFile(            pluginDll,            sharedTypes: new [] { typeof(IPlugin) });        loaders.Add(loader);    }}// Create an instance of plugin typesforeach (var loader in loaders){    foreach (var pluginType in loader        .LoadDefaultAssembly()        .GetTypes()        .Where(t => typeof(IPlugin).IsAssignableFrom(t) && !t.IsAbstract))    {         IPlugin plugin = (IPlugin)Activator.CreateInstance(pluginType);        Console.WriteLine($"Created plugin instance "{plugin.GetName()}".");    }}

支持 MVC 和 Razor

另外插件还支持加载 MVC 的 Controller 和 Razor Pages。通过安装下面的 Nuget 包。

dotnet add package McMaster.NETCore.Plugins.Mvc

加载程序集的方法如下:

public class Startup{    public void ConfigureServices(IServiceCollection services)    {        var pluginFile = Path.Combine(AppContext.BaseDirectory, "plugins/MyRazorPlugin/MyRazorPlugin.dll");        services            .AddMvc()             .AddPluginFromAssemblyFile(pluginFile);    }}

更多插件的使用方法,作者提供了一些示例项目,可以进行参考。

项目地址:https://github.com/natemcmaster/DotNetCorePlugins

推荐内容