关于Web端某音的登录逆向

其实,抖音逆向完成已经很久了,看到还有很多人私信我说登录过程什么样的,我翻了下源码,现在我自己都有点迷糊了。
还好,自己写的文档还是齐全的,流程如下:

1. 首先打开页面,拿到一个ms-token,这个应该很多接口都可以,我选取的是 https://creator.xxxxxx.com/aweme/v1/creator/data/v2/billboard_list/ 这个接口

2. 获取二维码检测的地址,简称回调注册,会返回一个 callback 的url

3. 先调用一次callback,从服务器拿到对应的cookie,为了与本次会话绑定

4. 正式的拿到二维码,返回有二维码png图片的base64 以及二维码内真是的url,这个看到时候需求,可以直接显示这个图片,如果服务器分开可以直接传输url,到用户服务器再转一个二维码出来,里面包含一个token,是本次二维码绑定对象

5. 一直循环调用 https://sso.xxxxxx.com/check_qrconnect/ 接口,传入二维码token会返回当前二维码状态(1.正常,2.已经扫码,等待用户确认,3.登录成功,5. 二维码过期(此时会包含一个新的二维码数据和新的token,官方页面是没有做刷新的,自己可以用这个二维码直接刷新页面使用))

6. 如果出现二次验证,上面 check_qrconnect 接口会返回弹出窗口的问题,比如需要使用短信验证码验证,还是用户自己发短信到指定的号码进行验证。当用户选择短信验证的时候,调用 https://creator.xxxxxx.com/passport/web/send_code/ 接口请求发送用户短信

6.1 用户得到验证码以后输入发送给 https://creator.xxxxxx.com/passport/web/validate_code/ 验证,如果成功,再在 6 操作中循环会返回 登录成功 成功状态

7. 不管是二次验证通过还是直接check_qrconnect 接口返回登录成功,都会返回一个 redirect_url 地址,需要访问这个 redirect_url 地址去拿登录以后得cookie。这里有2个坑,1个坑就是这个请求返回的是302,在使用requests时,一定不能让库自动跳转过去,否则cookie就拿不到了(里面加 allow_redirects=False 的原因)。第2个坑是,在第3步注册callback的时候给服务器传递了用户自己生成的私钥(加密的),在redirect返回的cookie里面有个bd_ticket_guard_server_data的东西,需要解码出来,拿出ts_sign,ticket数据组成新数据以后用私钥签名以后放入cookie,这一步不做会提示未登录的。

8. 拿到cookie,后面数据没什么难点了,直播前面是另一套,需要另外搞 (注意关键的请求记得用x_bugs.js 里面的 get_x_bugs 和 _signature 签名, get_x_bugs会签名url 和 body数据,如果有的话;_signature 就是只签名url部分)

其中逆向没有加密的几个简单的方法,比如短信验证码的加密方法:

# 加密  
def commom_encrypt(num):
    enc_str = ""
    num_list = []
    for ch in num:
        val = ord(ch)
        if val>=0 and val<=127 :
            num_list.append(val) 
        elif val>=128 and val <= 2047:
            num_list.append(192|31&val>>6) 
            num_list.append(128|63&val) 
        elif val>=2048 and val<= 55295 or val>=57344 and val<=65535:
            num_list.append(224|15&val>>12) 
            num_list.append(128|63&val>>6) 
            num_list.append(128|63&val) 
    for i in range(0, len(num_list)):
         enc_str += "%0.2x" % ((num_list[i]&0xff)^0x5)
    return enc_str

可以对照这个加密方法进行解密工作:

# 解密 
def commom_decrypt(enc_str):
    num_list = []
    str = ""
    for i in range(0, len(enc_str), 2):
        num_list.append(int(enc_str[i:i+2], 16)^5)
    index = 0
    while index < len(num_list):
        ch = num_list[index]
        if ch >= 0b11100000:
            str += chr( ((ch&0b11111)<<12) | ((num_list[index+1]&0b111111)<<6) | (num_list[index+2]&0b111111) )
            index += 3
        elif ch >= 0b11000000:
            str += chr( ((ch&0b11111)<<6) | (num_list[index+1]&0b111111) )
            index += 2
        else:
            str += chr(ch)
            index += 1
    return str

