【xLua】xLua-master签名、加密Lua文件

GitHub - Tencent/xLua: xLua is a lua programming solution for C# ( Unity, .Net, Mono) , it supports android, ios, windows, linux, osx, etc.

如果你想在项目工程上操作,又发现项目工程并没导入Tools,可以从xLua-master工程拷贝到项目工程Assets同级目录。

创建一个.bat文件(LuaSignBat.bat)

cd /d %~dp0
rmdir /q /s  LuaSign   
 
"FilesSignature.exe" "../Assets/GameMain/LuaScripts" "../Assets/GameMain/LuaSign"

"FilesSignature.exe"是执行签名的exe

"../Assets/GameMain/LuaScripts"是工程内要被签名的Lua文件根目录

"../Assets/GameMain/LuaSign"是签名之后的Lua文件根目录

注意:这个签名流程如果是放在有自动构建包体的项目里是要写入自动流程内的,比如打包ab包时,打的Lua文件ab包不是LuaScripts而是LuaSign,Lua的加载器也是加载的是LuaSign的,关于这部分不在本文讲解。

签名后的文件如下

仅有第一行是密文,其他还是明文状态的,签名仅是对文件头部加密,避免程序直接读取二进制文件,如果要全内容加密或混淆就要在Loader加载器解密,以及自己写个API对全部签名后的文件加密。

针对签名后的文件加载器的代码要修改为如下:

loader = new SignatureLoader(PUBLIC_KEY, 原加载器方法)
PUBLIC_KEY是KeyPairsGen.exe生成的key_ras.pub文件内容,可以文本形式打开复制。

如果要使用luaEnv.DoString(byte[], chunkName, luaTable)这类代码去获取lua对应的LuaTable表,那么我们就必须传入的byte[]是签名解码后的byte[],而这个解码API就位于SignatureLoader.cs脚本内。我们可以针对它进行封装一个提供外部的解码方法如下:

cs 复制代码
byte[] load_and_verify(ref string filepath)
{
    byte[] data = userLoader(ref filepath);
    return GetVerifyBytes(data, filepath);
}

public byte[] GetVerifyBytes(byte[] data, string filepath)
{            
    if (data == null)
    {
        return null;
    }
    if (data.Length < 128)
    {
        throw new InvalidProgramException(filepath + " length less than 128!");
    }

    byte[] sig = new byte[128];
    byte[] filecontent = new byte[data.Length - 128];
    Array.Copy(data, sig, 128);
    Array.Copy(data, 128, filecontent, 0, filecontent.Length);

#if !UNITY_WSA || UNITY_EDITOR
    if (!rsa.VerifyData(filecontent, sha, sig))
    {
        throw new InvalidProgramException(filepath + " has invalid signature!");
    }
#else
    if (!CryptographicEngine.VerifySignature(key, CryptographicBuffer.CreateFromByteArray(filecontent), CryptographicBuffer.CreateFromByteArray(sig)))
    {
        throw new InvalidProgramException(filepath + " has invalid signature!");
    }
#endif
    return filecontent;
}

其中load_and_verify是原有的方法,GetVerifyBytes是我自定义的提供外部解码byte[]的,需自行加载文件原始byte[],以及传入lua文件相对路径(不带.lua后缀)实际也可以写个不用传路径的方法,因为只是用来警告提示的。

cs 复制代码
string filepaths = Application.dataPath + "/XLua/Examples/10_SignatureLoader/signatured1.lua";
byte[] bytes = File.ReadAllBytes(filepaths);
bytes = loader.GetVerifyBytes(bytes, "signatured1");        
luaenv.DoString(bytes, "chunk", luaTable);        
luaTable.Get("OnStart", out luaOnStart);
luaOnStart();

特别注意:luaTable是必须设置元表的一个表,不然无法识别各种全局变量,例如: 'require'

cs 复制代码
luaMetaTable = luaenv.NewTable();
luaMetaTable.Set("__index", luaenv.Global);

luaTable = luaenv.NewTable();
luaTable.SetMetaTable(luaMetaTable);

加密部分:(针对签名后的文件加密)

