├── .gitattributes ├── demo.mov ├── README.md ├── CMakeArchive.cmake ├── src ├── rs_js.cc ├── rs_rb.c ├── rs_py.c ├── rs_js.c └── rs_php.c ├── generate_env.sh └── CMakeLists.txt /.gitattributes: -------------------------------------------------------------------------------- 1 | demo.mov filter=lfs diff=lfs merge=lfs -text 2 | -------------------------------------------------------------------------------- /demo.mov: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:05e309c356fa555eee9699c9468964c6bc33a054bd56bb8746ca9e029ff7bfde 3 | size 215129534 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Low-level RASP: Protecting Applications Implemented in High-level Programming Languages 2 | 3 | This is a technique I shared at Black Hat USA 2023 that can retrieve High-level Programing Language's stack traces from Native(Low-level) space. 4 | 5 | A *Low-level Runtime-self Application Protection*(LL-RASP) has been implemented to inspire more posibilities(eg: LL-Fuzz、 LL-Emulator、LL-AST ...) 6 | 7 | [Download Slides](https://i.blackhat.com/BH-US-23/Presentations/US-23-LI-Low-Level-RASP.pdf) 8 | -------------------------------------------------------------------------------- /CMakeArchive.cmake: -------------------------------------------------------------------------------- 1 | # Archive lightweight extensions. 2 | if (${LANG} MATCHES "node") 3 | message("[*] Archiving NodeJs to archive_${LANG}.tgz to ${CMAKE_CURRENT_BINARY_DIR}") 4 | execute_process(COMMAND ln -s librs_${LANG}.so librs_js_ext.so WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 5 | execute_process(COMMAND ln -s librs_js.so librs_ext.so WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 6 | execute_process(COMMAND tar -zcf archive_${LANG}.tgz librs_engine.so librs_${LANG}.so librs_ext.so librs_js_ext.so librs_js.so WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 7 | execute_process(COMMAND rm -rf librs_ext.so librs_js_ext.so WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 8 | else () 9 | message("[*] Archiving to archive_${LANG}.tgz") 10 | execute_process(COMMAND ln -s librs_${LANG}.so librs_ext.so WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 11 | execute_process(COMMAND tar -zcf archive_${LANG}.tgz librs_engine.so librs_${LANG}.so librs_ext.so WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 12 | execute_process(COMMAND rm -rf librs_ext.so WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 13 | endif () 14 | -------------------------------------------------------------------------------- /src/rs_js.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zhuonan li on 1/30/22. 3 | // 4 | 5 | // Thus, for an addon to remain ABI-compatible across Node.js major versions, 6 | // it must use Node-API exclusively by restricting itself to using 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "../rs_header.h" 12 | 13 | 14 | extern "C" void dump_back_trace(char *bt) { 15 | v8::Isolate *isolate = v8::Isolate::GetCurrent(); 16 | v8::Local st = v8::StackTrace::CurrentStackTrace(isolate, SIZE_256, v8::StackTrace::kOverview); 17 | char tmp_line[SIZE_256]; 18 | for (uint32_t i = 0; i < st->GetFrameCount(); i++) { 19 | 20 | #if NAPI_VERSION >= 4 21 | v8::Local frame = st->GetFrame(isolate, i); 22 | v8::String::Utf8Value scriptName(isolate, frame->GetScriptName()); 23 | v8::String::Utf8Value functionName(isolate, frame->GetFunctionName()); 24 | #else 25 | v8::Local frame = st->GetFrame(i); 26 | v8::String::Utf8Value scriptName(frame->GetScriptName()); 27 | v8::String::Utf8Value functionName(frame->GetFunctionName()); 28 | #endif 29 | 30 | if (*functionName != nullptr) { 31 | memset(tmp_line, 0, SIZE_256); 32 | snprintf(tmp_line, SIZE_256, "at %s (%s:%d:%d)\n", *functionName, *scriptName, frame->GetLineNumber(), 33 | frame->GetColumn()); 34 | strncat(bt, tmp_line, SIZE_256); 35 | } 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/rs_rb.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zhuonan li on 8/23/21. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include "../rs_header.h" 9 | 10 | 11 | extern void *__libc_dlopen_mode(const char *, int); 12 | 13 | extern void *__libc_dlsym(void *, const char *); 14 | 15 | extern void *__libc_dlclose(void *); 16 | 17 | extern struct elf_info get_elf_info(int pid, const char *elf_name); 18 | 19 | extern void hook_module(char *elf_path, const char *func_name, void *new_func, int inline_hook, int is_save_symbol); 20 | 21 | void (*analyze_event)(const char *type, char *param, char *bt, void *param_addr); 22 | 23 | 24 | __attribute__((unused)) void dump_back_trace(char *bt) { 25 | VALUE rb_bt = rb_make_backtrace(); 26 | VALUE a = rb_ary_join(rb_bt, rb_str_new_cstr("\n")); 27 | strncat(bt, rb_string_value_cstr(&a), SIZE_4K); 28 | } 29 | 30 | 31 | // ruby 2.7 32 | void rs_rb_execarg_new(int argc, const VALUE *argv, int accept_shell, int allow_exc_opt) { 33 | 34 | unsigned long j1 = 0; 35 | unsigned long j2 = 0; 36 | // ruby memory 37 | memcpy(&j1, argv, LONG_SIZE); 38 | 39 | memcpy(&j2, (void *) j1 + LONG_SIZE * 2, LONG_SIZE); 40 | 41 | char *cmd = calloc(1, SIZE_1K); 42 | 43 | void *param_addr; 44 | 45 | if (j2 < 0xFFFFFFFF) { 46 | unsigned long j3 = 0; 47 | memcpy(&j3, (void *) j1 + LONG_SIZE * 3, LONG_SIZE); 48 | param_addr = (void *) j3; 49 | } else { 50 | param_addr = (void *) j1 + LONG_SIZE * 2; 51 | } 52 | 53 | memcpy(cmd, param_addr, SIZE_1K); 54 | 55 | printf("[*] [UDS] [RS] Detected [rb_execarg_new] with cmd: [%s]\n", cmd); 56 | 57 | char *bt = calloc(1, SIZE_4K); 58 | dump_back_trace(bt); 59 | analyze_event("rb_execarg_new", cmd, bt, param_addr); 60 | free(bt); 61 | bt = NULL; 62 | } 63 | 64 | 65 | void *engine_module = NULL; 66 | 67 | __attribute__((unused)) void install() { 68 | 69 | // init analyze_event 70 | engine_module = __libc_dlopen_mode(RS_ENGINE_PATH, 2); 71 | 72 | if (engine_module == NULL) { 73 | printf("[!] [UDS] [RS] Failed to load engine module.\n"); 74 | return; 75 | } 76 | 77 | analyze_event = __libc_dlsym(engine_module, "analyze_event"); 78 | if (analyze_event == NULL) { 79 | printf("[!] [UDS] [RS] Failed to locate analyze_event.\n"); 80 | return; 81 | } 82 | 83 | struct elf_info elfInfo = get_elf_info(0, "libruby"); 84 | if (elfInfo.base != 0) { 85 | hook_module(elfInfo.path, "rb_execarg_new", rs_rb_execarg_new, 1, 0); 86 | } else { 87 | printf("[!] [UDS] [RS] Unsupported Ruby Version.\n"); 88 | } 89 | 90 | } 91 | 92 | extern void uninstall_hook(); 93 | 94 | __attribute__((unused)) void uninstall() { 95 | DEBUG_PRINT(("[*] [UDS] [RS] Uninstalling UDS-Ruby extension.\n")); 96 | uninstall_hook(); 97 | __libc_dlclose(engine_module); 98 | DEBUG_PRINT(("[*] [UDS] [RS] Ruby module has been successfully uninstalled.\n")); 99 | } 100 | -------------------------------------------------------------------------------- /generate_env.sh: -------------------------------------------------------------------------------- 1 | #CentOS Stream 8 x64 2 | 3 | yum update -y 4 | yum upgrade 5 | yum install bison cmake gcc gdb -y 6 | 7 | # Java Version Manager 8 | curl "https://get.sdkman.io" | bash 9 | sdk install java 6.0.119-zulu 10 | sdk install java 8.0.302-open 11 | sdk install java 7.0.342-zulu 12 | sdk use java 8.0.302-open 13 | 14 | # Node.js Version Manager 15 | # git clone https://github.com/nodejs/node.git 16 | curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash 17 | nvm install node 18 | nvm install 14.7.0 19 | nvm use 18 20 | 21 | # Python Version Manager 22 | wget https://www.python.org/ftp/python/2.0.1/Python-2.0.1.tgz 23 | wget https://www.python.org/ftp/python/2.1.3/Python-2.1.3.tgz 24 | wget https://www.python.org/ftp/python/2.2.3/Python-2.2.3.tgz 25 | wget https://www.python.org/ftp/python/2.3.6/Python-2.3.6.tar.bz2 26 | wget https://www.python.org/ftp/python/2.4.6/Python-2.4.6.tgz 27 | wget https://www.python.org/ftp/python/2.5.6/Python-2.5.6.tar.bz2 28 | wget https://www.python.org/ftp/python/2.6.9/Python-2.6.9.tgz 29 | wget https://www.python.org/ftp/python/2.7.18/Python-2.7.18.tgz 30 | wget https://www.python.org/ftp/python/3.0.1/Python-3.0.1.tgz 31 | wget https://www.python.org/ftp/python/3.1.5/Python-3.1.5.tgz 32 | wget https://www.python.org/ftp/python/3.2.6/Python-3.2.6.tgz 33 | wget https://www.python.org/ftp/python/3.3.7/Python-3.3.7.tgz 34 | wget https://www.python.org/ftp/python/3.4.10/Python-3.4.10.tgz 35 | wget https://www.python.org/ftp/python/3.5.10/Python-3.5.10.tgz 36 | wget https://www.python.org/ftp/python/3.6.15/Python-3.6.15.tgz 37 | wget https://www.python.org/ftp/python/3.7.12/Python-3.7.12.tgz 38 | wget https://www.python.org/ftp/python/3.8.13/Python-3.8.13.tgz 39 | wget https://www.python.org/ftp/python/3.9.13/Python-3.9.13.tgz 40 | wget https://www.python.org/ftp/python/3.10.5/Python-3.10.5.tgz 41 | 42 | find . -name "Python*.bz2" -exec bzip2 -d {} \; 43 | find . -name "Python*.tgz" -exec tar -zxf {} \; 44 | find . -name "Python*.tar.gz" -exec tar -zxf {} \; 45 | find . -name "Python*.tar" -exec tar -xf {} \; 46 | # configure all version 47 | find . -maxdepth 1 -name 'Python-*' -type d -exec bash -c 'pushd {};./configure;popd' \; 48 | rm -rf *.tar *.tgz *.tar.gz 49 | 50 | # PHP Version Manager 51 | wget http://museum.php.net/php5/php-5.0.0.tar.gz 52 | wget http://museum.php.net/php5/php-5.1.0.tar.gz 53 | wget http://museum.php.net/php5/php-5.2.0.tar.bz2 54 | wget http://museum.php.net/php5/php-5.3.0.tar.bz2 55 | wget http://museum.php.net/php5/php-5.4.0.tar.bz2 56 | wget http://museum.php.net/php5/php-5.5.0.tar.gz 57 | wget https://www.php.net/distributions/php-5.6.0.tar.gz 58 | wget https://www.php.net/distributions/php-7.0.0.tar.gz 59 | wget https://www.php.net/distributions/php-7.1.0.tar.gz 60 | wget https://www.php.net/distributions/php-7.2.0.tar.gz 61 | wget https://www.php.net/distributions/php-7.3.0.tar.gz 62 | wget https://www.php.net/distributions/php-7.4.0.tar.gz 63 | wget https://www.php.net/distributions/php-8.0.0.tar.gz 64 | wget https://www.php.net/distributions/php-8.1.0.tar.gz 65 | 66 | find . -name "php*.bz2" -exec bzip2 -d {} \; 67 | find . -name "php*.tgz" -exec tar -zxf {} \; 68 | find . -name "php*.tar.gz" -exec tar -zxf {} \; 69 | find . -name "php*.tar" -exec tar -xf {} \; 70 | find . -maxdepth 1 -name 'php-*' -type d -exec bash -c 'pushd {};./configure;popd' \; 71 | rm -rf *.tar *.tgz *.tar.gz 72 | 73 | # Ruby Version Manager 74 | wget https://cache.ruby-lang.org/pub/ruby/3.1/ruby-3.1.2.tar.gz 75 | wget https://cache.ruby-lang.org/pub/ruby/3.0/ruby-3.0.4.tar.gz 76 | wget https://cache.ruby-lang.org/pub/ruby/2.7/ruby-2.7.6.tar.gz 77 | 78 | find . -name "ruby*.tar.gz" -exec tar -zxf {} \; 79 | find . -maxdepth 1 -name 'ruby-*' -type d -exec bash -c 'pushd {};./configure;popd' \; 80 | rm -rf *.tar.gz 81 | 82 | -------------------------------------------------------------------------------- /src/rs_py.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zhuonan li on 8/18/21. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "../rs_header.h" 11 | 12 | extern char *get_binary_path(pid_t pid); 13 | 14 | extern struct elf_info get_elf_info(int pid, const char *elf_name); 15 | 16 | extern void hook_module(char *elf_path, const char *func_name, void *new_func, int inline_hook, int is_save_symbol); 17 | 18 | void (*analyze_event)(const char *type, char *param, char *bt, void *param_addr); 19 | 20 | #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 21 | #define to_cstring PyUnicode_AsUTF8String 22 | #else 23 | #define to_cstring PyString_AsString 24 | #endif 25 | 26 | __attribute__((unused)) void dump_back_trace(char *bt) { 27 | char tmp_line[SIZE_256]; 28 | PyGILState_STATE state = PyGILState_Ensure(); 29 | PyThreadState *t_state = PyThreadState_Get(); 30 | #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 11 31 | PyFrameObject *frame = PyThreadState_GetFrame(t_state); 32 | #else 33 | PyFrameObject *frame = t_state->frame; 34 | #endif 35 | 36 | while (frame != NULL) { 37 | #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 11 38 | int line = PyCode_Addr2Line(PyFrame_GetCode(frame), PyFrame_GetLasti(frame)); 39 | const char *file_name = PyUnicode_AsUTF8(PyFrame_GetCode(frame)->co_filename); 40 | const char *func_name = PyUnicode_AsUTF8(PyFrame_GetCode(frame)->co_name); 41 | #else 42 | int line = PyCode_Addr2Line(frame->f_code, frame->f_lasti); 43 | const char *file_name = (const char *) to_cstring(frame->f_code->co_filename); 44 | const char *func_name = (const char *) to_cstring(frame->f_code->co_name); 45 | #endif 46 | memset(tmp_line, 0, SIZE_256); 47 | snprintf(tmp_line, SIZE_256, "%s (%s:%d)\n", func_name, file_name, line); 48 | strncat(bt, tmp_line, SIZE_256); 49 | #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 11 50 | frame = PyFrame_GetBack(frame); 51 | #else 52 | frame = frame->f_back; 53 | #endif 54 | } 55 | 56 | PyGILState_Release(state); 57 | } 58 | 59 | 60 | int rs_system(char *line) { 61 | printf("[*] [UDS] [RS] Detected [system] with cmd: [%s]\n", line); 62 | char *bt = calloc(1, SIZE_4K); 63 | dump_back_trace(bt); 64 | analyze_event("system", line, bt, line); 65 | free(bt); 66 | bt = NULL; 67 | return system(line); 68 | } 69 | 70 | void *engine_module = NULL; 71 | 72 | void install() { 73 | 74 | engine_module = dlopen(RS_ENGINE_PATH, RTLD_LAZY); 75 | if (engine_module == NULL) { 76 | printf("[!] [UDS] [RS] Failed to load engine module.\n"); 77 | return; 78 | } 79 | 80 | analyze_event = dlsym(engine_module, "analyze_event"); 81 | if (analyze_event == NULL) { 82 | printf("[!] [UDS] [RS] Failed to locate analyze_event.\n"); 83 | return; 84 | } 85 | 86 | struct elf_info elfInfo = get_elf_info(0, "libpython"); 87 | 88 | if (elfInfo.base == 0) { 89 | char *binary_path = get_binary_path(getpid()); 90 | hook_module(binary_path, "system", rs_system, 0, 0); 91 | free(binary_path); 92 | binary_path = NULL; 93 | } else { 94 | hook_module(elfInfo.path, "system", rs_system, 0, 0); 95 | } 96 | 97 | } 98 | 99 | extern void uninstall_hook(); 100 | 101 | void uninstall() { 102 | DEBUG_PRINT(("[*] [UDS] [RS] Uninstalling UDS-Python extension.\n")); 103 | uninstall_hook(); 104 | dlclose(engine_module); 105 | DEBUG_PRINT(("[*] [UDS] [RS] Python module has been successfully uninstalled.\n")); 106 | } 107 | 108 | __attribute__((constructor)) void init() { 109 | printf("[*] [RS] [Python] install address: %p\n", install); 110 | printf("[*] [RS] [Python] uninstall address: %p\n", uninstall); 111 | } 112 | -------------------------------------------------------------------------------- /src/rs_js.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zhuonan li on 5/1/22. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include "../rs_header.h" 9 | 10 | #define JS_EXT_PATH RS_HOME"/librs_js_ext.so" 11 | 12 | 13 | extern struct elf_info get_elf_info(int pid, const char *elf_name); 14 | 15 | extern void hook_module(char *elf_path, const char *func_name, void *new_func, int inline_hook, int is_save_symbol); 16 | 17 | void (*dump_back_trace)(char *) = NULL; 18 | 19 | void (*analyze_event)(const char *type, char *param, char *bt, void *param_addr); 20 | 21 | int (*orig_uv_spawn)(void *, void *, void *) =NULL; 22 | 23 | 24 | void rs_uv_spawn_inline(void *loop, void *handle, void *options) { 25 | 26 | long file_address = 0; 27 | memcpy(&file_address, options + LONG_SIZE, LONG_SIZE); 28 | 29 | // http://iks.cs.ovgu.de/~elkner/tmp/ph/process.html 30 | // 0 exit_cb 31 | // 1 char* file 32 | // 2 char** args 33 | char *cmd = calloc(1, SIZE_1K); 34 | 35 | long args_address; 36 | long arg_address; 37 | memcpy(&args_address, options + LONG_SIZE * 2, LONG_SIZE); 38 | 39 | int arg_index = 0; 40 | long next_arg_address = 1; 41 | 42 | while (next_arg_address != 0) { 43 | memcpy(&arg_address, (void *) args_address + LONG_SIZE * arg_index, LONG_SIZE); 44 | memcpy(&next_arg_address, (void *) args_address + LONG_SIZE * (arg_index + 1), LONG_SIZE); 45 | 46 | strncat(cmd, (char *) arg_address, SIZE_256); 47 | 48 | if (next_arg_address != 0) { 49 | strncat(cmd, " ", SIZE_2); 50 | } 51 | 52 | arg_index++; 53 | } 54 | 55 | printf("[*] [UDS] [RS] Detected [uv_spawn] with cmd: [%s]\n", cmd); 56 | char *bt = calloc(1, SIZE_4K); 57 | dump_back_trace(bt); 58 | 59 | analyze_event("uv_spawn", cmd, bt, (void *) file_address); 60 | free(bt); 61 | bt = NULL; 62 | free(cmd); 63 | cmd = NULL; 64 | } 65 | 66 | int rs_uv_spawn(void *loop, void *handle, void *options) { 67 | rs_uv_spawn_inline(loop, handle, options); 68 | return orig_uv_spawn(loop, handle, options); 69 | } 70 | 71 | 72 | void *engine_module = NULL; 73 | void *ext_module = NULL; 74 | 75 | void install() { 76 | // init dump_back_trace; 77 | ext_module = dlopen(JS_EXT_PATH, RTLD_LAZY); 78 | if (ext_module == NULL) { 79 | printf("[!] [UDS] [RS] Failed to load ext module.\n"); 80 | return; 81 | } 82 | 83 | // init analyze_event 84 | engine_module = dlopen(RS_ENGINE_PATH, RTLD_LAZY); 85 | if (engine_module == NULL) { 86 | printf("[!] [UDS] [RS] Failed to load engine module.\n"); 87 | return; 88 | } 89 | 90 | analyze_event = dlsym(engine_module, "analyze_event"); 91 | if (analyze_event == NULL) { 92 | printf("[!] [UDS] [RS] Failed to locate analyze_event.\n"); 93 | return; 94 | } 95 | 96 | dump_back_trace = dlsym(ext_module, "dump_back_trace"); 97 | if (dump_back_trace == NULL) { 98 | printf("[!] [UDS] [RS] Failed to find symbol dump_back_trace.\n"); 99 | return; 100 | } 101 | 102 | struct elf_info elfInfo = get_elf_info(0, "libnode.so"); // dynamic link nodejs 103 | 104 | if (elfInfo.base != 0) { 105 | DEBUG_PRINT(("[*] Dynamic Linked Node.Js\n")); 106 | void *module_handle = dlopen(elfInfo.path, RTLD_LAZY); 107 | orig_uv_spawn = dlsym(module_handle, "uv_spawn"); 108 | hook_module(elfInfo.path, "uv_spawn", (void *) rs_uv_spawn, 0, 0); 109 | } else { 110 | DEBUG_PRINT(("[*] Static Linked Node.Js\n")); 111 | elfInfo = get_elf_info(0, "/bin/node"); // static link nodejs 112 | if (elfInfo.base != 0) { 113 | hook_module(elfInfo.path, "uv_spawn", (void *) rs_uv_spawn_inline, 1, 0); 114 | } else { 115 | printf("[!] [UDS] [RS] Unsupported Node Version.\n"); 116 | } 117 | } 118 | 119 | } 120 | 121 | extern void uninstall_hook(); 122 | 123 | void uninstall() { 124 | DEBUG_PRINT(("[*] [UDS] [RS] Uninstalling UDS-Node extension.\n")); 125 | uninstall_hook(); 126 | dlclose(ext_module); 127 | DEBUG_PRINT(("[*] [UDS] [RS] Node extension module has been successfully uninstalled.\n")); 128 | dlclose(engine_module); 129 | DEBUG_PRINT(("[*] [UDS] [RS] Node module has been successfully uninstalled.\n")); 130 | } 131 | 132 | __attribute__((constructor)) void init() { 133 | printf("[*] [RS] [Node.js] install address: %p\n", install); 134 | printf("[*] [RS] [Node.js] uninstall address: %p\n", uninstall); 135 | } 136 | -------------------------------------------------------------------------------- /src/rs_php.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by zhuonan li on 8/18/21. 3 | // 4 | 5 | #include "php.h" 6 | #include 7 | #include "../rs_header.h" 8 | 9 | 10 | extern void *__libc_dlopen_mode(const char *, int); 11 | 12 | extern void *__libc_dlsym(void *, const char *); 13 | 14 | extern void *__libc_dlclose(void *); 15 | 16 | extern char *get_binary_path(pid_t pid); 17 | 18 | extern void hook_module(char *elf_path, const char *func_name, void *new_func, int inline_hook, int is_save_symbol); 19 | 20 | void (*analyze_event)(const char *type, char *param, char *bt, void *param_addr); 21 | 22 | 23 | __attribute__((unused)) void dump_back_trace(char *bt) { 24 | zval backtrace; 25 | zend_fetch_debug_backtrace(&backtrace, 0, 0, 0); 26 | #if PHP_MAJOR_VERSION >= 7 27 | zend_array *ht = Z_ARRVAL(backtrace); 28 | Bucket *p = ht->arData; 29 | Bucket *end = p + ht->nNumUsed; 30 | 31 | for (; p != end; p++) { 32 | zval *z = &p->val; 33 | // stack trace array 34 | if (Z_TYPE_P(z) == IS_ARRAY) { 35 | zend_array *ht1 = Z_ARRVAL_P(z); 36 | Bucket *p1 = ht1->arData; 37 | Bucket *end1 = p1 + ht1->nNumUsed; 38 | zend_string *string_key; 39 | 40 | for (; p1 != end1; p1++) { 41 | zval *z1 = &p1->val; 42 | string_key = p1->key; 43 | 44 | if (string_key) { 45 | char *t = ZSTR_VAL(string_key); 46 | if (Z_TYPE_P(z1) == IS_STRING) { 47 | if (strncmp(t, "file", 4) == 0 || strncmp(t, "function", 8) == 0) { 48 | zend_string *str = zval_get_string(z1); 49 | char *c = ZSTR_VAL(str); 50 | strncat(bt, c, SIZE_256); 51 | if (strncmp(t, "function", 8) != 0) { 52 | strncat(bt, ".", SIZE_2); 53 | } 54 | zend_string_release(str); 55 | } 56 | } 57 | } 58 | } 59 | strncat(bt, "\n", SIZE_2); 60 | } 61 | } 62 | zend_array_destroy(ht); 63 | #else 64 | #if PHP_MINOR_VERSION >= 4 65 | zval **tmp; 66 | zval **file, **function; 67 | HashPosition hashPosition; 68 | 69 | zend_hash_internal_pointer_reset_ex(Z_ARRVAL(backtrace), &hashPosition); 70 | zend_hash_get_current_data_ex(Z_ARRVAL(backtrace), (void **) &tmp, &hashPosition); 71 | 72 | while (1) { 73 | int i = zend_hash_find(Z_ARRVAL_PP(tmp), "file", sizeof("file"), (void **) &file); 74 | zend_hash_find(Z_ARRVAL_PP(tmp), "function", sizeof("function"), (void **) &function); 75 | zend_hash_move_forward_ex(Z_ARRVAL(backtrace), &hashPosition); 76 | if (zend_hash_get_current_data_ex(Z_ARRVAL(backtrace), (void **) &tmp, &hashPosition) == FAILURE) { 77 | break; 78 | } 79 | if (i == SUCCESS) { 80 | strncat(bt, Z_STRVAL_PP(file), Z_STRLEN_PP(file)); 81 | strncat(bt, ".", SIZE_2); 82 | strncat(bt, Z_STRVAL_PP(function), Z_STRLEN_PP(function)); 83 | strncat(bt, "\n", SIZE_2); 84 | } 85 | } 86 | #else 87 | printf("[*] Not Supported PHP Version.\n"); 88 | #endif 89 | #endif 90 | } 91 | 92 | 93 | //* {{{ php_exec 94 | // * If type==0, only last line of output is returned (exec) 95 | // * If type==1, all lines will be printed and last lined returned (system) 96 | // * If type==2, all lines will be saved to given array (exec with &$array) 97 | // * If type==3, output will be printed binary, no lines will be saved or returned (passthru) 98 | // * 99 | // */ 100 | //PHPAPI int php_exec(int type, const char *cmd, zval *array, zval *return_value){ 101 | void rs_php_exec(int type, char *cmd, void *array, void *return_value) { 102 | printf("[*] [UDS] [RS] Detected [php_exec] with cmd: [%s]\n", cmd); 103 | 104 | char *bt = calloc(1, SIZE_1K); 105 | dump_back_trace(bt); 106 | analyze_event("php_exec", cmd, bt, cmd); 107 | free(bt); 108 | bt = NULL; 109 | 110 | } 111 | 112 | void *engine_module = NULL; 113 | 114 | 115 | __attribute__((unused)) void install() { 116 | 117 | engine_module = __libc_dlopen_mode(RS_ENGINE_PATH, 2); 118 | if (engine_module == NULL) { 119 | printf("[!] Failed to load engine module.\n"); 120 | return; 121 | } 122 | 123 | analyze_event = __libc_dlsym(engine_module, "analyze_event"); 124 | if (analyze_event == NULL) { 125 | printf("[!] Failed to locate analyze_event.\n"); 126 | return; 127 | } 128 | 129 | char *binary_path = get_binary_path(getpid()); 130 | hook_module(binary_path, "php_exec", rs_php_exec, 1, 0); 131 | free(binary_path); 132 | binary_path = NULL; 133 | } 134 | 135 | extern void uninstall_hook(); 136 | 137 | __attribute__((unused)) void uninstall() { 138 | DEBUG_PRINT(("[*] [UDS] [RS] Uninstalling UDS-PHP extension.\n")); 139 | uninstall_hook(); 140 | __libc_dlclose(engine_module); 141 | DEBUG_PRINT(("[*] [UDS] [RS] PHP module has been successfully uninstalled.\n")); 142 | } 143 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20.2) 2 | project(rosetta_s C) 3 | 4 | set(CMAKE_C_STANDARD 99) 5 | set(CMAKE_C_FLAGS_DEBUG "-DRS_DEBUG") 6 | 7 | # Module info 8 | add_definitions(-D__KERNEL__ -DMODULE) 9 | 10 | # Find kernel release 11 | execute_process( 12 | COMMAND uname -r 13 | OUTPUT_VARIABLE KERNEL_RELEASE 14 | OUTPUT_STRIP_TRAILING_WHITESPACE 15 | ) 16 | 17 | # Find Path of kernel headers 18 | find_path( 19 | KERNEL_HEADERS_DIR 20 | include/linux/user.h 21 | PATHS /usr/src/kernels/${KERNEL_RELEASE} /usr/src/linux-headers-${KERNEL_RELEASE} 22 | ) 23 | 24 | message(STATUS "Kernel Release: ${KERNEL_RELEASE}") 25 | message(STATUS "Kernel Headers: ${KERNEL_HEADERS_DIR}") 26 | 27 | # Add header files 28 | include_directories( 29 | # /usr/include 30 | # ${KERNEL_HEADERS_DIR}/include 31 | # ${KERNEL_HEADERS_DIR}/arch/x86/include 32 | ) 33 | 34 | add_library(ext_module src/playground/ext_module.c) 35 | add_executable(rs_debug src/playground/rs_debug.c src/rs_factory.c) 36 | 37 | add_executable(nflog src/playground/nf-log.c) 38 | target_include_directories(nflog PRIVATE /usr/include) 39 | 40 | # link section. 41 | target_link_libraries(rs_debug ${CMAKE_DL_LIBS} pthread ext_module) 42 | #target_compile_options(rs_debug PRIVATE "-fomit-frame-pointer") 43 | 44 | add_library(rs_engine SHARED src/rs_engine.c) 45 | 46 | add_library(rs_jni SHARED src/VM/rs_jni.c) 47 | target_include_directories(rs_jni PRIVATE /home/ubuntu/.sdkman/candidates/java/8.0.302-open/include /home/ubuntu/.sdkman/candidates/java/8.0.302-open/include/linux) 48 | 49 | add_library(rs_c SHARED src/VM/rs_c.c src/rs_factory.c) 50 | add_library(rs_js SHARED src/VM/rs_js.c src/rs_factory.c) 51 | add_library(rs_go SHARED src/VM/rs_go.c src/rs_factory.c) 52 | add_library(rs_jdk6 SHARED src/VM/rs_jvm.c src/rs_factory.c) 53 | add_library(rs_jdk7 SHARED src/VM/rs_jvm.c src/rs_factory.c) 54 | add_library(rs_jdk8 SHARED src/VM/rs_jvm.c src/rs_factory.c) 55 | add_library(rs_jdk17 SHARED src/VM/rs_jvm.c src/rs_factory.c) 56 | 57 | add_library(rs_py24 SHARED src/VM/rs_py.c src/rs_factory.c) 58 | add_library(rs_py25 SHARED src/VM/rs_py.c src/rs_factory.c) 59 | add_library(rs_py26 SHARED src/VM/rs_py.c src/rs_factory.c) 60 | add_library(rs_py27 SHARED src/VM/rs_py.c src/rs_factory.c) 61 | add_library(rs_py30 SHARED src/VM/rs_py.c src/rs_factory.c) 62 | add_library(rs_py31 SHARED src/VM/rs_py.c src/rs_factory.c) 63 | add_library(rs_py32 SHARED src/VM/rs_py.c src/rs_factory.c) 64 | add_library(rs_py33 SHARED src/VM/rs_py.c src/rs_factory.c) 65 | add_library(rs_py34 SHARED src/VM/rs_py.c src/rs_factory.c) 66 | add_library(rs_py35 SHARED src/VM/rs_py.c src/rs_factory.c) 67 | add_library(rs_py36 SHARED src/VM/rs_py.c src/rs_factory.c) 68 | add_library(rs_py37 SHARED src/VM/rs_py.c src/rs_factory.c) 69 | add_library(rs_py38 SHARED src/VM/rs_py.c src/rs_factory.c) 70 | add_library(rs_py39 SHARED src/VM/rs_py.c src/rs_factory.c) 71 | add_library(rs_py310 SHARED src/VM/rs_py.c src/rs_factory.c) 72 | add_library(rs_py311 SHARED src/VM/rs_py.c src/rs_factory.c) 73 | 74 | add_library(rs_php54 SHARED src/VM/rs_php.c src/rs_factory.c) 75 | add_library(rs_php55 SHARED src/VM/rs_php.c src/rs_factory.c) 76 | add_library(rs_php56 SHARED src/VM/rs_php.c src/rs_factory.c) 77 | add_library(rs_php70 SHARED src/VM/rs_php.c src/rs_factory.c) 78 | add_library(rs_php71 SHARED src/VM/rs_php.c src/rs_factory.c) 79 | add_library(rs_php72 SHARED src/VM/rs_php.c src/rs_factory.c) 80 | add_library(rs_php73 SHARED src/VM/rs_php.c src/rs_factory.c) 81 | add_library(rs_php74 SHARED src/VM/rs_php.c src/rs_factory.c) 82 | add_library(rs_php80 SHARED src/VM/rs_php.c src/rs_factory.c) 83 | add_library(rs_php81 SHARED src/VM/rs_php.c src/rs_factory.c) 84 | 85 | add_library(rs_rb27 SHARED src/VM/rs_rb.c src/rs_factory.c) 86 | add_library(rs_rb30 SHARED src/VM/rs_rb.c src/rs_factory.c) 87 | add_library(rs_rb31 SHARED src/VM/rs_rb.c src/rs_factory.c) 88 | 89 | # include section. 90 | target_include_directories(rs_jdk6 PRIVATE /home/ubuntu/.sdkman/candidates/java/6.0.119-zulu/include /home/ubuntu/.sdkman/candidates/java/6.0.119-zulu/include/linux) 91 | target_include_directories(rs_jdk7 PRIVATE /home/ubuntu/.sdkman/candidates/java/7.0.342-zulu/include /home/ubuntu/.sdkman/candidates/java/7.0.342-zulu/include/linux) 92 | target_include_directories(rs_jdk8 PRIVATE /home/ubuntu/.sdkman/candidates/java/8.0.302-open/include /home/ubuntu/.sdkman/candidates/java/8.0.302-open/include/linux) 93 | target_include_directories(rs_jdk17 PRIVATE /home/ubuntu/.sdkman/candidates/java/17.0.6-amzn/include /home/ubuntu/.sdkman/candidates/java/17.0.6-amzn/include/linux) 94 | 95 | target_include_directories(rs_py24 PRIVATE /home/ubuntu/python/Python-2.4.6 /home/ubuntu/python/Python-2.4.6/Include) 96 | target_include_directories(rs_py25 PRIVATE /home/ubuntu/python/Python-2.5.6 /home/ubuntu/python/Python-2.5.6/Include) 97 | target_include_directories(rs_py26 PRIVATE /home/ubuntu/python/Python-2.6.9 /home/ubuntu/python/Python-2.6.9/Include) 98 | target_include_directories(rs_py27 PRIVATE /home/ubuntu/python/Python-2.7.18 /home/ubuntu/python/Python-2.7.18/Include) 99 | target_include_directories(rs_py30 PRIVATE /home/ubuntu/python/Python-3.0.1 /home/ubuntu/python/Python-3.0.1/Include) 100 | target_include_directories(rs_py31 PRIVATE /home/ubuntu/python/Python-3.1.5 /home/ubuntu/python/Python-3.1.5/Include) 101 | target_include_directories(rs_py32 PRIVATE /home/ubuntu/python/Python-3.2.6 /home/ubuntu/python/Python-3.2.6/Include) 102 | target_include_directories(rs_py33 PRIVATE /home/ubuntu/python/Python-3.3.7 /home/ubuntu/python/Python-3.3.7/Include) 103 | target_include_directories(rs_py34 PRIVATE /home/ubuntu/python/Python-3.4.10 /home/ubuntu/python/Python-3.4.10/Include) 104 | target_include_directories(rs_py35 PRIVATE /home/ubuntu/python/Python-3.5.10 /home/ubuntu/python/Python-3.5.10/Include) 105 | target_include_directories(rs_py36 PRIVATE /home/ubuntu/python/Python-3.6.15 /home/ubuntu/python/Python-3.6.15/Include) 106 | target_include_directories(rs_py37 PRIVATE /home/ubuntu/python/Python-3.7.12 /home/ubuntu/python/Python-3.7.12/Include) 107 | target_include_directories(rs_py38 PRIVATE /home/ubuntu/python/Python-3.8.13 /home/ubuntu/python/Python-3.8.13/Include) 108 | target_include_directories(rs_py39 PRIVATE /home/ubuntu/python/Python-3.9.13 /home/ubuntu/python/Python-3.9.13/Include) 109 | target_include_directories(rs_py310 PRIVATE /home/ubuntu/python/Python-3.10.5 /home/ubuntu/python/Python-3.10.5/Include) 110 | target_include_directories(rs_py311 PRIVATE /home/ubuntu/python/Python-3.11.2 /home/ubuntu/python/Python-3.11.2/Include) 111 | 112 | target_include_directories(rs_php54 PRIVATE /home/ubuntu/php/php-5.4.0/ /home/ubuntu/php/php-5.4.0/main /home/ubuntu/php/php-5.4.0/Zend /home/ubuntu/php/php-5.4.0/TSRM /home/ubuntu/php/php-5.4.0/include) 113 | target_include_directories(rs_php55 PRIVATE /home/ubuntu/php/php-5.5.0/ /home/ubuntu/php/php-5.5.0/main /home/ubuntu/php/php-5.5.0/Zend /home/ubuntu/php/php-5.5.0/TSRM /home/ubuntu/php/php-5.5.0/include) 114 | target_include_directories(rs_php56 PRIVATE /home/ubuntu/php/php-5.6.0/ /home/ubuntu/php/php-5.6.0/main /home/ubuntu/php/php-5.6.0/Zend /home/ubuntu/php/php-5.6.0/TSRM /home/ubuntu/php/php-5.6.0/include) 115 | target_include_directories(rs_php70 PRIVATE /home/ubuntu/php/php-7.0.0/ /home/ubuntu/php/php-7.0.0/main /home/ubuntu/php/php-7.0.0/Zend /home/ubuntu/php/php-7.0.0/TSRM /home/ubuntu/php/php-7.0.0/include) 116 | target_include_directories(rs_php71 PRIVATE /home/ubuntu/php/php-7.1.0/ /home/ubuntu/php/php-7.1.0/main /home/ubuntu/php/php-7.1.0/Zend /home/ubuntu/php/php-7.1.0/TSRM /home/ubuntu/php/php-7.1.0/include) 117 | target_include_directories(rs_php72 PRIVATE /home/ubuntu/php/php-7.2.0/ /home/ubuntu/php/php-7.2.0/main /home/ubuntu/php/php-7.2.0/Zend /home/ubuntu/php/php-7.2.0/TSRM /home/ubuntu/php/php-7.2.0/include) 118 | target_include_directories(rs_php73 PRIVATE /home/ubuntu/php/php-7.3.0/ /home/ubuntu/php/php-7.3.0/main /home/ubuntu/php/php-7.3.0/Zend /home/ubuntu/php/php-7.3.0/TSRM /home/ubuntu/php/php-7.3.0/include) 119 | target_include_directories(rs_php74 PRIVATE /home/ubuntu/php/php-7.4.0/ /home/ubuntu/php/php-7.4.0/main /home/ubuntu/php/php-7.4.0/Zend /home/ubuntu/php/php-7.4.0/TSRM /home/ubuntu/php/php-7.4.0/include) 120 | target_include_directories(rs_php80 PRIVATE /home/ubuntu/php/php-8.0.0/ /home/ubuntu/php/php-8.0.0/main /home/ubuntu/php/php-8.0.0/Zend /home/ubuntu/php/php-8.0.0/TSRM /home/ubuntu/php/php-8.0.0/include) 121 | target_include_directories(rs_php81 PRIVATE /home/ubuntu/php/php-8.1.0/ /home/ubuntu/php/php-8.1.0/main /home/ubuntu/php/php-8.1.0/Zend /home/ubuntu/php/php-8.1.0/TSRM /home/ubuntu/php/php-8.1.0/include) 122 | 123 | target_include_directories(rs_rb27 PRIVATE /home/ubuntu/ruby/ruby-2.7.6/.ext/include/x86_64-linux /home/ubuntu/ruby/ruby-2.7.6/include) 124 | target_include_directories(rs_rb30 PRIVATE /home/ubuntu/ruby/ruby-3.0.4/.ext/include/x86_64-linux /home/ubuntu/ruby/ruby-3.0.4/include) 125 | target_include_directories(rs_rb31 PRIVATE /home/ubuntu/ruby/ruby-3.1.2/.ext/include/x86_64-linux /home/ubuntu/ruby/ruby-3.1.2/include) 126 | 127 | 128 | # cxx section. 129 | enable_language(CXX) 130 | 131 | add_library(rs_node6 SHARED src/VM/rs_js.cc) 132 | add_library(rs_node8 SHARED src/VM/rs_js.cc) 133 | add_library(rs_node9 SHARED src/VM/rs_js.cc) 134 | add_library(rs_node10 SHARED src/VM/rs_js.cc) 135 | add_library(rs_node11 SHARED src/VM/rs_js.cc) 136 | add_library(rs_node12 SHARED src/VM/rs_js.cc) 137 | add_library(rs_node13 SHARED src/VM/rs_js.cc) 138 | add_library(rs_node14 SHARED src/VM/rs_js.cc) 139 | add_library(rs_node15 SHARED src/VM/rs_js.cc) 140 | add_library(rs_node16 SHARED src/VM/rs_js.cc) 141 | add_library(rs_node17 SHARED src/VM/rs_js.cc) 142 | add_library(rs_node18 SHARED src/VM/rs_js.cc) 143 | 144 | 145 | target_include_directories(rs_node6 PRIVATE /home/ubuntu/.nvm/versions/node/v6.17.1/include/node) # NAPI 3 146 | target_include_directories(rs_node8 PRIVATE /home/ubuntu/.nvm/versions/node/v8.12.0/include/node) # NAPI 3 147 | target_include_directories(rs_node9 PRIVATE /home/ubuntu/.nvm/versions/node/v9.11.2/include/node) # NAPI 3 148 | target_include_directories(rs_node10 PRIVATE /home/ubuntu/.nvm/versions/node/v10.24.1/include/node) # NAPI 3 149 | target_include_directories(rs_node11 PRIVATE /home/ubuntu/.nvm/versions/node/v11.15.0/include/node) # NAPI 3 150 | target_include_directories(rs_node12 PRIVATE /home/ubuntu/.nvm/versions/node/v12.22.12/include/node) # NAPI 4 151 | target_include_directories(rs_node13 PRIVATE /home/ubuntu/.nvm/versions/node/v13.14.0/include/node) # NAPI 5 152 | target_include_directories(rs_node14 PRIVATE /home/ubuntu/.nvm/versions/node/v14.19.3/include/node) # NAPI 6 153 | target_include_directories(rs_node15 PRIVATE /home/ubuntu/.nvm/versions/node/v15.14.0/include/node) # NAPI 7 154 | target_include_directories(rs_node16 PRIVATE /home/ubuntu/.nvm/versions/node/v16.15.1/include/node) # NAPI 8 155 | target_include_directories(rs_node17 PRIVATE /home/ubuntu/.nvm/versions/node/v17.9.1/include/node) # NAPI 8 156 | target_include_directories(rs_node18 PRIVATE /home/ubuntu/.nvm/versions/node/v18.4.0/include/node) # NAPI 8 157 | 158 | 159 | # ----- private study ----- 160 | add_executable(test_cpp src/playground/test_cpp.cpp) 161 | 162 | # --- Custom MAKE Section 163 | set(LIBS 164 | c 165 | go 166 | jdk6 jdk7 jdk8 jdk17 167 | node6 node8 node9 node10 node11 node12 node13 node14 node15 node16 node17 node18 168 | py24 py25 py26 py27 py30 py31 py32 py33 py34 py35 py36 py37 py38 py39 py310 py311 169 | php54 php55 php56 php70 php71 php72 php73 php74 php80 php81 170 | rb27 rb30 rb31 171 | ) 172 | 173 | function(do_archive) 174 | foreach (lang ${LIBS}) 175 | # compile rs_engine rs_js first. 176 | add_dependencies(rs_${lang} rs_engine rs_js) 177 | add_custom_command(TARGET rs_${lang} 178 | POST_BUILD 179 | COMMENT "[*] Running Script after build ${lang} ..." 180 | COMMAND ${CMAKE_COMMAND} -DLANG=${lang} -P ${CMAKE_SOURCE_DIR}/CMakeArchive.cmake 181 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 182 | ) 183 | set_property(TARGET rs_${lang} 184 | APPEND 185 | PROPERTY ADDITIONAL_CLEAN_FILES archive_${lang}.tgz 186 | ) 187 | endforeach () 188 | endfunction() 189 | 190 | do_archive() 191 | --------------------------------------------------------------------------------