普通的检测总结以下几种手段:
检测/data/local/tmp下的frida特征文件,frida默认端口27042
双进程检测
检测/proc/pid/maps、/proc/pid/task/tid/stat、/proc/pid/fd中的frida特征
检测D-BUS
安卓系统使用Binder机制来实现进程间通信(IPC),而不是使用D-Bus。检测函数向所有端口发送d-bus消息,如果返回reject就说明fridaserver开启。
我们可以看到,上面使用的绕过技巧,大多都和系统函数有关系,那么如果app中不调用这些系统函数,而是用自实现的函数来进行操作,不就很难hook了吗?
这里就有一种自实现函数的技术:svc
。
通过安卓架构的学习,我们知道了安卓从上到下也是由层来分隔的,而层与层之间不能直接交互,而是需要一个中间层来进行操作。我们常见的jni
就是Java
层和native
层的交互。而syscall
就是kernel
和native
之间的中间层。
svc
是x86架构中的一个指令,用于在用户模式下发起系统调用。当执行svc
指令时,处理器会从用户态转换为内核态,执行内核级别的命令。

开发者可以通过syscall
来执行内核函数,而不是直接使用系统函数,下面介绍几种防护手段:
直接使用syscall替代libc函数:
不使用标准C库函数,而是直接调用系统调用。这样可以绕过常见的hook点,因为大多数hook工具主要针对libc函数。
实现关键功能的自定义syscall wrapper:
为关键的系统调用创建自己的包装函数
动态生成syscall:
在运行时动态生成syscall指令(直接使用机器码,和下面的汇编差不多)
使用汇编实现syscall
:
直接使用汇编语言实现系统调用
最后再举一个实际检测frida的syscall
例子:
网上对frida的检测通常会使用openat、open、strstr、pthread_create、snprintf、sprintf、readlinkat等一系列函数,这里hook了strstr和strcmp函数。
检测点说明:
我们通过上面的学习看到,自实现系统函数一个重要的前提就是它们都有标准的系统调用号,标准的机器码。所以我们绕过的时候也可以用同样的思路。
Frida的Memory API可以直接查找整个系统的内存内容,我们直接搜索对应函数的特征码,定位到之后再使用Interceptor进行Hook。(要注意每个架构对应的特征可能不一样)
#include <sys/syscall.h> //SYS_open SYS_read SYS_close都是syscall.h中的常量
#include <unistd.h>
#include <fcntl.h>
int
my_open(
const
char
*pathname,
int
flags) {
return
syscall(SYS_open, pathname, flags);
}
ssize_t my_read(
int
fd,
void
*buf,
size_t
count) {
return
syscall(SYS_read, fd, buf, count);
}
int
my_close(
int
fd) {
return
syscall(SYS_close, fd);
}
int
main() {
int
fd = my_open(
"/path/to/file"
, O_RDONLY);
if
(fd != -1) {
char
buffer[100];
ssize_t bytes_read = my_read(fd, buffer,
sizeof
(buffer));
my_close(fd);
}
return
0;
}
#include <sys/syscall.h> //SYS_open SYS_read SYS_close都是syscall.h中的常量
#include <unistd.h>
#include <fcntl.h>
int
my_open(
const
char
*pathname,
int
flags) {
return
syscall(SYS_open, pathname, flags);
}
ssize_t my_read(
int
fd,
void
*buf,
size_t
count) {
return
syscall(SYS_read, fd, buf, count);
}
int
my_close(
int
fd) {
return
syscall(SYS_close, fd);
}
int
main() {
int
fd = my_open(
"/path/to/file"
, O_RDONLY);
if
(fd != -1) {
char
buffer[100];
ssize_t bytes_read = my_read(fd, buffer,
sizeof
(buffer));
my_close(fd);
}
return
0;
}
#include <sys/syscall.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
ssize_t secure_read(
int
fd,
void
*buf,
size_t
count) {
if
(syscall(SYS_gettid) != syscall(SYS_getpid)) {
return
-1;
}
ssize_t bytes_read = syscall(SYS_read, fd, buf, count);
if
(bytes_read > 0) {
for
(ssize_t i = 0; i < bytes_read; i++) {
((
char
*)buf)[i] ^= 0x55;
}
}
return
bytes_read;
}
#include <sys/syscall.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
ssize_t secure_read(
int
fd,
void
*buf,
size_t
count) {
if
(syscall(SYS_gettid) != syscall(SYS_getpid)) {
return
-1;
}
ssize_t bytes_read = syscall(SYS_read, fd, buf, count);
if
(bytes_read > 0) {
for
(ssize_t i = 0; i < bytes_read; i++) {
((
char
*)buf)[i] ^= 0x55;
}
}
return
bytes_read;
}
#include <sys/mman.h>
#include <string.h>
typedef
long
(*syscall_fn)(
long
, ...);
syscall_fn generate_write_syscall() {
void
* mem = mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
unsigned
char
code[] = {
0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00,
0x0f, 0x05,
0xc3
};
memcpy
(mem, code,
sizeof
(code));
return
(syscall_fn)mem;
}
int
main() {
syscall_fn my_write = generate_write_syscall();
const
char
*msg =
"Hello, World!\n"
;
my_write(1, msg,
strlen
(msg));
return
0;
}
#include <sys/mman.h>
#include <string.h>
typedef
long
(*syscall_fn)(
long
, ...);
syscall_fn generate_write_syscall() {
void
* mem = mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
unsigned
char
code[] = {
0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00,
0x0f, 0x05,
0xc3
};
memcpy
(mem, code,
sizeof
(code));
return
(syscall_fn)mem;
}
int
main() {
syscall_fn my_write = generate_write_syscall();
const
char
*msg =
"Hello, World!\n"
;
my_write(1, msg,
strlen
(msg));
return
0;
}
#include <jni.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
JNIEXPORT jboolean JNICALL
Java_com_example_SecurityCheck_detectFrida(JNIEnv *env, jobject thiz) {
char
line[256];
int
fd = syscall(SYS_open,
"/proc/self/maps"
, O_RDONLY);
if
(fd != -1) {
while
(syscall(SYS_read, fd, line,
sizeof
(line)) > 0) {
if
(
strstr
(line,
"frida"
) ||
strstr
(line,
"gum-js-loop"
)) {
syscall(SYS_close, fd);
return
JNI_TRUE;
}
}
syscall(SYS_close, fd);
}
return
JNI_FALSE;
}
#include <jni.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
JNIEXPORT jboolean JNICALL
Java_com_example_SecurityCheck_detectFrida(JNIEnv *env, jobject thiz) {
char
line[256];
int
fd = syscall(SYS_open,
"/proc/self/maps"
, O_RDONLY);
if
(fd != -1) {
while
(syscall(SYS_read, fd, line,
sizeof
(line)) > 0) {
if
(
strstr
(line,
"frida"
) ||
strstr
(line,
"gum-js-loop"
)) {
syscall(SYS_close, fd);
return
JNI_TRUE;
}
}
syscall(SYS_close, fd);
}
return
JNI_FALSE;
}
function
replace_str() {
var
pt_strstr = Module.findExportByName(
"libc.so"
,
'strstr'
);
var
pt_strcmp = Module.findExportByName(
"libc.so"
,
'strcmp'
);
Interceptor.attach(pt_strstr, {
onEnter:
function
(args) {
var
str1 = args[0].readCString();
var
str2 = args[1].readCString();
if
(str2.indexOf(
"tmp"
) !== -1 ||
str2.indexOf(
"frida"
) !== -1 ||
str2.indexOf(
"gum-js-loop"
) !== -1 ||
str2.indexOf(
"gmain"
) !== -1 ||
str2.indexOf(
"gdbus"
) !== -1 ||
str2.indexOf(
"pool-frida"
) !== -1||
str2.indexOf(
"linjector"
) !== -1) {
this
.hook =
true
;
}
}, onLeave:
function
(retval) {
if
(
this
.hook) {
retval.replace(0);
}
}
});
Interceptor.attach(pt_strcmp, {
onEnter:
function
(args) {
var
str1 = args[0].readCString();
var
str2 = args[1].readCString();
if
(str2.indexOf(
"tmp"
) !== -1 ||
str2.indexOf(
"frida"
) !== -1 ||
str2.indexOf(
"gum-js-loop"
) !== -1 ||
str2.indexOf(
"gmain"
) !== -1 ||
str2.indexOf(
"gdbus"
) !== -1 ||
str2.indexOf(
"pool-frida"
) !== -1||
str2.indexOf(
"linjector"
) !== -1) {
this
.hook =
true
;
}
}, onLeave:
function
(retval) {
if
(
this
.hook) {
retval.replace(0);
}
}
})
}
replace_str(); 一把梭方案
function
replace_str() {
var
pt_strstr = Module.findExportByName(
"libc.so"
,
'strstr'
);
var
pt_strcmp = Module.findExportByName(
"libc.so"
,
'strcmp'
);
Interceptor.attach(pt_strstr, {
onEnter:
function
(args) {
var
str1 = args[0].readCString();
var
str2 = args[1].readCString();
if
(str2.indexOf(
"tmp"
) !== -1 ||
[招生]科锐逆向工程师培训(2025年3月11日实地,远程教学同时开班, 第52期)!