一、遍历运行的进程,对比进程名是否为fridaserver(只需改server名即可绕过)
二、通过遍历端口来检查frida server 10000~65535
UNEXPORT void AntiFrida::check_Server() {
Thread::create_thread_func(check_Server_Thread, nullptr);
}
/**
* 当前函数执行较耗时,需要放入子线程进行
* @return 检查到frida痕迹,返回true,否则返回false
*/
UNEXPORT bool AntiFrida::check_Server_func() {
LOGI("in AntiFrida::check_Server_func");
int sock;
struct sockaddr_in sa;
bzero(&sa,sizeof(sa));
sa.sin_family = AF_INET;
inet_aton("127.0.0.1", &(sa.sin_addr));
if( inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr) < 0){ //set ip address
LOGE(" %s - %d error:%s", __FILE__, __LINE__, strerror(errno));
return false;
}
char res[7];
for (int i = 10000; i <= 65535; i++) {
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1){
LOGI("test socket fail:%s", strerror(errno));
}
sa.sin_port = htons(i);
LOGI("check_Server_func test connect port:%d", i);
if ( connect(sock, (struct sockaddr *) &sa, sizeof(sa)) >= 0) {
LOGI("check_Server_func connect 127.0.0.1:%d", i);
memset(res, 0, 7);
send(sock, "\x00", 1, 0);
send(sock, "AUTH\r\n", 6, 0);
usleep(10); // Give it some time to answer
if ((recv(sock, res, 6, MSG_DONTWAIT)) != -1) {
if (strcmp(res, "REJECT") == 0) {
close(sock);
LOGI("check_Server find frida for port:%d", i);
return true;
}
}
}
close(sock);
}
return false;
}
/**
* 此函数必须作为线程函数被调用
* @param param
* @return
*/
UNEXPORT void* AntiFrida::check_Server_Thread(void* param) {
check_Server_func();
return nullptr;
}
三、maps检测,注入frida后,在/proc/self/maps 中会出现libgadget.so ,或frida-agent-32.so,frida-agent.so等模块
UNEXPORT bool AntiFrida::find_mem_string(size_t start, size_t end, char *bytes, unsigned int len) {
char *pmem = (char *) start;
int matched = 0;
while ((unsigned long) pmem < (end - len)) {
if (*pmem == bytes[0]) {
matched = 1;
char *p = pmem + 1;
while (*p == bytes[matched] && (unsigned long) p < end) {
matched++;
p++;
}
if (matched >= len) {
LOGI("find keyword LIBFRIDA");
return true;
}
}
pmem++;
}
return false;
}
UNEXPORT bool AntiFrida::scan_executable_segments(char *map) {
//LOGI("scan_executable_segments do:%s", map);
char buf[512];
size_t start, end;
sscanf(map, "%lx-%lx %s", &start, &end, buf);
if (Str::strstr(map, (char*)"libgadget") != NULL){
LOGI("find libgadget");
return true;
}
if (buf[2] == 'x' && buf[0] == 'r') { //可执行 还必须可读,不然会无法访问该地址
if (strstr(map, ".so") != NULL || strstr(map, "frida") != NULL){
return (find_mem_string(start, end, keyword, 8) == 1); // 查找LIBFRIDA子串
}
return false;
} else {
return false;
}
}
UNEXPORT char* AntiFrida::keyword = (char*)"LIBFRIDA";
UNEXPORT bool AntiFrida::read_line_scan(string str){
char* split = (char*)"\n";
string strs = str + split; // 在字符串末尾也加入分隔符,方便截取最后一段
size_t pos = strs.find(split);
while (pos != strs.npos)
{
string temp = strs.substr(0, pos);
//LOGI("read_line_scan maps -> %s", temp.c_str());
if (scan_executable_segments(const_cast<char *>(temp.c_str()))){
return true;
}
//去掉已分割的字符串,在剩下的字符串中进行分割
strs = strs.substr(pos + 1, strs.size());
pos = strs.find(split);
}
return false;
}
UNEXPORT bool AntiFrida::check_maps() {
Thread::lock_mutex();
Env::thread_share_switch_maps = true;
Thread::unLock_mutex();
string str = Syscall::readFile((char*)"/proc/self/maps"); // 读取maps查找子串LIBFRIDA
Thread::lock_mutex();
Env::thread_share_switch_maps = false;
Thread::unLock_mutex();
if (str != "null"){
return read_line_scan(str);
}
return false;
}
四、fd检测
UNEXPORT bool AntiFrida::check_fd() {
DIR *dir = NULL;
struct dirent *entry;
char link_name[100];
char buf[100];
bool ret = false;
if ((dir = opendir("/proc/self/fd/")) == NULL) {
LOGE(" %s - %d error:%s", __FILE__, __LINE__, strerror(errno));
} else {
entry = readdir(dir);
while (entry) {
switch (entry->d_type) {
case DT_LNK:
sprintf(link_name, "%s/%s", "/proc/self/fd/", entry->d_name);
readlink(link_name, buf, sizeof(buf));
if (strstr(buf, "frida") || strstr(buf, "gum-js-loop") || strstr(buf, "gmain") ||
strstr(buf, "-gadget") || strstr(buf, "linjector")) {
LOGI("check_fd -> find frida:%s", buf);
ret = true;
}
break;
default:
break;
}
entry = readdir(dir);
}
}
closedir(dir);
return ret;
}
五、status(线程名)检测
UNEXPORT bool AntiFrida::check_status() {
DIR *dir = NULL;
struct dirent *entry;
char status_path[128];
if ((dir = opendir("/proc/self/task/")) == NULL) {
LOGE(" %s - %d error:%s", __FILE__, __LINE__, strerror(errno));
} else {
entry = readdir(dir);
while (entry) {
switch (entry->d_type) {
case DT_DIR:
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
break;
}
sprintf(status_path, "%s/%s/status", "/proc/self/task", entry->d_name);
string status = Syscall::readFile(status_path); // 读取 /proc/self/task/tid/status 文件
if (status == "null"){
break;
}
size_t pos = status.find("\n");
while (pos != status.npos)
{
string temp = status.substr(0, pos);
if (Str::strstr(const_cast<char *>(temp.c_str()), (char*)"Name:") != nullptr){
if (Str::strstr(const_cast<char *>(temp.c_str()), (char*)"gmain") != nullptr ||
Str::strstr(const_cast<char *>(temp.c_str()), (char*)"gum-js-loop") != nullptr ||
Str::strstr(const_cast<char *>(temp.c_str()), (char*)"pool-frida") != nullptr ||
Str::strstr(const_cast<char *>(temp.c_str()), (char*)"gdbus") != nullptr){
LOGI("check_status -> find frida thread:%s", temp.c_str());
// TODO 发现frida
//Crash::crash_no_return();
//Crash::crash_kill(getpid(), SIGKILL); //进程被杀后会重启,然后frida会断开
return true;
}
break;
}
//去掉已分割的字符串,在剩下的字符串中进行分割
status = status.substr(pos + 1, status.size());
pos = status.find("\n");
}
break;
}
entry = readdir(dir);
}
}
closedir(dir);
return false;
}
六、inlinehook检测
// frida注入过程中,会在对libart的PrettyMethod函数进行hook,该函数头的代码会发生改变
// 只提取了安卓10的code
UNEXPORT bool AntiFrida::check_inlinehook(){
bool find = false;
#ifdef __arm__
string st_PrettyMethod = get_method_code("/apex/com.android.runtime/lib/libart.so", "ArtMethod12PrettyMethod");
#elif defined(__aarch64__)
string st_PrettyMethod = get_method_code("/apex/com.android.runtime/lib64/libart.so", "ArtMethod12PrettyMethod");
if (strstr(st_PrettyMethod.c_str(), "50 00 00 58 00 02 1F D6 00 9E 23 ED 78 00 00 00") != NULL){
LOGI("check inlinehook find frida patchcode PrettyMethod -> %s", st_PrettyMethod.c_str());
find = true;
}else{
LOGI("check inlinehook cant find frida patchcode PrettyMethod -> %s", st_PrettyMethod.c_str());
}
#else
#error "Arch unknown, please port me"
#endif
return find;
}
UNEXPORT string AntiFrida::get_method_code(const char* lib, const char* method){
string st;
void* handle = LoadLibrary::fake_dlopen(lib, RTLD_NOW);
if (handle == nullptr){
LOGE("dlopen failed -> %s", strerror(errno));
return st;
}
void* op = (void*)LoadLibrary::fake_dlsym(handle, method);
if (op == nullptr){
LOGE("dlsym failed -> %s", strerror(errno));
LoadLibrary::fake_dlclose(handle);
return st;
}
long pageSize = sysconf(_SC_PAGESIZE);
void* start = (void*)((long)op & -pageSize);
if (mprotect(start, pageSize * 2, PROT_READ | PROT_WRITE | PROT_EXEC) == 0){
LOGI("mprotect success");
}else{
LOGE("mprotect failed -> %s", strerror(errno));
}
char tmp[128] = {0};
sprintf(tmp, "%s %s -> %p\n", lib, method, op);
LOGI("tmp = %s ", tmp);
//st.append(tmp);
unsigned char*code = (unsigned char*)op;
for (int i = 0; i < 16; i++){
if (code[i] < 0x10){
sprintf(tmp, "0%X ",code[i]);
}else{
sprintf(tmp, "%X ",code[i]);
}
st.append(tmp);
}
LOGI("get_method_code -> %s", st.c_str());
LoadLibrary::fake_dlclose(handle);
return st;
}