吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 5341|回复: 90
上一主题 下一主题
收起左侧

[Android 原创] 某TV抓包和jce响应解析

  [复制链接]
跳转到指定楼层
楼主
我是不会改名的 发表于 2024-12-1 22:38 回帖奖励
本帖最后由 我是不会改名的 于 2024-12-1 22:39 编辑

某TV抓包和jce响应解析

免责声明

1、本贴仅作为技术讨论,本人不会利用以下技术盈利、或从事任何侵害该网站的行为。
2、如觉得此贴不妥,请联系本人将第一时间删除。

样本地址

aHR0cHM6Ly90di5xcS5jb20v

前言

大概是今年上半年,某视频TV端的软件就开始抓不到包,旧版的仍然能抓到。

使用sunny,抓包的时候发现,相比旧版的,新版多了许多UDP连接。

参考之前很多的app,猜测这是quic协议,根据**的习惯,找了找开源的项目。

发去年年底开源了一个叫做tquic项目

image-20241201144603919

看样子多半就是quic协议了,毕竟它就喜欢这样干,参考trpc,tars。

既然已经猜到是quic,复盘友商成功案例,迭代旧有传统打法。

一、禁用UDP抓包

毕竟quic还是相对比较新的东西,大部分软件为了兼容旧版本,同时支持多种协议,毕竟TV很多还是安卓5。

参考下面用抓包软件来禁用udp。

image-20241201145737909

image-20241201145934877

二、hook请求信息

参考https://github.com/TencentCloud/libtquic-sdk/blob/main/src/android/TnetQuicRequest.java里面代码,

hook nativeSendRequest 看堆栈一步步往上找。

let TnetQuicRequest = Java.use("com.tencent.qqlive.modules.vb.tquic.impl.TnetQuicRequest");
TnetQuicRequest["sendRequest"].implementation = function (bArr, i10, z10) {
    console.log(`TnetQuicRequest.sendRequest is called: bArr=${bArr}, i10=${i10}, z10=${z10}`);
    let result = this["sendRequest"](bArr, i10, z10);
    console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()));
    console.log(`TnetQuicRequest.sendRequest result=${result}`);
    return result;
};

下面图中performRequest传入了request, headers和请求头,同样hook一下

image-20241201153310410

image-20241201153513095

let TQuicStack = Java.use("com.ktcp.tencent.volley.toolbox.TQuicStack");
TQuicStack["performRequest"].implementation = function (request, map) {
    var url = request.getUrl();
    var Headers = request.getHeaders();
    var keys = Headers.keySet().toArray();
    console.log("Request URL: " + url);
    for (var i = 0; i < keys.length; i++) {
        console.log("Request Header: " + keys[i] + " : " + Headers.get(keys[i]));
    }
    keys=map.keySet().toArray();
    for (var i = 0; i < keys.length; i++) {
        console.log("Request Header: " + keys[i] + " : " + map.get(keys[i]));
    }
    var Cookie=request.getCookie();
    console.log("Request Cookie: " + Cookie);
    var methodid = request.getMethod();
    var method = function (id) {
        switch (id) {
            case 0:
                return "GET";
            case 1:
                return "POST";
            case 2:
                return "PUT";
            case 3:
                return "DELETE";
            default:
                return "UNKNOWN";
        }
    }(methodid);
    console.log("Request Method: " + method);
    if (method === "POST") {
        var body = request.getBody();
        var bodystr = Java.use("java.lang.String").$new(body);
        console.log("Request Body: " + bodystr);
    }
    let result = this["performRequest"](request, map);
    return result;
};

这里只hook了请求,响应是一样的就在下面几个函数。

image-20241201154520744

三、hook降级

根据上面hook nativeSendRequest打印的堆栈信息,可以判断出app使用的是OKHttp,添加了拦截器。

参考https://juejin.cn/post/7308909489154015247,利用addInterceptor函数嵌入了tquic。

image-20241201155217820

同样往上找堆栈,发现一个可疑函数selectHttpStack和chooseProtocol,尝试hook,同时抓包。

image-20241201155443687

chooseProtocol返回2的时候就是tquic,无法抓包,为1时能够抓到。

image-20241201155659319

直接hook返回1就行了

let MultiStackNetwork = Java.use("com.ktcp.tencent.volley.toolbox.MultiStackNetwork");
MultiStackNetwork["chooseProtocol"].implementation = function (request) {
    return 1;
};

image-20241201160050215

四、jce响应解析

