自搭的基于Frida一体化采集框架

本文介绍了cpfrida框架,集成了日志收集、设备运维、异常告警等功能,助力快速生成项目和频率调控,适用于简化逆向采集流程。

提示:本文仅分享框架设计思路和大体的使用,全是博主自己个人的设计思路,转载请注明出处


前言

随着自己做逆向采集已经快一年了,逆向的方向还是半桶水,但是优化采集流程,搭建自己的采集体系方便工作和运维却是在这一年中有较大的提升。本框架就提供一个学习思路和大体的使用,所有的一些操作都是博主个人经验的总结,用代码的形式展现出来而已,细节方面还得靠自己去看代码跟着思路走。下文中的cpfirda就是本次的框架,是基于frida进行的二次开发,好,下面进入正文,害怕起来了。。。。。。
在这里插入图片描述


一、cpfrida的功能

简单用一句话来说:搭好架构,完善hook脚本,配置好项目配置,手机直接接入,就可以采集数据

1.日志收集

提供了简易的日志收集,用来记录每次hook的数据、数据回传、对设备的操作以及一些异常信息的收集等等,用来分析一个完整的hook流程或者分析hook过程中的一些异常,方便后续优化流程等操作

2.设备操作

提供了简易的运维操作,因为在hook的时候设备或系统的原因,会导致app经常卡死,这时候就有重启手机或者app等简单的操作来保持hook流程的正常进行

3.异常告警

提供了一些消息通知,正常的一个项目,肯定要有自己的告警体系,这边我采用了飞书群通知的形式告知运维人员设备风控等情况,至于要其他形式,需要自己找api接入

4.模板处理

提供了模板快速生成代码的功能,为什么要这个呢?有做过frida采集的同学一定深有体会,frida是用脚本的形式去打桩的,每次脚本可能都不大一样,但是总体又可能相似,就差一些参数或者频率等等,这时候模板就会帮助我们去节省大量的开发时间了

5.进程维护

提供了进程维护功能,frida是脚本形式就奠定了它比较难以有效的集中管理,通常是以脚本的形式运行业务,那一些奇奇怪怪的问题就会导致脚本停止,这时候就需要在脚本运行的时候记录一下当前脚本的进程信息,方便后续运维

6.自动运维

提供了简单的自动运维功能,当脚本停止,数据长时间没有回调,我们是需要一个数据回调的统计和告警平台的,当数据长时间未回调,这个平台上的对应信息就会出现一个告警(这个流程也比较简单,就是监控数据回调的时长,如果超过某个值没有数据回传,那么就认为设备异常,出告警就行了),然后框架会定时去访问这个平台,捕获到告警信息,对对应的设备进行重启等操作,如果框架有在运维对应的进程,就会杀死对应的进程,重新在创建一个新的进程(为什么要杀死?因为如果那个进程还在的话没有什么问题,只是在等待hook的参数,那么当没有杀死进程的情况下,一旦拿到了hook参数,那么此时就有两条进程在同时跑一个设备一个app的打桩点,目前以我的经验这样是行不通的)

7.快速生成项目

基于第四个模板处理下,框架提供了根据配置文件快速生成项目的功能,说白了就是项目开始自有配置文件,框架会根据配置项生成不同的工作空间(这个后续在讲什么是工作空间)来完善整个项目。当然还提供了收纳项目了,会根据框架中你可能手动改过的配置进行更新已有的配置,同时删除了对应的工作空间,这样项目的可移植性非常之高了

8.频率可调控

做爬虫的或者逆向采集的,都知道频率是个很玄学的东西,我们会经常去修改频率来满足我们的业务需求或者来规避风控等等,该框架集成了一些采集的频率,我们可以通过认为的方式去调控频率,但不是实时的,需要停止该业务线在启动才生效(如果实时的我觉得IO应该会比较高。。。。。。)

9.服务端快速部署

提供了一快速部署项目的脚本,如果用Jenkins的话,拉玩代码执行该脚本就行,如果手动的也是一样,十分方便

10.hook脚本的雏形(自己编写hook逻辑)

集成了一些hook过程常用的工具,需要手动拉起对应的dex包到手机的/data/loacl/tmp目录,只有这个目录用户才有操作的权限

