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
如有錯誤請指正,大家一起進步。