commit 7bee616b8e071c39487405cf68764e09985880c5 Author: Redo Date: Mon Oct 6 23:04:30 2025 -0500 initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9ea0f13 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.* +!.gitignore diff --git a/inc/lua/lauxlib.h b/inc/lua/lauxlib.h new file mode 100644 index 0000000..a44f027 --- /dev/null +++ b/inc/lua/lauxlib.h @@ -0,0 +1,161 @@ +/* +** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $ +** Auxiliary functions for building Lua libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lauxlib_h +#define lauxlib_h + + +#include +#include + +#include "lua.h" + + +/* extra error code for `luaL_load' */ +#define LUA_ERRFILE (LUA_ERRERR+1) + +typedef struct luaL_Reg { + const char *name; + lua_CFunction func; +} luaL_Reg; + +LUALIB_API void (luaL_openlib) (lua_State *L, const char *libname, + const luaL_Reg *l, int nup); +LUALIB_API void (luaL_register) (lua_State *L, const char *libname, + const luaL_Reg *l); +LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e); +LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e); +LUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname); +LUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg); +LUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg, + size_t *l); +LUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg, + const char *def, size_t *l); +LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg); +LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def); + +LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg); +LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg, + lua_Integer def); + +LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg); +LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t); +LUALIB_API void (luaL_checkany) (lua_State *L, int narg); + +LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname); +LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname); + +LUALIB_API void (luaL_where) (lua_State *L, int lvl); +LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...); + +LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def, + const char *const lst[]); + +/* pre-defined references */ +#define LUA_NOREF (-2) +#define LUA_REFNIL (-1) + +LUALIB_API int (luaL_ref) (lua_State *L, int t); +LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref); + +LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename); +LUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz, + const char *name); +LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s); + +LUALIB_API lua_State *(luaL_newstate) (void); + + +LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p, + const char *r); + +LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx, + const char *fname, int szhint); + +/* From Lua 5.2. */ +LUALIB_API int luaL_fileresult(lua_State *L, int stat, const char *fname); +LUALIB_API int luaL_execresult(lua_State *L, int stat); +LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename, + const char *mode); +LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz, + const char *name, const char *mode); +LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1, const char *msg, + int level); +LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup); +LUALIB_API void (luaL_pushmodule) (lua_State *L, const char *modname, + int sizehint); +LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname); +LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname); + + +/* +** =============================================================== +** some useful macros +** =============================================================== +*/ + +#define luaL_argcheck(L, cond,numarg,extramsg) \ + ((void)((cond) || luaL_argerror(L, (numarg), (extramsg)))) +#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL)) +#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL)) +#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) +#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) +#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n))) +#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d))) + +#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i))) + +#define luaL_dofile(L, fn) \ + (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_dostring(L, s) \ + (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) + +#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) + +/* From Lua 5.2. */ +#define luaL_newlibtable(L, l) \ + lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1) +#define luaL_newlib(L, l) (luaL_newlibtable(L, l), luaL_setfuncs(L, l, 0)) + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + + + +typedef struct luaL_Buffer { + char *p; /* current position in buffer */ + int lvl; /* number of strings in the stack (level) */ + lua_State *L; + char buffer[LUAL_BUFFERSIZE]; +} luaL_Buffer; + +#define luaL_addchar(B,c) \ + ((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \ + (*(B)->p++ = (char)(c))) + +/* compatibility only */ +#define luaL_putchar(B,c) luaL_addchar(B,c) + +#define luaL_addsize(B,n) ((B)->p += (n)) + +LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B); +LUALIB_API char *(luaL_prepbuffer) (luaL_Buffer *B); +LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l); +LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s); +LUALIB_API void (luaL_addvalue) (luaL_Buffer *B); +LUALIB_API void (luaL_pushresult) (luaL_Buffer *B); + + +/* }====================================================== */ + +#endif diff --git a/inc/lua/lua.h b/inc/lua/lua.h new file mode 100644 index 0000000..850bd79 --- /dev/null +++ b/inc/lua/lua.h @@ -0,0 +1,402 @@ +/* +** $Id: lua.h,v 1.218.1.5 2008/08/06 13:30:12 roberto Exp $ +** Lua - An Extensible Extension Language +** Lua.org, PUC-Rio, Brazil (http://www.lua.org) +** See Copyright Notice at the end of this file +*/ + + +#ifndef lua_h +#define lua_h + +#include +#include + + +#include "luaconf.h" + + +#define LUA_VERSION "Lua 5.1" +#define LUA_RELEASE "Lua 5.1.4" +#define LUA_VERSION_NUM 501 +#define LUA_COPYRIGHT "Copyright (C) 1994-2008 Lua.org, PUC-Rio" +#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo & W. Celes" + + +/* mark for precompiled code (`Lua') */ +#define LUA_SIGNATURE "\033Lua" + +/* option for multiple returns in `lua_pcall' and `lua_call' */ +#define LUA_MULTRET (-1) + + +/* +** pseudo-indices +*/ +#define LUA_REGISTRYINDEX (-10000) +#define LUA_ENVIRONINDEX (-10001) +#define LUA_GLOBALSINDEX (-10002) +#define lua_upvalueindex(i) (LUA_GLOBALSINDEX-(i)) + + +/* thread status */ +#define LUA_OK 0 +#define LUA_YIELD 1 +#define LUA_ERRRUN 2 +#define LUA_ERRSYNTAX 3 +#define LUA_ERRMEM 4 +#define LUA_ERRERR 5 + + +typedef struct lua_State lua_State; + +typedef int (*lua_CFunction) (lua_State *L); + + +/* +** functions that read/write blocks when loading/dumping Lua chunks +*/ +typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz); + +typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud); + + +/* +** prototype for memory-allocation functions +*/ +typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); + + +/* +** basic types +*/ +#define LUA_TNONE (-1) + +#define LUA_TNIL 0 +#define LUA_TBOOLEAN 1 +#define LUA_TLIGHTUSERDATA 2 +#define LUA_TNUMBER 3 +#define LUA_TSTRING 4 +#define LUA_TTABLE 5 +#define LUA_TFUNCTION 6 +#define LUA_TUSERDATA 7 +#define LUA_TTHREAD 8 + + + +/* minimum Lua stack available to a C function */ +#define LUA_MINSTACK 20 + + +/* +** generic extra include file +*/ +#if defined(LUA_USER_H) +#include LUA_USER_H +#endif + + +/* type of numbers in Lua */ +typedef LUA_NUMBER lua_Number; + + +/* type for integer functions */ +typedef LUA_INTEGER lua_Integer; + + + +/* +** state manipulation +*/ +LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); +LUA_API void (lua_close) (lua_State *L); +LUA_API lua_State *(lua_newthread) (lua_State *L); + +LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); + + +/* +** basic stack manipulation +*/ +LUA_API int (lua_gettop) (lua_State *L); +LUA_API void (lua_settop) (lua_State *L, int idx); +LUA_API void (lua_pushvalue) (lua_State *L, int idx); +LUA_API void (lua_remove) (lua_State *L, int idx); +LUA_API void (lua_insert) (lua_State *L, int idx); +LUA_API void (lua_replace) (lua_State *L, int idx); +LUA_API int (lua_checkstack) (lua_State *L, int sz); + +LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n); + + +/* +** access functions (stack -> C) +*/ + +LUA_API int (lua_isnumber) (lua_State *L, int idx); +LUA_API int (lua_isstring) (lua_State *L, int idx); +LUA_API int (lua_iscfunction) (lua_State *L, int idx); +LUA_API int (lua_isuserdata) (lua_State *L, int idx); +LUA_API int (lua_type) (lua_State *L, int idx); +LUA_API const char *(lua_typename) (lua_State *L, int tp); + +LUA_API int (lua_equal) (lua_State *L, int idx1, int idx2); +LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2); +LUA_API int (lua_lessthan) (lua_State *L, int idx1, int idx2); + +LUA_API lua_Number (lua_tonumber) (lua_State *L, int idx); +LUA_API lua_Integer (lua_tointeger) (lua_State *L, int idx); +LUA_API int (lua_toboolean) (lua_State *L, int idx); +LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len); +LUA_API size_t (lua_objlen) (lua_State *L, int idx); +LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx); +LUA_API void *(lua_touserdata) (lua_State *L, int idx); +LUA_API lua_State *(lua_tothread) (lua_State *L, int idx); +LUA_API const void *(lua_topointer) (lua_State *L, int idx); + + +/* +** push functions (C -> stack) +*/ +LUA_API void (lua_pushnil) (lua_State *L); +LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n); +LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n); +LUA_API void (lua_pushlstring) (lua_State *L, const char *s, size_t l); +LUA_API void (lua_pushstring) (lua_State *L, const char *s); +LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt, + va_list argp); +LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...); +LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); +LUA_API void (lua_pushboolean) (lua_State *L, int b); +LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p); +LUA_API int (lua_pushthread) (lua_State *L); + + +/* +** get functions (Lua -> stack) +*/ +LUA_API void (lua_gettable) (lua_State *L, int idx); +LUA_API void (lua_getfield) (lua_State *L, int idx, const char *k); +LUA_API void (lua_rawget) (lua_State *L, int idx); +LUA_API void (lua_rawgeti) (lua_State *L, int idx, int n); +LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec); +LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz); +LUA_API int (lua_getmetatable) (lua_State *L, int objindex); +LUA_API void (lua_getfenv) (lua_State *L, int idx); + + +/* +** set functions (stack -> Lua) +*/ +LUA_API void (lua_settable) (lua_State *L, int idx); +LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k); +LUA_API void (lua_rawset) (lua_State *L, int idx); +LUA_API void (lua_rawseti) (lua_State *L, int idx, int n); +LUA_API int (lua_setmetatable) (lua_State *L, int objindex); +LUA_API int (lua_setfenv) (lua_State *L, int idx); + + +/* +** `load' and `call' functions (load and run Lua code) +*/ +LUA_API void (lua_call) (lua_State *L, int nargs, int nresults); +LUA_API int (lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc); +LUA_API int (lua_cpcall) (lua_State *L, lua_CFunction func, void *ud); +LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt, + const char *chunkname); + +LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data); + + +/* +** coroutine functions +*/ +LUA_API int (lua_yield) (lua_State *L, int nresults); +LUA_API int (lua_resume) (lua_State *L, int narg); +LUA_API int (lua_status) (lua_State *L); + +/* +** garbage-collection function and options +*/ + +#define LUA_GCSTOP 0 +#define LUA_GCRESTART 1 +#define LUA_GCCOLLECT 2 +#define LUA_GCCOUNT 3 +#define LUA_GCCOUNTB 4 +#define LUA_GCSTEP 5 +#define LUA_GCSETPAUSE 6 +#define LUA_GCSETSTEPMUL 7 +#define LUA_GCISRUNNING 9 + +LUA_API int (lua_gc) (lua_State *L, int what, int data); + + +/* +** miscellaneous functions +*/ + +LUA_API int (lua_error) (lua_State *L); + +LUA_API int (lua_next) (lua_State *L, int idx); + +LUA_API void (lua_concat) (lua_State *L, int n); + +LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); +LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud); + + + +/* +** =============================================================== +** some useful macros +** =============================================================== +*/ + +#define lua_pop(L,n) lua_settop(L, -(n)-1) + +#define lua_newtable(L) lua_createtable(L, 0, 0) + +#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) + +#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) + +#define lua_strlen(L,i) lua_objlen(L, (i)) + +#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION) +#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE) +#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA) +#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL) +#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN) +#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD) +#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE) +#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0) + +#define lua_pushliteral(L, s) \ + lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1) + +#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, (s)) +#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s)) + +#define lua_tostring(L,i) lua_tolstring(L, (i), NULL) + + + +/* +** compatibility macros and functions +*/ + +#define lua_open() luaL_newstate() + +#define lua_getregistry(L) lua_pushvalue(L, LUA_REGISTRYINDEX) + +#define lua_getgccount(L) lua_gc(L, LUA_GCCOUNT, 0) + +#define lua_Chunkreader lua_Reader +#define lua_Chunkwriter lua_Writer + + +/* hack */ +LUA_API void lua_setlevel (lua_State *from, lua_State *to); + + +/* +** {====================================================================== +** Debug API +** ======================================================================= +*/ + + +/* +** Event codes +*/ +#define LUA_HOOKCALL 0 +#define LUA_HOOKRET 1 +#define LUA_HOOKLINE 2 +#define LUA_HOOKCOUNT 3 +#define LUA_HOOKTAILRET 4 + + +/* +** Event masks +*/ +#define LUA_MASKCALL (1 << LUA_HOOKCALL) +#define LUA_MASKRET (1 << LUA_HOOKRET) +#define LUA_MASKLINE (1 << LUA_HOOKLINE) +#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT) + +typedef struct lua_Debug lua_Debug; /* activation record */ + + +/* Functions to be called by the debuger in specific events */ +typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); + + +LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar); +LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar); +LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n); +LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n); +LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count); +LUA_API lua_Hook lua_gethook (lua_State *L); +LUA_API int lua_gethookmask (lua_State *L); +LUA_API int lua_gethookcount (lua_State *L); + +/* From Lua 5.2. */ +LUA_API void *lua_upvalueid (lua_State *L, int idx, int n); +LUA_API void lua_upvaluejoin (lua_State *L, int idx1, int n1, int idx2, int n2); +LUA_API int lua_loadx (lua_State *L, lua_Reader reader, void *dt, + const char *chunkname, const char *mode); +LUA_API const lua_Number *lua_version (lua_State *L); +LUA_API void lua_copy (lua_State *L, int fromidx, int toidx); +LUA_API lua_Number lua_tonumberx (lua_State *L, int idx, int *isnum); +LUA_API lua_Integer lua_tointegerx (lua_State *L, int idx, int *isnum); + +/* From Lua 5.3. */ +LUA_API int lua_isyieldable (lua_State *L); + + +struct lua_Debug { + int event; + const char *name; /* (n) */ + const char *namewhat; /* (n) `global', `local', `field', `method' */ + const char *what; /* (S) `Lua', `C', `main', `tail' */ + const char *source; /* (S) */ + int currentline; /* (l) */ + int nups; /* (u) number of upvalues */ + int linedefined; /* (S) */ + int lastlinedefined; /* (S) */ + char short_src[LUA_IDSIZE]; /* (S) */ + /* private part */ + int i_ci; /* active function */ +}; + +/* }====================================================================== */ + + +/****************************************************************************** +* Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +******************************************************************************/ + + +#endif diff --git a/inc/lua/lua.hpp b/inc/lua/lua.hpp new file mode 100644 index 0000000..07e9002 --- /dev/null +++ b/inc/lua/lua.hpp @@ -0,0 +1,9 @@ +// C++ wrapper for LuaJIT header files. + +extern "C" { +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" +#include "luajit.h" +} + diff --git a/inc/lua/lua5.1.dll b/inc/lua/lua5.1.dll new file mode 100644 index 0000000..8f09561 Binary files /dev/null and b/inc/lua/lua5.1.dll differ diff --git a/inc/lua/luaconf.h b/inc/lua/luaconf.h new file mode 100644 index 0000000..c2d29d9 --- /dev/null +++ b/inc/lua/luaconf.h @@ -0,0 +1,152 @@ +/* +** Configuration header. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef luaconf_h +#define luaconf_h + +#ifndef WINVER +#define WINVER 0x0501 +#endif +#include +#include + +/* Default path for loading Lua and C modules with require(). */ +#if defined(_WIN32) +/* +** In Windows, any exclamation mark ('!') in the path is replaced by the +** path of the directory of the executable file of the current process. +*/ +#define LUA_LDIR "!\\lua\\" +#define LUA_CDIR "!\\" +#define LUA_PATH_DEFAULT \ + ".\\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" +#define LUA_CPATH_DEFAULT \ + ".\\?.dll;" LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll" +#else +/* +** Note to distribution maintainers: do NOT patch the following lines! +** Please read ../doc/install.html#distro and pass PREFIX=/usr instead. +*/ +#ifndef LUA_MULTILIB +#define LUA_MULTILIB "lib" +#endif +#ifndef LUA_LMULTILIB +#define LUA_LMULTILIB "lib" +#endif +#define LUA_LROOT "/usr/local" +#define LUA_LUADIR "/lua/5.1/" +#define LUA_LJDIR "/luajit-2.1.0-beta3/" + +#ifdef LUA_ROOT +#define LUA_JROOT LUA_ROOT +#define LUA_RLDIR LUA_ROOT "/share" LUA_LUADIR +#define LUA_RCDIR LUA_ROOT "/" LUA_MULTILIB LUA_LUADIR +#define LUA_RLPATH ";" LUA_RLDIR "?.lua;" LUA_RLDIR "?/init.lua" +#define LUA_RCPATH ";" LUA_RCDIR "?.so" +#else +#define LUA_JROOT LUA_LROOT +#define LUA_RLPATH +#define LUA_RCPATH +#endif + +#define LUA_JPATH ";" LUA_JROOT "/share" LUA_LJDIR "?.lua" +#define LUA_LLDIR LUA_LROOT "/share" LUA_LUADIR +#define LUA_LCDIR LUA_LROOT "/" LUA_LMULTILIB LUA_LUADIR +#define LUA_LLPATH ";" LUA_LLDIR "?.lua;" LUA_LLDIR "?/init.lua" +#define LUA_LCPATH1 ";" LUA_LCDIR "?.so" +#define LUA_LCPATH2 ";" LUA_LCDIR "loadall.so" + +#define LUA_PATH_DEFAULT "./?.lua" LUA_JPATH LUA_LLPATH LUA_RLPATH +#define LUA_CPATH_DEFAULT "./?.so" LUA_LCPATH1 LUA_RCPATH LUA_LCPATH2 +#endif + +/* Environment variable names for path overrides and initialization code. */ +#define LUA_PATH "LUA_PATH" +#define LUA_CPATH "LUA_CPATH" +#define LUA_INIT "LUA_INIT" + +/* Special file system characters. */ +#if defined(_WIN32) +#define LUA_DIRSEP "\\" +#else +#define LUA_DIRSEP "/" +#endif +#define LUA_PATHSEP ";" +#define LUA_PATH_MARK "?" +#define LUA_EXECDIR "!" +#define LUA_IGMARK "-" +#define LUA_PATH_CONFIG \ + LUA_DIRSEP "\n" LUA_PATHSEP "\n" LUA_PATH_MARK "\n" \ + LUA_EXECDIR "\n" LUA_IGMARK "\n" + +/* Quoting in error messages. */ +#define LUA_QL(x) "'" x "'" +#define LUA_QS LUA_QL("%s") + +/* Various tunables. */ +#define LUAI_MAXSTACK 65500 /* Max. # of stack slots for a thread (<64K). */ +#define LUAI_MAXCSTACK 8000 /* Max. # of stack slots for a C func (<10K). */ +#define LUAI_GCPAUSE 200 /* Pause GC until memory is at 200%. */ +#define LUAI_GCMUL 200 /* Run GC at 200% of allocation speed. */ +#define LUA_MAXCAPTURES 32 /* Max. pattern captures. */ + +/* Configuration for the frontend (the luajit executable). */ +#if defined(luajit_c) +#define LUA_PROGNAME "luajit" /* Fallback frontend name. */ +#define LUA_PROMPT "> " /* Interactive prompt. */ +#define LUA_PROMPT2 ">> " /* Continuation prompt. */ +#define LUA_MAXINPUT 512 /* Max. input line length. */ +#endif + +/* Note: changing the following defines breaks the Lua 5.1 ABI. */ +#define LUA_INTEGER ptrdiff_t +#define LUA_IDSIZE 60 /* Size of lua_Debug.short_src. */ +/* +** Size of lauxlib and io.* on-stack buffers. Weird workaround to avoid using +** unreasonable amounts of stack space, but still retain ABI compatibility. +** Blame Lua for depending on BUFSIZ in the ABI, blame **** for wrecking it. +*/ +#define LUAL_BUFFERSIZE (BUFSIZ > 16384 ? 8192 : BUFSIZ) + +/* The following defines are here only for compatibility with luaconf.h +** from the standard Lua distribution. They must not be changed for LuaJIT. +*/ +#define LUA_NUMBER_DOUBLE +#define LUA_NUMBER double +#define LUAI_UACNUMBER double +#define LUA_NUMBER_SCAN "%lf" +#define LUA_NUMBER_FMT "%.14g" +#define lua_number2str(s, n) sprintf((s), LUA_NUMBER_FMT, (n)) +#define LUAI_MAXNUMBER2STR 32 +#define LUA_INTFRMLEN "l" +#define LUA_INTFRM_T long + +/* Linkage of public API functions. */ +#if defined(LUA_BUILD_AS_DLL) +#if defined(LUA_CORE) || defined(LUA_LIB) +#define LUA_API __declspec(dllexport) +#else +#define LUA_API __declspec(dllimport) +#endif +#else +#define LUA_API extern +#endif + +#define LUALIB_API LUA_API + +/* Support for internal assertions. */ +#if defined(LUA_USE_ASSERT) || defined(LUA_USE_APICHECK) +#include +#endif +#ifdef LUA_USE_ASSERT +#define lua_assert(x) assert(x) +#endif +#ifdef LUA_USE_APICHECK +#define luai_apicheck(L, o) { (void)L; assert(o); } +#else +#define luai_apicheck(L, o) { (void)L; } +#endif + +#endif diff --git a/inc/lua/luajit.h b/inc/lua/luajit.h new file mode 100644 index 0000000..708a5a1 --- /dev/null +++ b/inc/lua/luajit.h @@ -0,0 +1,79 @@ +/* +** LuaJIT -- a Just-In-Time Compiler for Lua. http://luajit.org/ +** +** Copyright (C) 2005-2017 Mike Pall. All rights reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +** [ MIT license: http://www.opensource.org/licenses/mit-license.php ] +*/ + +#ifndef _LUAJIT_H +#define _LUAJIT_H + +#include "lua.h" + +#define LUAJIT_VERSION "LuaJIT 2.1.0-beta3" +#define LUAJIT_VERSION_NUM 20100 /* Version 2.1.0 = 02.01.00. */ +#define LUAJIT_VERSION_SYM luaJIT_version_2_1_0_beta3 +#define LUAJIT_COPYRIGHT "Copyright (C) 2005-2017 Mike Pall" +#define LUAJIT_URL "http://luajit.org/" + +/* Modes for luaJIT_setmode. */ +#define LUAJIT_MODE_MASK 0x00ff + +enum { + LUAJIT_MODE_ENGINE, /* Set mode for whole JIT engine. */ + LUAJIT_MODE_DEBUG, /* Set debug mode (idx = level). */ + + LUAJIT_MODE_FUNC, /* Change mode for a function. */ + LUAJIT_MODE_ALLFUNC, /* Recurse into subroutine protos. */ + LUAJIT_MODE_ALLSUBFUNC, /* Change only the subroutines. */ + + LUAJIT_MODE_TRACE, /* Flush a compiled trace. */ + + LUAJIT_MODE_WRAPCFUNC = 0x10, /* Set wrapper mode for C function calls. */ + + LUAJIT_MODE_MAX +}; + +/* Flags or'ed in to the mode. */ +#define LUAJIT_MODE_OFF 0x0000 /* Turn feature off. */ +#define LUAJIT_MODE_ON 0x0100 /* Turn feature on. */ +#define LUAJIT_MODE_FLUSH 0x0200 /* Flush JIT-compiled code. */ + +/* LuaJIT public C API. */ + +/* Control the JIT engine. */ +LUA_API int luaJIT_setmode(lua_State *L, int idx, int mode); + +/* Low-overhead profiling API. */ +typedef void (*luaJIT_profile_callback)(void *data, lua_State *L, + int samples, int vmstate); +LUA_API void luaJIT_profile_start(lua_State *L, const char *mode, + luaJIT_profile_callback cb, void *data); +LUA_API void luaJIT_profile_stop(lua_State *L); +LUA_API const char *luaJIT_profile_dumpstack(lua_State *L, const char *fmt, + int depth, size_t *len); + +/* Enforce (dynamic) linker error for version mismatches. Call from main. */ +LUA_API void LUAJIT_VERSION_SYM(void); + +#endif diff --git a/inc/lua/lualib.h b/inc/lua/lualib.h new file mode 100644 index 0000000..bfc130a --- /dev/null +++ b/inc/lua/lualib.h @@ -0,0 +1,43 @@ +/* +** Standard library header. +** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +*/ + +#ifndef _LUALIB_H +#define _LUALIB_H + +#include "lua.h" + +#define LUA_FILEHANDLE "FILE*" + +#define LUA_COLIBNAME "coroutine" +#define LUA_MATHLIBNAME "math" +#define LUA_STRLIBNAME "string" +#define LUA_TABLIBNAME "table" +#define LUA_IOLIBNAME "io" +#define LUA_OSLIBNAME "os" +#define LUA_LOADLIBNAME "package" +#define LUA_DBLIBNAME "debug" +#define LUA_BITLIBNAME "bit" +#define LUA_JITLIBNAME "jit" +#define LUA_FFILIBNAME "ffi" + +LUALIB_API int luaopen_base(lua_State *L); +LUALIB_API int luaopen_math(lua_State *L); +LUALIB_API int luaopen_string(lua_State *L); +LUALIB_API int luaopen_table(lua_State *L); +LUALIB_API int luaopen_io(lua_State *L); +LUALIB_API int luaopen_os(lua_State *L); +LUALIB_API int luaopen_package(lua_State *L); +LUALIB_API int luaopen_debug(lua_State *L); +LUALIB_API int luaopen_bit(lua_State *L); +LUALIB_API int luaopen_jit(lua_State *L); +LUALIB_API int luaopen_ffi(lua_State *L); + +LUALIB_API void luaL_openlibs(lua_State *L); + +#ifndef lua_assert +#define lua_assert(x) ((void)0) +#endif + +#endif diff --git a/luahooks32.lua b/luahooks32.lua new file mode 100644 index 0000000..158f5b8 --- /dev/null +++ b/luahooks32.lua @@ -0,0 +1,524 @@ + +hk = require('luahooks32core') + +-- Memory protection +-- Allow writing to code areas +local PAGE_EXECUTE_READWRITE = 0x40 +function hk.protectRWX(addr, len) + return hk.protect(addr, len, PAGE_EXECUTE_READWRITE) +end + +hk._openImages = hk._openImages or {} +-- open library memoization +function hk.open(name) + if name==nil then name = '_baseImage' end + local img = hk._openImages[name] + if img then return img end + img = hk._openRaw(name~='_baseImage' and name or nil) + hk._openImages[name] = img + return img +end + +-- Scanning +-- Convert text-style scan pattern into code-style pattern +-- Input: '11 22 33 ? 44' +-- Output: '\x11\x22\x33\x00\x44', 'xxx?x' +local function patToCode(p) + if p:find('^str:') then -- raw string + local pat = p:sub(4, #p) + local mask = ('x'):rep(#pat) + return pat, mask + else -- hex pattern + if p:find('[^a-fA-F0-9 \r\n\t%?]') then + error('hk pattern: pattern contains invalid character', 3) end + local patT, maskT = {}, {} + for word in p:gmatch('[^ \r\n\t]+') do + if word:find('%?') then + table.insert(patT, string.char(0)) + table.insert(maskT, '?') + else + local val = tonumber(word, 16) + assert(val and val>=0 and val<=255, 'invalid word in scan pattern: '..word, 3) + table.insert(patT, string.char(val)) + table.insert(maskT, 'x') + end + end + return table.concat(patT), table.concat(maskT) + end +end +-- Scan +-- hk.scan('15 1 ? 1f') - return first match (or nil) +-- hk.scan('15 1 ? 1f', 1) - return nth match (or nil) (starting from 1) +-- hk.scan('15 1 ? 1f', true) - return list of all matches (or {}) +local unhookAll, rehookAll +hk._scanResultCache = hk._scanResultCache or {} +function hk.scan(pat, opt, img) + if type(pat)~='string' then + error('hk.scan: argument #1: expected string', 2) end + local code, mask = patToCode(pat) + img = img or hk.open() + opt = opt or 1 + + local _ + local cacheEntry = tostring(img)..':'..pat..':'..tostring(opt) + local res = hk._scanResultCache[cacheEntry] + if res then return res end + + if opt==true then -- find all matches + res = {} + unhookAll() + while true do + local suc,a = pcall(hk._scanRaw, img, code, mask, #res) + if not a then break end + table.insert(res, a) + end + rehookAll() + elseif type(opt)=='number' and opt%1==0 and opt>0 then -- find nth match + unhookAll() + suc,res = pcall(hk._scanRaw, img, code, mask, opt-1) + rehookAll() + else + error('hk.scan: argument #2: expected true, positive integer, or nil', 2) + end + + hk._scanResultCache[cacheEntry] = res + return res +end + + +-- Writing +local function hexToStr(h) + local t = {} + if h:find('[^a-zA-Z0-9 \r\n\t]') then + error('hk.write: invalid character in hex string', 3) end + for w in h:gmatch('[^ \r\n\t]+') do + local v = tonumber(w, 16) + if not (v and v>=0 and v<=255) then + error('hk.write: invalid hex number: '..w, 3) end + table.insert(t, string.char(v)) + end + return table.concat(t) +end +local customWriters = { + hex = function(addr, str, len) + local data = hexToStr(str) + if len then return addr+#data end + return hk.writeStr(addr, data) + end, + str = function(addr, data, len) + if len then return addr+#data end + return hk.writeStr(addr, data) + end, + char = function(addr, val, len) + if len then return addr+1 end + return hk.writeChar(addr, val) + end, + short = function(addr, val, len) + if len then return addr+2 end + return hk.writeShort(addr, val) + end, + int = function(addr, val, len) + if len then return addr+4 end + return hk.writeInt(addr, val) + end, + rel = function(addr, val, len) + if len then return addr+4 end + return hk.writeInt(addr, val - (addr+4)) + end, + float = function(addr, val, len) + if len then return addr+4 end + return hk.writeFloat(addr, val) + end, + double = function(addr, val, len) + if len then return addr+8 end + return hk.writeFloat(addr, val) + end, +} +local customWriterDefaults = { + number = 'int', + string = 'hex', +} +local function writeData(addr, data, typ, len) + if type(data)=='table' then + if typ then + error('hk.write: argument #3: expected nil when argument #2 is table', 2) end + if not table.islist(data) then + error('hk.write: argument #2: table must be a list', 2) end + + local ntyp = nil + for i,v in ipairs(data) do + if type(v)=='string' and v:sub(#v,#v)==':' then + ntyp = v:sub(1,#v-1) + if not customWriters[ntyp] then + error('hk.write: argument #2: expected writer type at index ' + ..i..', got \''..ntyp..'\'', 2) end + else + addr = hk.write(addr, data[i], ntyp, len) + ntyp = nil + end + end + return addr + else + if not typ then + typ = customWriterDefaults[type(data)] + if not typ then + error('hk.write: argument #2: expected string, number, or table') end + end + if not customWriters[typ] then + error('hk.write: argument #3: expected writer type, got \''..typ..'\'') end + return customWriters[typ](addr, data, len) + end +end +function hk.write(addr, data, typ) + return writeData(addr, data, typ, false) +end +-- write to a write-protected area by turning off write protection first, +-- then re-enable write protection afterward +function hk.patch(addr, data, typ) + local len = writeData(addr, data, typ, true) - addr + local oldProt = hk.protectRWX(addr, len) + local addrW = writeData(addr, data, typ, false) + hk.protect(addr, len, oldProt) + return addrW +end + + +-- Hooking +local function writeTrampoline(trAddr, hkAddr, regsPtr) + return hk.write(trAddr, { + -- save registers and flags + 'a3',regsPtr, -- mov [regsPtr],eax + 'b8',regsPtr, -- mov eax,regsPtr + '89 58 04', -- mov [eax+0x04],ebx + '89 48 08', -- mov [eax+0x08],ecx + '89 50 0c', -- mov [eax+0x0c],edx + '89 70 10', -- mov [eax+0x10],esi + '89 78 14', -- mov [eax+0x14],edi + '89 60 18', -- mov [eax+0x18],esp + '89 68 1c', -- mov [eax+0x1c],ebp + 'c7 40 20',hkAddr, -- mov [eax+0x20],hkAddr (res->eip) + '9c', -- pushfd + '5a', -- pop edx + '89 50 24', -- mov [eax+0x24],edx (regs->flags) + 'c7 40 28',0, -- mov [eax+0x28],0 (regs->brk = 0) + -- load arguments into ecx+edx and call + '89 c1', -- mov ecx,eax + 'ba',hk._getLuaStatePtr(), -- mov edx,L + 'e8','rel:',hk._getCallbackPtr(), -- call hook function + -- restore registers + 'b8',regsPtr, -- mov eax,regsPtr + '8b 58 04', -- mov ebx,[eax+0x04] + '8b 48 08', -- mov ecx,[eax+0x08] + '8b 50 0c', -- mov edx,[eax+0x0c] + '8b 70 10', -- mov esi,[eax+0x10] + '8b 78 14', -- mov edi,[eax+0x14] + '8b 60 18', -- mov esp,[eax+0x18] + '8b 68 1c', -- mov ebp,[eax+0x1c] + -- if regs.brk, restore eax and retn; otherwise continue + '83 78 28 00', -- cmp dword ptr [eax+0x28],0 + '74 06', -- je (past eax restore and retn) + 'a1',regsPtr, -- mov eax,[regsPtr] + 'c3', -- retn + -- restore flags and eax + '8b 50 24', -- mov edx,[eax+0x24] (regs->flags) + '52', -- push edx + '9d', -- popfd + 'a1',regsPtr, -- mov eax,[regsPtr] + -- after this, moved code will be written, followed by a jump back + }) +end +local function writeTrampolineReturn(trAddr, retAddr) + return hk.write(trAddr, { + 'e9','rel:',retAddr, -- jmp retAddr + }) +end +local regsStructSize = 4*11 +local regsStruct = { + eax= 0, ebx= 4, ecx= 8, edx=12, + esi=16, edi=20, esp=24, ebp=28, + eip=32, flags=36, brk=40, +} +local regsList = {'eax','ebx','ecx','edx','esi','edi','esp','ebp','eip','flags','brk'} +local function newRegs() + return hk.malloc(regsStructSize) +end +local function readRegsStruct(regsPtr) + local regs = {} + for _,regname in ipairs(regsList) do + regs[regname] = hk.readInt(regsPtr + regsStruct[regname]) + end + return regs +end + +-- Basic instruction length determination for automatic trampoline construction +-- Add to this table as needed to define instructions +-- a value of true indicates a position-dependent instruction that cannot be moved +local instrLen = { + ['1b'] = { + ['c9'] = 2, -- sbb ecx,ecx + }, + ['23'] = { + ['c8'] = 2, -- and ecx,eax + }, + ['33'] = { + ['c4'] = 2, -- xor eax,esp + }, + ['3b'] = { + ['15'] = 6, -- cmp edx,i32 + }, + ['50'] = 1, -- push eax + ['51'] = 1, -- push ecx + ['53'] = 1, -- push ebx + ['55'] = 1, -- push ebp + ['56'] = 1, -- push esi + ['57'] = 1, -- push edi + ['5d'] = 1, -- pop ebp + ['5e'] = 1, -- pop esi + ['5f'] = 1, -- pop edi + ['68'] = 5, -- push i32 + ['6a'] = 2, -- push i8 + ['72'] = true, -- jb rel8 + ['74'] = true, -- jz rel8 + ['75'] = true, -- jnz rel8 + ['81'] = { + ['ec'] = 6, -- sub esp,i32 + ['c2'] = 6, -- add edx,i32 + }, + ['83'] = { + ['c4'] = 3, -- add esp,i8 + ['e4'] = 3, -- and esp,i8 + ['ec'] = 3, -- sub esp,i8 + }, + ['85'] = { + ['c0'] = 2, -- test eax,eax + }, + ['89'] = { + ['0d'] = 6, -- mov [i32],ecx + ['15'] = 6, -- mov [i32],edx + }, + ['8b'] = { + ['0d'] = 6, -- mov ecx,i32 + ['40'] = 3, -- mov eax,[eax+i8] + ['44'] = { + ['24'] = 4, -- mov eax,[esp+i8] + }, + ['45'] = 3, -- mov eax,[ebp+i8] + ['6b'] = 3, -- mov ebp,[ebx+i8] + ['c8'] = 2, -- mov ecx,eax + ['dc'] = 2, -- mov ebx,esp + ['e5'] = 2, -- mov esp,ebp + ['ec'] = 2, -- mov ebp,esp + ['f1'] = 2, -- mov esi,ecx + }, + ['8d'] = { + ['34'] = { + ['01'] = 3, -- lea esi,[ecx+eax] + }, + ['90'] = 6, -- lea edx,[eax+i32] + }, + ['a1'] = 5, -- mov eax,i32 + ['b8'] = 5, -- mov eax,i32 + ['c3'] = 1, -- retn + ['d9'] = { + ['47'] = 3, -- fld:32 [edi+i8] + ['87'] = 6, -- fld:32 [edi+i32] + }, + ['dd'] = { + ['5c'] = { + ['24'] = 4, -- fstp:64 [esp+i8] + }, + }, + ['e8'] = true, -- call rel32 + ['f7'] = { + ['d9'] = 2, -- neg ecx + }, +} +local function readByteHex(addr) + return ('%02x'):format(hk.readChar(addr)%256) +end +local jmpoutLen = 5 -- Length of the long jump instruction to be inserted as a hook +-- Determine the minimum code length >= jmpoutLen that can be +-- copied out into the trampoline. +-- Returns false if unrecognized instructions +-- Returns true if instructions are position-dependent and cannot be moved +local function determineOverwriteLen(addr) + local len = 0 + while len < jmpoutLen do + local val = readByteHex(addr) + local il = instrLen[val] + local ofs = 0 + while type(il)=='table' do + ofs = ofs+1 + val = readByteHex(addr+ofs) + il = il[val] + end + if not il then return false end + if il==true then return true end + addr = addr + il + len = len + il + end + return len +end +-- Write into existing function to jump to trampoline +local function writeJumpout(rh) + local oldProt = hk.protectRWX(rh.hkAddr, rh.hkLen) + local hkAddrW = hk.write(rh.hkAddr, { + 'e9','rel:',rh.trAddr, -- jmp trAddr + }) + for i = 1, rh.hkLen-jmpoutLen do -- fill extra space with nops + hkAddrW = hk.write(hkAddrW, '90') + end + hk.protect(rh.hkAddr, rh.hkLen, oldProt) -- restore write protection on jumpout + return hkAddrW +end +-- Write code copied from hooked function into trampoline +local function writeOldCode(rh) + local oldProt = hk.protectRWX(rh.hkAddr, rh.hkLen) + hk.writeStr(rh.hkAddr, rh.movedCode) + hk.protect(rh.hkAddr, rh.hkLen, oldProt) +end + +hk._registeredHooks = hk._registeredHooks or {} -- map of addr -> list of callbacks +-- Remove/replace all hooks, used when scanning +unhookAll = function() + for _,rh in pairs(hk._registeredHooks) do + writeOldCode(rh) + end +end +rehookAll = function() + for _,rh in pairs(hk._registeredHooks) do + writeJumpout(rh) + end +end +-- Called from C in trampoline +function _bllua_hk_callback(regsPtr) + local regs = readRegsStruct(regsPtr) + local rh = hk._registeredHooks[regs.eip] + if not rh then + error('_bllua_hk_callback: no callback registered at address') end + rh.callback(regs) +end +-- Main raw hooking function +function hk.hook(hkAddr, callback, hkLen) + if type(hkAddr)~='number' or hkAddr<0 or hkAddr%1~=0 then + error('hk.hook: argument #1: expected number >0 integer', 2) end + if type(callback)~='function' then + error('hk.hook: argument #2: expected function', 2) end + if hkLen~=nil and (type(hkLen)~='number' or hkLen<0 or hkLen%1~=0) then + error('hk.hook: argument #3: expected nil or number', 2) end + + -- if a hook is already registered, overwrite it + -- todo: multiple hooks? package names? + if hk._registeredHooks[hkAddr] then + print('hk.hook: warning: a hook is already registered at address '.. + ('%08x'):format(hkAddr)..', will overwrite.') + hk._registeredHooks[hkAddr].callback = callback + return + end + + if hkLen then + if hkLen= ' + ..jmpoutLen, 2) end + else + hkLen = determineOverwriteLen(hkAddr) + if hkLen==false then + error('hk.hook: could not automatically determine instruction length. ' + ..'please specify a length >= '..jmpoutLen..' in argument #3', 2) + elseif hkLen==true then + error('hk.hook: the hook location contains position-dependent code! ' + ..'please move the hook or use a different hooking method', 2) + end + end + + -- create register save struct + local regsPtr = newRegs() + + -- create trampoline code + local movedCode = hk.readStr(hkAddr, hkLen) + local trLen = 256 + #movedCode -- eh, good enough + local trAddr = hk.malloc(trLen) + local trOldProt = hk.protectRWX(trAddr, trLen) -- allow execution + local trAddrW = trAddr + trAddrW = writeTrampoline(trAddrW, hkAddr, regsPtr) + trAddrW = hk.writeStr(trAddrW, movedCode) + trAddrW = writeTrampolineReturn(trAddrW, hkAddr + hkLen) + + -- create info struct + local rh = { + hkAddr = hkAddr, + hkLen = hkLen, + regsPtr = regsPtr, + trAddr = trAddr, + trLen = trLen, + trOldProt = trOldProt, + movedCode = movedCode, + callback = callback, + } + + -- save to hook registry + hk._registeredHooks[hkAddr] = rh + + -- write jump out + writeJumpout(rh) +end +-- Remove a hook made by hk.hookRaw +function hk.unhook(hkAddr) + if type(hkAddr)~='number' or hkAddr<0 or hkAddr%1~=0 then + error('hk.unhook: argument #1: expected number >0 integer', 2) end + local rh = hk._registeredHooks[hkAddr] + assert(rh, 'hk.unhook: no hook registered at address '.. + ('%08x'):format(hkAddr)) + + -- remove jumpout + writeOldCode(rh) + + -- delete allocated data + hk.protect(rh.trAddr, rh.trLen, rh.trOldProt) + hk.free(rh.trAddr) + hk.free(rh.regsPtr) + + -- remove from hook registry + hk._registeredHooks[hkAddr] = nil +end +function hex(v) return v and ('%08x'):format(v):sub(1,8) end +-- called when blocklua unloads +function hk.unhookAll() + unhookAll() + for _,rh in pairs(hk._registeredHooks) do + hk.protect(rh.trAddr, rh.trLen, rh.trOldProt) + hk.free(rh.trAddr) + hk.free(rh.regsPtr) + end + hk._registeredHooks = {} +end +-- Utility to display addresses as hex +function hk.hex(v) + return ('%08x'):format(v) +end + +-- todo: stack manipulation + + +_bllua_on_unload['libhooks'] = hk.unhookAll + +return hk + + +-- testing +--[[ + +'dofile('modules/lualib/luahooks32.lua') + +'f = hk.scan('55 8B EC 83 E4 C0 83 EC 38 56 57 8B 7D 08 8B 47 44 D1 E8 A8 01 74 5C 8B 47 28', 2) + +'hk.hook(f, function(regs) print(regs) end) + + +not documented: +hk.open +hk.write / hk.patch formats other than hex +hk.protect +hk.protectRWX + +]] diff --git a/luahooks32core.cpp b/luahooks32core.cpp new file mode 100644 index 0000000..3ad0a32 --- /dev/null +++ b/luahooks32core.cpp @@ -0,0 +1,312 @@ +// Uses BlHooks for some things +// Todo: Make independent + +#include +#include + +#include "lua.hpp" + +// Custom types +void bll_hk_newMetatable(lua_State* L, const char* name, struct luaL_Reg* methods) { + if(luaL_newmetatable(L, name)) { + if(methods!=NULL) + luaL_setfuncs(L, methods, 0); + //lua_pushliteral(L, "__index"); + //lua_settable(L, -3); + //lua_pushliteral(L, "__metatable"); + //lua_pushliteral(L, "access to this metatable is disabled"); + //lua_settable(L, -3); + } + lua_pop(L, 1); +} + +// Image - Reference to a loaded binary, contains the base pointer and size +typedef struct { + void* base; + size_t size; +} bll_hk_image; +bll_hk_image* bll_hk_newImage(lua_State* L, void* base, size_t size) { + bll_hk_image* img = (bll_hk_image*)lua_newuserdata(L, sizeof(bll_hk_image)); + luaL_setmetatable(L, "_bllua_hk.image"); + img->base = base; + img->size = size; + return img; +} +int bll_hk_image_tostring(lua_State* L) { + bll_hk_image* img = (bll_hk_image*)luaL_checkudata(L, 1, "_bllua_hk.image"); + char buf[100]; + sprintf(buf, "image:{addr=%08X,size=%08X}", + (unsigned int)img->base, + (unsigned int)img->size); + lua_pushstring(L, buf); + return 1; +} +struct luaL_Reg bll_hk_image_methods[] = { + {"__tostring", bll_hk_image_tostring}, + NULL, +}; +void bll_hk_initTypeImage(lua_State* L) { + bll_hk_newMetatable(L, "_bllua_hk.image", bll_hk_image_methods); +} + +// Addr - Just a pointer, can be added/subtracted +// Just using numbers for now +//typedef struct { +// void* a; +//} bll_hk_addr; +//int bll_hk_newAddr(lua_State* L, void* a) { +// bll_hk_addr* addr = (bll_hk_addr*)lua_newuserdata(L, sizeof(bll_hk_addr)); +// luaL_setmetatable(L, "_bllua_hk.addr"); +// addr->a = a; +// return 1; +//} +//int bll_hk_addr_tostring(lua_State* L) { +// bll_hk_addr* addr = (bll_hk_addr*)luaL_checkudata(L, 1, "_bllua_hk.addr"); +// +// char buf[100]; +// sprintf(buf, "addr:%08X", (unsigned int)addr->a); +// lua_pushstring(L, buf); +// return 1; +//} +//int bll_hk_addr_add(lua_State* L) { +// bll_hk_addr* addr = (bll_hk_addr*)luaL_checkudata(L, 1, "_bllua_hk.addr"); +// size_t ofs = luaL_checkinteger(L, 2); +// +// bll_hk_newAddr(L, (char*)addr->a + ofs); +// return 1; +//} +//int bll_hk_addr_sub(lua_State* L) { +// bll_hk_addr* addr = (bll_hk_addr*)luaL_checkudata(L, 1, "_bllua_hk.addr"); +// size_t ofs = luaL_checkinteger(L, 2); +// +// bll_hk_newAddr(L, (char*)addr->a - ofs); +// return 1; +//} +//struct luaL_Reg bll_hk_addr_methods[] = { +// {"__tostring", bll_hk_addr_tostring}, +// {"__add", bll_hk_addr_add}, +// {"__sub", bll_hk_addr_sub}, +// NULL, +//}; +//void bll_hk_initTypeAddr(lua_State* L) { +// bll_hk_newMetatable(L, "_bllua_hk.addr", bll_hk_addr_methods); +//} + + +// Scanning +bool bll_hk_patternMatch(char* addr, const char* pat, const char* mask) { + for(; *mask; ++addr, ++pat, ++mask) { + if(*mask=='x' && *addr!=*pat) + return false; + } + return !*mask; +} +void* bll_hk_scan(void* imgBase, size_t imgSize, const char* pat, const char* mask, + int skip) { + char* scanStart = (char*)imgBase; + char* scanEnd = scanStart + imgSize; + for(char* addr=scanStart; addrbase, img->size, pat, mask, skip); + if(addr==NULL) + lua_pushnil(L); + else + BLL_HK_PUSHADDR(L, addr); + return 1; +} +int bll_libhk_readStr(lua_State* L) { + void* addr = BLL_HK_CHECKADDR(L, 1); + size_t len = BLL_HK_CHECKSIZET(L, 2); + lua_pushlstring(L, (const char*)addr, len); // based lua handling memory for us + return 1; +} +int bll_libhk_writeStr(lua_State* L) { + void* addr = BLL_HK_CHECKADDR(L, 1); + size_t len = 0; + const char* data = luaL_checklstring(L, 2, &len); + memcpy(addr, data, len); + BLL_HK_PUSHADDR(L, (char*)addr+len); + return 1; +} +int bll_libhk_readChar(lua_State* L) { + void* addr = BLL_HK_CHECKADDR(L, 1); + char data = *(char*)addr; + lua_pushinteger(L, data); + return 1; +} +int bll_libhk_writeChar(lua_State* L) { + void* addr = BLL_HK_CHECKADDR(L, 1); + char data = (char)luaL_checkinteger(L, 2); + *(char*)addr = data; + BLL_HK_PUSHADDR(L, (char*)addr + 1); + return 1; +} +int bll_libhk_readShort(lua_State* L) { + void* addr = BLL_HK_CHECKADDR(L, 1); + short data = *(short*)addr; + lua_pushinteger(L, data); + return 1; +} +int bll_libhk_writeShort(lua_State* L) { + void* addr = BLL_HK_CHECKADDR(L, 1); + short data = (short)luaL_checkinteger(L, 2); + *(short*)addr = data; + BLL_HK_PUSHADDR(L, (char*)addr + 2); + return 1; +} +int bll_libhk_readInt(lua_State* L) { + void* addr = BLL_HK_CHECKADDR(L, 1); + int data = *(int*)addr; + lua_pushinteger(L, data); + return 1; +} +int bll_libhk_writeInt(lua_State* L) { + void* addr = BLL_HK_CHECKADDR(L, 1); + int data = (int)luaL_checkinteger(L, 2); + *(int*)addr = data; + BLL_HK_PUSHADDR(L, (char*)addr + 4); + return 1; +} +int bll_libhk_readFloat(lua_State* L) { + void* addr = BLL_HK_CHECKADDR(L, 1); + float data = *(float*)addr; + lua_pushnumber(L, data); + return 1; +} +int bll_libhk_writeFloat(lua_State* L) { + void* addr = BLL_HK_CHECKADDR(L, 1); + float data = luaL_checknumber(L, 2); + *(float*)addr = data; + BLL_HK_PUSHADDR(L, (char*)addr + 4); + return 1; +} +int bll_libhk_readDouble(lua_State* L) { + void* addr = BLL_HK_CHECKADDR(L, 1); + double data = *(double*)addr; + lua_pushnumber(L, data); + return 1; +} +int bll_libhk_writeDouble(lua_State* L) { + void* addr = BLL_HK_CHECKADDR(L, 1); + double data = luaL_checknumber(L, 2); + *(double *)addr = data; + BLL_HK_PUSHADDR(L, (char*)addr + 8); + return 1; +} +int bll_libhk_protect(lua_State* L) { + void* addr = BLL_HK_CHECKADDR(L, 1); + size_t len = BLL_HK_CHECKSIZET(L, 2); + DWORD newProt = (DWORD)luaL_checkinteger(L, 3); + DWORD oldProt = 0; + VirtualProtect(addr, len, newProt, &oldProt); + lua_pushinteger(L, (lua_Integer)oldProt); + return 1; +} +int bll_libhk_malloc(lua_State* L) { + size_t size = (size_t)luaL_checkinteger(L, 1); + void* data = malloc(size); + BLL_HK_PUSHADDR(L, data); + return 1; +} +int bll_libhk_free(lua_State* L) { + void* data = BLL_HK_CHECKADDR(L, 1); + free(data); + return 0; +} +//typedef void(*bll_func_ptr)(void); +//int bll_libhk_call(lua_State* L) { +// void* jdest = BLL_HK_CHECKADDR(L, 1); +// ((bll_func_ptr)jdest)(); +// return 0; +//} + +// Hooks lib + +// regsPtr in ecx, L in edx +__declspec(fastcall) void bll_hk_callback(void* regsPtr, lua_State* L) { + lua_getglobal(L, "_bllua_hk_callback"); + BLL_HK_PUSHADDR(L, regsPtr); + lua_pcall(L, 1, 0, 0); +} +int bll_libhk_getLuaStatePtr(lua_State* L) { + BLL_HK_PUSHADDR(L, (void*)L); + return 1; +} +int bll_libhk_getCallbackPtr(lua_State* L) { + BLL_HK_PUSHADDR(L, (void*)bll_hk_callback); + return 1; +} +int bll_libhk_getStrPtr(lua_State* L) { + const char* str = luaL_checkstring(L, 1); + BLL_HK_PUSHADDR(L, (void*)str); + return 1; +} + +const luaL_Reg bll_hk_reg[] = { + {"_openRaw" , bll_libhk_open }, + {"_scanRaw" , bll_libhk_scan }, + {"readStr" , bll_libhk_readStr }, + {"writeStr" , bll_libhk_writeStr }, + {"protect" , bll_libhk_protect }, + {"readChar" , bll_libhk_readChar }, + {"writeChar" , bll_libhk_writeChar }, + {"readShort" , bll_libhk_readShort }, + {"writeShort" , bll_libhk_writeShort }, + {"readInt" , bll_libhk_readInt }, + {"writeInt" , bll_libhk_writeInt }, + {"readFloat" , bll_libhk_readFloat }, + {"writeFloat" , bll_libhk_writeFloat }, + {"readDouble" , bll_libhk_readDouble }, + {"writeDouble", bll_libhk_writeDouble}, + {"malloc" , bll_libhk_malloc }, + {"free" , bll_libhk_free }, + //{"call" , bll_libhk_call }, + {"getStrPtr" , bll_libhk_getStrPtr }, + {"_getLuaStatePtr", bll_libhk_getLuaStatePtr}, + {"_getCallbackPtr", bll_libhk_getCallbackPtr}, + NULL, +}; +extern "C" int __declspec(dllexport) luaopen_luahooks32core(lua_State* L) { + luaL_register(L, "luahooks32core", bll_hk_reg); + bll_hk_initTypeImage(L); + //bll_hk_initTypeAddr(L); + return 1; +} diff --git a/luahooks32core.dll b/luahooks32core.dll new file mode 100644 index 0000000..5c1cc0d Binary files /dev/null and b/luahooks32core.dll differ diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..2b9895d --- /dev/null +++ b/readme.md @@ -0,0 +1,55 @@ +# LuaHooks32 +Basic x86-32 hooks library for Lua + +## How to Install for Blockland+BlockLua +- Install RedBlocklandLoader and BlockLua +- Copy `luahooks32.lua` and `luahooks32core.dll` into the `modules/lualib` folder inside your Blockland install folder (you may have to create it) + +## Quick Reference + +`hk = require('luahooks32')` - Load the library + +### Basic Scanning and Patching +`addr = hk.scan('01 02 ? ff FF')` - Scan the application binary for a pattern +`hk.patch(addr, '01 02 03')` - Patch executable code with a series of bytes + +### Hooking +`hk.hook(addr, function(regs) ... end)` - Insert hook into binary code. Registers can be modified inside the hook. Registers are: +`regs.eax` `regs.ebx` `regs.ecx` `regs.edx` `regs.esi` `regs.edi` `regs.esp` `regs.ebp` `regs.eip` `regs.flags` +There is also `regs.brk`, which if set to 1 inside the hook, will cause the hook to execute a `retn` immediately after completing, returning out of whatever function the hook is inside. +Hooks can be inserted anywhere, provided there are at least 5 bytes of position-independent instructions that can be overwritten with a trampoline. Overwritten instructions are then copied into the hook routine. +If the basic builtin disassembler fails to detect the length of instructions to overwrite, a third argument to `bl.hook` can be used to manually specify the trampoline length. + +`hk.unhook(addr)` - Remove a previously inserted hook. +`hk.unhookAll()` - Remove all hooks. + +### Memory Access + +`hk.write(addr, '01 02 03')` - Write a series of bytes + +`str = hk.readStr(addr)` - Read a null-terminated C-style string from a memory address, and return its contents as a Lua string +`hk.writeStr(addr, 'hello\0')` - Write a Lua string to memory. Does NOT null-terminate, so you'll have to append `\0` yourself if you want that. +`val = hk.readInt(addr)` - Read a 32-bit signed integer from a memory address (little-endian) +`hk.writeInt(addr, val)` +`val = hk.readFloat(addr)` +`hk.writeFloat(addr, val)` +`val = hk.readChar(addr)` +`hk.writeChar(addr, val)` +`val = hk.readShort(addr)` +`hk.writeShort(addr, val)` +`val = hk.readDouble(addr)` +`hk.writeDouble(addr, val)` +Note that these functions can only write into writeable memory (i.e. stack/heap, not code). Use `hk.patch` to write into protected/executable memory. + +`ptr = hk.getStrPtr('str')` - Convert a Lua string into null-terminated C-style and return a pointer to it +`print(hk.hex(val))` - Simple utility to convert a number (usually an address) into 32-bit hexadecimal + +`ptr = hk.malloc(numBytes)` - Allocate memory and return a pointer to it +`hk.free(ptr)` - Free previously allocated memory + +## Compiling + +With any *32-bit* variant of GCC installed (such as MinGW or MSYS2), run the following command in the repo directory: +`g++ luahooks32core.cpp -o luahooks32core.dll -m32 -shared -static-libgcc -Iinc/lua -lpsapi -Linc/lua -llua5.1` + +LuaJIT (lua5.1.dll) can be obtained from https://luajit.org/