应用安全 --- 安卓加固 之 调试 反调试 反反调试
调试就是使用调试工具给软件下断点一步一步动态分析内存、寄存器和堆栈变化的程序指令执行追踪的一种手段。
简单来讲就是在软件运行时做手术,解刨内部构造
第一部分:调试方法
调试的核心目的是理解程序的行为、定位并修复错误。可以分为静态分析和动态分析两大类。
1. 静态分析
在不运行程序的情况下分析代码。
-
源代码分析:直接阅读源代码,这是最直接的方式。
-
反汇编:将可执行文件(二进制)转换回汇编代码。工具:IDA Pro, Ghidra, Binary Ninja。
-
反编译:尝试将二进制代码转换回更高级的、类似C的伪代码。工具:Ghidra, Hex-Rays Decompiler (IDA Pro插件), RetDec。
-
字符串分析:提取二进制文件中的明文字符串,寻找线索(如错误信息、API函数名、硬编码的密钥等)。
-
依赖项分析:检查程序调用了哪些外部库(DLL, so文件),这可以提示程序的功能。
2. 动态分析
在程序运行时进行分析,这是“调试”最核心的部分。
-
交互式调试器:
-
功能:设置断点、单步执行、查看和修改内存与寄存器、观察调用堆栈。
-
用户态调试器:用于分析普通应用程序。
-
Windows: x64dbg, OllyDbg, WinDbg (用户态模式)
-
Linux/macOS: GDB (GNU Debugger), LLDB
-
-
内核态调试器:用于分析操作系统内核和驱动程序。
-
Windows: WinDbg (内核态模式,通过双机调试)
-
Linux: KGDB
-
-
-
跟踪
-
指令跟踪:记录每一条被执行的指令,数据量巨大但信息最全。
-
函数调用跟踪:记录函数的调用和返回,了解程序执行流。工具:LTrace (Linux), Strace (系统调用跟踪)。
-
-
内存转储
-
在程序运行的特定时刻(如输入了关键数据后)将其整个内存空间保存到文件中,然后用静态分析工具进行离线分析。工具:ProcDump。
-
-
动态插桩
-
使用工具在程序运行时注入代码,以监控其行为。工具:Intel PIN, DynamoRIO。它们可以用于分析代码覆盖率、记录内存访问等。
-
-
模拟器/沙箱
-
在完全受控的虚拟环境中运行程序,可以不受限制地观察其所有行为。工具:QEMU。
-
第二部分:反调试方法
反调试技术的目的是阻止或干扰调试器的正常工作,增加分析和逆向工程的难度。这些技术利用了调试器与正常执行环境之间的细微差别。
1. 基于API的检测
-
Windows:
-
IsDebuggerPresent(): 最简单的API,检查当前进程是否被调试。 -
CheckRemoteDebuggerPresent(): 检查本地或远程进程是否被调试。 -
NtQueryInformationProcess(): 查询进程信息,传入ProcessDebugPort等标志来检测调试器。
-
-
Linux:
-
使用
ptrace进行自我附加。因为一个进程只能被一个调试器ptrace,如果自己ptrace(PTRACE_TRACEME, ...)失败,说明已经被调试。
-
2. 基于时间和时序的检测
-
时间差检查:在代码段开始和结束时记录时间戳。如果时间间隔过长(因为单步执行或断点),则判定被调试。使用
rdtsc指令(读取时间戳计数器)或GetTickCount()等API。 -
陷阱标志检测:利用单步执行会触发陷阱标志的特性,故意设置一个异常处理程序,然后设置陷阱标志。如果程序正常执行,会触发异常;如果被单步执行,调试器会接管异常,程序流程就会不同。
3. 断点检测
-
软件断点检测:软件断点通过将原指令替换为
INT 3(机器码0xCC)实现。可以检查关键代码段(如校验和)是否被修改,或者直接搜索内存中是否存在0xCC。 -
硬件断点检测:通过
GetThreadContext()等API检查线程上下文中的调试寄存器(DR0-DR3)是否被设置。
4. 进程和窗口检测
-
查找常见的调试器进程名(如“ollydbg.exe”, “x64dbg.exe”, “idaq.exe”, “gdb”)。
-
查找调试器对应的窗口类名或标题。
5. 异常干扰
-
制造无处理程序的异常:在正常流程中插入一个无效指令。如果没有调试器,程序的全局异常处理程序会接管;如果有调试器,调试器会首先收到异常通知。如果调试器选择“不处理并传递给程序”,其响应时间与正常执行不同,可以被检测到。
-
SEH(结构化异常处理)混淆:构建复杂的SEH链,使得调试器在解析时发生错误或行为与真实环境不一致。
6. 代码自修改与加壳/混淆
-
加壳:原始代码被压缩或加密,外面包裹一层“壳”程序。运行时,壳程序先解密原始代码到内存再执行。这大大增加了静态分析的难度。
-
代码混淆:通过花指令、控制流扁平化等技术,使反汇编代码变得极其复杂和难以阅读。
-
多态/变形代码:代码在每次运行时都会自我改变,但功能不变。
7. 虚拟机/模拟器检测
-
通过特定的指令(如
CPUID)或行为特征(如特定硬件端口、时间特性)来检测程序是否运行在VMware、VirtualBox等虚拟机或QEMU等模拟器中。分析人员常用虚拟机做沙箱分析,因此这也是一种反分析手段。
第三部分:反反调试方法
这是分析者为了绕过反调试技术而采取的对策,是一场永无休止的“猫鼠游戏”。
1. 对抗API检测
-
API Hook/打补丁:修改目标程序的二进制代码,将反调试API的调用指令替换为空操作(NOP)或直接让其返回0(表示“未被调试”)。
-
调试器插件:使用像ScyllaHide、TitanHide这样的插件,它们可以 hook 系统底层,当目标程序调用反调试API时,向它返回一个“未被调试”的假结果。
-
手动修改标志位:在调试器中,找到存储调试标志的内存位置或寄存器,并在检查前手动将其修改为0。
2. 对抗时间检测
-
隐藏调试器:一些高级调试器或插件可以隐藏自己的时间痕迹,使时间差检测失效。
-
手动跳过:通过静态分析找到时间检查代码,直接修改跳转指令,使其永远不跳转到“发现调试”的分支。
3. 对抗断点检测
-
硬件断点:使用数量有限但不会被检测的硬件断点。
-
内存断点:使用调试器的内存访问/写入断点功能,而不是修改代码。
-
条件断点:设置只在特定条件下触发的断点,减少对程序正常流程的干扰。
4. 对抗进程/窗口检测
-
重命名调试器:将x64dbg.exe改名为notepad.exe。
-
自定义编译调试器:编译一个自己修改过的、进程名和窗口特征不为人知的调试器。
-
插件隐藏:使用插件来清除调试器进程的特定特征。
5. 处理异常
-
正确配置调试器:仔细配置调试器的异常处理选项,确保在反调试代码制造异常时,调试器能“聪明地”将其传递给程序,或者按照分析者期望的方式处理。
-
使用脚本/插件:编写脚本自动处理已知的反调试异常。
6. 对抗加壳与混淆
-
脱壳:动态调试壳程序,在其将原始代码解密到内存并跳转执行时,将内存中的完整代码转储(Dump)出来,得到一个可被静态分析的“脱壳”后文件。
-
手动脱壳:使用调试器一步步跟踪找到原始程序入口点(OEP)。
-
自动脱壳:使用专用脱壳工具或调试器脚本。
-
-
动态分析为主:当静态分析因混淆而变得困难时,更多地依赖动态调试,在运行时观察关键数据和行为。
7. 对抗虚拟机检测
-
在真机中分析:在真实的物理机器上进行分析,但这有安全风险。
-
修改虚拟机:配置虚拟机,隐藏其典型的特征(如修改VMware的BIOS信息、禁用加速功能等)。
-
打补丁:找到并修改或绕过程序中的虚拟机检测代码。
总结
这是一个典型的“矛与盾”的循环:
-
调试是理解程序的“手术刀”。
-
反调试是程序为了保护自己而穿上的“盔甲”。
-
反反调试是分析者为了剥开“盔甲”而使用的更精密的“工具”。
这场技术博弈推动着软件安全和逆向工程领域的不断发展。对于软件开发者,了解反调试可以更好地保护自己的知识产权;对于安全研究员和恶意软件分析师,掌握反反调试则是揭开恶意软件面纱、保护网络安全的必备技能。
在逆向工程(Reverse Engineering)和恶意代码分析的攻防博弈中,调试、反调试与反-反调试构成了最激烈的对抗环节。
以下是为您详细梳理的技术体系:
1. 调试方法 (Debugging Techniques) —— 手术刀与显微镜
这是分析者用来解剖程序的手段,旨在完全控制程序的执行流程。
核心断点技术
断点(Breakpoints)是调试的灵魂,用于在特定位置暂停程序。
-
软件断点 (Software Breakpoints / CC断点):
-
原理: 调试器将目标指令的第一个字节修改为
0xCC(汇编指令INT 3)。CPU 执行到此时触发异常,控制权交给调试器。 -
特点: 数量无限,但修改了内存代码,容易被检测(CRC校验)。
-
-
硬件断点 (Hardware Breakpoints):
-
原理: 利用 CPU 的调试寄存器 (Dr0 - Dr3)。当访问特定内存地址(执行、写入或读取)时,CPU 硬件直接暂停。
-
特点: 不修改代码内存,难以通过代码完整性检查发现,但数量有限(通常只有 4 个)。
-
-
内存断点 (Memory Breakpoints):
-
原理: 修改内存页的访问属性(如设为不可访问 PAGE_NOACCESS)。当程序尝试访问该内存页时,触发异常,调试器捕获并暂停。
-
特点: 威力巨大,可监控大块内存区域。
-
动态追踪技术
-
单步执行 (Single Stepping):
-
Step Into (步入): 遇到函数调用时,跟进去逐行分析。
-
Step Over (步过): 遇到函数调用时,直接执行完该函数,停在下一行。
-
-
Hooking (挂钩):
-
拦截 API 调用(如
CreateFile,ConnectSocket),查看参数和返回值,甚至篡改数据。
-
-
堆栈回溯 (Stack Backtrace):
-
分析当前的堆栈帧(Stack Frame),查看是谁调用了当前函数,还原执行路径。
-
2. 反调试方法 (Anti-Debugging) —— 免疫系统的抵抗
软件开发者(或病毒作者)为了保护代码逻辑不被分析,会在程序中植入检测代码。一旦发现自己“正在被手术”,就会采取自毁、崩溃或欺骗行为。
A. API 与 标志位检测
这是最基础的检测手段。
-
IsDebuggerPresent: 调用 Windows API,直接询问系统“我被调试了吗?”。本质是读取 PEB (Process Environment Block) 结构中的
BeingDebugged标志位。 -
CheckRemoteDebuggerPresent: 检测是否有调试器附加(Attach)到当前进程。
-
NtGlobalFlag: 检查进程堆的标志位。在调试状态下,系统会开启特殊的堆标记,程序可以通过检查这些标记来判断。
B. 异常处理检测 (Exception Based)
调试器的工作原理严重依赖异常捕获。
-
INT 3 扫描: 程序扫描自己的代码段,如果发现
0xCC字节,说明有人下了软件断点。 -
INT 2D / ICE: 利用特殊的指令触发异常。如果调试器没有正确处理这些古老的或未文档化的指令,程序就会走入预设的“陷阱”路径。
-
SEH (结构化异常处理): 程序故意制造一个错误(如除以零)。正常情况下程序会崩溃或进入自带的异常处理函数;如果调试器介入并“吃掉”了异常,程序发现自己的处理函数没跑,就知道有调试器存在。
C. 时间差检测 (Timing Checks)
人肉调试是非常慢的(单步执行可能需要几秒,而 CPU 执行只需纳秒)。
-
RDTSC 指令: 读取 CPU 时间戳计数器。程序在一段代码前后分别记录时间,如果差值过大,说明有人在单步调试。
-
GetTickCount: 同样的原理,计算两个时间点的毫秒差。
D. 环境与特征检测
-
父进程检测: 正常双击运行的程序,父进程通常是
explorer.exe。如果父进程是ollydbg.exe或x64dbg.exe,则判定为被调试。 -
窗口/类名搜索: 程序会遍历系统所有窗口,查找名为 "OllyDbg", "IDA", "Wireshark" 的窗口并终止运行。
-
调试端口检测: 检查
NtQueryInformationProcess中的ProcessDebugPort,如果非 0,则说明被调试。
3. 反-反调试 (Anti-Anti-Debugging) —— 伪装与欺骗
这是分析者的反击,目的是让调试器“隐形”,让程序以为自己在正常裸奔。
A. 隐藏调试器 (Stealth Plugins)
现代调试器(如 x64dbg, OllyDbg)都有强大的插件系统(如 ScyllaHide, TitanHide)。
-
原理: 这些插件会 Hook 掉系统底层 API(如
NtQueryInformationProcess)。当程序调用这些 API 查房时,插件拦截请求并返回“False”(未被调试),从而欺骗程序。
B. 手动修改环境 (Memory Patching)
-
修改 PEB: 在调试器加载程序后,手动进入内存视图,找到 PEB 结构,将
BeingDebugged标志位从 1 改为 0。 -
NOP 掉检测代码: 在反汇编视图中找到程序的反调试检查函数(如
call IsDebuggerPresent),直接将其修改为NOP(空指令)或强制跳转(JMP),跳过检测逻辑。
C. 对抗时间检测
-
修改寄存器: 当遇到
RDTSC检测时,单步走过检测代码,然后在对比时间差之前,手动修改寄存器(EAX/EDX)的值,制造“执行很快”的假象。 -
高阶插件: 某些内核级工具可以直接接管时钟中断,让程序读到的时间是“冻结”的。
D. 使用硬件断点
-
避开 CRC 校验: 既然软件断点(修改代码为 CC)会被程序扫描发现,那就坚持使用硬件断点。因为硬件断点不改变内存中的代码字节,程序的自校验机制无法发现。
总结对照表
| 层面 | 角色 | 典型行为 | 核心工具/指令 |
| 调试 | 医生 (分析者) | 下断点、单步、查看寄存器 | OllyDbg, x64dbg, IDA, GDB |
| 反调试 | 病毒/加壳软件 | 查标志位、算时间差、故意抛异常 | IsDebuggerPresent, RDTSC, INT 3 扫描 |
| 反-反调试 | 隐形医生 | Hook API 返回假数据、Patch 检查点 | ScyllaHide, TitanHide, 修改 PEB |
Android应用安全 - 反调试技术详解
一、反调试技术概述
┌─────────────────────────────────────────────────────────────────┐
│ Android反调试体系架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Java层检测 │ │ Native层检测 │ │ 内核层检测 │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 反调试检测引擎 │ │
│ ├─────────────────────────────────────────────────┤ │
│ │ • 调试器检测 • 进程状态检测 • 时间检测 │ │
│ │ • 断点检测 • Hook检测 • 模拟器检测 │ │
│ │ • 端口检测 • 签名检测 • 环境检测 │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 响应策略 │ │
│ │ 崩溃退出 | 数据清除 | 功能禁用 | 静默上报 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
二、Java层反调试技术
2.1 调试标志检测
public class JavaAntiDebug {
/**
* 检测应用是否处于调试模式
*/
public static boolean isDebuggable(Context context) {
// 方法1: 检查ApplicationInfo标志
boolean debuggable = (context.getApplicationInfo().flags
& ApplicationInfo.FLAG_DEBUGGABLE) != 0;
// 方法2: 检查BuildConfig
boolean buildConfigDebug = BuildConfig.DEBUG;
// 方法3: 检查系统属性
boolean roDebuggable = "1".equals(
System.getProperty("ro.debuggable"));
return debuggable || buildConfigDebug || roDebuggable;
}
/**
* 检测是否有调试器连接
*/
public static boolean isDebuggerConnected() {
// 方法1: 直接检测
if (android.os.Debug.isDebuggerConnected()) {
return true;
}
// 方法2: 检测等待调试器标志
if (android.os.Debug.waitingForDebugger()) {
return true;
}
return false;
}
/**
* 综合调试检测
*/
public static void performDebugCheck(Context context) {
new Thread(() -> {
while (true) {
if (isDebuggable(context) || isDebuggerConnected()) {
// 检测到调试,执行保护策略
handleDebugDetected();
}
try {
Thread.sleep(1000); // 每秒检测一次
} catch (InterruptedException e) {
break;
}
}
}).start();
}
private static void handleDebugDetected() {
// 策略1: 直接退出
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(1);
// 策略2: 清除敏感数据后退出
// clearSensitiveData();
// System.exit(1);
}
}
2.2 调试端口检测
public class DebugPortDetector {
// 常见调试端口
private static final int[] DEBUG_PORTS = {
23946, // IDA默认端口
23947, // IDA备用端口
5037, // ADB端口
27042, // Frida默认端口
27043, // Frida备用端口
62001, // 夜神模拟器
21503, // 逍遥模拟器
};
/**
* 检测调试端口是否开放
*/
public static boolean detectDebugPorts() {
for (int port : DEBUG_PORTS) {
if (isPortOpen("127.0.0.1", port)) {
Log.w("AntiDebug", "Detected debug port: " + port);
return true;
}
if (isPortOpen("localhost", port)) {
return true;
}
}
return false;
}
private static boolean isPortOpen(String host, int port) {
Socket socket = null;
try {
socket = new Socket();
socket.connect(new InetSocketAddress(host, port), 100);
return true;
} catch (Exception e) {
return false;
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
// ignore
}
}
}
}
/**
* 通过/proc/net/tcp检测端口
*/
public static boolean detectPortsViaProcNet() {
try {
BufferedReader reader = new BufferedReader(
new FileReader("/proc/net/tcp"));
String line;
while ((line = reader.readLine()) != null) {
// 检查是否包含调试端口的十六进制表示
// 23946 -> 5D8A
if (line.contains(":5D8A") ||
line.contains(":69A2") || // 27042
line.contains(":13B5")) { // 5037
reader.close();
return true;
}
}
reader.close();
} catch (Exception e) {
// ignore
}
return false;
}
}
2.3 TracerPid检测
public class TracerPidDetector {
/**
* 通过/proc/self/status检测TracerPid
* TracerPid为0表示没有被跟踪
*/
public static boolean isBeingTraced() {
try {
BufferedReader reader = new BufferedReader(
new FileReader("/proc/self/status"));
String line;
while ((line = reader.readLine()) != null) {
if (line.startsWith("TracerPid:")) {
int tracerPid = Integer.parseInt(
line.substring(10).trim());
reader.close();
return tracerPid != 0;
}
}
reader.close();
} catch (Exception e) {
// 读取失败可能是被Hook,视为异常
return true;
}
return false;
}
/**
* 通过/proc/self/stat检测
*/
public static boolean checkStatFile() {
try {
BufferedReader reader = new BufferedReader(
new FileReader("/proc/self/stat"));
String line = reader.readLine();
reader.close();
if (line != null) {
String[] parts = line.split(" ");
// 第3个字段是状态,'t'表示被跟踪
if (parts.length > 2 && parts[2].equals("t")) {
return true;
}
}
} catch (Exception e) {
return true;
}
return false;
}
/**
* 多种方式综合检测
*/
public static boolean comprehensiveTracerCheck() {
// 检测1: TracerPid
if (isBeingTraced()) return true;
// 检测2: stat文件状态
if (checkStatFile()) return true;
// 检测3: wchan检测
if (checkWchan()) return true;
return false;
}
private static boolean checkWchan() {
try {
BufferedReader reader = new BufferedReader(
new FileReader("/proc/self/wchan"));
String wchan = reader.readLine();
reader.close();
// ptrace_stop表示被调试
if (wchan != null && wchan.contains("ptrace")) {
return true;
}
} catch (Exception e) {
// ignore
}
return false;
}
}
三、Native层反调试技术
3.1 基础反调试检测
// anti_debug.h
#ifndef ANTI_DEBUG_H
#define ANTI_DEBUG_H
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <elf.h>
#include <sys/mman.h>
#include <dlfcn.h>
#include <link.h>
#include <android/log.h>
#define LOG_TAG "AntiDebug"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
// 反调试检测结果
typedef enum {
DETECT_NONE = 0,
DETECT_PTRACE,
DETECT_TRACER_PID,
DETECT_DEBUG_PORT,
DETECT_FRIDA,
DETECT_XPOSED,
DETECT_BREAKPOINT,
DETECT_TIMING,
DETECT_EMULATOR
} DetectResult;
// 函数声明
int anti_debug_init();
int check_ptrace();
int check_tracer_pid();
int check_debug_ports();
int check_frida();
int check_breakpoints();
int check_timing();
void anti_debug_loop();
#endif // ANTI_DEBUG_H
// anti_debug.c
#include "anti_debug.h"
// 全局变量
static volatile int g_anti_debug_running = 0;
static pthread_t g_anti_debug_thread;
/**
* ptrace自我附加检测
* 原理:一个进程只能被一个调试器附加
*/
int check_ptrace() {
int result = 0;
// 方法1: 尝试ptrace自己
if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) == -1) {
LOGW("ptrace TRACEME failed - debugger detected");
result = 1;
} else {
// 成功附加后需要解除
ptrace(PTRACE_DETACH, 0, NULL, NULL);
}
return result;
}
/**
* fork子进程进行ptrace检测
* 更可靠的检测方式
*/
int check_ptrace_fork() {
int status;
pid_t child = fork();
if (child == 0) {
// 子进程尝试附加父进程
pid_t parent = getppid();
if (ptrace(PTRACE_ATTACH, parent, NULL, NULL) == 0) {
// 附加成功,等待父进程停止
waitpid(parent, NULL, 0);
// 恢复父进程
ptrace(PTRACE_DETACH, parent, NULL, NULL);
exit(0); // 正常,没有调试器
} else {
exit(1); // 附加失败,可能有调试器
}
} else if (child > 0) {
// 父进程等待子进程结果
waitpid(child, &status, 0);
if (WIFEXITED(status)) {
return WEXITSTATUS(status);
}
}
return 0;
}
/**
* TracerPid检测(Native实现)
*/
int check_tracer_pid() {
char buf[512];
int tracer_pid = 0;
FILE* fp = fopen("/proc/self/status", "r");
if (fp == NULL) {
LOGW("Cannot open /proc/self/status");
return 1; // 文件打不开可能被Hook
}
while (fgets(buf, sizeof(buf), fp)) {
if (strncmp(buf, "TracerPid:", 10) == 0) {
tracer_pid = atoi(&buf[10]);
break;
}
}
fclose(fp);
if (tracer_pid != 0) {
LOGW("TracerPid detected: %d", tracer_pid);
return 1;
}
return 0;
}
/**
* 多路径TracerPid检测
* 防止单一路径被Hook
*/
int check_tracer_pid_multi() {
char path[64];
char buf[512];
int tracer_pid;
// 检测路径数组
const char* paths[] = {
"/proc/self/status",
"/proc/%d/status"
};
pid_t pid = getpid();
for (int i = 0; i < 2; i++) {
if (i == 1) {
snprintf(path, sizeof(path), paths[i], pid);
} else {
strcpy(path, paths[i]);
}
int fd = open(path, O_RDONLY);
if (fd < 0) continue;
ssize_t len = read(fd, buf, sizeof(buf) - 1);
close(fd);
if (len > 0) {
buf[len] = '\0';
char* tracer_line = strstr(buf, "TracerPid:");
if (tracer_line) {
tracer_pid = atoi(tracer_line + 10);
if (tracer_pid != 0) {
return 1;
}
}
}
}
return 0;
}
/**
* 检测常见调试端口
*/
int check_debug_ports() {
int debug_ports[] = {
23946, // IDA
23947, // IDA
27042, // Frida
27043, // Frida
5037, // ADB
0 // 结束标记
};
struct sockaddr_in addr;
int sock;
for (int i = 0; debug_ports[i] != 0; i++) {
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) continue;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(debug_ports[i]);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
// 设置非阻塞连接超时
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 100000; // 100ms
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
close(sock);
LOGW("Debug port detected: %d", debug_ports[i]);
return 1;
}
close(sock);
}
return 0;
}
/**
* 通过/proc/net/tcp检测端口
*/
int check_ports_proc_net() {
FILE* fp = fopen("/proc/net/tcp", "r");
if (fp == NULL) return 0;
char line[256];
// 23946 = 0x5D8A, 27042 = 0x69A2
const char* hex_ports[] = {":5D8A", ":69A2", ":5D8B", ":69A3", NULL};
while (fgets(line, sizeof(line), fp)) {
for (int i = 0; hex_ports[i] != NULL; i++) {
if (strstr(line, hex_ports[i])) {
fclose(fp);
LOGW("Debug port found in /proc/net/tcp");
return 1;
}
}
}
fclose(fp);
return 0;
}
3.2 Frida检测
// frida_detect.c
#include "anti_debug.h"
#include <dirent.h>
/**
* 检测Frida特征字符串
*/
int check_frida_strings() {
// Frida特征字符串
const char* frida_strings[] = {
"frida",
"LIBFRIDA",
"frida-agent",
"frida-server",
"frida-gadget",
"linjector",
NULL
};
// 检测/proc/self/maps中的Frida特征
FILE* fp = fopen("/proc/self/maps", "r");
if (fp == NULL) return 0;
char line[512];
while (fgets(line, sizeof(line), fp)) {
for (int i = 0; frida_strings[i] != NULL; i++) {
if (strstr(line, frida_strings[i])) {
fclose(fp);
LOGW("Frida string detected in maps: %s", line);
return 1;
}
}
}
fclose(fp);
return 0;
}
/**
* 检测Frida端口
*/
int check_frida_port() {
int frida_ports[] = {27042, 27043, 0};
struct sockaddr_in addr;
int sock;
for (int i = 0; frida_ports[i] != 0; i++) {
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) continue;
// 设置超时
struct timeval timeout = {0, 100000};
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(frida_ports[i]);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
// 尝试发送Frida握手
char* frida_msg = "\x00";
send(sock, frida_msg, 1, 0);
char response[256];
int len = recv(sock, response, sizeof(response), 0);
close(sock);
if (len > 0) {
LOGW("Frida server detected on port %d", frida_ports[i]);
return 1;
}
}
close(sock);
}
return 0;
}
/**
* 检测Frida通过D-Bus通信
*/
int check_frida_dbus() {
DIR* dir = opendir("/proc");
if (dir == NULL) return 0;
struct dirent* entry;
char path[256];
char cmdline[256];
while ((entry = readdir(dir)) != NULL) {
// 只检查数字目录(进程)
if (entry->d_type != DT_DIR) continue;
int is_pid = 1;
for (char* p = entry->d_name; *p; p++) {
if (*p < '0' || *p > '9') {
is_pid = 0;
break;
}
}
if (!is_pid) continue;
// 读取cmdline
snprintf(path, sizeof(path), "/proc/%s/cmdline", entry->d_name);
FILE* fp = fopen(path, "r");
if (fp) {
if (fgets(cmdline, sizeof(cmdline), fp)) {
if (strstr(cmdline, "frida") ||
strstr(cmdline, "gum-js-loop") ||
strstr(cmdline, "gmain")) {
fclose(fp);
closedir(dir);
LOGW("Frida process detected: %s", cmdline);
return 1;
}
}
fclose(fp);
}
}
closedir(dir);
return 0;
}
/**
* 检测Frida通过内存特征
*/
int check_frida_memory() {
// Frida Agent的特征
const unsigned char frida_signature[] = {
0x46, 0x52, 0x49, 0x44, 0x41 // "FRIDA"
};
FILE* fp = fopen("/proc/self/maps", "r");
if (fp == NULL) return 0;
char line[512];
while (fgets(line, sizeof(line), fp)) {
// 只检查可读内存区域
if (strstr(line, "r-") == NULL) continue;
unsigned long start, end;
if (sscanf(line, "%lx-%lx", &start, &end) != 2) continue;
// 搜索特征
unsigned char* ptr = (unsigned char*)start;
unsigned long size = end - start;
for (unsigned long i = 0; i < size - sizeof(frida_signature); i++) {
if (memcmp(ptr + i, frida_signature, sizeof(frida_signature)) == 0) {
fclose(fp);
LOGW("Frida signature found in memory");
return 1;
}
}
}
fclose(fp);
return 0;
}
/**
* 综合Frida检测
*/
int check_frida() {
if (check_frida_strings()) return 1;
if (check_frida_port()) return 1;
if (check_frida_dbus()) return 1;
// check_frida_memory() 比较耗时,可选
return 0;
}
3.3 软件断点检测
// breakpoint_detect.c
#include "anti_debug.h"
/**
* 检测ARM软件断点指令
* ARM: 0xE7F001F0 (BKPT)
* Thumb: 0xBE00 (BKPT) 或 0xDEFE
*/
// ARM断点指令
#define ARM_BREAKPOINT 0xE7F001F0
#define ARM_BREAKPOINT_2 0xE1200070
// Thumb断点指令
#define THUMB_BREAKPOINT 0xBE00
#define THUMB_BREAKPOINT_2 0xDEFE
// x86断点指令
#define X86_BREAKPOINT 0xCC
/**
* 检测函数是否被下断点
*/
int check_function_breakpoint(void* func_addr) {
if (func_addr == NULL) return 0;
#if defined(__arm__)
// ARM模式检测
uint32_t* arm_ptr = (uint32_t*)func_addr;
if (*arm_ptr == ARM_BREAKPOINT || *arm_ptr == ARM_BREAKPOINT_2) {
LOGW("ARM breakpoint detected at %p", func_addr);
return 1;
}
// Thumb模式检测
uint16_t* thumb_ptr = (uint16_t*)((uintptr_t)func_addr & ~1);
if (*thumb_ptr == THUMB_BREAKPOINT || *thumb_ptr == THUMB_BREAKPOINT_2) {
LOGW("Thumb breakpoint detected at %p", func_addr);
return 1;
}
#elif defined(__aarch64__)
// ARM64断点检测
uint32_t* ptr = (uint32_t*)func_addr;
// BRK指令格式: 0xD4200000 | (imm16 << 5)
if ((*ptr & 0xFFE0001F) == 0xD4200000) {
LOGW("ARM64 breakpoint detected at %p", func_addr);
return 1;
}
#elif defined(__i386__) || defined(__x86_64__)
// x86/x64断点检测
uint8_t* ptr = (uint8_t*)func_addr;
if (*ptr == X86_BREAKPOINT) {
LOGW("x86 breakpoint detected at %p", func_addr);
return 1;
}
#endif
return 0;
}
/**
* 检测关键函数的断点
*/
int check_key_functions_breakpoint() {
// 需要检测的关键函数列表
void* key_functions[] = {
(void*)ptrace,
(void*)open,
(void*)read,
(void*)mmap,
(void*)dlopen,
(void*)dlsym,
NULL
};
for (int i = 0; key_functions[i] != NULL; i++) {
if (check_function_breakpoint(key_functions[i])) {
return 1;
}
}
return 0;
}
/**
* 通过CRC校验检测代码修改
*/
uint32_t calculate_crc32(const uint8_t* data, size_t length) {
uint32_t crc = 0xFFFFFFFF;
for (size_t i = 0; i < length; i++) {
crc ^= data[i];
for (int j = 0; j < 8; j++) {
crc = (crc >> 1) ^ (0xEDB88320 & -(crc & 1));
}
}
return ~crc;
}
/**
* 检测代码段完整性
*/
typedef struct {
void* start;
size_t size;
uint32_t expected_crc;
} CodeSection;
static CodeSection g_code_sections[16];
static int g_section_count = 0;
int register_code_section(void* start, size_t size) {
if (g_section_count >= 16) return -1;
g_code_sections[g_section_count].start = start;
g_code_sections[g_section_count].size = size;
g_code_sections[g_section_count].expected_crc =
calculate_crc32((uint8_t*)start, size);
g_section_count++;
return 0;
}
int check_code_integrity() {
for (int i = 0; i < g_section_count; i++) {
uint32_t current_crc = calculate_crc32(
(uint8_t*)g_code_sections[i].start,
g_code_sections[i].size
);
if (current_crc != g_code_sections[i].expected_crc) {
LOGW("Code integrity check failed for section %d", i);
return 1;
}
}
return 0;
}
3.4 时间检测
// timing_detect.c
#include "anti_debug.h"
#include <time.h>
#include <sys/time.h>
/**
* 基于时间的调试检测
* 原理:调试时代码执行会变慢
*/
#define TIMING_THRESHOLD_US 100000 // 100ms阈值
/**
* 获取高精度时间(微秒)
*/
static inline long long get_time_us() {
struct timeval tv;
gettimeofday(&tv, NULL);
return (long long)tv.tv_sec * 1000000 + tv.tv_usec;
}
/**
* 简单时间检测
*/
int check_timing_simple() {
long long start = get_time_us();
// 执行一些简单操作
volatile int dummy = 0;
for (int i = 0; i < 1000; i++) {
dummy += i;
}
long long elapsed = get_time_us() - start;
if (elapsed > TIMING_THRESHOLD_US) {
LOGW("Timing check failed: %lld us", elapsed);
return 1;
}
return 0;
}
/**
* 基于rdtsc的时间检测(x86)
*/
#if defined(__i386__) || defined(__x86_64__)
static inline unsigned long long rdtsc() {
unsigned int lo, hi;
__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
return ((unsigned long long)hi << 32) | lo;
}
int check_timing_rdtsc() {
unsigned long long start = rdtsc();
// 简单操作
volatile int x = 0;
for (int i = 0; i < 100; i++) x++;
unsigned long long elapsed = rdtsc() - start;
// 阈值需要根据实际CPU调整
if (elapsed > 10000000) {
return 1;
}
return 0;
}
#endif
/**
* 基于系统时钟的检测
*/
int check_timing_clock() {
struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);
// 执行操作
volatile int sum = 0;
for (int i = 0; i < 10000; i++) {
sum += i * i;
}
clock_gettime(CLOCK_MONOTONIC, &end);
long long elapsed_ns = (end.tv_sec - start.tv_sec) * 1000000000LL +
(end.tv_nsec - start.tv_nsec);
// 正常情况下应该在1ms以内
if (elapsed_ns > 10000000) { // 10ms
LOGW("Clock timing check failed: %lld ns", elapsed_ns);
return 1;
}
return 0;
}
/**
* 综合时间检测
*/
int check_timing() {
int score = 0;
// 多次检测取平均
for (int i = 0; i < 5; i++) {
if (check_timing_simple()) score++;
if (check_timing_clock()) score++;
usleep(10000); // 10ms间隔
}
// 如果多次检测都超时,则认为被调试
return (score >= 3) ? 1 : 0;
}
四、高级反调试技术
4.1 自修改代码(SMC)
// smc_anti_debug.c
#include "anti_debug.h"
#include <sys/mman.h>
/**
* 自修改代码示例
* 运行时解密代码,增加静态分析难度
*/
// 加密的代码段(示例)
static unsigned char encrypted_code[] = {
// 这里放置加密后的机器码
0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0
};
static const size_t encrypted_code_size = sizeof(encrypted_code);
static const unsigned char xor_key = 0x5A; // 简单XOR密钥
/**
* 解密并执行代码
*/
int execute_decrypted_code() {
// 分配可执行内存
void* exec_mem = mmap(NULL, encrypted_code_size,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0);
if (exec_mem == MAP_FAILED) {
LOGE("Failed to allocate executable memory");
return -1;
}
// 解密代码到可执行内存
unsigned char* src = encrypted_code;
unsigned char* dst = (unsigned char*)exec_mem;
for (size_t i = 0; i < encrypted_code_size; i++) {
dst[i] = src[i] ^ xor_key; // XOR解密
}
// 清空指令缓存
__builtin___clear_cache(exec_mem,
(char*)exec_mem + encrypted_code_size);
// 执行解密后的代码
typedef int (*func_t)(void);
func_t func = (func_t)exec_mem;
int result = func();
// 清理
memset(exec_mem, 0, encrypted_code_size); // 清除解密代码
munmap(exec_mem, encrypted_code_size);
return result;
}
/**
* 代码段动态解密
*/
void decrypt_code_section(void* start, size_t size, const unsigned char* key,
size_t key_size) {
// 修改内存权限为可写
uintptr_t page_start = (uintptr_t)start & ~(getpagesize() - 1);
size_t page_size = ((uintptr_t)start + size - page_start +
getpagesize() - 1) & ~(getpagesize() - 1);
if (mprotect((void*)page_start, page_size,
PROT_READ | PROT_WRITE | PROT_EXEC) != 0) {
LOGE("mprotect failed");
return;
}
// 解密
unsigned char* ptr = (unsigned char*)start;
for (size_t i = 0; i < size; i++) {
ptr[i] ^= key[i % key_size];
}
// 恢复内存权限
mprotect((void*)page_start, page_size, PROT_READ | PROT_EXEC);
// 清空指令缓存
__builtin___clear_cache(start, (char*)start + size);
}
4.2 反Hook检测
// anti_hook.c
#include "anti_debug.h"
#include <dlfcn.h>
/**
* 检测函数是否被Hook
* 通过检查函数入口指令
*/
// 常见Hook框架的跳转指令特征
#define ARM_B_INSTRUCTION 0xEA000000 // B指令
#define ARM_LDR_PC 0xE51FF004 // LDR PC, [PC, #-4]
#define THUMB_B_INSTRUCTION 0xE000 // B指令
#define X86_JMP_REL32 0xE9 // JMP rel32
/**
* 检测inline hook
*/
int detect_inline_hook(void* func_addr) {
if (func_addr == NULL) return 0;
#if defined(__arm__)
uint32_t* arm_ptr = (uint32_t*)func_addr;
// 检测ARM跳转指令
if ((*arm_ptr & 0xFF000000) == ARM_B_INSTRUCTION) {
LOGW("Possible ARM inline hook at %p", func_addr);
return 1;
}
// 检测LDR PC方式的Hook
if (*arm_ptr == ARM_LDR_PC) {
LOGW("LDR PC hook detected at %p", func_addr);
return 1;
}
// 检测Thumb跳转
uint16_t* thumb_ptr = (uint16_t*)((uintptr_t)func_addr & ~1);
if ((*thumb_ptr & 0xF800) == 0xE000) {
LOGW("Possible Thumb inline hook at %p", func_addr);
return 1;
}
#elif defined(__aarch64__)
uint32_t* ptr = (uint32_t*)func_addr;
// 检测B指令
if ((*ptr & 0xFC000000) == 0x14000000) {
LOGW("ARM64 B instruction hook at %p", func_addr);
return 1;
}
// 检测BR指令
if ((*ptr & 0xFFFFFC1F) == 0xD61F0000) {
LOGW("ARM64 BR instruction hook at %p", func_addr);
return 1;
}
#elif defined(__i386__) || defined(__x86_64__)
uint8_t* ptr = (uint8_t*)func_addr;
// 检测JMP rel32
if (*ptr == X86_JMP_REL32) {
LOGW("x86 JMP hook detected at %p", func_addr);
return 1;
}
#endif
return 0;
}
/**
* 检测GOT/PLT Hook
*/
int detect_got_hook(const char* lib_name, const char* func_name) {
// 获取函数的原始地址(从共享库)
void* handle = dlopen(lib_name, RTLD_NOW);
if (handle == NULL) return 0;
void* func_addr = dlsym(handle, func_name);
dlclose(handle);
if (func_addr == NULL) return 0;
// 读取maps获取库的地址范围
FILE* fp = fopen("/proc/self/maps", "r");
if (fp == NULL) return 0;
char line[512];
unsigned long lib_start = 0, lib_end = 0;
int found = 0;
while (fgets(line, sizeof(line), fp)) {
if (strstr(line, lib_name)) {
unsigned long start, end;
if (sscanf(line, "%lx-%lx", &start, &end) == 2) {
if (!found) {
lib_start = start;
found = 1;
}
lib_end = end;
}
}
}
fclose(fp);
// 检查函数地址是否在库的范围内
unsigned long func = (unsigned long)func_addr;
if (func < lib_start || func >= lib_end) {
LOGW("GOT hook detected for %s in %s", func_name, lib_name);
LOGW(" Expected range: %lx-%lx, Actual: %lx",
lib_start, lib_end, func);
return 1;
}
return 0;
}
/**
* 检测关键函数的Hook
*/
int check_critical_functions_hook() {
typedef struct {
const char* lib;
const char* func;
} FuncInfo;
FuncInfo critical_funcs[] = {
{"libc.so", "open"},
{"libc.so", "read"},
{"libc.so", "write"},
{"libc.so", "ptrace"},
{"libc.so", "mmap"},
{"libc.so", "mprotect"},
{"libdl.so", "dlopen"},
{"libdl.so", "dlsym"},
{NULL, NULL}
};
for (int i = 0; critical_funcs[i].func != NULL; i++) {
// 获取函数地址
void* func = dlsym(RTLD_DEFAULT, critical_funcs[i].func);
if (func != NULL) {
// 检测inline hook
if (detect_inline_hook(func)) {
return 1;
}
// 检测GOT hook
if (detect_got_hook(critical_funcs[i].lib,
critical_funcs[i].func)) {
return 1;
}
}
}
return 0;
}
4.3 反调试循环
// anti_debug_loop.c
#include "anti_debug.h"
// 反调试状态
static volatile int g_debug_detected = 0;
static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
/**
* 反调试检测线程函数
*/
static void* anti_debug_thread_func(void* arg) {
while (g_anti_debug_running) {
int detected = 0;
// 执行各种检测
if (check_ptrace()) detected = DETECT_PTRACE;
else if (check_tracer_pid()) detected = DETECT_TRACER_PID;
else if (check_debug_ports()) detected = DETECT_DEBUG_PORT;
else if (check_frida()) detected = DETECT_FRIDA;
else if (check_key_functions_breakpoint()) detected = DETECT_BREAKPOINT;
else if (check_timing()) detected = DETECT_TIMING;
if (detected) {
pthread_mutex_lock(&g_mutex);
g_debug_detected = detected;
pthread_mutex_unlock(&g_mutex);
// 执行保护措施
handle_debug_detected(detected);
}
// 随机睡眠时间,增加分析难度
usleep((rand() % 1000 + 500) * 1000); // 500-1500ms
}
return NULL;
}
/**
* 处理检测到调试的情况
*/
void handle_debug_detected(int detect_type) {
LOGW("Debug detected, type: %d", detect_type);
// 策略1: 立即退出
// exit(1);
// 策略2: 崩溃
// *(int*)0 = 0;
// 策略3: 删除关键数据并退出
// wipe_sensitive_data();
// exit(1);
// 策略4: 静默退出(延迟一段时间后退出)
// sleep(rand() % 10 + 5);
// exit(0);
// 策略5: 返回错误数据
// g_return_fake_data = 1;
// 实际实现:组合多种策略
kill(getpid(), SIGKILL);
}
/**
* 启动反调试
*/
int anti_debug_init() {
if (g_anti_debug_running) {
return 0; // 已经运行
}
g_anti_debug_running = 1;
g_debug_detected = 0;
// 创建反调试线程
if (pthread_create(&g_anti_debug_thread, NULL,
anti_debug_thread_func, NULL) != 0) {
LOGE("Failed to create anti-debug thread");
g_anti_debug_running = 0;
return -1;
}
// 设置线程为守护线程
pthread_detach(g_anti_debug_thread);
LOGI("Anti-debug initialized");
return 0;
}
/**
* 停止反调试
*/
void anti_debug_stop() {
g_anti_debug_running = 0;
}
/**
* 检查是否检测到调试
*/
int is_debug_detected() {
int result;
pthread_mutex_lock(&g_mutex);
result = g_debug_detected;
pthread_mutex_unlock(&g_mutex);
return result;
}
五、Xposed/Substrate检测
5.1 Xposed框架检测
public class XposedDetector {
/**
* 检测Xposed框架
*/
public static boolean detectXposed() {
return checkXposedPackage() ||
checkXposedClass() ||
checkXposedStack() ||
checkXposedNative();
}
/**
* 检测Xposed安装包
*/
private static boolean checkXposedPackage() {
String[] xposedPackages = {
"de.robv.android.xposed.installer",
"de.robv.android.xposed",
"io.va.exposed",
"com.saurik.substrate",
"de.robv.android.xposed.XposedBridge"
};
try {
PackageManager pm = getContext().getPackageManager();
for (String pkg : xposedPackages) {
try {
pm.getPackageInfo(pkg, 0);
return true;
} catch (PackageManager.NameNotFoundException e) {
// 未安装
}
}
} catch (Exception e) {
// ignore
}
return false;
}
/**
* 检测Xposed类
*/
private static boolean checkXposedClass() {
String[] xposedClasses = {
"de.robv.android.xposed.XposedBridge",
"de.robv.android.xposed.XposedHelpers",
"de.robv.android.xposed.XC_MethodHook",
"de.robv.android.xposed.callbacks.XC_LoadPackage"
};
for (String className : xposedClasses) {
try {
Class.forName(className);
return true;
} catch (ClassNotFoundException e) {
// 未找到
}
}
return false;
}
/**
* 检测调用栈中的Xposed
*/
private static boolean checkXposedStack() {
try {
throw new Exception("Stack trace check");
} catch (Exception e) {
for (StackTraceElement element : e.getStackTrace()) {
String className = element.getClassName();
if (className.contains("xposed") ||
className.contains("Xposed") ||
className.contains("substrate")) {
return true;
}
}
}
return false;
}
/**
* 通过Native检测Xposed
*/
private static boolean checkXposedNative() {
try {
// 检查/proc/self/maps中是否有Xposed相关库
BufferedReader reader = new BufferedReader(
new FileReader("/proc/self/maps"));
String line;
while ((line = reader.readLine()) != null) {
if (line.contains("XposedBridge") ||
line.contains("libxposed") ||
line.contains("substrate")) {
reader.close();
return true;
}
}
reader.close();
} catch (Exception e) {
// ignore
}
return false;
}
}
5.2 Native层Xposed检测
// xposed_detect.c
#include "anti_debug.h"
/**
* 检测Xposed/Substrate Native特征
*/
int check_xposed_native() {
// 检测maps中的特征
FILE* fp = fopen("/proc/self/maps", "r");
if (fp == NULL) return 0;
char line[512];
const char* xposed_signs[] = {
"XposedBridge",
"libxposed",
"libsubstrate",
"substrate",
"app_process_xposed",
"XposedHelpers",
"libart_xposed",
NULL
};
while (fgets(line, sizeof(line), fp)) {
for (int i = 0; xposed_signs[i] != NULL; i++) {
if (strstr(line, xposed_signs[i])) {
fclose(fp);
LOGW("Xposed signature found: %s", xposed_signs[i]);
return 1;
}
}
}
fclose(fp);
return 0;
}
/**
* 检测Xposed环境变量
*/
int check_xposed_env() {
const char* env_vars[] = {
"CLASSPATH",
"_"
};
for (int i = 0; i < sizeof(env_vars)/sizeof(env_vars[0]); i++) {
char* value = getenv(env_vars[i]);
if (value != NULL) {
if (strstr(value, "xposed") || strstr(value, "Xposed")) {
LOGW("Xposed env var detected: %s=%s", env_vars[i], value);
return 1;
}
}
}
return 0;
}
/**
* 检测ART Hook
* Xposed在ART上通过修改ArtMethod实现Hook
*/
int check_art_method_hook(void* method) {
// ArtMethod结构(简化版,实际根据Android版本有差异)
// entry_point_from_interpreter
// entry_point_from_compiled_code
// 这里需要根据具体Android版本实现
// 检查entry_point是否指向合法区域
return 0;
}
六、模拟器检测
6.1 综合模拟器检测
public class EmulatorDetector {
/**
* 综合模拟器检测
*/
public static boolean isEmulator(Context context) {
int score = 0;
// 检测Build信息
if (checkBuildInfo()) score += 20;
// 检测硬件特征
if (checkHardware()) score += 20;
// 检测传感器
if (checkSensors(context)) score += 15;
// 检测电池
if (checkBattery(context)) score += 10;
// 检测特殊文件
if (checkEmulatorFiles()) score += 15;
// 检测系统属性
if (checkSystemProperties()) score += 10;
// 检测网络特征
if (checkNetwork(context)) score += 10;
return score >= 50; // 综合评分
}
/**
* 检测Build信息
*/
private static boolean checkBuildInfo() {
String[] emulatorSigns = {
"generic", "unknown", "google_sdk", "Emulator", "Android SDK",
"Genymotion", "Andy", "nox", "bluestacks", "mumu"
};
String[] buildFields = {
Build.FINGERPRINT, Build.MODEL, Build.MANUFACTURER,
Build.BRAND, Build.DEVICE, Build.PRODUCT, Build.HARDWARE
};
for (String field : buildFields) {
if (field == null) continue;
String fieldLower = field.toLowerCase();
for (String sign : emulatorSigns) {
if (fieldLower.contains(sign.toLowerCase())) {
return true;
}
}
}
// 特殊检测
if (Build.FINGERPRINT.startsWith("generic") ||
Build.FINGERPRINT.startsWith("unknown")) {
return true;
}
if (Build.MODEL.contains("google_sdk") ||
Build.MODEL.contains("Emulator") ||
Build.MODEL.contains("Android SDK")) {
return true;
}
return false;
}
/**
* 检测硬件特征
*/
private static boolean checkHardware() {
// 检查CPU架构
String abi = Build.SUPPORTED_ABIS[0];
if (abi.contains("x86") && !isRealX86Device()) {
return true;
}
// 检查CPU信息
try {
BufferedReader reader = new BufferedReader(
new FileReader("/proc/cpuinfo"));
String line;
while ((line = reader.readLine()) != null) {
if (line.contains("hypervisor") ||
line.contains("QEMU") ||
line.contains("GenuineIntel")) {
reader.close();
return true;
}
}
reader.close();
} catch (Exception e) {
// ignore
}
return false;
}
private static boolean isRealX86Device() {
// 真实x86设备列表
String[] realX86 = {"ASUS", "Intel"};
for (String brand : realX86) {
if (Build.MANUFACTURER.contains(brand)) {
return true;
}
}
return false;
}
/**
* 检测传感器
*/
private static boolean checkSensors(Context context) {
SensorManager sensorManager = (SensorManager)
context.getSystemService(Context.SENSOR_SERVICE);
// 检查加速度传感器
Sensor accelerometer = sensorManager.getDefaultSensor(
Sensor.TYPE_ACCELEROMETER);
// 检查陀螺仪
Sensor gyroscope = sensorManager.getDefaultSensor(
Sensor.TYPE_GYROSCOPE);
// 模拟器通常缺少这些传感器或传感器数据异常
if (accelerometer == null && gyroscope == null) {
return true;
}
// 检查传感器数量
List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
if (sensors.size() < 5) { // 真机通常有更多传感器
return true;
}
return false;
}
/**
* 检测电池
*/
private static boolean checkBattery(Context context) {
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = context.registerReceiver(null, filter);
if (batteryStatus != null) {
int plugged = batteryStatus.getIntExtra(
BatteryManager.EXTRA_PLUGGED, -1);
int status = batteryStatus.getIntExtra(
BatteryManager.EXTRA_STATUS, -1);
int level = batteryStatus.getIntExtra(
BatteryManager.EXTRA_LEVEL, -1);
// 模拟器通常显示正在充电且电量为50%
if (plugged == BatteryManager.BATTERY_PLUGGED_AC &&
level == 50 &&
status == BatteryManager.BATTERY_STATUS_CHARGING) {
return true;
}
}
return false;
}
/**
* 检测模拟器特有文件
*/
private static boolean checkEmulatorFiles() {
String[] emulatorFiles = {
"/dev/socket/qemud",
"/dev/qemu_pipe",
"/system/lib/libc_malloc_debug_qemu.so",
"/sys/qemu_trace",
"/system/bin/qemu-props",
"/dev/socket/genyd",
"/dev/socket/baseband_genyd",
"/system/bin/nox-prop",
"/system/bin/nox",
"/system/lib/libnoxd.so",
"/system/lib/libnoxspeedup.so",
"/data/data/com.bignox.app.store.hd",
"/system/bin/microvirt-prop",
"/system/bin/androVM-prop",
"/system/lib/libdroid4x.so"
};
for (String path : emulatorFiles) {
File file = new File(path);
if (file.exists()) {
return true;
}
}
return false;
}
/**
* 检测系统属性
*/
private static boolean checkSystemProperties() {
String[][] emulatorProperties = {
{"ro.kernel.qemu", "1"},
{"ro.hardware", "goldfish"},
{"ro.hardware", "ranchu"},
{"ro.product.device", "generic"},
{"ro.product.model", "sdk"},
{"gsm.version.ril-impl", "android memu"},
{"init.svc.qemud", null},
{"init.svc.qemu-props", null}
};
for (String[] prop : emulatorProperties) {
String value = getSystemProperty(prop[0]);
if (value != null) {
if (prop[1] == null || value.contains(prop[1])) {
return true;
}
}
}
return false;
}
private static String getSystemProperty(String name) {
try {
Class<?> systemProperties = Class.forName(
"android.os.SystemProperties");
Method get = systemProperties.getMethod("get", String.class);
return (String) get.invoke(null, name);
} catch (Exception e) {
return null;
}
}
/**
* 检测网络特征