检测原理
安卓的native下,是运用醉倒的反调试方案是通过读取进程的status或stat来检测tracepid,它主要原理是调试状态下的进程tracepid不为0。
对于这种调试检测手段,因为android系统是开源的,所以最彻底的绕过方式就是修改系统源码后重新编译,让tracepid永远为0。
对抗这种bypass手段,我们可以创建一个子进程,让子进程主动ptrace自身设为调试状态,
此时正常情况下,子进程的tracepid应该不为0。此时我们检测子进程的tracepid是否为0,
如果为0说明源码被修改了。
代码实现
//检测系统实现
bool checkSystemData()
{
// 建立管道
int pipefd[2];
if (-1 == pipe(pipefd))
{
LOGA("pipe() error.\n");
return false;
}
// 创建一个子进程
pid_t pid = fork();
LOGB("father pid is: %d\n",getpid());
LOGB("child pid is: %d\n",pid);
// for失败
if(0 > pid) {
LOGA("fork() error.\n");
return false;
}
// 子进程程序
int childTracePid=0;
if ( 0 == pid )
{
int iRet = ptrace(PTRACE_TRACEME, 0, 0, 0);
if (-1 == iRet)
{
LOGA("child ptrace failed.\n");
exit(0);
}
LOGA("%s ptrace succeed.\n");
// 获取tracepid
char pathbuf[0x100] = {0};
char readbuf[100] = {0};
sprintf(pathbuf, "/proc/%d/status", getpid());
int fd = openat(NULL, pathbuf, O_RDONLY);
if (-1 == fd) {
LOGA("openat failed.\n");
}
read(fd, readbuf, 100);
close(fd);
uint8_t *start = (uint8_t *) readbuf;
uint8_t des[100] = {0x54, 0x72, 0x61, 0x63, 0x65, 0x72, 0x5
0, 0x69, 0x64, 0x3A,0x09};
int i = 100;
bool flag= false;
while (--i)
{
if( 0==memcmp(start,des,10) )
{
start=start+11;childTracePid=atoi((char*)start);
flag= true;
break;
}else
{
start=start+1;
flag= false;
}
}//while
if(false==flag) {
LOGA("get tracepid failed.\n");
return false;
}
// 向管道写入数据
close(pipefd[0]); // 关闭管道读端
write(pipefd[1], (void*)&childTracePid,4); // 向管道写端写入数据
close(pipefd[1]); // 写完关闭管道写端
LOGA("child succeed, Finish.\n");
exit(0);
}
else
{
// 父进程程序
LOGA("开始等待子进程.\n");
waitpid(pid,NULL,NULL); // 等待子进程
结束
int buf2 = 0;
close(pipefd[1]); // 关闭写端
read(pipefd[0], (void*)&buf2, 4); // 从读端读取
数据到buf
close(pipefd[0]); // 关闭读端
LOGB("子进程传递的内容为:%d\n", buf2); // 输出内容
// 判断子进程ptarce后的tracepid
if(0 == buf2) {
LOGA("源码被修改了.\n");
}else{
LOGA("源码没有被修改.\n");
}
return true;
}
}
//检测代码的调用
void main()
{
bool bRet=checkSystemData();
if(true==bRet)
LOGA("check succeed.\n");
elseLOGA("check failed.\n");
LOGB("main Finish pid:%d\n",getpid());
return;
}