首页
社区
课程
招聘
[原创]记录一次秀动APP的逆向
发表于: 2024-7-27 07:54 28336

[原创]记录一次秀动APP的逆向

2024-7-27 07:54
28336

记录一次秀动App逆向过程

目标应用:秀动

目标版本:5.2.7

CleanShot 2024-04-27 at 08.48.58

frida启动后发现有检测,尝试使用魔改frida过掉检测

CleanShot 2024-04-27 at 08.49.56

发现可以成功挂起

CleanShot 2024-04-27 at 08.50.15

CleanShot 2024-04-27 at 08.50.45

使用魔改frida成功过掉检测,现在开始分析具体的frida检测。

魔改frida已经随文件打包,大家可以去github支持下作者。

[!tip]

注意,遇到有frida检测的样本尽量要-f挂起,如果-F可能造成手机卡死重启,耽误调试进度

我们来hook一下open函数 并打印堆栈,自下而上找到frida检测逻辑代码的点,并学习此检测。

CleanShot 2024-04-27 at 09.13.08

发现有一个so在不断获取/proc下的状态信息

CleanShot 2024-04-27 at 09.14.38

搜索发现这貌似是一个sdk的附属so,不是作者自写so。

据搜集发现,应该是和webview的实现有关

故pass

[!tip]

逆向工程中搜集信息是一个非常重要的环节,不要吝惜你的谷歌不用

有很多大佬已经给你铺好了路

这句话写给你们,也写给我自己,这真的很重要

[!note]

在寻找frida的各种方法中,我们分为两条路线:

一种是以anti-frida-su.js为主的,去满足正常环境的要求,去各种抹除掉frida注入后的种种痕迹,但是这是致命的,因为frida有无数种特征,你无论如何是抹除不完的,不如重新按照frida写一个自己的HOOK工具。但是幸运的是,有无数大佬为我们铺路,例如非虫大佬的11个patch,能够过掉市场上百分之70的检测,但这是远远不够的。

CRC检测的出现让魔改的frida也无法招架得住。例如,对libart.so的prettymethod的方法的不可规避的注入,让frida无处遁形。 我们该怎么办?

非虫大佬已经给出了答案:修改hook pretymthod的hook时机,这样能过掉百分之5左右的样本(为了节约用户硬件资源,启屏后就会关闭),但是大部分样本还是通过开启线程进行crc循环检测

第一种方法变得更加曲折起来,必须要配合魔改rom以及linux内核来进行进一步隐藏。

但是请注意,crc检测必须要开启线程来检测,我们来讨论第二种过掉的方法,patch线程

在正式操作之前,我想和大家讨论检测粒度问题:

[!note]

请大家思考,frida的检测粒度一般在什么级别,我们先做假设,假

如我是一个开发人员,我在进行界面跳转,再或者数据请求后,加几句话,对23946端口的检测(frida检测一个例子,当然没有那么简单),那么frida检测我们可以认为是语句级别的粒度,因为只有几行代码,我们也无法避免,因为我们要进行操作。

接上部分,我们公司有安全开发人员,给了我一个函数,我不用考虑别的,直接调用即可,那frida检测我们可以认为是函数级别的粒度

如果我们公司没有开发人员怎么办,当然是外包啦,引入一个安全公司的so,打钱打钱。

接上面的讨论,第一种情况几乎不存在,我既要懂业务也要懂安全,除非我自己是全栈(这种适用于mini App)各大H播软件可能会出现。

第二种情况可能存在,也就是业务代码与安全代码掺杂,也是我们不希望看到的情况,一个线程里既有检测代码,也有业务逻辑代码。即patch掉线程业务也无法运行

第三种情况是最常见的,一些安全的sdk,对设备评估,基本数据请求加密,以及反调试的实现。

第三种情况也要分两种方式讨论,第一种,纯检测so,没有任何加密行为

我给他归结为so粒度的,那么直接跳过so加载即可。

第二种,安全公司提供的so,内部有设备id等加密方式计算,那么跳过就不是那么简单了 我们可以定位so加载的头部位置,在怀疑后,检测so的各个段的加载,定位so大致位置,来进行patch反调试

使用spawn方法启动frida(不要使用魔改的,看不到退出时间点)

CleanShot 2024-04-27 at 09.48.48

发现开启

这条线程的时候,软件挂掉了

我们增加脚本的过滤条件

CleanShot 2024-04-27 at 09.58.11

发现成功过掉了frida检测

失败案例:

没有过滤offset==360656,软件直接发生了崩溃,反正鼓励大家多试试

CleanShot 2024-04-27 at 09.59.04

ps:本文章不讨论脱qiao,脱qiao好的已经在根目录了,请大家合法使用

打开抓包后提示网络异常,关闭后就不异常了,确定为抓包检测。

[!tip]

这一步抓包检测(非证书教研式),有一种特殊方式,需要软路由。

以及透明代理(非证书教研式),考虑到第一种需要设备,第二种需要docker云手机,这里不做讨论。

如何进行抓包检测定位?首先找到登陆的activity,找到登陆函数,一步一步往下跟。

