❝
安卓的最新版 B 站 APP(7.76.0)有反 FRIDA 机制,本文介绍一下如何绕过它
❞
问题说明
截止到 2024 年 5 月 1 日,B 站最新版的安卓 APP(7.76.0)有反 Frida 机制,不管是 spawn 还是 attach,都无法注入 frida,如下图所示。本文介绍一下如何绕过它
方法
定位检测点
检测 Frida 的机制一般在 Native 层实现,通常会创建几个线程轮询检测。首先要知道检测机制是由哪个 so 实现的,通过 hook android_dlopen_ext 函数,观察加载到哪个 so 的时候,触发反调试进程终止即可。Frida 脚本代码与输出如下,最终可以定位到检测点在 libmsaoaidsec.so 中。
var interceptor = Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"),
{
onEnter: function (args) {
var pathptr = args[0];
if (pathptr !== undefined && pathptr != null) {
var path = ptr(pathptr).readCString();
console.log("[LOAD]", path)
}
},
}
)
绕过检测点
对抗
使用 IDA 载入 libmsaoaidsec.so,发现没有导入 pthread_create 符号
使用 Frida 脚本尝试 HOOK pthread_create 函数,也没有找到来自 libmsaoaidsec.so 的调用
var interceptor = Interceptor.attach(Module.findExportByName(null, "pthread_create"),
{
onEnter: function (args) {
var module = Process.findModuleByAddress(ptr(this.returnAddress))
if (module != null) {
console.log("[pthread_create] called from", module.name)
}
else {
console.log("[pthread_create] called from", ptr(this.returnAddress))
}
},
}
)
分析
尽管表现有点奇怪,但它一定会调用 pthread_create 创建检测线程。所以尝试 hook dlsym 函数,在加载 libmsaoaidsec.so 之前挂钩 dlsym 函数,代码如下
function hook_dlsym() {
var count = 0
console.log("=== HOOKING dlsym ===")
var interceptor = Interceptor.attach(Module.findExportByName(null, "dlsym"),
{
onEnter: function (args) {
const name = ptr(args[1]).readCString()
// const module = Process.findModuleByAddress(ptr(this.returnAddress))
console.log("[dlsym]", name)
if (name == "pthread_create") {
count++
}
}
}
)
return Interceptor
}
function hook_dlopen() {
var interceptor = Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"),
{
onEnter: function (args) {
var pathptr = args[0];
if (pathptr !== undefined && pathptr != null) {
var path = ptr(pathptr).readCString();
console.log("[LOAD]", path)
if (path.indexOf("libmsaoaidsec.so") > -1) {
hook_dlsym()
}
}
},
}
)
return interceptor
}
var dlopen_interceptor = hook_dlopen()
输出如下,在加载 libmsaoaidsec.so 后,调用了 2 次 dlsym 获取 pthread_create 函数,然后进程就终止了。证明它确实会调用 pthread_create,只是调用方式不是直接调用,可能采取了一些对抗手段。
绕过
它应该是使用了一些反 hook 的手段,没有必要和它正面对抗。简单描述一下我的绕过策略,创建一个 fake_pthread_create 函数,它只有一条 ret 汇编指令,然后 hook 来自 libmsaoaidsec.so 的前 2 次对 dlsym 的调用,返回 fake_pthread_create 函数的地址,这样就达成了欺骗它调用 fake_pthread_create 函数的目的。最终代码如下
function create_fake_pthread_create() {
const fake_pthread_create = Memory.alloc(4096)
Memory.protect(fake_pthread_create, 4096, "rwx")
Memory.patchCode(fake_pthread_create, 4096, code => {
const cw = new Arm64Writer(code, { pc: ptr(fake_pthread_create) })
cw.putRet()
})
return fake_pthread_create
}
function hook_dlsym() {
var count = 0
console.log("=== HOOKING dlsym ===")
var interceptor = Interceptor.attach(Module.findExportByName(null, "dlsym"),
{
onEnter: function (args) {
const name = ptr(args[1]).readCString()
console.log("[dlsym]", name)
if (name == "pthread_create") {
count++
}
},
onLeave: function(retval) {
if (count == 1) {
retval.replace(fake_pthread_create)
}
else if (count == 2) {
retval.replace(fake_pthread_create)
// 完成2次替换, 停止hook dlsym
interceptor.detach()
}
}
}
)
return Interceptor
}
function hook_dlopen() {
var interceptor = Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"),
{
onEnter: function (args) {
var pathptr = args[0];
if (pathptr !== undefined && pathptr != null) {
var path = ptr(pathptr).readCString();
console.log("[LOAD]", path)
if (path.indexOf("libmsaoaidsec.so") > -1) {
hook_dlsym()
}
}
}
}
)
return interceptor
}
// 创建虚假pthread_create
var fake_pthread_create = create_fake_pthread_create()
var dlopen_interceptor = hook_dlopen()
执行「frida -U -f tv.danmaku.bili -l bypass.js」后即可绕过反 frida 机制,如下图所示
代码也可从 github 下载,见引用[1]。
引用
[1] https://github.com/ddddhm1234/bypass_bilibili/
原文始发于微信公众号(网络空间威胁观察):绕过最新版bilibili APP反Frida机制