so中函数断点的的反调试检测

本文介绍了如何通过计算函数指令的hash值校验和搜索bkpt指令来检测apk应用中的函数是否被修改或设置了软件断点。在函数体中,如果检测到bkpt指令,说明可能被下断点。代码示例展示了针对ARM和Thumb模式的检查方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

反调试简介

在apk应用的so文件中函数的指令都是固定,但是如果被下了软件断点,指令就会发生改变(断点地址被改写为bkpt断点指令),可以计算内存中一段指令的hash值进行校验,检测函数是否被修改或被下断点。

实现原理

当我们程序中的函数被下软件断点,则断点地址会被改写为bkpt指令, 可以在函数体中搜索bkpt指令来检测软件断电。

代码实现



/*参数1:函数首地址 
参数2:函数size
*/

typedef uint8_t u8;
typedef uint32_t u32;

int checkFuncbkpt(u8* addr,u32 size)
{


u32 uRet=0;

// 定义的断点指令
// u8 armBkpt[4]={0xf0,0x01,0xf0,0xe7};
// u8 thumbBkpt[2]={0x10,0xde};

u8 armBkpt[4]={0};
armBkpt[0]=0xf0;
armBkpt[1]=0x01;
armBkpt[2]=0xf0;
armBkpt[3]=0xe7;

u8 thumbBkpt[2]={0};

thumbBkpt[0]=0x10;

thumbBkpt[1]=0xde;

// 判断模式
int mode=(u32)addr%2;
if(1==mode) 
{

LOGA("checkbkpt:(thumb mode)该地址为thumb模式\n");

u8* start=(u8*)((u32)addr-1);
u8* end=(u8*)((u32)start+size);

// 循环遍历对比
while(1)

{

if(start >= end) {

uRet=0;
LOGA("checkbkpt:(no find bkpt)没有发现断点.\n");
break;

}

if( 0==memcmp(start,thumbBkpt,2) )
{
uRet=1;
LOGA("checkbkpt:(find it)发现断点.\n");

break;

}

start=start+2;

}//while

}//if

else
{

LOGA("checkbkpt:(arm mode)该地址为arm模式\n");

u8* start=(u8*)addr;

u8* end=(u8*)((u32)start+size);

// 遍历对比

while(1){

if (start >= end) {

uRet = 0;

LOGA("checkbkpt:(no find)没有发现断点.\n");

break;

}

if (0 == memcmp(start,armBkpt , 4)){

uRet = 1;

LOGA("checkbkpt:(find it)发现断点.\n");

break;

}

start = start + 4;

}//while

}//else

return 1;

}