cs 复制代码
using UnityEngine;
using UnityEditor;
using System.IO;
public static class EncryptFileEditor
{
    [MenuItem("Lua/加密Lua")]
    public static void EncryptLuaFile()
    {
        //异或 加密,再用一次这个方法就是解密了
        string luaFileDir = Application.dataPath + "//XLua/Examples/10_SignatureLoader";
        byte[] key_char = System.Text.Encoding.Unicode.GetBytes("abcd");
        string[] files = Directory.GetFiles(luaFileDir, "*.lua");
        foreach (string filePath in files)
        {
            byte[] bytes = File.ReadAllBytes(filePath);
            for (int i = 0; i < bytes.Length; i++)
            {
                bytes[i] = (byte)(bytes[i] ^ key_char[i % key_char.Length]);
            }
            File.WriteAllBytes(filePath, bytes);
        }
    }
}

解密部分:(针对签名后的文件解密,而签名解码是在后面执行的)

cs 复制代码
 loader = new SignatureLoader(PUBLIC_KEY, (ref string filepath) =>
        {
            filepath = Application.dataPath + "/XLua/Examples/10_SignatureLoader/" + filepath.Replace('.', '/') + ".lua";
            if (File.Exists(filepath))
            {
                byte[] bytes = File.ReadAllBytes(filepath);
                //解密
                byte[] key_char = System.Text.Encoding.Unicode.GetBytes("abcd");
                for (int i = 0; i < bytes.Length; i++)
                {
                    bytes[i] = (byte)(bytes[i] ^ key_char[i % key_char.Length]);
                }
                return bytes;
            }
            else
            {
                return null;
            }
        });

使用方法:

cs 复制代码
string filepaths = Application.dataPath + "/XLua/Examples/10_SignatureLoader/signatured1.lua";
byte[] bytes = File.ReadAllBytes(filepaths); //读取原始文件
bytes = Decrypt(bytes); //先解密
bytes = loader.GetVerifyBytes(bytes, "signatured1");//后解签名 
luaenv.DoString(bytes, "chunk", luaTable);        
luaTable.Get("OnStart", out luaOnStart);
luaOnStart();

注意事项:必须是先签名、后加密,除非修改SignatureLoader.cs类的load_and_verify方法,将这个方法直接改写原本如下:

cs 复制代码
byte[] load_and_verify(ref string filepath)
{
    byte[] data = userLoader(ref filepath);
    return GetVerifyBytes(data, filepath);
}

userLoader就是进入到我们在实例化SignatureLoader时传入的第二个参数(一个委托对象),委托执行了加载原文件字节数组、解密操作,而load_and_verify函数是先进行的userLoader,拿到解密后的byte[]再进行签名解码,所以只能先签名文件、后加密文件,对应先解密文件,后解码签名。

相关推荐
重生之绝世牛码41 分钟前
Java设计模式 —— 【行为型模式】命令模式(Command Pattern) 详解
java·大数据·开发语言·设计模式·命令模式·设计原则
晚风_END2 小时前
node.js|浏览器插件|Open-Multiple-URLs的部署和使用,实现一键打开多个URL的强大工具
服务器·开发语言·数据库·node.js·dubbo
_周游4 小时前
【C语言】_指针与数组
c语言·开发语言
SyntaxSage5 小时前
Scala语言的数据库交互
开发语言·后端·golang
疯狂小料5 小时前
Python3刷算法来呀,贪心系列题单
开发语言·python·算法
码力全開5 小时前
C 语言奇幻之旅 - 第14篇:C 语言高级主题
服务器·c语言·开发语言·人工智能·算法
lsx2024065 小时前
PHP Array:精通数组操作
开发语言
三次元1115 小时前
JS中函数基础知识之查漏补缺(写给小白的学习笔记)
开发语言·前端·javascript·笔记·ecmascript·原型模式
武昌库里写JAVA5 小时前
Redis奇幻之旅(四)4. Redis Cluster
java·开发语言·spring boot·学习·课程设计
秋堂主5 小时前
2025第1周 | JavaScript中的正则表达式
开发语言·javascript·正则表达式