-
-
[原创][分享]某安全加固趣事分享
-
发表于: 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小时前
被嘎嘎真的很棒编辑
,原因: 增加代码
赞赏
他的文章
- [原创][分享]某安全加固趣事分享 88
- [原创]第六届金盾信安Reverse-WP 6739
- [原创]2024网鼎-Reveres-WP 31941
- [原创]DASCTF2024-金秋十月-REEZ-WP-特详细 6731
- [原创]嘟嘟牛在线分析-RPC调用 2889
赞赏
雪币:
留言: