Lua代码加密探索(Skynet添加第三方库方法)

在skynet中嵌入加密算法 https://github.com/luke-park/SecureCompatibleEncryptionExamples
  1. 在skynet中添加代码:

    [root@localhost wtserver]# tree 3rd
    3rd
    lua-cryption/
    	├── cryptionlib.c
    	├── SCEE.c
    	└── SCEE.h
    lua-filesystem/
    	├── lfs.c
    	├── lfs.def
    	└── lfs.h
    
  2. 修改skynet的makefile

    CSERVICE = snlua logger gate harbor
    LUA_CLIB = skynet \
    	client \
    	  bson md5 sproto lpeg cjson cryption lfs $(TLS_MODULE)
    
    $(LUA_CLIB_PATH)/cryption.so : 3rd/lua-cryption/cryptionlib.c 3rd/lua-cryption/SCEE.c | $(LUA_CLIB_PATH)
    $(CC) $(CFLAGS) $(SHARED) -I3rd/lua-cryption $^ -o $@ -lcrypto -lssl -ldl
    
    $(LUA_CLIB_PATH)/lfs.so : 3rd/lua-filesystem/lfs.c | $(LUA_CLIB_PATH)
    $(CC) $(CFLAGS) $(SHARED) -I3rd/lua-filesystem $^ -o $@
    
  3. 在skynet中使用案例:
    在这里插入图片描述

在skynet中嵌入文件系统 https://github.com/keplerproject/luafilesystem
  1. 参考在项目中嵌入加密算法,将文件系统添加至skynet中。
  2. 文件系统可以参考 库的说明文档。
lua源码修改方案一:

参考博客: http://just4coding.com/2016/11/15/luajit-encrypt/

思路:检查文件是否为加密文件,将加密文件读取出后解密,并存入到临时文件中。将临时文件的句柄替换 lf.f 的句柄

方案已废弃: 临时文件的创建会导致代码泄露。

  1. luaL_loadfilex 函数中添加如下代码:
    // lua代码
    lua_pushfstring(L, "@%s", filename);
    lf.f = fopen(filename, "r");
    if (lf.f == NULL) return errfile(L, "open", fnameindex);


    // 新增加代码
	// 检查文件是否为加密文件, 加密文件需要嗲用解密接口
    //char file_header[FILE_HEADER_LEN];
    char *file_header = malloc(FILE_HEADER_LEN);
	size_t sz = fread(file_header, sizeof(char), FILE_HEADER_LEN, lf.f);
    if (sz == FILE_HEADER_LEN) {
        if (memcmp(file_header, "wt_cipher_lua_file\n", FILE_HEADER_LEN - 1) == 0) {
            // lf.f = lf.f
            lf.f = decrypt_file(lf.f);
        }
    }
    free(file_header);
    fseek(lf.f, 0L, SEEK_SET);
  1. decrypt_file 的实现:
static FILE *decrypt_file(FILE *ofp)
{
  int            fd, len;
  size_t         sz;
  FILE          *fp;
  unsigned char *buf, *obuf;
  char           file_temp[] = "/tmp/luajit-XXXXXX";

  fp = NULL;
  buf = NULL;
  obuf = NULL;
  fd = -1;

  fseek(ofp, 0L, SEEK_END);
  sz = ftell(ofp);

  obuf = malloc(sz);
  if (obuf == NULL) {
    goto failed;
  }

  fseek(ofp, 0L, SEEK_SET);
  if (fread(obuf, 1, sz, ofp) < sz) {
    goto failed;
  }

  fclose(ofp);
  ofp = NULL;

  buf = blowfish_decrypt(obuf + FILE_HEADER_LEN,
                         sz - FILE_HEADER_LEN,
                         g_key,
                         g_iv,
                         &len);
  if (buf == NULL) {
    goto failed;
  }

  free(obuf);
  obuf = NULL;

  fd = mkstemp(file_temp);
  if (fd < 0) {
    goto failed;
  }
  unlink(file_temp);

  fp = fdopen(fd, "wb+");
  if (fp == NULL) {
    goto failed;
  }
  fwrite(buf, 1, len, fp);
  free(buf);
  buf = NULL;

  return fp;

failed:

  if (fp) {
    fclose(fp);
  }

  if (ofp) {
    fclose(ofp);
  }

  if (obuf) {
    free(obuf);
  }

  if (buf) {
    free(buf);
  }

  return NULL;
}
lua源码修改方案二:

