如何在运行时检测进程是否被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
相关产品推荐
相关产品推荐

