原理
在Android系统中,如果进程处于调试状态,/proc/进程pid/status (等同于/proc/self/status)文件中的tracePid不为0,所以只要在加载so的时候,读取status文件,若tracePid不为0,则说明进程处于调试状态。
status文件分析
Name: sh //当前进程名称
State: R (running)
Tgid: 23809
Pid: 23809
PPid: 195
TracerPid: 0 //若处于debug 状态,此处非0
Uid: 0 0 0 0
Gid: 0 0 0 0
FDSize: 32
Groups:
VmPeak: 3024 kB
VmSize: 3024 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 916 kB
VmRSS: 916 kB
VmData: 1132 kB
VmStk: 132 kB
VmExe: 160 kB
VmLib: 1260 kB
VmPTE: 6 kB
VmSwap: 0 kB
Threads: 1
SigQ: 4/12236
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000380000
SigCgt: 000000000801f4ff
CapInh: 0000000000000000
CapPrm: ffffffffffffffff
CapEff: ffffffffffffffff
CapBnd: ffffffffffffffff
Cpus_allowed: f
Cpus_allowed_list: 0-3
voluntary_ctxt_switches: 57
nonvoluntary_ctxt_switches: 7
C++ 代码
static int antiDebug() {
const char mode[2] = "r"; //以读的方式打开文件
const char path[20] = "/proc/self/status";//文件路径
FILE *fd = fopen(path, mode);
int traceId = 0;//用于存储tracePid的值
if (fd == NULL) {//判断文件是否打开失败
return traceId;
}
char *buf = static_cast<char *>(malloc(0x80));
if (buf == NULL) { //申请内存失败
return traceId;
}
memset(buf, 0, 0x80);
int flag = 0;
while (1) {
if (fgets(buf, 0x80, fd)) {//读取一行
char *temp;
char *temp2;
char delim[3] = {0x20, 0x09, 0x00};//切割文本的字符
temp2 = buf;
while (temp = strsep(&temp2, delim)) {//切割文本
const char check[20] = "TracerPid:";
if (!strcmp(temp, check)) {//比较字符
flag = 1;
break;
}
}
if (flag) {
long pid = strtol(temp2, NULL, 10);//截取tracePid
traceId = pid;
break;
}
} else {
traceId = 0;
break;
}
}
free(buf);
buf = NULL;
fclose(fd);
if (traceId != 0) {
return antiDebug();//若tracePid不等于0,任务处于调试阶段,会一直陷入死循环,导致应用闪退
} else {
return traceId;
}
}