检测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;
// Get currently running application processes
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) {
/* Frida server detected. Do something… */
}
}

绕过:修改端口。

在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查看进程号

1
frida-ps -U

然后看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);// 读取proc/pid/status中的TracerPid
while (1) {
fd = fopen(filename, "r");
while (fgets(line, 128, fd)) {
if (strncmp(line, "TracerPid", 9) == 0) {
//TracerPid值
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 /* this */) {
anti_debug();
}

Frida fgets反反调试

通过Hook libc.sofgets函数来检查。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var anti_fgets = function () {
// dmLogout("anti_fgets");
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");
// dmLogout("tracerpid replaced: " + Memory.readUtf8String(buffer));
}
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
/*
* Mini-portscan to detect frida-server on any local port.
*/
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 a D-Bus AUTH message. Expected answer is “REJECT"
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) {
/* Frida server detected. Do something… */
}
}
}
close(sock);
}

这串代码通过遍历所有端口,向其中开放的端口发送DBus通信来检测是否有Frida存在,而其中最核心的判定点其实是strcmp(res, "REJECT");,因此可以通过**Hook系统库strstrstrcmp**等字符串比对函数来进行绕过。

不过这种字符串比对函数确实没什么设计上的难度,因此如果遇到了自行实现字符串比对的代码的话,那就没戏了。

检测/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--p 00000000 fd:08 152 /system/bin/app_process32
08bf2000-08bf3000 r-xp 00001000 fd:08 152 /system/bin/app_process32
08bf3000-08bf4000 r--p 00001000 fd:08 152 /system/bin/app_process32
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--p 00000000 fd:08 1184 /system/framework/arm/boot.oat
...

等很多很多的玩意出来,表明了进程中内存的分布。

然后再使用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")) {
/* Evil library is loaded. Do something… */
}
}
fclose(fp);
} else {
/* Error opening /proc/self/maps. If this happens, something is off. */
}
}

还有一种改进版本,就是通过进一步进行内存字符串搜索,来检查Frida库特征,比如LIBFRIDAhttps://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) {
/* Frida Detected */
}
}

直接使用Hluda Frida Server是一种方案。