2025 腾讯游戏安全大赛 mobile 决赛 wp

输麻了,挂哥乱杀的比赛。。。

外挂功能分析

cheat

cheat程序开局执行 am start -n com.ACE2025.Game/com.epicgames.ue4.SplashActivity打开游戏,并在3s后进入函数主逻辑

image-20250413092210499

0x000000000241BF0 -> 0x000000000247504:(我之后所表示的地址,默认基址都为 0x5555555000)

这里的函数主要是判断外挂的控件是否被勾选或触发。

image-20250413092713336

我们点击初始化辅助后,

image-20250413093003249

cheat程序就会进入Initialize_the_plugin,来进行自瞄透视前的初始化工作。

比如说获取libUE4.so的基地址、获取GName绝对地址、获取并保存第一人称角色Actor地址,获取PlayerCameraManager对象地址、视觉转化矩阵

image-20250413101121975

image-20250413101158388

image-20250413101805781

透视功能的实现

程序在 000000555579C530 => 00000055557978D4的位置获取了手机屏幕的宽和高。

image-20250413104631937

之后的关键逻辑在 000000555579923C处的函数实现

image-20250413104448962

首先这里检测了是否开启了自瞄,如果开启了自瞄,那么程序将会调用 draw_yellow_circle => 0x0000055557C90B0 =>0x0000055557C6D34 来绘制黄色圆圈。

image-20250413104831644

就是这个黄色框:

image-20250413105120672

这里获取了摄像机的世界坐标以及rotation

image-20250413112934055

这里获取第三人称的世界坐标

image-20250413113036887

从这里开始一直到1300多行的 foot_r,这些都是在读取第三人称身体部位的坐标,包括:头、脊椎、左上臂、右上臂、左前臂、右前臂、左手、右手、左大腿、右大腿、左脚、右脚等等。

image-20250413113105702

image-20250413113449129

实际上是为了绘制人物骨骼做准备,如下图

image-20250413113740291

从 1367到1536的代码,是为了把这些世界坐标转化为手机平面坐标。

这里是自瞄位置的坐标转化,mb_selected___position的值为 0、1、2,分别代表瞄准胸部、头部、臀部。

image-20250413114019689

再往后就是绘制方式的选择

image-20250413114532961

box_selected=0、1、2分别代表绘制矩形方阵四角方针立方方阵

再之后就是选择是否绘制人物射线、人物骨骼了,人物骨骼的绘制代码很多,毕竟需要绘制非常多的部位。

image-20250413114628871

可以发现这里射线的起点给的是固定的屏幕值,transformer_and_draw函数主要功能就是对屏幕上的两个点之间进行线段绘制

image-20250413114932737

这就是外挂上透视的各个功能实现原理。

自瞄的功能实现

首先看这里:0x0000055557A331C

在外挂程序打开并且还未开启辅助的时候,就会执行这里的函数

image-20250413115633493

其功能大致如下:

  • /dev/input目录下获取了触摸屏的驱动设备(名称为fts),并启用设备抓取。
  • 之后创建了一个新的驱动设备,设备名称为随机的8字节字符串。并配置好设备事件。用来模拟触摸屏、按键等等。
  • 在辅助关闭的时候,恢复设备抓取,删除新的驱动设备。
image-20250413120210454

image-20250413120354276

之后,由新创建的驱动设备来代替实现 fts的功能。实际上,cheat程序也是通过操纵新驱动设备的读写来实现拖动触摸屏自瞄的。我们继续分析。

0000005555798810处的函数

image-20250413120921368

这里判断是否在黄圈内。如果在黄圈内,则继续往下执行。

这里获取视觉转化矩阵。

image-20250413120948944

随后把敌人要射击的身体部位坐标转化为屏幕坐标,进入 self_aim_func函数来进行处理自瞄。

image-20250413121216359

随后 self_aim_func=>0x0000055557A3268=>0x0000055557A21DC进入了新的虚拟设备驱动处理函数。

image-20250413121547166

通过向驱动设备写入数据来拖动屏幕实现自瞄射击。

ACEInject

这是一个Zygisk模块。关键逻辑在 libGame.so文件中。

主逻辑在 0x00000000000111C处的函数。

image-20250413121935272

可以看到,程序获取了libUE4.so文件的坐标,并且在 libUE4.sO + 0x6711AC4位置处修改了会被代码

image-20250413122114035

修改后:

image-20250413122150017

之后程序进入了反调试的死循环里:

image-20250413122227264
1、/proc/self/stat 反调试,读进程状态,判断进程是否被调试。
2、端口检测,检测 27043与27044端口有没有被使用
3、/proc/self/status 反调试,读取TracerPid,如果TracerPid 不为0,则进程正在被调试
4、check_gmain_frida,遍历进程的 /proc/self/task/ 目录,检查每个线程的 comm 文件中是否有与 gmain 或 pool-frida 相关的字符串,来找frida调试的痕迹。
5、check_frida,通过读取 /proc/self/maps来判断是否存在frida。
6、frida hook时的特征检测,
  (1)、检查指定地址 a1 是否来自 libc.so 或 libdl.so。Frida 注入的钩子通常会用到这些库,尤其是 dlsym、dlopen 和 fopen 等函数。因此,如果目标地址的符号来自这两个库,函数会判断为可能的调试行为。
  (2)、0x580000、0xD61F0、实际上是 检查 `xx 00 00 58  xx x0 1f d6`这种inline hook 的特征码
  

如果程序检测到反调试,则会立马终止外挂程序。

ACEInject 外挂检测

crc内存校验

新建线程,不停的循环检测即可.这里提前计算出了代码段的crc32值,之后线程只有循环检测即可。检测到外挂后,向/data/ldata/com.ACE2025.Game/log.txt文件中写入 [crc check], zgisk hack!

void* check_magisk1_crc_verify(void* arg){
    uint32 last_crc = 0xe9838d80;            // 0xa10ec2ab
    while (1) {
        sleep(4); 
       

        uint64 libUE4_base = get_base_so("libUE4.so"); 
        log("libUE4_base: %llx", libUE4_base);
        uint64 text_start = libUE4_base + 0x000000005AE7000;
        uint64 text_end = libUE4_base + 0x00000000A4EAC50;
        uint64 text_size = text_end - text_start;

        unsigned int current_crc = cal_mem_crc32(text_start, text_size);
        log("current crc: %x", current_crc);
        if (last_crc !=0){
            if(last_crc != current_crc){
                wlog("[crc check], zgisk hack!");
                log("[crc check], zgisk hack!");
            }
        }
        
    }
    return NULL;
}

段权限检查

写个循环不断检查段的权限,因为zgisk程序会修改代码段属性。

测到外挂后,向/data/ldata/com.ACE2025.Game/log.txt文件中写入 [seg permission check], zgisk hack!

void *check_magisk2_check_segment__permission(void* arg){
    uint64 libUE4_base = get_base_so("libUE4.so");
    uint64 invalid_seg = libUE4_base + 0x000000006711AC4;
    log("invalid_seg: %llx", invalid_seg);
    // 查询invalid_seg地址的权限
    while (1){
        FILE *fp = fopen("/proc/self/maps", "r");
        if (fp == NULL) {
            log("Failed to open /proc/self/maps");
            return NULL;
        }
        char line[256];
        while (fgets(line, sizeof(line), fp) != NULL) {
            if (strstr(line, "libUE4.so")) {
                unsigned long start, end;
                sscanf(line, "%lx-%lx", &start, &end);
                if (invalid_seg >= start && invalid_seg < end) {
                    // 该地址在libUE4.so的映射范围内
                    // 检查权限
                    if (strstr(line, "rwxp")) {
                        wlog("[seg permission check], zgisk hack!");
                        log("[seg permission check], zgisk hack!");
                    }
                }
            }
        }

        fclose(fp);
        sleep(5);

    }
    return NULL;
}

inotify 监控

inotify可以监控/proc/self/stat/proc/self/status, /proc/self/maps等等,我这里给出监控 /proc/self/maps的例子:

测到外挂后,向/data/ldata/com.ACE2025.Game/log.txt文件中写入 [inotify maps check], find cheat hack!

