【Unity】C#热更dll混淆加密 代码加固 dll合并 .NET Reactor

 使用.NET Reactor三方工具,把混淆加固热更dll的功能融入自动化打包工作流。

很多人用HybridCLR进行全代码热更,即使加密AB包也毫无安全可言。而HybridCLR社区版对于非全热更,AOT程序集往往还得作为AOT泛型元数据补充的aot dll直接打进包里,也就意味着AOT程序集也暴露出去了,同样毫无安全可言。

对于HybridCLR社区版,除了加密热更dll AB包,如何进一步提升安全性呢?代码混淆是一种比较常用的方式。

.Net Reactor是一款非常好用稳定的dll加固软件,并且支持dll合并。除了客户端GUI界面外,还提供了命令行工具,dotNET_Reactor.Console.exe -h 即可查看参数用法。

工具设计如图:

 工具设计:

1. 需支持自定义忽略混淆方法名列表,配置到列表内的方法名将不进行混淆,如MonoBehavior声明周期方法,Awake、Start、Update等等,这些方法Unity以反射方式获取,一旦方法名改变将不会被执行。

2.自动扫描dll中的外部接口、抽象方法,并跳过这些方法名的混淆。

3. 支持配置混淆策略,如是否混淆可序列化类、字符串、枚举等。

4. 在打包工具界面,可选择加密方式。只需实现接口自己即可扩展自定义加密方式;

然而代码混淆有利有弊,如果处理不当风险很大,比如某些类和方法通过反射调用,类名和方法名一旦混淆处理就无法通过名字反射调用,序列化类也会无法反序列化。因此过滤混淆就需要小心翼翼。

通过标签忽略某个类名混淆:

可参考官方文档:Declarative Protection

1. 跳过该类的任何混淆

[System.Reflection.Obfuscation(Feature = "renaming")]

2.  仅跳过该类的类名混淆,内部类、方法名、变量不跳过

[System.Reflection.Obfuscation(Feature = "renaming", ApplyToMembers = false)]

 通过调用dotNET_Reactor.Console.exe命令行加密dll:

如果不清楚命令行参数,.Net Reactor客户端还支持在UI上手动点选混淆配置,然后通过Command line菜单一键生成命令行,只需复制即可。

 自动化混淆:

using GameFramework;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using UnityEngine;

namespace UGF.EditorTools
{
    /// <summary>
    /// 默认热更代码加密:使用.Net Reactor对hotfix.dll进行混淆加密
    /// </summary>
    public class DefaultProtectHotfixDllsHandler : IProtectHotfixDllsHandler
    {
        const bool invalid_metadata = false;
        const bool obfuscate_public_types = false;//public类型加密
        const bool stringencryption = true;//字符串加密
        const bool enumencryption = false;//枚举加密
        const bool fieldsencryption = false;
        const bool serializableTypeEncryption = false;
        const string DotNetReactorPath = "Tools/NETReactor/dotNET_Reactor.Console.exe";
        const string IgnoreMethodsListFile = "Tools/NETReactor/IgnoreMethodsList.txt";
        string mainHotfixDllName = Path.GetFileNameWithoutExtension(ConstEditor.HotfixAssembly);
        public bool OnProtectDll(string inputDllFile, string outputDllFile)
        {
            if (Path.GetFileNameWithoutExtension(inputDllFile).CompareTo(mainHotfixDllName) == 0)
            {
                //只加密Hotfix.dll
                return DotNetReactor(inputDllFile, outputDllFile);
            }
            return false;
        }