11.手机端快速部署(自己实现)

这个我就给个思路吧,这个地方比较复杂,我们开发一个APP,这个APP有什么功能呢?
a、可以发送广播,这个广播可以启动内网穿透对应的程序和frida服务端的程序,而且要实时运行,这样就可以完全打通服务端和手机端的正常通信了
b、这个APP可以释放对应项目所需的dex包和frida-server包以及你们自己的一些脚本啊等的
c、这个APP可以拉起内网穿透程序配置,这个配置是把frida端口和ADB端口映射到服务端的,这样能方便我们工作,手机中去编辑配置,想想都蛋疼。。。。。。

二、宏观看架构

上面介绍完了功能,现在我们来看看项目的整体流程,直接上流程图:
在这里插入图片描述
这张图片是最早之前自己画的流程图,其中还包括了开发部分,很简单明了,相信一看就到明白了。传统的Frida在本地调试的时候需要用usb连接,我框架中直接采用远程连接,然后利用frp内网穿透工具把frida的端口和ADB的端口直接都映射到Linux服务器上,这样就可以直接在服务器上的某个端口访问手机端的frida端口和ADB端口实现远程操作了,像之前如何将手机变成一个(Linux)服务器提到的在手机上安装Linux服务,非常之不稳定和繁琐,这样做的稳定性和简便我自己是深有体会的

三、微观看架构

1.服务端环境

先上图:
a、python3.8:为了兼容frida15.1.10版本,这里我选用Anaconda3
b、frida-client:对于手机来说,此时linux就是客户端,frida对应的插件可以网上找一大堆,这里就不赘述了,我选用的是葫芦娃大佬编译的frida
c、adb工具:远程操作手机和app,网上一堆
d、frp-server:映射手机上的frida和adb端口的,网上都有,其他工具只要能实现内网穿透都行
e、linux自带的定时任务crontab:用来定时启动监控脚本,在项目启动文件中已经写好了

2.手机端环境

先上图:
在这里插入图片描述
a、fastjson.dex:阿里开发的对象和JSON的处理包
b、FRP_INSTALL_YouWant:自己开发的APP用来发送广播,维护frida和frp的app
c、helper:自己开发的JONS处理工具,删除JSON不需要的字段
d、Magisk:获取手机的root权限,在刷机的时候刷入,可以去玩主页学习怎么刷机点击学习
e、RootExplorer:文件管理工具,系统自带的不好用

3.辅助环境

这里的辅助指的是一件部署功能的,即jenkins,详细的部署流程和简单的使用可以去我主页看对应的博文点击学习

你也许会觉得上面的插件和APP怎么这么多啊,这个就看你自己规划了,可以根据自己的认知简易部署流程,或者可以舍弃一些自己觉得没用的东西,毕竟有些东西是辅助作用,不是项目的核心需要哈

四、代码详解

1.项目目录

项目目录这个东西啊,真的有些头疼,开发的小伙伴也行会感同身受,本项目的目录大部分以及配置好了,无需太多的改动,上项目目录图:
在这里插入图片描述
cpfrida:项目名称

v1.0:版本,和v2.0采集逻辑上不同,v1.0是采用权限分发的形式,怎么分发的呢?这么说吧,你自顾自的采集会导致数据失衡,满足不了业务,所有规则就是满足业务需求,举个例子:比如有几个接口detail、product、live、topic,业务需要的是detail一个,product三个,live五个,topic两个,这时候假设你要部署11个设备上去满足业务,但是在采集的过程中,你会发现某一天product数据量过多,在规定时间内完成不了,但是呢,live数据量过少很大一部分时间会处于空闲时间,这样就相当于浪费了资源,于是就有了权限分发,每台设备先按之前的方式部署好,detail一个,product三个,live五个,topic两个,当某台设备空闲的时候就会执行下级权限,现在live的设备权限顺序为:live–>product–>detail–>topic,当live空闲的时候,它就执行product,product也空闲的时候依次类推,每次完成一次完整的hook之后,权限又是重头开始,这样做就可以达到资源全部合理利用啦,对了,牢记frida无法同时在一个点打桩多次,按我hook的app来看的话是这样,上面的权限分发就有效的避开了这个问题

v2.0:版本,和v1.0采集逻辑上不同,v2.0是采用进程池的概念,它比一来的简单暴力,但是业务线过多的话Linux服务器可能就需要多个了,比较耗费一些资源。它没有那些采集规则,业务不够就加一套,还是接口detail、product、live、topic,一样的部署方式,只是此时11个设备我每个设备都跑四条业务,这时候就有44个业务了,是不是体量多太多了,想想都有点小刺激

common:一些通用模块
在这里插入图片描述

  • build_model.py:项目配置文件模块
  • handle_template.py:模板生成代码核心
  • ident_model.py:工作空间标识
  • inform_mode.py:消息通知模块,对接群机器人等api
  • logging_mode.py:日志模块
  • phone_model.py:手机操作模块
  • process_model.py:进程管理模块

deploy:快速生成项目相关,已经封装在项目部署文件中
在这里插入图片描述

  • template:代码模板

    • config_template:每个工作空间的配置项
    • startwork_template:工作空间启动项,sh
    • workspace_template:工作空间代码
  • build.ini:项目配置项,所有的工作空间都是根据这个配置项生成的

  • build_.ini:配置项的各项说明

  • deploy.py:快速生成代码的核心,已集成到项目启动脚本中

hookscript:hook脚本js代码
在这里插入图片描述

  • vx_hooker_8015.js:hook代码的逻辑,已经封装好了一些常用的工具
  • vx_hooker_8018.js:hook代码的逻辑,已经封装好了一些常用的工具

startproject:项目部署启动相关
在这里插入图片描述

  • kill_process.py:杀死当前系统下的说有python进程,所有建议服务器单一点就跑这个采集框架,v2.0是这样的,v1.0比较智能,是根据进程管理来进行kill的,也可能错杀,当没有v2.0这么暴力
  • reboot_phone.py:重启所有的设备,多进程模式重启
  • start_project.py:解压项目启动脚本,sh,因为直接sh通过jenkins部署会存在编码问题

workspace:工作空间
在这里插入图片描述
工作空间下自由的模块:

  • monitor:监控模块
    • monitor.py:监控基类,封装好了一些常用方法和操作
    • monitor.py:继承监控基类,是点对点的,专属我自己的一个监控,可以自行编写哈
  • base_script.py:frida的核心创建逻辑、对外部分交互和用到的模块载入
  • business.py:hook的业务逻辑和send数据处理
  • extend.py:项目的扩展功能,比如收集cookie,或者网页端采集等等
  • params.py:项目用到的固有参数,和一些任务常调控的参数,比如频率
  • device1:工作空间(设备名称),这个是根据build.ini生成出来的,一个完整的工作空间是有:
    • 配置文件 config.py,对应的配置项都有对应的说明
    • 工作者 device1.py,单个进程或进程池的入口
    • 工作空间启动项 device1.sh,值适用linux系统,window系统可以直接手动输入指令执行或者在IDE中执行
    • 工作空间身份 uuid

requirements.txt:项目依赖

2.javascript核心代码

不会反编译,一切都是徒劳的,下面就给出了js的辅助工具,至于hook的核心代码和接口,这边不便展示,懂得都懂(手动狗头):

/**
 * @Description: Wechat 8015 hook script
 * @author XQE
 * @date 2022/04/19
*/


var tools = {
   
   
    classexists: function (className) {
   
   
        var _exists = false
        try {
   
   
            Java.use(className)
            _exists = true
        } catch (err) {
   
   
        }
        return _exists
    },

    checkloaddex: function (className, dexfile) {
   
   
        if (!this.classexists(className)) {
   
   
            Java.openClassFile(dexfile).load()
        }
    },

    tojsonstring: function (obj) {
   
   
        try {
   
   
            this.checkloaddex("com.alibaba.fastjson.JSON", "/data/local/tmp/fastjson.dex")
            var _clz = Java.use("com.alibaba.fastjson.JSON")
            var _toJSONStringMehtod = _clz.toJSONString.overload("java.lang.Object")
            return _toJSONStringMehtod.call(_clz, obj)
        } catch (err) {
   
   
            console.log(err)
        }
        return "{}"
    },

    fromjsonstring: function (jsonStr, classObj = null) {
   
   
        try {
   
   
            this.checkloaddex("com.alibaba.fastjson.JSON", "/data/local/tmp/fastjson.dex")
            if (classObj == null) {
   
   
                var _clz = Java.use("com.alibaba.fastjson.JSON")
                return _clz.parseObject(jsonStr)
            } else {
   
   
                var _jsonObject = Java.use("com.alibaba.fastjson.JSONObject")
                var _obj = _jsonObject.parseObject(jsonStr, classObj.class)
                return _obj
            }
        } catch (err) {
   
   
            console.log(err)
        }
        return null
    },

    recursionremove: function (jsonObject, removeKey) {
   
   
        var _keyArray = jsonObject.keySet().toArray()
        for (var i = 0; i < _keyArray.length; i++) {
   
   
            var _key = _keyArray[i]
            var _object = jsonObject.get(_key)
            var _objectType = ""
            try {
   
   
                _objectType = _object.getClass().getName()
            } catch (e) {
   
   }
            if (_key == removeKey && _objectType == "java.lang.String") {
   
   
                jsonObject.remove(_key)
            }
            if (_objectType == "com.alibaba.fastjson.JSONObject") {
   
   
                this.recursionremove(jsonObject.getJSONObject(_key), removeKey)
            }
            if (_objectType == "com.alibaba.fastjson.JSONArray") {
   
   
                var jsonArray = jsonObject.getJSONArray(_key)
                for (var a = 0; a <= jsonArray.size(); a++) {
   
   
                    try {
   
   
                        var _childObj = jsonArray.getJSONObject(a)
                        if (_childObj) {
   
   
                            this.recursionremove(_childObj, removeKey)
                        }
                    } catch (e) {
   
   
                        console.log(e)
                    }
                }
            }
        }
    },

    wxhelper: function () {
   
   
        try {
   
   
            this.checkloaddex("com.alibaba.fastjson.JSON", "/data/local/tmp/fastjson.dex")
            this.checkloaddex("com.tencent.mm.wechathelperdex.WxHelper", "/data/local/tmp/wxhelper.dex")
            var _clz = Java.use("com.tencent.mm.wechathelperdex.WxHelper")
            return _clz
        } catch (err) {
   
   
            console.log(err)
        }
        return "{}"
    },

    hashset: function () {
   
   
        var _HashSetClz = Java.use("java.util.HashSet")
        var _hashset = _HashSetClz.$new()
        return _hashset
    },

    map: function () {
   
   
        var _hashMap_clz = Java.use("java.util.HashMap")
        var _map_clz = Java.use("java.util.Map")
        var _hashMap = _hashMap_clz.$new()
        var _map = Java.cast(_hashMap, _map_clz)
        return _map
    },

    base64: function (zsBase64) {
   
   
        var androidBase64 = Java.use("android.util.Base64")
        var _base64 = androidBase64.decode(zsBase64, 0)
        return _base64
    },
}

var hooker_fun = {
   
   
    runComment: function (device, reply, args) {
   
   },

    runDetailVideo: function (device, args) {
   
   },

    runDetailGoods: function (device, args) {
   
   },

    runLiveInfo: function (device, username, args) {
   
   },

    runLiveBarrage: function (device, username, args) {
   
   },
	
	runLiveContribution: function(device, username, args) {
   
   },

    runLiveGoods: function (device, username, args) {
   
   },

    runLiveSquare: function (device, cate) {
   
   },

    runLiveTab: function () {
   
   },

    runLiveBag: function (device, args) {
   
   },

    runProductList: function (device, selfappid, args) {
   
   },

    runProductInfo: function (device, selfappid, args) {
   
   },

    runProductStore: function (device, selfappid, args) {
   
   },

    runProductTakecenter: function (device, selfappid, cookie, args) {
   
   }

    runProductThird: function (device, selfappid, cookie, args) {
   
   },

    runGetcookie: function (device, username) {
   
   },

    runTopicTopic: function (device, args) {
   
   },

    runTopicActivity: function (device, tab, args) {
   
   },

    runVideoGoods: function (device, args) {
   
   },
    
    runVideoUrl: function (device
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值