安卓逆向某某单词,反frida注入完美绕过

本文最后更新于 2025年1月8日 下午

好久没更新博客,所以今天来给大家分享一个案例,某某单词app,由于该app有反调试,本文就介绍一下如何绕过他的反调试


参考资料

绕过最新版bilibili app反frida机制

绕过bilibili frida反调试


开始

检测Frida的机制一般在Native层实现,通常会创建几个线程轮询检测。首先要知道检测机制是由哪个so实现的,通过hook android_dlopen_ext函数,观察加载到哪个so的时候,触发反调试进程终止即可。下面我们来试一下

1
2
3
4
5
6
7
8
9
10
11
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);
}
}
}
);

可以看到,在加载libmsaoaidsec.so后,frida挂掉了

现在直接注入我们的线程替换脚本,看看这个so加载的线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function check_pthread_create(name = null) {
var pthread_create_addr = Module.findExportByName(null, 'pthread_create');

var pthread_create = new NativeFunction(pthread_create_addr, "int", ["pointer", "pointer", "pointer", "pointer"]);
Interceptor.replace(pthread_create_addr, new NativeCallback(function (parg0, parg1, parg2, parg3) {
var module = Process.findModuleByAddress(parg2)
var so_base = module.base;
var off = "0x" + parg2.sub(so_base).toString(16)
var so_name = module.name;
console.log(so_name, off, parg3)

return pthread_create(parg0, parg1, parg2, parg3);


}, "int", ["pointer", "pointer", "pointer", "pointer"]))
}
setImmediate(check_pthread_create)

这里创建了三个线程,内存偏移量为0x1c5440x1b8d40x26e5c注:画线的地方不是检测线程,请看清楚so名字

现在回去改dlopen,如果这个so一加载就nop线程

结果不尽人意,这里获取不了libmsaoaidsec.so的基址

因为安卓加载so整个流程如下

1
linker->init_proc ->JNI_OnLoad 

dlopen获取基址,是要等jni加载后的,但是现在jni还没加载我们的frida就被杀掉了,说明检测在init_proc里,我们需要寻找一个合适的hook时机

现在我们打开ida,把so拖进去,静态分析

这里我尝试过寻找发起线程的地方,但从导入函数并没有看到,pthread_create相关字样,他应该是间接调用的,但是我们已经知道了线程的偏移量,所以这里不太重要了

接下来我们继续看init_proc ,这里被ollvm混淆了,转成伪c代码,通过交叉引用,可以得到他是先执行sub_123f0

可以看到sub_123f0_system_property_get调用了一个ro.build.version.sdk,这里我们从注入system_property_get是个不错的选择,因为他是在init.proc阶段执行的

现在我们去hook_system_property_get

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
function locate_init() {
let r = null
Interceptor.attach(Module.findExportByName(null, "__system_property_get"),
{
onEnter: function (args) {
var name = args[0];
if (name !== undefined && name != null) {
name = ptr(name).readCString();
console.log(name)
if (name.indexOf("ro.build.version.sdk") >= 0) {
console.log(Process.findModuleByName("libmsaoaidsec.so").base)
}
}
}
}
);
}

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();
if(path.search("libmsaoaidsec.so") != -1){
this.hook = true
locate_init()
}
}
}
}
);

输出如下,我们成功获取到了基址

那我们就nop上面提到的偏移量吧

完美绕过frida检测


完整代码

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
function locate_init() {
let r = null
Interceptor.attach(Module.findExportByName(null, "__system_property_get"),
{
onEnter: function (args) {
var name = args[0];
if (name !== undefined && name != null) {
name = ptr(name).readCString();
//console.log(name)
if (name.indexOf("ro.build.version.sdk") >= 0) {
var r = Process.findModuleByName("libmsaoaidsec.so")
nop_64(r.base.add("0x1c544"))
nop_64(r.base.add("0x1b8d4"))
nop_64(r.base.add("0x26e5c"))
}
}
}
}
);
}

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();
if(path.search("libmsaoaidsec.so") != -1){
this.hook = true
locate_init()
}
}
}
}
);

function nop_64(addr) {
Memory.protect(addr, 4 , 'rwx');
var w = new Arm64Writer(addr);
w.putRet();
w.flush();
w.dispose();
}

安卓逆向某某单词,反frida注入完美绕过
http://blog.bingyue.top/2025/01/08/an_zhuo_ni_xiang_an_li_001/
作者
bingyue
发布于
2025年1月8日
许可协议