void* check_maps(void * arg){
    int fd, wd_mem;
    char buffer[BUF_LEN];
    char mem_file[128] = "/proc/self/maps";
    sleep(3);

    // 创建 inotify 实例
    fd = inotify_init();
    if (fd == -1) {
        log("inotify_init");
        return ;
    }

    // 监视 /proc/<pid>/maps 和 /proc/<pid>/mem 文件的打开和修改操作
    wd_mem = inotify_add_watch(fd, mem_file, IN_ACCESS | IN_MODIFY | IN_OPEN | IN_CLOSE_WRITE);
    if (wd_mem == -1) {
        log("inotify_add_watch error3");
        return NULL;
    }

    log("Monitoring /proc/self/mem for access and modifications...\n");

    while (1) {
        int length = read(fd, buffer, BUF_LEN);
        if (length == -1) {
            log("read error ");
            return NULL;
    
        }

        // 处理读取到的事件
        int i = 0;
        while (i < length) {
            struct inotify_event *event = (struct inotify_event *) &buffer[i];

            if (event->mask & IN_ACCESS) {
                log("File %s was accessed.\n", event->name);
                wlog("[inotify maps check], find cheat hack!");
                
            }
            if (event->mask & IN_MODIFY) {
                log("File %s was modified.\n", event->name);
                wlog("[inotify maps check], find cheat hack!");

            }
            if (event->mask & IN_OPEN) {
                log("File %s was opened.\n", event->name);
                wlog("[inotify maps check], find cheat hack!");
            }
            if (event->mask & IN_CLOSE_WRITE) {
                log("File %s was closed after write.\n", event->name);
                wlog("[inotify maps check], find cheat hack!");
                
            }

            // log("just circle");
            i += sizeof(struct inotify_event) + event->len;
        }
    }

    // 关闭 inotify 实例
    close(fd);
}

cheat 外挂检测

mincore检测

设置陷阱内存页。经过寻找与调试,找到了 so_base + 0xAF75B08;

测到外挂后,向/data/ldata/com.ACE2025.Game/log.txt文件中写入 [mincore check], find cheat hack!

uint64 mincore_one(){
    uint64 so_base = get_base_so("libUE4.so");
    uint64 detect_addr1 = so_base + 0xAF75B08;
    log("detect_addr1: %llx", detect_addr1);

    return detect_addr1;
    
}

// mincore 钓鱼吧 / /proc/self/pagemap 
void* check_mincore(void* arg){

    sleep(3);
    // 页面地址替换,先睡3s,让系统进行完所有的初始化,我们再写。
    int num_pages = 1; 
    uint64 mincore_check_list[num_pages];


    mincore_check_list[0] = mincore_one();
    uint64 mincore_mem_addr[num_pages];
    size_t PS = sysconf(_SC_PAGESIZE);                             //获取系统的页面大小
    for(int i = 0; i < num_pages; ++i) {
        mincore_mem_addr[i] = (uint64)mmap(nullptr, PS, PROT_READ | PROT_WRITE,
            MAP_NORESERVE | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
        if ((void*)mincore_check_list[i] == MAP_FAILED) {
            log("Failed to allocate memory for mincore check list");
            return NULL;
        }
    }


    for(int i = 0; i < num_pages; ++i) {
        *(uint64 *)mincore_check_list[i] = mincore_mem_addr[i];
        log("mincore_check_list[%d]: %llx  = mincore_mem_addr[x]: %llx", i, mincore_check_list[i], mincore_mem_addr[i]);
    }


    // 循环检测
    while(1){
        sleep(3);

        for(int i = 0; i < num_pages; ++i) {
            unsigned char vec[2];
            int res = mincore((void*)mincore_mem_addr[i],  PS, vec);
            if (res == -1) {
                log("mincore failed");
                return NULL;;
            }
            log("mincore check: %d", (vec[0] & 1));
            if ((vec[0] & 1) == 1) {
                wlog("[mincore check], find cheat hack! ");
                log("[mincore check], find cheat hack!");
                return NULL; ;
            }
        }
    }


}


inotify 监控 /proc/self/maps

ACEInject检测中的 inotify监控方法

void* check_maps(void * arg){
    int fd, wd_mem;
    char buffer[BUF_LEN];
    char mem_file[128] = "/proc/self/maps";
    sleep(3);

    // 创建 inotify 实例
    fd = inotify_init();
    if (fd == -1) {
        log("inotify_init");
        return ;
    }

    // 监视 /proc/<pid>/maps 和 /proc/<pid>/mem 文件的打开和修改操作
    wd_mem = inotify_add_watch(fd, mem_file, IN_ACCESS | IN_MODIFY | IN_OPEN | IN_CLOSE_WRITE);
    if (wd_mem == -1) {
        log("inotify_add_watch error3");
        return NULL;
    }

    log("Monitoring /proc/self/mem for access and modifications...\n");

    while (1) {
        int length = read(fd, buffer, BUF_LEN);
        if (length == -1) {
            log("read error ");
            return NULL;
    
        }

        // 处理读取到的事件
        int i = 0;
        while (i < length) {
            struct inotify_event *event = (struct inotify_event *) &buffer[i];

            if (event->mask & IN_ACCESS) {
                log("File %s was accessed.\n", event->name);
                wlog("[inotify maps check], find cheat hack!");
                
            }
            if (event->mask & IN_MODIFY) {
                log("File %s was modified.\n", event->name);
                wlog("[inotify maps check], find cheat hack!");

            }
            if (event->mask & IN_OPEN) {
                log("File %s was opened.\n", event->name);
                wlog("[inotify maps check], find cheat hack!");
            }
            if (event->mask & IN_CLOSE_WRITE) {
                log("File %s was closed after write.\n", event->name);
                wlog("[inotify maps check], find cheat hack!");
                
            }

            // log("just circle");
            i += sizeof(struct inotify_event) + event->len;
        }
    }

    // 关闭 inotify 实例
    close(fd);
}

inotify监控 /dev/input

事实上,通过监控 /dev/uinput 也能达到同样效果

需要在 Permissive 模式下才能执行,即需要提前开启setenforce 0

测到外挂后,向/data/ldata/com.ACE2025.Game/log.txt文件中写入 [inotify input check], find cheat xxx!

// 监控驱动设备的删除与打开,
void *check_detect_device(void* arg){
    int fd, wd;
    char buffer[BUF_LEN];
    // 创建 inotify 实例
    fd = inotify_init();
    if (fd == -1) {
        log("inotify_init error");
        return NULL ;
    }
  
    // UI_DEV_CREATE
    // 监视 /dev/input 目录下的文件
    wd = inotify_add_watch(fd, "/dev/input", IN_OPEN  | IN_CREATE | IN_DELETE);
    if (wd == -1) {
        log("inotify_add_watch error: %s\n", strerror(errno));
        return NULL ;
    }
    log("Monitoring /dev/uinput for access and modification events...\n");
    // 持续监听事件
    while (1) {
        int length = read(fd, buffer, BUF_LEN);
        if (length == -1) {
            log("read error !!");
            return NULL ;
        }
  
        int i = 0;
        while (i < length) {
            struct inotify_event *event = (struct inotify_event *) &buffer[i];
            if (event->mask & IN_OPEN) {
                log("File %s was opened.\n", event->name);
            }
            if (event->mask & IN_CREATE) {
                log("File %s was created.\n", event->name);
                char *str = (char *)malloc(128);
                sprintf(str, "[inotify input check], find cheat open! %s create.",event->name);
                wlog(str);
                free(str);
            }
            if (event->mask & IN_DELETE) {
                log("File %s was deleted.\n", event->name);
                char *str = (char *)malloc(128);
                sprintf(str, "[inotify input check], find cheat close! %s delete.",event->name);
                wlog(str);
                free(str);
            }
            i += sizeof(struct inotify_event) + event->len;
        }
    }
    close(fd);
    return NULL ;
}


检测虚拟设备的名称与数目

在.so文件刚被加载的时候获取虚拟设备的数目与名称,之后每隔一段时间,检测虚拟驱动设备的数目与名称,当虚拟设备的数目增加且名称为 8 字节长度的时候,判定为外挂程序。

需要root权限

测到外挂后,向/data/ldata/com.ACE2025.Game/log.txt文件中写入 [input devices check], find cheat open! xxx

void res_device_name() {
    DIR *dir = opendir("/dev/input");
    if (dir == NULL) {
        log("Failed to open /dev/input");
        return;
    }

    struct dirent *entry;
    char device_name[MAX_NAME_LEN];
    
    while ((entry = readdir(dir)) != NULL) {
        if (strncmp(entry->d_name, "event", 5) == 0) {
            char device_path[PATH_MAX];
            snprintf(device_path, sizeof(device_path), "/dev/input/%s", entry->d_name);

            // 获取设备名称
            get_device_name(device_path, device_name);
            log("Device name: %s", device_name);
            res_device_name_list[res_device_count] = (char *)malloc(MAX_NAME_LEN);
            strncpy(res_device_name_list[res_device_count], device_name, MAX_NAME_LEN - 1);
            res_device_name_list[res_device_count][MAX_NAME_LEN - 1] = '\0'; // 确保字符串以 null 结尾
            
            res_device_count++;
        }
    }
    log("res_device_count: %d", res_device_count);

    closedir(dir);
}


void * check_input_devices(void* arg) {

    while(1){
        sleep(3);
        DIR *dir = opendir("/dev/input");
        if (dir == NULL) {
            log("Failed to open /dev/input: %s", strerror(errno));
            log("error code: %s", strerror(errno));
            return NULL;
        }
    
        struct dirent *entry;
        char device_name[MAX_NAME_LEN];
        int device_count = 0;
        
        while ((entry = readdir(dir)) != NULL) {
            if (strncmp(entry->d_name, "event", 5) == 0) {
                char device_path[PATH_MAX];
                snprintf(device_path, sizeof(device_path), "/dev/input/%s", entry->d_name);
    
                // 获取设备名称
                get_device_name(device_path, device_name);
                int tag = 0;
                for(int i = 0;i<res_device_count;i++){
                    if (strcmp(device_name, res_device_name_list[i]) == 0) {
                        tag = 1;
                    }
                }
                if(tag == 0){
                    if(strlen(device_name) == 8){
                        char *str = (char *)malloc(128);
                        sprintf(str, "[input devices check], find cheat open! %s.",device_name);
                        wlog(str);
                        free(str);
                    }
                }
    
                device_count++;
            }
        }
    
        closedir(dir);
    }

   
}

检测虚拟设备抓取

利用的是这个原理,cheat在启动过程中对某一虚拟设备进行了设备抓取。

测到外挂后,向/data/ldata/com.ACE2025.Game/log.txt文件中写入 [grab check], find cheat open!

image-20250413180729275

那我们只需要每隔一段时间检测设备抓取状态,即可检测出cheat程序。需要root权限

void check_EVIOCGRAB(){
    DIR *dir;
    struct dirent *entry;
    int handle;
    int result;

    // 打开 /dev/input 目录
    dir = opendir("/dev/input");
    if (dir == NULL) {
        log("Failed to open /dev/input directory");
        return;
    }

    // 遍历目录中的所有文件
    while ((entry = readdir(dir)) != NULL) {
        if (strncmp(entry->d_name, "event", 5) == 0) {
            char device_path[256];
            snprintf(device_path, sizeof(device_path), "/dev/input/%s", entry->d_name);

            handle = open(device_path, O_RDWR);
            if (handle == -1) {
                log("Failed to open device");
                continue; // 继续处理下一个设备
            }

            // 尝试启用设备抓取
            result = ioctl(handle, EVIOCGRAB, 1);
            if (result == -1) {
                log("Failed to grab device");
                char *str = (char *)malloc(128);
                sprintf(str, "[grab check], find cheat open! %s.",device_path);
                free(str);
                wlog(str);
            } else {
                log("Successfully grabbed device");
                // 禁用抓取(释放设备)
                ioctl(handle, EVIOCGRAB, 0);
            }

            // 关闭设备文件
            close(handle);
        }
    }

    // 关闭目录
    closedir(dir);
}

image-20250413180902226

代码编写

非root下

root模式下有cec内存校验 ,段权限检测mincore检测, inotify检测 maps ,inotify监测 /dev/input(如果开了Permissive模式的话) 可用。对应展示视频no_root.mp4

#include <android/log.h>
#include <cstdio>
#include <cstdlib>
#include <cstddef>
#include <dirent.h>
#include <unistd.h>
#include <cmath>
#include <ctime>
#include <algorithm>
#include <string>
#include <list>
#include <vector>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <getopt.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/uio.h>
#include <sys/syscall.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/inotify.h>
#include <fcntl.h>
#include <linux/input.h>
#define MAX_NAME_LEN 256
#define TAG "tlsn"
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__))
#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__))
#define log(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__))
typedef unsigned char uint8;
typedef unsigned short int uint16;
typedef unsigned int uint32;
typedef unsigned long long uint64;
typedef signed char int8;
typedef signed short int int16;
typedef signed int int32;
typedef signed long long int64;
typedef uintptr_t kaddr;
#define BUF_LEN (1024 * (sizeof(struct inotify_event) + 16))
void *check_magisk(void* arg);
uint64 GWorld = 0xAFAC398;
uint64 GNAME = 0xADF07C0;
uint64 GUObjectArray = 0xAE34A98;

