首页
社区
课程
招聘
[原创]小试了一把bpf在android逆向中的辅助效果
发表于: 2023-5-4 13:21 16188

[原创]小试了一把bpf在android逆向中的辅助效果

2023-5-4 13:21
16188

起因

其实,这是一篇水文,没什么技术含量,都是些基础东西。样本是某比较流行的app,之前听说它有frida检测,正好有空就搂出来看看了。

初试(完结)

frida检测常规的手段一般都是

1
2
3
4
5
6
1.frida_server名字检测
2.端口号检测
3.D-Bus检测
4.maps检测
5.线程检测
6.内存特征检测

而1、2、3都有个特征,那就是在frida_server运行的时候才存在,不论是否附加进程,因为这个app是在附加时退出,所以直接就排除了这几个。
然后就从所有检测的共同点入手了,所有检测的共同点就是字符串检测,而字符串匹配最常见的就是strstr函数

然后就hook掉修改返回值,之后就尴尬了。。。

当然还有一些其它的检测,比如里面还对.dex进行了比较,不过目的是处理frida挂不上去的问题,所以就没有细看比较这东西是干嘛的,盲猜可能是检测dex注入的。
上面的几个检测点直接用frida进行hook就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var ph = Module.findExportByName(null, "strstr");
    Interceptor.attach(ptr(ph), {
        onEnter: function (args) {
            this.filename = args[0]
            this.checkname = args[1]
            // console.log(this.filename.readCString(), ptr(args[1]).readCString())
        }, onLeave: function (retval) {
 
            var s = ptr(this.filename).readCString()
            if (s.indexOf("gmain") >= 0){
                console.log("gmain anti.")
                retval.replace(0)
            }else if(s.indexOf("gum-js-loop") >= 0){
                console.log("gum-js-loop anti.")
                retval.replace(0)
            }else if(s.indexOf("linjector") >= 0){
                console.log("linjector anti.")
                retval.replace(0)
            }else if(s.indexOf("/data/local/tmp") >= 0){
                console.log("/data/local/tmp anti.")
                retval.replace(0)
            }
        }
    })

定位检测代码位置

定位检测代码的位置还是简单的采用了打印栈的方式

1
console.log(Thread.backtrace(this.context, Backtracer.FUZZY).map(DebugSymbol.fromAddress).join("\n"));


结合maps文件就直接定位到libmsaoaidsec.so里面的0x1AEE4方法了,里面一堆的字符串解密函数不用管

想看每一个代码段都是什么字符串的话可以跑一下脚本自解密即可,或者直接动态打印,不过这里没有必要。
排除这些解密字符串的代码直接找到里面的函数调用的地方:

字符串解密后死循环每隔4秒检测一次,初略看了一下,大概是这么个情况,因为没有验证所以仅供参考

1
2
3
4
sub_1A940 线程名检测
sub_1AAEC 通过/proc/self/fd文件的实际路径检测是否存在匹配的文件
sub_1AC00 maps检测
sub_23804 xxx内容有点大,需要具体分析

sub_23804初看貌似是对libart的某些函数的hook检测,有兴趣的可以分析看看,代码不适合直接stalker进行处理,因为里面像这种指令会导致死循环需要特殊处理,在系统的库代码里面这种原子指令到处都是

再则,里面好像用到了这个玩意

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    v2 = dlopen("libart.so", 0);
    if ( !v2 )
      goto LABEL_23;
    v3 = v2;
 
    ...
 
    off_47728[0] = (int *)dlsym(v3, (const char *)&v38);
    dlclose(v3);
    goto LABEL_23;
 
    ...
 
LABEL_23:
    v0 = off_47728[0];
    if ( off_47728[0] )
      return (unsigned int)*v0 >> 1 == 0x2C000028;
    return 0LL;

参考


[注意]看雪招聘,专注安全领域的专业人才平台!

收藏
免费 9
支持
分享
最新回复 (7)
雪    币: 136
活跃值: (1212)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
支持一下
2023-5-7 11:11
0
雪    币: 84
活跃值: (2178)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
不错,非常有借鉴意义
2023-5-9 01:00
0
雪    币: 1383
活跃值: (2195)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
4

然后试了一下通过bpf修改open函数的返回值,基于opensnoop.py做的尝试性修改

1

2

3

4

//在trace_return里面增加代码

 if(my_strnstr((const char*)data.name, "maps"2565) != NULL){

        bpf_override_return(ctx, -1);

    }

上面这部分能再稍微详细的贴下代码吗


2023-5-10 15:47
0
雪    币: 2122
活跃值: (4279)
能力值: ( LV9,RANK:140 )
在线值:
发帖
回帖
粉丝
5
司徒废人 然后试了一下通过bpf修改open函数的返回值,基于opensnoop.py做的尝试性修改1234//在trace_return里面增加代码 if(my_strnstr((const ...
这个随便处理都可以,目的只是将原strstr里面的无限循环变成有限循环即可,比如我这里后面两个参数的作用就是对前面两个字符串比较的长度进行了限定
2023-5-11 05:01
0
雪    币: 4033
活跃值: (31446)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
感谢分享
2023-5-11 09:08
1
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8
大佬,能加下qq,请教下技术问题吗
2024-5-21 16:08
0
游客
登录 | 注册 方可回帖
返回