HybridCLR代码混淆

作者:追风剑情 发布于:2026-1-27 14:22 分类:Unity3d

本教程使用 HybridCLR+Obfuz 方案来混淆代码(dll)防止破解。

一、安装 Obfuz 与 obfuz4hybridclr

通过 Unity Package Manager 安装:
https://gitee.com/focus-creative-games/obfuz.git
https://gitee.com/focus-creative-games/obfuz4hybridclr.git

obfuz.png

安装 Obfuz 时会报错,这是因为 Obfuz 和 HybridCLR 同时包含了 dnlib.dll 文件导致的,解决方法就是删除 HybridCLR 包下面的 dnlib.dll。

dnlib冲突报错.png

删除 HybridCLR 包下面的 dnlib.dll

3.png

二、配置 Project Settings

HotUpdate程序集的创建在《HybridCLR快速入门》中已讲解。
HybridCLRSettings.png

ObfuzSettings.png

三、创建热更测试脚本 Assets\HotUpdate\Entry.cs

using UnityEngine;

public class Entry : MonoBehaviour
{
    void Start()
    {
        Debug.Log("Entry Start");
    }

    void Update()
    {
        
    }
}  

四、生成加密虚拟机及密钥

执行菜单【Obfuz】->GenerateEncryptionVM 和 【Obfuz】->GenerateSecretKeyFile 命令。

genVM.png

gen.png

五、生成混淆后的DLL

在 Assets/Editor/HybridCLREditor.cs 中新增一个功能菜单。

using HybridCLR.Editor;
using HybridCLR.Editor.AOT;
using HybridCLR.Editor.Commands;
using Obfuz.Settings;
using Obfuz4HybridCLR;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;

public class HybridCLREditor
{
    // 进一步剔除AOT dll中非泛型函数元数据,输出到StrippedAOTAssembly2目录下
    [MenuItem("HybridCLR/Strip AOT Assembly")]
    public static void StripAOTAssembly()
    {
        BuildTarget target = EditorUserBuildSettings.activeBuildTarget;
        string srcDir = SettingsUtil.GetAssembliesPostIl2CppStripDir(target);
        string dstDir = $"{SettingsUtil.HybridCLRDataDir}/StrippedAOTAssembly2/{target}";
        foreach (var src in Directory.GetFiles(srcDir, "*.dll"))
        {
            string dllName = Path.GetFileName(src);
            string dstFile = $"{dstDir}/{dllName}";
            AOTAssemblyMetadataStripper.Strip(src, dstFile);
        }
    }

    [MenuItem("HybridCLR/CompileAndObfuscateAndCopyToStreamingAssets")]
    public static void CompileAndObfuscateAndCopyToStreamingAssets()
    {
        BuildTarget target = EditorUserBuildSettings.activeBuildTarget;
        CompileDllCommand.CompileDll(target);

        string obfuscatedHotUpdateDllPath = PrebuildCommandExt.GetObfuscatedHotUpdateAssemblyOutputPath(target);
        ObfuscateUtil.ObfuscateHotUpdateAssemblies(target, obfuscatedHotUpdateDllPath);

        Directory.CreateDirectory(Application.streamingAssetsPath);

        string hotUpdateDllPath = $"{SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target)}";
        List<string> obfuscationRelativeAssemblyNames = ObfuzSettings.Instance.assemblySettings.GetObfuscationRelativeAssemblyNames();

        foreach (string assName in SettingsUtil.HotUpdateAssemblyNamesIncludePreserved)
        {
            string srcDir = obfuscationRelativeAssemblyNames.Contains(assName) ? obfuscatedHotUpdateDllPath : hotUpdateDllPath;
            string srcFile = $"{srcDir}/{assName}.dll";
            string dstFile = $"{Application.streamingAssetsPath}/{assName}.dll.bytes";
            if (File.Exists(srcFile))
            {
                File.Copy(srcFile, dstFile, true);
                Debug.Log($"[CompileAndObfuscate] Copy {srcFile} to {dstFile}");
            }
        }
    }
}  

执行菜单 【HybridCLR】->ObfuzExtension->GenerateAll 生成混淆后的程序集(dll)

6.png

再执行菜单 【HybridCLR】->ObfuzExtension->CompileAndObfuscateAndCopyToStreamingAssets 将混淆后的热更DLL复制到StreamingAssets下。

7.png

六、创建启动脚本

using HybridCLR;
using Obfuz;
using Obfuz.EncryptionVM;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEngine;
/// <summary>
/// 引导程序
/// </summary>
public class Bootstrap : MonoBehaviour
{
    // 初始化EncryptionService后被混淆的代码才能正常运行,
    // 因此尽可能地早地初始化它。
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
    private static void SetUpStaticSecretKey()
    {
        Debug.Log("SetUpStaticSecret begin");
        EncryptionService<DefaultStaticEncryptionScope>.Encryptor = new GeneratedEncryptionVirtualMachine(Resources.Load<TextAsset>("Obfuz/defaultStaticSecretKey").bytes);
        Debug.Log("SetUpStaticSecret end");
    }

    void Start()
    {
#if UNITY_EDITOR
        Assembly ass = AppDomain.CurrentDomain.GetAssemblies().First(ass => ass.GetName().Name == "HotUpdate");
#else
        LoadMetadataForAOTAssemblies();
        Assembly ass = Assembly.Load(File.ReadAllBytes($"{Application.streamingAssetsPath}/HotUpdate.dll.bytes"));
#endif
        Type entry = ass.GetType("Entry");
        this.gameObject.AddComponent(entry);
    }

    private static void LoadMetadataForAOTAssemblies()
    {
        HomologousImageMode mode = HomologousImageMode.SuperSet;
        var aotDlls = new string[] { "mscorlib" };
        foreach (var aotDllName in aotDlls)
        {
            string filePath = $"{Application.streamingAssetsPath}/{aotDllName}.dll.bytes";
            if (!File.Exists(filePath))
                continue;
            byte[] dllBytes = File.ReadAllBytes(filePath);
            LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(dllBytes, mode);
            Debug.Log($"LoadMetadataForAOTAssembly:{aotDllName}. mode:{mode} ret:{err}");
        }
    }
}  

将启动脚本添加到场景中

8.png

七、用ILSpy查看HotUpdate.dll.bytes

9.png

从上面的截图可以看出热更程序集中的代码已经被混淆。这里要注意的是,Unity脚本类名和特殊方法名不会被混淆。

八、打包运行测试

从窗口中打印出的日志“Entry Start”可以看出混淆后的热更程序集(HotUpdate.dll.bytes)被正确执行。
10.png

标签: Unity3d

Powered by emlog  蜀ICP备18021003号-1   sitemap

川公网安备 51019002001593号