You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在运行时检测进程是否被LLDB调试?

运行时检测进程是否被LLDB调试

核心思路:LLDB调试进程时,会将进程标记为被跟踪状态,我们可以通过系统提供的进程信息查询接口,在运行时读取该状态来判断。以下是不同语言/汇编的实现方案:


1. 汇编实现(macOS x86_64)

利用sysctl系统调用查询当前进程的proc结构体,检查p_flag中的P_TRACED位(0x80000):

section .text
global is_debugged

is_debugged:
    ; 准备sysctl参数:[CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()]
    push rbp
    mov rbp, rsp
    sub rsp, 48                 ; 分配栈空间存参数和proc结构体

    ; 填充name数组
    mov dword [rsp], 1          ; CTL_KERN
    mov dword [rsp+4], 14       ; KERN_PROC
    mov dword [rsp+8], 1        ; KERN_PROC_PID
    call getpid
    mov dword [rsp+12], eax

    ; 设置参数:name, namelen, oldp, oldlenp, newp, newlen
    lea rdi, [rsp]              ; name
    mov esi, 4                  ; namelen
    lea rdx, [rsp+16]           ; oldp (proc结构体)
    mov rcx, [rsp+40]           ; oldlenp (初始设为44,proc结构体大小)
    mov dword [rsp+40], 44
    xor r8, r8                  ; newp = NULL
    xor r9, r9                  ; newlen = 0
    mov rax, 0x1000002          ; syscall number for sysctl
    syscall

    ; 检查sysctl返回值,失败则返回0
    cmp rax, 0
    jne .not_debugged

    ; 检查proc.p_flag中的P_TRACED位(0x80000)
    mov eax, dword [rsp+16+40]  ; proc.p_flag偏移40字节
    and eax, 0x80000
    cmp eax, 0x80000
    je .debugged

.not_debugged:
    mov eax, 0
    jmp .exit

.debugged:
    mov eax, 1

.exit:
    add rsp, 48
    pop rbp
    ret

2. C语言实现(跨macOS/Linux)

macOS版本(sysctl)

#include <stdbool.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <unistd.h>

bool is_being_debugged(void) {
    struct kinfo_proc proc_info;
    size_t proc_info_size = sizeof(proc_info);
    int name[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()};

    if (sysctl(name, 4, &proc_info, &proc_info_size, NULL, 0) != 0) {
        return false;
    }

    // 检查P_TRACED标志位
    return (proc_info.kp_proc.p_flag & P_TRACED) != 0;
}

Linux版本(ptrace 或 /proc 文件系统)

#include <stdbool.h>
#include <stdio.h>
#include <sys/ptrace.h>
#include <string.h>
#include <stdlib.h>

bool is_being_debugged(void) {
    // 方法1:尝试ptrace附加自己,若失败则说明已被调试
    if (ptrace(PTRACE_TRACEME, 0, NULL, 0) == -1) {
        return true;
    }
    // 若成功,取消附加
    ptrace(PTRACE_DETACH, 0, NULL, 0);
    return false;

    // 方法2:读取/proc/self/status中的TracerPid字段
    /*
    FILE *fp = fopen("/proc/self/status", "r");
    if (!fp) return false;

    char line[256];
    while (fgets(line, sizeof(line), fp)) {
        if (strncmp(line, "TracerPid:", 9) == 0) {
            int pid = atoi(line + 9);
            fclose(fp);
            return pid != 0;
        }
    }
    fclose(fp);
    return false;
    */
}

3. Rust语言实现(跨平台)

macOS版本(调用sysctl)

use std::io;
use std::mem;
use libc;

#[repr(C)]
#[derive(Debug)]
struct kinfo_proc {
    kp_proc: proc,
    // 省略其他无关字段
}

#[repr(C)]
#[derive(Debug)]
struct proc {
    p_flag: u32,
    // 省略其他无关字段
}

const CTL_KERN: u32 = 1;
const KERN_PROC: u32 = 14;
const KERN_PROC_PID: u32 = 1;
const P_TRACED: u32 = 0x80000;

fn is_being_debugged() -> Result<bool, io::Error> {
    let pid = std::process::id() as i32;
    let name = [CTL_KERN, KERN_PROC, KERN_PROC_PID, pid as u32];
    let mut proc_info = kinfo_proc {
        kp_proc: proc { p_flag: 0 },
    };
    let mut proc_info_size = mem::size_of::<kinfo_proc>() as u64;

    let res = unsafe {
        libc::sysctl(
            name.as_ptr(),
            name.len() as u32,
            &mut proc_info as *mut _ as *mut libc::c_void,
            &mut proc_info_size,
            std::ptr::null(),
            0,
        )
    };

    if res == -1 {
        return Err(io::Error::last_os_error());
    }

    Ok((proc_info.kp_proc.p_flag & P_TRACED) != 0)
}

Linux版本(ptrace 或 /proc 文件系统)

use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use libc;

fn is_being_debugged() -> bool {
    // 方法1:ptrace TRACEME尝试
    unsafe {
        if libc::ptrace(libc::PTRACE_TRACEME, 0, std::ptr::null(), 0) == -1 {
            return true;
        }
        let _ = libc::ptrace(libc::PTRACE_DETACH, 0, std::ptr::null(), 0);
    }

    // 方法2:读取/proc/self/status
    /*
    let file = match File::open("/proc/self/status") {
        Ok(f) => f,
        Err(_) => return false,
    };
    let reader = BufReader::new(file);
    for line in reader.lines() {
        let line = match line {
            Ok(l) => l,
            Err(_) => continue,
        };
        if line.starts_with("TracerPid:") {
            let parts: Vec<&str> = line.split_whitespace().collect();
            if let Some(pid_str) = parts.get(1) {
                if let Ok(pid) = pid_str.parse::<u32>() {
                    return pid != 0;
                }
            }
        }
    }
    false
    */
}

补充说明

如果需要精准判断调试器是LLDB而非其他调试器(如GDB),可以额外检查父进程的名称:

  • macOS:通过proc_pidinfo获取父进程ID,再读取进程名称是否为lldb
  • Linux:读取/proc/[parent_pid]/comm文件内容是否为lldb

通用的调试状态检测已经能覆盖大部分场景,精准判断可根据需求扩展。

内容的提问来源于stack exchange,提问作者Isaaс Weisberg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 04:35:19