某拉雅的登录协议复现

查看 103|回复 9
作者:你就是我的阳光   
闲来无事想找一个APP试试手,就看到了某个APP就有了这一篇文章,第一次发帖,如写的不好请谅解第一步当然是抓包啊


1.png (57.71 KB, 下载次数: 0)
下载附件
2022-7-7 19:36 上传

可以看到一共是三个请求,仔细看看这三个请求pwd这个请求带了5个参数,一看到有加密,瞬间想知道用的什么算法。这个时候看到pwd这个请求里面有一个字段是nonce,正巧上面也有一个请求的名字也是nonce所以就去看看吧。


2.png (72.88 KB, 下载次数: 0)
下载附件
2022-7-7 19:09 上传

这个时候好像发现了一个秘密,这竟然是pwd里的nonce字段那么同样,下面还有一个请求呢,那是不是里面也存在东西


3.png (45.22 KB, 下载次数: 0)
下载附件
2022-7-7 19:09 上传

发现pwd请求中的fdsOtp正是这个请求返回的token,那么这时候暗自窃喜,已经找到两个了, 模拟一下就可以得到,那剩下三个参数不是简简单单。
打开jadx吧


5.png (71.9 KB, 下载次数: 0)
下载附件
2022-7-7 19:09 上传

看到关于登录的就这么多,一个一个看呗,也不多。


6.png (23.65 KB, 下载次数: 0)
下载附件
2022-7-7 19:09 上传

account不是出现在请求中么,先往后看一眼是什么逻辑


7.png (12.73 KB, 下载次数: 0)
下载附件
2022-7-7 19:09 上传

这跟进来看着很清晰啊


8.png (12.43 KB, 下载次数: 0)
下载附件
2022-7-7 19:09 上传

在看看
[color=]ZaZJiHJbQT这个函数的逻辑是什么


9.png (19 KB, 下载次数: 0)
下载附件
2022-7-7 19:09 上传

[color=]哦豁 是个native函数。那打开IDA吧


10.png (22.36 KB, 下载次数: 0)
下载附件
2022-7-7 19:09 上传


[color=]起来就一个函数啊,那应该就是这个,加密就在里面快冲
当我双击进去的时候花的时间有点久,我就觉得可能大事不妙


11.png (25.03 KB, 下载次数: 0)
下载附件
2022-7-7 19:09 上传

好家伙,果然。看到这个流程图完全不想看啊。
这时候有一个办法 上unidbg,补环境是一个很头疼的问题。花的时间有点久


12.png (56.8 KB, 下载次数: 0)
下载附件
2022-7-7 19:09 上传

但是我这里不知道是什么原因,结果出不来,那就重头好好看看吧


13.png (257.24 KB, 下载次数: 0)
下载附件
2022-7-7 19:10 上传

嗯.......全是反射,不过上来就看到了RSA往下看也没什么好像就是一个RSA,公钥在第一行也给出来了。那么,账号密码都是RSA加密了,但是每次RSA加密的结果都是不一样的,只能往后模拟一下请求吧。
现在就剩最后一个参数了。


14.png (42.39 KB, 下载次数: 0)
下载附件
2022-7-7 19:10 上传

还是跟刚才一样找关键字呗,到了这个函数的时候一瞬间,这不就是请求的五个参数


15.png (31.09 KB, 下载次数: 0)
下载附件
2022-7-7 19:10 上传

跟到最后发现调用的是RIdAPyTgEt,不用想这个肯定也混淆了。还试试unidbg呗
哦对了忘了说,这个参数如果不知道是什么的话,先用frida看一下
[JavaScript] 纯文本查看 复制代码function hook_native() {
     Java.perform(function() {
         var i = Java.use("com.ximalaya.ting.android.loginservice.LoginEncryptUtil");
         i.RIdAPyTgEt.overload('android.content.Context', 'boolean', 'java.lang.String').implementation = function (arg1,arg2,arg3) {
            console.log('RIdAPyTgEt arg1 = ' + arg1)
            console.log('RIdAPyTgEt arg2 = ' + arg2)
            console.log('RIdAPyTgEt arg3 = ' + arg3)
            var ret = this.RIdAPyTgEt(arg1,arg2,arg3)
            console.log('RIdAPyTgEt ret = ' + ret)
            return ret
        }
        i.ZaZJiHJbQT.overload('java.lang.String').implementation = function (arg1) {
            console.log('ZaZJiHJbQT arg1 = ' + arg1)
            // console.log('arg2 = ' + arg2)
            var ret = this.ZaZJiHJbQT(arg1)
            console.log('ZaZJiHJbQT ret = ' + ret)
            return ret
        }
     });
}


