
#include <jni.h>
#include <string>
#include <fstream>
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <android/log.h>
#include <pthread.h>
#include <dlfcn.h>
#include <link.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ptrace.h> // Add this line

#define LOG_TAG "NativeLib"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

// Function to check for common root binaries
bool isRooted()
{
    std::string paths[] = {
        "/system/app/Superuser.apk",
        "/system/xbin/su",
        "/system/bin/su",
        "/system/bin/magisk",
        "/system/xbin/magisk",
        "/sbin/magisk"};

    for (const auto &path : paths)
    {
        std::ifstream file(path);
        if (file.good())
        {
            return true;
        }
    }
    return false;
}

// Function to check for the presence of files or directories commonly associated with Frida.
bool checkFridaFiles()
{
    std::string fridaFiles[] = {
        "/data/local/tmp/re.frida.server", // Common Frida server path
        "/data/local/tmp/frida-server",
        "/usr/lib/libfrida-gadget.so", // Frida gadget (injected library)
        "/usr/lib64/libfrida-gadget.so",
        "/data/local/re.frida.server",

    };

    for (const auto &path : fridaFiles)
    {
        if (access(path.c_str(), F_OK) != -1)
        {
            LOGE("Frida file detected: %s", path.c_str());
            return true;
        }
    }
    return false;
}

// Checks for open ports commonly used by Frida.  This is less reliable, as ports can be changed.
bool checkFridaPorts()
{
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == -1)
    {
        return false; // Couldn't create socket, not a strong indicator.
    }

    sockaddr_in sa;
    memset(&sa, 0, sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_port = htons(27042); // Default Frida port
    inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr);

    if (connect(sock, (struct sockaddr *)&sa, sizeof(sa)) != -1)
    {
        LOGE("Frida default port (27042) is open.");
        close(sock);
        return true;
    }

    close(sock);
    return false;
}

// Check the maps file of the current process for any suspicious entries.
bool checkMaps()
{
    std::ifstream mapsFile("/proc/self/maps");
    std::string line;

    if (mapsFile.is_open())
    {
        while (std::getline(mapsFile, line))
        {
            // Look for lines that indicate injected libraries, especially Frida.
            if (line.find("frida") != std::string::npos ||
                line.find("gum-js-") != std::string::npos)
            { // Gum is Frida's JavaScript engine
                LOGE("Suspicious entry in /proc/self/maps: %s", line.c_str());
                return true;
            }
        }
        mapsFile.close();
    }
    else
    {
        LOGE("Could not open /proc/self/maps");
        return false;
    }
    return false;
}

// Check loaded modules.
bool checkLoadedModules()
{
    bool found = false;
    dl_iterate_phdr([](struct dl_phdr_info *info, size_t size, void *data)
                    {
                        bool *found_ptr = static_cast<bool *>(data);
                        if (std::string(info->dlpi_name).find("frida") != std::string::npos)
                        {
                            LOGE("Frida module detected: %s", info->dlpi_name);
                            *found_ptr = true;
                            return 1; // Stop iterating
                        }
                        return 0; // Continue iterating
                    },
                    &found);

    return found;
}

// This is a simple ptrace check.  More sophisticated checks are possible (and necessary for robust detection).
// bool checkPtrace() {
//    // Attempt to ptrace ourselves.  If another process is already tracing us, this will fail.
//    if (ptrace(PTRACE_TRACEME, 0, 1, 0) < 0) {
//        LOGE("ptrace failed.  Debugger or tracer detected.");
//        return true; // Likely being traced
//    }
//    // Detach. If attached, need to detach to not interfere.
//    ptrace(PTRACE_DETACH, 0, 0, 0);
//    return false;
//}

extern "C" JNIEXPORT jboolean JNICALL
Java_com_intaleq_intaleq_admin_RootDetection_isNativeRooted(JNIEnv *env, jobject /* this */)
{

    if (isRooted())
    {
        return JNI_TRUE;
    }

    if (checkFridaFiles())
    {
        return JNI_TRUE;
    }

    if (checkFridaPorts())
    {
        return JNI_TRUE;
    }
    if (checkMaps())
    {
        return JNI_TRUE;
    }

    if (checkLoadedModules())
    {
        return JNI_TRUE;
    }

    //    if (checkPtrace()) {
    //        return JNI_TRUE;
    //    }

    return JNI_FALSE;
}