利用 Linux 信号机制(SIGTRAP)实现 Android 下的反调试

版权归作者所有,如有转发,请注明文章出处:cyrus-studio.github.io/blog/

利用 SIGTRAP 检测调试器

在 Linux 的进程管理中,信号(Signal) 是调试器与被调试进程沟通的核心机制。断点、单步执行、进程暂停与恢复等操作,背后都依赖于特定信号的传递。

其中,SIGTRAP (陷阱信号)尤为特殊。通常它由调试器在调试过程中触发,例如执行断点指令或单步运行时都会引发 SIGTRAP。

如果一个程序主动触发 SIGTRAP,并设置了对应的信号处理函数,就能实现一种"自检":

  • 若信号处理函数被成功调用,说明 SIGTRAP 没有被调试器截获,此时可以推断程序处于非调试状态

  • 若信号处理函数未被调用,反而被调试器捕获并消费,则意味着程序正被调试。

由此,利用 SIGTRAP 信号进行反调试检测 ,就成为 Android 应用中一种更隐蔽、更底层的安全防护手段。

常见 Linux 信号列表(基于 signal.h)

编号 名称 说明
1 SIGHUP 挂起信号,通常在控制终端关闭时发送给进程
2 SIGINT 中断信号,通常由 Ctrl+C 触发
3 SIGQUIT 退出信号,通常由 Ctrl+\ 触发,并生成 core dump
4 SIGILL 非法指令(非法 CPU 指令执行)
5 SIGTRAP 陷阱信号,主要用于调试(断点、单步)
6 SIGABRT 异常终止,通常由 abort() 触发
7 SIGBUS 总线错误(非法内存访问,例如未对齐访问)
8 SIGFPE 浮点异常(除零、溢出等算术错误)
9 SIGKILL 强制终止信号,无法被捕获或忽略
11 SIGSEGV 无效内存访问(段错误)
13 SIGPIPE 向无读端的管道写数据时触发
14 SIGALRM 定时器超时,由 alarm() 触发
15 SIGTERM 终止信号,可被捕获和处理
17 SIGCHLD 子进程状态发生变化时通知父进程
18 SIGCONT 继续执行(与 SIGSTOP 配合使用)
19 SIGSTOP 停止进程,无法被捕获或忽略
20 SIGTSTP 终端暂停信号,通常由 Ctrl+Z 触发
21 SIGTTIN 后台进程尝试从终端读输入时触发
22 SIGTTOU 后台进程尝试往终端写输出时触发
23 SIGURG socket 上的紧急数据到达
24 SIGXCPU 超过 CPU 时间限制
25 SIGXFSZ 文件大小超过限制
26 SIGVTALRM 虚拟时钟超时
27 SIGPROF profiling 定时器超时
28 SIGWINCH 窗口大小发生变化(终端调整大小)
29 SIGIO / SIGPOLL 异步 I/O 事件
30 SIGPWR 电源故障
31 SIGSYS 非法系统调用

此外,Linux 还支持 实时信号(Real-Time Signals) ,编号从 32 开始 ,具体上限依赖于系统实现(通常是 SIGRTMIN 到 SIGRTMAX),通常是用于用户自定义的信号,应用程序可根据需要使用这些信号。

Android 下利用 SIGTRAP 反调试流程

在 Android 环境中,利用 SIGTRAP 进行反调试的核心思路是:让程序主动触发 SIGTRAP,并观察信号是否能够进入我们自己定义的处理器。如果信号能被捕获,说明程序运行正常;如果信号被调试器拦截,则说明进程正处于被调试状态。

Android 下利用 SIGTRAP + 自定义 Handler 的反调试流程:

scss 复制代码
                ┌─────────────────────────────┐
                │   App 进程启动 (目标进程)   │
                └──────────────┬──────────────┘
                               │
                               ▼
                   ┌──────────────────────────┐
                   │ 注册 SIGTRAP Handler     │
                   │ sigaction(SIGTRAP, ...)  │
                   └───────────┬─────────────┘
                               │
                               ▼
                   ┌────────────────────┐
                   │ 调用 raise(SIGTRAP)│
                   └───────────┬────────┘
                               │
               ┌───────────────┼─────────────────┐
               │                                 │
   ┌───────────▼────────────┐        ┌───────────▼────────────┐
   │ 无调试器存在            │        │ 有调试器附加           │
   │ ──────────────          │        │ ──────────────         │
   │ 信号交由自定义 handler  │        │ 调试器捕获 SIGTRAP     │
   │ handler 正常执行        │        │ handler 未被调用       │
   └───────────┬────────────┘        └───────────┬────────────┘
               │                                 │
               ▼                                 ▼
       [App 判定未被调试]                [App 判定被调试]
               │                                 │
               ▼                                 ▼
 ┌───────────────────────────┐     ┌───────────────────────────┐
 │   正常运行 (继续业务逻辑) │     │ 采取对抗动作:             │
 │                           │     │  - exit() / 延时退出       │
 │                           │     │  - 触发异常崩溃           │
 │                           │     │  - 执行混淆/假逻辑        │
 └───────────────────────────┘     └───────────────────────────┘