在Linux系统中,实现反调试技术通常涉及利用操作系统提供的机制来检测或阻止调试器的附加。以下是一些常见的反调试技术及其具体实现方法: ### 1. 使用 `ptrace` 检测调试器 `ptrace` 是 Linux 提供的一个系统调用,用于进程跟踪和调试。程序可以通过调用 `ptrace(PTRACE_TRACEME, ...)` 来检查是否已经被调试器附加。如果返回值为 `-1` 并且 `errno` 设置为 `EPERM`,则表示当前进程已经被调试器附加。 ```c #include <sys/ptrace.h> #include <stdio.h> #include <errno.h> int is_debugger_present() { if (ptrace(PTRACE_TRACEME, 0, NULL, 0) == -1) { if (errno == EPERM) { printf("Debugger detected!\n"); return 1; } else { printf("Not being debugged.\n"); return 0; } } ptrace(PTRACE_DETACH, 0, NULL, 0); return 0; } ``` 此方法可以有效防止简单的调试行为[^1]。 --- ### 2. 利用 `/proc/self/status` 文件检测调试状态 每个进程的状态信息都可以在 `/proc/self/status` 文件中找到,其中包含 `TracerPid` 字段。如果该字段的值不为零,则表示当前进程正在被调试器附加。 ```bash cat /proc/self/status | grep TracerPid ``` 通过读取该文件并解析 `TracerPid` 的值,可以判断当前进程是否处于调试状态。例如: ```python def check_tracer_pid(): with open("/proc/self/status") as f: for line in f: if "TracerPid:" in line: tracer_pid = int(line.strip().split()[1]) if tracer_pid != 0: print(f"Debugger detected! TracerPid: {tracer_pid}") return True else: print("No debugger detected.") return False return False ``` 这种方法适用于不需要修改代码的情况下进行快速检测[^2]。 --- ### 3. 监控 `/proc/pid/maps` 和 `/proc/pid/mem` 文件 某些调试工具(如 `gdb`)会通过读取 `/proc/pid/maps` 或写入 `/proc/pid/mem` 来访问目标进程的内存。可以通过监控这些文件的操作来实现反调试功能。 #### 使用 `inotify` 实现文件监控 `inotify` 是 Linux 内核提供的一种文件系统事件通知机制,可用于监控特定文件的变化。以下是一个使用 `inotify` 监控 `/proc/pid/maps` 文件的示例: ```c #include <sys/inotify.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <string.h> #define EVENT_SIZE (sizeof(struct inotify_event)) #define BUF_LEN (1024 * (EVENT_SIZE + 16)) int main() { int fd, wd; char buffer[BUF_LEN]; fd = inotify_init(); if (fd < 0) { perror("inotify_init"); exit(EXIT_FAILURE); } // 假设要监控的进程 PID 为 1234 char path[256]; snprintf(path, sizeof(path), "/proc/1234/maps"); wd = inotify_add_watch(fd, path, IN_ACCESS | IN_MODIFY); if (wd < 0) { perror("inotify_add_watch"); exit(EXIT_FAILURE); } while (1) { int length = read(fd, buffer, BUF_LEN); if (length < 0) { perror("read"); exit(EXIT_FAILURE); } struct inotify_event *event = (struct inotify_event *)buffer; for (int i = 0; i < length; ) { event = (struct inotify_event *)((char *)buffer + i); if (event->mask & IN_ACCESS) { printf("File %s was accessed.\n", event->name); } if (event->mask & IN_MODIFY) { printf("File %s was modified.\n", event->name); } i += EVENT_SIZE + event->len; } } inotify_rm_watch(fd, wd); close(fd); return 0; } ``` 通过这种方式,可以在检测到对关键文件的访问时采取相应的防护措施[^2]。 --- ### 4. 使用信号处理机制干扰调试器 调试器通常会拦截某些信号(如 `SIGTRAP`、`SIGINT` 等),因此可以通过自定义信号处理器来干扰调试器的行为。例如,可以注册一个 `SIGTRAP` 处理函数,并在其内部执行异常操作或终止程序。 ```c #include <signal.h> #include <stdio.h> #include <stdlib.h> void sigtrap_handler(int signum) { printf("SIGTRAP received. Exiting...\n"); exit(EXIT_FAILURE); } int main() { signal(SIGTRAP, sigtrap_handler); raise(SIGTRAP); // 触发 SIGTRAP 信号 return 0; } ``` 当调试器尝试设置断点时,可能会触发 `SIGTRAP` 信号,从而导致程序提前退出[^3]。 --- ### 5. 使用 `LD_PRELOAD` 技术劫持调试器相关函数 `LD_PRELOAD` 允许用户在运行时动态加载共享库,覆盖默认的库函数。可以利用这一特性劫持与调试相关的函数(如 `ptrace`),并在其中插入反调试逻辑。 例如,创建一个名为 `anti_debug.c` 的文件: ```c #define _GNU_SOURCE #include <dlfcn.h> #include <sys/ptrace.h> #include <stdio.h> int ptrace(int request, pid_t pid, void *addr, void *data) { static int (*real_ptrace)(int, pid_t, void *, void *) = NULL; if (!real_ptrace) { real_ptrace = dlsym(RTLD_NEXT, "ptrace"); } if (request == PTRACE_TRACEME) { printf("Anti-debug: Blocking PTRACE_TRACEME\n"); return -1; // 阻止调试器附加 } return real_ptrace(request, pid, addr, data); } ``` 编译并运行该库: ```bash gcc -shared -fPIC -o libanti_debug.so anti_debug.c -ldl LD_PRELOAD=./libanti_debug.so ./your_program ``` 通过这种方式,可以有效地阻止调试器对 `ptrace` 的调用[^3]。 --- ### 6. 使用 `seccomp` 过滤系统调用 `seccomp` 是 Linux 提供的一种安全机制,允许应用程序限制其能够使用的系统调用。可以通过配置 `seccomp` 规则来禁止 `ptrace` 调用,从而防止调试器附加。 ```c #include <linux/seccomp.h> #include <linux/filter.h> #include <linux/audit.h> #include <sys/prctl.h> #include <sys/syscall.h> #include <unistd.h> #include <stdio.h> int main() { struct sock_filter filter[] = { // Load architecture BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, arch)), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AUDIT_ARCH_X86_64, 0, 7), // Load syscall number BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, syscall)), // Allow all syscalls except ptrace BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SYS_ptrace, 0, 1), BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | (EPERM << 16)), // Block ptrace BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), // Allow others // Default action: kill the process BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL) }; struct sock_fprog prog = { .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])), .filter = filter, }; prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog); printf("Seccomp filter applied. Try to attach a debugger...\n"); sleep(10); // 给调试器时间附加 return 0; } ``` 此方法可以有效阻止调试器通过 `ptrace` 附加到目标进程[^3]。 --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小道安全

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值