本教程使用 HybridCLR+Obfuz 方案来混淆代码(dll)防止破解。
通过 Unity Package Manager 安装:
https://gitee.com/focus-creative-games/obfuz.git
https://gitee.com/focus-creative-games/obfuz4hybridclr.git
安装 Obfuz 时会报错,这是因为 Obfuz 和 HybridCLR 同时包含了 dnlib.dll 文件导致的,解决方法就是删除 HybridCLR 包下面的 dnlib.dll。
删除 HybridCLR 包下面的 dnlib.dll
HotUpdate程序集的创建在《HybridCLR快速入门》中已讲解。
using UnityEngine;
public class Entry : MonoBehaviour
{
void Start()
{
Debug.Log("Entry Start");
}
void Update()
{
}
}
执行菜单【Obfuz】->GenerateEncryptionVM 和 【Obfuz】->GenerateSecretKeyFile 命令。
在 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)
再执行菜单 【HybridCLR】->ObfuzExtension->CompileAndObfuscateAndCopyToStreamingAssets 将混淆后的热更DLL复制到StreamingAssets下。
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}");
}
}
}
将启动脚本添加到场景中
从上面的截图可以看出热更程序集中的代码已经被混淆。这里要注意的是,Unity脚本类名和特殊方法名不会被混淆。
从窗口中打印出的日志“Entry Start”可以看出混淆后的热更程序集(HotUpdate.dll.bytes)被正确执行。