至于其他一些签名算法,没必要折腾逆向,直接抠jsvmp补环境就可以了。抠代码就略过了(全放容易收律师函)

这里放出最关键的RSA Cookie方法:

# 二维码登录2: 注册回调 
def register_callback(callback_url):
    http = requests.session()
    http.keep_alive = True
    r = http.get(callback_url, timeout=3, headers=module.data.http_headers, verify=False)
    if 'Set-Cookie' in r.headers:
        set_cookie_list = r.headers['Set-Cookie'].replace(',', ';').split(';')
        for cookie in set_cookie_list:
            ckl = cookie.strip().split('=', 1)
            if len(ckl)>1:
                # 有些重复的要加进去 
                module.data.http_header_cookies[ckl[0]] = ckl[1]
        # 调用callback之前前要准备的数据 
        module.data.http_header_cookies['bd_ticket_guard_client_web_domain'] = '2'
        module.data.http_header_cookies['_bd_ticket_crypt_doamin'] = '2'
        module.data.http_header_cookies['__security_server_data_status'] = '1'
        # 本地生成共有、私有秘钥 
        [public_key, private_key] = bd_ticket_crypt.call("_private_key")
        # 需要存储起来,登录需要用到 
        module.data.header_base_data['public_key'] = public_key
        module.data.header_base_data['private_key'] = private_key
        # 需要把私有秘钥加密以后发给服务器 
        public_xt_key = bd_ticket_crypt.call('xt', private_key)
        module.data.header_base_data['Bd-Ticket-Guard-Ree-Public-Key'] = public_xt_key
        bd_ticket_guard_client_data = '{"bd-ticket-guard-version":2,"bd-ticket-guard-iteration-version":1,"bd-ticket-guard-ree-public-key":"' + public_xt_key + '","bd-ticket-guard-web-version":1}'
        to_base64 = base64.b64encode(bd_ticket_guard_client_data.encode('utf-8'))
        module.data.http_header_cookies['bd_ticket_guard_client_data'] = to_base64.decode()

# 二维码登录6:获取最终的登录cookie,用作之后通讯 
def get_login_cookie(redirect_url):
    http = requests.session()
    http.keep_alive = True
    module.data.http_headers['Cookie'] = module.data.get_http_cookes(module.data.http_header_cookies)
    # 这里返回是302,不能让他跳过去,不然没有cookie了 
    r = http.get(redirect_url, timeout=3, headers=module.data.http_headers, verify=False, allow_redirects=False) 
    if 'Set-Cookie' in r.headers:
        set_cookie_list = r.headers['Set-Cookie'].replace(',', ';').split(';')
        for cookie in set_cookie_list:
            ckl = cookie.strip().split('=', 1)
            if len(ckl)>1:
                # 这个数据要特殊处理 
                if ckl[0] == 'bd_ticket_guard_server_data':
                    bd_ticket = base64.b64decode(ckl[1]).decode('utf-8')
                    bdtJson = json.loads(bd_ticket)
                    # bdtJson['client_cert']
                    ts_sign = bdtJson['ts_sign']
                    ticket = bdtJson['ticket']
                    timestamp = int(time.time())
                    t = 'ticket='+ ticket +'&path=/passport/token/beat/web/&timestamp=' + str(timestamp)
                    req_sign = bd_ticket_crypt.call('req_sign', t, module.data.header_base_data['private_key'])
                    jdata = '{"ts_sign":"' + ts_sign + '","req_content":"ticket,path,timestamp","req_sign":"' + req_sign + '","timestamp":' +  str(timestamp) + '}'
                    bd_ticket_guard_client_data = base64.b64encode(jdata.encode('utf-8')).decode('utf-8')
                    module.data.http_header_cookies['bd_ticket_guard_client_data'] = bd_ticket_guard_client_data
                else:
                    # 覆盖参数  
                    module.data.http_header_cookies[ckl[0]] = ckl[1]

