检测frida-server文件名
检测有没有叫做frida-server
的文件或进程存在。比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public boolean checkRunningProcesses() { boolean returnValue = false; List<RunningServiceInfo> list = manager.getRunningServices(300); if(list != null){ String tempName; for(int i=0;i<list.size();++i){ tempName = list.get(i).process; if(tempName.contains("frida-server")) { returnValue = true; } } } return returnValue; }
|
绕过:改名字
1
| mv frida-server rf-server
|
检查27042默认端口
检查Frida默认27042端口,比如:
1 2 3 4 5 6 7 8 9 10 11
| boolean is_frida_server_listening() { struct sockaddr_in sa; memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_port = htons(27047); inet_aton("127.0.0.1", &(sa.sin_addr)); int sock = socket(AF_INET , SOCK_STREAM , 0); if (connect(sock , (struct sockaddr*)&sa , sizeof sa) != -1) { } }
|
绕过:修改端口。
在adb shell里面执行:
1
| ./rf-server -l 0.0.0.0:11451
|
然后
1 2
| adb forward tcp:11451 tcp:11451 frida -H 127.0.0.1:11451 -f com.baidu.naviauto
|
父子进程调试(双进程保护)
frida -U -f com.shark.tracerpidapp
让Frida去spawn APP父进程,让子进程没法提前Attach
TracerPid反调试
首先ps查看进程号
然后看status
1 2
| adb shell cat /proc/25421/status
|
比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| redfin:/ $ cat /proc/25421/status Name: .baidu.naviauto Umask: 0077 State: S (sleeping) Tgid: 25421 Ngid: 0 Pid: 25421 PPid: 29984 TracerPid: 0 Uid: 10253 10253 10253 10253 Gid: 10253 10253 10253 10253 FDSize: 512 Groups: 3001 3002 3003 9997 20253 50253 VmPeak: 2345508 kB VmSize: 1659672 kB VmLck: 0 kB VmPin: 0 kB VmHWM: 513300 kB VmRSS: 249324 kB RssAnon: 120584 kB RssFile: 123048 kB RssShmem: 5692 kB VmData: 912304 kB VmStk: 8192 kB VmExe: 12 kB VmLib: 154680 kB VmPTE: 1552 kB VmSwap: 0 kB CoreDumping: 0 Threads: 104 SigQ: 0/28561 SigPnd: 0000000000000000 ShdPnd: 0000000000000000 SigBlk: 0000000080001204 SigIgn: 0000000000000001 SigCgt: 0000006e400084f8 CapInh: 0000000000000000 CapPrm: 0000000000000000 CapEff: 0000000000000000 CapBnd: 0000000000000000 CapAmb: 0000000000000000 NoNewPrivs: 0 Seccomp: 2 Speculation_Store_Bypass: thread vulnerable Cpus_allowed: 30 Cpus_allowed_list: 4-5 Mems_allowed: 1 Mems_allowed_list: 0 voluntary_ctxt_switches: 3317 nonvoluntary_ctxt_switches: 413
|
TracerPid就是调试此进程的pid,在同一时刻只能有一个进程调试本进程
在使用IDA android_server对进程进行调试的时候,这个TracerPid
就会变成server的PID。
这是一个检测demo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| #include <jni.h> #include <string> #include "antidebug.h"
#define NULL 0 #define CHECK_TIME 10 #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "shark chilli", __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "shark chilli", __VA_ARGS__)
pthread_t id_anti_debug = NULL;
void readStatus() { FILE *fd; char filename[128]; char line[128]; pid_t pid = syscall(__NR_getpid); LOGI("PID : %d", pid); sprintf(filename, "/proc/%d/status", pid); while (1) { fd = fopen(filename, "r"); while (fgets(line, 128, fd)) { if (strncmp(line, "TracerPid", 9) == 0) { int status = atoi(&line[10]); LOGI("########## status = %d, %s", status, line); fclose(fd); syscall(__NR_close, fd); if (status != 0) { LOGI("########## FBI WARNING ##########"); LOGI("######### FIND DEBUGGER #########"); kill(pid, SIGKILL); return; } break; } } sleep(CHECK_TIME); } }
void checkAnti() { LOGI("Call readStatus..."); readStatus(); }
void anti_debug() { LOGI("Call anti_debug..."); if (pthread_create(&id_anti_debug, NULL, (void *(*)(void *)) &checkAnti, NULL) != 0) { LOGE("Failed to create a debug checking thread!"); exit(-1); }; pthread_detach(id_anti_debug); }
extern "C" JNIEXPORT void JNICALL Java_com_shark_tracerpidapp_MainActivity_checkTracerPid( JNIEnv *env, jobject ) { anti_debug(); }
|
Frida fgets反反调试
通过Hook libc.so
的fgets
函数来检查。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| var anti_fgets = function () { var fgetsPtr = Module.findExportByName("libc.so", "fgets"); var fgets = new NativeFunction(fgetsPtr, 'pointer', ['pointer', 'int', 'pointer']); Interceptor.replace(fgetsPtr, new NativeCallback(function (buffer, size, fp) { var retval = fgets(buffer, size, fp); var bufstr = Memory.readUtf8String(buffer); if (bufstr.indexOf("TracerPid:") > -1) { Memory.writeUtf8String(buffer, "TracerPid:\t0"); } return retval; }, 'pointer', ['pointer', 'int', 'pointer'])); };
|
检测D-Bus
frida-server
使用D-Bus协议通信,我们为每个开放的端口发送D-Bus的认证消息,哪个端口回复了哪个就是frida-server
。比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
for(i = 0 ; i <= 65535 ; i++) { sock = socket(AF_INET , SOCK_STREAM , 0); sa.sin_port = htons(i); if (connect(sock , (struct sockaddr*)&sa , sizeof sa) != -1) { __android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "FRIDA DETECTION [1]: Open Port: %d", i); memset(res, 0 , 7); send(sock, "\x00", 1, NULL); send(sock, "AUTH\r\n", 6, NULL); usleep(100); if (ret = recv(sock, res, 6, MSG_DONTWAIT) != -1) { if (strcmp(res, "REJECT") == 0) { } } } close(sock); }
|
这串代码通过遍历所有端口,向其中开放的端口发送DBus通信来检测是否有Frida存在,而其中最核心的判定点其实是strcmp(res, "REJECT");
,因此可以通过**Hook系统库strstr
,strcmp
**等字符串比对函数来进行绕过。
不过这种字符串比对函数确实没什么设计上的难度,因此如果遇到了自行实现字符串比对的代码的话,那就没戏了。
检测/proc/pid/maps
映射文件
首先手动测试一下,在没有Frida的APP上:
1 2 3
| adb shell su cat /proc/17637/maps
|
然后打印:
1 2 3 4 5 6 7 8 9 10 11
| redfin:/ # cat /proc/17637/maps 08bf0000-08bf2000 r 08bf2000-08bf3000 r-xp 00001000 fd:08 152 /system/bin/app_process32 08bf3000-08bf4000 r 12c00000-32c00000 rw-p 00000000 00:00 0 [anon:dalvik-main space (region space)] 6fdce000-6ffb4000 rw-p 00000000 00:00 0 [anon:dalvik-/system/framework/boot.art] 6ffb4000-6ffe6000 rw-p 00000000 00:00 0 [anon:dalvik-/system/framework/boot-core-libart.art] 6ffe6000-70005000 rw-p 00000000 00:00 0 [anon:dalvik-/system/framework/boot-okhttp.art]70005000-7003a000 rw-p 00000000 00:00 0 [anon:dalvik-/system/framework/boot-bouncycastle.art] 7003a000-7003b000 rw-p 00000000 00:00 0 [anon:dalvik-/system/framework/boot-apache-xml.art] 7003b000-700c9000 r ...
|
等很多很多的玩意出来,表明了进程中内存的分布。
然后再使用Frida的spawn模式去打开APP:
1
| frida -U -f com.baidu.naviauto
|
然后用同样的方式拿到maps,经过搜索会发现有frida
相关的字符串。因此判断代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| char line[512]; FILE* fp; fp = fopen("/proc/self/maps", "r"); if (fp) { while (fgets(line, 512, fp)) { if (strstr(line, "frida")) { } } fclose(fp); } else { } }
|
还有一种改进版本,就是通过进一步进行内存字符串搜索,来检查Frida库特征,比如LIBFRIDA
:https://github.com/b-mueller/frida-detection-demo/blob/master/AntiFrida/app/src/main/cpp/native-lib.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| static char keyword[] = "LIBFRIDA"; num_found = 0; int scan_executable_segments(char * map) { char buf[512]; unsigned long start, end; sscanf(map, "%lx-%lx %s", &start, &end, buf); if (buf[2] == 'x') { return (find_mem_string(start, end, (char*)keyword, 8) == 1); } else { return 0; } } void scan() { if ((fd = my_openat(AT_FDCWD, "/proc/self/maps", O_RDONLY, 0)) >= 0) { while ((read_one_line(fd, map, MAX_LINE)) > 0) { if (scan_executable_segments(map) == 1) { num_found++; } } if (num_found > 1) { } }
|
直接使用Hluda Frida Server
是一种方案。