• cocos2dxlua工程的lua脚本加密


    2014/1/26 更新

    最近又发现了一个很简单的方法,其实coco2dx已经给我们提供设置loader的方法。

    注意:有个局限性,在非android平台下调用pEngine->executeScriptFile是不调用loader的,只有require这种才会调用loader。也就是说你直接executeScriptFile("main.lua")这个脚本不能加密,main.lua里面require的才能加密

    步骤如下:

    1、实现自己的loader(参考int cocos2dx_lua_loader(lua_State *L))

    #define CODE_MASK 250
    
    extern "C"
    {
        int decode_lua_loader(lua_State *L)
        {
            std::string filename(luaL_checkstring(L, 1));
            size_t pos = filename.rfind(".lua");
            if (pos != std::string::npos)
            {
                filename = filename.substr(0, pos);
            }
            
            pos = filename.find_first_of(".");
            while (pos != std::string::npos)
            {
                filename.replace(pos, 1, "/");
                pos = filename.find_first_of(".");
            }
            filename.append(".lua");
            
            unsigned long codeBufferSize = 0;
            unsigned char* codeBuffer = CCFileUtils::sharedFileUtils()->getFileData(filename.c_str(), "rb", &codeBufferSize);
            
            //-------------decode here
            for(int i=0; i<codeBufferSize; i++)
            {
                codeBuffer[i] ^= CODE_MASK; //xor decode
            }
            //-------------
    
            if (codeBuffer)
            {
                if (luaL_loadbuffer(L, (char*)codeBuffer, codeBufferSize, filename.c_str()) != 0)
                {
                    luaL_error(L, "error loading module %s from file %s :\n\t%s",
                        lua_tostring(L, 1), filename.c_str(), lua_tostring(L, -1));
                }
                delete []codeBuffer;
            }
            else
            {
                CCLog("can not get file data of %s", filename.c_str());
            }
            
            return 1;
        }
    }

    2、添加loader

    CCScriptEngineManager::sharedManager()->setScriptEngine(pEngine);
    pEngine->addLuaLoader(decode_lua_loader);//add this

    我在win32上测试通过

    ----------------------------------------------------------------------------------------分割线----------------------------------------------------------------------------------------

    发了这篇博客之后好多同学联系我,说感觉还是不太明白,那我再说清楚一些

    2014/1/15 更新

    转载请注明出处http://www.cnblogs.com/mrblue/admin/EditPosts.aspx?postid=3122543

    前言部分

    1、本文是以cocos2d-x-lua项目为例说明的。

    2、我这里只说下我自己遇到的问题和解决方法,当大家也遇到时可以参考下。

    3、我所使用的cocos2d-x版本是cocos2d-2.1rc0-x-2.1.3,这个版本使用的是luajit,而不是源生的lua,luajit有很多好处。

    ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    方法一:

    这种其实并不是真正意义上的加密,而是用luajit把lua脚本编译成字节码(我之前实验过用luac编译出的字节码不能被luajit执行)。

    1)编译luajit,这里并不是编译luajit的库,而是编译luajit的控制台程序,用作后面把lua脚本代码文件编译成字节码用。

      cd到 coco2dx安装目录/scripting/lua/luajit/LuaJIT-2.0.1

    2) 输入make(如果控台显示command not found,那就要先安装make,Mac作为UNIX系却没有提供make,需要打开xCode->xCode(菜单栏)->Open Developer Tool->More Developer Tools->注册/登陆AppID->选择你对应的Command Line Tool->下载安装)

      这时会在src文件夹下生成可执行文件luajit(这b玩意我找了好久,根本找不到在哪生成的)

    3) 有了luajit这个可执行文件,我们就可以拿它编译lua脚本了。

      这时cd进src目录

      输入./luajit -b hello1.lua hello1.out

      这个hello1.lua应该是你要编译的源文件,必要时请带上路径。

      注意:你不能把luajit这个可执行文件单独拿出来到别处去运行,因为在src里有些它需要依赖的东西,如果你想把luajit移地方,那你把(当前已经在src文件夹了)luajit、jit文件夹、好几个.dasc文件打个包,这样你就可以到其他的地方运行luajit了。

    4)把这个hello1.out加到你工程里,就可以直接当成普通的脚本一样运行。

    【严重注意】:如果你hello1.lua里面写了require "hello2"那你最好把hello2.lua的生成的文件也命名成hello2.lua,否则再执行hello1.lua的时候就找不到依赖的hello2.lua。当然你也可以require的文件写带后缀名的文件,但这我没有试验过行不行。

    方法二:

    方法二的前提是你不用luajit,而使用lua。

    这样这种方法是真正加密,原理是我们自己替换掉lua的的loader函数。

    其实这种方法是因为我之前以为coco2dx用的lua,我在调试lua代码的时候发现的,当然也受到别的大神的启发。

    先贴出一段代码

    LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) {
      LoadF lf;
      int status, readstatus;
      int c;
      int fnameindex = lua_gettop(L) + 1;  /* index of filename on the stack */
      lf.extraline = 0;
      if (filename == NULL) {
        lua_pushliteral(L, "=stdin");
        lf.f = stdin;
      }
      else {
        lua_pushfstring(L, "@%s", filename);
        lf.f = fopen(filename, "r");
        if (lf.f == NULL) return errfile(L, "open", fnameindex);
      }
      c = getc(lf.f);
      if (c == '#') {  /* Unix exec. file? */
        lf.extraline = 1;
        while ((c = getc(lf.f)) != EOF && c != '\n') ;  /* skip first line */
        if (c == '\n') c = getc(lf.f);
      }
      if (c == LUA_SIGNATURE[0] && filename) {  /* binary file? */
        lf.f = freopen(filename, "rb", lf.f);  /* reopen in binary mode */
        if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
        /* skip eventual `#!...' */
       while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) ;
        lf.extraline = 0;
      }
      ungetc(c, lf.f);
      status = lua_load(L, getF, &lf, lua_tostring(L, -1));
      readstatus = ferror(lf.f);
      if (filename) fclose(lf.f);  /* close file (even in case of errors) */
      if (readstatus) {
        lua_settop(L, fnameindex);  /* ignore results from `lua_load' */
        return errfile(L, "read", fnameindex);
      }
      lua_remove(L, fnameindex);
      return status;
    }

    每当要加载新的lua文件时都会调用luaL_loadfile,当走到status = lua_load(L, getF, &lf, lua_tostring(L, -1))时就说明要从文件读取内容了,注意这里穿了一个参数getF,它其实是个函数指针,我们把这个贴出来

    static const char *getF (lua_State *L, void *ud, size_t *size) {
      LoadF *lf = (LoadF *)ud;
      (void)L;
      if (lf->extraline) {
        lf->extraline = 0;
        *size = 1;
        return "\n";
      }
      if (feof(lf->f)) return NULL;
      *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f);
      return (*size > 0) ? lf->buff : NULL;
    }

    这个函数走完了,lf->buff就有了脚本文件的内容,说道这俩大家就明白了把,我们可以在getF里搞点小动作,比如这样

    #if 0
    static const char *getF (lua_State *L, void *ud, size_t *size) {
      LoadF *lf = (LoadF *)ud;
      (void)L;
      if (lf->extraline) {
        lf->extraline = 0;
        *size = 1;
        return "\n";
      }
      if (feof(lf->f)) return NULL;
      *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f);
      return (*size > 0) ? lf->buff : NULL;
    }
    
    #else
    int DeCode(char *pBuffer,int nSize);
    
    static const char *getF (lua_State *L, void *ud, size_t *size) {
    	LoadF *lf = (LoadF *)ud;
    	(void)L;
    	if (lf->extraline) {
    		lf->extraline = 0;
    		*size = 1;
    		return "\n";
    	}
    	if (feof(lf->f)) return NULL;
    	*size = fread(lf->buff, 1, sizeof(lf->buff), lf->f);
    	
    	DeCode(lf->buff,*size); //decode file content
    	
    	return (*size > 0) ? lf->buff : NULL;
    }
    #endif
    

      这样这个lua库编译是会通过,但是链接是不通过的,就需要我们在外面实现一下DeCode函数。

    那我们写个测试程序

    #include <stdlib.h>
    #include <fstream>
    
    #include <Windows.h>
    
    extern "C" {
    #include "lua.h"
    #include "lualib.h"
    #include "lauxlib.h"
    }
    
    using namespace std;
    
    #define CODE_MASK 250	//:)
    
    extern "C" {
    int EnCode(char *pBuffer,int nSize)
    {
    	for (size_t i=0; i<nSize; i++)
    	{
    		pBuffer[i] ^= CODE_MASK;
    	}
    	return nSize;
    }
    
    int DeCode(char *pBuffer,int nSize)
    {
    	return EnCode(pBuffer,nSize);
    }
    
    }
    
    string GetCodeFile(const char* file)
    {
    	char * pBuffer = NULL;
    	size_t nSize = 0;
    
    	//read file
    	FILE *fp = fopen(file, "rb");
    	fseek(fp,0,SEEK_END);
    	nSize = ftell(fp);
    	fseek(fp,0,SEEK_SET);
    	pBuffer = new char[nSize];
    	nSize = fread(pBuffer,sizeof(char), nSize,fp);
    	fclose(fp);
    
    	//encode
    	EnCode(pBuffer,nSize);
    
    	//save file
    	string str = file;
    	str+=".out";
    	FILE *fpw = fopen(str.c_str(), "wb");
    	fwrite(pBuffer, nSize, 1, fpw);
    	fclose(fpw);
    
    	//free memory
    	delete pBuffer;
    
    	return str;
    }
    
    int main(int argc,char* argv[])
    {
    	
    	lua_State   *L = lua_open();
    	luaL_openlibs(L);
    	if (2==argc)
    	{
    		//do original file
    		//luaL_dofile(L, argv[1]);
    
    		//do code file
    		luaL_dofile(L, GetCodeFile(argv[1]).c_str());
    	}
    
    	system("PAUSE");
    
    	return 0;
    }

    这种方法我自己测试可行的。怎么加密在你,我这只是说了个简单的异或加密解密,

    需要注意的是:实际上我跟踪代码发现lua并不是一次性把整个文件全部加载,而是每次512的字节。也就是说如果你的文件很大的话,加载它是会多次调用getF

       如有疑问eMail我,blue-1986@hotmail.com

     如有错误请指正,大家一起进步。

  • 相关阅读:
    type() & dir()

    手机操作API
    APP模拟手势高级操作
    APP元素事件操作API
    APP元素信息操作API
    APP元素定位操作
    手机控件查看工具uiautomatorviewer
    App基础操作API
    Appium入门
  • 原文地址:https://www.cnblogs.com/mrblue/p/3122543.html
Copyright © 2020-2023  润新知