注入监听脚本,并点击按钮

[22041216C::秀动 ]-> [WatchEvent] onClick: com.showstartfans.activity.activitys.login.XDLoginActivity

CleanShot 2024-04-27 at 10.32.13

我们可以看到账号密码的位置,往下跟,进入this.Z函数

CleanShot 2024-04-27 at 10.33.36

发现这是在组装登陆bean 返回上一层

进入j函数

CleanShot 2024-04-27 at 10.35.37

CleanShot 2024-04-27 at 10.35.53

继续往下跟

CleanShot 2024-04-27 at 10.36.12

发现进入了okhttp逻辑

我们进入n.j这个函数

CleanShot 2024-04-27 at 10.40.14

判断d函数在组装client,这里可能会设置禁止代理相关函数

[!tip]

建议大家自己开发一个okhttp的应用跟一下,有助于文章理解

CleanShot 2024-04-27 at 10.41.24

进入g(context)

CleanShot 2024-04-27 at 10.41.42

发现第一处代理检测点

X0.g是一个函数,疑似代理管理类

CleanShot 2024-04-27 at 10.42.32

发现确实是这样的,相关的代理检测逻辑都在这里

CleanShot 2024-04-27 at 10.43.03

CleanShot 2024-04-27 at 10.43.55

这里调用后,决定我们是否被检测到是开启代理,核心逻辑在本类的i函数里

这个教给大家自己去学习了

CleanShot 2024-04-27 at 10.48.27

CleanShot 2024-04-27 at 10.48.45

这次我们打算分析crpsign这个参数

CleanShot 2024-04-27 at 10.49.34

jadx直接搜就搜到了

CleanShot 2024-04-27 at 10.49.54

发现来源正是这里的native函数

showstart_net.so

使用龙哥的 ida脚本 findhash一把梭,可以直接找到加密位置,发现是个标准的md5加盐,结合入参就能分析成功

[!note]

值得讨论的点:这个so是rust配合开发的,

下单接口的请求体和相应体都是加密的,就在上面的so中实现,没有找到任何aes的特征,但是找到了rust库的aes包的引用

推测原因1:rust数据结构和标准的c不一样,就连字符串结尾都不是以\0结尾的

导致反编译出的so贼乱

解决办法:开发rust aes 并提取特征点写出插件

在rust和jni交互中,rust似乎实现了自己的一套runtime,有一些系统调用unidbg无法跑起来,希望有大佬一起来研究下!如果unidbg补好了,那么接下来所有rust开发的so都可以进行基本的补环境的能力。

感兴趣的大佬可以继续研究与我交流