16.png (115.4 KB, 下载次数: 0)
下载附件
2022-7-7 19:10 上传

这样子参数不就很清晰了嘛,就只接用unidbg模拟一下吧


17.png (55.91 KB, 下载次数: 0)
下载附件
2022-7-7 19:10 上传

但是很不幸,死在了系统调用。没有办法,拿出同事的脚本。


18.png (57.22 KB, 下载次数: 0)
下载附件
2022-7-7 19:10 上传

这时候我发现了跟我传进来的格式差不多,但是多了一串东西


19.png (66.7 KB, 下载次数: 0)
下载附件
2022-7-7 19:10 上传

继续往下发现,全大写了。


20.png (19.66 KB, 下载次数: 0)
下载附件
2022-7-7 19:10 上传

紧接着直接做了加密,那么就没错了,那就是传进来的东西拼接一个东西,在做一个加密,那么这个加密是啥啊,40个字节的一个加密,像sha1啊,找个在线的试试去


21.png (41.62 KB, 下载次数: 0)
下载附件
2022-7-7 19:10 上传

一模一样啊,那就是sha1
这个时候算法已经很清晰了啊。
1.先发送一个请求,在服务器返回包中拿到 fdsOtp
2.发送第二个请求,在服务器返回包中拿到nonce
3.在使用ZaZJiHJbQT加密账号密码算法是RSA,加密以后的格式要确保一致
4.用同一个so中的RIdAPyTgEt加密得到signature,在so中拼接了一个字符串,使用sha1加密。
5.上面步骤完成以后才可以正确请求到。写代码写代码。
[Python] 纯文本查看 复制代码# -*- coding:utf-8 -*-
import json
import time
import hashlib
import requests
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
from base64 import b64decode
from base64 import b64encode
proxy = {'http': 'http://139.155.48.55:1089', 'https': 'http://139.155.48.55:1089'}
def get_nonce():
    """
    :return: nonce的值
    """
    t = time.time()
    data_time = int(round(t * 1000))  # 毫秒级时间戳
    url = "https://passport.ximalaya.com/mobile/nonce/" + str(data_time)
    header = {
        "Cookie": "1&_device=android&59683601-c043-33af-95d4-413ba0788573&9.0.22;channel=Avril22;impl=com.ximalaya.ting.android;osversion=29;fp=009314627x2222q2v644v0500b0000022002000000000000200000000000;device_model=MI+8;XIM=;c-oper=%E6%9C%AA%E7%9F%A5;net-mode=WIFI;freeFlowType=0;res=1080%2C2029;AID=n+Ew49Cv/1Y=;manufacturer=Xiaomi;XD=NSRvWEdehdyqqdzAdbPbJbOfbPFruUAJe3HFBaJ0z438KV7oI/Q/czqtjV2TmpUvQtc0VKk72doYCGDVyUUkqe+kAr9y9XC5FdiYPvv8e66Wf7z4JEulczsJmwNd27g/;umid=0e42f21fc3f4e8704728805785e97255od;xm_grade=0;minorProtectionStatus=0;oaid=5d3cfedc6c1bd5aa;domain=.ximalaya.com;path=/;",
        "Cookie2": "$version=1",
        "Accept": "*/*",
        "user-agent": "ting_9.0.22(MI+8,Android29)",
        "x-xuid": "78c5c143-aa52-3f77-b3b6-fb4d0c289c3a",
        "Host": "passport.ximalaya.com",
        "Connection": "Keep-Alive",
        "Accept-Encoding": "gzip"
    }
    req = requests.get(url, headers=header)
    print("nonce响应结果 = ",req.text)
    nonce = json.loads(req.text)
    nonce = nonce["nonce"]
    print('nonce = ', nonce)
    return nonce
