一、檢測Android——server文件
void check(){
const char* Path="/data/local/tmp";
dir =opendir(Path);
int pid =getpid();
if(dir!=NULL){
dirent *currentDir;
while((currentDir=readdir(dir)!=NULL)){
//使用readdir函數讀取當前目錄下的每一個文件
if(strncmp(currentDir->d_name,"android_server",14)==0)//currentDir->de_name結構體能夠獲取當前的文件名
{
kill(pid,SIGKILL);
}
}
closedir(dir);//關閉文件
}else{
}
}
分析:
這算的上是最簡單的一種反調試檢測了,原理很簡單,使用currentDir
結構體檢測獲取
/data/local/tmp目錄下是否有android_server文件
反制措施:
反制措施有很多種,最便捷的方法就是進入/data/local/tmp目錄下,把文件名改掉就行了。
二、檢測進程名
這種方法之前已經分析過,這裏進行更詳細的分析
void check()
{
const int bufsize=1024;
char filename[bufsize];
char line[bufsize];
char name[bufsize];
char nameline[bufsize];
//獲取Tracepid的值
int pid=getpid();
sprint(filename,"/proc/%d/status",pid);
FILE *fd=fopen(filename,"r");
if(fd!=NULL)
{
while(fgets(line,bufsize,fd))
{
//遍歷讀取到“TracePid”
if(strstr(line,"TracePid")!=NULL)
{
//判斷TracePid的第10個數是否爲0
int statue=atoi(&line[10]);
if(statue!=0)
{
//不等於0時,將當前的statue寫入到name中
sprint(name,"/proc/%d/cmdline",statue);
//讀取文件fdname
FILE *fdname=fopen(name,"r");
if(fdname!=NULL)
{
//遍歷找到“android_server”
while(fgets(nameline,bufsize,fdname))
{
if(strstr(nameline,"android_server"!=NULL))
{
int ret =kill(pid,SIGKILL);
//kill進程
}
}
}
fclose(fdname);
}
}
}
}
fclose(fd);
}
strstr:定義:char *strstr(const char *haystack, const char *needle) 功能:haystack – 要被檢索的 C 字符串。 needle – 在 haystack 字符串內要搜索的小字符串。該函數返回在 haystack 中第一次出現 needle 字符串的位置,如果未找到則返回 null。
atoi: 定義:int atoi(const char *str),功能:str – 要轉換爲整數的字符串。該函數返回轉換後的長整數,如果沒有執行有效的轉換,則返回零。
分析:首先檢測Tracepid的值是否爲0,如果爲0,在遍歷檢測是否有android_server,如果有直接kill進程
反制措施: 還是簡單的說一下,一般這種檢測,我們會嘗試在ida下斷,在運行到指定函數時將其返回值改爲0,當然也可以採用其他的方法比如nop掉跳轉指令,讓它不進行檢測,複雜一點的有個方法可以從根本上解決這個反調試問題,那就是刷機修改源碼,這裏貼一個大佬的帖子https://se8s0n.github.io/2019/04/19/%E5%B0%9D%E8%AF%95%E7%BB%95%E8%BF%87TracePID%E5%8F%8D%E8%B0%83%E8%AF%95%E4%BA%8C%E2%80%94%E2%80%94%E4%BB%8E%E6%BA%90%E7%A0%81%E5%85%A5%E6%89%8B/
三、檢測常用的端口
void check()
{
FILE* pfile=NULL;
char buf[0x10000]={0};
//使用代碼執行cmd命令
char* strCatTcpPort="cat /proc/net/tcp |grep :5D8A";
pfile = popen(strCatTcpPort,"r");
int pid=getpid();//獲取進程的pid
if(NULL==pfile)
{
//命令運行失敗
return;
}
//讀取pfile文件內容,如果android_server沒有啓動的話,常文件內不會有內容
while(fgets(buf,sizeof(buf),pfile))
{
//檢測到23946端口被佔用,直接kill進程
int ret=kill(pid,SIGKILL);
}
pclose(pfile);
}
分析:
這裏使用了cat /proc/net/tcp |grep :5D8A
命令,讀取了23946端口被使用後的產生的數據,在使用strCatTcpPort獲取的其數據,然後寫入pfile中,最後
ps:fget函數
如果成功,該函數返回相同的 str 參數。
如果到達文件末尾或者沒有讀取到任何字符
**模擬執行效果:**當我們沒有啓動android_server時,使用了cat /proc/net/tcp |grep :5D8A
命令效果:
沒有任何輸出
而我們使用了啓動了android_server後
反制措施:
既然是檢測端口,那麼直接更改掉端口android_server就可以了
使用./android_server -p
後面跟上你的端口
四、輪循檢測
void anti_debugger()
{
pthread_t tid;
pthread_create(&tid,NULL,&anti_debug_thread,NULL);
}
void *anti_debugger_thread(void*data)
{
pid_t pid=getpid();
while(true)
{
check_debugger(pid)
}
}
bool check_debugger(pid_t pid)
{
const int pathSize=256;
const int bufSize=1024;
char path[pathSize];
char line[bufSize];
snprintf(path,sizeof(path)-1,"/proc/%d/status",pid);
bool result =true;
FILE *fp=fopen(path,"rt");
if(fp!=NULL)
{
while(fgets(line,sizeof(line),fp))
{
if(strncmp(line,TRACERPID,TRACERPID_LEN)==0)
{
pid_t tracerPid=0;
sscanf(line,"%*s%d",&tracerPid);
if(!tracerPid)
result =false
break;
}
}
fclose(fp);
}
return result;
}
分析:也是檢測pid判斷其是否爲0,如果不爲0就進行死循環,這種方法缺點很明顯就是佔用內存,一般不常見
五、fork子進程調試父進程
void protect_father()
{
pid_t ppid=getppid();
//獲取父進程pid
attach(ppid);
}
void attach(pid_t pid)
{
long err =ptrace(PTRACE_ATTACH,pid,NULL,NULL);
if(err<0)
{
perror("PTRACE_ATTACH");
exit(EXIT_FAILURE);
}
}
分析:由於父進程被子進程調試這裏就可以考慮去直接調試其子進程。
六、自己ptrace自己
void anti_debug01()
{
ptrace(PTRACE_TRACEME,0,0,0);
}
這個之前文章已經講得很詳細說過了
總結
so層反調試過掉有很多方法,最主要的方法就是nop法,動態調試修改返回值等方法,需要注意的是,現在很多的反調試都不會只是單純的反調試,同時還會和許多加密算法聯繫在一起,這樣就會大大增加了分析反調試的能力。