在抓包过程中会发现,响应大多数是二进制,不是json。在请求参数里面有个jce,大部分修改为json能够返回json格式,下面图片中的接口,修改后仍然是二进制的。

image-20241201170810606

4.1 jce是什么

jce 是一个类似于 pb 的二进制编码协议,最早是在腾讯 rpc 框架 tars 设计的,不过现在都统一为tars。

类似.proto文件,.jce/.tars文件是腾讯tars rpc框架的基础(现在又出一个trpc pb协议)。

目前 tars 支持 C++,Java,PHP,Nodejs,Go ;所以解析jce最简单方法还是直接把app里面代码扣出来调用。

4.2 .jce/.tars基本格式

参考官网文档

4.3 从java代码中还原.jce/.tars文件

建议自己先多看看官方文档,文档里面有点就不说了。

根据官方提供的示例

StatF.tars

StatMicMsgHead.java

image-20241201210739538

image-20241201210816012

两个文件对照看可以找到对应关系

java .jce/.tars
true require
false optional
boolean bool
byte byte
String string
int int
long long
float float
double double
short short
[] vector<  >
Map map< , >

顺序在readFrom和TarsStructProperty里面都有,但是在app里面只能通过readFrom里面的参数来了。

参考上面的关系很容易手动还原,不多写了。

4.4 快速定位java代码

根据官方文件在java中函数readString,能够读取字符串,直接hook

let JceInputStream = Java.use("com.qq.taf.jce.JceInputStream");
JceInputStream["readString"].implementation = function (i10, z10) {
    console.log(`JceInputStream.readString is called: i10=${i10}, z10=${z10}`);
    let result = this["readString"](i10, z10);
    console.log(`JceInputStream.readString result=${result}`);
    return result;
};

根据打印堆栈信息判断出所需的代码在SpecPageContentResp

然后还原出tars文件,附件里面提供我还原后的文件,仅供参考,需要注意的是module qqlivetv,module 最好写一样的。

4.5  利用go解析响应

根据官方文件配置tarsgo

go install github.com/TarsCloud/TarsGo/tars/tools/tarsgo@latest
go install github.com/TarsCloud/TarsGo/tars/tools/tars2go@latest

创建一个新的.tars文件,包含还原出来的全部.tars文件

#include "tras/ottProto/OttHead.tars"
.................
#include "tras/tvVideoSuper/SingleLinePlayerViewInfo.tars"

然后生成对应go代码,这里建议先看看官方文档,生成对应的服务,先了解一下rpc整个流程。

 tars2go all.tars

根据官方提供的文件server.go,同样是在ReadFrom函数中解析

// Invoke process request and send response
func (s *serverProtocol) Invoke(ctx context.Context, reqBytes []byte) []byte {
        req := &requestf.RequestPacket{}
        rsp := &requestf.ResponsePacket{}
        is := codec.NewReader(reqBytes[4:])
        if err := req.ReadFrom(is); err != nil {
                rsp.IRet = 1
                rsp.SResultDesc = "decode request package error"
        } else {
                rsp.IVersion = req.IVersion
                rsp.CPacketType = req.CPacketType
                rsp.IRequestId = req.IRequestId
                if req.SFuncName != "tars_ping" {
                        rspData := s.s.OnConnect(ctx, tools.Int8ToByte(req.SBuffer))
                        rsp.SBuffer = tools.ByteToInt8(rspData)
                }
        }
        return response2Bytes(rsp)
}

很容易写出我们需要的代码

u := "https://tv.t002.ottcn.com/i-tvbin/qtv_video/home_page/hp_real_waterfall"
headers := map[string]string{
    **********
}
cookies := map[string]string{
**********
}
params := map[string]string{
    "format":   "jce",
    "req_type": "spec",
    "area_id":  "zq_80485272",
    "Q-UA":     "**********",
}
req := url.NewRequest()
req.Params = url.ParseParams(params)
req.Headers = url.ParseHeaders(headers)
req.Cookies = url.ParseCookies(u, cookies)
r, err := requests.Get(u, req)
if err != nil {
    fmt.Println(err)
}
var SpecPageContentResp qqlivetv.SpecPageContentResp
reader := codec.NewReader(r.Content)
err = SpecPageContentResp.ReadFrom(reader)
if err != nil {
    fmt.Println(err)
}
CurPageContents := SpecPageContentResp.Data.PageContent.CurPageContent
var items []qqlivetv.ItemInfo
for _, v := range CurPageContents {
    for _, group := range v.Groups {
       for _, line := range group.Lines {
          for _, component := range line.Components {
             for _, grid := range component.Grids {
                for _, item := range grid.Items {
                   items = append(items, item)
                }
             }
          }
       }
    }
}
for _, item := range items {
    voiceTitle := item.ExtraData["voiceTitle"].StrVal
    s := item.Action.PageSnapshot.Url
    if s != "" {
       var VideoInfo VideoInfo
       r, err := requests.Get(s, nil)
       if err != nil {
          fmt.Println(err)
       }
       content := r.Content
       err = json.Unmarshal(content, &VideoInfo)
       if err != nil {
          fmt.Println(err)
       }
       for _, v := range VideoInfo.VideoList.Videos {
          fmt.Println(v.Vid, v.Title)
       }
       continue
    }
    fmt.Println(voiceTitle)
}