void init_main_func();
char* get_current_time() ;
__attribute__((section(".init_array")))
void (*my_init_array[])() = { init_main_func };


char *res_device_name_list[20];
int res_device_count =0; 
const char* logpath = "/data/data/com.ACE2025.Game/log.txt";

FILE *logfp = NULL;
void init_log_file(){
    logfp = fopen(logpath, "w+");
    if (logfp == NULL) {
        LOGE("Failed to open log file: %s", logpath);
        return;
    }
}

void wlog(const char *str){
    if (logfp == NULL) {
        LOGE("Log file not opened");
        return;
    }

    char *now_time= get_current_time();
    fprintf(logfp, "[+] %s\t%s\n",now_time ,str);
    fflush(logfp);
    free(now_time);
}

void close_log_file(){
    if (logfp != NULL) {
        fclose(logfp);
        logfp = NULL;
    }
}

char* get_current_time() {
    time_t raw_time;
    struct tm * time_info;
    char *time_buffer = (char*)malloc(80);

    time(&raw_time);  // 获取当前时间(秒)
    time_info = localtime(&raw_time);  // 转换为本地时间

    // 将时间格式化为字符串
    strftime(time_buffer, 80, "%Y-%m-%d %H:%M:%S", time_info);
    return time_buffer;
}



uint64 so_base = 0;
uint64 get_base_so(const char* so_name){

    if(so_base != 0){
        return so_base;
    }else{
        FILE *fp = fopen("/proc/self/maps", "r");
        if (fp == NULL) {
            log("Failed to open /proc/self/maps");
            return -1;
        }
    
        char line[256];
        while (fgets(line, sizeof(line), fp) != NULL) {
            if (strstr(line, so_name)) {
                unsigned long start, end;
                sscanf(line, "%lx-%lx", &start, &end);
                fclose(fp);
                return start; 
            }
        }
        fclose(fp);

    }


    return 0;
}

static const unsigned int crc32_table[] =
{
  0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
  0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
  0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
  0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
  0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
  0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
  0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
  0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
  0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
  0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
  0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
  0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
  0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
  0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
  0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
  0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
  0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
  0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
  0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
  0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
  0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
  0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
  0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
  0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
  0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
  0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
  0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
  0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
  0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
  0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
  0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
  0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
  0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
  0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
  0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
  0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
  0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
  0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
  0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
  0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
  0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
  0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
  0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
  0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
  0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
  0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
  0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
  0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
  0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
  0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
  0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
  0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
  0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
  0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
  0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
  0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
  0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
  0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
  0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
  0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
  0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
  0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
  0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
  0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};


unsigned int xcrc32 (const unsigned char *buf, int len, unsigned int init)
{
  unsigned int crc = init;
  while (len--)
    {
      crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ *buf) & 255];
      buf++;
    }
    
  return crc;
}

uint32 cal_mem_crc32(uint64 addr, uint32 size) {
    unsigned char* mem_data = (unsigned char *)malloc(size + 10);
    memcpy(mem_data, (void*)addr, size);
    if (mem_data == NULL) {
        LOGE("Failed to allocate memory for CRC calculation");
        return -1;
    }
    unsigned int init_value = 0xFFFFFFFF;
    unsigned int crc_value = xcrc32(mem_data, size, init_value);
    
    free(mem_data);
    return crc_value;
}







void* check_magisk1_crc_verify(void* arg){
    uint32 last_crc = 0xe9838d80;            // 0xa10ec2ab
    while (1) {
        sleep(4); 
       

        uint64 libUE4_base = get_base_so("libUE4.so"); 
        log("libUE4_base: %llx", libUE4_base);
        uint64 text_start = libUE4_base + 0x000000005AE7000;
        uint64 text_end = libUE4_base + 0x00000000A4EAC50;
        uint64 text_size = text_end - text_start;

        unsigned int current_crc = cal_mem_crc32(text_start, text_size);
        log("current crc: %x", current_crc);
        if (last_crc !=0){
            if(last_crc != current_crc){
                wlog("[crc check], zgisk hack!");
                log("[crc check], zgisk hack!");
            }
        }
        
    }
    return NULL;
}

