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,說明就有問題。
反調試代碼:
未完