require 实现
- require函数在实现上是依次调用package.searchers(lua51中是package.loaders)中的加载函数,成功后返回。在loadlib.c文件中有四个加载函数的实现,分别为searcher_preload, searcher_Lua, searcher_C, searcher_Croot。
- searcher_preload是从读取LUA_REGISTRYINDEX的_PRELOAD字段,已经require过的文件会写入到该表中
- searcher_Lua是根据文件名查找package.path中的所有路径的lua文件,存在文件则返回
- searcher_C是搜索package.cpath中的所有路径下的库文件
- searcher_Croot是对require(“a.b.c”)的情况,读取c库,然后查找函数名为lua_a_b_c的lua_CFunction函数
static void findloader (lua_State *L, const char *name) {
int i;
luaL_Buffer msg; /* to build error message */
luaL_buffinit(L, &msg);
lua_getfield(L, lua_upvalueindex(1), "searchers"); /* will be at index 3 */
if (!lua_istable(L, 3))
luaL_error(L, LUA_QL("package.searchers") " must be a table");
/* iterate over available searchers to find a loader */
for (i = 1; ; i++) {
lua_rawgeti(L, 3, i); /* get a searcher */
if (lua_isnil(L, -1)) { /* no more searchers? */
lua_pop(L, 1); /* remove nil */
luaL_pushresult(&msg); /* create error message */
luaL_error(L, "module " LUA_QS " not found:%s",
name, lua_tostring(L, -1));
}
lua_pushstring(L, name);
lua_call(L, 1, 2); /* call it */
if (lua_isfunction(L, -2)) /* did it find a loader? */
return; /* module loader found */
else if (lua_isstring(L, -2)) { /* searcher returned error message? */
lua_pop(L, 1); /* remove extra return */
luaL_addvalue(&msg); /* concatenate error message */
}
else
lua_pop(L, 2); /* remove both returns */
}
}
- 现在要实现在require时能读取加密文件,有两种办法,一种是直接修改源代码,即修改第二个加载函数,重新实现其中读取文件内容的函数,第二种办法是在lua中修改package.searchers表,在加载器的第一和第二种之间添加一个加载器函数,该加载器模拟searcher_Lua函数,搜索path路径,然后逐个匹配文件,然后读取文件内容,解密,然后调用load加载并返回(c中为luaL_loadbufferx),这里在加载时最好传入文件名作为来源参数,方便在调试信息中定位.
- 加密方案可使用类似xxtea轻量级的加密算法
- 在对lua文件进行加密打包时,可以在文件头写入指定的签名内容,以方便在解密前预先判断是否为有效的加密文件
修改lua源代码方案
- 在searcher_Lua中最终是调用lua_load(L, getF, &lf, lua_tostring(L, -1), mode)加载源文件,该函数的第二个参数getF是一个lua_Reader函数,所以这里可以重写该函数以实现解密,也可以向外部暴露一个接口用来将自定义的文件读取函数作为参数传给lua_load。下面是原版的getF实现
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;
}
外部修改加载器方案
- 直接修改package.searchers表,向其中添加加载器,c版实现如下
void addLuaSearcher(lua_CFunction func)
{
if (!func) return;
// stack content after the invoking of the function
// get loader table
lua_getglobal(m_state, "package"); /* L: package */
lua_getfield(m_state, -1, "loaders"); /* L: package, loaders */
// insert loader into index 2
lua_pushcfunction(m_state, func); /* L: package, loaders, func */
for (int i = lua_objlen(m_state, -2) + 1; i > 2; --i)
{
lua_rawgeti(m_state, -2, i - 1); /* L: package, loaders, func, function */
// we call lua_rawgeti, so the loader table now is at -3
lua_rawseti(m_state, -3, i); /* L: package, loaders, func */
}
lua_rawseti(m_state, -2, 2); /* L: package, loaders */
// set loaders into package
lua_setfield(m_state, -2, "loaders"); /* L: package */
lua_pop(m_state, 1);
}
- 加载器函数实现根据传入的文件名,逐个匹配的package.path中的内容,存在文件后,然后读取文件内容,解密,最后再将解出的内容调用load加载并返回(c中为luaL_loadbufferx),实现可以参照lua源码中的searcher_Lua实现
时间: 2024-10-03 11:05:13