1.检测ida远程调试所占的常用端口23946,是否被占用
//检测idaserver是否占用了23946端口
void CheckPort23946ByTcp() {
FILE* pfile=NULL;
char buf[0x1000]={0};
//执行命令
char* strCatTcp="cat /proc/net/tcp | grep :5D8A";
//char* strNetstat="netstat -apn | grep :23946"
pfile=popen(strCatTcp,"r");
//说明是没有被调试
if(NULL==pfile)
{
return;
}
//获取执行命令后的结果,并存入buf字符数组中
while(fgets(buf,sizeof(buf),pfile))
{
printf("执行 cat /proc/net/tcp | grep :5D8A的结果:\n");
printf("%s",buf);
}
pclose(pfile);
}
上面的netstat -apn | grep 23946 那个-apn是必须加的,之前看大佬的pdf好像漏了
反反调试方法:
1.直接nop掉
2.汇编级直接改寄存器值绕过
3. 既然是检测23946端口,那我就不运行在23946端口了,换一个端口运行
二.调试器进程名检测
原理: android调试时需要运行androidserver,androidserver64,gdb,gdbserver等进程
反调试代码:
void SerachObjectProcess()
{
FILE* pfile=NULL;
char buf[0x1000]={0};
//执行命令
//pfile=popen("ps | awk'{print $9}'","r");
pfile=popen("ps","r");
if(pfile==NULL) {
printf("命令打开失败");
return;
}
//获取查询结果
while(fgets(buf,sizeof(buf),pfile))
{
//打印进程
printf("遍历进程:%s\n",buf);
//查找子串
char* strA=NULL;
char *strB = NULL;
char *strC=NULL;
char *strD=NULL;
//IDA检测
strA=strstr(buf,"android_server");
//gdb检测
strB=strstr(buf,"gdbserver");
strC=strstr(buf,"gdb");
strD=strstr(buf,"fuwu");
if(strA||strB||strC||strD) {
printf("被调试了,%s\n", buf);
return;
}
}
pclose(pfile);
}
反反调试:
直接修改调试器server的名字,运行的话./自定义的server名字 -p xxxx(自定义端口)
三.父进程名检测
原理:附加调试时,父进程名都为zygote,有时候调试会使用可执行文件直接加载so文件进行调试,所以如果父进程名非zygote的话,必然是被调试的,充分非必要条件
反调试代码:
void CheckParents()
{
char strPpidCmdline[0x100]={0};
snprintf(strPpidCmdline, sizeof(strPpidCmdline),"proc/%d/cmdline",getppid());
int file=open(strPpidCmdline,O_RDONLY);
if(file<0)
{
printf("打开文件错误");
return;
}
//初始化一下
memset(strPpidCmdline,0, sizeof(strPpidCmdline));
//将文件内容读入内存中,方便比较
ssize_t ret=read(file,strPpidCmdline, sizeof(strPpidCmdline));
if(-1==ret)
{
printf("读入内存失败");
return;
}
char* sRet=strstr(strPpidCmdline,"zygote");
if(sRet==NULL)
{
printf("被调试了");
return;
}
int i=0;
return;
}
反反调试:
那就直接附加调试呗,其他方法暂时我也不知道233
四.自身进程名检测
原理: 和上文一样如果用可执行文件加载so配合脱壳的话,进程名也会发生改变,检测是否是apk那种com.xxx.xx
五:检测线程的数量
原理: 正常apk启动时是要有许多进程要启动的,而如果用可执行文件加载so文件,那么必然只有一个线程
反调试代码:
void CheckTaskCount()
{
char buf[0x100]={0};
char* str="/proc/%d/task";
snprintf(buf,sizeof(buf),str,getpid());
//打开目录
DIR* pdir=opendir(buf);
if(!pdir)
{
perror("CheckTaskCount open() fail.\n");
return;
}
//查看目录下文件的个数
struct dirent* pde=NULL;
int count=0;
while((pde=readdir(pdir)))
{
//字符过滤,每个文件都是一个线程id
if((pde->d_name[0]<='9')&&(pde->d_name[0]>='0'))
{
count++;
printf("%d 线程名称:%s\n",count,pde->d_name);
}
if(count<=1)
{
//说明被调试了
printf("被调试了");
}
return;
}
}
https://blog.csdn.net/qq_40732350/article/details/81986548
六:apk进程的fd文件数量差异检测
原理:/proc/pid/fd目录下文件数,调试与非调试fd文件数量不同
七.安卓系统自带的检测函数
android.os.Debug.isDebuggerConnected(),这个函数是在java层中直接调用就行,
但是如果在native层使用这个也是有办法的,
1. dvm下的方式
找到进程中的libdvm.so中的dvmDbgIsDebuggerConnect()函数,调用它,通过返回值来判断程序是否被调试
dlopen(/system/lib/libdvm.so)
dlsym(_Z25dvmDbgIsDebuggerConnect())
typedef unsigned char wbool;
typedef wbool (*ppp)();
void NativeIsDBGConnected()
{
void* Handle=NULL;
Handle=dlopen("/system/lib/libdvm.so",RTLD_LAZY);
if(Handle==NULL)
{
return;
}
ppp Fun=(ppp)dlsym(Handle,"_Z25dvmDbgIsDebuggerConnect"); //根据动态链接库的句柄和符号名,返回地址
if(Fun==NULL) {
printf("获取函数地址失败");
return;
} else
{
wbool ret=Fun();
if(ret==1)
{
printf("被调试了");
return;
}
}
}
2.art模式
结果存放在libart.so中的全局变量gDebuggerActive中,符号名
void checkPtrace()
{
int iRet;
iRet=ptrace(PTRACE_TRACEME,0,0,0);
if(iRet==-1)
{
//说明父进程调试失败,说明进程已经被别的进程ptrace了
printf("已经被调试了!");
return;
} else
{
printf("还没被调试");
}
}
反反调试:
1. 修改系统源码,将ptrace返回值直接返回0
2. hook ptrace
3.nop这个函数,或者汇编级修改寄存器绕过
九.函数hash值检测
原理:文件的函数指令一般固定,如果被下了断点,指令会发生改变(bkpt断点指令),可以计算内存中一段指令的hash值,做校验
十.断点指令检测
和上文一样,如果被下了断点的话,指令会被替换成(bkpt断点指令),那么在内存搜索一下不就完事了吗,注意arm和thumb指令有所区别
反调试代码:
void checkbkpt(u8* addr,u32 size)
{
//结果
u32 uRet=0;
//断点指令
u8 armBkpt[4]={0xf0,0x01,0xf0,0xe7};
u8 thumbBkpt[2]={0x10,0xde};
int mode=(u32)addr%2;
if(1==mode)
{
u8* start=(u8*)((u32)addr-1);
u8* end=(u8*)((u32)start+size);
while(1)
{
if(start>=end)
{
uRet=0;
return;
}
if(0==memcmp(start,thumbBkpt,2))
{
uRet=1;
break;
}
start=start+2;
}
} else{
//arm
u8* start=(u8*)addr;
u8* end=(u8*)((u32)start+size);
while (1)
{
if(start>=end)
{
uRet=0;
return;
}
if(0==memcmp(start,armBkpt,4))
{
uRet=1;
break;
}
start=start+4;
}
}
}
十一.安卓系统源码修改反调试
原理: 直接通过修改安卓源码修改,ptrace的返回值,使其永远为零,那么我们可以先自身trace自身,然后再通过子进程再trace一遍,如果还返回为0,说明就有问题。
反调试代码:
未完