void *check_magisk2_check_segment__permission(void* arg){
    uint64 libUE4_base = get_base_so("libUE4.so");
    uint64 invalid_seg = libUE4_base + 0x000000006711AC4;
    log("invalid_seg: %llx", invalid_seg);
    // 查询invalid_seg地址的权限
    while (1){
        FILE *fp = fopen("/proc/self/maps", "r");
        if (fp == NULL) {
            log("Failed to open /proc/self/maps");
            return NULL;
        }
        char line[256];
        while (fgets(line, sizeof(line), fp) != NULL) {
            if (strstr(line, "libUE4.so")) {
                unsigned long start, end;
                sscanf(line, "%lx-%lx", &start, &end);
                if (invalid_seg >= start && invalid_seg < end) {
                    // 该地址在libUE4.so的映射范围内
                    // 检查权限
                    if (strstr(line, "rwxp")) {
                        wlog("[seg permission check], zgisk hack!");
                        log("[seg permission check], zgisk hack!");
                    }
                }
            }
        }

        fclose(fp);
        sleep(5);

    }
    return NULL;
}





void test_log_file(){
    wlog("start check ...\n");
}



uint64 mincore_one(){
    uint64 so_base = get_base_so("libUE4.so");
    uint64 detect_addr1 = so_base + 0xAF75B08;
    log("detect_addr1: %llx", detect_addr1);

    return detect_addr1;
    
}



// mincore 钓鱼吧 
void* check_mincore(void* arg){

    sleep(3);
    // 页面地址替换,先睡3s,让系统进行完所有的初始化,我们再写。
    int num_pages = 1; 
    uint64 mincore_check_list[num_pages];


    mincore_check_list[0] = mincore_one();
    uint64 mincore_mem_addr[num_pages];
    size_t PS = sysconf(_SC_PAGESIZE);                             //获取系统的页面大小
    for(int i = 0; i < num_pages; ++i) {
        mincore_mem_addr[i] = (uint64)mmap(nullptr, PS, PROT_READ | PROT_WRITE,
            MAP_NORESERVE | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
        if ((void*)mincore_check_list[i] == MAP_FAILED) {
            log("Failed to allocate memory for mincore check list");
            return NULL;
        }
    }


    for(int i = 0; i < num_pages; ++i) {
        *(uint64 *)mincore_check_list[i] = mincore_mem_addr[i];
        log("mincore_check_list[%d]: %llx  = mincore_mem_addr[x]: %llx", i, mincore_check_list[i], mincore_mem_addr[i]);
    }


    // 循环检测
    while(1){
        sleep(3);

        for(int i = 0; i < num_pages; ++i) {
            unsigned char vec[2];
            int res = mincore((void*)mincore_mem_addr[i],  PS, vec);
            if (res == -1) {
                log("mincore failed");
                return NULL;;
            }
            log("mincore check: %d", (vec[0] & 1));
            if ((vec[0] & 1) == 1) {
                wlog("[mincore check], find cheat hack! ");
                log("[mincore check], find cheat hack!");
                return NULL; ;
            }
        }
    }


}


// 能监听 /proc/self/maps,但监听不了 /proc/self/mem?
void* check_maps(void * arg){
    int fd, wd_mem;
    char buffer[BUF_LEN];
    char mem_file[128] = "/proc/self/maps";
    sleep(3);

    // 创建 inotify 实例
    fd = inotify_init();
    if (fd == -1) {
        log("inotify_init");
        return NULL;
    }

    // 监视 /proc/<pid>/maps 和 /proc/<pid>/mem 文件的打开和修改操作
    wd_mem = inotify_add_watch(fd, mem_file, IN_ACCESS );
    if (wd_mem == -1) {
        log("inotify_add_watch error3");
        return NULL;
    }

    log("Monitoring /proc/self/mem for access and modifications...\n");

    while (1) {
        int length = read(fd, buffer, BUF_LEN);
        if (length == -1) {
            log("read error ");
            return NULL;
    
        }

        // 处理读取到的事件
        int i = 0;
        while (i < length) {
            struct inotify_event *event = (struct inotify_event *) &buffer[i];

            if (event->mask & IN_ACCESS) {
                log("/proc/self/maps/ %s was accessed. mb cheat / zgisk\n", event->name);
                wlog("[inotify /proc/self/maps/ check], find cheat hack!");
                
            }


            // log("just circle");
            i += sizeof(struct inotify_event) + event->len;
        }
    }

    // 关闭 inotify 实例
    close(fd);
}



// 检测设备抓取。通过 ioctl(handle, EVIOCGRAB, 1) 启用抓取,捕获返回的错误码。需要root权限
// 需要 root权限
void* check_EVIOCGRAB(void* arg){
    while(1){
        DIR *dir;
        struct dirent *entry;
        int handle;
        int result;
        sleep(3);
        // 打开 /dev/input 目录
        dir = opendir("/dev/input");
        if (dir == NULL) {
            log("Failed to open /dev/input directory , mb u need root ");
            return NULL;
        }
    
        // 遍历目录中的所有文件
        while ((entry = readdir(dir)) != NULL) {
            if (strncmp(entry->d_name, "event", 5) == 0) {
                char device_path[256];
                snprintf(device_path, sizeof(device_path), "/dev/input/%s", entry->d_name);
    
                handle = open(device_path, O_RDWR);
                if (handle == -1) {
                    log("Failed to open device, mb u need root");
                    continue; // 继续处理下一个设备
                }
    
                // 尝试启用设备抓取
                result = ioctl(handle, EVIOCGRAB, 1);
                if (result == -1) {
                    log("Failed to grab device");
                    char *str = (char *)malloc(128);
                    sprintf(str, "[grab check], find cheat open! %s.",device_path);
                    free(str);
                    wlog(str);
                } else {
                    log("Successfully grabbed device");
                    // 禁用抓取(释放设备)
                    ioctl(handle, EVIOCGRAB, 0);
                }
    
                // 关闭设备文件
                close(handle);
            }
        }
    
        // 关闭目录
        closedir(dir);
    }

}



void get_device_name(const char *device, char *name) {
    log("name: %s", name);
    int fd = open(device, O_RDONLY);
    if (fd == -1) {
        log("Failed to open device");
        return;
    }

    // 获取设备名称
    if (ioctl(fd, EVIOCGNAME(sizeof(name)), name) == -1) {
        log("Failed to get device name");
    }

    close(fd);
}



// 保存设备名称与数目
void res_device_name() {
    DIR *dir = opendir("/dev/input");
    if (dir == NULL) {
        log("Failed to open /dev/input");
        return;
    }

    struct dirent *entry;
    
    while ((entry = readdir(dir)) != NULL) {
    char device_name[MAX_NAME_LEN]={0};

        if (strncmp(entry->d_name, "event", 5) == 0) {
            char device_path[PATH_MAX];
            snprintf(device_path, sizeof(device_path), "/dev/input/%s", entry->d_name);

            // 获取设备名称
            get_device_name(device_path, device_name);
            log("Device name: %s", device_name);
            strcpy(res_device_name_list[res_device_count], device_name);
            
            res_device_count++;
        }
    }
    log("res_device_count: %d", res_device_count);

    closedir(dir);
}

void * check_input_devices(void* arg) {

    while(1){
        sleep(3);
        DIR *dir = opendir("/dev/input");
        if (dir == NULL) {
            log("Failed to open /dev/input: %s , mb u need root", strerror(errno));
            return NULL;
        }
    
        struct dirent *entry;
        char device_name[MAX_NAME_LEN];
        int device_count = 0;
        
        while ((entry = readdir(dir)) != NULL) {
            if (strncmp(entry->d_name, "event", 5) == 0) {
                char device_path[PATH_MAX];
                snprintf(device_path, sizeof(device_path), "/dev/input/%s", entry->d_name);
    
                // 获取设备名称
                get_device_name(device_path, device_name);
                int tag = 0;
                for(int i = 0;i<res_device_count;i++){
                    if (strcmp(device_name, res_device_name_list[i]) == 0) {
                        tag = 1;
                    }
                }
                if(tag == 0){
                    if(strlen(device_name) == 8){
                        char *str = (char *)malloc(128);
                        sprintf(str, "[input devices check], find cheat open! %s.",device_name);
                        wlog(str);
                        free(str);
                    }
                }
    
                device_count++;
            }
        }
    
        closedir(dir);
    }

   
}

// 监控驱动设备的删除与打开,
void *check_detect_device(void* arg){
    int fd, wd;
    char buffer[BUF_LEN];
    // 创建 inotify 实例
    fd = inotify_init();
    if (fd == -1) {
        log("inotify_init error");
        return NULL ;
    }
  
    // UI_DEV_CREATE
    // 监视 /dev/input 目录下的文件
    wd = inotify_add_watch(fd, "/dev/input",    IN_CREATE | IN_DELETE);
    if (wd == -1) {
        log("inotify_add_watch error: %s\n, mb u need root", strerror(errno));
        return NULL ;
    }
    log("Monitoring /dev/uinput for access and modification events...\n");
    // 持续监听事件
    while (1) {
        int length = read(fd, buffer, BUF_LEN);
        if (length == -1) {
            log("read error !!");
            return NULL ;
        }
  
        int i = 0;
        while (i < length) {
            struct inotify_event *event = (struct inotify_event *) &buffer[i];

            if (event->mask & IN_CREATE) {
                log("File %s was created.\n", event->name);
                char *str = (char *)malloc(128);
                sprintf(str, "[inotify input check], find cheat open! %s create.",event->name);
                wlog(str);
                free(str);
            }
            if (event->mask & IN_DELETE) {
                log("File %s was deleted.\n", event->name);
                char *str = (char *)malloc(128);
                sprintf(str, "[inotify input check], find cheat close! %s delete.",event->name);
                wlog(str);
                free(str);
            }
            i += sizeof(struct inotify_event) + event->len;
        }
    }
    close(fd);
    return NULL ;
}





// 需要 root 权限
void check_uinput(){
    
    int fd, wd;
    char buffer[BUF_LEN];
    sleep(3);
    // 创建 inotify 实例
    fd = inotify_init();
    if (fd == -1) {
        log("inotify_init");
        return ;
    }
    // 监视 /dev/input 目录下的文件
    wd = inotify_add_watch(fd, "/dev/uinput",  IN_OPEN | IN_CREATE | IN_DELETE);
    if (wd == -1) {
        log("inotify_add_watch error1");
        log("inotify_add_watch error: %s\n", strerror(errno));
        return ;
    }

    log("Monitoring /dev/uinput for access and modification events...\n");

    // 持续监听事件
    while (1) {
        int length = read(fd, buffer, BUF_LEN);
        if (length == -1) {
            log("read error !!");
            return ;
        }

        int i = 0;
        while (i < length) {
            struct inotify_event *event = (struct inotify_event *) &buffer[i];


            if (event->mask & IN_OPEN) {
                log("File %s was opened.\n", event->name);
                char *str = (char *)malloc(128);
                sprintf(str, "[inotify uinput check], find cheat open! %s open.",event->name);
                wlog(str);
                free(str);
            }
            if (event->mask & IN_CREATE) {
                log("File %s was create \n", event->name);
                char *str = (char *)malloc(128);
                sprintf(str, "[inotify uinput check], find cheat create! %s open.",event->name);
                wlog(str);
                free(str);
            }
            if (event->mask & IN_DELETE) {
                log("File %s was deleted.\n", event->name);
                char *str = (char *)malloc(128);
                sprintf(str, "[inotify uinput check], find cheat delete! %s delete.",event->name);
                wlog(str);
                free(str);
            }
            i += sizeof(struct inotify_event) + event->len;
        }
    }

    close(fd);
}




// void *check_cheat(void* arg){
//     check_mincore();
//     check_detect_device();
//     // check_maps();
//     // check_EVIOCGRAB();
//     // check_uinput();
//     return NULL;
// }

void init_main_func() {

    get_base_so("libUE4.so");
    
    init_log_file();
    test_log_file();
    log("Load libAnswer.so");
    pthread_t thread_id;

    // 1、crc 内存校验
    if (pthread_create(&thread_id, NULL, check_magisk1_crc_verify, NULL) != 0) {
        log("Failed to create thread");
        return ;
    }


    // 2、段权限检测
    pthread_t thread_id2;
    if (pthread_create(&thread_id2, NULL, check_magisk2_check_segment__permission, NULL) != 0) {
        log("Failed to create thread");
        return ;
    }


    // 3、inotify 监控,当然如果要架空 /proc/self/maps的话,就与 段权限检测冲突了。
    // pthread_t thread_id3;
    // if (pthread_create(&thread_id3, NULL, check_maps, NULL) != 0) {
    //     log("Failed to create thread3");
    //     return ;
    // }



    // 4、mincore检测
    pthread_t thread_id4;
    if (pthread_create(&thread_id4, NULL, check_mincore, NULL) != 0) {
        log("Failed to create thread2");
        return ;
    }


    // 5、inotify监控 /dev/input
    pthread_t thread_id5;
    if (pthread_create(&thread_id5, NULL, check_detect_device, NULL) != 0) {
        log("Failed to create thread2");
        return ;
    }


    // 6、检测虚拟设备的名称与数目
    // res_device_name();
    // pthread_t thread_id6;
    // if (pthread_create(&thread_id6, NULL, check_input_devices, NULL) != 0) {
    //     log("Failed to create thread2");
    //     return ;
    // }


    // // 7、检测虚拟设备的抓取
    // pthread_t thread_id7;
    // if (pthread_create(&thread_id7, NULL, check_EVIOCGRAB, NULL) != 0) {
    //     log("Failed to create thread2");
    //     return ;
    // }
    

}
    



// D:/tlsn/compiler/Android_studio/SDK/ndk/27.0.12077973/toolchains/llvm/prebuilt/windows-x86_64/bin/aarch64-linux-android24-clang.cmd ./libAnswer.cpp -fPIC -shared -g -o ./libAnswer.so -llog

root下

另创建了个文件:need_root.cpp,需要在 root权限下执行的函数,全部放在了这里,并在libAnsver.cpp中通过 popen("su -c xxx","r") 进行执行与交互。

libAnsver.cpp:

#include <android/log.h>
#include <cstdio>
#include <cstdlib>
#include <cstddef>
#include <dirent.h>
#include <unistd.h>
#include <cmath>
#include <ctime>
#include <algorithm>
#include <string>
#include <list>
#include <vector>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <getopt.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/uio.h>
#include <sys/syscall.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/inotify.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <linux/input.h>
#define MAX_NAME_LEN 256
#define TAG "tlsn"
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__))
#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__))
#define log(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__))
typedef unsigned char uint8;
typedef unsigned short int uint16;
typedef unsigned int uint32;
typedef unsigned long long uint64;
typedef signed char int8;
typedef signed short int int16;
typedef signed int int32;
typedef signed long long int64;
typedef uintptr_t kaddr;
#define BUF_LEN (1024 * (sizeof(struct inotify_event) + 16))
void *check_magisk(void* arg);
uint64 GWorld = 0xAFAC398;
uint64 GNAME = 0xADF07C0;
uint64 GUObjectArray = 0xAE34A98;

