贸易 来源:博客园 时间:2023-04-28 10:38:04
你好,这里是 Dotnet 工具箱,定期分享 Dotnet 有趣,实用的工具和组件,希望对您有用!
【JIEJIE.NET - 强大的 .NET 代码混淆工具】JIEJIE.NETJIEJIE.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
标签:
下一篇:最后一页