def get_fdsOtp():
    """
    :return: fdsOtp的值
    """
    t = time.time()
    data_time = int(round(t * 1000))  # 毫秒级时间戳
    get_token_url = "https://mobile.ximalaya.com/captcha-web/check/slide/get?bpId=139&sessionId=59683601-c043-33af-95d4-413ba0788573" + str(data_time) + "&requestType=xmClient"
    get_token_header = {
        "Cookie": "1&_device=android&59683601-c043-33af-95d4-413ba0788573&9.0.22;channel=Avril22;impl=com.ximalaya.ting.android;osversion=29;fp=009314627x2222q2v644v0500b0000022002000000000000200000000000;device_model=MI+8;XIM=;c-oper=%E6%9C%AA%E7%9F%A5;net-mode=WIFI;freeFlowType=0;res=1080%2C2029;AID=n+Ew49Cv/1Y=;manufacturer=Xiaomi;XD=NSRvWEdehdyqqdzAdbPbJbOfbPFruUAJe3HFBaJ0z438KV7oI/Q/czqtjV2TmpUvQtc0VKk72doYCGDVyUUkqe+kAr9y9XC5FdiYPvv8e66Wf7z4JEulczsJmwNd27g/;umid=0e42f21fc3f4e8704728805785e97255od;xm_grade=0;minorProtectionStatus=0;oaid=5d3cfedc6c1bd5aa;domain=.ximalaya.com;path=/;",
        "Cookie2": "$version=1",
        "Accept": "*/*",
        "user-agent": "ting_9.0.22(MI+8,Android29)",
        "x-xuid": "78c5c143-aa52-3f77-b3b6-fb4d0c289c3a",
        "Accept-Encoding": "gzip"
    }
    req = requests.get(get_token_url, headers=get_token_header)
    print("fdsOtp响应结果 = ",req.text)
    fdsOtp = json.loads(req.text)
    fdsOtp = fdsOtp['token']
    print("fdsOtp = ", fdsOtp)
    return fdsOtp
def sha1_secret_str(s):
    """
    :param s: 需要加密的字符串
    :return: 做完sha1的结果
    """
    sha = hashlib.sha1(s.encode('utf-8'))
    encrypts = sha.hexdigest()
    return encrypts
def login(account,password,fdsOtp,nonce):
    """
    :param account: 用户名
    :param password: 密码
    :param fdsOtp: fdsOtp
    :param nonce: nonce
    :return:
    """
    post_url = "https://passport.ximalaya.com/mobile/login/pwd/v3"
    post_header = {
        "Cookie": "1&_device=android&59683601-c043-33af-95d4-413ba0788573&9.0.22;channel=Avril22;impl=com.ximalaya.ting.android;osversion=29;fp=009314627x2222q2v644v0500b0000022002000000000000200000000000;device_model=MI+8;XIM=;c-oper=%E6%9C%AA%E7%9F%A5;net-mode=WIFI;freeFlowType=0;res=1080%2C2029;AID=n+Ew49Cv/1Y=;manufacturer=Xiaomi;XD=NSRvWEdehdyqqdzAdbPbJbOfbPFruUAJe3HFBaJ0z438KV7oI/Q/czqtjV2TmpUvQtc0VKk72doYCGDVyUUkqRWBD1AcqgIlS9o5aEQyWw97unfTMyod0vYnavI0Tm/o;umid=0e42f21fc3f4e8704728805785e97255od;xm_grade=0;minorProtectionStatus=0;oaid=5d3cfedc6c1bd5aa;domain=.ximalaya.com;path=/;",
        "Cookie2": "$version=1",
        "Accept": "*/*",
        "user-agent": "ting_9.0.22(MI+8,Android29)",
        "x-xuid": "78c5c143-aa52-3f77-b3b6-fb4d0c289c3a",
        "Host": "passport.ximalaya.com",
        "Content-Type": "application/json; charset=utf-8",
        "Accept-Encoding": "gzip",
        "Connection": "Keep-Alive"
    }
    signature = "ACCOUNT=" + account.upper() + "&FDSOTP=" + fdsOtp + "&NONCE=" + nonce.upper() + "&PASSWORD=" + password.upper() + "&MOBILE-V1-PRODUCT-7D74899B338B4F348E2383970CC09991E8E8D8F2BC744EF0BEE94D76D718C089"
    print("signature = ", signature)
    print("signature sha1 =", sha1_secret_str(signature.upper()))
    signature_sha1 = sha1_secret_str(signature)
    post_dict = {
        "password": password,
        "fdsOtp": fdsOtp,
        "signature": signature_sha1,
        "nonce": nonce,
        "account": account
    }
    print("Body = ",post_dict)
    req = requests.post(post_url, data=json.dumps(post_dict).encode("utf-8"), headers=post_header)
    print("登录结果 = ",req.text)
