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

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

反调试简介

在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系统中,`.so`(共享对象)文件是动态链接库的一种形式,广泛用于程序运行时加载模块化代码。为了防止`.so`文件被逆向工程或调试,开发者通常会采用多种反调试技术。以下是一些常见的`.so`文件反调试技术及其原理和实现方式: ### 1. 检测`ptrace`调用 Linux下常用的调试器(如`gdb`)通过`ptrace`系统调用来实现对目标进程的控制。因此,检测是否已被调试的一种方式是尝试调用`ptrace(PTRACE_TRACEME, ...)`,如果返回错误,则说明进程已经被调试器附加。 ```c #include <sys/ptrace.h> if (ptrace(PTRACE_TRACEME, 0, 1, 0) == -1) { // 已被调试,执行反调试措施 exit(1); } ``` ### 2. 检查`/proc/self/status`中的`TracerPid` 在Linux中,如果一个进程被调试器附加,其`/proc/self/status`文件中的`TracerPid`字段将显示调试器进程的PID。通过读取并解析该文件可以检测是否被调试。 ```c #include <stdio.h> #include <stdlib.h> int is_debugger_present() { FILE *fp = fopen("/proc/self/status", "r"); if (!fp) return 0; char line[128]; while (fgets(line, sizeof(line), fp)) { if (strncmp(line, "TracerPid:", 10) == 0) { int pid = atoi(line + 10); fclose(fp); return pid != 0; } } fclose(fp); return 0; } ``` ### 3. 使用信号处理机制 某些调试器会拦截特定的信号(如`SIGTRAP`、`SIGSEGV`),导致程序行为异常。可以通过注册信号处理函数检测信号是否被异常处理。 ```c #include <signal.h> #include <stdio.h> #include <stdlib.h> void sigtrap_handler(int sig) { // 如果信号被正常处理,说明未被调试 printf("Signal %d received\n", sig); exit(0); } int main() { signal(SIGTRAP, sigtrap_handler); raise(SIGTRAP); // 触发陷阱 return 0; } ``` ### 4. 检测调试器符号 某些调试器会在内存中留下特定的符号或字符串,可以通过扫描内存来检测这些符号是否存在。例如,检查`/proc/self/maps`中是否包含`libgdb`、`gdb`等关键字。 ```c #include <stdio.h> #include <string.h> int detect_gdb() { FILE *fp = fopen("/proc/self/maps", "r"); if (!fp) return 0; char line[256]; while (fgets(line, sizeof(line), fp)) { if (strstr(line, "libgdb") || strstr(line, "gdb")) { fclose(fp); return 1; // 检测到GDB } } fclose(fp); return 0; } ``` ### 5. 检测调试器环境变量 某些调试器会在启动时设置特定的环境变量(如`LD_DEBUG`),可以通过检查这些环境变量是否存在来判断是否被调试。 ```c #include <stdlib.h> #include <stdio.h> int check_env_vars() { if (getenv("LD_DEBUG")) { return 1; // 可能被调试 } return 0; } ``` ### 6. 使用内联汇编检测调试器 通过内联汇编插入一些特殊指令(如`int3`),观察程序是否因断点异常而中断。如果程序未中断,则可能被调试器拦截。 ```c void check_debugger() { __asm__ volatile ( "int3\n\t" "jmp 1f\n\t" "1:\n\t" ); } ``` ### 7. 检测时间差异常 调试器通常会减慢程序执行速度,尤其是单步执行时。可以通过测量两个时间点之间的间隔来判断是否被调试。 ```c #include <time.h> #include <stdio.h> int detect_debugger_by_time() { clock_t start = clock(); // 执行一些简单操作 for (volatile int i = 0; i < 1000000; i++); clock_t end = clock(); double elapsed = (double)(end - start) / CLOCKS_PER_SEC; if (elapsed > 0.1) { return 1; // 超过预期时间,可能被调试 } return 0; } ``` ### 8. 检测调试器附加标志 某些系统提供了API来检测是否被调试。例如,在Android中,可以通过`isDebuggerConnected()`函数来判断调试器是否连接。 ```cpp static void Dalvik_dalvik_system_VMDebug_isDebuggerConnected(const u4* args, JValue* pResult) { UNUSED_PARAMETER(args); RETURN_BOOLEAN(dvmDbgIsDebuggerConnected()); } ``` ### 9. 修改ELF文件结构 通过对`.so`文件进行加密或混淆,使得调试器难以加载或解析其内容。可以在运行时解密并加载到内存中,避免静态分析。 ### 10. 使用反调试库 一些第三方库(如`libantidbg`)提供了完整的反调试功能,开发者可以直接集成到项目中,简化反调试逻辑的实现。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小道安全

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

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

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

打赏作者

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

抵扣说明:

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

余额充值