void init_main_func();
char* get_current_time() ;
__attribute__((section(".init_array")))
void (*my_init_array[])() = { init_main_func };


char *res_device_name_list[20];
int res_device_count =0; 
const char* logpath = "/data/data/com.ACE2025.Game/log.txt";

FILE *logfp = NULL;
void init_log_file(){
    logfp = fopen(logpath, "w+");
    if (logfp == NULL) {
        LOGE("Failed to open log file: %s", logpath);
        return;
    }
}

void wlog(const char *str){
    if (logfp == NULL) {
        LOGE("Log file not opened");
        return;
    }

    char *now_time= get_current_time();
    fprintf(logfp, "[+] %s\t%s\n",now_time ,str);
    fflush(logfp);
    free(now_time);
}

void close_log_file(){
    if (logfp != NULL) {
        fclose(logfp);
        logfp = NULL;
    }
}

char* get_current_time() {
    time_t raw_time;
    struct tm * time_info;
    char *time_buffer = (char*)malloc(80);

    time(&raw_time);  // 获取当前时间(秒)
    time_info = localtime(&raw_time);  // 转换为本地时间

    // 将时间格式化为字符串
    strftime(time_buffer, 80, "%Y-%m-%d %H:%M:%S", time_info);
    return time_buffer;
}



uint64 so_base = 0;
uint64 get_base_so(const char* so_name){

    if(so_base != 0){
        return so_base;
    }else{
        FILE *fp = fopen("/proc/self/maps", "r");
        if (fp == NULL) {
            log("Failed to open /proc/self/maps");
            return -1;
        }
    
        char line[256];
        while (fgets(line, sizeof(line), fp) != NULL) {
            if (strstr(line, so_name)) {
                unsigned long start, end;
                sscanf(line, "%lx-%lx", &start, &end);
                fclose(fp);
                return start; 
            }
        }
        fclose(fp);

    }


    return 0;
}

static const unsigned int crc32_table[] =
{
  0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
  0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
  0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
  0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
  0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
  0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
  0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
  0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
  0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
  0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
  0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
  0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
  0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
  0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
  0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
  0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
  0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
  0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
  0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
  0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
  0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
  0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
  0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
  0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
  0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
  0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
  0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
  0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
  0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
  0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
  0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
  0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
  0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
  0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
  0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
  0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
  0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
  0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
  0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
  0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
  0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
  0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
  0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
  0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
  0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
  0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
  0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
  0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
  0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
  0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
  0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
  0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
  0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
  0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
  0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
  0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
  0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
  0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
  0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
  0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
  0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
  0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
  0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
  0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};


