Frida
进程与端口:
- 默认端口:
27042
(Frida Server 通信端口),可能被修改为其他端口(如6666
、9999
)。 - 进程名:
- 典型进程:
frida-server
、frida-helper
(Android)、frida-agent
(嵌入式模式)。 - 可能伪装为普通进程名(如
com.android.settings
)。
- 典型进程:
文件痕迹:
- 二进制文件:
- 常见路径:
/data/local/tmp/frida-server
、/data/local/tmp/re.frida.server
。 - 嵌入式模式:应用内集成
frida-gadget.so
(路径如/data/app/<包名>/lib/arm64/libfrida-gadget.so
)。
- 常见路径:
- 动态库:
- 加载
libfrida.so
、libfrida-gumjs.so
或libagent.so
。 - 可能被重命名(如
libhello.so
)以绕过静态检测。
- 加载
行为特征:
- 代码注入:
- 使用
ptrace
或LD_PRELOAD
注入代码,劫持关键函数(如open
、fopen
)。 - 修改内存页权限(
mprotect
或mmap
)以动态执行代码。
- 使用
- 内存特征:
- 内存中存在字符串
frida-gadget
、frida:rpc
、gum-js-loop
、FridaScriptEngine
。 - 函数指针表(如
GOT/PLT
)被篡改,指向 Frida 的代理代码。
- 内存中存在字符串
- Hook 检测:
- 函数完整性校验:
- 检查关键函数(如
ptrace
、open
)的前几条指令是否被修改(如JMP
指令)。 - 对比函数代码的哈希值(如
.text
段的 SHA256)与预期是否一致。
- 检查关键函数(如
- 内存映射异常:
- 检测内存中是否存在可写且可执行的页(
rwx
权限),常见于 Frida 的代码注入。
- 检测内存中是否存在可写且可执行的页(
- Inline Hook 检测:
- 扫描内存中
FAR JUMP
(远跳转指令)或trampoline
代码片段。
- 扫描内存中
- 函数完整性校验:
检测方法:
-
静态检测:
- 扫描 APK 或二进制文件中的
frida
相关字符串、库文件或端口配置。 - 检查
AndroidManifest.xml
中是否声明可疑权限(如ptrace
、注入
)。
- 扫描 APK 或二进制文件中的
-
动态检测:
- 进程与线程扫描:
- 遍历
/proc/<pid>/cmdline
和/proc/<pid>/status
,查找frida
相关进程或线程名。 - 检测异常线程(如
pool-frida
、gmain
)。
- 遍历
- 内存映射分析:
- 检查
/proc/self/maps
或/proc/<pid>/maps
中是否存在frida
、gadget
或匿名可执行内存段。
- 检查
- 主动探测:
- 尝试连接默认端口(
127.0.0.1:27042
),若返回{"type":"error"}
则可能存在 Frida。 - 调用
frida_get_uid()
或frida_enumerate_devices()
等未公开函数,若未崩溃则可能未注入。
- 尝试连接默认端口(
- 进程与线程扫描:
-
Hook 防御:
- 反 Hook 技术:
- 使用
syscall
直接调用系统函数(绕过libc
的 Hook)。 - 在敏感函数入口插入
SIGTRAP
或INT3
指令,触发断点异常。
- 使用
- 完整性校验:
- 使用
dl_iterate_phdr()
遍历已加载模块,检查是否有未签名的库(如libfrida
)。 - 实时校验关键函数代码(如
memcmp
对比.text
段内容)。
- 使用
- 反 Hook 技术:
-
对抗隐藏技术:
- 检测重命名进程:
- 监控进程的父进程和子进程关系(Frida 可能由
adb
或shell
启动)。
- 监控进程的父进程和子进程关系(Frida 可能由
- 检测端口复用:
- 使用
netstat
或/proc/net/tcp
分析本地端口通信模式(Frida 通信流量有固定特征)。
- 使用
- 检测重命名进程:
Root
- 核心用途:获取 Android 系统的最高权限(
root
)。 - 特征:
- 文件系统痕迹:
- 存在
su
二进制文件(路径如/system/bin/su
,/system/xbin/su
)。 - Root 管理应用(如 SuperSU、Magisk 等)的安装痕迹。
- 存在
- 系统属性:
ro.build.tags
可能包含test-keys
(非官方系统签名)。ro.debuggable=1
(系统允许调试)。
- 行为特征:
/system
分区被挂载为可写(正常应为只读)。- SELinux 状态为
Permissive
或关闭。
- 检测方法:
- 检查
su
命令是否存在或尝试执行su -v
。 - 验证系统分区的哈希或只读属性(如
mount | grep /system
)。 - 使用
SafetyNet API
(已废弃)或Play Integrity API
(Google 官方检测)。
- 检查
- 文件系统痕迹:
Xposed 框架
- 核心用途:通过加载模块在系统层面 Hook 应用和系统方法。
- 特征:
- 文件痕迹:
- 安装 Xposed 管理应用(如
Xposed Installer
)。 - 存在 Xposed 库文件(如
libxposed_art.so
)。
- 安装 Xposed 管理应用(如
- 运行时特征:
- 应用进程加载 Xposed 模块(如
XposedBridge.jar
)。 - 系统类(如
java.lang.ClassLoader
)被修改。
- 应用进程加载 Xposed 模块(如
- 检测方法:
- 检查
XposedHelper
类或de.robv.android.xposed.XposedBridge
是否存在。 - 遍历已安装应用列表,寻找 Xposed 模块包名(如
de.robv.android.xposed.installer
)。 - 监控方法调用栈中是否包含 Xposed 相关调用(如
XposedBridge.handleHookedMethod
)。
- 检查
- 文件痕迹:
8poll
应用模拟器 root 环境下会跳出 toast 警告并自动退出
尝试 hook toast
包名 com.miniclip.eightballpool
Java.perform(function() {
var Toast = Java.use("android.widget.Toast");
Toast.makeText.overload('android.content.Context', 'java.lang.CharSequence', 'int').implementation = function(context: any, text: any, duration: any) {
console.log("[!] Toast.makeText called with text: " + text);
var stackTrace = Java.use("java.lang.Exception").$new().getStackTrace();
for (var i = 0; i < stackTrace.length; i++) {
console.log("Stack Trace: " + stackTrace[i].toString());
}
return this.makeText(context, text, duration);
};
Toast.show.implementation = function() {
console.log("[!] Toast.show called");
var stackTrace = Java.use("java.lang.Exception").$new().getStackTrace();
for (var i = 0; i < stackTrace.length; i++) {
console.log("Stack Trace: " + stackTrace[i].toString());
}
// 调用原始 show 方法
return this.show();
};
});
从堆栈追踪来看,InjectedActivity
类中的 showToast
方法在 handleExitToast
中被调用。
因此,这个 Toast
是通过 InjectedActivity
类中的 handleExitToast
方法间接调用的。如果你想要更深入地分析 Toast
被调用的具体逻辑,应该查看 InjectedActivity
中 showToast
和 handleExitToast
方法的实现。
runtime.loading.InjectedActivity.showPopup
加载了 libloader
通过库可以跟踪到 io.adjoe.protection.DeviceUtils
调用了可疑的检测 emulator 的方法
应该是通过 org.json.JSONObject
这个 Mozilla 的库来传递设备当前的信息。然后通过读取 JSON 来判断异常情况。
可以 hook 结果看看,然后看看能不能改变值绕过去
试一下 toString()
方法:
竟然 hook 不到,我研究一下整个调用逻辑链看看,是不是我搞错了(还没到这步就挂了)