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

相关推荐
香蕉可乐荷包蛋3 分钟前
Python面试问题
开发语言·python·面试
ErizJ12 分钟前
Golang|分布式索引架构
开发语言·分布式·后端·架构·golang
.生产的驴13 分钟前
SpringBoot 接口国际化i18n 多语言返回 中英文切换 全球化 语言切换
java·开发语言·spring boot·后端·前端框架
八股文领域大手子21 分钟前
深入浅出限流算法(三):追求极致精确的滑动日志
开发语言·数据结构·算法·leetcode·mybatis·哈希算法
几度泥的菜花1 小时前
优雅实现网页弹窗提示功能:JavaScript与CSS完美结合
开发语言·javascript·css
weixin_307779131 小时前
AWS Glue ETL设计与调度最佳实践
开发语言·数据仓库·云计算·etl·aws
兜小糖的小秃毛2 小时前
文号验证-同时对两个输入框验证
开发语言·前端·javascript
anqi272 小时前
如何在 IntelliJ IDEA 中编写 Speak 程序
java·大数据·开发语言·spark·intellij-idea
XuX032 小时前
MATLAB小试牛刀系列(1)
开发语言·matlab
Suckerbin2 小时前
第十四章-PHP与HTTP协议
开发语言·http·php