function hook_open() {
    // https://blog.csdn.net/a656343072/article/details/40539889
    /*
        函数原型:int open( const char * pathname, int oflags);
                int open( const char * pathname,int oflags, mode_t mode);
         
        mode仅当创建新文件时才使用,用于指定文件的访问权限。
        pathname 是待打开/创建文件的路径名;
        oflags用于指定文件的打开/创建模式,这个参数可由以下常量(定义于 fcntl.h)通过逻辑或构成。
        O_RDONLY       只读模式
        O_WRONLY      只写模式
        O_RDWR          读写模式
        以上三者是互斥的,即不可以同时使用。
    */
    var open_addr = Module.findExportByName("libc.so","open")
    var io_map = Memory.allocUtf8String("/proc/13585/maps");
    Interceptor.attach(open_addr, {
        onEnter: function (args) {
            console.log('targetFunction called from:\n' +
                Thread.backtrace(this.context, Backtracer.ACCURATE)
                    .map(DebugSymbol.fromAddress).join('\n') + '\n');
            if(args[0].readCString().indexOf("/proc/")!=-1 && args[0].readCString().indexOf("maps")!=-1){
                args[0] = io_map
                // ptr(args[0]).writePointer(Memory.allocUtf8String(args[0].readCString().replaceAll(/\d+/g,"1")))
                // args[0] = Memory.allocUtf8String("/proc/1/maps")
                // Memory.protect(ptr(args[0]), args[0].readCString().length, 'rwx');
                // var value_new_str = Memory.allocUtf8String("/proc/1/maps")
                // console.log("args0="+args[0].readCString())
                // ptr(args[0]).writeByteArray([0x2f,0x70,0x72,0x6f,0x63,0x2f,0x32,0x2f,0x6d,0x61,0x70,0x73,0x0])
                // console.log("args0="+args[0].readCString())
            }
            this.pathname = args[0]
            this.oflags = args[1]
            this.mode = args[2]
             
        },
        onLeave: function (retval) {
            console.log("retval="+retval+"---"+"pathname="+this.pathname.readCString()+"---oflags="+this.oflags)
            if(this.pathname.readCString().indexOf("libmsaoaidsec")!=-1){ // 过了惠头条
                retval.replace(0xffffffff)
            }
            console.log("open pathname="+this.pathname.readCString()+"---oflags="+this.oflags)
            if(this.pathname.readCString().indexOf("/proc/")!=-1 && this.pathname.readCString().indexOf("maps")!=-1){
                retval.replace(0x0)
            }
        }
    })
}
function hook_open() {
    // https://blog.csdn.net/a656343072/article/details/40539889
    /*
        函数原型:int open( const char * pathname, int oflags);
                int open( const char * pathname,int oflags, mode_t mode);
         
        mode仅当创建新文件时才使用,用于指定文件的访问权限。
        pathname 是待打开/创建文件的路径名;
        oflags用于指定文件的打开/创建模式,这个参数可由以下常量(定义于 fcntl.h)通过逻辑或构成。
        O_RDONLY       只读模式
        O_WRONLY      只写模式
        O_RDWR          读写模式
        以上三者是互斥的,即不可以同时使用。
    */
    var open_addr = Module.findExportByName("libc.so","open")
    var io_map = Memory.allocUtf8String("/proc/13585/maps");
    Interceptor.attach(open_addr, {
        onEnter: function (args) {
            console.log('targetFunction called from:\n' +
                Thread.backtrace(this.context, Backtracer.ACCURATE)
                    .map(DebugSymbol.fromAddress).join('\n') + '\n');
            if(args[0].readCString().indexOf("/proc/")!=-1 && args[0].readCString().indexOf("maps")!=-1){
                args[0] = io_map
                // ptr(args[0]).writePointer(Memory.allocUtf8String(args[0].readCString().replaceAll(/\d+/g,"1")))
                // args[0] = Memory.allocUtf8String("/proc/1/maps")
                // Memory.protect(ptr(args[0]), args[0].readCString().length, 'rwx');

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2024-7-31 08:13 被mb_qzwrkwda编辑 ,原因:
上传的附件:
收藏
免费 11
支持
分享
最新回复 (19)
雪    币: 23
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
感谢大佬分享,请问打包魔改frida在哪里下载呢?
2024-7-28 11:50
0
雪    币: 4200
活跃值: (1421)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
3
像风一样呢 感谢大佬分享,请问打包魔改frida在哪里下载呢?[em_71]
github搜索 florida 一个大佬制作的
2024-7-28 15:39
0
雪    币: 2255
活跃值: (10229)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
我下载了一个最新版是御安全加固的
2024-7-29 16:07
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
大佬,能帮忙app去壳吗?https://f.ws28.cn/f/epnkmz8iilm
2024-7-29 19:18
0
雪    币: 4200
活跃值: (1421)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
6
你瞒我瞒 我下载了一个最新版是御安全加固的
御安全防护等级比较低,建议大佬尝试fart8或使用fridafart尝试脱壳,内存dump貌似有一定的检测,需要找好时机
2024-7-29 19:27
0
雪    币: 4200
活跃值: (1421)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
7
mb_rugymctl 大佬,能帮忙app去壳吗?https://f.ws28.cn/f/epnkmz8iilm
抱歉,您可以尝试下 https://dump.bf/ 这个网站 非常好用
2024-7-29 19:28
0
雪    币: 86
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8
大佬 您是不是忘记发那个脚本了 定位线程开启的脚本
2024-7-30 22:51
0
雪    币: 4200
活跃值: (1421)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
9
mb_ihgczpkl 大佬 您是不是忘记发那个脚本了 定位线程开启的脚本
我现在补一下,等会发你链接
2024-7-31 08:11
0
雪    币: 4200
活跃值: (1421)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
10
mb_ihgczpkl 大佬 您是不是忘记发那个脚本了 定位线程开启的脚本
看一下附件
2024-7-31 08:13
0
雪    币: 86
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
mb_qzwrkwda 看一下附件
谢谢大佬 不经意的举动真的对我们这些小白帮助很大 非常感谢!
2024-7-31 13:41
0
雪    币: 23
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
12
mb_qzwrkwda github搜索 florida 一个大佬制作的
非常感谢大佬的回复!
2024-8-3 21:21
0
雪    币: 202
活跃值: (1165)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
16.1.4这个好用吗
2024-8-4 15:05
0
雪    币: 1378
活跃值: (2505)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
感谢分享
2024-8-6 11:49
0
雪    币: 215
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
15
大佬,florida大佬这个魔改的是怎么用的,是只要替换firda_server并启动就行吗,我看还有florida-gadget和florida-inject,这两个怎么替换?
2024-8-9 14:08
0
雪    币: 202
活跃值: (1165)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
楼主能分享下你那个吗,自己下载的没有这几个字
strong-frida powered by feicong
2024-8-22 10:01
0
雪    币: 202
活跃值: (1165)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
楼主有空看下i国网这个app,用你的方法过不了检测
2024-8-22 12:09
0
雪    币: 338
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
18
我下了一个最新版的,也是御安全的,后面我去豌豆荚下了个和楼主一样版本的就可以了
2024-8-26 10:56
0
雪    币: 20
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
19
楼主大大, 我尝试的版本一模一样过不了检测呀
2024-9-29 09:56
0
雪    币: 210
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
20
脱壳修复也很麻烦啊,有的androidmanifest.xml都是加密乱码
2024-9-29 16:52
0
游客
登录 | 注册 方可回帖
返回
//