Copyright belongs to the author. If reposting, please indicate the source: https://cyrus-studio.github.io/blog/
Using SIGTRAP to Detect Debuggers
In Linux process management, signals are the core mechanism for communication between the debugger and the debugged process. Operations such as breakpoints, single-step execution, and process suspension and resumption rely on the transmission of specific signals.
Among these, SIGTRAP (trap signal) is particularly special. It is typically triggered by the debugger during the debugging process, such as when executing breakpoint instructions or single-stepping.
If a program actively triggers SIGTRAP and sets a corresponding signal handler, it can implement a form of “self-check”:
- • If the signal handler is successfully called, it indicates that SIGTRAP was not intercepted by the debugger, allowing us to infer that the program is in a non-debugging state;
- • If the signal handler is not called and is instead captured and consumed by the debugger, it means the program is being debugged.
Thus, using the SIGTRAP signal for anti-debugging detection becomes a more covert and lower-level security measure in Android applications.
Common Linux Signal List (Based on signal.h)
| Number | Name | Description |
|---|---|---|
| 1 | SIGHUP | Hangup signal, usually sent to a process when the controlling terminal is closed |
| 2 | SIGINT | Interrupt signal, usually triggered by Ctrl+C |
| 3 | SIGQUIT | Quit signal, usually triggered by Ctrl+\ and generates a core dump |
| 4 | SIGILL | Illegal instruction (illegal CPU instruction execution) |
| 5 | SIGTRAP | Trap signal, mainly used for debugging (breakpoints, single-step) |
| 6 | SIGABRT | Abnormal termination, usually triggered by abort() |
| 7 | SIGBUS | Bus error (illegal memory access, such as unaligned access) |
| 8 | SIGFPE | Floating-point exception (division by zero, overflow, etc.) |
| 9 | SIGKILL | Force termination signal, cannot be caught or ignored |
| 11 | SIGSEGV | Invalid memory access (segmentation fault) |
| 13 | SIGPIPE | Triggered when writing data to a pipe with no reader |
| 14 | SIGALRM | Timer timeout, triggered by alarm() |
| 15 | SIGTERM | Termination signal, can be caught and handled |
| 17 | SIGCHLD | Notifies the parent process when the child process state changes |
| 18 | SIGCONT | Continues execution (used with SIGSTOP) |
| 19 | SIGSTOP | Stops the process, cannot be caught or ignored |
| 20 | SIGTSTP | Terminal stop signal, usually triggered by Ctrl+Z |
| 21 | SIGTTIN | Triggered when a background process tries to read input from the terminal |
| 22 | SIGTTOU | Triggered when a background process tries to write output to the terminal |
| 23 | SIGURG | Urgent data arrives on a socket |
| 24 | SIGXCPU | Exceeds CPU time limit |
| 25 | SIGXFSZ | File size exceeds limit |
| 26 | SIGVTALRM | Virtual clock timeout |
| 27 | SIGPROF | Profiling timer timeout |
| 28 | SIGWINCH | Window size changes (terminal resizing) |
| 29 | SIGIO / SIGPOLL | Asynchronous I/O events |
| 30 | SIGPWR | Power failure |
| 31 | SIGSYS | Illegal system call |
Additionally, Linux supports real-time signals, numbered from 32 onwards, with the specific upper limit depending on the system implementation (usually from SIGRTMIN to SIGRTMAX). These are typically used for user-defined signals, which applications can use as needed.
Anti-Debugging Process Using SIGTRAP on Android
In the Android environment, the core idea of using SIGTRAP for anti-debugging is to have the program actively trigger SIGTRAP and observe whether the signal can enter our defined handler. If the signal can be captured, it indicates that the program is running normally; if the signal is intercepted by the debugger, it indicates that the process is being debugged.
The anti-debugging process using SIGTRAP + custom handler on Android is as follows:
┌─────────────────────────────┐
│ App Process Starts (Target Process) │
└──────────────┬──────────────┘
│
▼
┌──────────────────────────┐
│ Register SIGTRAP Handler │
│ sigaction(SIGTRAP, ...) │
└───────────┬─────────────┘
│
▼
┌────────────────────┐
│ Call raise(SIGTRAP)│
└───────────┬────────┘
│
┌───────────────┼─────────────────┐
│ │
┌───────────▼────────────┐ ┌───────────▼────────────┐
│ No Debugger Present │ │ Debugger Attached │
│ ────────────── │ │ ────────────── │
│ Signal Handed to Custom handler │ │ Debugger Captures SIGTRAP │
│ Handler Executes Normally │ │ Handler Not Called │
└───────────┬────────────┘ └───────────┬────────────┘
│ │
▼ ▼
[App Determines Not Debugged] [App Determines Debugged]
│ │
▼ ▼
┌───────────────────────────┐ ┌───────────────────────────┐
│ Running Normally (Continue Business Logic) │ │ Take Countermeasures: │
│ │ │ - exit() / Delay Exit │
│ │ │ - Trigger Exception Crash │
│ │ │ - Execute Obfuscation/Fake Logic │
└───────────────────────────┘ └───────────────────────────┘
Implementing Anti-Debugging Using SIGTRAP on Android
1. Define Signal Handler and Detect Debugger
First, we write the anti-debugging logic in C code, primarily by triggering the SIGTRAP signal using raise(SIGTRAP) and determining whether the signal is captured.
#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__)
// Flag variable to determine if SIGTRAP is caught
volatile int sigtrap_caught = 0;
// SIGTRAP signal handler function
void sigtrap_handler(int sig) {
LOGI("Caught SIGTRAP. No debugger present.");
sigtrap_caught = 1; // Mark SIGTRAP as caught
}
// JNI method to trigger SIGTRAP signal and detect debugger
JNIEXPORT jboolean JNICALL
Java_com_cyrus_example_antidebug_AntiDebug_detectDebugger(JNIEnv *env, jobject instance) {
// Register SIGTRAP handler
signal(SIGTRAP, sigtrap_handler);
// Trigger SIGTRAP signal
raise(SIGTRAP);
// Check if the signal was caught
if (sigtrap_caught) {
LOGI("No debugger detected.");
return JNI_FALSE; // No debugger detected
} else {
// If the signal was not caught, it indicates a debugger is present
LOGI("Debugger detected! The program will exit in 3 seconds...");
sleep(3); // Wait for 3 seconds
exit(EXIT_FAILURE); // Exit the program
return JNI_TRUE; // Return true, indicating a debugger was detected
}
}
Configure CMakeLists.txt file
cmake_minimum_required(VERSION 3.4.1)
find_library( # Find log library
log-lib
# Library name
log )
add_library( # Library name
antidebug
# Library type
SHARED
# Source file
anti_debug.c )
target_link_libraries( # Link library to log library
antidebug
${log-lib} )
2. Calling JNI Method from Kotlin Layer
In the Kotlin layer, we call the detectDebugger function via JNI to check for the presence of a debugger. Based on the result, the program can respond differently.
package com.cyrus.example.antidebug
import android.util.Log
object AntiDebug {
init {
// Load native library
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. Invoking Anti-Debugging Functionality
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()
}
Testing Anti-Debugging
1. No Debugging State
In a non-debugging state, clicking the “SIGTRAP Anti-Debugging” button in the app calls the anti-debugging function, and no debugger is detected, allowing the program to run normally.

2. In Debugging State
Attach to the current application using IDA Pro

Related articles:Static analysis is not enough! Complete practical guide to dynamic debugging of Android applications with IDA Pro[1]
Clicking the “SIGTRAP Anti-Debugging” button in the app triggers the anti-debugging function, and the SIGTRAP signal is captured by the IDA Pro debugger.

Triggering the program’s anti-debugging mechanism causes the program to exit after 3 seconds.

Complete Source Code
Open source address: https://github.com/CYRUS-STUDIO/AndroidExample
Reference Links
<span>[1]</span> Static analysis is not enough! Complete practical guide to dynamic debugging of Android applications with IDA Pro: https://cyrus-studio.github.io/blog/posts/%E9%9D%99%E6%80%81%E5%88%86%E6%9E%90%E6%A0%B9%E6%9C%AC%E4%B8%8D%E5%A4%9Fida-pro-%E5%8A%A8%E6%80%81%E8%B0%83%E8%AF%95-android-%E5%BA%94%E7%94%A8%E7%9A%84%E5%AE%8C%E6%95%B4%E5%AE%9E%E6%88%98/