某东直播弹幕实时抓取

.抓包,查找sign

对于已经拿到sign只想看弹幕的部分直接跳到四。

用的抓包工具是HttpCanary,随便进入一间直播间,可以看到弹幕走的是websocket,携带的url参数token来自于下面post返回的结果,经过分析只有st,sign,sv三个参数会变,其余都不变。

image.png

image.png

image.png

image.png

image.png

先从sign开始入手。当时我找这个sign找了一会没找出来,由于sign会跟其它参数拼接成url,所以我就搜其它参数wifiBssid,搜出来很明显就在第一个。

image.png

image.png

这里hook这个getReportString方法,把它的调用堆栈打印出来,然后去查看。

image.png

这里我就直接贴出来了,省得大家浪费时间去找,眼尖的可以看到里面有signature,不就跟我们要的sign很像吗,这里把b方法三个参数hook打印一下,

image.png

进到signature这个方法,可以看到这只是接口,那就搜索这个类,看它在哪里被实现。

image.png

image.png

被getSignFromJni方法实现了,再深入看一下,可以看到加密过程在so里面。

image.png

image.png

这里就直接hook这个函数打印参数,多调用几次,然后跟抓到的数据做对比,看哪些参数不变。经过我的测试,这些参数不用修改,可以直接拿过来用,而且都是string类型的,也不用做什么类型转换。返回值的st,sign,sv都是组成url需要的参数,而且这里的参数v3是post的body的值。

image.png

二.使用rpc进行调用

这里就直接使用rpc进行调用就可以了,根据它的参数照搬就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import  frida
import  json
rpc_sign  =  """
     rpc.exports = {    
         getsign: function(function_id, body_string, uuid){      
             var sig = "";      
             Java.perform(        
                 function(){            
                     //拿到context上下文            
                     var currentApplication = Java.use('android.app.ActivityThread').currentApplication();            
                     var context = currentApplication.getApplicationContext();            
                     var BitmapkitUtils = Java.use('com.jingdong.common.utils.BitmapkitUtils');            
                     sig = BitmapkitUtils.getSignFromJni(context, function_id, body_string, uuid, 'android', '9.2.0');        
                     }       
              )       
              return sig;    
           }
       };
"""
def  get_sign(function_id, body_string, u):    
     process  =  frida.get_remote_device().attach( 'com.jingdong.app.mall' )    
     script  =  process.create_script(rpc_sign)    
     script.load()    
     sign  =  script.exports.getsign(function_id, body_string, u)    
     return  sign
body_data  =  {
     "appId" : "jd.mall" ,
     "content" : "tbV8seY199tCdw6GllmkWyCNNENuGsgwLByA7svt5HbPXvlI9wQhHMk3dT7f0ldfpq6M0MCiUD+A\nVrY390Yct0FSub03INUml9n1bS9rZSF3XT0q1kQdehKPO4CccMiEA6NQXYiqYn9wLsDDYEIjmkVA\nEbXI88CwO0K7uhwemdhQMZrcIFj6jMmyiDNDxSA1OjFw88hR0oSCF0m8ll9o9iU2MVSHDipF5ZDn\nFR4E+82mwfRYIxamafB+nWG8GuHcKhiQOWGbChTcG3TxcGT053wfcc6uuMD7+L4PcsNRQjM9syFc\nXR6FBu/sCV/kH/3rT8w/m3zV1c9JpW9lq/7WVzCVvAIj7RNt2zzYFisymCE="
     }
body_string  =  json.dumps(body_data, ensure_ascii = False ).replace( " " , "")
function_id  =  'liveauth'
=  '-a08d16f38776'
sign  =  get_sign(function_id, body_string, u)
print (sign)

结果:

1
st=1608288325570&sign=bf449b04f88afea4c08e18006127cace&sv=121

三.构造请求拿到认证

上面只是拿到了sign,还得拼接url进行请求,这里就不展示了,要的参数基本都有了,其它不变的参数照搬就行,我就直接post请求拿结果了。

1
{ 'code' : 0,  'msg' '鉴权成功' 'data' : { 'liveUrl' 'wss://live-ws4.jd.com' 'token' 'amQubWFsbF_muLjlrqJfOTMxMTE0MzY4NDNfMTYwODI3OTE4NzA5NGxTNkViVw==' }}

四.建立WebSocket

保存刚才请求websocket,用文本打开可以看到它请求的参数。

1
2
3
4
5
6
7
8
9
{  
   "app" "com.jingdong.app.mall" ,  
   "host" "live-ws4.jd.com" ,  
   "remoteIp" "106.39.169.212" ,  
   "remotePort" : 443,  
   "sessionId" "c77ac6e9-019d-4ac9-8611-92f24eb29851" ,  
   "time" "2020-12-19 23:48:18" ,  
   "url" "wss://live-ws4.jd.com/?token\u003damQubWFsbF_muLjlrqJfMjg5NDU0ODc3NDZfMTYwODI3Mjg5ODE0NFdSb0xibw\u003d\u003d"
}

怎么建立WebSocket连接呢?

这里就需要用到python的aiowebsocket库。

详细的说明和使用方法请看:https://zhuanlan.zhihu.com/p/82089648

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import  asyncio
import  logging
from  datetime  import  datetime
from  aiowebsocket.converses 
import  AioWebSocket
import  LiveBarrage
import  jsonasync 
def  startup(uri):    
     async with AioWebSocket(uri) as aws:        
         converse  =  aws.manipulator        
       # 客户端给服务端发送消息        
       =  {            
         "aid" "dongdong" ,            
         "body" : { "ext" :{ "appid" : "jd.mall" , "entrace" : "6" , "ver" : "1.1" },
             "groupid" : "3092724" # 这个是房间号                    
             "nickName" : "神秘观众" ,            
             "type" : "join_live_broadcast" },            
         "from" :{ "app" : "jd.live" , "clientType" : "android" , "dvc" : "-a08d16f38776" , "pin" :""},
         "id" : "df09a645-8e9b-4aad-b699-d44753ca8738" ,            
         "type" : "chat_group_message" ,            
         "ver" : "9.3.2"        
       }        
       # 把json转换为字符串        
       body_string  =  json.dumps(a, ensure_ascii = False ).replace( " " , "")        
       # print(body_string)        
       await converse.send(body_string)        
       while  True :            
           mes  =  await converse.receive()            
         # 设置编码            
         mes  =  mes.decode( 'utf8' )            
         # nickName是用户名,判断nickName可以过滤非弹幕消息            
         if  'nickName'  in  mes:                
             print ( '{time}-Client receive: {rec}' . format (time = datetime.now().strftime( '%Y-%m-%d %H:%M:%S' ), rec = mes))
if  __name__  = =  '__main__' :    
     token  =  LiveBarrage.token()   # 这里返回的是post请求后拿到的token       
     print (token)    
     try :        
         asyncio.get_event_loop().run_until_complete(startup(token))    
     except  KeyboardInterrupt as exc:        
         logging.info( 'Quit.' )

运行结果如下:

image.png

image.png

可以看到是实时抓取的,到这里就结束了。

五.其它接口

这个app的接口参数都是一样的,不同的接口由functionId这个参数决定的。

image.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值