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里搞点小动作,比如这样
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
#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函数。
那我们写个测试程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
|
#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
如有错误请指正,大家一起进步。