├── glua.h
├── init
├── embed.lua
├── main.c
├── COPYING.txt
├── whereami.h
├── binject.h
├── test
└── binject.sh
├── binject_example.cc
├── example_launcher.lua
├── Readme.md
├── binject.c
├── glua.c
└── whereami.c
/glua.h:
--------------------------------------------------------------------------------
1 |
2 | typedef struct lua_State lua_State;
3 |
4 | int set_self_binary_path(const char* self_path);
5 | int binject_main_app_has_internal_script();
6 | int binject_main_app_internal_script_handle(lua_State *L, int argc, char **argv);
7 | int luaopen_glua(lua_State* L);
8 |
9 |
--------------------------------------------------------------------------------
/init:
--------------------------------------------------------------------------------
1 | local root = arg[0]:match('^(.-[/\\])[^/\\]*$') or ''
2 | package.path = root .. '?.lua;' .. root .. '?/init.lua'
3 | package.cpath = root .. '?.dll;' .. root .. 'lib?.so'
4 | local p = print
5 | local r = io.read
6 | xpcall(function()
7 | require 'default_launcher'
8 | end,function(x)
9 | end)
10 | p('press return to quit')
11 | r(1)
12 |
--------------------------------------------------------------------------------
/embed.lua:
--------------------------------------------------------------------------------
1 | local INITFILE = 'init'
2 | local f, err = io.open(INITFILE, 'rb')
3 | if not f or err then
4 | io.stderr:write("Can not open " .. INITFILE .. " " .. err .. "\n")
5 | return -1
6 | end
7 | local s = f:read('a')
8 | f:close()
9 | local f, err = load(s)
10 | if not f or err then
11 | io.stderr:write("Can not open " .. INITFILE .. " " .. err .. "\n")
12 | return -1
13 | end
14 | f()
15 |
--------------------------------------------------------------------------------
/main.c:
--------------------------------------------------------------------------------
1 |
2 | #include "whereami.h"
3 | #include "glua.h"
4 | #include "binject.h"
5 |
6 | #define ERROR_EXIT 13
7 |
8 | #define luaL_openlibs(L) luaopen_glua(L)
9 | #define main lua_main
10 | #include ENABLE_STANDARD_LUA_CLI
11 | #undef main
12 | #undef luaL_openlibs(...)
13 |
14 | int main(int argc, char **argv) {
15 |
16 | // Set the binary path
17 | #ifndef USE_WHEREAMI
18 | set_self_binary_path(argv[0]);
19 | #else // USE_WHEREAMI
20 | char * exe_path = "";
21 | int length;
22 | length = wai_getExecutablePath(0, 0, 0);
23 | char *buf = malloc(length+1);
24 | buf[0] = '\0';
25 | if (length > 0) {
26 | exe_path = buf;
27 | int dirpathlen;
28 | wai_getExecutablePath(exe_path, length, &dirpathlen);
29 | exe_path[length] = '\0';
30 | set_self_binary_path(exe_path);
31 | } else {
32 | set_self_binary_path(argv[0]);
33 | }
34 | #endif // USE_WHEREAMI
35 |
36 | if (binject_main_app_has_internal_script()){
37 | // Script found: run it
38 | return binject_main_app_internal_script_handle(0, argc, argv);
39 | } else {
40 | // No script found: run lua
41 | return lua_main(argc, argv);
42 | }
43 | return ERROR_EXIT;
44 | }
45 |
46 |
--------------------------------------------------------------------------------
/COPYING.txt:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/whereami.h:
--------------------------------------------------------------------------------
1 | // (‑●‑●)> released under the WTFPL v2 license, by Gregory Pakosz (@gpakosz)
2 | // https://github.com/gpakosz/whereami
3 |
4 | #ifndef WHEREAMI_H
5 | #define WHEREAMI_H
6 |
7 | #ifdef __cplusplus
8 | extern "C" {
9 | #endif
10 |
11 | #ifndef WAI_FUNCSPEC
12 | #define WAI_FUNCSPEC
13 | #endif
14 | #ifndef WAI_PREFIX
15 | #define WAI_PREFIX(function) wai_##function
16 | #endif
17 |
18 | /**
19 | * Returns the path to the current executable.
20 | *
21 | * Usage:
22 | * - first call `int length = wai_getExecutablePath(NULL, 0, NULL);` to
23 | * retrieve the length of the path
24 | * - allocate the destination buffer with `path = (char*)malloc(length + 1);`
25 | * - call `wai_getExecutablePath(path, length, NULL)` again to retrieve the
26 | * path
27 | * - add a terminal NUL character with `path[length] = '\0';`
28 | *
29 | * @param out destination buffer, optional
30 | * @param capacity destination buffer capacity
31 | * @param dirname_length optional recipient for the length of the dirname part
32 | * of the path.
33 | *
34 | * @return the length of the executable path on success (without a terminal NUL
35 | * character), otherwise `-1`
36 | */
37 | WAI_FUNCSPEC
38 | int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length);
39 |
40 | /**
41 | * Returns the path to the current module
42 | *
43 | * Usage:
44 | * - first call `int length = wai_getModulePath(NULL, 0, NULL);` to retrieve
45 | * the length of the path
46 | * - allocate the destination buffer with `path = (char*)malloc(length + 1);`
47 | * - call `wai_getModulePath(path, length, NULL)` again to retrieve the path
48 | * - add a terminal NUL character with `path[length] = '\0';`
49 | *
50 | * @param out destination buffer, optional
51 | * @param capacity destination buffer capacity
52 | * @param dirname_length optional recipient for the length of the dirname part
53 | * of the path.
54 | *
55 | * @return the length of the module path on success (without a terminal NUL
56 | * character), otherwise `-1`
57 | */
58 | WAI_FUNCSPEC
59 | int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length);
60 |
61 | #ifdef __cplusplus
62 | }
63 | #endif
64 |
65 | #endif // #ifndef WHEREAMI_H
66 |
--------------------------------------------------------------------------------
/binject.h:
--------------------------------------------------------------------------------
1 |
2 | #ifndef _BINJECT_H_
3 | #define _BINJECT_H_
4 |
5 | #include
6 |
7 | // --------------------------------------------------------------------------
8 |
9 | typedef enum {
10 | NO_ERROR = 0,
11 | GENERIC_ERROR = -1,
12 | ACCESS_ERROR = -2,
13 | INVALID_RESOURCE_ERROR = -3,
14 | SIZE_ERROR = -4,
15 | INVALID_DATA_ERROR = -5,
16 | } binject_error_t;
17 |
18 | // --------------------------------------------------------------------------
19 | // Handle custom struct static data
20 |
21 | // e.g.
22 | // BINJECT_STATIC("`````", double, the_data, 3.14);
23 | // ... { ...
24 | // double * content = (double*) binject_data(the_data);
25 |
26 | typedef struct binject_static_s binject_static_t;
27 |
28 | // This MUST be instantiated as the STATIC values (i.e. global/top-level)
29 | // T: in-file mark (string literal)
30 | // S: type of the content data
31 | // N: identifier of the binject_static_t * variable that will be defined
32 | // V: initialization data as C struct literal
33 | #define BINJECT_STATIC_DATA(T, S, N, V) \
34 | typedef struct { \
35 | unsigned int tag_size; \
36 | unsigned int content_size; \
37 | unsigned int content_offset; \
38 | char start_tag[sizeof(T)]; \
39 | S content; \
40 | } N ## _t; \
41 | static N ## _t N ## _istance = { \
42 | .tag_size = sizeof(T), \
43 | .content_size = sizeof(S), \
44 | .content_offset = offsetof(N ## _t, content),\
45 | .start_tag = T, \
46 | .content = V, \
47 | }; \
48 | binject_static_t * N = (binject_static_t*) & N ## _istance
49 |
50 | // -------------------------------------------------------------------------
51 | // Handle custom static char array
52 |
53 | // e.g.
54 | // BINJECT_STATIC_STRING("`````", 256, the_data);
55 | // ... { ...
56 | // unsigned int size;
57 | // char * content = binject_info(the_data, &size);
58 |
59 | // This MUST be instantiated as the STATIC values (i.e. global/top-level)
60 | // T: in-file mark (string literal)
61 | // S: size of the char[] content
62 | // N: identifier of the binject_static_t * variable that will be defined
63 | #define BINJECT_STATIC_STRING(T, S, N) \
64 | typedef struct { \
65 | unsigned int tail_position; \
66 | unsigned int len; \
67 | unsigned int max; \
68 | char raw[S]; \
69 | } N ## _inner_t; \
70 | BINJECT_STATIC_DATA(T, N ## _inner_t, N, {.max = S})
71 |
72 | // -------------------------------------------------------------------------
73 | // API functions for Read
74 |
75 | void * binject_data(binject_static_t * ds);
76 | char * binject_get_static_script(binject_static_t * DS, unsigned int * script_size, unsigned int * file_offset);
77 | int binject_get_tail_script(binject_static_t * DS, const char * self_path, char * buffer, unsigned int size, unsigned int offset);
78 |
79 | // -------------------------------------------------------------------------
80 | // API functions for Write
81 |
82 | int binject_duplicate_binary(binject_static_t * DS, const char * self_path, const char * destination_path);
83 | int binject_step(binject_static_t * DS, const char * destination_path, const char * data, unsigned int r);
84 |
85 | // -------------------------------------------------------------------------
86 |
87 | #endif // _BINJECT_H_
88 |
89 |
--------------------------------------------------------------------------------
/test/binject.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | echo "Running some tests (note: error states are not currently tested)."
4 |
5 | #############################################################
6 | # Prepare directory
7 |
8 | # Linux
9 | # SHEXT="so"
10 | # CC_ARCH_FLAG="-fPIC"
11 |
12 | # Windows
13 | SHEXT="dll"
14 | CC_ARCH_FLAG=""
15 |
16 | TEST_DIR="$(readlink -f "$(dirname "$0")")/tmp"
17 | CC="gcc -std=c99 -Wall $CC_ARCH_FLAG "
18 |
19 | rm -fR "$TEST_DIR"
20 | mkdir "$TEST_DIR"
21 | cd "$TEST_DIR"
22 |
23 | #############################################################
24 | # Compile static
25 |
26 | $CC -o ./array_static.exe ../../*.c
27 | strip ./array_static.exe
28 |
29 | $CC -D'BINJECT_ARRAY_SIZE=3' -o ./tail_static.exe ../../*.c
30 | strip ./tail_static.exe
31 |
32 | cp ./tail_static.exe ./tail_static_bis.exe
33 |
34 | #############################################################
35 | # Compile shared
36 |
37 | $CC -shared -fPIC -o ./libbinject_array.$SHEXT ../../binject.c || exit -1
38 | $CC -o ./array_shared.exe ../../example.c -L ./ -lbinject_array || exit -1
39 | strip ./libbinject_array.$SHEXT
40 | strip ./array_shared.exe
41 |
42 | cp ./array_shared.exe ./array_shared_bis.exe
43 |
44 | export LD_LIBRARY_PATH="./"
45 |
46 | #############################################################
47 | # Test working
48 |
49 | SEDSIZCOL='s:^[^ ]* *[^ ]* *[^ ]* *[^ ]* *[^ ]* *\([^ ]*\) *.*$:\1:g'
50 |
51 | should_be() {
52 | if [ "$2" = "=" -a "$1" = "$3" ] ; then return ; fi
53 | if [ "$2" = "!=" -a "$1" != "$3" ] ; then return ; fi
54 | echo "TEST FAILS ! EXPECTING >>>"
55 | echo "$1"
56 | echo "<<< TO BE $2 TO >>>"
57 | echo "$3"
58 | echo "<<<"
59 | exit -1
60 | }
61 |
62 | test_working_sequence(){
63 | TEXT="hello world $1"
64 |
65 | # create text to embed
66 | echo "$TEXT" > ./"$1".txt || exit -1
67 |
68 | # embed the text
69 | echo "------> $1"
70 | ./"$1" ./"$1".txt || exit -1
71 | mv injed.exe ./"$1".emb || exit -1
72 | chmod ugo+x ./"$1".emb || exit -1
73 |
74 | # run the app with the embedded text
75 | echo "------> $1.emb"
76 | ./"$1".emb > ./"$1".rpt || exit -1
77 |
78 | # check output
79 | LEN="+$TEXT"
80 | LEN="${#LEN}"
81 | RES=$(cat ./"$1".rpt | tail -n 2 | head -n 1 | sed 's:^ A .. ::')
82 | EXP="A $LEN byte script was found (dump:)[$TEXT"
83 | should_be "$EXP" = "$RES"
84 |
85 | echo "------"
86 | }
87 |
88 | test_working_sequence array_static.exe
89 | test_working_sequence tail_static.exe
90 | test_working_sequence tail_static_bis.exe
91 | test_working_sequence array_shared.exe
92 | test_working_sequence array_shared_bis.exe
93 |
94 | #############################################################
95 | # Test Array / Tail method trhough exe size
96 |
97 | ls -l > my_size_info.txt
98 | grep '.exe.emb$' my_size_info.txt
99 |
100 | ARRAY=$(grep 'array_shared.exe.emb$' my_size_info.txt | sed "$SEDSIZCOL")
101 | ARRAYBIS=$(grep 'array_shared_bis.exe.emb$' my_size_info.txt | sed "$SEDSIZCOL")
102 |
103 | # If the array method was chosen, the size of the exe is always the same
104 | should_be "$ARRAY" = "$ARRAYBIS"
105 |
106 | TAIL=$(grep 'tail_static.exe.emb$' my_size_info.txt | sed "$SEDSIZCOL")
107 | TAILBIS=$(grep 'tail_static_bis.exe.emb$' my_size_info.txt | sed "$SEDSIZCOL")
108 |
109 | # If the tail method was chosen, the size of the exe depend on the embeded script
110 | should_be "$TAIL" != "$TAILBIS"
111 |
112 | #############################################################
113 | # Test shared
114 |
115 | rm *.$SHEXT
116 | ./array_shared.exe.emb > array_shared.exe.empty.rpt 2> ./array_shared.exe.error.rpt
117 |
118 | RES=$(cat array_shared.exe.empty.rpt)
119 | should_be "" = "$RES"
120 |
121 | #############################################################
122 | # Print succesfull summary
123 |
124 | echo "ALL RIGHT"
125 |
126 |
--------------------------------------------------------------------------------
/binject_example.cc:
--------------------------------------------------------------------------------
1 |
2 | #include "binject.h"
3 | #include
4 | #include
5 | #include
6 | #include "unistd.h"
7 |
8 | // Size of the data for the INTERNAL ARRAY mechanism. It should be
9 | // a positive integer
10 | #ifndef BINJECT_ARRAY_SIZE
11 | #define BINJECT_ARRAY_SIZE (9216)
12 | #endif // BINJECT_ARRAY_SIZE
13 |
14 | static void error_report(int internal_error) {
15 | if (internal_error != NO_ERROR)
16 | fprintf(stderr, "Error %d\n", internal_error);
17 | if (0 != errno)
18 | fprintf(stderr, "Error %d: %s\n", errno, strerror(errno));
19 | }
20 |
21 | BINJECT_STATIC_STRING("```replace_data```", BINJECT_ARRAY_SIZE, static_data);
22 |
23 | static int aux_print_help(const char * command){
24 | printf("\nUsage:\n %s script.txt\n\n", command);
25 | printf("script.txt.exe executable will be generated or overwritten.\n");
26 | printf("script.txt.exe will print to the stdout an embedded copy of script.txt.\n\n");
27 | printf("NOTE: depending on the chosen embedding mechanism, some help information will be\n");
28 | printf("appended at end of script.txt.exe.\n");
29 | return 0;
30 | }
31 |
32 | static char * aux_script_prepare(char * buf, int * off, int * siz){
33 | *off = *siz; // buffer processing finished
34 | return buf;
35 | }
36 |
37 | static int aux_script_run(const char * scr, int size, int argc, char ** argv){
38 | // Script echo
39 | printf("A %d byte script was found (dump:)[", size);
40 | int w = fwrite(scr, 1, size, stdout);
41 | if (w != size) return ACCESS_ERROR;
42 | printf("]\n");
43 | return 0;
44 | }
45 |
46 | // --------------------------------------------------------------------
47 |
48 | static int binject_main_app_internal_script_inject(binject_static_t * info, const char * scr_path, const char* bin_path, const char * outpath){
49 | int result = ACCESS_ERROR;
50 |
51 | // Open the scipt
52 | FILE * scr = fopen(scr_path, "rb");
53 | if (!scr) goto end;
54 |
55 | // Get the original binary size
56 | if (fseek(scr, 0, SEEK_END)) goto end;
57 |
58 | { // Scope block to avoid goto and variable length issue
59 | int siz = ftell(scr);
60 | if (siz < 0) goto end;
61 | if (fseek(scr, 0, SEEK_SET)) goto end;
62 |
63 | int bufsize = siz;
64 | int off = 0;
65 | char buf[bufsize];
66 |
67 | // Copy the binary
68 | result = binject_duplicate_binary(info, bin_path, outpath);
69 | if (NO_ERROR != result) goto end;
70 |
71 | // Prepare the script for the injection
72 | if (0> fread(buf, 1, siz, scr)) goto end;
73 | while (off >=0 && off < siz) {
74 | char * injdat = buf;
75 | injdat = aux_script_prepare(injdat, &off, &siz);
76 |
77 | // Inject the partial script and update static info into the binary
78 | result = binject_step(info, outpath, injdat, siz);
79 | if (NO_ERROR != result) goto end;
80 | }
81 | }
82 |
83 | end:
84 | error_report(0);
85 | if (scr) fclose(scr);
86 | return result;
87 | }
88 |
89 | static int binject_main_app_internal_script_handle(binject_static_t * info, const char* bin_path, int argc, char **argv) {
90 | unsigned int size;
91 | unsigned int offset;
92 |
93 | // Get information from static section
94 | char * script = binject_get_static_script(info, &size, &offset);
95 |
96 | if (script) {
97 | // Script found in the static section
98 | return aux_script_run(script, size, argc, argv);
99 |
100 | } else {
101 | // Script should be at end of the binary
102 | unsigned int script_size = binject_get_tail_script(info, bin_path, 0, 0, offset);
103 | char buf[script_size];
104 | binject_get_tail_script(info, bin_path, buf, script_size, offset);
105 | return aux_script_run(buf, script_size, argc, argv);
106 | }
107 |
108 | return NO_ERROR;
109 | }
110 |
111 | int main(int argc, char **argv) {
112 | int result = GENERIC_ERROR;
113 |
114 | // Get information from static section
115 | unsigned int size = 0;
116 | unsigned int offset = 0;
117 | binject_get_static_script(static_data, &size, &offset);
118 |
119 | // Run the proper tool
120 | if (size > 0 || offset > 0) {
121 | // Script found: handle it
122 | result = binject_main_app_internal_script_handle(static_data, argv[0], argc, argv);
123 |
124 | } else if (argc < 2 || argv[1][0] == '\0') {
125 | // No arguments: print help
126 | aux_print_help(argv[0]);
127 | result = NO_ERROR;
128 |
129 | } else {
130 | // No script found: inject
131 | if (argc < 2) { aux_print_help(argv[0]); goto end; }
132 | result = binject_main_app_internal_script_inject(static_data, argv[1], argv[0], "injed.exe");
133 | }
134 |
135 | end:
136 | if (result != NO_ERROR) fprintf(stderr, "Error %d\n", result);
137 | error_report(0);
138 | return result;
139 | }
140 |
141 |
--------------------------------------------------------------------------------
/example_launcher.lua:
--------------------------------------------------------------------------------
1 |
2 | --[[DOC
3 |
4 | A more complex example to be embedded with linject, e.g. into luancher.exe. It
5 | is a lua script runner that handle some non-trivial cases.
6 |
7 | luancher.exe will set the lua package.path and cpath so that lua will load all
8 | the .lua and .dll files in the same directory (of luancher.exe). Then it will
9 | try to execute two lua script.
10 |
11 | The first script is config.lua in the same directory. Its purpose is to
12 | configure global variables, so it is run in a sandbox environment just contain
13 | - the variable 'this_directory' containing the obsolute path of the directory
14 | containing config.lua file with the links expanded
15 | - the following standard lua globals: assert, coroutine, error, os.getenv,
16 | ipairs, math, next, pairs, pcall, string, table, tonumber, tostring, type,
17 | unpack, xpcall.
18 | - the 'chainload' global function that will load another lua script in the same
19 | environment (it takes the path to the file as the argument)
20 |
21 | If the config.lua file is not found luancher.exe will not raise any error.
22 |
23 | The second script is the first match of the following list:
24 | - the main.lua file in the same directory of the executable
25 | - the .lua file with same name in the same directory of the executable (e.g.
26 | luancher.lua)
27 | - the first argument ONLY IF it has the .lua extension
28 | - the file handle_e_YYY.XXX.lua in the same directory of luancher.exe, where
29 | YYY is the laucher name (e.g. luancher) and .XXX is the extension of the
30 | file passed as first argument (can be empty)
31 | - the file handler_n_YYY.XXX.lua where YYY is the laucher name (e.g. luancher)
32 | and XXX is the first argument ONLY IF the first argument has no path and no
33 | extension
34 |
35 | It is run in the full lua environment and if it is not found luancher will
36 | print an error message containing all the script tryed.
37 |
38 | Whatever script is run, it will receve the following arguments:
39 | same arguments descibed in the
40 | luaject section but with arg[0] replaced by
41 |
42 | - arg[-1] - command line path to the executable (the C argv[0]) with links resolved ( ??? TODO : check this !!! )
43 | - arg[0] - the script that was chosen (absolute path with links resolved)
44 | - arg[N] - (N>0) are the rest of command line arguments
45 |
46 | Note that the directory containing the launcher, that is used for package.path
47 | and cpath also, is the one passed by luamain.o as arg[-1] i.e. has all the
48 | filesystem link resolved. So, for example, if you have
49 |
50 | ```
51 | ...
52 | /bin/luancher.exe
53 | /bin/luancher.lua
54 | /bin/module.lua
55 | /module.lua
56 | /luancher.lua
57 | /luancher.exe -> my_project/bin/luancher.exe
58 | ...
59 | ```
60 |
61 | and /bin/luancher.lua contains
62 |
63 | ```
64 | local ex = require 'module'
65 | ```
66 |
67 | while /luancher.lua is empty, launching /luancher.exe will
68 | results in the loading of /bin/luancher.lua (not /luancher.lua) and then it will
69 | require /bin/module.lua (not /module.lua)
70 |
71 | Note that you can find tools similar to luancher around the web, e.g.
72 | [l-bia](http://l-bia.sourceforge.net) (but I never tryed it).
73 |
74 | Why all this words for such small tool? I changed a lot of times the way this
75 | software work. So I just needed to FREEZE all, and print my thought somewhere
76 | for the me of the future. :)
77 |
78 | ]]
79 |
80 | if type(arg[-1]) ~= 'string' then return nil end
81 |
82 | function path_split(str)
83 | local p = str:match('(.*[/\\])[^/\\]*')
84 | local n = str:match('([^/\\]*)$'):gsub('%.[^/\\%.]*$','')
85 | local e = str:match('%.[^/\\%.]*$')
86 | if not p then p = '' end
87 | if not n then n = '' end
88 | if not e then e = '' end
89 | return p,n,e
90 | end
91 |
92 | local realpath, progname, extension = path_split(whereami)
93 | package.path = realpath .. '?.lua;' .. realpath .. '?/init.lua'
94 | package.cpath = realpath .. '?.dll;' .. realpath .. 'lib?.so'
95 |
96 | local chainload = (function()
97 | local sandbox = {
98 | --print = print,
99 | assert = assert, coroutine = coroutine, error = error,
100 | os = {getenv = os.getenv}, ipairs = ipairs, math = math, next = next,
101 | pairs = pairs, pcall = pcall, string = string, table = table,
102 | tonumber = tonumber, tostring = tostring, type = type, unpack = unpack,
103 | xpcall = xpall,
104 | }
105 | sandbox.chainload = function (file)
106 | sandbox.this_directory = file:gsub('[/\\][^/\\]*$','')
107 | local t = io.open(file)
108 | if t then
109 | t:close()
110 | loadfile(file,'t',sandbox)()
111 | end -- missing config is not an error !
112 | return sandbox
113 | end
114 | return sandbox.chainload
115 | end)()
116 |
117 | local conf = chainload(realpath..'config.lua')
118 | -- CONF keys are merged into global when there is no conflict.
119 | -- Conflicting keys are avaiable in the CONFLICT global
120 | for k,v in pairs(conf) do
121 | if _G[k] == nil then
122 | _G[k] = v
123 | else
124 | if not CONFLICT then CONFLICT = {} end
125 | CONFLICT[k] = v
126 | end
127 | end
128 |
129 | local script_list = {}
130 | function script_list:append(s) script_list[1+#(script_list)]=s end
131 |
132 | script_list:append(realpath .. 'main.lua')
133 | script_list:append(realpath .. progname .. '.lua')
134 | if arg[1] then
135 | if ae == '.lua' then
136 | script_list:append (arg[1])
137 | end
138 | local ap,an,ae = path_split(arg[1])
139 | script_list:append (realpath .. 'handle_e_' .. progname .. ae .. '.lua')
140 | if ap == '' and ae == '' and an ~= '' then
141 | script_list:append (realpath .. 'handle_n_' .. progname .. '.' .. an .. '.lua')
142 | end
143 | end
144 |
145 | for _,s in ipairs(script_list) do
146 | local file = io.open(s,'rb')
147 | if file then
148 | file:close()
149 | arg[0] = s
150 | local chunk,err = loadfile(arg[0])
151 | if not chunk then
152 | error('error loading file '..arg[0]..':\n'..err)
153 | end
154 | return chunk()
155 | end
156 | end
157 |
158 | local err = ''
159 | if #(script_list) == 0 then
160 | err = ' None'
161 | else
162 | for _,s in ipairs(script_list) do
163 | err = err .. '\n ' .. s
164 | end
165 | end
166 | error('Can not find the script to launch. Tryed: '..err)
167 |
168 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 |
2 | Glua
3 | =====
4 |
5 | This utility generates single binary file containing both the lua runtime with
6 | some utility functions, and optionally a script to run. You can compile it
7 | upon the standard lua or luajit, as well as any C implementation of the lua
8 | API.
9 |
10 | It is tested with lua 5.4.0, but older version should work too.
11 |
12 | Built packages for linux, windows and mac can be found in [lua static
13 | battery](http://github.com/pocomane/lua_static_battery).
14 |
15 | This software is released under the [Unlicense](http://unlicense.org), so the
16 | legal statement in COPYING.txt applies, unless differently stated in individual
17 | files.
18 |
19 | Usage
20 | ------
21 |
22 | If no script is embed in the binary, it behaves like a standard lua command
23 | line application, with some library added. If a script is embeded, it is run
24 | instead of the standard lua command line application. By default it has
25 | no script embeded.
26 |
27 | The embeded funcitons can be accessed with `require "whereami"`, that is a
28 | function that returns the path to the executable, and `require "glua_pack"`.
29 | The latter is a function that takes two arguments: a script to embed an a path. It
30 | copies whole application in the path and embeds the script in it.
31 |
32 | So, for example, you can generate an executable that embeds the `test.lua` script in it,
33 | and execute it when launched, with the following one liner:
34 |
35 | ```
36 | echo "print'hello world!" > hello_world.lua
37 |
38 | ./glua.exe -e 'require"glua_pack"("test.lua", "glued.exe")'
39 |
40 | chmod ugo+x glued.exe
41 | ./glued.exe
42 | ```
43 |
44 | The newly created `glued.exe` contains the script. Launching it will diplay the
45 | message `hello world!` in the console. You can simplify its command line with:
46 |
47 | ```
48 | echo "require"glua_pack"(argv[1], "glued.exe")" > test.lua
49 | ./glua.exe -e 'require"glua_pack"("test.lua", "gluasimple.exe")'
50 | chmod ugo+x gluasmple.exe
51 | ```
52 |
53 | This `gluasimple.exe` can generate new executables simply with:
54 |
55 | ```
56 | ./glua2.exe test.lua
57 | ```
58 |
59 | (or drag `hello_world.lua` on `glua2.exe`).
60 |
61 | Please, be aware that glua.exe is (deliberately) an extremly simple tool. It
62 | does not try to reduce size, or to embed other lua modules. For such advanced
63 | operation you can use something like [lua squish](http://matthewwild.co.uk/projects/squish/home) and then use glua.exe
64 | on the resulting file.
65 |
66 | There are other tools that archive somehow the same result of glua:
67 | - [bin2c](https://sourceforge.net/p/wxlua/svn/217/tree/trunk/wxLua/util/bin2c/bin2c.lua)
68 | converts the lua bytestream in lua C API call ready to be compiled.
69 | - [srlua](http://webserver2.tecgraf.puc-rio.br/~lhf/ftp/lua/#srlua) paste the
70 | script at end of the exe, and at run-time open the exe searching for the
71 | script.
72 |
73 | Build
74 | ------
75 |
76 | There is no actual build system. You can compile it with gcc using:
77 |
78 | ```
79 | gcc -I . -o glua.exe *.c lua_lib -lm -ldl
80 | ```
81 |
82 | This assumes that you have copied the lua headers in the current directoy and
83 | the binary library (static or shared) in the `lua_lib file`. For windows you can
84 | add `-D_WIN32_WINNT=0x0600`, and `-mconsole` or `-mwindows` (depending on if
85 | you want or not a console to appear at start of the application).
86 |
87 | // TODO : document gcc linker ORIGIN
88 |
89 | Build options
90 | --------------
91 |
92 | This library must be linked statically in the executable.
93 |
94 | If the compilation flag `USE_WHEREAMI` is enabled, the embedded `whereami`
95 | library will use some system dependent code to guess where the binary is.
96 | Otherwise it will use the first command line argument.
97 |
98 | If you define `ENABLE_STANDARD_LUA_CLI` pointing the lua `lua.c`, it will be
99 | included in the `glua.exe`/`glued.exe` binaries. This enable to run the
100 | standard lua interpreter when `--lua` is passed as the LAST argument to
101 | `glua.exe` or `glued.exe`. Please note that the macro definition must begin and
102 | end `"`, e.g. `gcc -DENABLE_STANDARD_LUA_CLI='"/path/tp/lua.c"' ...`
103 |
104 | The code that actually embed and extract the script is [binject](#Binject), so
105 | refer to its [documentation](#Binject working) for additional options.
106 |
107 | Link extra modules
108 | -------------------
109 |
110 | To embed extra C modules in `glua.exe`, just call `luaL-openlibs`-like function
111 | fron `preload.c`.
112 |
113 | // TODO : multiple script from command line -> include wrapping in a
114 | require-able enclosure
115 |
116 | luancher
117 | ---------
118 |
119 | The command
120 |
121 | ```
122 | ./glua.exe embed.lua
123 | ```
124 |
125 | will generate int `glued.exe` a minimal lua script launcher. It simply load the
126 | `init` file in its same directory, and run it as a lua script/bytecode. The
127 | script will have access to the common lua globals. Moreover the libraries
128 | defined in [preload.c](preload.c) are embedded.
129 |
130 | This tool is usefull while developing, when you are ready to deploy, you can
131 | embed your script directly in a executable by means of `glua.exe`. In this
132 | phase, if you have some issue with the package.path and cpath, just copy the
133 | ones you found on the top of the default_launcher.lua (that is the script
134 | embeded in luancher).
135 |
136 | example_launcher
137 | -----------------
138 |
139 | A more complex lua script launcher is in the `example_laucher.lua`. Open it for
140 | some documentation.
141 |
142 | preload.c
143 | ----------
144 |
145 | The `preload.c` defines what lua libraries are embedded into the executables. To
146 | add new C-modules, you just need to call their standard `loaopen_` function from
147 | the `preaload_all` one.
148 |
149 | Note: pure lua modules can be preloaded in the same way, wrapping them in a C
150 | `luaopen_` function that just do a `luaL_loadbuffer` plus a `lua_call`.
151 |
152 | By default just the `whereami` library is loaded, it can be called with
153 | `local path = require'whereami'()`.
154 |
155 | Binject
156 | --------
157 |
158 | The code that actually embed the script in the executable is in the
159 | `binject.c/h` files. It is designed as a "Library" so you can use them alone
160 | without lua or anything else. You just need to provide the parsing function or
161 | manipulate the data before the injection.
162 |
163 | A complete but simple "Echo" example is provided for reference. It can be
164 | compiled with
165 |
166 | ```
167 | gcc -std=c99 -o binject.exe binject.c binject_example.cc
168 | ```
169 |
170 | As it is, this will compile the "Echo" example. For some customization, read
171 | the 'Binject working' section.
172 |
173 | When called without argument, some help information will be printed. To embed a
174 | script pass it as argument.
175 |
176 | ```
177 | ./binject.exe my_script
178 | ```
179 |
180 | This will generate the file injed.exe. Then:
181 |
182 | ```
183 | echo "hello world" > my_text.txt
184 | ./binject.exe my_text.txt
185 | rm my_text.txt
186 | ./injed.exe
187 | ```
188 |
189 | will print "hello world" to the screen.
190 |
191 | In the test directory there is a test that will execute all the commands seen
192 | before. At end it will also check that the output of the example app is the
193 | expected one.
194 |
195 | Binject working
196 | ----------------
197 |
198 | Two methods are avaiable to embed the script. By default, the "Array" method
199 | will be tryed first, and if the script is too big, it will fallback to the
200 | "Tail" method.
201 |
202 | In the "Array" method the script will overwrite the initialization data of a
203 | static struct.
204 |
205 | In the "Tail" method the script will be appended at end of the
206 | executable, and in the static struct will be kept only the informations
207 | about where the script begin. With this method you can edit you script
208 | directly in the exectuable.
209 |
210 | The applications using `binject` can be configured at compile time by means of
211 | the following definitions.
212 |
213 | `BINJECT_ARRAY_SIZE` - Size of the data for the INTERNAL ARRAY
214 | mechanism. It should be a positive integer. If you put this value to 0,
215 | you can actually force to always use the tail method. The default is
216 | 9216 byte.
217 |
218 |
--------------------------------------------------------------------------------
/binject.c:
--------------------------------------------------------------------------------
1 |
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include "binject.h"
7 |
8 | // --------------------------------------------------------------------------
9 |
10 | struct binject_static_s {
11 | unsigned int tag_size;
12 | unsigned int content_size;
13 | unsigned int content_offset;
14 | char start_tag[1];
15 | // .content considered inside the previous Flexible Array Member
16 | };
17 |
18 | typedef struct {
19 | unsigned int tail_position;
20 | unsigned int len;
21 | unsigned int max;
22 | char raw[1];
23 | } binject_data_t;
24 |
25 | static long int binject_find_last_tag_byte(FILE* f, const char* tag, size_t tagsize){
26 | int c = '\n';
27 | int count = 0;
28 | long match = 0;
29 |
30 | long int result = -1;
31 | if (tagsize <= 0) return result;
32 |
33 | while (!match) {
34 |
35 | c = fgetc(f);
36 | if (c == EOF) break;
37 |
38 | if (c != tag[count]) count = 0;
39 | else count += 1;
40 |
41 | if (count >= tagsize) {
42 | match = 1;
43 | break;
44 | }
45 | }
46 |
47 | if (match) result = ftell(f);
48 | if (result < 0) result = ACCESS_ERROR;
49 |
50 | return result;
51 | }
52 |
53 | static long int binject_find_static_data(binject_static_t * ds, FILE* file){
54 | return binject_find_last_tag_byte(file, ds->start_tag, ds->tag_size)
55 | - ds->tag_size - offsetof(binject_static_t, start_tag);
56 | }
57 |
58 | static unsigned int container_size(binject_static_t * ds) {
59 | return ds->content_offset + ds->content_size;
60 | }
61 |
62 | static binject_error_t binject_write_data(binject_static_t * ds, FILE * file, long int position){
63 | long int result = NO_ERROR;
64 |
65 | if (0 != fseek(file, position, SEEK_SET)) result = ACCESS_ERROR;
66 | if (result == NO_ERROR)
67 | if (container_size(ds) != fwrite(ds, 1, container_size(ds), file))
68 | result = ACCESS_ERROR;
69 |
70 | return result;
71 | }
72 |
73 | static int binject_tail_append(unsigned int * pos, const char * self_path, const char * data, unsigned int size){
74 | FILE * f = fopen(self_path, "r+b");
75 | if (!f) return ACCESS_ERROR;
76 |
77 | if (fseek(f, 0, SEEK_END)) goto err;
78 | if (pos && *pos == 0) {
79 | *pos = ftell(f);
80 | if (*pos < 0) goto err;
81 | }
82 |
83 | if (size != fwrite(data, 1, size, f))
84 | goto err;
85 |
86 | fclose(f);
87 | return NO_ERROR;
88 |
89 | err:
90 | fclose(f);
91 | return ACCESS_ERROR;
92 | }
93 |
94 | static long int binject_read_static_data(const char * path, binject_static_t * ds){
95 | FILE * file = fopen(path, "rb");
96 | if (!file) return ACCESS_ERROR;
97 |
98 | long int position = binject_find_static_data(ds, file);
99 | if (0 != fseek(file, position, SEEK_SET)) {
100 | fclose(file);
101 | return ACCESS_ERROR;
102 | }
103 |
104 | long int result = fread(ds, 1, container_size(ds), file);
105 | fclose(file);
106 | return result;
107 | }
108 |
109 | static binject_error_t binject_inject(binject_static_t * ds, const char * path){
110 |
111 | FILE * file = fopen(path, "r+b");
112 | if (!file) return ACCESS_ERROR;
113 |
114 | long int position = binject_find_static_data(ds, file);
115 |
116 | binject_error_t result = INVALID_RESOURCE_ERROR;
117 | if (position > 0) result = binject_write_data(ds, file, position);
118 | fclose(file);
119 |
120 | return result;
121 | }
122 |
123 | static void binject_use_tail(binject_static_t * DS) {
124 | binject_data_t * toinj = (binject_data_t *)binject_data(DS);
125 | toinj->max = 0;
126 | }
127 |
128 | static int binject_does_use_tail(binject_static_t * DS) {
129 | binject_data_t * toinj = (binject_data_t *)binject_data(DS);
130 | if (toinj->max > 0) return 0;
131 | return 1;
132 | }
133 |
134 | // --------------------------------------------------------------------
135 |
136 | void * binject_data(binject_static_t * ds){
137 | return ((char*)ds) + ds->content_offset;
138 | }
139 |
140 | char * binject_get_static_script(binject_static_t * DS, unsigned int * script_size, unsigned int * file_offset){
141 |
142 | if (script_size) *script_size = 0;
143 | if (file_offset) *file_offset = 0;
144 |
145 | binject_data_t * data = (binject_data_t*) binject_data(DS);
146 | if (binject_does_use_tail(DS)) {
147 |
148 | if (file_offset) *file_offset = data->tail_position;
149 | return NULL;
150 |
151 | } else {
152 | if (script_size) *script_size = data->len;
153 | return data->raw;
154 | }
155 | }
156 |
157 | int binject_get_tail_script(binject_static_t * DS, const char * self_path, char * buffer, unsigned int size, unsigned int offset){
158 |
159 | // Open file
160 | FILE * f = fopen(self_path, "rb");
161 | if (!f) return ACCESS_ERROR;
162 | if (0 != fseek(f, offset, SEEK_SET)) return ACCESS_ERROR;
163 |
164 | // Read data
165 | int actread = fread(buffer, 1, size, f);
166 | if (0> actread && actread != size) return ACCESS_ERROR;
167 |
168 | // Calc remaining bytes
169 | if (0 != fseek(f, 0, SEEK_END)) return ACCESS_ERROR;
170 | int result = ftell(f) - offset - actread;
171 |
172 | // TODO : close file on error ? it could override errno !
173 | fclose(f);
174 | return result;
175 | }
176 |
177 | // --------------------------------------------------------------------
178 |
179 | int binject_duplicate_binary(binject_static_t * DS, const char * self_path, const char * destination_path){
180 | int r = 0;
181 | int w = 0;
182 | char b[128];
183 | unsigned int stop = ( (binject_data_t *) binject_data(DS) ) -> tail_position;
184 |
185 | // Open files
186 | FILE * fs = fopen(self_path, "rb");
187 | if (!fs) return ACCESS_ERROR;
188 | FILE * fd = fopen(destination_path, "wb");
189 | if (!fd) return ACCESS_ERROR;
190 |
191 | // Copy from source file to destination
192 | unsigned int tot = 0;
193 | while (tot <= stop) {
194 | r = fread(b, 1, sizeof(b), fs);
195 | if (0 > r) break;
196 | if (stop > 0 && tot + r > stop) r = stop - tot; // Do not copy the possible final script: it must be injected again if needed.
197 | w = fwrite(b, 1, r, fd);
198 | if (r != sizeof(b) || r != w) break;
199 | }
200 |
201 | // Error report
202 | if (r != w) return ACCESS_ERROR;
203 |
204 | // TODO : close file on error ? it could override errno !
205 | fclose(fd);
206 | fclose(fs);
207 |
208 | // Clear the static data section
209 | binject_static_t *clean_static_data = (binject_static_t *) malloc(container_size(DS));
210 | memcpy(clean_static_data, DS, container_size(DS));
211 | binject_data_t *clean_content = (binject_data_t *)binject_data(clean_static_data);
212 | clean_content->len = 0;
213 | memset(clean_content->raw, 0, clean_content->max);
214 | binject_inject(clean_static_data, destination_path);
215 | free(clean_static_data);
216 |
217 | return NO_ERROR;
218 | }
219 |
220 | int binject_step(binject_static_t * DS, const char * destination_path, const char * data, unsigned int r){
221 |
222 | // Using the static data FROM the target binary
223 | binject_static_t * ds = (binject_static_t *) malloc(container_size(DS));
224 | memcpy(ds, DS, container_size(DS));
225 | binject_read_static_data(destination_path, ds);
226 |
227 | binject_data_t * toinj = (binject_data_t *)binject_data(ds);
228 | int result = NO_ERROR;
229 |
230 | if (binject_does_use_tail(ds)) {
231 | // Tail mode
232 | result = binject_tail_append(0, destination_path, data, r);
233 |
234 | } else {
235 | if ((long)toinj->len + (long)r < (long)toinj->max-1) {
236 | // Static arry mode
237 | memcpy(toinj->raw + toinj->len, data, r);
238 | toinj->len += r;
239 | result = NO_ERROR;
240 |
241 | } else {
242 | // Switch to tail mode
243 | binject_use_tail(ds);
244 | unsigned int * pos = &(( (binject_data_t *) binject_data(ds) ) -> tail_position);
245 | if (*pos > 0) pos = NULL;
246 | result = binject_tail_append(pos, destination_path, toinj->raw, toinj->len);
247 | if (NO_ERROR == result) result = binject_tail_append(pos, destination_path, data, r);
248 | }
249 | }
250 | if (NO_ERROR == result) result = binject_inject(ds, destination_path);
251 | free(ds);
252 | return result;
253 | }
254 |
255 | // --------------------------------------------------------------------
256 |
257 |
--------------------------------------------------------------------------------
/glua.c:
--------------------------------------------------------------------------------
1 |
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include "unistd.h"
8 |
9 | #include "lua.h"
10 | #include "lualib.h"
11 | #include "lauxlib.h"
12 | #include "binject.h"
13 |
14 | // --------------------------------------------------------------------------------
15 |
16 | #define FAIL_INIT -125
17 | #define FAIL_ALLOC -126
18 | #define FAIL_EXECUTION -127
19 | #define ALL_IS_RIGHT 0
20 |
21 | #ifdef LUA_OK
22 | #define is_lua_ok(status_code) (LUA_OK == status_code)
23 | #else
24 | #define is_lua_ok(status_code) (!status_code)
25 | #endif
26 |
27 | #ifdef LUA_OK
28 | #define lua_is_bad() (!LUA_OK)
29 | #else
30 | #define lua_is_bad() (is_lua_ok(0) ? 1 : 0)
31 | #endif
32 |
33 | void luaL_openlibs (lua_State *L); // Lua internal - not part of the lua API
34 |
35 | static int script_msghandler (lua_State *L) {
36 |
37 | // is error object not a string?
38 | const char *msg = lua_tostring(L, 1);
39 | if (msg == NULL) {
40 |
41 | // call a tostring metamethod if any
42 | if (luaL_callmeta(L, 1, "__tostring") && lua_type(L, -1) == LUA_TSTRING){
43 | msg = lua_tostring(L, -1);
44 | lua_remove(L, -1);
45 |
46 | // else push a standard error message
47 | } else {
48 | msg = lua_pushfstring(L, "(error object is a %s value)", luaL_typename(L, 1));}
49 | }
50 |
51 | // append a traceback
52 | luaL_traceback(L, L, msg, 1);
53 |
54 | return 1;
55 | }
56 |
57 | // Signal hook: stop the interpreter. Just like standard lua interpreter.
58 | static void clear_and_stop(lua_State *L, lua_Debug *ar) {
59 | (void)ar; // unused arg.
60 | lua_sethook(L, NULL, 0, 0);
61 | luaL_error(L, "interrupted!");
62 | }
63 | static lua_State *script_globalL = NULL;
64 | static void sigint_handler (int i) {
65 | signal(i, SIG_DFL); // if another SIGINT happens, terminate process
66 | lua_State *L = script_globalL;
67 | script_globalL = NULL;
68 | lua_sethook(L, clear_and_stop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1); // Run 'clear_and_stop' before any other lua code
69 | }
70 |
71 | static void report_error(lua_State *L, const char * title){
72 | if (!title || title[0] == '\0')
73 | title = "An error occurred somewhere.\n";
74 | char * data = "No other information are avaiable\n";
75 | if (lua_isstring(L, -1)) data = (char *) lua_tostring(L, -1);
76 | lua_getglobal(L, "print");
77 | lua_pushstring(L, title);
78 | lua_pushstring(L, data);
79 | lua_pcall(L, 2, 1, 0);
80 | }
81 |
82 | int luamain_start(lua_State *L, char* script, int size, int argc, char **argv) {
83 | int status;
84 | int create_lua = 0;
85 | int base = 0;
86 |
87 | // create state as needed
88 | if (L == NULL) {
89 | create_lua = 1;
90 | L = luaL_newstate();
91 | if (L == NULL) return FAIL_ALLOC;
92 | }
93 |
94 | // os signal handler
95 | void (*previous_sighandl)(int) = signal(SIGINT, sigint_handler);
96 | if (SIG_DFL == previous_sighandl) {
97 | script_globalL = L; // to be available to 'sigint_handler'
98 | }
99 |
100 | luaopen_glua(L);
101 |
102 | // Prepare the stack with the error handler
103 | lua_pushcfunction(L, script_msghandler);
104 | base = lua_gettop(L);
105 |
106 | // Create a table to store the command line arguments
107 | lua_createtable(L, argc-1, 1);
108 |
109 | // Arg 0 : command-line-like path to the executable:
110 | // it may be a link and/or be relative to the current directory
111 | lua_pushstring(L, argv[0]);
112 | lua_rawseti(L, -2, 0);
113 |
114 | // Args N... : command line arguments
115 | for (int i = 1; i < argc; i++) {
116 | lua_pushstring(L, argv[i]);
117 | lua_rawseti(L, -2, i);
118 | }
119 |
120 | // Save the table in the global namespace
121 | lua_setglobal(L, "arg");
122 |
123 | // Load the script in the stack
124 | if (size < 0) size = strlen(script);
125 | status = luaL_loadbuffer(L, script, size, "embedded");
126 | if (!is_lua_ok(status)) {
127 | report_error(L, "An error occurred during the script load.");
128 | status = FAIL_EXECUTION;
129 | goto luamain_end;
130 | }
131 |
132 | // Run the script with the signal handler
133 | status = lua_is_bad();
134 | status = lua_pcall(L, 0, LUA_MULTRET, base);
135 | if (is_lua_ok(status)) {
136 | status = ALL_IS_RIGHT;
137 | goto luamain_end;
138 | }
139 |
140 | // Report error
141 | report_error(L, "An error accurred during the script execution.");
142 | if (lua_isnumber(L, -1)) status = lua_tonumber(L, -1);
143 | else status = FAIL_EXECUTION;
144 |
145 | luamain_end:
146 |
147 | // clear C-signal handler
148 | if (SIG_DFL == previous_sighandl) {
149 | signal(SIGINT, SIG_DFL);
150 | script_globalL = NULL;
151 | }
152 |
153 | if (base>0) lua_remove(L, base); // remove lua message handler
154 | if (create_lua) lua_close(L);
155 | return status;
156 | }
157 |
158 | // --------------------------------------------------------------------------------
159 |
160 | // Size of the data for the INTERNAL ARRAY mechanism. It should be
161 | // a positive integer
162 | #ifndef BINJECT_ARRAY_SIZE
163 | #define BINJECT_ARRAY_SIZE (9216)
164 | #endif // BINJECT_ARRAY_SIZE
165 |
166 | BINJECT_STATIC_STRING("```replace_data```", BINJECT_ARRAY_SIZE, static_data);
167 |
168 | static char* self_binary_path = 0;
169 |
170 | int set_self_binary_path(const char* self_path){
171 | self_binary_path = (char*) self_path;
172 | }
173 |
174 | int binject_main_app_has_internal_script() {
175 | unsigned int size = 0;
176 | unsigned int offset = 0;
177 | binject_get_static_script(static_data, &size, &offset);
178 | if (size > 0 || offset > 0) return 1;
179 | return 0;
180 | }
181 |
182 | int binject_main_app_internal_script_handle(lua_State *L, int argc, char **argv) {
183 | unsigned int size;
184 | unsigned int offset;
185 |
186 | // Get information from static section
187 | char * script = binject_get_static_script(static_data, &size, &offset);
188 |
189 | if (script) {
190 | // Script found in the static section
191 | return luamain_start(L, script, size, argc, argv);
192 |
193 | } else {
194 | // Script should be at end of the binary
195 | unsigned int script_size = binject_get_tail_script(static_data, self_binary_path, 0, 0, offset);
196 | char buf[script_size];
197 | binject_get_tail_script(static_data, self_binary_path, buf, script_size, offset);
198 | return luamain_start(L, buf, script_size, argc, argv);
199 | }
200 |
201 | return NO_ERROR;
202 | }
203 |
204 | static int binject_main_app_internal_script_inject(const char * scr_path, const char * outpath){
205 | int result = ACCESS_ERROR;
206 | errno = 0;
207 |
208 | // Open the scipt
209 | FILE * scr = fopen(scr_path, "rb");
210 | if (!scr) goto end;
211 |
212 | // Get the original binary size
213 | if (fseek(scr, 0, SEEK_END)) goto end;
214 | int siz = ftell(scr);
215 | if (siz < 0) goto end;
216 | if (fseek(scr, 0, SEEK_SET)) goto end;
217 |
218 | { // Scope block to avoid goto and variable length issue
219 | char buf[siz];
220 |
221 | // Copy the binary
222 | result = binject_duplicate_binary(static_data, self_binary_path, outpath);
223 | if (NO_ERROR != result) goto end;
224 |
225 | // Read the script for the injection
226 | if (0> fread(buf, 1, siz, scr)) goto end;
227 |
228 | // Inject the script and update static info into the binary
229 | result = binject_step(static_data, outpath, buf, siz);
230 | if (NO_ERROR != result) goto end;
231 | }
232 |
233 | end:
234 | if (0 != errno)
235 | fprintf(stderr, "Error %d: %s\n", errno, strerror(errno));
236 | if (scr) fclose(scr);
237 | return result;
238 | }
239 |
240 | // --------------------------------------------------------------------------------
241 |
242 | static int glua_pack_call(lua_State* L){
243 | if (!self_binary_path){
244 | lua_pushnil(L);
245 | lua_pushstring(L, "can not retrieve the self binary path");
246 | return 2;
247 | }
248 | const char *inpath = luaL_checkstring(L, 1);
249 | const char *outpath = luaL_checkstring(L, 2);
250 | if (!inpath || !outpath || *inpath == '\0' || *outpath == '\0'){
251 | lua_pushnil(L);
252 | lua_pushstring(L, "input or output file not provided");
253 | return 2;
254 | }
255 | const int result = binject_main_app_internal_script_inject(inpath, outpath);
256 | if (result) {
257 | lua_pushnil(L);
258 | lua_pushstring(L, "can not read input file or generate output one");
259 | return 2;
260 | }
261 | return 0;
262 | }
263 |
264 | // --------------------------------------------------------------------------------
265 |
266 | int luaopen_glua_pack(lua_State* L){
267 | lua_pushcfunction(L, glua_pack_call);
268 | return 1;
269 | }
270 |
271 | int luaopen_whereami(lua_State* L){
272 | lua_pushstring(L, self_binary_path);
273 | return 1;
274 | }
275 |
276 | #ifdef PRELOAD_EXTRA
277 | int PRELOAD_EXTRA(lua_State* L);
278 | #endif
279 |
280 | int luaopen_glua(lua_State* L){
281 | luaL_openlibs(L);
282 | #ifdef PRELOAD_EXTRA
283 | PRELOAD_EXTRA(L);
284 | #endif
285 |
286 | luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE);
287 |
288 | lua_pushcfunction(L, luaopen_whereami); lua_setfield(L, -2, "whereami");
289 | lua_pushcfunction(L, luaopen_glua_pack); lua_setfield(L, -2, "glua_pack");
290 |
291 | lua_pop(L, 1);
292 | return 0;
293 | }
294 |
295 |
--------------------------------------------------------------------------------
/whereami.c:
--------------------------------------------------------------------------------
1 | // (‑●‑●)> released under the WTFPL v2 license, by Gregory Pakosz (@gpakosz)
2 | // https://github.com/gpakosz/whereami
3 |
4 | // in case you want to #include "whereami.c" in a larger compilation unit
5 | #if !defined(WHEREAMI_H)
6 | #include
7 | #endif
8 |
9 | #ifdef __cplusplus
10 | extern "C" {
11 | #endif
12 |
13 | #if !defined(WAI_MALLOC) || !defined(WAI_FREE) || !defined(WAI_REALLOC)
14 | #include
15 | #endif
16 |
17 | #if !defined(WAI_MALLOC)
18 | #define WAI_MALLOC(size) malloc(size)
19 | #endif
20 |
21 | #if !defined(WAI_FREE)
22 | #define WAI_FREE(p) free(p)
23 | #endif
24 |
25 | #if !defined(WAI_REALLOC)
26 | #define WAI_REALLOC(p, size) realloc(p, size)
27 | #endif
28 |
29 | #ifndef WAI_NOINLINE
30 | #if defined(_MSC_VER)
31 | #define WAI_NOINLINE __declspec(noinline)
32 | #elif defined(__GNUC__)
33 | #define WAI_NOINLINE __attribute__((noinline))
34 | #else
35 | #error unsupported compiler
36 | #endif
37 | #endif
38 |
39 | #if defined(_MSC_VER)
40 | #define WAI_RETURN_ADDRESS() _ReturnAddress()
41 | #elif defined(__GNUC__)
42 | #define WAI_RETURN_ADDRESS() __builtin_extract_return_addr(__builtin_return_address(0))
43 | #else
44 | #error unsupported compiler
45 | #endif
46 |
47 | #if defined(_WIN32)
48 |
49 | #define WIN32_LEAN_AND_MEAN
50 | #if defined(_MSC_VER)
51 | #pragma warning(push, 3)
52 | #endif
53 | #include
54 | #include
55 | #if defined(_MSC_VER)
56 | #pragma warning(pop)
57 | #endif
58 |
59 | static int WAI_PREFIX(getModulePath_)(HMODULE module, char* out, int capacity, int* dirname_length)
60 | {
61 | wchar_t buffer1[MAX_PATH];
62 | wchar_t buffer2[MAX_PATH];
63 | wchar_t* path = NULL;
64 | int length = -1;
65 |
66 | for (;;)
67 | {
68 | DWORD size;
69 | int length_, length__;
70 |
71 | size = GetModuleFileNameW(module, buffer1, sizeof(buffer1) / sizeof(buffer1[0]));
72 |
73 | if (size == 0)
74 | break;
75 | else if (size == (DWORD)(sizeof(buffer1) / sizeof(buffer1[0])))
76 | {
77 | DWORD size_ = size;
78 | do
79 | {
80 | wchar_t* path_;
81 |
82 | path_ = (wchar_t*)WAI_REALLOC(path, sizeof(wchar_t) * size_ * 2);
83 | if (!path_)
84 | break;
85 | size_ *= 2;
86 | path = path_;
87 | size = GetModuleFileNameW(module, path, size_);
88 | }
89 | while (size == size_);
90 |
91 | if (size == size_)
92 | break;
93 | }
94 | else
95 | path = buffer1;
96 |
97 | if (!_wfullpath(buffer2, path, MAX_PATH))
98 | break;
99 | length_ = (int)wcslen(buffer2);
100 | length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_ , out, capacity, NULL, NULL);
101 |
102 | if (length__ == 0)
103 | length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_, NULL, 0, NULL, NULL);
104 | if (length__ == 0)
105 | break;
106 |
107 | if (length__ <= capacity && dirname_length)
108 | {
109 | int i;
110 |
111 | for (i = length__ - 1; i >= 0; --i)
112 | {
113 | if (out[i] == '\\')
114 | {
115 | *dirname_length = i;
116 | break;
117 | }
118 | }
119 | }
120 |
121 | length = length__;
122 |
123 | break;
124 | }
125 |
126 | if (path != buffer1)
127 | WAI_FREE(path);
128 |
129 | return length;
130 | }
131 |
132 | WAI_NOINLINE
133 | WAI_FUNCSPEC
134 | int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
135 | {
136 | return WAI_PREFIX(getModulePath_)(NULL, out, capacity, dirname_length);
137 | }
138 |
139 | WAI_NOINLINE
140 | WAI_FUNCSPEC
141 | int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
142 | {
143 | HMODULE module;
144 | int length = -1;
145 |
146 | #if defined(_MSC_VER)
147 | #pragma warning(push)
148 | #pragma warning(disable: 4054)
149 | #endif
150 | if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)WAI_RETURN_ADDRESS(), &module))
151 | #if defined(_MSC_VER)
152 | #pragma warning(pop)
153 | #endif
154 | {
155 | length = WAI_PREFIX(getModulePath_)(module, out, capacity, dirname_length);
156 | }
157 |
158 | return length;
159 | }
160 |
161 | #elif defined(__linux__) || defined(__CYGWIN__)
162 |
163 | #include
164 | #include
165 | #include
166 | #include
167 | #ifndef __STDC_FORMAT_MACROS
168 | #define __STDC_FORMAT_MACROS
169 | #endif
170 | #include
171 |
172 | #if !defined(WAI_PROC_SELF_EXE)
173 | #define WAI_PROC_SELF_EXE "/proc/self/exe"
174 | #endif
175 |
176 | WAI_FUNCSPEC
177 | int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
178 | {
179 | char buffer[PATH_MAX];
180 | char* resolved = NULL;
181 | int length = -1;
182 |
183 | for (;;)
184 | {
185 | resolved = realpath(WAI_PROC_SELF_EXE, buffer);
186 | if (!resolved)
187 | break;
188 |
189 | length = (int)strlen(resolved);
190 | if (length <= capacity)
191 | {
192 | memcpy(out, resolved, length);
193 |
194 | if (dirname_length)
195 | {
196 | int i;
197 |
198 | for (i = length - 1; i >= 0; --i)
199 | {
200 | if (out[i] == '/')
201 | {
202 | *dirname_length = i;
203 | break;
204 | }
205 | }
206 | }
207 | }
208 |
209 | break;
210 | }
211 |
212 | return length;
213 | }
214 |
215 | #if !defined(WAI_PROC_SELF_MAPS_RETRY)
216 | #define WAI_PROC_SELF_MAPS_RETRY 5
217 | #endif
218 |
219 | #if !defined(WAI_PROC_SELF_MAPS)
220 | #define WAI_PROC_SELF_MAPS "/proc/self/maps"
221 | #endif
222 |
223 | #if defined(__ANDROID__) || defined(ANDROID)
224 | #include
225 | #include
226 | #include
227 | #endif
228 |
229 | WAI_NOINLINE
230 | WAI_FUNCSPEC
231 | int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
232 | {
233 | int length = -1;
234 | FILE* maps = NULL;
235 | int i;
236 |
237 | for (i = 0; i < WAI_PROC_SELF_MAPS_RETRY; ++i)
238 | {
239 | maps = fopen(WAI_PROC_SELF_MAPS, "r");
240 | if (!maps)
241 | break;
242 |
243 | for (;;)
244 | {
245 | char buffer[PATH_MAX < 1024 ? 1024 : PATH_MAX];
246 | uint64_t low, high;
247 | char perms[5];
248 | uint64_t offset;
249 | uint32_t major, minor;
250 | char path[PATH_MAX];
251 | uint32_t inode;
252 |
253 | if (!fgets(buffer, sizeof(buffer), maps))
254 | break;
255 |
256 | if (sscanf(buffer, "%" PRIx64 "-%" PRIx64 " %s %" PRIx64 " %x:%x %u %s\n", &low, &high, perms, &offset, &major, &minor, &inode, path) == 8)
257 | {
258 | uint64_t addr = (uint64_t)(uintptr_t)WAI_RETURN_ADDRESS();
259 | if (low <= addr && addr <= high)
260 | {
261 | char* resolved;
262 |
263 | resolved = realpath(path, buffer);
264 | if (!resolved)
265 | break;
266 |
267 | length = (int)strlen(resolved);
268 | #if defined(__ANDROID__) || defined(ANDROID)
269 | if (length > 4
270 | &&buffer[length - 1] == 'k'
271 | &&buffer[length - 2] == 'p'
272 | &&buffer[length - 3] == 'a'
273 | &&buffer[length - 4] == '.')
274 | {
275 | int fd = open(path, O_RDONLY);
276 | char* begin;
277 | char* p;
278 |
279 | begin = (char*)mmap(0, offset, PROT_READ, MAP_SHARED, fd, 0);
280 | p = begin + offset;
281 |
282 | while (p >= begin) // scan backwards
283 | {
284 | if (*((uint32_t*)p) == 0x04034b50UL) // local file header found
285 | {
286 | uint16_t length_ = *((uint16_t*)(p + 26));
287 |
288 | if (length + 2 + length_ < (int)sizeof(buffer))
289 | {
290 | memcpy(&buffer[length], "!/", 2);
291 | memcpy(&buffer[length + 2], p + 30, length_);
292 | length += 2 + length_;
293 | }
294 |
295 | break;
296 | }
297 |
298 | p -= 4;
299 | }
300 |
301 | munmap(begin, offset);
302 | close(fd);
303 | }
304 | #endif
305 | if (length <= capacity)
306 | {
307 | memcpy(out, resolved, length);
308 |
309 | if (dirname_length)
310 | {
311 | int i;
312 |
313 | for (i = length - 1; i >= 0; --i)
314 | {
315 | if (out[i] == '/')
316 | {
317 | *dirname_length = i;
318 | break;
319 | }
320 | }
321 | }
322 | }
323 |
324 | break;
325 | }
326 | }
327 | }
328 |
329 | fclose(maps);
330 |
331 | if (length != -1)
332 | break;
333 | }
334 |
335 | return length;
336 | }
337 |
338 | #elif defined(__APPLE__)
339 |
340 | #define _DARWIN_BETTER_REALPATH
341 | #include
342 | #include
343 | #include
344 | #include
345 | #include
346 |
347 | WAI_FUNCSPEC
348 | int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
349 | {
350 | char buffer1[PATH_MAX];
351 | char buffer2[PATH_MAX];
352 | char* path = buffer1;
353 | char* resolved = NULL;
354 | int length = -1;
355 |
356 | for (;;)
357 | {
358 | uint32_t size = (uint32_t)sizeof(buffer1);
359 | if (_NSGetExecutablePath(path, &size) == -1)
360 | {
361 | path = (char*)WAI_MALLOC(size);
362 | if (!_NSGetExecutablePath(path, &size))
363 | break;
364 | }
365 |
366 | resolved = realpath(path, buffer2);
367 | if (!resolved)
368 | break;
369 |
370 | length = (int)strlen(resolved);
371 | if (length <= capacity)
372 | {
373 | memcpy(out, resolved, length);
374 |
375 | if (dirname_length)
376 | {
377 | int i;
378 |
379 | for (i = length - 1; i >= 0; --i)
380 | {
381 | if (out[i] == '/')
382 | {
383 | *dirname_length = i;
384 | break;
385 | }
386 | }
387 | }
388 | }
389 |
390 | break;
391 | }
392 |
393 | if (path != buffer1)
394 | WAI_FREE(path);
395 |
396 | return length;
397 | }
398 |
399 | WAI_NOINLINE
400 | WAI_FUNCSPEC
401 | int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
402 | {
403 | char buffer[PATH_MAX];
404 | char* resolved = NULL;
405 | int length = -1;
406 |
407 | for(;;)
408 | {
409 | Dl_info info;
410 |
411 | if (dladdr(WAI_RETURN_ADDRESS(), &info))
412 | {
413 | resolved = realpath(info.dli_fname, buffer);
414 | if (!resolved)
415 | break;
416 |
417 | length = (int)strlen(resolved);
418 | if (length <= capacity)
419 | {
420 | memcpy(out, resolved, length);
421 |
422 | if (dirname_length)
423 | {
424 | int i;
425 |
426 | for (i = length - 1; i >= 0; --i)
427 | {
428 | if (out[i] == '/')
429 | {
430 | *dirname_length = i;
431 | break;
432 | }
433 | }
434 | }
435 | }
436 | }
437 |
438 | break;
439 | }
440 |
441 | return length;
442 | }
443 |
444 | #elif defined(__QNXNTO__)
445 |
446 | #include
447 | #include
448 | #include
449 | #include
450 | #include
451 |
452 | #if !defined(WAI_PROC_SELF_EXE)
453 | #define WAI_PROC_SELF_EXE "/proc/self/exefile"
454 | #endif
455 |
456 | WAI_FUNCSPEC
457 | int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
458 | {
459 | char buffer1[PATH_MAX];
460 | char buffer2[PATH_MAX];
461 | char* resolved = NULL;
462 | FILE* self_exe = NULL;
463 | int length = -1;
464 |
465 | for (;;)
466 | {
467 | self_exe = fopen(WAI_PROC_SELF_EXE, "r");
468 | if (!self_exe)
469 | break;
470 |
471 | if (!fgets(buffer1, sizeof(buffer1), self_exe))
472 | break;
473 |
474 | resolved = realpath(buffer1, buffer2);
475 | if (!resolved)
476 | break;
477 |
478 | length = (int)strlen(resolved);
479 | if (length <= capacity)
480 | {
481 | memcpy(out, resolved, length);
482 |
483 | if (dirname_length)
484 | {
485 | int i;
486 |
487 | for (i = length - 1; i >= 0; --i)
488 | {
489 | if (out[i] == '/')
490 | {
491 | *dirname_length = i;
492 | break;
493 | }
494 | }
495 | }
496 | }
497 |
498 | break;
499 | }
500 |
501 | fclose(self_exe);
502 |
503 | return length;
504 | }
505 |
506 | WAI_FUNCSPEC
507 | int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
508 | {
509 | char buffer[PATH_MAX];
510 | char* resolved = NULL;
511 | int length = -1;
512 |
513 | for(;;)
514 | {
515 | Dl_info info;
516 |
517 | if (dladdr(WAI_RETURN_ADDRESS(), &info))
518 | {
519 | resolved = realpath(info.dli_fname, buffer);
520 | if (!resolved)
521 | break;
522 |
523 | length = (int)strlen(resolved);
524 | if (length <= capacity)
525 | {
526 | memcpy(out, resolved, length);
527 |
528 | if (dirname_length)
529 | {
530 | int i;
531 |
532 | for (i = length - 1; i >= 0; --i)
533 | {
534 | if (out[i] == '/')
535 | {
536 | *dirname_length = i;
537 | break;
538 | }
539 | }
540 | }
541 | }
542 | }
543 |
544 | break;
545 | }
546 |
547 | return length;
548 | }
549 |
550 | #elif defined(__DragonFly__) || defined(__FreeBSD__) || \
551 | defined(__FreeBSD_kernel__) || defined(__NetBSD__)
552 |
553 | #include
554 | #include
555 | #include
556 | #include
557 | #include
558 | #include
559 |
560 | WAI_FUNCSPEC
561 | int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
562 | {
563 | char buffer1[PATH_MAX];
564 | char buffer2[PATH_MAX];
565 | char* path = buffer1;
566 | char* resolved = NULL;
567 | int length = -1;
568 |
569 | for (;;)
570 | {
571 | int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
572 | size_t size = sizeof(buffer1);
573 |
574 | if (sysctl(mib, (u_int)(sizeof(mib) / sizeof(mib[0])), path, &size, NULL, 0) != 0)
575 | break;
576 |
577 | resolved = realpath(path, buffer2);
578 | if (!resolved)
579 | break;
580 |
581 | length = (int)strlen(resolved);
582 | if (length <= capacity)
583 | {
584 | memcpy(out, resolved, length);
585 |
586 | if (dirname_length)
587 | {
588 | int i;
589 |
590 | for (i = length - 1; i >= 0; --i)
591 | {
592 | if (out[i] == '/')
593 | {
594 | *dirname_length = i;
595 | break;
596 | }
597 | }
598 | }
599 | }
600 |
601 | break;
602 | }
603 |
604 | if (path != buffer1)
605 | WAI_FREE(path);
606 |
607 | return length;
608 | }
609 |
610 | WAI_NOINLINE
611 | WAI_FUNCSPEC
612 | int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
613 | {
614 | char buffer[PATH_MAX];
615 | char* resolved = NULL;
616 | int length = -1;
617 |
618 | for(;;)
619 | {
620 | Dl_info info;
621 |
622 | if (dladdr(WAI_RETURN_ADDRESS(), &info))
623 | {
624 | resolved = realpath(info.dli_fname, buffer);
625 | if (!resolved)
626 | break;
627 |
628 | length = (int)strlen(resolved);
629 | if (length <= capacity)
630 | {
631 | memcpy(out, resolved, length);
632 |
633 | if (dirname_length)
634 | {
635 | int i;
636 |
637 | for (i = length - 1; i >= 0; --i)
638 | {
639 | if (out[i] == '/')
640 | {
641 | *dirname_length = i;
642 | break;
643 | }
644 | }
645 | }
646 | }
647 | }
648 |
649 | break;
650 | }
651 |
652 | return length;
653 | }
654 |
655 | #else
656 |
657 | #error unsupported platform
658 |
659 | #endif
660 |
661 | #ifdef __cplusplus
662 | }
663 | #endif
664 |
--------------------------------------------------------------------------------