吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 13340|回复: 15
收起左侧

[Android 原创] 从去哪儿看React Native安卓App逆向

[复制链接]
Lupinus 发表于 2018-10-9 17:07
本帖最后由 Lupinus 于 2018-10-11 09:45 编辑

去哪儿

App:去哪儿客户端

versionCode="172"

versionName="8.8.5"

去哪儿是采用基于React Native技术的QRN开发,使用QRN Ext做路由转发,QHotDogNetWork做网络请求。

先分析自定义App

如果没有React Native的开发基础,需要搭建一个React Native开发环境,反编译hello world来分析React Native App的特征。关于React Native开发环境的搭建可以参考这里。运气不好可能会遇到比较多的问题,多百度总是可以解决的。

成功运行App以后,打包App,分别用jadx和apktool反编译自己打包的apk,在assets目录下,可以看到index.android.bundle文件,App的入口是MainActivity,其实现是空,继承自Facebook包里的一个类。结合前面的开发环境搭建可以知道,我们实际上写的是JS,综合来看只有index.android.bundle这个文件比较可疑。打开发现是压缩了的js,没关系,美化一下,还是可以阅读的,稍微看一下,可以发现前面部分是React Native的基础框架,后面部分,跟之前的App.js的代码相似度非常高,基本可以确定这个就是真正的逻辑代码了。

原App.js


const instructions = Platform.select({

  ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu',

  android:

    'Double tap R on your keyboard to reload,\n' +

    'Shake or press menu button for dev menu',

});

type Props = {};

export default class App extends Component<Props> {

  render() {

    return (

      <View style={styles.container}>

        <Text style={styles.welcome}>Welcome to React Native!</Text>

        <Text style={styles.instructions}>To get started, edit App.js</Text>

        <Text style={styles.instructions}>{instructions}</Text>

      </View>

    );

  }

}

const styles = StyleSheet.create({

  container: {

    flex: 1,

    justifyContent: 'center',

    alignItems: 'center',

    backgroundColor: '#F5FCFF',

  },

  welcome: {

    fontSize: 20,

    textAlign: 'center',

    margin: 10,

  },

  instructions: {

    textAlign: 'center',

    color: '#333333',

    marginBottom: 5,

  },

});

获取的代码:


                v = y.Platform.select({

                        ios: "Press Cmd+R to reload,\nCmd+D or shake for dev menu",

                        android: "Double tap R on your keyboard to reload,\nShake or press menu button for dev menu"

                }),

                p = (function(t) {

                        function n() {

                                return (0, o.

                        default)(this, n), (0, u.

                        default)(this, (0, c.

                        default)(n).apply(this, arguments))

                        }

                        return (0, s.

                default)(n, t), (0, l.

                default)(n, [{

                                key: "render",

                                value: function() {

                                        return f.

                                default.createElement(y.View, {

                                                style: h.container

                                        }, f.

                                default.createElement(y.Text, {

                                                style: h.welcome

                                        }, "Welcome to React Native!"), f.

                                default.createElement(y.Text, {

                                                style: h.instructions

                                        }, "To get started, edit App.js"), f.

                                default.createElement(y.Text, {

                                                style: h.instructions

                                        }, v))

                                }

                        }]), n

                })(f.Component);

        e.

default = p;

        var h = y.StyleSheet.create({

                container: {

                        flex: 1,

                        justifyContent: 'center',

                        alignItems: 'center',

                        backgroundColor: '#F5FCFF'

                },

                welcome: {

                        fontSize: 20,

                        textAlign: 'center',

                        margin: 10

                },

                instructions: {

                        textAlign: 'center',

                        color: '#333333',

                        marginBottom: 5

                }

        })

}, 435, [436, 1, 103, 104, 114, 124, 140, 156, 2]);

关于React Native的参考:

ReactNative Android源码分析

Native与Javascript通信原理(一)

Native与Javascript通信原理(二)

Native与Javascript通信原理(三)

SoLoader加载动态链接库

再看去哪儿

分析去哪儿这种App,虽然知道抓包可能抓不出什么,但是还是可以看看,有什么有用的信息没。这里没有用Charles和fiddler之类的,直接用的Packet Capture,果然request body和response body都是加密的,这里有一个特征,request body的开头都是a1708,稍微记一下。把去哪儿拖到jadx看看,发现基本没有Activity,但是找到了com.mqunar.network.NetRequestManager,写个xposed,hook一下request方法,发现请求还真的是走的这里,跟抓包的结果一摸一样。

在lib文件夹下面,找到libq_lib_rnqp.so,这个是去哪儿的静态资源离线方案,把这个so解压,里面的assets文件夹,全是qp和qpmd5文件,随机打开一个QP文件,可以发现前面是一个类似json字符串的东西,里面:


{

        "files": [{

                "url": "https://rn.qunar.com/packages/f_flight_search_rn_android/index.bundle",

                "md5": "lO+fevWF8DT4S4lV1FE/vj4Rpwv3z0I9sM9HN93ZLwwEaw5pE01vW/a94Q==",

                "sl": "0,477450"

        }, {

                "url": "https://rn.qunar.com/packages/f_flight_search_rn_android/index.bundle.meta",

                "md5": "BZRwzup822tj5sA0O/IterLMCgmJmgIVQeSGI2oSGcKdkSNxFF4pxAWf6g==",

                "sl": "477450,21"

        }, {

                "url": "https://s.qunarzz.com/flight_search_rn/fonts/0.0.16/f_flight_search_rn_font.ttf",

                "md5": "PyIDorrKuPUsyRoSnjCkqYWGgUIJ8b8evQ7nOB5rxWtlTAJ7bnkua/n32g==",

                "sl": "477471,10320"

        }, {

                "url": "https://s.qunarzz.com/route_service_rn/bgBody.png",

                "md5": "AhojMss6hxmBo39WS3ikUxI7gKykolUSSzT9DBGn5vRxbU+gNRH2bM9FqQ==",

                "sl": "487791,736"

        }, {

                "url": "https://s.qunarzz.com/flight_search_rn/img/FOTAPopLogo.png",

                "md5": "AWq74ZbLcpb19+FUteBPPuJ2X2PC4GFFzwZtii+p+SguUpP+Im0nviULsg==",

                "sl": "488527,3512"

        }, {

                "url": "https://s.qunarzz.com/flight_search_rn/img/FOTAYixuan.png",

                "md5": "wkRBXC16U7lu1/pgEMQ1inI5H3ZZyGkO/6XME61LRVdCdrfifwiaekSQ/A==",

                "sl": "492039,342"

        }, {

                "url": "https://s.qunarzz.com/flight_search_rn/img/atom_flight_pay_booking.png",

                "md5": "Qp/7XxZ424PMSpQd+n4R5tDcyLbtIj2zASr1neJdb7pehBIobsXYTGQdNw==",

                "sl": "492381,994"

        }, {

                "url": "https://s.qunarzz.com/flight_search_rn/img/atom_flight_down_arrow.png",

                "md5": "Vpx0GnB7CXrcJ03rpiUfz5T9lNWbPANmYhZTSosK9xsnSxQQ5QIKSy8GTQ==",

                "sl": "493375,262913"

        }, {

                "url": "https://s.qunarzz.com/flight_search_rn/img/cry_camel.png",

                "md5": "ne3OZ535RNS34nI9LDf/C1gMTRQtaldSrrHjDQ+pEaOlFZE3qQ2ezJxT9g==",

                "sl": "756288,4551"

        }, {

                "url": "https://s.qunarzz.com/flight_search_rn/img/qunar_icon.jpg",

                "md5": "lW0X/N3EP3Y7t4IXvfq2DdTV5s/vPVx9ftAfn2P9IUK/FODBYY6Wo/vR8Q==",

                "sl": "760839,2602"

        }],

        "timestamp": 1536903580677,

        "hybridid": "f_flight_search_rn_android",

        "version": "64",

        "iOS_vid": "vid_80019999",

        "android_vid": "vid_60001194,com.mqunar.react_41",

        "pid": "10010",

        "rnpackage": true,

        "platform": "android"

}

这里就是去哪儿的资源文件了,不用猜也知道了,https://rn.qunar.com/packages/f_flight_search_rn_android/index.bundle 贴到浏览器,下载打开,可以发现,这个就是真正的js代码了。但是它跟我们自己导出的js不一样,去哪儿对这个优化了,所有模块共用同一套RN框架的js代码,所以每个模块的就只有真正的逻辑代码了。这个QHotDogNetWork请求的使用说明:


import { QHotDogNetWork } from 'qunar-react-native';

//如果需要使用APP中配置的 hotdog 地址,则 requestParam 中不设置 url 属性

var requestParam = {

    serviceType:'',  //网络请求type,serviceType和url不能同时为空

    url:'',          //网络请求url(默认为APP中设置的hotdog 地址),serviceType和url不能同时为空

    param:{},        //网络请求参数

    useCache:true,   //是否可以使用cache, true或者false

    cacheKey:'',     //cacheKey,如果useCache为true则cacheKey不能为空

    timeout: 300     //如果 300ms 内没有返回,则会触发 failCallback 回调

    successCallback:(response)=>{},    //网络请求成功的回调

    cacheCallback:(response)=>{},      //网络请求从cache返回的回调

    failCallback:()=>{},               //网络请求失败的回调

}

//发起网络请求,返回该网络请求的requestID
var requestID = QHotDogNetWork.postRequest(requestParam);
//取消网络请求

QHotDogNetWork.cancelNetWorkTask(requestID);

跟这里是不是一样?

QHotDog.png
https://imgchr.com/i/iYF84H

后记

去哪儿的代码逻辑找到了,但是加密的问题并没有解决,因为它不会把加密放在js里面呐!libgoblin_3_1_7.so有个字符串是a1708,这个你还记得吗?哥布林这个so里面有xposed和Cydia Substrate的检测,还有ollvm混淆,加密的函数是int __fastcall sub_123D0(unsigned int a1, int a2, int a3, int a4, _DWORD a5, int a6)。

由于上传图片太麻烦了,就没什么图,都在这里了:链接:https://pan.baidu.com/s/1manoCV4e3By7Hf5-_hgb5A
提取码:8olp

免费评分

参与人数 2吾爱币 +2 热心值 +2 收起 理由
uatlaosiji + 1 + 1 我很赞同!
tigerxiao + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

头像被屏蔽
yyspawn 发表于 2018-10-10 07:11
提示: 作者被禁止或删除 内容自动屏蔽
河北网友 发表于 2018-10-9 18:00
ench4nt3r 发表于 2018-10-9 18:18
丿颠覆灬虎哥 发表于 2018-10-9 18:35
谢谢分享
_pan 发表于 2018-10-9 18:57
先收藏了,再细细的看
tigerxiao 发表于 2018-10-10 06:32
谢谢分享
km852753951 发表于 2018-10-10 13:53
学习一下 谢谢分享
Shutd0wn 发表于 2018-10-10 15:12
现在app都是kolin,js了,本以为java就可以了,哎
 楼主| Lupinus 发表于 2018-10-11 09:43
Shutd0wn 发表于 2018-10-10 15:12
现在app都是kolin,js了,本以为java就可以了,哎

含有kotlin的代码反编译出来,也不是那么舒服,js的代码看起来就更难受了,宁愿看各种混淆都不想看js的各种闭包操作
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-26 22:32

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表