unsigned int xcrc32 (const unsigned char *buf, int len, unsigned int init)
{
  unsigned int crc = init;
  while (len--)
    {
      crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ *buf) & 255];
      buf++;
    }
    
  return crc;
}

uint32 cal_mem_crc32(uint64 addr, uint32 size) {
    unsigned char* mem_data = (unsigned char *)malloc(size + 10);
    memcpy(mem_data, (void*)addr, size);
    if (mem_data == NULL) {
        LOGE("Failed to allocate memory for CRC calculation");
        return -1;
    }
    unsigned int init_value = 0xFFFFFFFF;
    unsigned int crc_value = xcrc32(mem_data, size, init_value);
    
    free(mem_data);
    return crc_value;
}







void* check_magisk1_crc_verify(void* arg){
    uint32 last_crc = 0xe9838d80;            // 0xa10ec2ab
    while (1) {
        sleep(4); 
       

        uint64 libUE4_base = get_base_so("libUE4.so"); 
        log("libUE4_base: %llx", libUE4_base);
        uint64 text_start = libUE4_base + 0x000000005AE7000;
        uint64 text_end = libUE4_base + 0x00000000A4EAC50;
        uint64 text_size = text_end - text_start;

        unsigned int current_crc = cal_mem_crc32(text_start, text_size);
        log("current crc: %x", current_crc);
        if (last_crc !=0){
            if(last_crc != current_crc){
                wlog("[crc check], zgisk hack!");
                log("[crc check], zgisk hack!");
            }
        }
        
    }
    return NULL;
}

void *check_magisk2_check_segment__permission(void* arg){
    uint64 libUE4_base = get_base_so("libUE4.so");
    uint64 invalid_seg = libUE4_base + 0x000000006711AC4;
    log("invalid_seg: %llx", invalid_seg);
    // 查询invalid_seg地址的权限
    while (1){
        FILE *fp = fopen("/proc/self/maps", "r");
        if (fp == NULL) {
            log("Failed to open /proc/self/maps");
            return NULL;
        }
        char line[256];
        while (fgets(line, sizeof(line), fp) != NULL) {
            if (strstr(line, "libUE4.so")) {
                unsigned long start, end;
                sscanf(line, "%lx-%lx", &start, &end);
                if (invalid_seg >= start && invalid_seg < end) {
                    // 该地址在libUE4.so的映射范围内
                    // 检查权限
                    if (strstr(line, "rwxp")) {
                        wlog("[seg permission check], zgisk hack!");
                        log("[seg permission check], zgisk hack!");
                    }
                }
            }
        }

        fclose(fp);
        sleep(5);

    }
    return NULL;
}





void test_log_file(){
    wlog("start check ...\n");
}



uint64 mincore_one(){
    uint64 so_base = get_base_so("libUE4.so");
    uint64 detect_addr1 = so_base + 0xAF75B08;
    log("detect_addr1: %llx", detect_addr1);

    return detect_addr1;
    
}



