Loading... # 一、缘起 之前文章[某游戏自动化脚本系列(四)核心功能DLL实现](http://www.wuubu.com/index.php/2021/04/09/15.html)的方法,需要在每次需要多个返回值的时候,在外层增加函数调用,但是这种方式侵入程度太高,容易被发现。 # 二、思路 通过反复研究lua 5.1.4源码,发现可以自己实现单独的api调用函数,lua源码如下: ```c LUA_API int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc) { struct CallS c; int status; ptrdiff_t func; lua_lock(L); api_checknelems(L, nargs+1); checkresults(L, nargs, nresults); if (errfunc == 0) func = 0; else { StkId o = index2adr(L, errfunc); api_checkvalidindex(L, o); func = savestack(L, o); } c.func = L->top - (nargs+1); /* function to be called */ c.nresults = nresults; status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func); adjustresults(L, nresults); lua_unlock(L); return status; } ``` errfunc可以先忽略,主要填充了 CallS结构,再调用luaD_pcall。 与下面游戏lua_pcall伪代码对比。 ```c/c++ void __fastcall lua_pcall(__int64 pLua_State, int nargs, int nresults, unsigned int msgh) { __int64 v7; // r10 __int64 func; // rax void *v9; // rax __int64 v10; // r9 __int64 v11; // [rsp+30h] [rbp+0h] BYREF int v12; // [rsp+38h] [rbp+8h] if ( msgh ) { v9 = index2adr(pLua_State, msgh); v7 = *(_QWORD *)(pLua_State + 88); func = (__int64)v9 - v7; } else { v7 = *(_QWORD *)(pLua_State + 88); func = 0i64; } v10 = *(_QWORD *)(pLua_State + 40); v12 = nresults; v11 = v10 - 24i64 * (nargs + 1); luaD_pcall(pLua_State, (unsigned int)&f_call, (unsigned int)&v11, v11 - v7, func); JUMPOUT(0x140455D59i64); // 后面为混淆过的返回地址检查及其处理函数 } ``` 不难看出,我们需要做以下几件事情: 1. 所需调用的API入栈 通过lua源码``#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, (s))``发现,`lua_setglobal`是调用的`lua_setfield`,而游戏中`LUA_GLOBALSINDEX`的值可通过`index2adr`来确定。 2. 参数入栈(可能有多个) 通过调用`lua_push*`实现。 3. 调用`lua_pcall` 同时通过动态修改硬编码解决返回地址检查。 4. 返回值获取 通过调用`lua_to*`实现。 # 三、弊端 调用的时候,需要确定具体有几个返回值,略显麻烦(别个脚本的都不需要)。 最后修改:2021 年 09 月 22 日 11 : 37 AM © 禁止转载