【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[]再进行签名解码,所以只能先签名文件、后加密文件,对应先解密文件,后解码签名。

相关推荐
软件开发技术局31 分钟前
撕碎QT面具(8):对控件采用自动增加函数(转到槽)的方式,发现函数不能被调用的解决方案
开发语言·qt
周杰伦fans2 小时前
C#中修饰符
开发语言·c#
yngsqq2 小时前
c# —— StringBuilder 类
java·开发语言
mikey棒棒棒2 小时前
Redis——优惠券秒杀问题(分布式id、一人多单超卖、乐悲锁、CAS、分布式锁、Redisson)
数据库·redis·lua·redisson·watchdog·cas·并发锁
赔罪2 小时前
Python 高级特性-切片
开发语言·python
子豪-中国机器人3 小时前
2月17日c语言框架
c语言·开发语言
夏天的阳光吖3 小时前
C++蓝桥杯基础篇(四)
开发语言·c++·蓝桥杯
oioihoii4 小时前
C++17 中的 std::to_chars 和 std::from_chars:高效且安全的字符串转换工具
开发语言·c++
秋窗74 小时前
Mac下Python版本管理,适用于pyenv不起作用的情况
开发语言·python·macos
柯腾啊5 小时前
VSCode 中使用 Snippets 设置常用代码块
开发语言·前端·javascript·ide·vscode·编辑器·代码片段