首页
社区
课程
招聘
[原创][分享]某安全加固趣事分享
发表于: 2小时前 87

[原创][分享]某安全加固趣事分享

2小时前
87

该样本很早之前就分析了,前几天样本更新了,打开之后会直接闪退,这与之前所有版本都不太一样,猜测是更新了检测ROOT的方式,故又分析了一下。

该加固检测ROOT的方式是通过多次syscall调用__NR_faccessat和__NR_fchownat计算其差值判断是否处于ROOT环境的。

但是这种检测方式并不稳定,且在最新版的KernelSu中已经被修复了。

有趣的来了,4.2号的时候发布的版本是带有这个ROOT检测的,4.9号的时候APP更新了。

让我感到搞笑的是更新日志是这样写的:修复部分设备进入闪退问题

我一看到这个就猜到了绝对是因为ROOT检测,下载下来分析,果不其然,直接把加固SO文件换成旧版本的SO了。

检测代码AI复现如下

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sched.h>
#include <stdint.h>
#include <sys/utsname.h>
#include <sys/syscall.h>

/* ── ARM64 syscall numbers ── */
#ifndef __NR_faccessat
#define __NR_faccessat  48
#endif
#ifndef __NR_fchownat
#define __NR_fchownat   54
#endif
#ifndef __NR_gettid
#define __NR_gettid     178
#endif

/* ── 读取 ARM 硬件计数器 CNTVCT_EL0(纳秒级精度)── */
static inline uint64_t read_cntvct(void)
{
    uint64_t val;
    __asm__ __volatile__("mrs %0, cntvct_el0" : "=r"(val));
    return val;
}

/* ── 读取计数器频率 CNTFRQ_EL0(用于换算为时间)── */
static inline uint64_t read_cntfrq(void)
{
    uint64_t val;
    __asm__ __volatile__("mrs %0, cntfrq_el0" : "=r"(val));
    return val;
}

/* ── qsort 比较函数 ── */
static int cmp_u64(const void *a, const void *b)
{
    uint64_t va = *(const uint64_t *)a;
    uint64_t vb = *(const uint64_t *)b;
    return (va > vb) - (va < vb);
}

/* ── 查找最快的 CPU 大核 ── */
static int find_fastest_core(void)
{
    int num_cpus = (int)sysconf(_SC_NPROCESSORS_CONF);
    int best_core = 0;
    int best_freq = 0;

    for (int i = 0; i < num_cpus; i++) {
        char path[256];
        snprintf(path, sizeof(path),
                 "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", i);

        FILE *f = fopen(path, "r");
        int freq = -1;
        if (f) {
            fscanf(f, "%d", &freq);
            fclose(f);
        }

        if (freq > best_freq) {
            best_freq = freq;
            best_core = i;
        }
    }

    printf("[*] CPU cores: %d, fastest: CPU%d (%d KHz)\n",
           num_cpus, best_core, best_freq);
    return best_core;
}

/* ── 将当前线程绑定到指定 CPU 核心 ── */
static int pin_to_core(int core)
{
    cpu_set_t set;
    CPU_ZERO(&set);
    CPU_SET(core, &set);

    pid_t tid = (pid_t)syscall(__NR_gettid);
    if (tid == -1)
        tid = getpid();

    return sched_setaffinity(tid, sizeof(set), &set);
}

/**
 * detect_syscall_timing - 核心检测函数(复现 sub_301EC)
 * 
 * @n: 采样次数
 * @return: faccessat 比 fchownat 慢的次数
 *          正常内核 → 返回值远小于 n*70%
 *          hook内核 → 返回值接近 n(99%+)
 */
static int detect_syscall_timing(int n)
{
    uint64_t *times_faccessat = (uint64_t *)calloc(n, sizeof(uint64_t));
    uint64_t *times_fchownat  = (uint64_t *)calloc(n, sizeof(uint64_t));

    if (!times_faccessat || !times_fchownat) {
        free(times_faccessat);
        free(times_fchownat);
        return -1;
    }

    /* 保存原始 CPU 亲和性 */
    cpu_set_t orig_set;
    int saved = sched_getaffinity(0, sizeof(orig_set), &orig_set);

    /* 绑定到最快的大核 */
    int core = find_fastest_core();
    pin_to_core(core);

    /*
     * 测量循环 1:faccessat(48)
     * 参数全部无效 → 内核立即返回错误
     * 但如果 KernelSU hook 了这个 syscall,会有额外开销
     */
    for (int i = 0; i < n; i++) {
        uint64_t t0 = read_cntvct();
        syscall(__NR_faccessat, 0xFFFFFFFF, NULL, 0xFFFFFFFF, 0);
        times_faccessat[i] = read_cntvct() - t0;
    }

    /*
     * 测量循环 2:fchownat(54)
     * 同样参数无效 → 立即返回错误
     * 作为基准参照(通常不会被 hook 或 hook 开销更小)
     */
    for (int i = 0; i < n; i++) {
        uint64_t t0 = read_cntvct();
        syscall(__NR_fchownat, 0xFFFFFFFF, NULL, 0, 0, 0xFFFFFFFF);
        times_fchownat[i] = read_cntvct() - t0;
    }

    /* 恢复原始 CPU 亲和性 */
    if (saved == 0)
        sched_setaffinity(0, sizeof(orig_set), &orig_set);

    /* 排序——消除偶发的调度噪声 */
    qsort(times_faccessat, n, sizeof(uint64_t), cmp_u64);
    qsort(times_fchownat,  n, sizeof(uint64_t), cmp_u64);

    /* 逐一对比:统计 faccessat 比 fchownat 慢超过 1 tick 的次数 */
    int count = 0;
    for (int i = 0; i < n; i++) {
        if (times_faccessat[i] > times_fchownat[i] + 1)
            count++;
    }

    /* 输出前10个样本供参考 */
    printf("    [samples] faccessat vs fchownat (first 10, sorted):\n");
    for (int i = 0; i < 10 && i < n; i++) {
        printf("      [%d] %llu vs %llu  %s\n",
               i,
               (unsigned long long)times_faccessat[i],
               (unsigned long long)times_fchownat[i],
               times_faccessat[i] > times_fchownat[i] + 1 ? "← SLOWER" : "");
    }

    free(times_faccessat);
    free(times_fchownat);
    return count;
}

/* ── 检查内核版本是否 >= 4.14(原版条件)── */
static int check_kernel_version(void)
{
    struct utsname uts;
    if (uname(&uts) != 0)
        return 1; /* uname 失败时仍然执行检测 */

    printf("[*] Kernel: %s\n", uts.release);

    char *release = strdup(uts.release);
    char *saveptr;

    char *tok1 = strtok_r(release, ".", &saveptr);
    int major = tok1 ? atoi(tok1) : 0;

    char *tok2 = strtok_r(NULL, ".", &saveptr);
    int minor = tok2 ? atoi(tok2) : 0;

    /* 消费掉 "-xxx" 后缀 */
    strtok_r(NULL, "-", &saveptr);
    free(release);

    printf("[*] Parsed version: %d.%d\n", major, minor);

    if (major > 4 || (major == 4 && minor >= 14))
        return 1;

    printf("[!] Kernel < 4.14, original BangBang would skip this check\n");
    return 0;
}

int main(int argc, char *argv[])
{
    int n = 10000;
    if (argc > 1)
        n = atoi(argv[1]);

    uint64_t freq = read_cntfrq();
    printf("[*] Timer frequency: %llu Hz (%.1f MHz)\n\n",
           (unsigned long long)freq, freq / 1e6);

    if (!check_kernel_version()) {
        printf("[*] Running anyway for demonstration...\n\n");
    }
    printf("\n");

    /* 三轮测试*/
    printf("── Test 1: n=%d ──\n", n);
    int r1 = detect_syscall_timing(n);

    printf("\n── Test 2: n=%d ──\n", n / 2);
    int r2 = detect_syscall_timing(n / 2);

    printf("\n── Test 3: n=%d ──\n", n / 2);
    int r3 = detect_syscall_timing(n / 2);

    /* ── 汇总结果 ── */
    printf("\n╔══════════════════════════════════════════════════╗\n");
    printf("║                    Results                       ║\n");
    printf("╠══════════════════════════════════════════════════╣\n");
    printf("║ Test 1: %5d / %5d  (%5.1f%%)                  ║\n",
           r1, n, r1 * 100.0 / n);
    printf("║ Test 2: %5d / %5d  (%5.1f%%)                  ║\n",
           r2, n / 2, r2 * 100.0 / (n / 2));
    printf("║ Test 3: %5d / %5d  (%5.1f%%)                  ║\n",
           r3, n / 2, r3 * 100.0 / (n / 2));
    printf("╚══════════════════════════════════════════════════╝\n\n");


    /*
     * 按比例缩放到用户指定的 n
     */
    int t1 = (int)((long long)n      * 7001 / 10000);
    int t2 = (int)((long long)(n / 2) * 3500 / 5000);
    int t3 = (int)((long long)(n / 2) * 3501 / 5000);

    printf("── BangBang Detection Thresholds (~70%%) ──\n");
    printf("  Test 1: %d >= %d ? %s\n", r1, t1,
           r1 >= t1 ? "⚠ TRIGGERED" : "✅ CLEAN");
    printf("  Test 2: %d >  %d ? %s\n", r2, t2,
           r2 >  t2 ? "⚠ TRIGGERED" : "✅ CLEAN");
    printf("  Test 3: %d >= %d ? %s\n", r3, t3,
           r3 >= t3 ? "⚠ TRIGGERED" : "✅ CLEAN");

    printf("\n");
    if (r1 >= t1 && r2 > t2 && r3 >= t3) {
        printf("╔══════════════════════════════════════════════════╗\n");
        printf("║  ⚠  DETECTED: Kernel syscall table is hooked!   ║\n");
        printf("║     Possible: KernelSU / Magisk / other root    ║\n");
        printf("╚══════════════════════════════════════════════════╝\n");
    } else {
        printf("╔══════════════════════════════════════════════════╗\n");
        printf("║  ✅  CLEAN: No kernel syscall hook detected      ║\n");
        printf("╚══════════════════════════════════════════════════╝\n");
    }

    return 0;
}

传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 1小时前 被嘎嘎真的很棒编辑 ,原因: 增加代码
收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回