Android 下利用 SIGTRAP 反调试实现

1. 定义信号处理器并检测调试器

首先,我们在 C 代码中编写反调试逻辑,核心是通过 raise(SIGTRAP) 触发 SIGTRAP 信号,并判断信号是否被捕获。

arduino 复制代码
#include <jni.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <android/log.h>

#define LOG_TAG "AntiDebug"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

// 标志变量,判断 SIGTRAP 是否被捕获
volatile int sigtrap_caught = 0;

// SIGTRAP 信号处理函数
void sigtrap_handler(int sig) {
    LOGI("Caught SIGTRAP. No debugger present.");
    sigtrap_caught = 1; // 标记 SIGTRAP 被捕获
}

// JNI 方法,触发 SIGTRAP 信号并检测调试器
JNIEXPORT jboolean JNICALL
Java_com_cyrus_example_antidebug_AntiDebug_detectDebugger(JNIEnv *env, jobject instance) {
    // 注册 SIGTRAP 处理器
    signal(SIGTRAP, sigtrap_handler);

    // 触发 SIGTRAP 信号
    raise(SIGTRAP);

    // 检查信号是否被捕获
    if (sigtrap_caught) {
        LOGI("No debugger detected.");
        return JNI_FALSE; // 没有检测到调试器
    } else {
        // 如果信号未被捕获,说明有调试器
        LOGI("Debugger detected! The program will exit in 3 seconds...");
        sleep(3); // 等待 3 秒
        exit(EXIT_FAILURE); // 退出程序
        return JNI_TRUE; // 返回 true,表示检测到调试器
    }
}

配置 CMakeLists.txt 文件

perl 复制代码
cmake_minimum_required(VERSION 3.4.1)



find_library( # 查找 log 库
              log-lib

              # 库名
              log )

add_library( # 库名称
             antidebug

             # 库类型
             SHARED

             # 源文件
             anti_debug.c )

target_link_libraries( # 绑定库到 log 库
                       antidebug
                       ${log-lib} )

2. Kotlin 层调用 JNI 方法

在 Kotlin 层,我们通过 JNI 调用 detectDebugger 函数,以检测是否存在调试器。根据结果,程序可以作出不同的响应。

kotlin 复制代码
package com.cyrus.example.antidebug

import android.util.Log

object AntiDebug {

    init {
        // 加载 native 库
        System.loadLibrary("antidebug")
    }

    external fun detectDebugger(): Boolean

    fun isDebuggerDetected(): Boolean {
        val detected = detectDebugger()
        if (detected) {
            Log.i("AntiDebug", "Debugger detected!")
        } else {
            Log.i("AntiDebug", "No debugger detected.")
        }
        return detected
    }
}

3. 调用反调试功能

kotlin 复制代码
val debuggerDetected = AntiDebug.isDebuggerDetected()
if (debuggerDetected) {
    Toast.makeText(this, "Debugger Detected", Toast.LENGTH_SHORT).show()
} else {
    Toast.makeText(this, "No Debugger Detected", Toast.LENGTH_SHORT).show()
}

测试反调试

1. 无调试状态

无调式状态 App 中点击 "SIGTRAP 反调试" 按钮调用反调试功能,未检测到调试器,程序正常运行。

2. 调试状态下

通过 IDA Pro 附加到当前应用

相关文章:静态分析根本不够!IDA Pro 动态调试 Android 应用的完整实战

App 中点击 "SIGTRAP 反调试" 按钮调用反调试功能,SIGTRAP 信号 被 IDA Pro 调试器捕获。

触发程序反调试机制,程序在 3 秒后退出。

完整源码

开源地址:github.com/CYRUS-STUDI...

相关推荐
TG_yunshuguoji16 小时前
亚马逊云渠道商:本地SSD缓存如何保障数据安全?
运维·服务器·安全·云计算·aws
Android小码家16 小时前
Android8.0+Camera2编译&烧录&源码研习
android·framework
电话交换机IPPBX-3CX17 小时前
电话交换机软件3CX安全访问实践:屏蔽IP访问,仅允许域名访问
安全·域名·ippbx·1024程序员节·电话交换机
消失的旧时光-194317 小时前
Kotlin 高阶函数在回调设计中的最佳实践
android·开发语言·kotlin
LucianaiB17 小时前
掌握 Rust:从内存安全到高性能服务的完整技术图谱
开发语言·安全·rust
ks胤墨18 小时前
网站安全太复杂?雷池SafeLine+cpolar实现“躺平式”防护!
安全
AI智能架构工坊19 小时前
提升AI虚拟健康系统开发效率:架构师推荐10款低代码开发平台
android·人工智能·低代码·ai
百锦再19 小时前
低代码开发的约束性及ABP框架的实践解析
android·开发语言·python·低代码·django·virtualenv·rxjava
泷羽Sec-静安19 小时前
Less-1 GET-Error based-Single quotes-String GET-基于错误-单引号-字符串
前端·css·网络·sql·安全·web安全·less
那我掉的头发算什么19 小时前
【数据库】navicat的下载以及数据库约束
android·数据库·数据仓库·sql·mysql·数据库开发·数据库架构