思路:修改代码读取函数。lua文件文段加密,设置分段标识。

方案已废弃:lua代码文件读取单次 BUFSIZ = 512, 并且后序处理流程复杂!代码修改过多,担心修改后影响后序流程。(能力不够)

typedef struct LoadF {
  int n;  /* number of pre-read characters */
  FILE *f;  /* file being read */
  char buff[BUFSIZ];  /* area for reading file */
} LoadF;


static const char *getF (lua_State *L, void *ud, size_t *size) {
  LoadF *lf = (LoadF *)ud;
  (void)L;  /* not used */
  if (lf->n > 0) {  /* are there pre-read characters to be read? */
    *size = lf->n;  /* return them (chars already in buffer) */
    lf->n = 0;  /* no more pre-read characters */
  }
  else {  /* read a block from file */
    /* 'fread' can return > 0 *and* set the EOF flag. If next call to
       'getF' called 'fread', it might still wait for user input.
       The next check avoids this problem. */
    if (feof(lf->f)) return NULL;
    *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f);  /* read block */
  }
  return lf->buff;
}
Lua中文件加密流程:
--[[
auth:zlf
date: 2021.12.28
note: 对指定路径内所有的文件加密,按原文件子路径保存在特定路径中。
]]
local lfs = require "lfs"
local cryption = require "cryption"

local M = {}
local l_Api = {}

-- 请勿改动, 改动后请随便改动 lua 的c代码相应文件
local l_CiphertextHead = "--jm_cipher_lua_file\n"
--[[
    @note 检查该文件是否在为指定的格式文件
    @param tbFileType 需要的文件类型
    @param sFileName 被检测的文件
    @return true: 合适的文件
]]
function l_Api.contain_file_type(tbFileType, sFileName)
  if (not tbFileType) or #tbFileType == 0 then 
    return true 
  end

  -- 只搜索文件尾部
  local sFileLen = string.len(sFileName)
  local nFindPlace = sFileLen
  for _, sType in pairs(tbFileType) do
    if string.find(sFileName, sType, (nFindPlace-string.len(sType)) , true) then
      return true
    end
  end
end

--[[
    @note 扫描指定目录的文件(可指定文件类型)
    @param sRootPath 需要扫描的目录
    @param tbAllFilePath 扫描到的文件存放
    @param tbFileType 需要的文件类型
    @return nil
]]
function l_Api.get_all_files(sRootPath, tbAllFilePath, tbFileType)
  for entry in lfs.dir(sRootPath) do
    if string.sub(entry,1,1) ~= "." then
      local sPath = sRootPath.."/"..entry
      local sAttr = lfs.attributes(sPath)
      assert(type(sAttr)=="table") --如果获取不到属性表则报错
      if(sAttr.mode == "directory") then
        l_Api.get_all_files(sPath, tbAllFilePath, tbFileType) --自调用遍历子目录
      elseif sAttr.mode=="file" then
        if l_Api.contain_file_type(tbFileType, sPath) then
          table.insert(tbAllFilePath,sPath)
        end
      end
    end
  end
end

--[[
  @TODO: 在 sRootDir目录下, 创建 sDirPath 中所有的目录
]]
function l_Api.mkdir(sRootDir, sDirPath)
  -- for sDir in string.gmatch(sDirPath, "(/%g+/)") do
  --   log_info("DDDDDDDDDDDD ", sDir)
  -- end
  local s = 0
  -- local e = 0
  local bSign = false
  local sCreatePath
  while true do
    local n = string.find(sDirPath, "/", s+1)
    s = n
    if s == nil then
      break
    end
    -- 创建目录
    if bSign then
      sCreatePath = sRootDir .. string.sub(sDirPath, 1, s)
      -- log_info("DDDDDDDDDDDD ", sCreatePath)
      local tbRet = {lfs.mkdir(sCreatePath)}

      -- log_info("############## ", table.printT(tbRet), sCreatePath)
      if not tbRet[1] and (tbRet[3] or 0) ~= 17 then  -- 17: 文件夹已经存在
        assert(false, "Can't create dir faile " .. sCreatePath)
      end
    end
    bSign = true
  end
