APK防護(hù)中Anti_Virtual App的思路和實(shí)現(xiàn)是怎樣的,很多新手對此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。
創(chuàng)新互聯(lián)公司是一家集網(wǎng)站建設(shè),安陸企業(yè)網(wǎng)站建設(shè),安陸品牌網(wǎng)站建設(shè),網(wǎng)站定制,安陸網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營銷,網(wǎng)絡(luò)優(yōu)化,安陸網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競爭力。可充分滿足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時(shí)我們時(shí)刻保持專業(yè)、時(shí)尚、前沿,時(shí)刻以成就客戶成長自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。
Virtual App是一個(gè)很強(qiáng)大的存在,破壞了Android 系統(tǒng)本身的隔離措施,可以進(jìn)行免root hook和其他黑科技操作,你可以用這個(gè)做很多在原來APP里做不到事情,于此同時(shí)Virtual App的安全威脅也不言而喻??梢匀タ纯催@篇文章。VirtualApp技術(shù)黑產(chǎn)利用研究報(bào)告。
當(dāng)然還有其他東西,可以去各大論壇進(jìn)行深度挖掘。
我們這次的重點(diǎn)是放在Anti_Virtual App上。
我也不知道用什么來命名,感覺要是把思路直接寫在標(biāo)題里之后不好展開。不啰嗦了正文開始。
我們要防止App在VirtualAPP上運(yùn)行就要通過Virtual啟動App時(shí)的一些特征來逆向分析,VirtualAPP是開源的我們也可以結(jié)合源碼來進(jìn)行分析。
Android應(yīng)用隔離是基于Linux系統(tǒng)的多用戶機(jī)制實(shí)現(xiàn)的,即每個(gè)應(yīng)用在安裝時(shí)被分配了不同的Linux用戶uid/gid。而在VirtualApp中,client應(yīng)用(通過VirtualApp安裝的應(yīng)用)與host應(yīng)用(即VirtualApp本身)是具有相同用戶uid的。
這個(gè)是在Virtual資料里的介紹,這里有一個(gè)值得關(guān)注的地方就是,client應(yīng)用和host應(yīng)用具有相同的uid。
我們來進(jìn)行一個(gè)測試。
這個(gè)是我們運(yùn)行在正常環(huán)境下的。
用grep過濾一下。然后我們運(yùn)行在虛擬機(jī)下用grep過濾一下。
有一個(gè)前提就是,Android 系統(tǒng)中的UID是在app安裝的時(shí)候進(jìn)行分配的,之后是不會進(jìn)行更改的。而且為了可以進(jìn)行沙箱和隔離,每一個(gè)APP分配到的UID是不同的,而且不同的UID僅僅擁有一個(gè)進(jìn)程。這是Linux的多用戶系統(tǒng)被閹割下成為了現(xiàn)在的狀態(tài),當(dāng)然也提高了APP的安全性。相同的UID具有共享的特性。
就這個(gè)不同的點(diǎn),我們進(jìn)行測試的實(shí)現(xiàn)。
實(shí)現(xiàn)的方法直接使用NDK來進(jìn)行開發(fā),或者使用java也可以。
實(shí)現(xiàn)思路就是我在app里調(diào)用ps |grep,拿到返回行數(shù),簡單粗暴易懂,可能會有bug,針對一般情況。輕踩。
我們需要做的第一步就是,獲取到APP的UID對應(yīng)的UNAME。
我這里使用的是封裝方法。
struct passwd *pwd; pwd = getpwuid(getuid()); char *find=pwd->pw_name;
這樣可以直接拿到UNAME
然后我們使用字符串拼接,將命令結(jié)合,我是不是太啰嗦了。。。
char cmd[20]="ps | grep "; LOGD("%s",cmd); strcat(cmd,find); LOGD("%s",cmd);
使用popen進(jìn)行命令的運(yùn)行
int getEnd(char * cmd){ FILE *pp = popen(cmd, "r"); //建立管道 if (!pp) { LOGD("error"); } int i=0; char tmp[1024]; //設(shè)置一個(gè)合適的長度,以存儲每一行輸出 while (fgets(tmp, sizeof(tmp), pp) != NULL) { if (tmp[strlen(tmp) - 1] == '\n') { tmp[strlen(tmp) - 1] = '\0'; //去除換行符 i++; } LOGD("%s",tmp); } LOGD("i:%d",i); return i; }
最后進(jìn)行調(diào)用判斷。整合一下最后的結(jié)果。
struct passwd *pwd; pwd = getpwuid(getuid()); char *find=pwd->pw_name; LOGD("%s",find); char cmd[20]="ps | grep "; LOGD("%s",cmd); strcat(cmd,find); LOGD("%s",cmd); int i =getEnd(cmd); if (i>4) { LOGD("This is VA!"); kill(0, SIGKILL); }
Virtual App有一個(gè)特點(diǎn),就是在運(yùn)行app的時(shí)候,如果存在so文件的話,會將so文件拷貝到自己的目錄下,那么是不是可以對so文件路勁進(jìn)行讀取,然后進(jìn)行判斷,就可以區(qū)分開Virtual App和正常運(yùn)行環(huán)境呢。
這個(gè)實(shí)現(xiàn)要對/proc/PID/maps這個(gè)文件進(jìn)行分析。實(shí)現(xiàn)起來可能有點(diǎn)復(fù)雜。去git上看看有沒有開源項(xiàng)目。
最后鎖定了一個(gè)目標(biāo)。https://github.com/ysrc/AntiVirtualApp
(1)拿到PID
(2)拿到/proc/PID/maps
(3) 拿到包名
(4)拿到SO路徑
(5)分析比對
拿到當(dāng)前進(jìn)程的PID的方法很多。這里有一個(gè)很簡單的方法就是
int pid=getpid();
當(dāng)然還有另外一種就是通過對java層進(jìn)行反射拿到pid,兩種都是實(shí)現(xiàn)了的。
反射三步走,輕松拿到,這里對返回值的掌控還沒有到輕車熟路的程度,但是這樣子的反射還是可以拿到的。
//反射拿到pid jclass Process=env->FindClass("android/os/Process"); jmethodID myPid=env->GetStaticMethodID(Process,"myPid","()I"); LOGD("%d",(int)env->CallStaticIntMethod(Process,myPid)); return (int)env->CallStaticIntMethod(Process,myPid);
這里通過文件拼接讀取就可以拿到文件指針了。
char data[256]; char s[64] = {0}; int pid=getpid(); sprintf(s, "/proc/%d/maps", pid); FILE *fd = fopen(s, "r"); if (fd==NULL) { LOGD("The file is field"); } else { LOGD("ok"); }
包名可以通過/proc/PID/cmdline這個(gè)文件來拿到
我們還是進(jìn)行相同的操作。然后對文件進(jìn)行處理,最后拿到了我們的包名。
char *buffer = (char *) malloc(1024); memset(buffer, 0, 1024); char path_t[256] = {0}; int pid = getpid(); sprintf(path_t, "/proc/%d/cmdline", pid); int fd = open(path_t, O_RDONLY); if (fd > 0) { int read_count = (int) read(fd, buffer, 1024); close(fd); if (read_count > 0) { return buffer; } } free(buffer); return NULL;
進(jìn)行一個(gè)測試:
對我們拿到的maps進(jìn)行處理。
char path[128] = {0}; char uid[10] = {0}; char * filter="libnative-lib.so"; while (fgets(data, 256, fd)) { int len = (int) strlen(data); if (len <= 0) { continue; } data[--len] = '\0'; if (sscanf(data, "%*llx-%*llx %s %*s %*s %*s %s", uid, path) != 2) { continue; } LOGD("%s",data); if (strcmp(uid, "r-xp") == 0 && endsWith(path, filter)) { LOGD("getSoPath2:%s",path); break; } }
進(jìn)行測試
現(xiàn)在so也拿到了。
比對的原理是so加載的地方大多只有三個(gè),通過這三個(gè)加上包名進(jìn)行比對,然后就可以發(fā)現(xiàn)VirtualApp下運(yùn)行的App的so包地址已經(jīng)更改為VirtualApp的地址,原因很有可能就是因?yàn)楦綦x的特性不能越界訪問。
size_t len = strlen(p); int i=0; if (strstr(path,p) != NULL) { if (startsWith(path, SO_APP_LIB)) { if (strncmp(path + SO_APP_LIB_LEN, p, len)) { i++; } } else if (startsWith(path, SO_DATA_APP)) { if (strncmp(path + SO_DATA_APP_LEN, p, len)) { i++; } } else if (startsWith(path, SO_DATA_DATA)) { if (strncmp(path + SO_DATA_DATA_LEN, p, len)) { i++; } } }
在正常的環(huán)境下運(yùn)行
在VirtualApp下運(yùn)行
看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進(jìn)一步的了解或閱讀更多相關(guān)文章,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對創(chuàng)新互聯(lián)的支持。