cocos2d-x-lua工程的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

 如有錯誤請指正,大家一起進步。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM