游客发表
拿到一个目标,贝壳第一件事不是渗透掏工具,是整流搞清楚它跑在什么上面。贝壳找房的贝壳App,安卓端,渗透做了基本的整流加固和混淆。反编译一看,贝壳DEX文件被抽了,渗透关键字符串全加密,整流类名混淆成a、贝壳b、渗透c。整流这不是贝壳能直接读的代码,得先脱壳。渗透
脱壳方案选了FART。整流跑在Pixel 3的LineageOS 18.1上,刷入Magisk,把FART的dexfile导出模块激活。启动App,让它自己跑完所有加载逻辑,然后从内存里dump出真实的DEX。这一步拿到的代码,比静态反编译出来的完整得多。用jadx打开,能看到网络层用的是OkHttp,请求签名逻辑藏在一个native方法里。
签名算法是第一个硬骨头。native层用了OLLVM混淆,控制流平坦化加指令替换,IDA里看过去全是调度块。没硬啃,换了个思路——动态Hook。用Frida挂上libsign.so,定位到JNI_OnLoad,跟踪RegisterNatives的调用,找到签名函数的入口地址。然后Hook住它,打印入参和返回值。反复构造不同请求,对比输入输出,大概能推断出签名结构:时间戳+请求体+固定盐值,走一遍HMAC-SHA256,再截取前16字节做AES加密。
光猜不够,得验证。写了个Python脚本复现签名逻辑,拿同一个请求体跑出来的签名和App实际发出的做比对。前三次都对不上,差在时间戳的精度上——App用的是毫秒级,我用了秒级。改过来之后,签名一致。到这一步,已经可以脱离App自己构造合法请求了。
但能发请求不代表能拿到数据。贝壳的接口做了风控,同一设备指纹短时间内高频请求会触发验证码。设备指纹的采集点很多:IMEI、AndroidID、MAC地址、屏幕分辨率、传感器列表、已安装应用列表。Frida脚本里把这些值全部Hook住,返回预设的固定值。传感器列表比较麻烦,App会读加速度计和陀螺仪的数据做模拟器检测。真机上跑的时候没问题,模拟器上就得用Frida伪造传感器事件流。
证书固定也绕不过去。App用了OkHttp的CertificatePinner,还额外在native层做了SSL验证。Frida的SSL unpinning脚本能干掉Java层的,native层的得手动patch libssl.so里的验证函数。用IDA找到X509_verify_cert的调用点,改成直接返回1。重新打包so,替换进去,再用Frida确认握手不再校验证书。
跑通之后,写了个爬虫框架。请求频率控制在每分钟30次以内,随机间隔1.5到4秒。User-Agent从App的真实请求里抓,设备指纹每次会话随机生成但保持稳定。数据落库前先做清洗,小区名称、户型、面积、价格这些字段从JSON里提取,经纬度做逆地理编码补全行政区划。
实际跑了一周,覆盖了北京朝阳区大概1200个小区。中间被封了两次,换IP池和调整指纹后恢复。数据质量还行,但发现一个坑:不同端(App、小程序、网页)返回的房源列表有差异,App端能看到一些网页端不展示的房源。这可能是渠道策略,也可能是AB测试。要拿全量数据,得多端交叉验证。
整个过程下来,最耗时的不是脱壳也不是签名,是风控对抗。签名算法固定,逆向一次就完了。风控是动态的,今天能过的策略明天可能就触发验证。得持续监控请求成功率,一旦下降就回头查指纹和频率。说白了,渗透和反渗透是场拉锯战,没有一劳永逸的方案。
友情链接