        private static bool DotNetReactor(string inputDllFile, string outputDllFile)
        {
            string toolsFile = UtilityBuiltin.AssetsPath.GetCombinePath(Directory.GetParent(Application.dataPath).FullName, DotNetReactorPath);
            if (!File.Exists(toolsFile))
            {
                return false;
            }

            StringBuilder strBuilder = new StringBuilder();
            strBuilder.AppendFormat(@"{0} -quiet -file ""{1}"" -targetfile ""{2}""", toolsFile, inputDllFile, outputDllFile);
            strBuilder.AppendFormat(@" -invalid_metadata {0} -stringencryption {1} -exclude_enums {2} -exclude_fields {3} -exclude_serializable_types {4} -obfuscate_public_types {5} -rules "".*::methods:", invalid_metadata ? 0 : 1, stringencryption ? 0 : 1, enumencryption ? 0 : 1,
                fieldsencryption ? 0 : 1, serializableTypeEncryption ? 0 : 1, obfuscate_public_types ? 1 : 0);
            var ignoreMethods = MethodsFilter(Path.GetFileNameWithoutExtension(inputDllFile));
            strBuilder.Append(ignoreMethods);
            strBuilder.Append(@"::fields:$.::properties:$.::events:$.::onamespaces:""");//::otypes:
            string commandLineStr = strBuilder.ToString();
            GFBuiltin.LogInfo(commandLineStr);
            var proceInfo = new System.Diagnostics.ProcessStartInfo(toolsFile, commandLineStr);
            proceInfo.CreateNoWindow = true;
            proceInfo.UseShellExecute = false;
            proceInfo.Verb = "runas";
            proceInfo.WorkingDirectory = Directory.GetParent(Application.dataPath).FullName;
            bool success;
            using (var proce = System.Diagnostics.Process.Start(proceInfo))
            {
                proce.WaitForExit();
                success = proce.ExitCode == 0;
                if (success)
                {
                    GFBuiltin.LogInfo($"加密dll成功:{inputDllFile}");
                }
                return success;
            }
        }
        
        private static string MethodsFilter(string assemblyName)
        {
            var assemblies = Utility.Assembly.GetAssemblies();
            System.Reflection.Assembly hotfixAssembly = null;
            foreach (var item in assemblies)
            {
                if (item.GetName().Name == assemblyName)
                {
                    hotfixAssembly = item;
                    break;
                }
            }

            if (hotfixAssembly == null) return null;

            HashSet<string> ignoreMethodsNames = new HashSet<string>();
            string ignoreFile = UtilityBuiltin.AssetsPath.GetCombinePath(Directory.GetParent(Application.dataPath).FullName, IgnoreMethodsListFile);
            if (File.Exists(ignoreFile))
            {
                var methodNames = File.ReadAllLines(ignoreFile);
                foreach (var method in methodNames)
                {
                    if (string.IsNullOrWhiteSpace(method) || method.StartsWith("//")) continue;

                    ignoreMethodsNames.Add(method);
                }
            }
            var typesInAssembly = hotfixAssembly.GetTypes();
            foreach (var tp in typesInAssembly)
            {
                System.Type currentType = tp;

                while (currentType != null)
                {
                    if (currentType.BaseType != null && currentType.BaseType.Assembly != hotfixAssembly)
                    {
                        var methods = currentType.GetMethods(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
                        if (currentType.IsInterface)
                        {
                            foreach (var method in methods)
                            {
                                ignoreMethodsNames.Add(method.Name);
                            }
                        }
                        else
                        {
                            foreach (var method in methods)
                            {
                                if (method.IsVirtual || method.IsAbstract)
                                {
                                    ignoreMethodsNames.Add(method.Name);
                                }
                            }
                        }

                    }
                    currentType = currentType.BaseType;
                }
            }

            StringBuilder result = new StringBuilder();
            foreach (var methodName in ignoreMethodsNames)
            {
                //Debug.Log(methodName);
                result.AppendFormat("^{0}$,", methodName);
            }

            return result.ToString(0, result.Length - 1);
        }
    }

}

混淆前:

 混淆后:

This asset obfuscates your code to make it harder for bad guys to reverse engineer your projects. Specifically designed for Unity, it seamlessly links in with its build process. The top priority of this package is to work straight out of the box with no extra steps required. While other obfuscators can stop a game from working, Beebyte's obfuscator looks for specific Unity related code that must be protected. The contents of your source files are unchanged, the obfuscation targets the compiled assembly. Features: - Supports IL2CPP - Supports Assembly Definition Files (Unity 2017.3+) - Removes Namespaces without any conflicts - Recognises Unity related code that must not be changed - Renames Classes (including MonoBehaviours) - Renames Methods - Renames Parameters - Renames Fields - Renames Properties - Renames Events - String literal obfuscation - Adds fake methods - Easy and extensive customisation using the Unity inspector window - Consistent name translations are possible across multiple builds and developers - Semantically secure cryptographic naming convention for renamed members The asset works for both Unity Free and Unity Pro version 4.2.0 onwards (including Unity 5 & 2017 & 2018). Build targets include Standalone, Android, iOS, WebGL, UWP. Other platforms are not guaranteed or supported but may become supported at a future date. IL2CPP builds are much harder to reverse engineer but strings and member information (class, method names etc) are visible in the global-metadata.dat file. Obfuscation will apply to this file adding further security. Why not complement your security with the Anti-Cheat Toolkit - a great third party asset. For more information about the Obfuscator, please see the FAQ
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值