美文网首页我爱编程
百度HI使用XPOSED自动抢红包插件的编写

百度HI使用XPOSED自动抢红包插件的编写

作者: 烤土豆啦 | 来源:发表于2018-05-23 15:10 被阅读0次

    百度HI使用XPOSED自动抢红包

    xposed是一个安卓的hook框架,这个就不介绍了。大致流程是百度hi接收红包消息,xposed 去hook接收消息的函数,根据接收到的消息类型,如果是红包,则想办法打开它。

    1 分析接收红包代码

    百度hi作为我厂比较成熟的内部聊天工具,考虑兼容性等,不敢加壳,但是肯定敢混淆。代码零零散散,但是有些关键的地方肯定不会混淆。百度hi/微信等,接收到消息后,第一步会将消息格式化等等,并插入sqlite数据库中。插入数据户的参数一般使用contentvalue,其他两个参数分别是表名,库名等等,类型为Sting。首先使用enjarify 去反编译百度hi,拿到代码后,查找sqlite,insert关键字。可以发现只有一个函数满足要求,函数签名如下

    net.sqlcipher.database.SQLiteDatabase", "insert",String.class, String.class, ContentValues.class
    
    

    然后写一个简单的xposed插件,hook这个函数,拿到ContentValue请求,并遍历ContentValue。可以看到请求如下


    1.png

    多看一下请求,可以得到以下规律

    1. 函数的第一个参数,为接收的消息类型,message为个人消息,topic_message为讨论组消息,group_message为群消息
    2. 函数的第二个参数,不明。。。
    3. ContentValues的msg_body,原始xml格式的百度hi消息,此消息会显示到聊天对话框
    4. sent_status 1为发送,-1位接收消息
    5. display_msg,原始xml格式经过解析后的消息体
    6. display_time ,接收/发送消息的时间
    7. display_name ,接收/发送的消息人

    如果是红包消息的话,msg_body类似于下面:

    <msg><font c="0" b="0" i="0" n="宋体" ul="0" s="10" cs="134" /><text c="" cfn="3" apns="[百度红包]Best Wishes"><luckymoney id="73a14eaf9d244bd7bca5a9b0a48e4888" message="Best Wishes" type="2" source="1" sender_uid="150458547" sender_header="2d043522e6dc14225309b43e11a7c0bf.jpg" sender_name="111111111" /></text><text c="发红包了,快用新版手机Hi去抢:" cfn="999" /><url ref="http://im.baidu.com/upgrade?t=luckymoney" c="http://im.baidu.com/upgrade?t=luckymoney" cfn="999" t="2" /></msg>
    
    

    聪明的你可能也看出来了,luckymoney的id,可能和红包有关。现在开始逆向代码吧。


    2.png

    反正混淆代码我逆向了半天实在看不懂逻辑,下面介绍另一种方法。

    首先使用burp suit代理安卓手机请求(具体自己谷歌),我厂百度hi肯定会有https双向认证。然后不出意外就会报错,也就是xxxException。这时候,我们打开adb log | grep err,就可以拿到抛出异常的错误堆栈了,如下所示,

    05-23 14:57:04.436 12462 13611 W System.err: org.apache.http.conn.ConnectTimeoutException: Connect to /195.168.2.1:8080 timed out
    05-23 14:57:04.447 12462 13611 W System.err:    at org.apache.http.conn.scheme.PlainSocketFactory.connectSocket(PlainSocketFactory.java:126)
    05-23 14:57:04.449 12462 13611 W System.err:    at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:149)
    05-23 14:57:04.450 12462 13611 W System.err:    at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:169)
    05-23 14:57:04.451 12462 13611 W System.err:    at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:124)
    05-23 14:57:04.452 12462 13611 W System.err:    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:366)
    05-23 14:57:04.453 12462 13611 W System.err:    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:560)
    05-23 14:57:04.455 12462 13611 W System.err:    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:492)
    05-23 14:57:04.456 12462 13611 W System.err:    at com.loopj.android.http.AsyncHttpRequest.makeRequest(SourceFile:146)
    05-23 14:57:04.457 12462 13611 W System.err:    at com.loopj.android.http.AsyncHttpRequest.makeRequestWithRetries(SourceFile:177)
    05-23 14:57:04.458 12462 13611 W System.err:    at com.loopj.android.http.AsyncHttpRequest.run(SourceFile:106)
    05-23 14:57:04.460 12462 13611 W System.err:    at com.loopj.android.http.SyncHttpClient.sendRequest(SourceFile:95)
    05-23 14:57:04.462 12462 13611 W System.err:    at com.loopj.android.http.AsyncHttpClient.post(SourceFile:1087)
    05-23 14:57:04.463 12462 13611 W System.err:    at com.baidu.hi.luckymoney.channel.c.c.c.load(SourceFile:209)
    05-23 14:57:04.463 12462 13611 W System.err:    at com.baidu.hi.luckymoney.channel.c.b.a.a(SourceFile:26)
    05-23 14:57:04.464 12462 13611 W System.err:    at com.baidu.hi.luckymoney.channel.c.b.n.Qn(SourceFile:43)
    05-23 14:57:04.465 12462 13611 W System.err:    at com.baidu.hi.luckymoney.channel.c.b.n.Qc(SourceFile:10)
    05-23 14:57:04.466 12462 13611 W System.err:    at com.baidu.hi.luckymoney.channel.c.b.a.Qb(SourceFile:80)
    05-23 14:57:04.466 12462 13611 W System.err:    at com.baidu.hi.luckymoney.channel.c.b.a.oT(SourceFile:11)
    05-23 14:57:04.467 12462 13611 W System.err:    at com.baidu.hi.file.a.EP(SourceFile:33)
    05-23 14:57:04.468 12462 13611 W System.err:    at com.baidu.hi.luckymoney.channel.c.i.Pw(SourceFile:58)
    05-23 14:57:04.469 12462 13611 W System.err:    at com.baidu.hi.luckymoney.channel.a.Px(SourceFile:15)
    05-23 14:57:04.470 12462 13611 W System.err:    at com.baidu.hi.luckymoney.channel.a.run(SourceFile:32)
    05-23 14:57:04.471 12462 13611 W System.err:    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
    05-23 14:57:04.471 12462 13611 W System.err:    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
    05-23 14:57:04.472 12462 13611 W System.err:    at java.lang.Thread.run(Thread.java:818)
    

    这样可以愉快地分析啦,原来是因为双向证书校验不通过报错,抛出异常信息。我们可以分析错误对战,找到从点击接受红包到发送http请求的所有逻辑。在接收到红包后,经过一系列复杂的过程,通过com.baidu.hi.luckymoney.c.c.c的load函数组合成合适的参数,并通过https的请求参数,然后调用com.loopj.android.http.AsyncHttpClient.Post函数,去发送post请求。下面是DDMS的详情


    3.png

    下一个问题是,怎么吧post的请求拿出来。虽然有双向认证,但是可以使用xposed的JustTrustme插件啊,就是信任所有的https请求,轻松绕过证书校验。下面是抓取到的https请求


    4.png

    解释一下,首先,百度hi拿到luckymonry的id,也就是xml消息格式的红包id,构造请求字符串,请求tryopen,是否可以打开红包,也就是红包校验,金额等等等等全都在服务器端操作,防止本地出现伪造的情况。返回json格式的字符串,如果返回200,则说明可以打开红包。否则给用户提示,红包已经被抢/xxx等等等。

    5.png

    然后再发送post到/luckymonry/open,去打开红包,post请求参数位json字符串。name为抢红包用户的名字,money_id为上面所说的红包id,user_type未知,user_agent是用户的设备详细信息,cuid等等,不知道,但是貌似重复使用一个也没有问题,imei是手机串号,sim_serial_number是用户sim卡的地区代码,89860为中国。

    好了,现在知道红包是怎么打开的了,那我们可以在xposed中判断,如果接收到的msg_body消息中含有luckymoney标签,则构造http请求去打开红包。开干。。

    2. 编写xposed代码

    还有一个问题,是BDUSS,根据我厂资料,此cookie参数是为了识别用户的,如果这个写死,灵活性怕是大打折扣,于是看一下com.baidu.hi.luckymoney.c.c.c的load函数


    6.png

    原来是初始化cookie类,然后设置key-value为BDUSS,设置cookie的domain为baidu.com。看一下bduss,发现不知道是怎么生成的。并且xposed也不能hook 初始化之后的变量。但是,xposed可以hook构造函数啊,我们可以得出结论,百度hi肯定会初始化cookie类,不管在什么时候。这样的话,我们可以hook org.apache.http.impl.cookie.BasicClientCookie的构造函数啊,如果key包含BDUSS,很有可能是百度hi在构造http请求并访问百度系的网站。
    代码如下

    
    
    // hook sqlite insert function to filter all message and find luckymoney
            if (loadPackageParam.packageName.equals("com.baidu.hi")) {
                XposedBridge.log("We are in baidu hi!!");
                findAndHookMethod("net.sqlcipher.database.SQLiteDatabase", loadPackageParam.classLoader,
                        "insert",
                        String.class, String.class, ContentValues.class,
                        new XC_MethodHook() {
                            @Override
                            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                                XposedBridge.log(param.getResult().toString());
                                ContentValues contentValues = (ContentValues) param.args[2];
                                String msg_type = (String) param.args[0];
                                String msg_body = contentValues.getAsString("msg_body");
                                if (msg_body != null && msg_body.contains("luckymoney") && msg_body.contains("sender_name")){
                                    String luckymoney = msg_body.substring(msg_body.indexOf("<luckymoney"));
                                    luckymoney = luckymoney.substring(0,luckymoney.indexOf("/>"));
                                    String Lucky_id = luckymoney.substring(16,48);
                                    XposedBridge.log("hi: found lucky" + Lucky_id);
                                    tryOpen(Lucky_id);
                                }
                            }
                        });
    
    

    反正是接收到消息中包含luckymoney,相办法拿到id,并调用tryopen函数,反正大部分不重要的参数都写死了,看看就行了。

    // try open luckymoney ,if return 200 in response json,then call Open
        public void tryOpen(String id){
            String json = "{\"money_id\":\"" + id + "\"}";
            String post = Post("https://ext.im.baidu.com/luckymoney/tryopen", json);
            OpenLucky(id);
        }
        public void OpenLucky(String id){
            String json = "{\"name\":\"11111111\",\"money_id\":\""+id+"\",\"user_type\":1,\"user_agent\":\"Baidu Hi-5.0.3.0-Android-hi_1080_1794_AOSP on BullHead_25_7.1.1_6.12.7_6.12.7.0\",\"cuid\":\"F0DCC1DB2B6CE8500AACE56F961338F2|0\",\"imei\":\"\",\"imsi\":\"460022476553448\",\"sim_serial_num\":\"898600\",\"version\":\"2\"}";
            String resp = Post("https://ext.im.baidu.com/luckymoney/open",json);
            XposedBridge.log("Baidu HI: luckymoney");
        }
    
        public String Post(String url,String json){
            String result = "";
            OkHttpClient okHttpClient = new OkHttpClient();
            //创建一个RequestBody(参数1:数据类型 参数2传递的json串)
            RequestBody requestBody = RequestBody.create(JSON, json);
            //创建一个请求对象
            Request request = new Request.Builder()
                    .url(url)
                    .post(requestBody)
                    .addHeader("Cookie","BDUSS="+BDUSS)
                    .addHeader("Cookie2","$Version=1")
                    .build();
            try {
                Response response=okHttpClient.newCall(request).execute();
                if(response.isSuccessful()){
                    result =  response.body().string();
    
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            return result;
        }
        
    

    下面是获取BDUSS的xposed代码

    
    
                //TRY TO HOOK org.apache.http.impl.cookie.BasicClientCookie CONSTROCTOR TO GET BDUSS
                findAndHookConstructor("org.apache.http.impl.cookie.BasicClientCookie",loadPackageParam.classLoader, String.class,
                        String.class,
                        new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        String name = (String)param.args[0];
                        if (name.contains("BDUSS")){
                            BDUSS = (String)param.args[1];
                            XposedBridge.log("HI:BDUSS :" + BDUSS);
                        }
                    }
                    });
                    
    

    然后,编译好apk,上传手机,激活xposed模块,就可以愉快的抢红包了,再也不怕部门大佬发大红包抢不到了

    相关文章

      网友评论

        本文标题:百度HI使用XPOSED自动抢红包插件的编写

        本文链接:https://www.haomeiwen.com/subject/vcmrjftx.html