// mincore 钓鱼吧 
void* check_mincore(void* arg){

    sleep(3);
    // 页面地址替换,先睡3s,让系统进行完所有的初始化,我们再写。
    int num_pages = 1; 
    uint64 mincore_check_list[num_pages];


    mincore_check_list[0] = mincore_one();
    uint64 mincore_mem_addr[num_pages];
    size_t PS = sysconf(_SC_PAGESIZE);                             //获取系统的页面大小
    for(int i = 0; i < num_pages; ++i) {
        mincore_mem_addr[i] = (uint64)mmap(nullptr, PS, PROT_READ | PROT_WRITE,
            MAP_NORESERVE | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
        if ((void*)mincore_check_list[i] == MAP_FAILED) {
            log("Failed to allocate memory for mincore check list");
            return NULL;
        }
    }


    for(int i = 0; i < num_pages; ++i) {
        *(uint64 *)mincore_check_list[i] = mincore_mem_addr[i];
        log("mincore_check_list[%d]: %llx  = mincore_mem_addr[x]: %llx", i, mincore_check_list[i], mincore_mem_addr[i]);
    }


    // 循环检测
    while(1){
        sleep(3);

        for(int i = 0; i < num_pages; ++i) {
            unsigned char vec[2];
            int res = mincore((void*)mincore_mem_addr[i],  PS, vec);
            if (res == -1) {
                log("mincore failed");
                return NULL;;
            }
            log("mincore check: %d", (vec[0] & 1));
            if ((vec[0] & 1) == 1) {
                wlog("[mincore check], find cheat hack! ");
                log("[mincore check], find cheat hack!");
                return NULL; ;
            }
        }
    }


}


// 能监听 /proc/self/maps,但监听不了 /proc/self/mem?
void* check_maps(void * arg){
    int fd, wd_mem;
    char buffer[BUF_LEN];
    char mem_file[128] = "/proc/self/maps";
    sleep(3);

    // 创建 inotify 实例
    fd = inotify_init();
    if (fd == -1) {
        log("inotify_init");
        return NULL;
    }

    // 监视 /proc/<pid>/maps 和 /proc/<pid>/mem 文件的打开和修改操作
    wd_mem = inotify_add_watch(fd, mem_file, IN_ACCESS );
    if (wd_mem == -1) {
        log("inotify_add_watch error3");
        return NULL;
    }

    log("Monitoring /proc/self/mem for access and modifications...\n");

    while (1) {
        int length = read(fd, buffer, BUF_LEN);
        if (length == -1) {
            log("read error ");
            return NULL;
    
        }

        // 处理读取到的事件
        int i = 0;
        while (i < length) {
            struct inotify_event *event = (struct inotify_event *) &buffer[i];

            if (event->mask & IN_ACCESS) {
                log("/proc/self/maps/ %s was accessed. mb cheat / zgisk\n", event->name);
                wlog("[inotify /proc/self/maps/ check], find cheat hack!");
                
            }


            // log("just circle");
            i += sizeof(struct inotify_event) + event->len;
        }
    }

    // 关闭 inotify 实例
    close(fd);
}



// 检测设备抓取。通过 ioctl(handle, EVIOCGRAB, 1) 启用抓取,捕获返回的错误码。需要root权限
// 需要 root权限
void* check_EVIOCGRAB(void* arg){

    
    while(1){
        DIR *dir;
        struct dirent *entry;
        int handle;
        int result;
        sleep(3);
        // 打开 /dev/input 目录
        dir = opendir("/dev/input");
        if (dir == NULL) {
            log("Failed to open /dev/input directory , mb u need root ");
            return NULL;
        }
    
        // 遍历目录中的所有文件
        while ((entry = readdir(dir)) != NULL) {
            if (strncmp(entry->d_name, "event", 5) == 0) {
                char device_path[256];
                snprintf(device_path, sizeof(device_path), "/dev/input/%s", entry->d_name);
    
                handle = open(device_path, O_RDWR);
                if (handle == -1) {
                    log("Failed to open device, mb u need root");
                    continue; // 继续处理下一个设备
                }
    
                // 尝试启用设备抓取
                result = ioctl(handle, EVIOCGRAB, 1);
                if (result == -1) {
                    log("Failed to grab device");
                    char *str = (char *)malloc(128);
                    sprintf(str, "[grab check], find cheat open! %s.",device_path);
                    free(str);
                    wlog(str);
                } else {
                    log("Successfully grabbed device");
                    // 禁用抓取(释放设备)
                    ioctl(handle, EVIOCGRAB, 0);
                }
    
                // 关闭设备文件
                close(handle);
            }
        }
    
        // 关闭目录
        closedir(dir);
    }

}



void get_device_name(const char *device, char *name) {
    int fd = open(device, O_RDONLY);
    if (fd == -1) {
        log("Failed to open device");
        return;
    }

    // 获取设备名称
    if (ioctl(fd, EVIOCGNAME(sizeof(name)), name) == -1) {
        log("Failed to get device name: %s", strerror(errno));
    }

    close(fd);
}



// 保存设备名称与数目
void res_device_name() {
    DIR *dir = opendir("/dev/input");
    if (dir == NULL) {
        printf("Failed to open /dev/input\n");
        return;
    }

    struct dirent *entry;
    
    while ((entry = readdir(dir)) != NULL) {
    char device_name[MAX_NAME_LEN]={0};

        if (strncmp(entry->d_name, "event", 5) == 0) {
            char device_path[PATH_MAX];
            snprintf(device_path, sizeof(device_path), "/dev/input/%s", entry->d_name);

            // 获取设备名称
            get_device_name(device_path, device_name);
            printf("Device name: %s\n", device_name);
            strcpy((char*)&res_device_name_list[res_device_count], device_name);
            
            res_device_count++;
        }
    }
    printf("res_device_count: %d\n", res_device_count);

    closedir(dir);
}
void * check_input_devices(void* arg) {

  
    
    while(1){
        sleep(3);
        DIR *dir = opendir("/dev/input");
        if (dir == NULL) {
            log("Failed to open /dev/input: %s , mb u need root", strerror(errno));
            return NULL;
        }
    
        struct dirent *entry;
        char device_name[MAX_NAME_LEN];
        int device_count = 0;
        
        while ((entry = readdir(dir)) != NULL) {
            if (strncmp(entry->d_name, "event", 5) == 0) {
                char device_path[PATH_MAX];
                snprintf(device_path, sizeof(device_path), "/dev/input/%s", entry->d_name);
    
                // 获取设备名称
                get_device_name(device_path, device_name);
                int tag = 0;
                for(int i = 0;i<res_device_count;i++){
                    if (strcmp(device_name, res_device_name_list[i]) == 0) {
                        tag = 1;
                    }
                }
                if(tag == 0){
                    if(strlen(device_name) == 8){
                        char *str = (char *)malloc(128);
                        sprintf(str, "[input devices check], find cheat open! %s.",device_name);
                        wlog(str);
                        free(str);
                    }
                }
    
                device_count++;
            }
        }
    
        closedir(dir);
    }

   
}

// 监控驱动设备的删除与打开,
void *check_detect_device(void* arg){
    int fd, wd;
    char buffer[BUF_LEN];
    // 创建 inotify 实例
    fd = inotify_init();
    if (fd == -1) {
        log("inotify_init error");
        return NULL ;
    }
  
    // UI_DEV_CREATE
    // 监视 /dev/input 目录下的文件
    wd = inotify_add_watch(fd, "/dev/input",    IN_CREATE | IN_DELETE);
    if (wd == -1) {
        log("inotify_add_watch error: %s\n, mb u need root", strerror(errno));
        return NULL ;
    }
    log("Monitoring /dev/input for access and modification events...\n");
    // 持续监听事件
    while (1) {
        int length = read(fd, buffer, BUF_LEN);
        if (length == -1) {
            log("read error !!");
            return NULL ;
        }
  
        int i = 0;
        while (i < length) {
            struct inotify_event *event = (struct inotify_event *) &buffer[i];

            if (event->mask & IN_CREATE) {
                log("File %s was created.\n", event->name);
                char *str = (char *)malloc(128);
                sprintf(str, "[inotify input check], find cheat open! %s create.",event->name);
                wlog(str);
                free(str);
            }
            if (event->mask & IN_DELETE) {
                log("File %s was deleted.\n", event->name);
                char *str = (char *)malloc(128);
                sprintf(str, "[inotify input check], find cheat close! %s delete.",event->name);
                wlog(str);
                free(str);
            }
            i += sizeof(struct inotify_event) + event->len;
        }
    }
    close(fd);
    return NULL ;
}





// 需要 root 权限
void check_uinput(){
    
    int fd, wd;
    char buffer[BUF_LEN];
    sleep(3);
    // 创建 inotify 实例
    fd = inotify_init();
    if (fd == -1) {
        log("inotify_init");
        return ;
    }
    // 监视 /dev/input 目录下的文件
    wd = inotify_add_watch(fd, "/dev/uinput",  IN_OPEN | IN_CREATE | IN_DELETE);
    if (wd == -1) {
        log("inotify_add_watch error1");
        log("inotify_add_watch error: %s\n", strerror(errno));
        return ;
    }

    log("Monitoring /dev/uinput for access and modification events...\n");

    // 持续监听事件
    while (1) {
        int length = read(fd, buffer, BUF_LEN);
        if (length == -1) {
            log("read error !!");
            return ;
        }

        int i = 0;
        while (i < length) {
            struct inotify_event *event = (struct inotify_event *) &buffer[i];


            if (event->mask & IN_OPEN) {
                log("File %s was opened.\n", event->name);
                char *str = (char *)malloc(128);
                sprintf(str, "[inotify uinput check], find cheat open! %s open.",event->name);
                wlog(str);
                free(str);
            }
            if (event->mask & IN_CREATE) {
                log("File %s was create \n", event->name);
                char *str = (char *)malloc(128);
                sprintf(str, "[inotify uinput check], find cheat create! %s open.",event->name);
                wlog(str);
                free(str);
            }
            if (event->mask & IN_DELETE) {
                log("File %s was deleted.\n", event->name);
                char *str = (char *)malloc(128);
                sprintf(str, "[inotify uinput check], find cheat delete! %s delete.",event->name);
                wlog(str);
                free(str);
            }
            i += sizeof(struct inotify_event) + event->len;
        }
    }

    close(fd);
}




// void *check_cheat(void* arg){
//     check_mincore();
//     check_detect_device();
//     // check_maps();
//     // check_EVIOCGRAB();
//     // check_uinput();
//     return NULL;
// }



void get_file_path(char *dirpath) {
    Dl_info dl_info;
    if (dladdr((void*)get_file_path, &dl_info)) {
        // 获取文件路径
        char path[PATH_MAX] = {0};
        strcpy(path, dl_info.dli_fname);       
        // 过滤得到目录
        char *pppath = strrchr(path, '/');         
        if (pppath != NULL) {
            *pppath = '\0'; // 将最后一个斜杠替换为字符串结束符
        }
        // 赋给dirpath
        strcpy(dirpath, path);
        // 打印文件路径
        printf("File path: %s\n", dirpath);

    } else {
        printf("Failed to get library path.\n");
    }
}


void* root_exec(void*arg){
    while (1)
    {
        sleep(3);


        char *dirpath = (char*)malloc(PATH_MAX);
        char * nee_root_path = (char*)malloc(PATH_MAX);
        get_file_path(dirpath);
        strcpy(nee_root_path, dirpath);
        strcat(nee_root_path, "/need_root");
        log("need_root_path: %s\n", nee_root_path);
        free(dirpath);
        
        char command[256] = {0};
        snprintf(command, sizeof(command), "su -c  %s", nee_root_path);
        free(nee_root_path);
        FILE* fp = popen(command, "r");
        char output[1024];
        if (fp == NULL) {
            log("popen failed");
            return NULL;
        }
        // Read the output of the command line by line
        while (fgets(output, sizeof(output), fp) != NULL) {
            if(strstr(output,"[input devices check]") != NULL){
                wlog(output);
                log(output);
            }else if (strstr(output,"[grab check]") != NULL){
                wlog(output);
                log(output);
            }else if (strstr(output,"[inotify input check]") != NULL){
                wlog(output);
                log(output);
            }        
            
        }
    
        // Close the pipe
        fclose(fp);
    
    }
    return NULL;
    
    
}
void init_main_func() {

    get_base_so("libUE4.so");
    
    init_log_file();
    test_log_file();
    log("Load libAnswer.so");
    pthread_t thread_id;

    // 1、crc 内存校验
    if (pthread_create(&thread_id, NULL, check_magisk1_crc_verify, NULL) != 0) {
        log("Failed to create thread");
        return ;
    }


    // 2、段权限检测
    pthread_t thread_id2;
    if (pthread_create(&thread_id2, NULL, check_magisk2_check_segment__permission, NULL) != 0) {
        log("Failed to create thread");
        return ;
    }


    // 3、inotify 监控,当然如果要架空 /proc/self/maps的话,就与 段权限检测冲突了。
    // pthread_t thread_id3;
    // if (pthread_create(&thread_id3, NULL, check_maps, NULL) != 0) {
    //     log("Failed to create thread3");
    //     return ;
    // }



    // 4、mincore检测
    pthread_t thread_id4;
    if (pthread_create(&thread_id4, NULL, check_mincore, NULL) != 0) {
        log("Failed to create thread2");
        return ;
    }


    // // 5、inotify监控 /dev/input
    // pthread_t thread_id5;
    // if (pthread_create(&thread_id5, NULL, check_detect_device, NULL) != 0) {
    //     log("Failed to create thread2");
    //     return ;
    // }


    // // 6、检测虚拟设备的名称与数目
    // // res_device_name();
    // pthread_t thread_id6;
    // if (pthread_create(&thread_id6, NULL, check_input_devices, NULL) != 0) {
    //     log("Failed to create thread2");
    //     return ;
    // }


    // // // 7、检测虚拟设备的抓取
    // pthread_t thread_id7;
    // if (pthread_create(&thread_id7, NULL, check_EVIOCGRAB, NULL) != 0) {
    //     log("Failed to create thread2");
    //     return ;
    // }
    


    // 使用root权限执行下面命令:
    
    pthread_t thread_id8;
    if(pthread_create(&thread_id8, NULL, root_exec, NULL) != 0) {
        log("Failed to create thread2");
        return ;
    }
}
    

// adb shell pm path  com.ACE2025.Game
// D:/tlsn/compiler/Android_studio/SDK/ndk/27.0.12077973/toolchains/llvm/prebuilt/windows-x86_64/bin/aarch64-linux-android24-clang.cmd ./libAnswer.cpp -fPIC -shared -g -o ./libAnswer.so -llog

need_root.cpp

#include <cstdio>
#include <cstdlib>
#include <cstddef>
#include <dirent.h>
#include <unistd.h>
#include <cmath>
#include <ctime>
#include <algorithm>
#include <string>
#include <list>
#include <vector>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <getopt.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/uio.h>
#include <sys/syscall.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/inotify.h>
#include <fcntl.h>
#include <linux/input.h>
#define MAX_NAME_LEN 256
#define TAG "tlsn"
typedef unsigned char uint8;
typedef unsigned short int uint16;
typedef unsigned int uint32;
typedef unsigned long long uint64;
typedef signed char int8;
typedef signed short int int16;
typedef signed int int32;
typedef signed long long int64;
typedef uintptr_t kaddr;
#define BUF_LEN (1024 * (sizeof(struct inotify_event) + 16))
char *res_device_name_list[20];
int res_device_count =0; 
const char* printfpath = "/data/data/com.ACE2025.Game/printf.txt";
void get_device_name(const char *device, char *name) {
    int fd = open(device, O_RDONLY);
    if (fd == -1) {
        printf("Failed to open device\n");
        return;
    }

    // 获取设备名称
    if (ioctl(fd, EVIOCGNAME(MAX_NAME_LEN), name) == -1) {
        printf("Failed to get device name: %s\n", strerror(errno));
    }

    close(fd);
}

void * check_input_devices(void* arg) {

    while(1){
        sleep(3);
        DIR *dir = opendir("/dev/input");
        if (dir == NULL) {
            printf("Failed to open /dev/input: %s , mb u need root\n", strerror(errno));
            return NULL;
        }
    
        struct dirent *entry;
        char device_name[MAX_NAME_LEN];
        int device_count = 0;
        
        while ((entry = readdir(dir)) != NULL) {
            if (strncmp(entry->d_name, "event", 5) == 0) {
                char device_path[PATH_MAX];
                snprintf(device_path, sizeof(device_path), "/dev/input/%s", entry->d_name);
    
                // 获取设备名称
                get_device_name(device_path, device_name);
                int tag = 0;
                for(int i = 0;i<res_device_count;i++){
                    // printf("device_name: %s cmp %s \n", device_name, (const char*)res_device_name_list[i]);
                    if (strcmp(device_name, (const char*)res_device_name_list[i]) == 0) {
                        tag = 1;
                    }
                }
                if(tag == 0){
                    if(strlen(device_name) == 8){
                        char *str = (char *)malloc(128);
                        fflush(stdout);
                        sprintf(str, "[input devices check], find cheat open! %s.",device_name);
                        printf("%s\n", str);
                        fflush(stdout);
                        free(str);
                    }
                }
    
                device_count++;
            }
        }
    
        closedir(dir);
    }

   
}



void res_device_name() {
    DIR *dir = opendir("/dev/input");
    if (dir == NULL) {
        printf("Failed to open /dev/input\n");
        return;
    }

    struct dirent *entry;
    
    while ((entry = readdir(dir)) != NULL) {
    char device_name[MAX_NAME_LEN]={0};

        if (strncmp(entry->d_name, "event", 5) == 0) {
            char device_path[PATH_MAX];
            snprintf(device_path, sizeof(device_path), "/dev/input/%s", entry->d_name);

            // 获取设备名称
            get_device_name(device_path, device_name);
            res_device_name_list[res_device_count] = (char*)malloc(MAX_NAME_LEN);
            strcpy(res_device_name_list[res_device_count], device_name);
            // printf("Device name: %s\n", res_device_name_list[res_device_count]);
            
            res_device_count++;
        }
    }
    // printf("res_device_count: %d\n", res_device_count);

    closedir(dir);
}



void* check_EVIOCGRAB(void* arg){

    
    while(1){
        DIR *dir;
        struct dirent *entry;
        int handle;
        int result;
        sleep(3);
        // 打开 /dev/input 目录
        dir = opendir("/dev/input");
        if (dir == NULL) {
            printf("Failed to open /dev/input directory , mb u need root ");
            return NULL;
        }
    
        // 遍历目录中的所有文件
        while ((entry = readdir(dir)) != NULL) {
            if (strncmp(entry->d_name, "event", 5) == 0) {
                char device_path[256];
                snprintf(device_path, sizeof(device_path), "/dev/input/%s", entry->d_name);
    
                handle = open(device_path, O_RDWR);
                if (handle == -1) {
                    // printf("Failed to open device, mb u need root");
                    continue; // 继续处理下一个设备
                }
    
                // 尝试启用设备抓取
                result = ioctl(handle, EVIOCGRAB, 1);
                if (result == -1) {
                    // printf("Failed to grab device");
                    char *str = (char *)malloc(128);
                    fflush(stdout);
                    sprintf(str, "[grab check], find cheat open! %s.",device_path);
                    free(str);
                    printf("%s\n",str);
                    fflush(stdout);
                } else {
                    // printf("Successfully grabbed device");
                    // 禁用抓取(释放设备)
                    ioctl(handle, EVIOCGRAB, 0);
                }
    
                // 关闭设备文件
                close(handle);
            }
        }
    
        // 关闭目录
        closedir(dir);
    }

}


void *check_detect_device(void* arg){
    int fd, wd;
    char buffer[BUF_LEN];
    // 创建 inotify 实例
    fd = inotify_init();
    if (fd == -1) {
        printf("inotify_init error");
        return NULL ;
    }
  
    // UI_DEV_CREATE
    // 监视 /dev/input 目录下的文件
    wd = inotify_add_watch(fd, "/dev/input",    IN_CREATE | IN_DELETE);
    if (wd == -1) {
        printf("inotify_add_watch error: %s\n, mb u need root", strerror(errno));
        return NULL ;
    }
    printf("Monitoring /dev/input for access and modification events...\n");
    // 持续监听事件
    while (1) {
        int length = read(fd, buffer, BUF_LEN);
        if (length == -1) {
            printf("read error !!");
            return NULL ;
        }
  
        int i = 0;
        while (i < length) {
            struct inotify_event *event = (struct inotify_event *) &buffer[i];

            if (event->mask & IN_CREATE) {
                // printf("File %s was created.\n", event->name);
                char *str = (char *)malloc(128);
                fflush(stdout);
                sprintf(str, "[inotify input check], find cheat open! %s create.",event->name);
                printf("%s\n",str);
                fflush(stdout);
                free(str);
            }
            if (event->mask & IN_DELETE) {
                // printf("File %s was deleted.\n", event->name);
                char *str = (char *)malloc(128);
                fflush(stdout);
                sprintf(str, "[inotify input check], find cheat close! %s delete.",event->name);
                printf("%s\n",str);
                fflush(stdout);
                free(str);
            }
            i += sizeof(struct inotify_event) + event->len;
        }
    }
    close(fd);
    return NULL ;
}


int main(){
    res_device_name();
    pthread_t thread_id5;
    if (pthread_create(&thread_id5, NULL, check_detect_device, NULL) != 0) {
        printf("Failed to create thread2\n");
        return 0;
    }

    pthread_t thread_id6;
    if (pthread_create(&thread_id6, NULL, check_input_devices, NULL) != 0) {
        printf("Failed to create thread2\n");
        return 0;
    }

    pthread_t thread_id7;
    if (pthread_create(&thread_id7, NULL, check_EVIOCGRAB, NULL) != 0) {
        printf("Failed to create thread2\n");
        return 0;
    }
    
    
    if(pthread_join(thread_id5, NULL) != 0) {
        printf("Failed to join thread\n");
        return 0;
    }
        
    if (pthread_join(thread_id6, NULL) != 0) {
        printf("Failed to join thread\n");
        return 0;
    }


    if (pthread_join(thread_id7, NULL) != 0) {
        printf("Failed to join thread\n");
        return 0;
    }
    
    
}


// D:/tlsn/compiler/Android_studio/SDK/ndk/27.0.12077973/toolchains/llvm/prebuilt/windows-x86_64/bin/aarch64-linux-android24-clang.cmd ./input_device.cpp -g -o ./input_device 

posted @ 2025-04-22 19:58  TLSN  阅读(320)  评论(0)    收藏  举报