def get_end_username(account):
    """
    :param account: 没有组成官方格式的用户名
    :return: 按照官方格式组成的加密结果
    """
    account_1 = account[:76]
    account_2 = account[76:152]
    account_3 = account[152:]
    print("account = ",account)
    # print("account_1 = ",account_1)
    # print("account_2 = ",account_2)
    # print("account_3 = ",account_3)
    return account_1 + "\n" + account_2 + "\n" + account_3
def get_end_password(password):
    """
    :param password: 没有组成官方格式的密码
    :return: 按照官方格式组成的加密结果
    """
    password_1 = password[:76]
    password_2 = password[76:152]
    password_3 = password[152:]
    print("password = ", password)
    # print("password_1 = ", password_1)
    # print("password_2 = ", password_2)
    # print("password_3 = ", password_3)
    return password_1 + "\n" + password_2 + "\n" + password_3
public_key = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVhaR3Or7suUlwHUl2Ly36uVmboZ3+HhovogDjLgRE9CbaUokS2eqGaVFfbxAUxFThNDuXq/fBD+SdUgppmcZrIw4HMMP4AtE2qJJQH/KxPWmbXH7Lv+9CisNtPYOlvWJ/GHRqf9x3TBKjjeJ2CjuVxlPBDX63+Ecil2JR9klVawIDAQAB";
def rsa_encrypt(s):
    key = b64decode(public_key)
    # print(key)
    key = RSA.importKey(key)
    cipher = PKCS1_v1_5.new(key)
    ciphertext = b64encode(cipher.encrypt(bytes(s, "utf-8")))
    return ciphertext
def main(event, context):
    # 获取fdsOtp
    fdsOtp = get_fdsOtp()
    # 获取nonce
    nonce = get_nonce()
    # 账号密码加密
    account = rsa_encrypt("xxxxxx").decode()
    password = rsa_encrypt("xxxxxx").decode()
    # # 重组account
    # account = get_end_username(account)
    # # password
    # password = get_end_password(password)
    # print(account)
    # print(password)
    # 登录
    login(account,password,fdsOtp,nonce)
if __name__ == '__main__':
    main('','')


21.png (202.86 KB, 下载次数: 0)
下载附件
2022-7-7 19:38 上传

总体来说呢,花的时间有点长了,还是因为太菜了。很多图也是后来补的,大概的一个思路就是这个样子,可能写的不太好,轻喷。
改了代码,Python不用调用java了,但是我这里登陆出现滑块验证了...token拿不到报错了,不知道是为什么。。。。。

下载次数, 下载附件

简猿   

大佬第一帖,加个精,期待大佬后续佳作
aozo13   

看看, 学习学习
Baiii   

厉害!!!学习一下
李杨   

老哥牛批啊,能帮忙看下小黑盒的那个加密吗
yuqic987   

就应该这样嘛 多出来走走
zgy150010318   

这是大佬啊,学习下大佬的思路
此子非池中之物   

这是大佬啊,学习下大佬的思路
L1ni_0i   

woaipaojie
简猿   

牛逼,学习一下大佬的思路
您需要登录后才可以回帖 登录 | 立即注册

返回顶部