[
  {
    "path": ".dockerignore",
    "content": "cmake-build*/\n.cerberus*/\nbuild/\n.idea/\nradare2/\nGoliath/\n"
  },
  {
    "path": ".github/workflows/main.yml",
    "content": "name: main\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n  workflow_dispatch:\n\njobs:\n  check_app:\n    runs-on: ubuntu-22.04\n    steps:\n    - name: clone_project\n      uses: actions/checkout@v3\n    - name: init_submodules\n      run: git submodule update --init --recursive\n    - name: apt_dependencies\n      run: sudo apt update && sudo apt upgrade && sudo apt -y install libarchive-dev libcurl4-openssl-dev zlib1g-dev libelf-dev gcc-multilib g++-multilib make cmake\n    - name: setup_radare2\n      run: |\n        git clone https://github.com/radare/radare2.git\n        cd radare2\n        ./sys/install.sh\n    - name: setup_rust\n      uses: actions-rs/toolchain@v1\n      with:\n        toolchain: stable\n    - name: setup_go\n      uses: actions/setup-go@v4\n      with:\n        go-version: '>=1.15.0'\n    - name: setup_python\n      uses: actions/setup-python@v4\n      with:\n        python-version: '3.10'\n    - name: compile\n      run: mkdir build && cd build && cmake .. && make\n    - name: append_path\n      run: echo \"`pwd`/build\" >> $GITHUB_PATH\n    - name: compile_rust_tests\n      run: |\n        echo \"Installing Rust 32-bit architecture\"\n        rustup target install i686-unknown-linux-gnu\n        echo \"Compiling Rust tests\"\n        for i in $(seq 1 1 `ls ./test/Rust | wc -l`)\n        do\n          echo \"Compiling Rust test n°$i\"\n          cd test/Rust/test_$i\n          cargo build --release\n          cargo build --release --target=i686-unknown-linux-gnu\n          cd ../../..\n        done\n    - name: run_rust_tests_32\n      run: |\n        echo \"Running Rust 32bit tests\"\n        for i in $(seq 1 1 `ls ./test/Rust | wc -l`)\n        do\n          echo \"Starting Rust test n°$i\"\n          cd test/Rust/test_$i/target/i686-unknown-linux-gnu/release\n          cerberus ./test_$i --no-prompt -output ./test_$i-syms\n          result=$(nm test_$i-syms)\n          cd ../../..\n          expected_result=$(cat 'result.txt')\n          IFS='\\n' read -ra expected_result_arr <<< $expected_result\n          for expected_result_line in ${expected_result_arr[@]};\n          do\n            if [[ $result != *$expected_result_line* ]]\n            then\n              echo \"Failure !\"\n              echo \"Content of result :\"\n              echo $result\n              exit 1\n            fi\n          done\n          echo \"Success !\"\n          cd ../../../\n        done\n    - name: run_rust_tests_64\n      run: |\n        echo \"Running Rust 64bit tests\"\n        for i in $(seq 1 1 `ls ./test/Rust | wc -l`)\n        do\n          echo \"Starting Rust test n°$i\"\n          cd test/Rust/test_$i/target/release\n          cerberus ./test_$i --no-prompt -output ./test_$i-syms\n          result=$(nm test_$i-syms)\n          cd ../..\n          expected_result=$(cat 'result.txt')\n          IFS='\\n' read -ra expected_result_arr <<< $expected_result\n          for expected_result_line in ${expected_result_arr[@]};\n          do\n            if [[ $result != *$expected_result_line* ]]\n            then\n              echo \"Failure !\"\n              echo \"Content of result :\"\n              echo $result\n              exit 1\n            fi\n          done\n          echo \"Success !\"\n          cd ../../../\n        done\n    - name: compile_go_tests\n      run: |\n        for i in $(seq 1 1 `ls ./test/Go | wc -l`)\n        do\n          echo \"Compiling Go test n°$i\"\n          cd test/Go/test_$i/src\n          go get ./...\n          go build -ldflags=\"-s -w\" -o ../test_1 test_1.go\n          cd ../../../..\n        done\n    - name: run_go_tests\n      run: |\n        echo \"Running Go tests\"\n        for i in $(seq 1 1 `ls ./test/Go | wc -l`)\n        do\n          echo \"Starting Go test n°$i\"\n          cd test/Go/test_1\n          cerberus ./test_$i --no-prompt -output ./test_$i-syms\n          result=$(nm test_$i-syms)\n          expected_result=$(cat 'result.txt')\n          IFS='\\n' read -ra expected_result_arr <<< $expected_result\n          for expected_result_line in ${expected_result_arr[@]};\n          do\n            if [[ $result != *$expected_result_line* ]]\n            then\n              echo \"Failure !\"\n              echo \"Content of result :\"\n              echo $result\n              exit 1\n            fi\n          done\n          echo \"Success !\"\n          cd ../../../\n        done\n"
  },
  {
    "path": ".gitignore",
    "content": "cmake-build*/\n.cerberus*/\nbuild/\n.idea/\nradare2/\nGoliath/\nlab/\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"lib/Goliath\"]\n\tpath = lib/Goliath\n\turl = https://github.com/h311d1n3r/Goliath\n[submodule \"lib/argparse\"]\n\tpath = lib/argparse\n\turl = https://github.com/p-ranav/argparse\n[submodule \"lib/lief\"]\n\tpath = lib/lief\n\turl = https://github.com/lief-project/LIEF\n[submodule \"lib/gzip-hpp\"]\n\tpath = lib/gzip-hpp\n\turl = https://github.com/mapbox/gzip-hpp\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.22)\nproject(Cerberus)\n\nset(CMAKE_CXX_STANDARD 17)\n\nfind_path(libelf.h NAMES LIBELF)\n\n# will be included statically\nadd_subdirectory(lib/lief)\nadd_subdirectory(lib/argparse)\n# will be included dynamically\nfind_package(CURL REQUIRED)\nfind_package(ZLIB REQUIRED)\nfind_package(LibArchive REQUIRED)\n\ninclude_directories(lib/argparse/include lib/lief/include lib/gzip-hpp/include ${LIBELF} src)\n\nadd_executable(Cerberus src/main.cpp src/utils/logger.h src/utils/logger.cpp src/utils/arg_parser.h src/utils/config.h src/utils/arg_parser.cpp src/global_defs.h src/langs/lang_manager.h src/langs/lang_manager.cpp src/types/value_ordered_map.h src/binaries/bin_identifier.cpp src/user/user_prompt.cpp src/utils/convert.h src/binaries/bin_handler.h src/binaries/handlers/elf_handler.h src/binaries/handlers/elf_handler.cpp src/binaries/bin_extractor.h src/binaries/bin_types.h src/binaries/extractors/lief_extractor.h src/binaries/extractors/lief_extractor.cpp src/binaries/extractors/radare_extractor.h src/binaries/extractors/radare_extractor.cpp src/binaries/handlers/pe_handler.h src/binaries/handlers/pe_handler.cpp src/binaries/bin_handler.cpp src/langs/lib_regex.h src/utils/search.h src/utils/search.cpp src/langs/lang_types.h src/langs/lib_regex.cpp src/utils/convert.cpp src/binaries/lib/lib_manager.h src/binaries/lib/lib_manager.cpp src/binaries/lib/install/lib_installer.h src/binaries/lib/install/rust_lib_installer.h src/binaries/lib/install/go_lib_installer.h src/binaries/lib/install/rust_lib_installer.cpp src/binaries/lib/install/go_lib_installer.cpp src/utils/file_downloader.h src/utils/file_downloader.cpp src/utils/file_operations.h src/utils/file_operations.cpp src/user/dependencies/dependency_manager.h src/command/command_executor.h src/command/command_executor.cpp src/user/local_config.h src/user/local_config.cpp src/user/dependencies/dependency_manager.cpp src/algorithm/part_hash_algorithm.h src/algorithm/part_hash_algorithm.cpp src/algorithm/algorithm.h src/binaries/extractors/libelf_extractor.h src/binaries/extractors/libelf_extractor.cpp src/binaries/pe_types.h src/binaries/extractors/go_extractor.h src/binaries/extractors/go_extractor.cpp)\n\ntarget_link_libraries(Cerberus PRIVATE argparse PRIVATE LIEF::LIEF PRIVATE CURL::libcurl PRIVATE ZLIB::ZLIB PRIVATE LibArchive::LibArchive PRIVATE uuid PRIVATE elf PRIVATE stdc++fs)\nset_target_properties(Cerberus PROPERTIES OUTPUT_NAME cerberus)\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 Aurélien Tournebise\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Cerberus\n## Description\n### A C++ tool to unstrip Rust and Go binaries (ELF and PE) \n**Cerberus** is the tool you want to use to make RUST and GO static analysis a lot easier.  \nBased on hashing and scoring systems, it can retrieve lots of symbol names.\n## How does it work ?\nAfter analyzing your ELF/PE binary to find the used libraries, **Cerberus** will download and build them.  \nThen the tool will hash (in various ways) the functions in your file and in the libraries to make matches.  \n## Table of contents\n[Installation](#install)  \n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[Download a release](#install_release)  \n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[Build the tool with Docker](#install_build_docker)  \n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[Build the tool on host](#install_build_host)  \n[How to use ?](#how)  \n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[Syntax](#how_syntax)  \n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[Parameters](#how_params)  \n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[Flags](#how_flags)  \n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[Example](#how_example)  \n[Warning](#warning)  \n\n<a name=\"install\"/>\n\n## Installation\n\n<a name=\"install_release\"/>\n\n### Download a release\nCheck the [Releases](https://github.com/h311d1n3r/Cerberus/releases/) tab on the Github project and download the latest one.  \n\n<a name=\"install_build_docker\"/>\n\n### Build the tool with Docker\n1. Clone the repository `git clone https://github.com/h311d1n3r/Cerberus && cd Cerberus`.\n2. Initialize git dependencies : `git submodule update --init`  \n3. Check the available Dockerfiles under `Cerberus/docker/{OS}`.  \n4. Build the docker image of your choice `docker build -f ./docker/{OS}/Dockerfile-{version} .`.\n5. You can run **Cerberus** from inside the docker or extract the binary on your host. This second choice needs to install the libraries listed in [this section](#install_build_host).  \n\n<a name=\"install_build_host\"/>\n\n### Build the tool on host  \n1. You need to have **libarchive**, **libcurl4-openssl**, **zlib1g**, **libelf** and the **uuid-dev** libraries installed on your system.  \nWith APT just do `apt -y install libarchive-dev libcurl4-openssl-dev zlib1g-dev libelf-dev`\n2. Clone the repository `git clone https://github.com/h311d1n3r/Cerberus && cd Cerberus`.\n3. Initialize git dependencies : `git submodule update --init`  \n4. Create the build directory `mkdir build && cd build`.  \n5. Run CMake to configure the project `cmake ..`.\n6. Run make to compile the project `make`.  \n\n<a name=\"how\"/>\n\n## How to use ?\n\n<a name=\"how_syntax\"/>\n\n### Syntax\n`cerberus binary [-param value] [--flag]`\n\n<a name=\"how_params\"/>\n\n### Parameters\n`output` -> Specifies the path for the resulting ELF file.  \n`part_hash_len` -> Specifies the length of a `part hash`. The `part hash` of a function is just a reduction of the function with a linear pace.\nThis technique is used to prevent fixed addresses from corrupting a standard hash. Default value : 20  \n`part_hash_trust` -> Specifies minimum ratio of similarity between the two hashed functions to compare. The kept function will be the one with the most matches anyway.\nIncreasing this value will reduce the number of matched functions but speed up execution time. Default value : 0.6  \n`min_func_size` -> The minimum length a function must be to get analyzed. Decreasing this value will increase matches but also false positives. Default value : 10  \n\n<a name=\"how_flags\"/>\n\n### Flags\n`help` -> Displays a help message.  \n`debug` -> Displays outputs of commands.  \n`no-prompt` -> Automatically skips user prompts.  \n\n<a name=\"how_example\"/>\n\n### Example\n#### Command\nThe following command will try to unstrip the file ./rust_example into a new ELF called ./rust_example_syms.  \n`cerberus ./rust_example -output ./rust_example_syms`\n#### Result\nHere is a comparison of the main function in the two files using Binary Ninja :  \n\n<p align=\"center\">\n  <img src=\"https://i.imgur.com/uvpC63E.png\" alt=\"before.png\"/>\n</p>\n\n<p align=\"center\">\n  <img src=\"https://i.imgur.com/Sp3ct49.png\" alt=\"after.png\"/>\n</p>\n\n<a name=\"warning\"/>\n\n## Warning\n**This software must only be used to carry out lawful experiments and I am not responsible for any breach of this rule !**  \n"
  },
  {
    "path": "docker/debian/Dockerfile-10",
    "content": "FROM debian:10\n\nWORKDIR /root\n\nRUN apt -y update\nRUN apt -y upgrade\nRUN apt -y install g++ gcc make tar wget\nRUN apt -y install libarchive-dev libcurl4-openssl-dev libssl-dev zlib1g-dev libelf-dev uuid-dev\n\nRUN wget https://github.com/Kitware/CMake/releases/download/v3.27.5/cmake-3.27.5.tar.gz\nRUN tar -xzf cmake-3.27.5.tar.gz\nWORKDIR cmake-3.27.5\nRUN ./bootstrap\nRUN make\nRUN make install\nWORKDIR ..\n\nRUN mkdir -p Cerberus\nWORKDIR Cerberus\nADD ./ ./\nRUN mkdir -p build\nWORKDIR build\n\nRUN cmake ..\nRUN make\n"
  },
  {
    "path": "docker/fedora/Dockerfile-37",
    "content": "FROM fedora:37\n\nWORKDIR /root\n\nRUN dnf -y update\nRUN dnf -y upgrade\nRUN dnf -y install g++ gcc make cmake\nRUN dnf -y install libarchive-devel libcurl-devel zlib-devel elfutils-libelf-devel libuuid-devel\n\nRUN mkdir -p Cerberus\nWORKDIR Cerberus\nADD ./ ./\nRUN mkdir -p build\nWORKDIR build\n\nRUN cmake ..\nRUN make\n"
  },
  {
    "path": "docker/ubuntu/Dockerfile-20.04",
    "content": "FROM ubuntu:20.04\n\nWORKDIR /root\n\nRUN apt -y update\nRUN apt -y upgrade\nRUN apt -y install g++ gcc make tar wget\nRUN apt -y install libarchive-dev libcurl4-openssl-dev libssl-dev zlib1g-dev libelf-dev uuid-dev\n\nRUN wget https://github.com/Kitware/CMake/releases/download/v3.27.5/cmake-3.27.5.tar.gz\nRUN tar -xzf cmake-3.27.5.tar.gz\nWORKDIR cmake-3.27.5\nRUN ./bootstrap\nRUN make\nRUN make install\nWORKDIR ..\n\nRUN mkdir -p Cerberus\nWORKDIR Cerberus\nADD ./ ./\nRUN mkdir -p build\nWORKDIR build\n\nRUN cmake ..\nRUN make\n"
  },
  {
    "path": "docker/ubuntu/Dockerfile-22.04",
    "content": "FROM ubuntu:22.04\n\nWORKDIR /root\n\nRUN apt -y update\nRUN apt -y upgrade\nRUN apt -y install g++ gcc make cmake\nRUN apt -y install libarchive-dev libcurl4-openssl-dev zlib1g-dev libelf-dev uuid-dev\n\nRUN mkdir -p Cerberus\nWORKDIR Cerberus\nADD ./ ./\nRUN mkdir -p build\nWORKDIR build\n\nRUN cmake ..\nRUN make\n"
  },
  {
    "path": "src/algorithm/algorithm.h",
    "content": "#ifndef CERBERUS_ALGORITHM_H\n#define CERBERUS_ALGORITHM_H\n\n#include <vector>\n#include <fstream>\n#include <binaries/bin_types.h>\n#include <utils/config.h>\n#include <memory>\n\nclass Algorithm {\nprotected:\n    std::ifstream* bin_file;\n    CONFIG* config;\npublic:\n    Algorithm(CONFIG* config) : config(config) {\n        bin_file = new std::ifstream(config->binary_path, std::ios::binary);\n    }\n    ~Algorithm() {\n        bin_file->close();\n    }\n    virtual void process_binary(std::vector<std::unique_ptr<FUNCTION>>* bin_funcs) = 0;\n    virtual void process_lib(std::string lib_path, std::vector<std::unique_ptr<FUNCTION>>* lib_funcs) = 0;\n    virtual void post_process(std::vector<std::unique_ptr<FUNCTION>>* bin_funcs) = 0;\n};\n\n#endif //CERBERUS_ALGORITHM_H\n"
  },
  {
    "path": "src/algorithm/part_hash_algorithm.cpp",
    "content": "#include <algorithm/part_hash_algorithm.h>\n#include <utils/convert.h>\n\nusing namespace std;\n\nvoid PartHashAlgorithm::compute_part_hash(unique_ptr<FUNCTION>& func, ifstream* file) {\n    char data[func->end - func->start + 1];\n    file->seekg(func->start);\n    file->read(data, sizeof(data));\n    float pace = sizeof(data) / (float)config->part_hash_len;\n    if(pace == 0) pace = 1;\n    func->hash = \"\";\n    for(float i = 0; i < sizeof(data); i+=pace) {\n        func->hash += data[(uint32_t)i];\n    }\n    for(uint8_t i = func->hash.size(); i < config->part_hash_len; i++) func->hash+='\\x00';\n    func->hash = func->hash.substr(0, config->part_hash_len);\n}\n\nfloat PartHashAlgorithm::compare_part_hashes(string hash1, string hash2) {\n    uint8_t score = 0;\n    for(uint8_t i = 0; i < hash1.size(); i++) {\n        if(hash1.at(i) == hash2.at(i)) score++;\n    }\n    return score / (float) config->part_hash_len;\n}\n\nvoid PartHashAlgorithm::process_binary(vector<unique_ptr<FUNCTION>>* bin_funcs) {\n    for(unique_ptr<FUNCTION>& func : *bin_funcs) {\n        func->name = \"\";\n        if(func->end - func->start + 1 >= config->min_func_size) this->compute_part_hash(func, bin_file);\n        funcs_by_sz[func->end - func->start + 1].push_back(move(func));\n    }\n}\n\nvoid PartHashAlgorithm::process_lib(string lib_path, vector<unique_ptr<FUNCTION>>* lib_funcs) {\n    ifstream lib_file(lib_path, ios::binary);\n    for (unique_ptr<FUNCTION>& lib_func : *lib_funcs) {\n        if(!lib_func->name.size()) continue;\n        size_t lib_func_sz = lib_func->end - lib_func->start + 1;\n        if(lib_func_sz < config->min_func_size) continue;\n        for(unique_ptr<FUNCTION>& func : funcs_by_sz[lib_func_sz]) {\n            this->compute_part_hash(lib_func, &lib_file);\n            float score = compare_part_hashes(lib_func->hash, func->hash);\n            if (score >= config->part_hash_trust) {\n                if(func->score < score) {\n                    func->name = lib_func->name;\n                    func->score = score;\n                }\n            }\n        }\n    }\n    lib_file.close();\n}\n\nvoid PartHashAlgorithm::post_process(vector<unique_ptr<FUNCTION>>* bin_funcs) {\n    bin_funcs->clear();\n    for(auto& f_pair : funcs_by_sz) {\n        for(unique_ptr<FUNCTION>& f : f_pair.second) bin_funcs->push_back(move(f));\n    }\n}"
  },
  {
    "path": "src/algorithm/part_hash_algorithm.h",
    "content": "#ifndef CERBERUS_PART_HASH_ALGORITHM_H\n#define CERBERUS_PART_HASH_ALGORITHM_H\n\n#include <algorithm/algorithm.h>\n#include <map>\n#include <memory>\n\nclass PartHashAlgorithm : public Algorithm {\nprivate:\n    std::map<size_t, std::vector<std::unique_ptr<FUNCTION>>> funcs_by_sz;\n    void compute_part_hash(std::unique_ptr<FUNCTION>& func, std::ifstream* file);\n    float compare_part_hashes(std::string hash1, std::string hash2);\npublic:\n    PartHashAlgorithm(CONFIG* config) : Algorithm(config) {}\n    void process_binary(std::vector<std::unique_ptr<FUNCTION>>* bin_funcs) override;\n    void process_lib(std::string lib_path, std::vector<std::unique_ptr<FUNCTION>>* lib_funcs) override;\n    void post_process(std::vector<std::unique_ptr<FUNCTION>>* bin_funcs) override;\n};\n\n#endif //CERBERUS_PART_HASH_ALGORITHM_H\n"
  },
  {
    "path": "src/binaries/bin_extractor.h",
    "content": "#ifndef CERBERUS_BIN_EXTRACTOR_H\n#define CERBERUS_BIN_EXTRACTOR_H\n\n#include <string>\n#include <vector>\n#include <binaries/bin_types.h>\n#include <memory>\n\nclass BinaryExtractor {\nprotected:\n    std::string bin_path;\n    BIN_TYPE type;\npublic:\n    BinaryExtractor(std::string bin_path, BIN_TYPE type) : bin_path(bin_path), type(type) {};\n    virtual BIN_ARCH extract_arch() = 0;\n    virtual std::vector<std::unique_ptr<FUNCTION>> extract_functions(BIN_ARCH arch, size_t image_base) = 0;\n    virtual std::vector<std::unique_ptr<SECTION>> extract_sections() = 0;\n};\n\n#endif //CERBERUS_BIN_EXTRACTOR_H\n"
  },
  {
    "path": "src/binaries/bin_handler.cpp",
    "content": "#include <binaries/bin_handler.h>\n#include <binaries/lib/install/lib_installer.h>\n#include <binaries/lib/install/rust_lib_installer.h>\n#include <binaries/lib/install/go_lib_installer.h>\n#include <langs/lib_regex.h>\n#include <utils/search.h>\n#include <utils/convert.h>\n#include <utils/logger.h>\n\nusing namespace std;\n\nBIN_ARCH BinaryHandler::extract_architecture() {\n    this->arch = lief_extractor->extract_arch();\n    return this->arch;\n}\n\nvoid BinaryHandler::extract_image_base() {\n    this->image_base = lief_extractor->extract_image_base();\n}\n\nsize_t BinaryHandler::libs_extraction() {\n    vector<string> lib_regex;\n    switch(lang) {\n        case LANG::RUST:\n            lib_regex = rust_lib_regex;\n            break;\n        case LANG::GO:\n            lib_regex = go_lib_regex;\n            break;\n        default:\n            return 0;\n    }\n    ifstream bin_file(this->bin_path, ios::binary);\n    bin_file.seekg(0, ios::end);\n    size_t bin_file_sz = bin_file.tellg();\n    bin_file.seekg(0);\n    size_t bin_file_off = 0;\n    char data[2048];\n    while(bin_file_off < bin_file_sz) {\n        bin_file.seekg(bin_file_off);\n        bin_file.read(data, sizeof(data));\n        for (string reg: lib_regex) {\n            vector<string> matches = search_regex(data, sizeof(data), reg, 256);\n            for (string match: matches) {\n                unique_ptr<LIBRARY> lib = lib_extract_callbacks[lang](match);\n                if (lib) {\n                    bool exists = false;\n                    for (unique_ptr<LIBRARY> &lib2: this->libs) {\n                        if (lib->name == lib2->name && lib->version == lib2->version) {\n                            exists = true;\n                            break;\n                        }\n                    }\n                    if (!exists) this->libs.push_back(move(lib));\n                }\n            }\n        }\n        bin_file_off += 1024;\n    }\n    sort(this->libs.begin(), this->libs.end(), [](const unique_ptr<LIBRARY>& a, const unique_ptr<LIBRARY>& b) {\n        return a->name < b->name;\n    });\n    bin_file.close();\n    return this->libs.size();\n}\n\nsize_t BinaryHandler::libs_installation() {\n    unique_ptr<LibInstaller> installer;\n    switch(lang) {\n        case RUST:\n            installer = make_unique<RustLibInstaller>(this->work_dir, this->arch, this->type);\n            break;\n        case GO:\n            installer = make_unique<GoLibInstaller>(this->work_dir, this->arch, this->type);\n            break;\n        default:\n            return 0;\n    }\n    if(!installer->pre_install_hook(this->libs)) return 0;\n    size_t success_ctr = 0;\n    for(std::unique_ptr<LIBRARY>& lib : this->libs) {\n        fcout << \"$(info)Installing $(bright_magenta:b)\" << lib->name << \"$$(red):$$(magenta:b)\" << lib->version << \"$...\" << endl;\n        if(installer->install_lib(*lib.get())) {\n            success_ctr++;\n            fcout << \"$(success)Success !\" << endl;\n        } else fcout << \"$(error)Failure...\" << endl;\n    }\n    if(!installer->post_install_hook()) return 0;\n    return success_ctr;\n}\n\nsize_t BinaryHandler::get_matches_sz() {\n    for(unique_ptr<FUNCTION>& func : this->functions) if(func->name.size()) matches_sz++;\n    return matches_sz;\n}\n\nvoid BinaryHandler::demangle_functions() {\n    for(unique_ptr<FUNCTION>& func : this->functions) {\n        if(func->name.size()) func->name = demangle_function_name(func->name);\n    }\n}"
  },
  {
    "path": "src/binaries/bin_handler.h",
    "content": "#ifndef CERBERUS_BIN_HANDLER_H\n#define CERBERUS_BIN_HANDLER_H\n\n#include <string>\n#include <vector>\n#include <binaries/bin_types.h>\n#include <binaries/extractors/lief_extractor.h>\n#include <binaries/extractors/radare_extractor.h>\n#include <langs/lang_types.h>\n#include <user/dependencies/dependency_manager.h>\n#include <algorithm/algorithm.h>\n#include <binaries/extractors/libelf_extractor.h>\n\nclass BinaryHandler {\nprotected:\n    std::string bin_path;\n    std::string work_dir;\n    LANG lang;\n    Algorithm* algorithm;\n    BIN_ARCH arch;\n    BIN_TYPE type;\n    size_t image_base;\n    bool stripped;\n    std::vector<std::unique_ptr<LIBRARY>> libs;\n    std::vector<std::unique_ptr<FUNCTION>> functions;\n    size_t matches_sz = 0;\n    LiefExtractor* lief_extractor;\n    RadareExtractor* radare_extractor;\npublic:\n    BinaryHandler(std::string bin_path, std::string work_dir, LANG lang, Algorithm* algorithm, BIN_TYPE type) : bin_path(bin_path), work_dir(work_dir), lang(lang), algorithm(algorithm), type(type) {\n        this->lief_extractor = new LiefExtractor(bin_path, type);\n        this->radare_extractor = new RadareExtractor(bin_path, type, *this->lief_extractor);\n    }\n    BIN_ARCH extract_architecture();\n    void extract_image_base();\n    virtual void strip_analysis() = 0;\n    size_t libs_extraction();\n    size_t libs_installation();\n    virtual size_t functions_analysis() = 0;\n    virtual void functions_matching(std::string lib_path) = 0;\n    virtual void post_matching() = 0;\n    size_t get_matches_sz();\n    void demangle_functions();\n    virtual bool write_output(std::string output_path) = 0;\n    bool is_stripped() {\n        return this->stripped;\n    }\n    std::vector<std::unique_ptr<LIBRARY>>& get_libs() {\n        return this->libs;\n    }\n    std::vector<std::unique_ptr<FUNCTION>>& get_functions() {\n        return this->functions;\n    }\n};\n\n#endif //CERBERUS_BIN_HANDLER_H\n"
  },
  {
    "path": "src/binaries/bin_identifier.cpp",
    "content": "#include <binaries/bin_identifier.h>\n#include <fstream>\n#include <cstring>\n\nusing namespace std;\n\nmap<BIN_TYPE, string> bin_type_names {\n    {UNKNOWN_TYPE, \"Unknown\"},\n    {ELF, \"UNIX - Executable and Linkable Format (ELF)\"},\n    {PE, \"WINDOWS - Portable Executable (PE)\"}\n};\n\nmap<string, BIN_TYPE> bin_type_from_magic = {\n    {std::string(\"\\x7f\")+\"ELF\", BIN_TYPE::ELF},\n    {\"MZ\", BIN_TYPE::PE}\n};\n\nBIN_TYPE identify_binary(string input_path) {\n    ifstream input_file(input_path, ios::binary);\n    input_file.seekg(0, ios::end);\n    size_t input_sz = input_file.tellg();\n    for (const pair<string, BIN_TYPE> p : bin_type_from_magic) {\n        input_file.seekg(0);\n        uint8_t magic_length = p.first.length();\n        if(input_sz >= magic_length) {\n            char magic_buffer[magic_length];\n            input_file.read(magic_buffer, magic_length);\n            if(!strncmp(p.first.c_str(), magic_buffer, magic_length)) {\n                input_file.close();\n                return p.second;\n            }\n        }\n    }\n    input_file.close();\n    return UNKNOWN_TYPE;\n}"
  },
  {
    "path": "src/binaries/bin_identifier.h",
    "content": "#ifndef CERBERUS_BIN_IDENTIFIER_H\n#define CERBERUS_BIN_IDENTIFIER_H\n\n#include <map>\n#include <string>\n#include <binaries/bin_types.h>\n\nextern std::map<BIN_TYPE, std::string> bin_type_names;\n\nextern std::map<std::string, BIN_TYPE> bin_type_from_magic;\n\nBIN_TYPE identify_binary(std::string input_path);\n\n#endif //CERBERUS_BIN_IDENTIFIER_H\n"
  },
  {
    "path": "src/binaries/bin_types.h",
    "content": "#ifndef CERBERUS_BIN_TYPES_H\n#define CERBERUS_BIN_TYPES_H\n\n#include <cstdint>\n#include <string>\n\nstruct FUNCTION {\n    uint64_t start;\n    uint64_t end;\n    std::string name;\n    std::string hash;\n    uint8_t score = 0;\n    size_t ordinal = 0;\n};\n\nstruct SECTION {\n    uint64_t start;\n    uint64_t end;\n    std::string name;\n};\n\nstruct LIBRARY {\n    std::string name;\n    std::string version;\n};\n\nenum BIN_TYPE {\n    UNKNOWN_TYPE,\n    ELF,\n    PE,\n};\n\nenum BIN_ARCH {\n    UNKNOWN_ARCH,\n    X86_64,\n    X86\n};\n\n#endif //CERBERUS_BIN_TYPES_H\n"
  },
  {
    "path": "src/binaries/extractors/go_extractor.cpp",
    "content": "#include <binaries/extractors/go_extractor.h>\n#include <utils/convert.h>\n#include <fstream>\n\nusing namespace std;\n\nBIN_ARCH GoExtractor::extract_arch() {\n\n}\n\nvector<unique_ptr<FUNCTION>> GoExtractor::extract_functions(BIN_ARCH arch, size_t image_base) {\n    ifstream bin_file(this->bin_path, ios::binary);\n    LIEF::PE::Section* text_sec;\n    if(type == BIN_TYPE::PE) text_sec = this->lief_extractor.extract_text_section();\n    vector<unique_ptr<FUNCTION>> funcs;\n    COMMAND_RESULT res;\n    executor.execute_command(\"go tool nm -size \"+this->bin_path, &res);\n    if(res.code) return funcs;\n    vector<string> lines = split_string(res.response, '\\n');\n    for(string line : lines) {\n        vector<string> vals = split_string(line, ' ');\n        vals = filter_empty_strings(vals);\n        if(vals.size() < 4 || !isdigit(vals.at(0).at(0))) continue;\n        unique_ptr<FUNCTION> func = make_unique<FUNCTION>();\n        switch(type) {\n            case BIN_TYPE::ELF:\n                func->start = stoull(vals.at(0), nullptr, 16) - image_base;\n                break;\n            case BIN_TYPE::PE:\n                func->start = stoull(vals.at(0), nullptr, 16) - image_base - text_sec->virtual_address() + text_sec->offset();\n                break;\n        }\n        func->end = func->start + stoull(vals.at(1)) - 1;\n        unsigned char b;\n        bin_file.seekg(func->end);\n        bin_file.read((char*)&b, 1);\n        bool found_cc = false;\n        if(b == 0xCC) found_cc = true;\n        while(b == 0xCC) {\n            bin_file.seekg(func->end--);\n            bin_file.read((char*)&b, 1);\n        }\n        if(found_cc) func->end++;\n        func->name = vals.at(3);\n        if(!func->name.find(\"$f64\") || !func->name.find(\"$f32\")) func->name = \"\";\n        funcs.push_back(move(func));\n    }\n    return funcs;\n}\n\nvector<unique_ptr<SECTION>> GoExtractor::extract_sections() {\n\n}"
  },
  {
    "path": "src/binaries/extractors/go_extractor.h",
    "content": "#ifndef CERBERUS_GO_EXTRACTOR_H\n#define CERBERUS_GO_EXTRACTOR_H\n\n#include <binaries/bin_extractor.h>\n#include <command/command_executor.h>\n#include <binaries/extractors/lief_extractor.h>\n\nclass GoExtractor : public BinaryExtractor {\nprivate:\n    LiefExtractor& lief_extractor;\n    CommandExecutor executor;\npublic:\n    GoExtractor(std::string bin_path, BIN_TYPE type, LiefExtractor& lief_extractor) : BinaryExtractor(bin_path, type), lief_extractor(lief_extractor), executor(\"./\") {}\n    BIN_ARCH extract_arch() override;\n    std::vector<std::unique_ptr<FUNCTION>> extract_functions(BIN_ARCH arch, size_t image_base) override;\n    std::vector<std::unique_ptr<SECTION>> extract_sections() override;\n};\n\n\n#endif //CERBERUS_GO_EXTRACTOR_H\n"
  },
  {
    "path": "src/binaries/extractors/libelf_extractor.cpp",
    "content": "#include <binaries/extractors/libelf_extractor.h>\n#include <fcntl.h>\n#include <unistd.h>\n\nusing namespace std;\n\nLibelfExtractor::LibelfExtractor(std::string bin_path, BIN_TYPE type) : BinaryExtractor(bin_path, type) {\n    this->fd = open(bin_path.c_str(), O_RDONLY, 0);\n    this->fp = fdopen(fd, \"r\");\n    this->bin = elf_begin(fd, ELF_C_READ, NULL);\n}\n\nLibelfExtractor::~LibelfExtractor() {\n    elf_end(this->bin);\n    close(fd);\n}\n\nBIN_ARCH LibelfExtractor::extract_arch() {\n\n}\n\nvector<unique_ptr<FUNCTION>> LibelfExtractor::extract_functions_32(size_t image_base) {\n    vector<unique_ptr<FUNCTION>> funcs;\n    Elf_Scn *scn = nullptr;\n    Elf_Scn *sym_scn = nullptr;\n    uint64_t str_scn_start = 0;\n    while ((scn = elf_nextscn(this->bin, scn)) != nullptr) {\n        Elf32_Shdr *shdr = elf32_getshdr(scn);\n        if(!sym_scn) {\n            if(shdr->sh_type == SHT_SYMTAB || shdr->sh_type == SHT_DYNSYM) {\n                sym_scn = scn;\n                if(str_scn_start) break;\n            }\n        }\n        if(!str_scn_start) {\n            if(shdr->sh_type == SHT_STRTAB) {\n                str_scn_start = shdr->sh_addr;\n                if(sym_scn) break;\n            }\n        }\n    }\n    if(sym_scn) {\n        Elf_Data *data = nullptr;\n        data = elf_getdata(sym_scn, data);\n        Elf32_Sym *symtab = (Elf32_Sym*) data->d_buf;\n        size_t sym_count = data->d_size / sizeof(Elf32_Sym);\n        for (size_t i = 0; i < sym_count; i++) {\n            if(ELF32_ST_TYPE(symtab[i].st_info) != STT_FUNC || !symtab[i].st_size) continue;\n            unique_ptr<FUNCTION> func = make_unique<FUNCTION>();\n            func->start = symtab[i].st_value - image_base;\n            func->end = func->start + symtab[i].st_size - 1;\n            if(!str_scn_start || !symtab[i].st_value) {\n                funcs.push_back(move(func));\n                continue;\n            }\n            char buf[1024];\n            fseek(this->fp, symtab[i].st_name-image_base+str_scn_start, SEEK_SET);\n            fread(buf, 1024, 1, this->fp);\n            func->name = string(buf);\n            funcs.push_back(move(func));\n        }\n    }\n    return funcs;\n}\n\nvector<unique_ptr<FUNCTION>> LibelfExtractor::extract_functions_64(size_t image_base) {\n    vector<unique_ptr<FUNCTION>> funcs;\n    Elf_Scn *scn = nullptr;\n    Elf_Scn *sym_scn = nullptr;\n    uint64_t str_scn_start = 0;\n    while ((scn = elf_nextscn(this->bin, scn)) != nullptr) {\n        Elf64_Shdr *shdr = elf64_getshdr(scn);\n        if(!sym_scn) {\n            if(shdr->sh_type == SHT_SYMTAB || shdr->sh_type == SHT_DYNSYM) {\n                sym_scn = scn;\n                if(str_scn_start) break;\n            }\n        }\n        if(!str_scn_start) {\n            if(shdr->sh_type == SHT_STRTAB) {\n                str_scn_start = shdr->sh_addr;\n                if(sym_scn) break;\n            }\n        }\n    }\n    if(sym_scn) {\n        Elf_Data *data = nullptr;\n        data = elf_getdata(sym_scn, data);\n        Elf64_Sym *symtab = (Elf64_Sym*) data->d_buf;\n        size_t sym_count = data->d_size / sizeof(Elf64_Sym);\n        for (size_t i = 0; i < sym_count; i++) {\n            if(ELF64_ST_TYPE(symtab[i].st_info) != STT_FUNC || !symtab[i].st_size) continue;\n            unique_ptr<FUNCTION> func = make_unique<FUNCTION>();\n            func->start = symtab[i].st_value - image_base;\n            func->end = func->start + symtab[i].st_size - 1;\n            if(!str_scn_start || !symtab[i].st_value) {\n                funcs.push_back(move(func));\n                continue;\n            }\n            char buf[1024];\n            fseek(this->fp, symtab[i].st_name-image_base+str_scn_start, SEEK_SET);\n            fread(buf, 1024, 1, this->fp);\n            func->name = string(buf);\n            funcs.push_back(move(func));\n        }\n    }\n    return funcs;\n}\n\nvector<unique_ptr<FUNCTION>> LibelfExtractor::extract_functions(BIN_ARCH arch, size_t image_base) {\n    if(type==BIN_TYPE::PE) image_base = 0;\n    vector<unique_ptr<FUNCTION>> funcs;\n    switch(arch) {\n        case BIN_ARCH::X86_64:\n            funcs = extract_functions_64(image_base);\n            break;\n        case BIN_ARCH::X86:\n            funcs = extract_functions_32(image_base);\n            break;\n    }\n    return funcs;\n}\n\nvector<unique_ptr<SECTION>> LibelfExtractor::extract_sections() {\n\n}\n"
  },
  {
    "path": "src/binaries/extractors/libelf_extractor.h",
    "content": "#ifndef CERBERUS_LIBELF_EXTRACTOR_H\n#define CERBERUS_LIBELF_EXTRACTOR_H\n\n#include <binaries/bin_extractor.h>\n#include <libelf.h>\n\nclass LibelfExtractor : public BinaryExtractor {\nprivate:\n    Elf* bin;\n    int32_t fd;\n    FILE* fp;\npublic:\n    LibelfExtractor(std::string bin_path, BIN_TYPE type);\n    ~LibelfExtractor();\n    BIN_ARCH extract_arch() override;\n    std::vector<std::unique_ptr<FUNCTION>> extract_functions_32(size_t image_base);\n    std::vector<std::unique_ptr<FUNCTION>> extract_functions_64(size_t image_base);\n    std::vector<std::unique_ptr<FUNCTION>> extract_functions(BIN_ARCH arch, size_t image_base) override;\n    std::vector<std::unique_ptr<SECTION>> extract_sections() override;\n};\n\n#endif //CERBERUS_LIBELF_EXTRACTOR_H\n"
  },
  {
    "path": "src/binaries/extractors/lief_extractor.cpp",
    "content": "#include <binaries/extractors/lief_extractor.h>\n#include <utils/convert.h>\n#include <binaries/pe_types.h>\n#include <fstream>\n#include <filesystem>\n\nusing namespace std;\n\nLiefExtractor::LiefExtractor(std::string bin_path, BIN_TYPE type) : BinaryExtractor(bin_path, type) {\n    this->bin = LIEF::Parser::parse(bin_path);\n    switch(type) {\n        case BIN_TYPE::ELF:\n            this->elf_bin = LIEF::ELF::Parser::parse(bin_path);\n            break;\n        case BIN_TYPE::PE:\n            this->pe_bin = LIEF::PE::Parser::parse(bin_path);\n            break;\n    }\n}\n\nBIN_ARCH LiefExtractor::extract_arch() {\n    switch(this->bin->header().architecture()) {\n        case LIEF::ARCH_X86:\n            if(this->bin->header().is_32()) return BIN_ARCH::X86;\n            return BIN_ARCH::X86_64;\n    }\n    return BIN_ARCH::UNKNOWN_ARCH;\n}\n\nsize_t LiefExtractor::extract_image_base() {\n    return this->bin->imagebase();\n}\n\nsize_t LiefExtractor::resolve_pe_rva(size_t rva) {\n    return this->pe_bin->rva_to_offset(rva);\n}\n\nLIEF::PE::Section* LiefExtractor::extract_text_section() {\n    return this->pe_bin->get_section(\".text\");\n}\n\nvector<unique_ptr<FUNCTION>> LiefExtractor::extract_functions(BIN_ARCH arch, size_t image_base) {\n    if(type == BIN_TYPE::PE) image_base = 0;\n    vector<unique_ptr<FUNCTION>> funcs;\n    vector<LIEF::Function> lief_funcs;\n    switch(type) {\n        case BIN_TYPE::ELF:\n            lief_funcs = this->elf_bin->functions();\n            break;\n        case BIN_TYPE::PE:\n            lief_funcs = this->pe_bin->functions();\n            break;\n    }\n    size_t ordinal = 0;\n    for(LIEF::Function lief_func : lief_funcs) {\n        if(!lief_func.size()) continue;\n        unique_ptr<FUNCTION> func = make_unique<FUNCTION>();\n        switch(type) {\n            case BIN_TYPE::ELF:\n                func->start = lief_func.address() - image_base;\n                break;\n            case BIN_TYPE::PE:\n                func->start = resolve_pe_rva(lief_func.address());\n                break;\n        }\n        func->end = func->start + lief_func.size() - 1;\n        func->name = lief_func.name();\n        func->ordinal = ordinal;\n        funcs.push_back(move(func));\n        ordinal++;\n    }\n    return funcs;\n}\n\nvector<unique_ptr<SECTION>> LiefExtractor::extract_sections() {\n    vector<unique_ptr<SECTION>> sections;\n    for(LIEF::Section lief_section : this->bin->sections()) {\n        unique_ptr<SECTION> section = make_unique<SECTION>();\n        section->start = lief_section.offset();\n        section->end = lief_section.offset() + lief_section.size();\n        section->name = lief_section.name();\n        sections.push_back(move(section));\n    }\n    return sections;\n}\n\nbool LiefExtractor::write_elf_output(string output_path, size_t image_base, vector<unique_ptr<FUNCTION>>& funcs, bool stripped) {\n    if(!this->elf_bin->has_section(\".symtab\")) {\n        LIEF::ELF::Section symtab_sec = LIEF::ELF::Section();\n        symtab_sec.name(\".symtab\");\n        symtab_sec.type(LIEF::ELF::ELF_SECTION_TYPES::SHT_SYMTAB);\n        symtab_sec.entry_size(0x18);\n        symtab_sec.alignment(8);\n        symtab_sec.link(this->elf_bin->header().numberof_sections()+1);\n        vector<uint8_t> content(100, 0);\n        symtab_sec.content(content);\n        *this->elf_bin->add(symtab_sec, false);\n    }\n    if(!this->elf_bin->has_section(\".strtab\")) {\n        LIEF::ELF::Section strtab_sec = LIEF::ELF::Section();\n        strtab_sec.name(\".strtab\");\n        strtab_sec.type(LIEF::ELF::ELF_SECTION_TYPES::SHT_STRTAB);\n        strtab_sec.entry_size(1);\n        strtab_sec.alignment(1);\n        vector<uint8_t> content(100, 0);\n        strtab_sec.content(content);\n        *this->elf_bin->add(strtab_sec, false);\n    }\n    vector<uint64_t> sym_addresses;\n    if(stripped) {\n        LIEF::ELF::Symbol symbol = LIEF::ELF::Symbol();\n        symbol.name(\"\");\n        symbol.type(LIEF::ELF::ELF_SYMBOL_TYPES::STT_NOTYPE);\n        symbol.value(0);\n        symbol.binding(LIEF::ELF::SYMBOL_BINDINGS::STB_LOCAL);\n        symbol.size(0);\n        symbol.shndx(0);\n        this->elf_bin->add_static_symbol(symbol);\n    } else {\n        for(auto& symbol : this->elf_bin->symbols()) {\n            uint64_t sym_addr = symbol.value();\n            if(find(sym_addresses.begin(), sym_addresses.end(), sym_addr) == sym_addresses.end()) sym_addresses.push_back(sym_addr);\n        }\n    }\n    for(unique_ptr<FUNCTION>& func : funcs) {\n        if(stripped || std::find(sym_addresses.begin(), sym_addresses.end(), func->start) == sym_addresses.end()) {\n            LIEF::ELF::Symbol symbol = LIEF::ELF::Symbol();\n            symbol.name(func->name);\n            symbol.type(LIEF::ELF::ELF_SYMBOL_TYPES::STT_FUNC);\n            symbol.value(func->start+image_base);\n            symbol.binding(LIEF::ELF::SYMBOL_BINDINGS::STB_LOCAL);\n            symbol.shndx(14);\n            this->elf_bin->add_static_symbol(symbol);\n        }\n    }\n    this->elf_bin->write(output_path);\n    return true;\n}\n\nbool LiefExtractor::write_pe_output(string output_path, size_t image_base, size_t matches_sz, vector<unique_ptr<FUNCTION>>& funcs) {\n    vector<uint8_t> edata_content;\n    EXPORT_DIRECTORY_TABLE edt;\n    for(size_t i = 0; i < sizeof(edt) + matches_sz * 8; i++) edata_content.push_back(0);\n    for(uint16_t i = 0; i < matches_sz; i++) {\n        edata_content.push_back(i & 0xff);\n        edata_content.push_back(i >> 8);\n    }\n    for(unique_ptr<FUNCTION>& func : funcs) {\n        if(!func->name.size()) continue;\n        for(char c : func->name) edata_content.push_back(c);\n        edata_content.push_back(0);\n    }\n    LIEF::PE::Section edata_section;\n    edata_section.name(\".edata\");\n    edata_section.content(edata_content);\n    edata_section.characteristics((uint32_t)(\n            LIEF::PE::SECTION_CHARACTERISTICS::IMAGE_SCN_CNT_INITIALIZED_DATA |\n            LIEF::PE::SECTION_CHARACTERISTICS::IMAGE_SCN_MEM_READ\n        ) | 0x300000);\n    edata_section = *this->pe_bin->add_section(edata_section);\n    this->pe_bin->header().numberof_sections(this->pe_bin->header().numberof_sections() + 1);\n    this->pe_bin->write(output_path);\n    ifstream bin_file(output_path, ios::binary);\n    bin_file.seekg(0, ios::end);\n    size_t bin_file_sz = bin_file.tellg();\n    ofstream new_bin_file(output_path+\".tmp\", ios::binary);\n    size_t edata_start = edata_section.virtual_address();\n    edt.name_rva = edata_start + matches_sz * 10;\n    uint32_t edata_content_sz = edata_content.size();\n    edt.address_table_entries = matches_sz;\n    edt.number_of_name_pointers = matches_sz;\n    edt.export_address_table_rva = edata_start + sizeof(edt);\n    edt.name_pointer_rva = edata_start + sizeof(edt) + 4 * matches_sz;\n    edt.ordinal_table_rva = edata_start + sizeof(edt) + 8 * matches_sz;\n    for(uint8_t i = 0; i < sizeof(edt); i++) edata_content[i] = (((char*)&edt)[i]);\n    size_t func_i = 0;\n    size_t name_pos = 0;\n    for(unique_ptr<FUNCTION>& func : funcs) {\n        if(!func->name.size()) continue;\n        uint32_t func_vaddr = this->pe_bin->offset_to_virtual_address(func->start).value();\n        memcpy((char*)(edata_content.data()+sizeof(edt)+func_i*4), &func_vaddr, sizeof(uint32_t));\n        uint32_t name_vaddr = edata_start + sizeof(edt) + matches_sz * 10 + name_pos;\n        memcpy((char*)(edata_content.data()+sizeof(edt)+matches_sz*4+func_i*4), &name_vaddr, sizeof(uint32_t));\n        func_i++;\n        name_pos += func->name.size()+1;\n    }\n    char buffer[1024];\n    uint32_t pe_start;\n    bin_file.seekg(0x3c);\n    bin_file.read((char*)&pe_start, sizeof(pe_start));\n    size_t edt_data_dir = pe_start + 0x88;\n    bin_file.seekg(0, ios::beg);\n    bin_file.read(buffer, edt_data_dir);\n    new_bin_file.write(buffer, edt_data_dir);\n    new_bin_file.write((char*)&edata_start, 4);\n    new_bin_file.write((char*)&edata_content_sz, 4);\n    bin_file.seekg((size_t)bin_file.tellg()+8);\n    size_t off = edt_data_dir+0x8;\n    while(off < edata_section.offset() - sizeof(buffer)) {\n        bin_file.read(buffer, sizeof(buffer));\n        new_bin_file.write(buffer, sizeof(buffer));\n        off+=sizeof(buffer);\n    }\n    bin_file.read(buffer, edata_section.offset() - off);\n    new_bin_file.write(buffer, edata_section.offset() - off);\n    new_bin_file.write((char*)edata_content.data(), edata_content.size());\n    bin_file.seekg((size_t)bin_file.tellg()+edata_content.size());\n    off = edata_section.offset() + edata_content.size();\n    while(off < bin_file_sz - sizeof(buffer)) {\n        bin_file.read(buffer, sizeof(buffer));\n        new_bin_file.write(buffer, sizeof(buffer));\n        off+=sizeof(buffer);\n    }\n    new_bin_file.write(buffer, bin_file_sz - off);\n    bin_file.close();\n    new_bin_file.close();\n    filesystem::rename(output_path+\".tmp\", output_path);\n    return true;\n}"
  },
  {
    "path": "src/binaries/extractors/lief_extractor.h",
    "content": "#ifndef CERBERUS_LIEF_EXTRACTOR_H\n#define CERBERUS_LIEF_EXTRACTOR_H\n\n#include <binaries/bin_extractor.h>\n#include <LIEF/LIEF.hpp>\n\nclass LiefExtractor : public BinaryExtractor {\nprivate:\n    std::unique_ptr<LIEF::Binary> bin;\n    std::unique_ptr<LIEF::ELF::Binary> elf_bin;\n    std::unique_ptr<LIEF::PE::Binary> pe_bin;\npublic:\n    LiefExtractor(std::string bin_path, BIN_TYPE type);\n    BIN_ARCH extract_arch() override;\n    size_t extract_image_base();\n    size_t resolve_pe_rva(size_t rva);\n    LIEF::PE::Section* extract_text_section();\n    std::vector<std::unique_ptr<FUNCTION>> extract_functions(BIN_ARCH arch, size_t image_base) override;\n    std::vector<std::unique_ptr<SECTION>> extract_sections() override;\n    bool write_elf_output(std::string output_path, size_t image_base, std::vector<std::unique_ptr<FUNCTION>>& funcs, bool stripped);\n    bool write_pe_output(std::string output_path, size_t image_base, size_t matches_sz, std::vector<std::unique_ptr<FUNCTION>>& funcs);\n};\n\n#endif //CERBERUS_LIEF_EXTRACTOR_H\n"
  },
  {
    "path": "src/binaries/extractors/radare_extractor.cpp",
    "content": "#include <binaries/extractors/radare_extractor.h>\n#include <utils/convert.h>\n\nusing namespace std;\n\nBIN_ARCH RadareExtractor::extract_arch() {\n\n}\n\nvector<unique_ptr<FUNCTION>> RadareExtractor::extract_functions(BIN_ARCH arch, size_t image_base) {\n    LIEF::PE::Section* text_sec;\n    if(type == BIN_TYPE::PE) text_sec = this->lief_extractor.extract_text_section();\n    vector<unique_ptr<FUNCTION>> funcs;\n    COMMAND_RESULT res;\n    executor.execute_command(string(\"radare2 -q -c aaa -c afl \\\"\") + bin_path + string(\"\\\"\"), &res);\n    if (!res.code) {\n        vector<string> lines = split_string(res.response, '\\n');\n        for(string& line : lines) {\n            if(line.length() < 2 || line.substr(0, 2) != \"0x\") continue;\n            vector<string> vals = split_string(line, ' ');\n            vals = filter_empty_strings(vals);\n            unique_ptr<FUNCTION> func = make_unique<FUNCTION>();\n            switch(type) {\n                case BIN_TYPE::ELF:\n                    func->start = stoull(vals[0].substr(2), nullptr, 16) - image_base;\n                    break;\n                case BIN_TYPE::PE:\n                    func->start = stoull(vals[0].substr(2), nullptr, 16) - image_base - text_sec->virtual_address() + text_sec->offset();\n                    break;\n            }\n            func->end = func->start + stoull(vals[2]) - 1;\n            if(!func->name.find(\"fcn.\")) func->name = vals[3];\n            funcs.push_back(move(func));\n        }\n    }\n    return funcs;\n}\n\nvector<unique_ptr<SECTION>> RadareExtractor::extract_sections() {\n\n}"
  },
  {
    "path": "src/binaries/extractors/radare_extractor.h",
    "content": "#ifndef CERBERUS_RADARE_EXTRACTOR_H\n#define CERBERUS_RADARE_EXTRACTOR_H\n\n#include <binaries/bin_extractor.h>\n#include <command/command_executor.h>\n#include <binaries/extractors/lief_extractor.h>\n\nclass RadareExtractor : public BinaryExtractor {\nprivate:\n    LiefExtractor& lief_extractor;\n    CommandExecutor executor;\npublic:\n    RadareExtractor(std::string bin_path, BIN_TYPE type, LiefExtractor& lief_extractor) : BinaryExtractor(bin_path, type), lief_extractor(lief_extractor), executor(\"./\") {}\n    BIN_ARCH extract_arch() override;\n    std::vector<std::unique_ptr<FUNCTION>> extract_functions(BIN_ARCH arch, size_t image_base) override;\n    std::vector<std::unique_ptr<SECTION>> extract_sections() override;\n};\n\n#endif //CERBERUS_RADARE_EXTRACTOR_H\n"
  },
  {
    "path": "src/binaries/handlers/elf_handler.cpp",
    "content": "#include <binaries/handlers/elf_handler.h>\n#include <vector>\n#include <binaries/extractors/libelf_extractor.h>\n\nusing namespace std;\n\nvoid ElfHandler::strip_analysis() {\n    this->stripped = true;\n    vector<unique_ptr<SECTION>> sections = this->lief_extractor->extract_sections();\n    const vector<string> debug_sections = {\".symtab\", \".strtab\"};\n    for(unique_ptr<SECTION>& section : sections) {\n        if(find(debug_sections.begin(), debug_sections.end(), section->name) != debug_sections.end()) {\n            this->stripped = false;\n            return;\n        }\n    }\n}\n\nsize_t ElfHandler::functions_analysis() {\n    vector<unique_ptr<FUNCTION>> funcs = this->lief_extractor->extract_functions(this->arch, this->image_base);\n    if(!funcs.size()) funcs = this->radare_extractor->extract_functions(this->arch, this->image_base);\n    if(!funcs.size()) funcs = this->libelf_extractor->extract_functions(this->arch, this->image_base);\n    algorithm->process_binary(&funcs);\n    this->functions = move(funcs);\n    return this->functions.size();\n}\n\nvoid ElfHandler::functions_matching(string lib_path) {\n    LiefExtractor lib_lief_extractor(lib_path, type);\n    LibelfExtractor lib_libelf_extractor(lib_path, type);\n    RadareExtractor lib_radare_extractor(lib_path, type, lib_lief_extractor);\n    size_t lib_image_base = lib_lief_extractor.extract_image_base();\n    vector<unique_ptr<FUNCTION>> funcs = lib_lief_extractor.extract_functions(this->arch, lib_image_base);\n    if(!funcs.size()) funcs = lib_libelf_extractor.extract_functions(this->arch, lib_image_base);\n    if(!funcs.size()) funcs = lib_radare_extractor.extract_functions(this->arch, lib_image_base);\n    algorithm->process_lib(lib_path, &funcs);\n}\n\nvoid ElfHandler::post_matching() {\n    algorithm->post_process(&functions);\n}\n\nbool ElfHandler::write_output(string output_path) {\n    return this->lief_extractor->write_elf_output(output_path, this->image_base, this->functions, this->stripped);\n}"
  },
  {
    "path": "src/binaries/handlers/elf_handler.h",
    "content": "#ifndef CERBERUS_ELF_HANDLER_H\n#define CERBERUS_ELF_HANDLER_H\n\n#include <binaries/bin_handler.h>\n\nclass ElfHandler : public BinaryHandler {\nprivate:\n    LibelfExtractor* libelf_extractor;\npublic:\n    ElfHandler(std::string bin_path, std::string work_dir, LANG lang, Algorithm* algorithm) : BinaryHandler(bin_path, work_dir, lang, algorithm, BIN_TYPE::ELF) {\n        this->libelf_extractor = new LibelfExtractor(bin_path, type);\n    }\n    void strip_analysis() override;\n    size_t functions_analysis() override;\n    void functions_matching(std::string lib_path) override;\n    void post_matching() override;\n    bool write_output(std::string output_path) override;\n};\n\n#endif //CERBERUS_ELF_HANDLER_H\n"
  },
  {
    "path": "src/binaries/handlers/pe_handler.cpp",
    "content": "#include <binaries/handlers/pe_handler.h>\n#include <binaries/extractors/go_extractor.h>\n\nusing namespace std;\n\nvoid PeHandler::strip_analysis() {\n    this->stripped = !this->extract_section_header_start(\".edata\", this->bin_path) &&\n            !this->extract_section_header_start(\".zdebug_info\", this->bin_path);\n}\n\nsize_t PeHandler::functions_analysis() {\n    vector<unique_ptr<FUNCTION>> funcs = this->lief_extractor->extract_functions(this->arch, this->image_base);\n    if(!funcs.size()) funcs = this->radare_extractor->extract_functions(this->arch, this->image_base);\n    algorithm->process_binary(&funcs);\n    this->functions = move(funcs);\n    return this->functions.size();\n}\n\nsize_t PeHandler::extract_section_header_start(std::string section_name, std::string path) {\n    ifstream bin_file(path);\n    uint32_t pe_start;\n    bin_file.seekg(0x3c);\n    bin_file.read((char*)&pe_start, sizeof(pe_start));\n    uint16_t number_of_sections;\n    bin_file.seekg(pe_start+0x6);\n    bin_file.read((char*)&number_of_sections, sizeof(number_of_sections));\n    uint16_t size_of_opt_header;\n    bin_file.seekg(pe_start+0x14);\n    bin_file.read((char*)&size_of_opt_header, sizeof(size_of_opt_header));\n    for(uint16_t sec_i = 0; sec_i < number_of_sections; sec_i++) {\n        size_t sec_start = pe_start + 0x18 + size_of_opt_header + sec_i * 0x28;\n        bin_file.seekg(sec_start);\n        char sec_name[8];\n        bin_file.read(sec_name, sizeof(sec_name));\n        if(!strcmp(sec_name, section_name.c_str())) {\n            bin_file.close();\n            return sec_start;\n        }\n    }\n    bin_file.close();\n    return 0;\n}\n\nvoid PeHandler::fix_functions_names(vector<unique_ptr<FUNCTION>>& funcs, std::string path, LiefExtractor& extractor) {\n    size_t edata_header_start = this->extract_section_header_start(\".edata\", path);\n    if(!edata_header_start) return;\n    ifstream bin_file(path);\n    uint32_t edata_start;\n    bin_file.seekg(edata_header_start+0xc);\n    bin_file.read((char*)&edata_start, sizeof(edata_start));\n    edata_start = extractor.resolve_pe_rva(edata_start);\n    uint32_t pointers_sz;\n    bin_file.seekg(edata_start+0x18);\n    bin_file.read((char*)&pointers_sz, sizeof(pointers_sz));\n    uint32_t eat_start;\n    bin_file.seekg(edata_start+0x1c);\n    bin_file.read((char*)&eat_start, sizeof(eat_start));\n    eat_start = extractor.resolve_pe_rva(eat_start);\n    uint32_t name_table_start;\n    bin_file.seekg(edata_start+0x20);\n    bin_file.read((char*)&name_table_start, sizeof(name_table_start));\n    name_table_start = extractor.resolve_pe_rva(name_table_start);\n    map<size_t, string> eat;\n    for(size_t entry_i = 0; entry_i < pointers_sz; entry_i++) {\n        size_t eat_entry = eat_start + entry_i * sizeof(uint32_t);\n        uint32_t func_addr;\n        bin_file.seekg(eat_entry);\n        bin_file.read((char*)&func_addr, sizeof(func_addr));\n        func_addr = extractor.resolve_pe_rva(func_addr);\n        size_t name_entry = name_table_start + entry_i * sizeof(uint32_t);\n        uint32_t name_addr;\n        bin_file.seekg(name_entry);\n        bin_file.read((char*)&name_addr, sizeof(name_addr));\n        name_addr = extractor.resolve_pe_rva(name_addr);\n        char name[1024];\n        bin_file.seekg(name_addr);\n        bin_file.read(name, sizeof(name));\n        eat[func_addr] = string(name);\n    }\n    for(unique_ptr<FUNCTION>& func : funcs) {\n        if(func->name.length()) continue;\n        if(eat.find(func->start) != eat.end()) {\n            func->name = eat[func->start];\n        }\n    }\n    bin_file.close();\n}\n\nvoid PeHandler::functions_matching(string lib_path) {\n    LiefExtractor lib_lief_extractor(lib_path, type);\n    GoExtractor lib_go_extractor(lib_path, type, lib_lief_extractor);\n    RadareExtractor lib_radare_extractor(lib_path, type, lib_lief_extractor);\n    size_t lib_image_base = lib_lief_extractor.extract_image_base();\n    vector<unique_ptr<FUNCTION>> funcs = lib_lief_extractor.extract_functions(this->arch, lib_image_base);\n    if(!funcs.size()) funcs = lib_go_extractor.extract_functions(this->arch, lib_image_base);\n    if(!funcs.size()) funcs = lib_radare_extractor.extract_functions(this->arch, lib_image_base);\n    this->fix_functions_names(funcs, lib_path, lib_lief_extractor);\n    algorithm->process_lib(lib_path, &funcs);\n}\n\nvoid PeHandler::post_matching() {\n    algorithm->post_process(&functions);\n}\n\nbool PeHandler::write_output(string output_path) {\n    return this->lief_extractor->write_pe_output(output_path, this->image_base, this->matches_sz, this->functions);\n}"
  },
  {
    "path": "src/binaries/handlers/pe_handler.h",
    "content": "#ifndef CERBERUS_PE_HANDLER_H\n#define CERBERUS_PE_HANDLER_H\n\n#include <binaries/bin_handler.h>\n\nclass PeHandler : public BinaryHandler {\nprivate:\n    size_t extract_section_header_start(std::string section_name, std::string path);\n    void fix_functions_names(std::vector<std::unique_ptr<FUNCTION>>& funcs, std::string path, LiefExtractor& extractor);\npublic:\n    PeHandler(std::string bin_path, std::string work_dir, LANG lang, Algorithm* algorithm) : BinaryHandler(bin_path, work_dir, lang, algorithm, BIN_TYPE::PE) {}\n    void strip_analysis() override;\n    size_t functions_analysis() override;\n    void functions_matching(std::string lib_path) override;\n    void post_matching() override;\n    bool write_output(std::string output_path) override;\n};\n\n#endif //CERBERUS_PE_HANDLER_H\n"
  },
  {
    "path": "src/binaries/lib/install/go_lib_installer.cpp",
    "content": "#include <binaries/lib/install/go_lib_installer.h>\n#include <command/command_executor.h>\n#include <utils/logger.h>\n#include <algorithm>\n#include <fstream>\n\nusing namespace std;\n\nbool GoLibInstaller::pre_install_hook(vector<std::unique_ptr<LIBRARY>>& libs) {\n    filesystem::create_directory(this->work_dir+\"/standalone\");\n    auto go_lib_it = find_if(libs.begin(), libs.end(), [](const unique_ptr<LIBRARY>& item) {\n        return item->name == \"go\";\n    });\n    if(go_lib_it == libs.end()) return false;\n    LIBRARY* go_lib = go_lib_it->get();\n    libs.erase(go_lib_it);\n    if(!this->install_go_version(go_lib->version)) return false;\n    CommandExecutor executor(this->work_dir+\"/standalone\");\n    COMMAND_RESULT res;\n    executor.execute_command(\"../go mod init build_mod\", &res);\n    if(res.code || res.response.find(\"creating new go.mod\") == string::npos) return false;\n    ofstream build_mod_file(this->work_dir+\"/standalone/build_mod.go\");\n    build_mod_file << \"package main\" << endl << endl;\n    build_mod_file << \"import (\" << endl;\n    for(std::unique_ptr<LIBRARY>& lib : libs) {\n        string lib_name = lib->name;\n        if(!lib_name.find(\"std::\")) lib_name = lib_name.substr(string(\"std::\").length());\n        build_mod_file << \"\\t_ \\\"\" << lib_name << \"\\\"\" << endl;\n    }\n    build_mod_file << \")\" << endl << endl;\n    build_mod_file << \"func main() {}\";\n    build_mod_file.close();\n    return true;\n}\n\nbool GoLibInstaller::install_go_version(std::string version) {\n    fcout << \"$(info)Installing $(bright_magenta:b)go$$(red):$$(magenta:b)\" << version << \"$...\" << endl;\n    CommandExecutor executor(work_dir);\n    COMMAND_RESULT res;\n    executor.execute_command(\"goliath \"+version, &res);\n    bool success = !res.code;\n    this->goliath_output_dir = this->work_dir+\"/go-\"+version;\n    if(success) {\n        filesystem::rename(goliath_output_dir+\"/go/bin/go\", this->work_dir+\"/go\");\n        fcout << \"$(success)Success !\" << endl;\n    } else fcout << \"$(error)Failure...\" << endl;\n    return success;\n}\n\nbool GoLibInstaller::install_lib(LIBRARY lib) {\n    CommandExecutor executor(this->work_dir+\"/standalone\");\n    COMMAND_RESULT res;\n    string lib_name = lib.name;\n    if(!lib_name.find(\"std::\")) lib_name = lib_name.substr(string(\"std::\").length());\n    if(lib.version != \"unk\") lib_name += \"@v\"+lib.version;\n    executor.execute_command(\"../go get \"+lib_name, &res);\n    return !res.code;\n}\n\nbool GoLibInstaller::post_install_hook() {\n    fcout << \"$(info)Building standalone library...\" << endl;\n    string goos, goarch;\n    string lib_extension;\n    switch(type) {\n        case BIN_TYPE::ELF:\n            goos = \"linux\";\n            lib_extension = \".so\";\n            break;\n        case BIN_TYPE::PE:\n            goos = \"windows\";\n            lib_extension = \".dll\";\n            break;\n    }\n    switch(arch) {\n        case BIN_ARCH::X86_64:\n            goarch = \"amd64\";\n            break;\n        case BIN_ARCH::X86:\n            goarch = \"386\";\n            break;\n    }\n    CommandExecutor executor(this->work_dir+\"/standalone\");\n    COMMAND_RESULT res;\n    executor.execute_command(\"GOOS=\"+goos+\" GOARCH=\"+goarch+\" ../go build -o ./standalone\"+lib_extension+\" build_mod.go\", &res);\n    bool success = !res.code;\n    if(success) {\n        filesystem::rename(this->work_dir+\"/standalone/standalone\"+lib_extension, this->work_dir+\"/standalone\"+lib_extension);\n        fcout << \"$(success)Success !\" << endl;\n    }\n    else fcout << \"$(error)Failure...\" << endl;\n    filesystem::remove(this->work_dir+\"/go\");\n    filesystem::remove_all(this->goliath_output_dir);\n    filesystem::remove_all(this->work_dir+\"/standalone\");\n    return success;\n}"
  },
  {
    "path": "src/binaries/lib/install/go_lib_installer.h",
    "content": "#ifndef CERBERUS_GO_LIB_INSTALLER_H\n#define CERBERUS_GO_LIB_INSTALLER_H\n\n#include <binaries/lib/install/lib_installer.h>\n#include <memory>\n\nclass GoLibInstaller : public LibInstaller {\nprivate:\n    bool install_go_version(std::string version);\n    std::string goliath_output_dir;\npublic:\n    GoLibInstaller(std::string work_dir, BIN_ARCH arch, BIN_TYPE type) : LibInstaller(work_dir, arch, type) {}\n    bool install_lib(LIBRARY lib) override;\n    bool pre_install_hook(std::vector<std::unique_ptr<LIBRARY>>& libs) override;\n    bool post_install_hook() override;\n};\n\n#endif //CERBERUS_GO_LIB_INSTALLER_H\n"
  },
  {
    "path": "src/binaries/lib/install/lib_installer.h",
    "content": "#ifndef CERBERUS_LIB_INSTALLER_H\n#define CERBERUS_LIB_INSTALLER_H\n\n#include <string>\n#include <binaries/bin_types.h>\n#include <memory>\n#include <vector>\n\nclass LibInstaller {\nprotected:\n    std::string work_dir;\n    BIN_ARCH arch;\n    BIN_TYPE type;\npublic:\n    LibInstaller(std::string work_dir, BIN_ARCH arch, BIN_TYPE type) : work_dir(work_dir), arch(arch), type(type) {}\n    virtual bool install_lib(LIBRARY lib) = 0;\n    virtual bool pre_install_hook(std::vector<std::unique_ptr<LIBRARY>>& libs) = 0;\n    virtual bool post_install_hook() = 0;};\n\n#endif //CERBERUS_LIB_INSTALLER_H\n"
  },
  {
    "path": "src/binaries/lib/install/rust_lib_installer.cpp",
    "content": "#include <binaries/lib/install/rust_lib_installer.h>\n#include <utils/file_operations.h>\n#include <utils/convert.h>\n#include <filesystem>\n#include <fstream>\n#include <vector>\n#include <string>\n#include <map>\n#include <command/command_executor.h>\n#include <utils/logger.h>\n#include <user/user_prompt.h>\n\nnamespace fs = std::filesystem;\nusing namespace std;\n\nconst string RBF_PREFIX = \"$(bright_blue:b)[$(blue:b)RBF$]$ \";\n\nvoid newer_edition_patch(string crate_path) {\n    if(fs::exists(crate_path+\"/Cargo.toml\")) {\n        ifstream cargo_input_file(crate_path + \"/Cargo.toml\");\n        string line;\n        vector<string> lines;\n        while(getline(cargo_input_file, line)) {\n            line = strip(line);\n            if(line.find(\"[package]\") != string::npos) {\n                lines.push_back(\"[package]\");\n                lines.push_back(\"edition = \\\"2021\\\"\");\n            } else if(line.find(\"edition \") == string::npos && line.find(\"edition=\") == string::npos) {\n                lines.push_back(line);\n            }\n        }\n        cargo_input_file.close();\n        ofstream cargo_output_file(crate_path+\"/Cargo.toml\");\n        for(string l : lines) {\n            cargo_output_file.write((l + string(\"\\n\")).c_str(), l.size() + 1);\n        }\n        cargo_output_file.close();\n    }\n}\n\nPATCH NEWER_EDITION_PATCH{\n    \"EDITION 2021\",\n    newer_edition_patch\n};\n\nvoid std_redefinition_patch(string crate_path) {\n    if(fs::exists(crate_path+\"/src/lib.rs\")) {\n        ifstream lib_input_file(crate_path+\"/src/lib.rs\");\n        string line;\n        vector<string> lines;\n        while(getline(lib_input_file, line)) {\n            if(line.find(\"no_std\") == string::npos && line.find(\"as std;\") == string::npos) lines.push_back(line);\n        }\n        lib_input_file.close();\n        ofstream lib_output_file(crate_path+\"/src/lib.rs\");\n        for(string l : lines) {\n            lib_output_file.write((l + string(\"\\n\")).c_str(), l.size() + 1);\n        }\n        lib_output_file.close();\n    }\n}\n\nPATCH STD_REDEFINITION_PATCH {\n    \"STD REDEFINITION\",\n    std_redefinition_patch\n};\n\nvoid add_workspace_patch(string crate_path) {\n    if(fs::exists(crate_path+\"/Cargo.toml\")) {\n        ifstream cargo_input_file(crate_path+\"/Cargo.toml\");\n        vector<string> lines;\n        string line;\n        while(getline(cargo_input_file, line)) {\n            lines.push_back(line);\n        }\n        lines.push_back(\"[workspace]\");\n        cargo_input_file.close();\n        ofstream cargo_output_file(crate_path+\"/Cargo.toml\");\n        for(string l : lines) {\n            cargo_output_file.write((l + string(\"\\n\")).c_str(), l.size() + 1);\n        }\n        cargo_output_file.close();\n    }\n}\n\nPATCH ADD_WORKSPACE_PATCH {\n    \"ADD WORKSPACE\",\n    add_workspace_patch\n};\n\nmap<string, PATCH> patches = {\n    {\"maybe a missing crate `core`?\", NEWER_EDITION_PATCH},\n    {\"the name `std` is defined multiple times\", STD_REDEFINITION_PATCH},\n    {\"language item required, but not found: `eh_personality`\", STD_REDEFINITION_PATCH},\n    {\"current package believes it's in a workspace when it's not\", ADD_WORKSPACE_PATCH}\n};\n\nbool RustBuildFixer::process_error(string command, string error) {\n    for(pair<string, PATCH> patch_pair : patches) {\n        if(error.find(patch_pair.first) != string::npos) {\n            PATCH patch = patch_pair.second;\n            if(last_patch && patch.patch_func == last_patch->patch_func) return false;\n            last_patch = &patch;\n            fcout << \"$(info)\" << RBF_PREFIX << \"Applying patch $(red:b)\" << patch.name << \"$...\" << endl;\n            patch.patch_func(crate_path);\n            COMMAND_RESULT res;\n            executor.execute_command(command, &res);\n            if(!res.code) return true;\n            return process_error(command, res.response);\n        }\n    }\n    return false;\n}\n\nvoid RustLibInstaller::check_and_install_arch(string arch_name) {\n    CommandExecutor executor(\"./\");\n    COMMAND_RESULT res;\n    executor.execute_command(\"rustup target list\", &res);\n    if(res.code) return;\n    vector<string> lines = split_string(res.response, '\\n');\n    for(string& line : lines) {\n        if(line.find(arch_name) != string::npos) {\n            if(line.find(\"installed\") == string::npos) {\n                fcout << \"$(info)You need to install the toolchain for $(info:b)\" << arch_name << \"$ architecture.\" << endl;\n                bool agree_install = ask_yes_no(\"Proceed to installation ?\", true);\n                if(agree_install) {\n                    COMMAND_RESULT res;\n                    string install_cmd = \"rustup target install \" + arch_name;\n                    executor.execute_command(install_cmd, &res);\n                    if(res.code) fcout << \"$(error)An error occurred during installation... Run $(error:b)\" << install_cmd << \"$ for more information.\" << endl;\n                    else fcout << \"$(success)Done !\" << endl;\n                    return;\n                } else return;\n            } else {\n                fcout << \"$(info)Requested architecture is already installed.\" << endl;\n                return;\n            }\n        }\n    }\n}\n\nRustLibInstaller::RustLibInstaller(string work_dir, BIN_ARCH arch, BIN_TYPE type) : LibInstaller(work_dir, arch, type), downloader() {\n    if(arch == BIN_ARCH::X86) check_and_install_arch(\"i686-unknown-linux-gnu\");\n}\n\nbool RustLibInstaller::install_lib(LIBRARY lib) {\n    string output_dir_name = this->work_dir+\"/\"+lib.name+\"-\"+lib.version;\n    string zip_file_name = output_dir_name+\".crate\";\n    string tar_file_name = output_dir_name+\".tar\";\n    if(!this->downloader.download_file(\"https://crates.io/api/v1/crates/\"+lib.name+\"/\"+lib.version+\"/download\", zip_file_name)) return false;\n    if(!decompress_gzip_file(zip_file_name, tar_file_name)) return false;\n    fs::remove(zip_file_name);\n    if(!decompress_tar_file(tar_file_name, output_dir_name)) return false;\n    fs::remove(tar_file_name);\n    ifstream cargo_toml_input(output_dir_name+\"/Cargo.toml\");\n    if(!cargo_toml_input.is_open()) return false;\n    vector<string> cargo_toml_lines;\n    string line;\n    bool found_lib = false;\n    while(getline(cargo_toml_input, line)) {\n        strip(line);\n        if(line.find(\"[lib]\") != string::npos) {\n            found_lib = true;\n            cargo_toml_lines.push_back(\"[lib]\");\n            cargo_toml_lines.push_back(\"crate-type = [\\\"dylib\\\"]\");\n        } else if(line.find(\"crate-type \") && line.find(\"crate-type=\")) cargo_toml_lines.push_back(line);\n    }\n    cargo_toml_input.close();\n    if(!found_lib) {\n        cargo_toml_lines.push_back(\"[lib]\");\n        cargo_toml_lines.push_back(\"crate-type = [\\\"dylib\\\"]\");\n    }\n    if(!fs::exists(output_dir_name+\"/Cargo.toml\")) return false;\n    ofstream cargo_toml_output(output_dir_name+\"/Cargo.toml\");\n    for(string line : cargo_toml_lines) cargo_toml_output.write((line+string(\"\\n\")).c_str(), line.size()+1);\n    cargo_toml_output.close();\n    CommandExecutor executor(output_dir_name);\n    COMMAND_RESULT res;\n    string command;\n    switch(type) {\n        case BIN_TYPE::ELF:\n            command = \"cargo build --release\";\n            if(arch == BIN_ARCH::X86) command += \" --target=i686-unknown-linux-gnu\";\n            break;\n        case BIN_TYPE::PE:\n            if(arch == BIN_ARCH::X86_64) command = \"cross build --target x86_64-pc-windows-gnu --release\";\n            else if(arch == BIN_ARCH::X86) command = \"cross build --target i686-pc-windows-gnu --release\";\n            break;\n    }\n    executor.execute_command(command, &res);\n    bool success = res.code == 0;\n    if(!success) {\n        fcout << \"$(warning)An error occurred during build, delegating to $(warning:b)RBF$ (Rust Build Fixer)...\" << endl;\n        success = RustBuildFixer(output_dir_name, type).process_error(command, res.response);\n    }\n    if(!success) return false;\n    string release_dir;\n    switch(type) {\n        case BIN_TYPE::ELF:\n            release_dir = output_dir_name+string(\"/target/release\");\n            break;\n        case BIN_TYPE::PE:\n            if(arch == BIN_ARCH::X86_64) release_dir = output_dir_name+string(\"/target/x86_64-pc-windows-gnu/release\");\n            else if(arch == BIN_ARCH::X86) release_dir = output_dir_name+string(\"/target/i686-pc-windows-gnu/release\");\n            break;\n    }\n    string lib_extension;\n    switch(type) {\n        case BIN_TYPE::ELF:\n            lib_extension = \".so\";\n            break;\n        case BIN_TYPE::PE:\n            lib_extension = \".dll\";\n            break;\n    }\n    if(fs::exists(release_dir)) {\n        for (const auto& entry : fs::directory_iterator(release_dir)) {\n            if (fs::is_regular_file(entry)) {\n                fs::path file_path = entry.path();\n                string file_name = file_path.filename();\n                if(ends_with(file_name, lib_extension)) {\n                    fs::rename(file_path, this->work_dir+\"/\"+file_name);\n                }\n            }\n        }\n    }\n    fs::remove_all(output_dir_name);\n    return true;\n}"
  },
  {
    "path": "src/binaries/lib/install/rust_lib_installer.h",
    "content": "#ifndef CERBERUS_RUST_LIB_INSTALLER_H\n#define CERBERUS_RUST_LIB_INSTALLER_H\n\n#include <binaries/lib/install/lib_installer.h>\n#include <utils/file_downloader.h>\n#include <command/command_executor.h>\n\nstruct PATCH {\n    std::string name;\n    void (*patch_func)(std::string crate_path);\n};\n\nclass RustBuildFixer {\nprivate:\n    std::string crate_path;\n    BIN_TYPE type;\n    PATCH* last_patch = nullptr;\n    CommandExecutor executor;\npublic:\n    RustBuildFixer(std::string crate_path, BIN_TYPE type) : crate_path(crate_path), type(type), executor(crate_path) {}\n    bool process_error(std::string command, std::string error);\n};\n\nclass RustLibInstaller : public LibInstaller {\nprivate:\n    FileDownloader downloader;\n    void check_and_install_arch(std::string arch_name);\npublic:\n    RustLibInstaller(std::string work_dir, BIN_ARCH arch, BIN_TYPE type);\n    bool install_lib(LIBRARY lib) override;\n    bool pre_install_hook(std::vector<std::unique_ptr<LIBRARY>>& libs) override {return true;}\n    bool post_install_hook() override {return true;}\n};\n\n#endif //CERBERUS_RUST_LIB_INSTALLER_H\n"
  },
  {
    "path": "src/binaries/lib/lib_manager.cpp",
    "content": "#include <binaries/lib/lib_manager.h>\n#include <utils/logger.h>\n#include <user/user_prompt.h>\n#include <iostream>\n\nusing namespace std;\n\nvoid LibManager::main_menu() {\n    fcout << \"$(info)Here is the current list of libraries :\\n\";\n    fcout << \"$(red:b)-------------------------------$\\n\";\n    uint8_t lib_i = 1;\n    for(std::unique_ptr<LIBRARY>& lib : this->libs) {\n        fcout << to_string(lib_i) << \". $(bright_magenta:b)\" << lib->name << \"$$(red):$$(magenta:b)\" << lib->version << \"$\\n\";\n        lib_i++;\n    }\n    fcout << \"$(red:b)-------------------------------$\\n\";\n    fcout << \"$(info)$(info:b)1$. Validate $(info:b)2$. Add library $(info:b)3$. Change library version $(info:b)4$. Remove library\" << endl;\n}\n\nvoid LibManager::add_lib_menu() {\n    std::unique_ptr<LIBRARY> lib = make_unique<LIBRARY>();\n    lib->name = \"\";\n    lib->version = \"\";\n    fcout << \"$(info)Name:$ \";\n    while(!lib->name.size()) getline(cin, lib->name);\n    fcout << \"$(info)Version:$ \";\n    while(!lib->version.size()) getline(cin, lib->version);\n    this->libs.push_back(move(lib));\n}\n\nvoid LibManager::change_version_menu() {\n    if(!this->libs.size()) {\n        fcout << \"$(warning)No libraries remaining.\" << endl;\n        return;\n    }\n    size_t index = ask_n(\"Index:\", 1, this->libs.size());\n    std::unique_ptr<LIBRARY>& lib = this->libs.at(index - 1);\n    lib->version = \"\";\n    fcout << \"$(info)Version:$ \";\n    while(!lib->version.size()) getline(cin, lib->version);\n}\n\nvoid LibManager::remove_lib_menu() {\n    if(!this->libs.size()) {\n        fcout << \"$(warning)No libraries remaining.\" << endl;\n        return;\n    }\n    size_t index = ask_n(\"Index:\", 1, this->libs.size());\n    this->libs.erase(this->libs.begin() + index - 1);\n}\n\nvoid LibManager::manage() {\n    USER_CHOICE choice = USER_CHOICE::NO_CHOICE;\n    do {\n        main_menu();\n        choice = (USER_CHOICE) ask_n(\"Your choice ?\", 1, 4);\n        switch(choice) {\n            case ADD_LIB:\n                this->add_lib_menu();\n                break;\n            case CHANGE_VERSION:\n                this->change_version_menu();\n                break;\n            case REMOVE_LIB:\n                this->remove_lib_menu();\n                break;\n        }\n    } while(choice != USER_CHOICE::VALIDATE);\n}"
  },
  {
    "path": "src/binaries/lib/lib_manager.h",
    "content": "#ifndef CERBERUS_LIB_MANAGER_H\n#define CERBERUS_LIB_MANAGER_H\n\n#include <vector>\n#include <binaries/bin_types.h>\n#include <string>\n#include <memory>\n\nenum USER_CHOICE {\n    NO_CHOICE = 0,\n    VALIDATE = 1,\n    ADD_LIB = 2,\n    CHANGE_VERSION = 3,\n    REMOVE_LIB = 4\n};\n\nclass LibManager {\nprivate:\n    std::vector<std::unique_ptr<LIBRARY>>& libs;\n    void main_menu();\n    void add_lib_menu();\n    void change_version_menu();\n    void remove_lib_menu();\npublic:\n    LibManager(std::vector<std::unique_ptr<LIBRARY>>& libs) : libs(libs) {}\n    void manage();\n};\n\n#endif //CERBERUS_LIB_MANAGER_H\n"
  },
  {
    "path": "src/binaries/pe_types.h",
    "content": "#ifndef CERBERUS_PE_TYPES_H\n#define CERBERUS_PE_TYPES_H\n\n#include <cstdint>\n\nstruct EXPORT_DIRECTORY_TABLE {\n    uint32_t export_flags = 0;\n    uint32_t time_date_stamp = 0;\n    uint16_t major_version = 0;\n    uint16_t minor_version = 0;\n    uint32_t name_rva;\n    uint32_t ordinal_base = 1;\n    uint32_t address_table_entries;\n    uint32_t number_of_name_pointers;\n    uint32_t export_address_table_rva;\n    uint32_t name_pointer_rva;\n    uint32_t ordinal_table_rva;\n};\n\n#endif //CERBERUS_PE_TYPES_H\n"
  },
  {
    "path": "src/command/command_executor.cpp",
    "content": "#include <command/command_executor.h>\n#include <unistd.h>\n#include <cstring>\n#include <utils/logger.h>\n\nusing namespace std;\n\nbool COMMANDS_DEBUG_MODE = false;\n\nbool CommandExecutor::test_password(std::string password) {\n    COMMAND_RESULT res;\n    this->execute_command(\"echo \"+password+\" | sudo -Sk echo test\", &res);\n    return !res.code;\n}\n\nvoid CommandExecutor::execute_command(string command, COMMAND_RESULT* result) {\n    char current_dir[1024];\n    getcwd(current_dir, sizeof(current_dir));\n    if (chdir(this->env_dir.c_str()) != 0) {\n        result->code = -1;\n        result->response = \"\";\n        return;\n    }\n    FILE* pipe = popen((command+string(\" 2>&1\")).c_str(), \"r\");\n    if (!pipe) {\n        result->code = -1;\n        result->response = \"\";\n        chdir(current_dir);\n        return;\n    }\n    stringstream ss;\n    char buffer[128];\n    if(COMMANDS_DEBUG_MODE) fcout << \"$(debug)------ COMMAND OUTPUT ------\" << endl;\n    while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {\n        if(COMMANDS_DEBUG_MODE) fcout << \"$(debug)\" << buffer << \"$\";\n        ss << string(buffer, strlen(buffer));\n    }\n    if(COMMANDS_DEBUG_MODE) fcout << \"$(debug)----------------------------\" << endl;\n    result->code = pclose(pipe);\n    result->response = ss.str();\n    chdir(current_dir);\n}"
  },
  {
    "path": "src/command/command_executor.h",
    "content": "#ifndef CERBERUS_COMMAND_EXECUTOR_H\n#define CERBERUS_COMMAND_EXECUTOR_H\n\n#include <cstdint>\n#include <string>\n#include <filesystem>\n\nextern bool COMMANDS_DEBUG_MODE;\n\nstruct COMMAND_RESULT {\n    int32_t code;\n    std::string response;\n};\n\nclass CommandExecutor {\nprivate:\n    std::string env_dir;\npublic:\n    CommandExecutor(std::string env_dir) : env_dir(std::filesystem::absolute(env_dir)) {};\n    bool test_password(std::string password);\n    void execute_command(std::string command, COMMAND_RESULT* result);\n};\n\n#endif //CERBERUS_COMMAND_EXECUTOR_H\n"
  },
  {
    "path": "src/global_defs.h",
    "content": "#ifndef CERBERUS_GLOBAL_DEFS_H\n#define CERBERUS_GLOBAL_DEFS_H\n\n#include <string>\n\nconst std::string TOOL_ART = \"   ___         _       \\n\"\n                    \"  / __|___ _ _| |__  ___ _ _ _  _ ___\\n\"\n                    \" | (__/ -_) '_| '_ \\\\/ -_) '_| || (_-<\\n\"\n                    \"  \\\\___\\\\___|_| |_.__/\\\\___|_|  \\\\_,_/__/\\n\";\nconst std::string TOOL_NAME = \"Cerberus\";\nconst std::string TOOL_VERSION = \"2.0\";\nconst std::string TOOL_AUTHOR = \"h311d1n3r\";\nconst std::string install_dir = \"~/.local/bin\";\n\n#endif //CERBERUS_GLOBAL_DEFS_H"
  },
  {
    "path": "src/langs/lang_manager.cpp",
    "content": "#include <langs/lang_manager.h>\n#include <utils/logger.h>\n#include <fstream>\n#include <cstring>\n#include <algorithm>\n\nusing namespace std;\n\nmap<LANG, string> name_from_lang = {\n    {LANG::UNKNOWN_LANG, \"Unknown\"},\n    {LANG::RUST, \"Rust\"},\n    {LANG::GO, \"Go\"}\n};\n\nvector<pair<string, LANG>> LANG_PATTERNS = {\n    std::pair(\"/rustc-\", LANG::RUST),\n    std::pair(\"/.cargo/\", LANG::RUST),\n    std::pair(\"\\\\rustc-\", LANG::RUST),\n    std::pair(\"\\\\.cargo\\\\\", LANG::RUST),\n    std::pair(\"/go-\", LANG::GO),\n    std::pair(\"\\\\go-\", LANG::GO),\n    std::pair(\"runtime.go\", LANG::GO)\n};\n\nLangIdentifier::LangIdentifier(string input_path) {\n    this->input_path = input_path;\n}\n\nvalue_ordered_map<LANG, size_t> LangIdentifier::identify() {\n    value_ordered_map<LANG, size_t> matches;\n    for(uint32_t i = 1; i < name_from_lang.size(); i++) matches[(LANG)i] = 0;\n    ifstream input_file(this->input_path);\n    if (!input_file.is_open()) {\n        fcout << \"$(critical)File $(critical:u)\" << input_path << \"$ can't be opened !\" << endl;\n        exit(1);\n    }\n    input_file.seekg(0, ios::end);\n    size_t input_sz = input_file.tellg();\n    input_file.seekg(0);\n    char input_data[2048];\n    size_t input_off = 0;\n    while(input_off < input_sz) {\n        input_file.seekg(input_off);\n        input_file.read(input_data, sizeof(input_data));\n        for(pair<string, LANG> lang_pattern : LANG_PATTERNS) {\n            char* current_pos = input_data;\n            char* occurrence;\n            while(current_pos-input_data < sizeof(input_data)) {\n                if ((occurrence = strstr(current_pos, lang_pattern.first.c_str())) != nullptr) {\n                    current_pos = occurrence + lang_pattern.first.length();\n                    matches[lang_pattern.second]++;\n                } else current_pos++;\n            }\n        }\n        input_off += 1024;\n    }\n    input_file.close();\n    matches.invert_sort();\n    return matches;\n}"
  },
  {
    "path": "src/langs/lang_manager.h",
    "content": "#ifndef CERBERUS_LANG_MANAGER_H\n#define CERBERUS_LANG_MANAGER_H\n\n#include <string>\n#include <map>\n#include <vector>\n#include <types/value_ordered_map.h>\n#include <langs/lang_types.h>\n\nextern std::map<LANG, std::string> name_from_lang;\n\nextern std::vector<std::pair<std::string, LANG>> LANG_PATTERNS;\n\nclass LangIdentifier {\nprivate:\n    std::string input_path;\npublic:\n    LangIdentifier(std::string input_path);\n    value_ordered_map<LANG, size_t> identify();\n};\n\n#endif //CERBERUS_LANG_MANAGER_H\n"
  },
  {
    "path": "src/langs/lang_types.h",
    "content": "#ifndef CERBERUS_LANG_TYPES_H\n#define CERBERUS_LANG_TYPES_H\n\nenum LANG {\n    UNKNOWN_LANG,\n    RUST,\n    GO\n};\n\n#endif //CERBERUS_LANG_TYPES_H\n"
  },
  {
    "path": "src/langs/lib_regex.cpp",
    "content": "#include <langs/lib_regex.h>\n#include <utils/convert.h>\n\nusing namespace std;\n\nstd::vector<std::string> rust_lib_regex = {\n    \"/.cargo/(.+?)\\\\.rs\",\n    \"/cargo/(.+?)\\\\.rs\",\n    \"index\\\\.crates\\\\.io-(.+?)\\\\\\\\(.+?)\\\\\\\\\",\n    \"\\\\\\\\.cargo\\\\\\\\(.+?)\\\\.rs\",\n    \"\\\\\\\\cargo\\\\\\\\(.+?)\\\\.rs\",\n    \"index\\\\.crates\\\\.io-(.+?)/(.+?)/\",\n};\n\nstd::vector<std::string> go_lib_regex = {\n    \"go(.*?)/pkg/mod/(.+?)\\\\.(s|go)\",\n    \"go(.*?)/src/(.+?)\\\\.(s|go)\",\n    \"go(.*?)\\\\\\\\pkg\\\\\\\\mod/(.+?)\\\\.(s|go)\",\n    \"go(.*?)\\\\\\\\src\\\\\\\\(.+?)\\\\.(s|go)\",\n    \"go\\\\d+\\\\.\\\\d+\\\\.\\\\d+\\\\x00\",\n    \"go\\\\d+\\\\.\\\\d+\\\\x00\"\n};\n\nunique_ptr<LIBRARY> rust_extract_callback(string match) {\n    size_t null_term_index;\n    if((null_term_index = match.find('\\x00')) != string::npos) {\n        match = match.substr(0, null_term_index);\n    }\n    string lib_and_version = \"\";\n    if(match.find(\"index.crates.io-\") != string::npos) {\n        size_t delim_index = match.find(\"/\");\n        if(delim_index == string::npos) delim_index = match.find(\"\\\\\");\n        if(delim_index == string::npos) return nullptr;\n        vector<string> match_parts = split_string(match, match.at(delim_index));\n        if(match_parts.size() < 2) return nullptr;\n        lib_and_version = match_parts.at(1);\n    } else {\n        vector<string> match_parts = split_string(match, match.at(0));\n        if(match_parts.size() < 6) return nullptr;\n        lib_and_version = match_parts.at(5);\n    }\n    size_t delim_index;\n    if((delim_index = lib_and_version.rfind('-')) == string::npos) return nullptr;\n    unique_ptr<LIBRARY> lib = make_unique<LIBRARY>();\n    lib->name = lib_and_version.substr(0, delim_index);\n    lib->version = lib_and_version.substr(delim_index+1);\n    return lib;\n}\n\nunique_ptr<LIBRARY> go_extract_callback(string match) {\n    size_t null_term_index;\n    if((null_term_index = match.find('\\x00')) != string::npos) {\n        match = match.substr(0, null_term_index);\n    }\n    uint8_t mode = 0;\n    if(match.find(\"/\") != string::npos) mode = 1;\n    else if(match.find(\"\\\\\") != string::npos) mode = 2;\n    if(mode == 0 && match.length() > 2 && isdigit(match.at(2))) {\n        unique_ptr<LIBRARY> lib = make_unique<LIBRARY>();\n        lib->name = \"go\";\n        lib->version = match.substr(2);\n        return lib;\n    }\n    size_t pkg_index, src_index;\n    if((pkg_index = (mode == 1 ? match.find(\"/pkg/mod/\") : match.find(\"\\\\pkg\\\\mod\\\\\"))) != string::npos) {\n        size_t version_index = match.find(\"@\");\n        if(match.find(\"golang.org\") == string::npos && version_index != string::npos) {\n            unique_ptr<LIBRARY> lib = make_unique<LIBRARY>();\n            size_t name_start_index = pkg_index+string(\"/pkg/mod/\").length();\n            lib->name = match.substr(name_start_index, version_index-name_start_index);\n            lib->version = match.substr(version_index+2);\n            lib->version = lib->version.substr(0, lib->version.find('/'));\n            return lib;\n        }\n    } else if((src_index = (mode == 1 ? match.find(\"/src/\") : match.find(\"\\\\src\\\\\"))) != string::npos) {\n        const string forbidden_list[] = {\"internal\",\"runtime\",\"github.com\",\"golang.org\"};\n        for(string forbidden : forbidden_list) if(match.find(forbidden) != string::npos) return nullptr;\n        match = match.substr(src_index+string(\"/src/\").length());\n        match = match.substr(0, match.find_last_of(mode == 1 ? '/' : '\\\\'));\n        unique_ptr<LIBRARY> lib = make_unique<LIBRARY>();\n        lib->name = \"std::\" + match;\n        lib->version = \"unk\";\n        return lib;\n    }\n    return nullptr;\n}\n\nmap<LANG, LibExtractCallback> lib_extract_callbacks = {\n    {LANG::RUST, rust_extract_callback},\n    {LANG::GO, go_extract_callback}\n};\n"
  },
  {
    "path": "src/langs/lib_regex.h",
    "content": "#ifndef CERBERUS_LIB_REGEX_H\n#define CERBERUS_LIB_REGEX_H\n\n#include <vector>\n#include <string>\n#include <map>\n#include <langs/lang_types.h>\n#include <binaries/bin_types.h>\n#include <functional>\n#include <memory>\n\nextern std::vector<std::string> rust_lib_regex;\n\nextern std::vector<std::string> go_lib_regex;\n\nusing LibExtractCallback = std::function<std::unique_ptr<LIBRARY>(std::string)>;\n\nstd::unique_ptr<LIBRARY> rust_extract_callback(std::string match);\nstd::unique_ptr<LIBRARY> go_extract_callback(std::string match);\n\nextern std::map<LANG, LibExtractCallback> lib_extract_callbacks;\n\n#endif //CERBERUS_LIB_REGEX_H\n"
  },
  {
    "path": "src/main.cpp",
    "content": "#include <utils/arg_parser.h>\n#include <utils/config.h>\n#include <langs/lang_manager.h>\n#include <binaries/bin_identifier.h>\n#include <utils/logger.h>\n#include <global_defs.h>\n#include <user/user_prompt.h>\n#include <binaries/handlers/elf_handler.h>\n#include <binaries/handlers/pe_handler.h>\n#include <binaries/lib/lib_manager.h>\n#include <user/local_config.h>\n#include <filesystem>\n#include <uuid/uuid.h>\n#include <curl/curl.h>\n#include <algorithm/part_hash_algorithm.h>\n#include <utils/convert.h>\n\nnamespace fs = std::filesystem;\nusing namespace std;\n\nstd::string work_dir;\nCONFIG* config;\nLANG selected_lang = LANG::UNKNOWN_LANG;\nBIN_TYPE type;\nLOCAL_CONFIG* usr_config;\n\nvector<PACKAGE*> packages = {\n    new OS_PACKAGE{\"git\", \"git\"},\n    new OS_PACKAGE{\"cargo\", \"cargo\"},\n    new OS_PACKAGE{\"golang\", \"go\"},\n    new OS_PACKAGE{\"binutils\", \"c++filt\"},\n    new OS_PACKAGE{\"python3\", \"python3\"},\n    new OS_PACKAGE{\"python3-pip\", \"pip3\"},\n    new OS_PACKAGE{\"patch\", \"patch\"},\n    new PIP3_PACKAGE{\"pyinstaller\", \"pyinstaller\"},\n    new GIT_PACKAGE{\"radare2\", \"radare2\", \"https://github.com/radareorg/radare2\", 0, \"cd .. ; mv radare2 ../ ; ../radare2/sys/install.sh\", false},\n    new GIT_PACKAGE{\"Goliath\", \"goliath\", \"https://github.com/h311d1n3r/Goliath\", 0, \"cd .. ; mv Goliath ../ ; cd ../Goliath ; ./build.sh; mv ./dist/goliath \"+install_dir, false, false},\n    new CARGO_PACKAGE{\"cross\", \"cross\"},\n    new CUSTOM_PACKAGE{\"rust\", \"rustup\", \"curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rust_install.sh ; sh +x rust_install.sh -y ; rm rust_install.sh\"}\n};\n\nvoid global_init() {\n    elf_version(EV_CURRENT);\n    curl_global_init(CURL_GLOBAL_ALL);\n    string home_dir = string(getenv(\"HOME\"));\n    replace_all((string&)install_dir, \"~\", home_dir);\n    if(!fs::exists(install_dir)) fs::create_directories(install_dir);\n    const char* current_path = getenv(\"PATH\");\n    string new_path = string(current_path)+string(\":\")+install_dir;\n    const char* go_path = getenv(\"GOPATH\");\n    if(go_path && strlen(go_path)) new_path+=string(\":\")+string(go_path)+string(\"/bin\");\n    else new_path+=\":\"+home_dir+\"/go/bin\";\n    setenv(\"PATH\", new_path.c_str(), 1);\n}\n\nvoid global_exit() {\n    curl_global_cleanup();\n}\n\nbool install_dependencies(BinaryHandler* handler) {\n    DependencyManager dep_manager(usr_config, work_dir);\n    std::vector<OS_PACKAGE*> os_packages;\n    std::vector<GIT_PACKAGE*> git_packages;\n    std::vector<PIP3_PACKAGE*> pip3_packages;\n    std::vector<CARGO_PACKAGE*> cargo_packages;\n    std::vector<GO_PACKAGE*> go_packages;\n    std::vector<CUSTOM_PACKAGE*> custom_packages;\n    for(PACKAGE* package : packages) {\n        if(!dep_manager.is_package_installed(package)) {\n            switch(package->package_type) {\n                case 0:\n                    os_packages.push_back((OS_PACKAGE*) package);\n                    break;\n                case 1:\n                    pip3_packages.push_back((PIP3_PACKAGE*) package);\n                    break;\n                case 2:\n                    git_packages.push_back((GIT_PACKAGE*) package);\n                    break;\n                case 3:\n                    cargo_packages.push_back((CARGO_PACKAGE*) package);\n                    break;\n                case 4:\n                    go_packages.push_back((GO_PACKAGE*) package);\n                    break;\n                case 5:\n                    custom_packages.push_back((CUSTOM_PACKAGE*) package);\n                    break;\n            }\n        }\n    }\n    if(!os_packages.size() && !git_packages.size() && !pip3_packages.size() && !cargo_packages.size() && !go_packages.size() && !custom_packages.size()) fcout << \"$(info)No additional package is required.\" << endl;\n    else {\n        fcout << \"$(info)The following packages are required :\" << endl;\n        if(os_packages.size()) {\n            fcout << \"$(bright_magenta)With $(bright_magenta:b)\" << name_from_package_manager[usr_config->package_manager] << \"$:\" << endl;\n            for (OS_PACKAGE *package : os_packages) {\n                fcout << \"$(magenta)- $(magenta:b)\" + package->name << endl;\n            }\n        }\n        if(pip3_packages.size()) {\n            fcout << \"$(bright_blue)With $(bright_blue:b)pip3$:\" << endl;\n            for (PIP3_PACKAGE *package : pip3_packages) {\n                fcout << \"$(blue)- $(blue:b)\" + package->package_name << endl;\n            }\n        }\n        if(git_packages.size()) {\n            fcout << \"$(bright_red)With $(bright_red:b)git$:\" << endl;\n            for (GIT_PACKAGE *package : git_packages) {\n                fcout << \"$(red)- $(red:b)\" + package->repo_name << endl;\n            }\n        }\n        if(cargo_packages.size()) {\n            fcout << \"$(bright_yellow)With $(bright_yellow:b)cargo$:\" << endl;\n            for (CARGO_PACKAGE *package : cargo_packages) {\n                fcout << \"$(yellow)- $(yellow:b)\" + package->package_name << endl;\n            }\n        }\n        if(go_packages.size()) {\n            fcout << \"$(bright_white)With $(bright_white:b)go$:\" << endl;\n            for (GO_PACKAGE *package : go_packages) {\n                fcout << \"$(white)- $(white:b)\" + package->package_name << endl;\n            }\n        }\n        if(custom_packages.size()) {\n            fcout << \"$(bright_green)$(bright_green:b)Custom$:\" << endl;\n            for (CUSTOM_PACKAGE *package : custom_packages) {\n                fcout << \"$(green)- $(green:b)\" + package->package_name << endl;\n            }\n        }\n        if(usr_config->is_root || usr_config->has_sudo) {\n            if(usr_config->package_manager != PACKAGE_MANAGER::UNKNOWN || !os_packages.size()) {\n                bool agrees_install = ask_yes_no(\"Proceed to installation ?\", true);\n                if(agrees_install) {\n                    if(!usr_config->is_root) {\n                        CommandExecutor executor(\"./\");\n                        string password;\n                        while(true) {\n                            password = ask_password(\"$(info)Input your password for sudo\");\n                            if(executor.test_password(password)) break;\n                            fcout << \"$(warning)Wrong password ! Try again...\" << endl;\n                        }\n                        dep_manager.set_password(password);\n                    }\n                    for(OS_PACKAGE* package : os_packages) {\n                        if(dep_manager.install_package(package)) fcout << \"$(success)Done.\" << endl;\n                        else {\n                            fcout << \"$(error)An error occurred during installation...\" << endl;\n                            return false;\n                        }\n                    }\n                    for(PIP3_PACKAGE* package : pip3_packages) {\n                        if(dep_manager.install_package(package)) fcout << \"$(success)Done.\" << endl;\n                        else {\n                            fcout << \"$(error)An error occurred during installation...\" << endl;\n                            return false;\n                        }\n                    }\n                    for(GIT_PACKAGE* package : git_packages) {\n                        if(dep_manager.install_package(package)) fcout << \"$(success)Done.\" << endl;\n                        else {\n                            fcout << \"$(error)An error occurred during installation...\" << endl;\n                            return false;\n                        }\n                    }\n                    for(CARGO_PACKAGE* package : cargo_packages) {\n                        if(dep_manager.install_package(package)) fcout << \"$(success)Done.\" << endl;\n                        else {\n                            fcout << \"$(error)An error occurred during installation...\" << endl;\n                            return false;\n                        }\n                    }\n                    for(GO_PACKAGE* package : go_packages) {\n                        if(dep_manager.install_package(package)) fcout << \"$(success)Done.\" << endl;\n                        else {\n                            fcout << \"$(error)An error occurred during installation...\" << endl;\n                            return false;\n                        }\n                    }\n                    for(CUSTOM_PACKAGE* package : custom_packages) {\n                        if(dep_manager.install_package(package)) fcout << \"$(success)Done.\" << endl;\n                        else {\n                            fcout << \"$(error)An error occurred during installation...\" << endl;\n                            return false;\n                        }\n                    }\n                } else {\n                    fcout << \"$(error)Ending execution.\" << endl;\n                    return false;\n                }\n            } else {\n                fcout << \"$(error)Without a known package manager, please install these packages and try again.\" << endl;\n                return false;\n            }\n        } else {\n            fcout << \"$(error)Without being root or having sudo, please manually install these packages and try again.\" << endl;\n            return false;\n        }\n    }\n    return true;\n}\n\nvoid start_analysis() {\n    BinaryHandler* handler;\n    Algorithm* algorithm = new PartHashAlgorithm(config);\n    switch(type) {\n        case PE:\n            handler = new PeHandler(config->binary_path, work_dir, selected_lang, algorithm);\n            break;\n        case ELF:\n            handler = new ElfHandler(config->binary_path, work_dir, selected_lang, algorithm);\n            break;\n        default:\n            return;\n    }\n    if(handler->extract_architecture() == BIN_ARCH::UNKNOWN_ARCH) {\n        fcout << \"$(error)Unsupported architecture !\" << endl;\n        return;\n    }\n    handler->extract_image_base();\n    if(!install_dependencies(handler)) return;\n    handler->strip_analysis();\n    if(handler->is_stripped()) fcout << \"$(info)File was found to be $(info:b)stripped$.\" << endl;\n    else {\n        fcout << \"$(warning)File was not found to be $(warning:b)stripped$...\" << endl;\n        if(!ask_yes_no(\"Resume analysis ?\", false)) return;\n    }\n    fcout << \"$(info)Extracting libraries...\" << endl;\n    size_t libs_amount = handler->libs_extraction();\n    if(!libs_amount) {\n        fcout << \"$(error)No libraries were found.\" << endl;\n        return;\n    }\n    fcout << \"$(success)Identified $(success:b)\" << to_string(libs_amount) << \"$ libraries.\" << endl;\n    std::vector<std::unique_ptr<LIBRARY>>& libs = handler->get_libs();\n    LibManager lib_manager(libs);\n    lib_manager.manage();\n    if(!libs.size()) {\n        fcout << \"$(error)No libraries remaining.\" << endl;\n        return;\n    }\n    fcout << \"$(info)Installing libraries...\" << endl;\n    size_t libs_installed = handler->libs_installation();\n    if(!libs_installed) {\n        fcout << \"$(error)No libraries were successfully installed...\" << endl;\n        return;\n    }\n    fcout << \"$(success)Installed $(success:b)\" << to_string(libs_installed) << \"$ libraries.\" << endl;\n    fcout << \"$(info)Analyzing target functions...\" << endl;\n    size_t funcs_sz = handler->functions_analysis();\n    if(!funcs_sz) {\n        fcout << \"$(error)No functions were successfully analyzed...\" << endl;\n        return;\n    }\n    fcout << \"$(success)Analyzed $(success:b)\" << to_string(funcs_sz) << \"$ functions.\" << endl;\n    fcout << \"$(info)Matching with functions from libraries...\" << endl;\n    string lib_extension;\n    switch(type) {\n        case BIN_TYPE::ELF:\n            lib_extension = \".so\";\n            break;\n        case BIN_TYPE::PE:\n            lib_extension = \".dll\";\n            break;\n    }\n    for(const auto& entry : fs::directory_iterator(work_dir)) {\n        if (fs::is_regular_file(entry)) {\n            fs::path file_path = entry.path();\n            if(ends_with(file_path.filename(), lib_extension)) {\n                handler->functions_matching(file_path.string());\n            }\n        }\n    }\n    handler->post_matching();\n    size_t matches_sz = handler->get_matches_sz();\n    if(!matches_sz) {\n        fcout << \"$(error)No functions were successfully matched...\" << endl;\n        return;\n    }\n    fcout << \"$(success)Matched $(success:b)\" << to_string(matches_sz) << \"$ functions. Matching rate: $(success:b)\" << to_string((uint8_t)(matches_sz/(float)funcs_sz*100)) << \"%\" << endl;\n    fcout << \"$(info)Demangling function names...\" << endl;\n    handler->demangle_functions();\n    fcout << \"$(success)Done !\" << endl;\n    fcout << \"$(info)Writing output file...\" << endl;\n    if(handler->write_output(config->output_path)) fcout << \"$(success)Done !\" << endl;\n    else fcout << \"$(error)An error occurred when writing to output file...\" << endl;\n}\n\nstd::string generate_work_dir() {\n    uuid_t uuid;\n    uuid_generate_random(uuid);\n    char uuid_str[37];\n    uuid_unparse_lower(uuid, uuid_str);\n    work_dir = \".cerberus-\"+string(uuid_str, 37).substr(0, 16);\n    fs::create_directory(work_dir);\n    return work_dir;\n}\n\nint main(int argc, char *argv[]) {\n    global_init();\n    ArgParser parser;\n    config = parser.compute_args(argc, argv);\n    COMMANDS_DEBUG_MODE = config->debug;\n    NO_PROMPT = config->no_prompt;\n    if(config) {\n        fcout << \"$(bright_red:b)---------- $(red:b)\" << TOOL_NAME << \" (v\" << TOOL_VERSION << \")$ ----------\" << endl;\n        usr_config = identify_local_config();\n        if(usr_config->package_manager != PACKAGE_MANAGER::UNKNOWN) fcout << \"$(info)Identified package manager: $(info:b)\" << name_from_package_manager[usr_config->package_manager] << endl;\n        else fcout << \"$(warning)Could not identify package manager...\" << endl;\n        if(usr_config->is_root) fcout << \"$(info)Running as root.\" << endl;\n        else if(usr_config->has_sudo) fcout << \"$(info)Not running as root but sudo detected.\" << endl;\n        else fcout << \"$(warning)Not running as root and sudo couldn't be detected.\" << endl;\n        type = identify_binary(config->binary_path);\n        if(type == BIN_TYPE::UNKNOWN_TYPE) {\n            fcout << \"$(critical)The input file $(critical:u)\" << config->binary_path << \"$ couldn't be recognized...\" << endl;\n            return 1;\n        }\n        fcout << \"$(info)Identified file as $(info:b)\" << bin_type_names[type] << \"$.\" << endl;\n        LangIdentifier identifier(config->binary_path);\n        value_ordered_map langs = identifier.identify();\n        if(langs.at(0).second) {\n            fcout << \"$(info)Identified language : $(magenta:b)\" << name_from_lang[langs.at(0).first] << endl;\n            bool agree_lang = ask_yes_no(\"Continue analysis with this language ?\", true);\n            if(agree_lang) selected_lang = langs.at(0).first;\n        } else fcout << \"$(warning)Couldn't identify language automatically...\" << endl;\n        if(selected_lang == LANG::UNKNOWN_LANG) {\n            fcout << \"$(info)Currently supported languages :\\n\";\n            for(size_t lang_i = 0; lang_i < langs.size(); lang_i++) {\n                fcout << \"$(magenta)\" << to_string(lang_i+1) << \". $(magenta:b)\" << name_from_lang[langs.at(lang_i).first] << endl;\n            }\n            uint8_t selected_lang_i = ask_n(\"$(info)Your choice ?\", 1, langs.size());\n            selected_lang = langs.at(selected_lang_i-1).first;\n        }\n        work_dir = generate_work_dir();\n        fcout << \"$(info)Using $(magenta:b)\" << name_from_lang[selected_lang] << \"$ for analysis.\" << endl;\n        start_analysis();\n        fs::remove_all(work_dir);\n    }\n    global_exit();\n    return 0;\n}\n"
  },
  {
    "path": "src/types/value_ordered_map.h",
    "content": "#ifndef CERBERUS_VALUE_ORDERED_MAP_H\n#define CERBERUS_VALUE_ORDERED_MAP_H\n\n#include <vector>\n#include <string>\n#include <algorithm>\n\ntemplate <typename KeyType, typename ValueType>\nclass value_ordered_map {\nprivate:\n    std::vector<std::pair<KeyType, ValueType>> map;\npublic:\n    ValueType& operator[](const KeyType key) {\n        for(std::pair<KeyType, ValueType>& pair : this->map) {\n            if(pair.first == key) return pair.second;\n        }\n        std::pair<KeyType, ValueType> new_pair = std::pair<KeyType, ValueType>(key, 0);\n        this->map.push_back(new_pair);\n        return this->map.at(this->map.size()-1).second;\n    }\n    void sort() {\n        std::sort(map.begin(), map.end(), [](const auto& a, const auto& b) {\n            return a.second > b.second;\n        });\n    }\n    void invert_sort() {\n        std::sort(map.begin(), map.end(), [](const auto& a, const auto& b) {\n            return a.second > b.second;\n        });\n    }\n    std::pair<KeyType, ValueType> at(int64_t i) {\n        return this->map[i];\n    }\n    size_t size() {\n        return this->map.size();\n    }\n};\n\n#endif //CERBERUS_VALUE_ORDERED_MAP_H\n"
  },
  {
    "path": "src/user/dependencies/dependency_manager.cpp",
    "content": "#include <user/dependencies/dependency_manager.h>\n#include <utils/logger.h>\n\nusing namespace std;\n\nmap<PACKAGE_MANAGER, string> install_commands = {\n    {PACKAGE_MANAGER::APT, \"apt -y install %s\"},\n    {PACKAGE_MANAGER::APT_GET, \"apt-get -y install %s\"},\n    {PACKAGE_MANAGER::DNF, \"dnf -y install %s\"},\n    {PACKAGE_MANAGER::YUM, \"yum -y install %s\"},\n    {PACKAGE_MANAGER::ZYPPER, \"zypper -y install %s\"},\n    {PACKAGE_MANAGER::PACMAN, \"pacman -y -S %s\"},\n    {PACKAGE_MANAGER::PORTAGE, \"emerge -y %s\"},\n    {PACKAGE_MANAGER::SLACKPKG, \"slackpkg -y install %s\"},\n    {PACKAGE_MANAGER::SWARET, \"swaret -y --install %s\"},\n    {PACKAGE_MANAGER::XBPS, \"xbps-install -y -S %s\"},\n    {PACKAGE_MANAGER::APK, \"apk -y add %s\"},\n    {PACKAGE_MANAGER::NIX, \"nix-env -y -iA nixpkgs.%s\"},\n    {PACKAGE_MANAGER::PETGET, \"petget -y %s.pet\"}\n};\n\nvoid DependencyManager::set_password(string password) {\n    this->password = password;\n}\n\nbool DependencyManager::is_package_installed(PACKAGE* package) {\n    return is_binary_on_path(package->binary);\n}\n\nbool DependencyManager::install_package(OS_PACKAGE* package) {\n    fcout << \"$(info)Installing $(info:b)\" << package->name << \"$...\" << endl;\n    string command = install_commands[config->package_manager];\n    char formatted_cmd_buf[command.length() - 2 + package->name.length() + 1];\n    snprintf(formatted_cmd_buf, sizeof(formatted_cmd_buf), command.c_str(), package->name.c_str());\n    string formatted_cmd(formatted_cmd_buf);\n    if(config->is_root) {\n        COMMAND_RESULT res;\n        executor.execute_command(formatted_cmd, &res);\n        return res.code == 0;\n    }\n    else if(config->has_sudo) {\n        COMMAND_RESULT res;\n        executor.execute_command(string(\"echo \")+password+string(\" | sudo -S \")+formatted_cmd, &res);\n        return res.code == 0;\n    }\n    return false;\n}\n\nbool DependencyManager::install_package(PIP3_PACKAGE *package) {\n    fcout << \"$(info)Installing $(info:b)\" << package->package_name << \"$...\" << endl;\n    COMMAND_RESULT res;\n    executor.execute_command(string(\"pip3 install \")+package->package_name, &res);\n    return !res.code;\n}\n\nbool DependencyManager::install_package(GIT_PACKAGE* package) {\n    fcout << \"$(info)Installing $(info:b)\" << package->repo_name << \"$...\" << endl;\n    COMMAND_RESULT res;\n    executor.execute_command(string(\"git clone \")+package->url, &res);\n    if(res.code != 0) return false;\n    string build_cmd = \"mkdir build; cd build; cmake ..; make; make install\";\n    if(package->custom_command.size()) build_cmd = package->custom_command;\n    if(!config->is_root && package->needs_root) build_cmd = string(\"echo \")+password+string(\" | sudo -S sh -c \\\"unset SUDO_USER ; \") + build_cmd + string(\"\\\"\");\n    executor.execute_command(string(\"cd \")+package->repo_name+string(\"; \")+build_cmd, &res);\n    if(package->remove_dir) filesystem::remove_all(work_dir+string(\"/\")+package->repo_name);\n    return res.code == package->success_code;\n}\n\nbool DependencyManager::install_package(CARGO_PACKAGE *package) {\n    fcout << \"$(info)Installing $(info:b)\" << package->package_name << \"$...\" << endl;\n    COMMAND_RESULT res;\n    executor.execute_command(string(\"cargo install \")+package->package_name, &res);\n    return !res.code;\n}\n\nbool DependencyManager::install_package(GO_PACKAGE *package) {\n    fcout << \"$(info)Installing $(info:b)\" << package->package_name << \"$...\" << endl;\n    COMMAND_RESULT res;\n    executor.execute_command(string(\"go install \")+package->url+string(\"@latest\"), &res);\n    return !res.code;\n}\n\nbool DependencyManager::install_package(CUSTOM_PACKAGE *package) {\n    fcout << \"$(info)Installing $(info:b)\" << package->package_name << \"$...\" << endl;\n    COMMAND_RESULT res;\n    executor.execute_command(string(\"sh -c \\\"\") + package->command + string(\"\\\"\"), &res);\n    return res.code == package->success_code;\n}"
  },
  {
    "path": "src/user/dependencies/dependency_manager.h",
    "content": "#ifndef CERBERUS_DEPENDENCY_MANAGER_H\n#define CERBERUS_DEPENDENCY_MANAGER_H\n\n#include <string>\n#include <map>\n#include <user/local_config.h>\n#include <command/command_executor.h>\n\nstruct PACKAGE {\n    uint8_t package_type;\n    std::string binary;\n};\n\nstruct OS_PACKAGE : PACKAGE {\n    std::string name;\n    OS_PACKAGE(std::string name, std::string binary) {\n        PACKAGE::package_type = 0;\n        PACKAGE::binary = binary;\n        OS_PACKAGE::name = name;\n    }\n};\n\nstruct PIP3_PACKAGE : PACKAGE {\n    std::string package_name;\n    PIP3_PACKAGE(std::string binary, std::string package_name) {\n        PACKAGE::package_type = 1;\n        PACKAGE::binary = binary;\n        PIP3_PACKAGE::package_name = package_name;\n    }\n};\n\nstruct GIT_PACKAGE : PACKAGE {\n    std::string repo_name;\n    std::string url;\n    std::string custom_command = \"\";\n    std::int32_t success_code = 0;\n    bool remove_dir = true;\n    bool needs_root = true;\n    GIT_PACKAGE(std::string repo_name, std::string binary, std::string url) {\n        package_type = 2;\n        PACKAGE::binary = binary;\n        GIT_PACKAGE::repo_name = repo_name;\n        GIT_PACKAGE::url = url;\n    }\n    GIT_PACKAGE(std::string repo_name, std::string binary, std::string url, int32_t success_code) : GIT_PACKAGE(repo_name, binary, url) {\n        GIT_PACKAGE::success_code = success_code;\n    }\n    GIT_PACKAGE(std::string repo_name, std::string binary, std::string url, int32_t success_code, std::string custom_command) : GIT_PACKAGE(repo_name, binary, url, success_code) {\n        GIT_PACKAGE::custom_command = custom_command;\n    }\n    GIT_PACKAGE(std::string repo_name, std::string binary, std::string url, int32_t success_code, std::string custom_command, bool remove_dir) : GIT_PACKAGE(repo_name, binary, url, success_code, custom_command) {\n        GIT_PACKAGE::remove_dir = remove_dir;\n    }\n    GIT_PACKAGE(std::string repo_name, std::string binary, std::string url, int32_t success_code, std::string custom_command, bool remove_dir, bool needs_root) : GIT_PACKAGE(repo_name, binary, url, success_code, custom_command, remove_dir) {\n        GIT_PACKAGE::needs_root = needs_root;\n    }\n};\n\nstruct CARGO_PACKAGE : PACKAGE {\n    std::string package_name;\n    CARGO_PACKAGE(std::string binary, std::string package_name) {\n        PACKAGE::package_type = 3;\n        PACKAGE::binary = binary;\n        CARGO_PACKAGE::package_name = package_name;\n    }\n};\n\nstruct GO_PACKAGE : PACKAGE {\n    std::string package_name;\n    std::string url;\n    GO_PACKAGE(std::string binary, std::string package_name, std::string url) {\n        PACKAGE::package_type = 4;\n        PACKAGE::binary = binary;\n        GO_PACKAGE::package_name = package_name;\n        GO_PACKAGE::url = url;\n    }\n};\n\nstruct CUSTOM_PACKAGE : PACKAGE {\n    std::string package_name;\n    std::string command;\n    int32_t success_code = 0;\n    CUSTOM_PACKAGE(std::string package_name, std::string binary, std::string command) {\n        PACKAGE::package_type = 5;\n        PACKAGE::binary = binary;\n        CUSTOM_PACKAGE::package_name = package_name;\n        CUSTOM_PACKAGE::command = command;\n    }\n    CUSTOM_PACKAGE(std::string package_name, std::string binary, std::string command, int32_t success_code) : CUSTOM_PACKAGE(package_name, binary, command) {\n        CUSTOM_PACKAGE::success_code = success_code;\n    }\n};\n\nextern std::map<PACKAGE_MANAGER, std::string> install_commands;\n\nclass DependencyManager {\nprivate:\n    LOCAL_CONFIG* config;\n    std::string work_dir;\n    CommandExecutor executor;\n    std::string password;\npublic:\n    DependencyManager(LOCAL_CONFIG* config, std::string work_dir) : config(config), work_dir(work_dir), executor(work_dir) {};\n    void set_password(std::string password);\n    bool is_package_installed(PACKAGE* package);\n    bool install_package(OS_PACKAGE* package);\n    bool install_package(GIT_PACKAGE* package);\n    bool install_package(PIP3_PACKAGE* package);\n    bool install_package(CARGO_PACKAGE* package);\n    bool install_package(GO_PACKAGE* package);\n    bool install_package(CUSTOM_PACKAGE* package);\n};\n\n#endif //CERBERUS_DEPENDENCY_MANAGER_H\n"
  },
  {
    "path": "src/user/local_config.cpp",
    "content": "#include <user/local_config.h>\n#include <unistd.h>\n#include <utils/convert.h>\n\nusing namespace std;\n\nmap<PACKAGE_MANAGER, string> name_from_package_manager = {\n    {UNKNOWN, \"your package manager\"},\n    {APT, \"apt\"},\n    {APT_GET, \"apt-get\"},\n    {DNF, \"dnf\"},\n    {YUM, \"yum\"},\n    {ZYPPER, \"zypper\"},\n    {PACMAN, \"pacman\"},\n    {PORTAGE, \"emerge\"},\n    {SLACKPKG, \"slackpkg\"},\n    {SWARET, \"swaret\"},\n    {XBPS, \"xbps-install\"},\n    {APK, \"apk\"},\n    {NIX, \"nix-env\"},\n    {PETGET, \"petget\"}\n};\n\nPACKAGE_MANAGER find_package_manager() {\n    for(pair<PACKAGE_MANAGER, string> manager : name_from_package_manager) {\n        if(is_binary_on_path(manager.second)) return manager.first;\n    }\n    return PACKAGE_MANAGER::UNKNOWN;\n}\n\nbool is_root() {\n    return geteuid() == 0;\n}\n\nbool has_sudo() {\n    return is_binary_on_path(\"sudo\");\n}\n\nLOCAL_CONFIG* identify_local_config() {\n    LOCAL_CONFIG* config = new LOCAL_CONFIG();\n    config->package_manager = find_package_manager();\n    config->is_root = is_root();\n    config->has_sudo = has_sudo();\n    return config;\n}\n\nbool is_binary_on_path(string binary) {\n    const char* path_env = std::getenv(\"PATH\");\n    if (path_env == nullptr) return false;\n    string path_env_str(path_env);\n    vector<string> paths = split_string(path_env_str,':');\n    for(string path : paths) {\n        string full_path = path + '/' + binary;\n        if (access(full_path.c_str(), X_OK) == 0) return true;\n    }\n    return false;\n}"
  },
  {
    "path": "src/user/local_config.h",
    "content": "#ifndef CERBERUS_LOCAL_CONFIG_H\n#define CERBERUS_LOCAL_CONFIG_H\n\n#include <string>\n#include <map>\n\nenum PACKAGE_MANAGER {\n    UNKNOWN,\n    APT,\n    APT_GET,\n    DNF,\n    YUM,\n    ZYPPER,\n    PACMAN,\n    PORTAGE,\n    SLACKPKG,\n    SWARET,\n    XBPS,\n    APK,\n    NIX,\n    PETGET\n};\n\nstruct LOCAL_CONFIG {\n    PACKAGE_MANAGER package_manager;\n    bool is_root;\n    bool has_sudo;\n};\n\nextern std::map<PACKAGE_MANAGER, std::string> name_from_package_manager;\n\nLOCAL_CONFIG* identify_local_config();\n\nbool is_binary_on_path(std::string binary);\n\n#endif //CERBERUS_LOCAL_CONFIG_H\n"
  },
  {
    "path": "src/user/user_prompt.cpp",
    "content": "#include <user/user_prompt.h>\n#include <utils/logger.h>\n#include <utils/convert.h>\n#include <iostream>\n#include <termios.h>\n#include <unistd.h>\n\nusing namespace std;\n\nbool NO_PROMPT = false;\n\nbool ask_yes_no(string question, bool should_yes) {\n    if(NO_PROMPT) return should_yes;\n    fcout << \"$(info)\" << question << \" (\" << (should_yes?\"Y\":\"y\") << \"/\" << (should_yes?\"n\":\"N\") << \") $\";\n    string response;\n    getline(cin, response);\n    if(!response.size()) return should_yes;\n    if(should_yes) {\n        if(tolower(response.at(0), locale()) == 'n') return false;\n        return true;\n    } else {\n        if(tolower(response.at(0), locale()) == 'y') return true;\n        return false;\n    }\n}\n\nuint8_t ask_n(string question, uint8_t min, uint8_t max) {\n    if(NO_PROMPT) return min;\n    fcout << \"$(info)\" << question << \" (\" << to_string(min) << \"-\" << to_string(max) << \") $\";\n    string response;\n    uint8_t response_i = max + 1;\n    while(response_i < min || response_i > max) {\n        getline(cin, response);\n        while (!string_to_int<uint8_t>(response, response_i)) {\n            fcout << \"$(error)Wrong value, please try again (\" << to_string(min) << \"-\" << to_string(max) << \") $\";\n            getline(cin, response);\n        }\n        if(response_i < min || response_i > max) fcout << \"$(error)Value is not in range, please try again (\" << to_string(min) << \"-\" << to_string(max) << \") $\";\n    }\n    return response_i;\n}\n\nstring ask_password(string question) {\n    termios oldt;\n    tcgetattr(STDIN_FILENO, &oldt);\n    termios newt = oldt;\n    newt.c_lflag &= ~ECHO;\n    tcsetattr(STDIN_FILENO, TCSANOW, &newt);\n    fcout << \"$(info)\" << question << \": $\";\n    string password;\n    getline(cin, password);\n    fcout << endl;\n    tcsetattr(STDIN_FILENO, TCSANOW, &oldt);\n    return password;\n}"
  },
  {
    "path": "src/user/user_prompt.h",
    "content": "#ifndef CERBERUS_USER_PROMPT_H\n#define CERBERUS_USER_PROMPT_H\n\n#include <string>\n#include <cstdint>\n\nextern bool NO_PROMPT;\n\nbool ask_yes_no(std::string question, bool should_yes);\nuint8_t ask_n(std::string question, uint8_t min, uint8_t max);\nstd::string ask_password(std::string question);\n\n#endif //CERBERUS_USER_PROMPT_H\n"
  },
  {
    "path": "src/utils/arg_parser.cpp",
    "content": "#include <utils/arg_parser.h>\n#include <utils/logger.h>\n#include <global_defs.h>\n#include <filesystem>\n\nnamespace fs = std::filesystem;\nusing namespace std;\n\nstring ArgParser::format_help() {\n    string res = \"$(cyan)$(cyan:b)\" + TOOL_ART + \"$\\n\";\n    res += \" Version: $(white:b)\" + TOOL_VERSION + \"$\\n\";\n    res += \" Author: $(white:b)\" + TOOL_AUTHOR + \"$\\n\";\n    res += \"______________________________________\\n\\n\";\n    res += \"$(cyan:b)Syntax: $(red:b)cerberus binary [-param value] [--flag]$$\\n\\n\";\n    res += \"$(cyan:b)Parameters:$\\n\";\n    res += \"   $(red:b)output (o)$ -> Specifies the path for the resulting executable file. $(cyan:b)Default value: [input_binary]-patched$\\n\";\n    res += \"   $(red:b)part_hash_len (phl)$ -> Specifies the length of a part hash. The part hash of a function is just a reduction of the function with a linear pace. This technique is used to prevent fixed addresses from corrupting a standard hash. $(cyan:b)Default value: 20$\\n\";\n    res += \"   $(red:b)part_hash_trust (pht)$ -> Specifies minimum ratio of similarity between the two hashed functions to compare. The kept function will be the one with the most matches anyway. Increasing this value will reduce the number of matched functions but speed up execution time. $(cyan:b)Default value: 0.6$\\n\";\n    res += \"   $(red:b)min_func_size (mfs)$ -> Specifies the minimum length a function must be to get analyzed. Decreasing this value will increase matches but also false positives. $(cyan:b)Default value : 10$\\n\\n\";\n    res += \"$(cyan:b)Flags:$\\n\";\n    res += \"   $(red:b)help (h)$ -> Displays this message.\\n\";\n    res += \"   $(red:b)debug (h)$ -> Displays outputs of commands.\\n\";\n    res += \"   $(red:b)no-prompt (np)$ -> Automatically skips user prompts.\";\n    return res;\n}\n\nvoid ArgParser::prepare_args() {\n    this->parser.add_argument(\"binary\").default_value(\"\");\n    this->parser.add_argument(\"-output\", \"-o\");\n    this->parser.add_argument(\"-part_hash_len\", \"-phl\");\n    this->parser.add_argument(\"-part_hash_trust\", \"-pht\");\n    this->parser.add_argument(\"-min_func_size\", \"-mfs\");\n    this->parser.add_argument(\"--help\", \"--h\").implicit_value(true);\n    this->parser.add_argument(\"--debug\", \"--dbg\").implicit_value(true);\n    this->parser.add_argument(\"--no-prompt\", \"--np\").implicit_value(true);\n}\n\nCONFIG* ArgParser::compute_args(int argc, char **argv) {\n    CONFIG* config = new CONFIG;\n    if(argc >= 2) {\n        this->parser.parse_args(argc, argv);\n        config->binary_path = this->parser.get<string>(\"binary\");\n        if(this->parser.is_used(\"-output\")) config->output_path = this->parser.get<string>(\"output\");\n        else {\n            size_t extension_idx;\n            string extension = \"\";\n            if((extension_idx = config->binary_path.find_last_of('.')) != string::npos) {\n                if(config->binary_path.find_last_of('/') < extension_idx && config->binary_path.find_last_of('\\\\') < extension_idx) {\n                    extension = config->binary_path.substr(extension_idx);\n                }\n            }\n            config->output_path = config->binary_path + extension + \"-patched\";\n        }\n        if (this->parser.is_used(\"-part_hash_len\")) config->part_hash_len = this->parser.get<uint16_t>(\"part_hash_len\");\n        if (this->parser.is_used(\"-part_hash_trust\"))\n            config->part_hash_trust = this->parser.get<float>(\"part_hash_trust\");\n        if (this->parser.is_used(\"-min_func_size\")) config->min_func_size = this->parser.get<uint16_t>(\"min_func_size\");\n        if (this->parser.is_used(\"--debug\")) config->debug = true;\n        if (this->parser.is_used(\"--no-prompt\")) config->no_prompt = true;\n    }\n    if(argc < 2 || this->parser.is_used(\"--help\") || !config->binary_path.length()) {\n        string help = this->format_help();\n        fcout << help << endl;\n        exit(0);\n    }\n    if(!fs::exists(config->binary_path)) {\n        fcout << \"$(critical)File $(critical:u)\" << config->binary_path << \"$ does not exist !\" << endl;\n        exit(1);\n    }\n    return config;\n}\n\nArgParser::ArgParser() {\n    this->parser = argparse::ArgumentParser(\"cerberus\");\n    this->prepare_args();\n}"
  },
  {
    "path": "src/utils/arg_parser.h",
    "content": "#ifndef CERBERUS_ARG_PARSER_H\n#define CERBERUS_ARG_PARSER_H\n\n#include <utils/config.h>\n#include <argparse/argparse.hpp>\n\nclass ArgParser {\nprivate:\n    argparse::ArgumentParser parser;\n    std::string format_help();\n    void prepare_args();\npublic:\n    ArgParser();\n    CONFIG* compute_args(int argc, char *argv[]);\n};\n\n#endif //CERBERUS_ARG_PARSER_H\n"
  },
  {
    "path": "src/utils/config.h",
    "content": "#ifndef CERBERUS_CONFIG_H\n#define CERBERUS_CONFIG_H\n\n#include <string>\n#include <cstdint>\n\nstruct CONFIG {\n    std::string binary_path;\n    std::string output_path;\n    uint16_t part_hash_len = 20;\n    float part_hash_trust = 0.6;\n    uint16_t min_func_size = 10;\n    bool debug = false;\n    bool no_prompt = false;\n};\n\n#endif //CERBERUS_CONFIG_H\n"
  },
  {
    "path": "src/utils/convert.cpp",
    "content": "#include <utils/convert.h>\n#include <sstream>\n#include <command/command_executor.h>\n\nusing namespace std;\n\nvector<string> split_string(const string& input, char delimiter) {\n    vector<std::string> tokens;\n    istringstream token_stream(input);\n    string token;\n    while (getline(token_stream, token, delimiter)) {\n        tokens.push_back(token);\n    }\n    return tokens;\n}\n\nvector<std::string> filter_empty_strings(const vector<std::string>& tab) {\n    vector<std::string> result;\n    for (const std::string& s : tab) {\n        if (!s.empty()) {\n            result.push_back(s);\n        }\n    }\n    return result;\n}\n\nstring strip(const string& str) {\n    size_t first = str.find_first_not_of(\" \\t\\n\");\n    if (string::npos == first) {\n        return \"\";\n    }\n    size_t last = str.find_last_not_of(\" \\t\\n\");\n    return str.substr(first, (last - first + 1));\n}\n\nbool ends_with(string const &value, string const &ending) {\n    if (ending.size() > value.size()) return false;\n    return equal(ending.rbegin(), ending.rend(), value.rbegin());\n}\n\nvoid replace_all(string& str, const string& from, const string& to) {\n    if(from.empty()) return;\n    size_t start_pos = 0;\n    while((start_pos = str.find(from, start_pos)) != std::string::npos) {\n        str.replace(start_pos, from.length(), to);\n        start_pos += to.length();\n    }\n}\n\nstring demangle_function_name(string& mangled_name) {\n    replace_all(mangled_name, string(\"$\"), string(\"\\\\$\"));\n    CommandExecutor executor(\"./\");\n    COMMAND_RESULT res;\n    executor.execute_command(string(\"c++filt \\\"\")+mangled_name+string(\"\\\"\"), &res);\n    if(!res.code) return res.response.substr(0, res.response.length()-1);\n    return mangled_name;\n}"
  },
  {
    "path": "src/utils/convert.h",
    "content": "#ifndef CERBERUS_CONVERT_H\n#define CERBERUS_CONVERT_H\n\n#include <string>\n#include <vector>\n\ntemplate<typename IntType> bool string_to_int(std::string s, IntType& val) {\n    try {\n        if (s.size() >= 2 && s.substr(0, 2) == \"0x\") val = static_cast<IntType>(stoll(s.substr(2), nullptr, 16));\n        else val = static_cast<IntType>(stoll(s, nullptr, 10));\n        return true;\n    } catch (const std::exception& e) {\n        return false;\n    }\n}\n\nstd::vector<std::string> split_string(const std::string& input, char delimiter);\n\nstd::vector<std::string> filter_empty_strings(const std::vector<std::string>& tab);\n\nstd::string strip(const std::string& str);\n\nbool ends_with(std::string const& value, std::string const& ending);\n\nvoid replace_all(std::string& str, const std::string& from, const std::string& to);\n\nstd::string demangle_function_name(std::string& mangled_name);\n\n#endif //CERBERUS_CONVERT_H\n"
  },
  {
    "path": "src/utils/file_downloader.cpp",
    "content": "#include <utils/file_downloader.h>\n\nusing namespace std;\n\nFileDownloader::FileDownloader() {\n    curl = curl_easy_init();\n}\n\nFileDownloader::~FileDownloader() {\n    curl_easy_cleanup(curl);\n}\n\nsize_t write_callback(void* contents, size_t size, size_t nmemb, FILE* file) {\n    return fwrite(contents, size, nmemb, file);\n}\n\nbool FileDownloader::download_file(string url, string path) {\n    FILE* file = fopen(path.c_str(), \"wb\");\n    if (file) {\n        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());\n        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);\n        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);\n        curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);\n        CURLcode res = curl_easy_perform(curl);\n        fclose(file);\n        if (res == CURLE_OK) return true;\n    }\n    return false;\n}"
  },
  {
    "path": "src/utils/file_downloader.h",
    "content": "#ifndef CERBERUS_FILE_DOWNLOADER_H\n#define CERBERUS_FILE_DOWNLOADER_H\n#include <curl/curl.h>\n#include <string>\n\nclass FileDownloader {\nprivate:\n    CURL* curl;\npublic:\n    FileDownloader();\n    ~FileDownloader();\n    bool download_file(std::string url, std::string path);\n};\n\n#endif //CERBERUS_FILE_DOWNLOADER_H\n"
  },
  {
    "path": "src/utils/file_operations.cpp",
    "content": "#include <utils/file_operations.h>\n#include <fstream>\n#include <gzip/utils.hpp>\n#include <gzip/decompress.hpp>\n#include <archive.h>\n#include <archive_entry.h>\n#include <fcntl.h>\n#include <filesystem>\n#include <iostream>\n\nusing namespace std;\n\nbool decompress_gzip_file(string input, string output) {\n    ifstream input_file(input, ios::binary);\n    input_file.seekg(0, ios::end);\n    size_t input_file_sz = input_file.tellg();\n    input_file.seekg(0);\n    char* data = (char*) malloc(input_file_sz);\n    if(!data) return false;\n    input_file.read(data, input_file_sz);\n    input_file.close();\n    if(!gzip::is_compressed(data, input_file_sz)) return false;\n    string decompressed = gzip::decompress(data, input_file_sz);\n    ofstream output_file(output, ios::binary);\n    output_file.write(decompressed.c_str(), decompressed.size());\n    output_file.close();\n    free(data);\n    return true;\n}\n\nbool decompress_tar_file(string input, string output) {\n    struct archive* a = archive_read_new();\n    archive_read_support_format_all(a);\n    if (archive_read_open_filename(a, input.c_str(), 10240) != ARCHIVE_OK) {\n        return false;\n    }\n    if (!filesystem::create_directory(output)) {\n        return false;\n    }\n    struct archive_entry* entry;\n    while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {\n        string entry_name = string(archive_entry_pathname(entry));\n        size_t first_separator_index = entry_name.find('/');\n        if(first_separator_index == string::npos) continue;\n        entry_name = entry_name.substr(first_separator_index+1);\n        string output_file_path = string(output) + \"/\" + entry_name;\n        size_t last_separator_index = output_file_path.rfind('/');\n        if(last_separator_index != string::npos) {\n            string directory_part = output_file_path.substr(0, last_separator_index);\n            filesystem::create_directories(directory_part);\n        }\n        int outputFd = open(output_file_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666);\n        if (outputFd == -1) {\n            continue;\n        }\n        const void* buffer;\n        size_t size;\n        int64_t offset;\n        while (archive_read_data_block(a, &buffer, &size, &offset) == ARCHIVE_OK) {\n            write(outputFd, buffer, size);\n        }\n        close(outputFd);\n    }\n    archive_read_close(a);\n    archive_read_free(a);\n    return true;\n}"
  },
  {
    "path": "src/utils/file_operations.h",
    "content": "#ifndef CERBERUS_FILE_OPERATIONS_H\n#define CERBERUS_FILE_OPERATIONS_H\n\n#include <string>\n\nbool decompress_gzip_file(std::string input, std::string output);\nbool decompress_tar_file(std::string input, std::string output);\n\n#endif //CERBERUS_FILE_OPERATIONS_H\n"
  },
  {
    "path": "src/utils/logger.cpp",
    "content": "#include <utils/logger.h>\n#include <iostream>\n\nusing namespace std;\n\nFCout fcout;\n\nmap<string, uint8_t> LOG_COLORS = {\n    {\"black\", 30},\n    {\"red\", 31},\n    {\"green\", 32},\n    {\"yellow\", 33},\n    {\"blue\", 34},\n    {\"magenta\", 35},\n    {\"cyan\", 36},\n    {\"white\", 37},\n    {\"bright_black\", 90},\n    {\"bright_red\", 91},\n    {\"bright_green\", 92},\n    {\"bright_yellow\", 93},\n    {\"bright_blue\", 94},\n    {\"bright_magenta\", 95},\n    {\"bright_cyan\", 96},\n    {\"bright_white\", 97}\n};\n\nmap<string, pair<uint8_t, uint8_t>> LOG_LEVELS = {\n    {\"success\", pair(32, '+')},\n    {\"info\", pair(36, '*')},\n    {\"debug\", pair(34, '~')},\n    {\"warning\", pair(33, '#')},\n    {\"error\", pair(35, '?')},\n    {\"critical\", pair(31, '!')}\n};\n\nmap<char, uint8_t> LOG_STYLES = {\n    {'n', 0},\n    {'b', 1},\n    {'i', 3},\n    {'u', 4},\n    {'s', 9},\n};\n\nFCout FCout::operator<<(string s) {\n    format(s);\n    std::cout << s;\n    fcout = *this;\n    return *this;\n}\n\nFCout FCout::operator<<(std::ostream&(*pManip)(std::ostream&)) {\n    cout << *pManip << \"\\033[0;\"+to_string(LOG_COLORS[\"gray\"])+\"m\";\n    args_stack.clear();\n    fcout = *this;\n    return *this;\n}\n\nvoid FCout::format(std::string& s) {\n   size_t index;\n   string res = \"\";\n   while((index = s.find('$')) != string::npos) {\n        res += s.substr(0, index);\n        if(index < s.length()-1 && s.at(index+1) == '(') {\n            string arg_name = s.substr(index+2);\n            arg_name = arg_name.substr(0, arg_name.find(')'));\n            int index2;\n            uint8_t font_mode = 0;\n            bool has_font_mode = false;\n            if((index2 = arg_name.find(\":\")) != string::npos) {\n                has_font_mode = true;\n                uint8_t font_mode_chr = arg_name.at(index2+1);\n                if(LOG_STYLES.find(font_mode_chr) != LOG_STYLES.end()) {\n                    font_mode = LOG_STYLES[font_mode_chr];\n                }\n                arg_name = arg_name.substr(0, arg_name.length()-2);\n            }\n            if(LOG_COLORS.find(arg_name) != LOG_COLORS.end()) {\n                args_stack.push_back(pair(LOG_COLORS[arg_name], font_mode));\n                res += \"\\033[\"+to_string(font_mode)+\";\"+to_string(LOG_COLORS[arg_name])+\"m\";\n            } else if(LOG_LEVELS.find(arg_name) != LOG_LEVELS.end()) {\n                pair<uint8_t, uint8_t> level = LOG_LEVELS[arg_name];\n                res += \"\\033[\"+to_string(font_mode)+\";\"+to_string(level.first)+\"m\";\n                if(!this->args_stack.size() || this->args_stack.at(this->args_stack.size()-1).first != level.first) res += string(\"[\")+(char)level.second+\"] \";\n                args_stack.push_back(pair(level.first, font_mode));\n            }\n            s = s.substr(index+3+arg_name.length()+(has_font_mode?2:0));\n        } else if(args_stack.size() > 0) {\n            args_stack.pop_back();\n            pair<uint8_t, uint8_t> arg;\n            if(args_stack.size() > 0) arg = args_stack.at(args_stack.size()-1);\n            else arg = pair(LOG_COLORS[\"gray\"], 0);\n            res += \"\\033[\"+to_string(arg.second)+\";\"+to_string(arg.first)+\"m\";\n            s = s.substr(index+1);\n        }\n   }\n   res += s;\n   s = res;\n}"
  },
  {
    "path": "src/utils/logger.h",
    "content": "#ifndef CERBERUS_LOGGER_H\n#define CERBERUS_LOGGER_H\n\n#include <string>\n#include <map>\n#include <vector>\n#include <cstdint>\n\nextern std::map<std::string, uint8_t> LOG_COLORS;\nextern std::map<std::string, std::pair<uint8_t, uint8_t>> LOG_LEVELS;\nextern std::map<char, uint8_t> LOG_STYLES;\n\nclass FCout {\nprivate:\n    std::vector<std::pair<uint8_t,uint8_t>> args_stack;\npublic:\n    FCout operator<<(std::string s);\n    FCout operator<<(std::ostream&(*pManip)(std::ostream&));\n    void format(std::string& s);\n};\n\nextern FCout fcout;\n\n#endif //CERBERUS_LOGGER_H\n"
  },
  {
    "path": "src/utils/search.cpp",
    "content": "#include <utils/search.h>\n\nusing namespace std;\n\nvector<string> search_regex(char* data, size_t data_sz, string pattern, size_t match_max_sz) {\n    vector<string> matches;\n    regex reg(pattern);\n    size_t data_i = 0;\n    while(data_i < data_sz) {\n        string data_str(data+data_i, 1024+match_max_sz);\n        sregex_iterator it(data_str.begin(), data_str.end(), reg);\n        sregex_iterator end;\n        while (it != end) {\n            smatch match = *it;\n            matches.push_back(match.str());\n            ++it;\n        }\n        data_i += 1024;\n    }\n    return matches;\n}"
  },
  {
    "path": "src/utils/search.h",
    "content": "#ifndef CERBERUS_SEARCH_H\n#define CERBERUS_SEARCH_H\n\n#include <regex>\n#include <vector>\n\nstd::vector<std::string> search_regex(char* data, size_t data_sz, std::string pattern, size_t match_max_sz);\n\n#endif //CERBERUS_SEARCH_H\n"
  },
  {
    "path": "test/Go/test_1/result.txt",
    "content": "github.com/fatih/color.colorPrint\ncrypto/aes.NewCipher\n"
  },
  {
    "path": "test/Go/test_1/src/go.mod",
    "content": "module test_1\n\ngo 1.18\n\nrequire (\n\tgithub.com/fatih/color v1.15.0 // indirect\n\tgithub.com/mattn/go-colorable v0.1.13 // indirect\n\tgithub.com/mattn/go-isatty v0.0.17 // indirect\n\tgolang.org/x/sys v0.6.0 // indirect\n)\n"
  },
  {
    "path": "test/Go/test_1/src/go.sum",
    "content": "github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=\ngithub.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=\ngithub.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=\ngithub.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=\ngolang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\n"
  },
  {
    "path": "test/Go/test_1/src/test_1.go",
    "content": "package main\n\nimport (\n\t\"github.com/fatih/color\"\n\t\"bytes\"\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"crypto/rand\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n)\n\nfunc main() {\n\tfmt.Println(\"Hey ! Starting the program...\")\n\n\tkey := []byte(\"The secret key !\")\n\n\tplaintext := []byte(\"Message to encrypt\")\n\n\tiv := make([]byte, aes.BlockSize)\n\tif _, err := io.ReadFull(rand.Reader, iv); err != nil {\n\t\tpanic(err)\n\t}\n\n\tblock, err := aes.NewCipher(key)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tmode := cipher.NewCBCEncrypter(block, iv)\n\n\tplaintext = padPKCS7(plaintext, aes.BlockSize)\n\n\tciphertext := make([]byte, len(plaintext))\n\n\tmode.CryptBlocks(ciphertext, plaintext)\n\n\tciphertext = append(iv, ciphertext...)\n\n\tencodedText := base64.StdEncoding.EncodeToString(ciphertext)\n\tfmt.Println(\"Encrypted text (base64):\", encodedText)\n\n\tdecodedText, err := base64.StdEncoding.DecodeString(encodedText)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tiv = decodedText[:aes.BlockSize]\n\tciphertext = decodedText[aes.BlockSize:]\n\n\tmode = cipher.NewCBCDecrypter(block, iv)\n\n\tdecryptedText := make([]byte, len(ciphertext))\n\n\tmode.CryptBlocks(decryptedText, ciphertext)\n\n\tdecryptedText = unpadPKCS7(decryptedText)\n\n\tfmt.Println(\"Decrypted text:\", string(decryptedText))\n\t\n\tcolor.Cyan(\"Prints text in cyan.\")\n}\n\nfunc padPKCS7(data []byte, blockSize int) []byte {\n\tpadding := blockSize - (len(data) % blockSize)\n\tpadText := bytes.Repeat([]byte{byte(padding)}, padding)\n\treturn append(data, padText...)\n}\n\nfunc unpadPKCS7(data []byte) []byte {\n\tlength := len(data)\n\tunpadding := int(data[length-1])\n\treturn data[:length-unpadding]\n}\n"
  },
  {
    "path": "test/Rust/test_1/Cargo.toml",
    "content": "[package]\nname = \"test_1\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\naes = \"0.8.2\"\n\n[profile.release]\nstrip = true\n"
  },
  {
    "path": "test/Rust/test_1/result.txt",
    "content": "aes::soft::fixslice::aes128_encrypt\n<aes::ni::Aes128Enc as crypto_common::KeyInit>::new\n"
  },
  {
    "path": "test/Rust/test_1/src/main.rs",
    "content": "use aes::Aes128;\nuse aes::cipher::{\n    BlockEncrypt, BlockDecrypt, KeyInit,\n    generic_array::GenericArray,\n};\n\nfn main() {\n    let key = GenericArray::from([0u8; 16]);\n    let mut block = GenericArray::from([42u8; 16]);\n    let cipher = Aes128::new(&key);\n    let block_copy = block.clone();\n    cipher.encrypt_block(&mut block);\n    cipher.decrypt_block(&mut block);\n    assert_eq!(block, block_copy);\n    let mut blocks = [block; 100];\n    cipher.encrypt_blocks(&mut blocks);\n    for block in blocks.iter_mut() {\n        cipher.decrypt_block(block);\n        assert_eq!(block, &block_copy);\n    }\n    cipher.decrypt_blocks(&mut blocks);\n    for block in blocks.iter_mut() {\n        cipher.encrypt_block(block);\n        assert_eq!(block, &block_copy);\n    }\n}\n"
  }
]