整体流程代码框架,可以根据这个过程,自己去实现逆向:

# 获取第一个cookie 
def get_base_cookie():
    pass

# 二维码登录1: 获取注册接口   
def get_register_addr():
    pass

# 二维码登录2:获取服务器返回的msToken数据 
def get_report_msToken():
    pass

# 二维码登录2: 注册回调 
def register_callback(callback_url):
    pass

# 手机号码登录,需要检测是否滑块校验 
def get_verify_code(phone_num):
    pass

# 二维码登录3: 获取二维码进行登录 
def get_login_qrcode():
    pass

# 二维码登录4: 监控二维码扫码情况 
def loop_check_qrcode(token_id):
    pass

# 二维码登录5: 二维码二次校验 
def send_check_code(verify_ticket):
    pass

# 二维码登录6: 发送验证码到服务器校验  
def send_validate_code(msm_code, verify_ticket):
    pass

# 7. 登录以后获取用户信息                 
def get_user_info():
    pass

# 显示登录二维码 
def show_img():
    pass

def test():
    # 获取一个cookie 
    cookie = get_base_cookie()
    module.data.header_base_data['msToken'] = cookie
    module.data.http_header_cookies['msToken'] = cookie
    # 只获取一次 
    module.data.header_base_data['biz_trace_id'] = module.data.get_trace_id()
    # 第一阶段,注册,获取回调函数 
    callback_url = ''
    while True:
        register_result = get_register_addr()
        if not register_result:
            print('regist failed')
            exit(0)
        jvRegister = json.loads(register_result)
        if int(jvRegister['status_code']) == 0:
            if 'redirect_url' in jvRegister and len(jvRegister['redirect_url']) > 0:
                callback_url = jvRegister['redirect_url']
                break
    # 第二阶段,访问回调接口,获取cookie 
    register_callback(callback_url)
    # 头部新增 
    module.data.http_headers['Referer'] = 'https://creator.xxxxxx.com/'
    # 使用二维码扫码登录 
    qr_json = get_login_qrcode()
    if qr_json:
        jvQrcode = json.loads(qr_json)
        qr_login = False
        qr_data = ''
        if jvQrcode['error_code'] == 0 and 'data' in jvQrcode:
            # 二维码数据 
            qrcode_base64 = jvQrcode['data']['qrcode']
            image_bin = base64.b64decode(qrcode_base64)
            with open('qr.png', 'wb') as fp:
                fp.write(image_bin)
            # 显示 
            imgthread = threading.Thread(target=show_img)
            imgthread.start()
            # 拿取服务器的mstoken 
            get_report_msToken()
            # 监控扫码情况 
            qr_token = jvQrcode['data']['token']
            qr_login,qr_data = loop_check_qrcode(qr_token)
        if qr_login == True:
            if qr_data:
                jvQrData = json.loads(qr_data)
                # 那登录需要的scid 
                fp_code = get_ck_fp()
                module.data.http_header_cookies['tt_scid'] = fp_code
                # 获取到调整地址,需要更新Cookie 
                if int(jvQrData['error_code']) == 0:
                     # cookie 
                    if not 'ttcid' in module.data.http_header_cookies:
                        module.data.http_header_cookies['ttcid'] = x_bugs.call("get_ttid")
                    redirect_url = jvQrData['data']['redirect_url']
                else:
                    # 错误啦 
                    print('---------------------------------------------------------------')
                    print(qr_data)
                    exit(0)

                # 等了完成会有个重定位地址,拿回来需要用私钥处理以后才能正常登录 
                get_login_cookie(redirect_url)
                module.data.http_header_cookies['oc_login_type'] = 'LOGIN_PERSON'
                # print(module.data.http_header_cookies)
        print('---------------------------------------------------------------')
        print('-----------------------登录成功,获取用户信息--------------------')
        while True:
            # 获取用户信息 
            print(get_user_info())
            time.sleep(5)

测试:

263933.png

不过异地登录会有提示风险


7630b7a371c3cf0ab24cbce6e9bd73e.jpg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容