end

--[[
    @TODO: 对指定路径的文件进行加密, 加密后存放到指定路径中。可指定文件的需要转换的文件类型  2021-12-28 14:51:42
    @sSpecifyPathSource: 加密源目录                             /data/jm/jmserver/jm
    @sSpecifyPathTarget: 加密后输出根目录(需要手动创建好改目录)   /data/jm/jmserver/jm/jm_encrypt
    @sPassWord: 密码
    @tbFileType: 需要的文件类型(不指定,所有文件都会被转换)
    @return 
    @
]]
function M.encrypt_path_all_files(sSpecifyPathSource, sSpecifyPathTarget, sPassWord, tbFileType)
  if not sSpecifyPathSource then
    log_error("jm.tools.encrypt_file encrypt_path_all_files missing parameter sSpecifyPathSource")
    return
  end
  if not sSpecifyPathTarget then
    log_error("jm.tools.encrypt_file encrypt_path_all_files missing parameter sSpecifyPathTarget")
    return
  end
  if not sPassWord then
    log_error("jm.tools.encrypt_file encrypt_path_all_files missing parameter sPassWord")
    return
  end
  if not tbFileType then
    log_error("jm.tools.encrypt_file encrypt_path_all_files missing parameter tbFileType")
    return
  end

  -- 搜索文件
  local tbAllFilePath = {}
  l_Api.get_all_files(sSpecifyPathSource,tbAllFilePath, tbFileType);

  -- 路径长度
  local nLenPathSource = string.len(sSpecifyPathSource)

  -- 读取文件并加密
  local sFileContext = nil
  local sCiphertext = nil
  local sSavePath = nil
  local nFilePr
  for _, sFilePath in ipairs(tbAllFilePath) do
    nFilePr = assert(io.open(sFilePath), "Can't open faile :" .. sFilePath)
    sFileContext = nFilePr:read("a")
    nFilePr:close()

    sCiphertext = cryption.encryption(sFileContext, sPassWord)

    -- 将文件写入到指定路径中
    -- 裁切 sFileName, 将 sSpecifyPathSource 剔除后,拼接到 sSpecifyPathTarget 后面
    sSavePath = sSpecifyPathTarget .. string.sub(sFilePath, nLenPathSource+1)
    l_Api.mkdir(sSpecifyPathTarget, string.sub(sFilePath, nLenPathSource+1))
    -- 将文件写入到 sSavePath 中
    nFilePr = assert(io.open(sSavePath, "w+"),"Can't create faile :" .. sSavePath)
    assert(nFilePr:write(l_CiphertextHead), "Can't write faile :" .. sSavePath)
    assert(nFilePr:write(sCiphertext), "Can't write faile :" .. sSavePath)

    nFilePr:close()
    -- break
  end
end

--[[
    @param sciphertext 密文
    @return string 明文 or nil
]]
function M.decrypt(sCiphertext)
    -- plaintext
    -- log_error(":############# ", sFileName)
    -- 测试解密
    -- local temp = cryption.decryption(sCiphertext, sPassWord)
    -- log_error("============= \n", temp)
end

function M.test()
  -- local tbAllFilePath = {}
  -- local tbFileType = {".lua"}
  -- l_Api.get_all_files(lfs.currentdir(),tbAllFilePath, tbFileType);
  -- log_info("#############", table.printT(tbAllFilePath))

  M.encrypt_path_all_files(lfs.currentdir() .. "/jm", lfs.currentdir() .. "/jm/jm_encrypt", "dafasgsfdg",{".lua", ".pb"})
  log_info("############## ", string.len(l_CiphertextHead))

  -- l_Api.mkdir("/data/jm/jmserver", "/jm_encrypt/config/DockerTag.lua")
end

return M
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值