From eaf547d83c3da5b8979b0c3dc0f3b9b0bb121b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=E1=BA=B9o=20K=C3=A9o?= Date: Tue, 28 May 2024 00:00:26 +0700 Subject: [PATCH] Add files via upload --- .github/ssc/src/embed.h | 104 +++++++++++++ .github/ssc/src/main.cpp | 285 ++++++++++++++++++++++++++++++++++ .github/ssc/src/obfuscate.h | 196 +++++++++++++++++++++++ .github/ssc/src/rc4.cpp | 46 ++++++ .github/ssc/src/rc4.h | 80 ++++++++++ .github/ssc/src/untar.h | 142 +++++++++++++++++ .github/ssc/src/untraceable.h | 78 ++++++++++ .github/ssc/src/utils.h | 57 +++++++ 8 files changed, 988 insertions(+) create mode 100644 .github/ssc/src/embed.h create mode 100644 .github/ssc/src/main.cpp create mode 100644 .github/ssc/src/obfuscate.h create mode 100644 .github/ssc/src/rc4.cpp create mode 100644 .github/ssc/src/rc4.h create mode 100644 .github/ssc/src/untar.h create mode 100644 .github/ssc/src/untraceable.h create mode 100644 .github/ssc/src/utils.h diff --git a/.github/ssc/src/embed.h b/.github/ssc/src/embed.h new file mode 100644 index 0000000..56cf55a --- /dev/null +++ b/.github/ssc/src/embed.h @@ -0,0 +1,104 @@ +#pragma once +#include +#include +#include +#include +#include +#include "utils.h" +#ifdef EMBED_ARCHIVE +#include "untar.h" +#endif + +#ifdef __APPLE__ +#include + +FORCE_INLINE std::vector read_data_sect(const char *name) { + const struct section_64 *sect = getsectbyname("binary", name); + if (!sect) { + perror(OBF("find data section failed")); + return std::vector(); + } + std::vector buf(sect->size, '\0'); + int fd_exe = open(get_exe_path().c_str(), O_RDONLY); + if (fd_exe == -1) { + perror(OBF("open executable file failed")); + return std::vector(); + } + lseek(fd_exe, sect->offset, SEEK_SET); + if (read(fd_exe, buf.data(), sect->size) != sect->size) { + perror(OBF("read data section failed")); + return std::vector(); + } + close(fd_exe); + return buf; +} +#endif + +FORCE_INLINE std::string extract_embeded_file() { +#ifdef __APPLE__ + auto buf = read_data_sect("i"); + if (buf.empty()) + exit(1); + char *data = buf.data(); + size_t size = buf.size(); +#else + extern char _binary_i_start; + extern char _binary_i_end; + char *data = &_binary_i_start; + size_t size = &_binary_i_end - &_binary_i_start; +#endif + + auto dir = OBF("/tmp/ssc"); + // delete the whole directory can cause troubles when multiple instances are running + //remove_directory(dir); + mkdir(dir, 0755); + char path[PATH_MAX]; + snprintf(path, sizeof(path), "%s/XXXXXX", dir); + if (!mkdtemp(path)) { + perror(OBF("create output directory failed")); + exit(1); + } + strcat(path, "/"); + +#if defined(EMBED_INTERPRETER_NAME) + strcat(path, base_name(STR(EMBED_INTERPRETER_NAME)).c_str()); + int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC); + if (fd == -1) { + perror(OBF("open output file failed")); + exit(1); + } + if (write(fd, data, size) != size) { + perror(OBF("write output file failed")); + exit(1); + } + close(fd); + if (chmod(path, 0755) == -1) { + perror(OBF("chmod 755 failed")); + exit(1); + } +#elif defined(EMBED_ARCHIVE) + char cwd[PATH_MAX]; + if (getcwd(cwd, sizeof(cwd)) == NULL) { + perror(OBF("get current dir failed")); + exit(1); + } + if (chdir(path) == -1) { + perror(OBF("change dir failed")); + exit(1); + } + extract_from_mem(data, size); + if (chdir(cwd) == -1) { + perror(OBF("change back dir failed")); + exit(1); + } +#endif + return path; +} + +static void remove_extract_dir() { + auto extract_dir = getenv("SSC_EXTRACT_DIR"); + if (extract_dir && !strncmp(extract_dir, "/tmp/", 5)) { + //fprintf(stderr, "remove %s\n", extract_dir); + remove_directory(extract_dir); + } +} diff --git a/.github/ssc/src/main.cpp b/.github/ssc/src/main.cpp new file mode 100644 index 0000000..147e81a --- /dev/null +++ b/.github/ssc/src/main.cpp @@ -0,0 +1,285 @@ +#define _LINUX_SOURCE_COMPAT +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "obfuscate.h" +#include "utils.h" +#ifdef __linux__ +#include +#endif + +#ifdef UNTRACEABLE +#include "untraceable.h" +#endif +#if defined(EMBED_INTERPRETER_NAME) || defined(EMBED_ARCHIVE) || defined(RC4_KEY) +#include "embed.h" +#endif +#ifdef RC4_KEY +#include "rc4.h" +#endif + +enum ScriptFormat { + SHELL, + PYTHON, + PERL, + JAVASCRIPT, + RUBY, + PHP, + R, + LUA, +}; + +int main(int argc, char* argv[]) { +#ifdef UNTRACEABLE + check_debugger(); +#endif + + std::string exe_path = get_exe_path(), base_dir = dir_name(exe_path); + std::string interpreter_path, extract_dir; + +#if defined(INTERPRETER) + interpreter_path = OBF(STR(INTERPRETER)); +#endif +#if defined(EMBED_INTERPRETER_NAME) + interpreter_path = extract_embeded_file(); + extract_dir = dir_name(interpreter_path); + atexit(remove_extract_dir); +#elif defined(EMBED_ARCHIVE) + base_dir = extract_dir = extract_embeded_file(); + atexit(remove_extract_dir); +#endif + setenv("SSC_EXTRACT_DIR", extract_dir.c_str(), 1); + setenv("SSC_EXECUTABLE_PATH", exe_path.c_str(), 1); + setenv("SSC_ARGV0", argv[0], 1); + + std::string script_name = OBF(R"SSC(SCRIPT_FILE_NAME)SSC"); +#ifdef RC4_KEY + const char* rc4_key = OBF(STR(RC4_KEY)); +#ifdef __APPLE__ + auto buf = read_data_sect("s"); + if (buf.empty()) + exit(1); + char* script_data = buf.data(); + int script_len = buf.size(); +#else + extern char _binary_s_start; + extern char _binary_s_end; + char* script_data = &_binary_s_start; + int script_len = &_binary_s_end - &_binary_s_start; +#endif + rc4((u8*) script_data, script_len, (u8*) rc4_key, strlen(rc4_key)); + const char* script = script_data; +#else + const char* script = OBF(R"SSC(SCRIPT_CONTENT)SSC"); + int script_len = strlen(script); +#endif + + // detect script format by file name suffix + ScriptFormat format = SHELL; + std::string shell("sh"); + auto pos = script_name.find_last_of("."); + if (pos != std::string::npos) { + auto suffix = script_name.substr(pos + 1); + std::transform(suffix.begin(), suffix.end(), suffix.begin(), [] (unsigned char c) { + return std::tolower(c); + }); + if (str_ends_with(suffix, "sh")) { + format = SHELL; + shell = suffix; + } else if (suffix == "py" || suffix == "pyw") { + format = PYTHON; + } else if (suffix == "pl") { + format = PERL; + } else if (suffix == "js") { + format = JAVASCRIPT; + } else if (suffix == "rb") { + format = RUBY; + } else if (suffix == "php") { + format = PHP; + } else if (suffix == "r") { + format = R; + } else if (suffix == "lua") { + format = LUA; + } + } + + std::vector args; + const char *shebang_end = script; + // parse shebang + if (script[0] == '#' && script[1] == '!') { + std::string line; + auto p = strpbrk(script, "\r\n"); + if (p) { + line.assign(script + 2, p - script - 2); + shebang_end = p + (p[0] == '\r' && p[1] == '\n' ? 2 : 1); + } else { + line.assign(script + 2); + shebang_end = script + script_len; + } + + wordexp_t wrde; + if (wordexp(line.c_str(), &wrde, 0) != 0) { + perror(OBF("parse shebang failed")); + return 1; + } + for (size_t i = 0; i < wrde.we_wordc; i++) { + auto s = wrde.we_wordv[i]; + if (args.empty()) { + if (!strcmp(s, "env") || !strcmp(s, "/usr/bin/env")) { + continue; + } + for (p = s; *p == '_' || isalnum(*p); ++p); + if (*p == '=') { + std::string name(s, p - s); + setenv(name.c_str(), ++p, 1); + continue; + } + } + args.emplace_back(s); + } + wordfree(&wrde); + + // detect script format by shebang + if (!args.empty()) { + if (str_ends_with(args[0], "sh")) { + format = SHELL; + shell = base_name(args[0]); + } else if (args[0].find("python") != std::string::npos || args[0].find("conda") != std::string::npos) { + format = PYTHON; + } else if (args[0].find("perl") != std::string::npos) { + format = PERL; + } else if (args[0].find("node") != std::string::npos) { + format = JAVASCRIPT; + } else if (args[0].find("ruby") != std::string::npos) { + format = RUBY; + } else if (args[0].find("php") != std::string::npos) { + format = PHP; + } else if (args[0].find("Rscript") != std::string::npos) { + format = R; + } else if (args[0].find("lua") != std::string::npos) { + format = LUA; + } + } + } + if (args.empty()) { + switch (format) { + case SHELL: args.emplace_back(shell); break; + case PYTHON: args.emplace_back("python"); break; + case PERL: args.emplace_back("perl"); break; + case JAVASCRIPT: args.emplace_back("node"); break; + case RUBY: args.emplace_back("ruby"); break; + case PHP: args.emplace_back("php"); break; + case R: args.emplace_back("Rscript"); break; + case LUA: args.emplace_back("lua"); break; + default: perror(OBF("unknown format")); return 4; + } + } + if (interpreter_path.empty()) { + // support relative path + pos = args[0].find('/'); + if (pos != std::string::npos && pos != 0) { + interpreter_path = base_dir + args[0]; + } else { + interpreter_path = args[0]; + } + } + setenv("SSC_INTERPRETER_PATH", interpreter_path.c_str(), 1); + + int fd_script[2]; + if (pipe(fd_script) == -1) { + perror(OBF("create pipe failed")); + return 2; + } + + int p = fork(); + if (p < 0) { + perror(OBF("fork failed")); + return 1; + } else if (p > 0) { // parent process + close(fd_script[1]); + + std::string fd_path = OBF("/proc/self/fd"); + struct stat st; +#ifdef __linux__ + if ((stat(fd_path.c_str(), &st) != 0 || !S_ISDIR(st.st_mode)) && getuid() == 0) { + mkdir("/proc", 0755); + mount("none", "/proc", "proc", 0, nullptr); + } +#endif + if (stat(OBF("/dev/fd"), &st) == 0 && S_ISDIR(st.st_mode)) { + fd_path = OBF("/dev/fd"); + } + fd_path += '/'; + fd_path += std::to_string(fd_script[0]); + + if (format == JAVASCRIPT) { + args.emplace_back("--preserve-symlinks-main"); + } +#ifdef FIX_ARGV0 + if (format == SHELL) { + args.emplace_back("-c"); + args.emplace_back(". " + fd_path); + args.emplace_back(argv[0]); + } else +#endif + args.emplace_back(fd_path); + + for (auto i = 1; i < argc; i++) { + args.emplace_back(argv[i]); + } + + std::vector cargs; + cargs.reserve(args.size() + 1); + for (const auto& arg : args) { + cargs.push_back(arg.c_str()); + } + cargs.push_back(NULL); + execvp(interpreter_path.c_str(), (char* const*) cargs.data()); + // error in execvp + perror(OBF("execvp failed")); + return 3; + + } else { // child process + + close(fd_script[0]); + +#ifdef FIX_ARGV0 + script_len -= shebang_end - script; + script = shebang_end; + if (format == SHELL) { + if (shell == "bash") { + // only bash 5+ support BASH_ARGV0 + //dprintf(fd_script[1], "BASH_ARGV0='%s'\n", str_replace_all(argv[0], "'", "'\\''").c_str()); + } else if (shell == "zsh") { + dprintf(fd_script[1], "0='%s'\n", str_replace_all(argv[0], "'", "'\\''").c_str()); + } else if (shell == "fish") { + dprintf(fd_script[1], "set 0 '%s'\n", str_replace_all(argv[0], "'", "'\\''").c_str()); + } + } else if (format == PYTHON) { + dprintf(fd_script[1], "import sys; sys.argv[0] = '''%s'''\n", argv[0]); + } else if (format == PERL) { + dprintf(fd_script[1], "$0 = '%s';\n", str_replace_all(argv[0], "'", "\\'").c_str()); + } else if (format == JAVASCRIPT) { + dprintf(fd_script[1], " __filename = `%s`; process.argv[1] = `%s`;\n", argv[0], argv[0]); + } +#endif + // write script content to writing end of fd_in pipe, then close it + write(fd_script[1], script, script_len); + close(fd_script[1]); + + // exit without calling atexit handlers + _Exit(0); + } +} diff --git a/.github/ssc/src/obfuscate.h b/.github/ssc/src/obfuscate.h new file mode 100644 index 0000000..7082f60 --- /dev/null +++ b/.github/ssc/src/obfuscate.h @@ -0,0 +1,196 @@ +/* --------------------------------- ABOUT ------------------------------------- + +Original Author: Adam Yaxley +Website: https://github.com/adamyaxley +License: See end of file + +Obfuscate +Guaranteed compile-time string literal obfuscation library for C++14 + +Usage: +Pass string literals into the AY_OBFUSCATE macro to obfuscate them at compile +time. AY_OBFUSCATE returns a reference to an ay::obfuscated_data object with the +following traits: + - Guaranteed obfuscation of string + The passed string is encrypted with a simple XOR cipher at compile-time to + prevent it being viewable in the binary image + - Global lifetime + The actual instantiation of the ay::obfuscated_data takes place inside a + lambda as a function level static + - Implicitly convertable to a char* + This means that you can pass it directly into functions that would normally + take a char* or a const char* + +Example: +const char* obfuscated_string = AY_OBFUSCATE("Hello World"); +std::cout << obfuscated_string << std::endl; + +----------------------------------------------------------------------------- */ +#pragma once +#include + +namespace ay +{ + // Obfuscates a string at compile time + template + class obfuscator + { + public: + // Obfuscates the string 'data' on construction + constexpr obfuscator(const char* data) + { + static_assert(KEY != '\0', "KEY must not be the null character."); + + // On construction each of the characters in the string is + // obfuscated with an XOR cipher based on KEY + for (std::size_t i = 0; i < N; i++) + { + m_data[i] = data[i] ^ KEY; + } + } + + constexpr const char* getData() const + { + return &m_data[0]; + } + + constexpr std::size_t getSize() const + { + return N; + } + + constexpr char getKey() const + { + return KEY; + } + + private: + + char m_data[N]{}; + }; + + // Handles decryption and re-encryption of an encrypted string at runtime + template + class obfuscated_data + { + public: + obfuscated_data(const obfuscator& obfuscator) + { + for (std::size_t i = 0; i < N; i++) + { + m_data[i] = obfuscator.getData()[i]; + } + } + + ~obfuscated_data() + { + // Zero m_data to remove it from memory + for (std::size_t i = 0; i < N; i++) + { + m_data[i] = 0; + } + } + + // Returns a pointer to the plain text string, decrypting it if + // necessary + operator char*() + { + decrypt(); + return m_data; + } + + // Manually decrypt the string + void decrypt() + { + if (is_encrypted()) + { + for (std::size_t i = 0; i < N; i++) + { + m_data[i] ^= KEY; + } + } + } + + // Manually re-encrypt the string + void encrypt() + { + if (!is_encrypted()) + { + for (std::size_t i = 0; i < N; i++) + { + m_data[i] ^= KEY; + } + } + } + + // Returns true if this string is currently encrypted, false otherwise. + bool is_encrypted() const + { + return m_data[N - 1] != '\0'; + } + + private: + + // Local storage for the string. Call is_encrypted() to check whether or + // not the string is currently obfuscated. + char m_data[N]; + }; + + // This function exists purely to extract the number of elements 'N' in the + // array 'data' + template + constexpr auto make_obfuscator(const char(&data)[N]) + { + return obfuscator(data); + } +} + +// Obfuscates the string 'data' at compile-time and returns a reference to a +// ay::obfuscated_data object with global lifetime that has functions for +// decrypting the string and is also implicitly convertable to a char* +#define AY_OBFUSCATE(data) AY_OBFUSCATE_KEY(data, '.') + +// Obfuscates the string 'data' with 'key' at compile-time and returns a +// reference to a ay::obfuscated_data object with global lifetime that has +// functions for decrypting the string and is also implicitly convertable to a +// char* +#define AY_OBFUSCATE_KEY(data, key) \ + []() -> ay::obfuscated_data& { \ + constexpr auto n = sizeof(data)/sizeof(data[0]); \ + static_assert(data[n - 1] == '\0', "String must be null terminated"); \ + constexpr auto obfuscator = ay::make_obfuscator(data); \ + static auto obfuscated_data = ay::obfuscated_data(obfuscator); \ + return obfuscated_data; \ + }() + +/* -------------------------------- LICENSE ------------------------------------ + +Public Domain (http://www.unlicense.org) + +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------------------------------------------------------------------------- */ + +#ifdef OBFUSCATE_KEY + #define OBF(x) (const char *) AY_OBFUSCATE_KEY(x, OBFUSCATE_KEY) +#else + #define OBF(x) (const char *) AY_OBFUSCATE(x) +#endif diff --git a/.github/ssc/src/rc4.cpp b/.github/ssc/src/rc4.cpp new file mode 100644 index 0000000..2d58715 --- /dev/null +++ b/.github/ssc/src/rc4.cpp @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include +#include "rc4.h" + +int main(int argc, const char **argv) { + if (argc != 4) { + return 1; + } + int fd_in = open(argv[1], O_RDONLY); + if (fd_in == -1) { + perror("open input file failed"); + return 1; + } + int fd_out = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd_out == -1) { + perror("open output file failed"); + return 1; + } + off_t size = lseek(fd_in, 0, SEEK_END); + lseek(fd_in, 0, SEEK_SET); + if (size == (off_t) -1) { + perror("seek failed"); + return 1; + } + char *buf = (char*) malloc(size); + if (!buf) { + perror("malloc failed"); + return 1; + } + if (read(fd_in, buf, size) != size) { + perror("read file failed"); + return 1; + } + rc4((u8*) buf, size, (u8*) argv[3], strlen(argv[3])); + if (write(fd_out, buf, size) != size) { + perror("write file failed"); + return 1; + } + free(buf); + close(fd_in); + close(fd_out); + return 0; +} diff --git a/.github/ssc/src/rc4.h b/.github/ssc/src/rc4.h new file mode 100644 index 0000000..2d7d590 --- /dev/null +++ b/.github/ssc/src/rc4.h @@ -0,0 +1,80 @@ +/* + * RC4 stream cipher + * Copyright (c) 2002-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ +#pragma once +#include "utils.h" + +typedef unsigned char u8; +typedef unsigned int u32; + +#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0) +/** + * rc4 - XOR RC4 stream to given data with skip-stream-start + * @key: RC4 key + * @keylen: RC4 key length + * @skip: number of bytes to skip from the beginning of the RC4 stream + * @data: data to be XOR'ed with RC4 stream + * @data_len: buf length + * + * Generate RC4 pseudo random stream for the given key, skip beginning of the + * stream, and XOR the end result with the data buffer to perform RC4 + * encryption/decryption. + */ +FORCE_INLINE void rc4_skip(const u8 *key, size_t keylen, size_t skip, + u8 *data, size_t data_len) +{ + u32 i, j, k; + u8 S[256], *pos; + size_t kpos; + /* Setup RC4 state */ + for (i = 0; i < 256; i++) + S[i] = i; + j = 0; + kpos = 0; + for (i = 0; i < 256; i++) { + j = (j + S[i] + key[kpos]) & 0xff; + kpos++; + if (kpos >= keylen) + kpos = 0; + S_SWAP(i, j); + } + /* Skip the start of the stream */ + i = j = 0; + for (k = 0; k < skip; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + } + /* Apply RC4 to data */ + pos = data; + for (k = 0; k < data_len; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *pos++ ^= S[(S[i] + S[j]) & 0xff]; + } +} +/** + * rc4 - XOR RC4 stream to given data + * @buf: data to be XOR'ed with RC4 stream + * @len: buf length + * @key: RC4 key + * @key_len: RC4 key length + * + * Generate RC4 pseudo random stream for the given key and XOR this with the + * data buffer to perform RC4 encryption/decryption. + */ +FORCE_INLINE void rc4(u8 *buf, size_t len, const u8 *key, size_t key_len) +{ + rc4_skip(key, key_len, 0, buf, len); +} diff --git a/.github/ssc/src/untar.h b/.github/ssc/src/untar.h new file mode 100644 index 0000000..844dd85 --- /dev/null +++ b/.github/ssc/src/untar.h @@ -0,0 +1,142 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "utils.h" + +/* + * These reporting functions use low-level I/O; on some systems, this + * is a significant code reduction. Of course, on many server and + * desktop operating systems, malloc() and even crt rely on printf(), + * which in turn pulls in most of the rest of stdio, so this is not an + * optimization at all there. (If you're going to pay 100k or more + * for printf() anyway, you may as well use it!) + */ +static FORCE_INLINE void +msg(const char *m) +{ + write(1, m, strlen(m)); +} + +static FORCE_INLINE void +errmsg(const char *m) +{ + write(2, m, strlen(m)); +} + +static FORCE_INLINE void +warn(const char *f, const char *m) +{ + errmsg(f); + errmsg(" failed: "); + errmsg(m); + errmsg("\n"); +} + +static FORCE_INLINE void +fail(const char *f, const char *m, int r) +{ + warn(f, m); + exit(r); +} + +static FORCE_INLINE int +copy_data(struct archive *ar, struct archive *aw) +{ + int r; + const void *buff; + size_t size; +#if ARCHIVE_VERSION_NUMBER >= 3000000 + int64_t offset; +#else + off_t offset; +#endif + + for (;;) { + r = archive_read_data_block(ar, &buff, &size, &offset); + if (r == ARCHIVE_EOF) + return (ARCHIVE_OK); + if (r != ARCHIVE_OK) + return (r); + r = archive_write_data_block(aw, buff, size, offset); + if (r != ARCHIVE_OK) { + warn("archive_write_data_block()", + archive_error_string(aw)); + return (r); + } + } +} + +static FORCE_INLINE void +extract_from_mem(const void *data, size_t size, int do_extract = 1, int verbose = 0, + int flags = ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS) +{ + struct archive *a; + struct archive *ext; + struct archive_entry *entry; + int r; + + a = archive_read_new(); + ext = archive_write_disk_new(); + archive_write_disk_set_options(ext, flags); + /* + * Note: archive_write_disk_set_standard_lookup() is useful + * here, but it requires library routines that can add 500k or + * more to a static executable. + */ + archive_read_support_format_tar(a); + archive_read_support_filter_gzip(a); + /* + * On my system, enabling other archive formats adds 20k-30k + * each. Enabling gzip decompression adds about 20k. + * Enabling bzip2 is more expensive because the libbz2 library + * isn't very well factored. + */ + // if (filename != NULL && strcmp(filename, "-") == 0) + // filename = NULL; + // if ((r = archive_read_open_filename(a, filename, 10240))) + // fail("archive_read_open_filename()", + // archive_error_string(a), r); + if (r = archive_read_open_memory(a, data, size)) + fail("archive_read_open_memory()", + archive_error_string(a), r); + for (;;) { + r = archive_read_next_header(a, &entry); + if (r == ARCHIVE_EOF) + break; + if (r != ARCHIVE_OK) + fail("archive_read_next_header()", + archive_error_string(a), 1); + if (verbose && do_extract) + msg("x "); + if (verbose || !do_extract) + msg(archive_entry_pathname(entry)); + if (do_extract) { + r = archive_write_header(ext, entry); + if (r != ARCHIVE_OK) + warn("archive_write_header()", + archive_error_string(ext)); + else { + copy_data(a, ext); + r = archive_write_finish_entry(ext); + if (r != ARCHIVE_OK) + fail("archive_write_finish_entry()", + archive_error_string(ext), 1); + } + + } + if (verbose || !do_extract) + msg("\n"); + } + archive_read_close(a); + archive_read_free(a); + + archive_write_close(ext); + archive_write_free(ext); +} diff --git a/.github/ssc/src/untraceable.h b/.github/ssc/src/untraceable.h new file mode 100644 index 0000000..2caae07 --- /dev/null +++ b/.github/ssc/src/untraceable.h @@ -0,0 +1,78 @@ +#pragma once +#include "obfuscate.h" +#include "utils.h" + +#if defined(__CYGWIN__) +#include + +FORCE_INLINE void check_debugger() { + if (IsDebuggerPresent()) { + //perror(OBF("debugger present!")); + exit(1); + } +} + +#elif defined(__linux__) || defined(__APPLE__) +#include +#include +#include +#include + +#if !defined(PT_ATTACHEXC) /* New replacement for PT_ATTACH */ + #if defined(PTRACE_ATTACH) + #define PT_ATTACHEXC PTRACE_ATTACH + #elif defined(PT_ATTACH) + #define PT_ATTACHEXC PT_ATTACH + #endif +#endif +#if !defined(PT_DETACH) + #if defined(PTRACE_DETACH) + #define PT_DETACH PTRACE_DETACH + #endif +#endif + +FORCE_INLINE void check_debugger() { +#ifdef __linux__ + std::ifstream ifs(OBF("/proc/self/status")); + std::string line, needle = OBF("TracerPid:\t"); + int tracer_pid = 0; + while (std::getline(ifs, line)) { + auto idx = line.find(needle); + if (idx != std::string::npos) { + tracer_pid = atoi(line.c_str() + idx + needle.size()); + break; + } + } + ifs.close(); + if (tracer_pid != 0) { + //fprintf(stderr, OBF("found tracer. tracer_pid=%d\n"), tracer_pid); + exit(1); + } + std::ifstream ifs2(OBF("/proc/sys/kernel/yama/ptrace_scope")); + int ptrace_scope = 0; + ifs2 >> ptrace_scope; + ifs2.close(); + if (getuid() != 0 && ptrace_scope != 0) { + //fprintf(stderr, OBF("skip ptrace detection. uid=%d ptrace_scope=%d\n"), getuid(), ptrace_scope); + return; + } +#endif + int ppid = getpid(); + int p = fork(); + if (p < 0) { + perror(OBF("fork failed")); + exit(1); + } else if (p > 0) { // parent process + waitpid(p, 0, 0); + } else { + if (ptrace(PT_ATTACHEXC, ppid, 0, 0) == 0) { + wait(0); + ptrace(PT_DETACH, ppid, 0, 0); + } else { + //perror(OBF("being traced!")); + kill(ppid, SIGKILL); + } + exit(0); + } +} +#endif diff --git a/.github/ssc/src/utils.h b/.github/ssc/src/utils.h new file mode 100644 index 0000000..8614728 --- /dev/null +++ b/.github/ssc/src/utils.h @@ -0,0 +1,57 @@ +#pragma once +#include +#include +#include +#include +#if defined(__APPLE__) +#include +#endif +#include "obfuscate.h" + +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +#define FORCE_INLINE __attribute__((always_inline)) inline + +FORCE_INLINE std::string get_exe_path() { + char buf[PATH_MAX] = {0}; + int size = sizeof(buf); +#if defined(__linux__) || defined(__CYGWIN__) + size = readlink(OBF("/proc/self/exe"), buf, size); + return size == -1 ? std::string() : std::string(buf, size); +#elif defined(__APPLE__) + return _NSGetExecutablePath(buf, (unsigned*) &size) ? std::string() : std::string(buf); +#else + #error unsupported operating system! +#endif +} + +FORCE_INLINE std::string dir_name(const std::string& s) { + return s.substr(0, s.find_last_of("\\/") + 1); +} + +FORCE_INLINE std::string base_name(const std::string& s) { + return s.substr(s.find_last_of("\\/") + 1); +} + +FORCE_INLINE bool str_ends_with(const std::string& s, const std::string& e) { + return s.size() >= e.size() && s.compare(s.size() - e.size(), e.size(), e) == 0; +} + +FORCE_INLINE std::string str_replace_all(std::string str, const std::string& from, const std::string& to) { + size_t start_pos = 0; + while ((start_pos = str.find(from, start_pos)) != std::string::npos) { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); + } + return str; +} + +static int _remove_file(const char *pathname, const struct stat *sbuf, int type, struct FTW *ftwb) { + remove(pathname); + return 0; +} + +FORCE_INLINE void remove_directory(const char *dir) { + nftw(dir, _remove_file, 10, FTW_DEPTH | FTW_MOUNT | FTW_PHYS); +}