image-20241201222206760

五、后记

总的来说都比较简单,jce和proto类似,主要是还原.tars文件。如果app没加固可以尝试直接反编译导出.java文件,然后递归遍历,生成全部或者需要的.tars,基本jce java文件都在一个目录下面。

附件

只有本地本文档,和还原出来的tras文件
https://nicaicai.lanzouo.com/i1PFO2guyx2d

免费评分

参与人数 40威望 +2 吾爱币 +137 热心值 +37 收起 理由
Sydyanlei0 + 1 + 1 谢谢@Thanks!
Psyber + 1 谢谢@Thanks!
airzen + 1 + 1 用心讨论,共获提升!
xbxbxbxb + 1 + 1 热心回复!
jdz888 + 1 + 1 超赞&amp;amp;#128077;&amp;amp;#127995;
抱歉、 + 1 用心讨论,共获提升!
fengbolee + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
a6600 + 1 热心回复!
wyh21cn + 1 + 1 热心回复!
Huibq120 + 1 + 1 谢谢@Thanks!
gqdsc + 1 + 1 老牛了!!佩服
lin5789 + 1 用心讨论,共获提升!
tty6a + 1 + 1 热心回复!
wdj500 + 1 + 1 我很赞同!
朮小凯 + 1 谢谢@Thanks!
4everlove + 1 + 1 用心讨论,共获提升!
牧尘凉凉 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
allspark + 1 + 1 用心讨论,共获提升!
Leaf08 + 1 + 1 我很赞同!
linqiu431 + 1 我很赞同!
willbe001 + 1 + 1 我很赞同!
greendays + 1 + 1 我很赞同!
NBA456017 + 1 我很赞同!
shuaibiliao + 1 + 1 鼓励转贴优秀软件安全工具和文档!
xuanle6 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
漁滒 + 4 + 1 我很赞同!
杨辣子 + 1 + 1 谢谢@Thanks!
aqkj + 1 + 1 我很赞同!
Kanou + 1 + 1 用心讨论,共获提升!
晓丶玉女 + 1 + 1 用心讨论,共获提升!
wuge21 + 1 + 1 向大佬学习
正己 + 2 + 100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
pp67868450 + 1 + 1 用心讨论,共获提升!
0jiao0 + 1 + 1 用心讨论,共获提升!
sngss + 1 用心讨论,共获提升!
为之奈何? + 1 + 1 我很赞同!
DH91 + 1 + 1 用心讨论,共获提升!
huangkang2297 + 1 + 1 热心回复!
puyuancheng + 1 热心回复!
xiaohuo1251 + 1 + 1 谢谢@Thanks!

查看全部评分

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

推荐
DH91 发表于 2024-12-2 02:03
原来如此,当时我就说怎么突然抓不到什么有用的了,果然升级了&#129394;。我还以为抓包软件失效了&#128531;。不过代码操作还是看不懂,顶一下技术大佬。
推荐
linix 发表于 2024-12-2 13:52
豌豆荚下载的最新T视频,11月19号的好像postern+charles还可以抓到包啊。搜索和单个点进去。稍微看了下混淆的很乱
3#
xiaohuo1251 发表于 2024-12-1 23:17
4#
A6789 发表于 2024-12-1 23:49
感谢大佬分享
5#
xuz2z 发表于 2024-12-2 00:02
虽然大部分没懂,但感觉很牛
6#
bfbkj998 发表于 2024-12-2 00:19
看着一堆代码就头疼
7#
zdmjl 发表于 2024-12-2 08:11
代码完全看不懂


8#
kongson 发表于 2024-12-2 08:16
太棒了,感谢分享!
9#
77wyb 发表于 2024-12-2 08:25
谢谢分享!
10#
0jiao0 发表于 2024-12-2 08:31
大佬写的很详细,感谢分享思路
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2024-12-26 16:06

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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