Reverse analysis of Android APP packet capture based on inspectage

Foreword: I have understood the usage of inspecage a little recently. I take the yuepao app as an example to record in detail the process of APP bag grabbing and simple reverse analysis, so as to make notes and learn together! In addition, warm reminder, there are many pictures in this article, it is recommended to connect WiFi to read!

catalog:

1, Preparation

1. APP needed

2. Installation and configuration

2, Start grabbing data

1. Inspectage monitoring

2. HttpCanary grab

3, Data analysis

1. HttpCanary data section

2,InspeckageData section

4, Code implementation (Python) 

1. AES-CBC-PKCS5 encryption and decryption

2. md5 encryption

3. Request data construction

4. Send request and parse response

5. Total code (including other interfaces and other data)

Text:

1, Preparation

1. APP needed

  • VMOS Pro
  • HttpCanary
  • JustTrustMe
  • Inspeckage

It has been arranged in blue play cloud, please download it by yourself!

Link:

https://huanxingke.lanzoui.com/b02069isj

password:

lptiyu

2. Installation and configuration

(1) Configuration of VMOS Pro

I. It is installed on the real machine;

II. In order to achieve better results, I opened a membership, using the following virtual machine:

III. after successfully loading and opening the virtual machine, you will see the following page and click file transfer:

IV. click I want to import:

5. Click Install Package, select inspect and justtrust me, and after confirmation, it will be installed automatically

Vi. installation of trail happy run: first install the trail happy run APP on the real machine, and then click I want to import > Application > find trail happy run > confirm the installation;

Then go back to the home page and click to enter Xpose:

8. Click the three bars in the upper left corner -- > module -- > select inspect and justtrust me module, and then restart the virtual machine to activate the module

IX. after restarting the virtual machine, the JustTrustMe module has been activated successfully by default, and then configure inspectage: enter inspectage. When Module enable and Server start are displayed, the initialization is successful, as shown in the following figure:

(2) Configuration of HttpCanary

I. It is installed on the real machine;

II. Open HttpCanary, click the three bars in the upper left corner -- > click settings in the lower left corner -- > Click SSL certificate settings, as shown in the figure:

III. click Install certificate, as shown in the figure, and then follow the prompts:

IV. return to the previous page and select the target application, as shown in the figure:

V. click the + sign in the upper right corner and select VOMS Pro, as shown in the figure

So far, the preparatory work is complete, ready to start to grab data.

 

2, Start grabbing data

1. Inspectage monitoring

(1) First of all, please log in your account number on the peddler APP, and then exit;

(2) Enter inspectage and select the monitoring trail happy running APP according to the following figure:

(3) After selection, keep the virtual machine running in the background, return to the real machine browser, and enter 127.0.0.1:8008. If you see the following interface, the configuration is preliminarily successful. Note that App is running is false and the top left corner switch is OFF

(4) Keep the real browser in the background, don't close it, and then proceed to the next step;

2. HttpCanary grab

(1) Open HttpCanary, click the small plane in the lower right corner, and the small plane turns green to represent the beginning of packet capture;

(2) Then go back directly, pay attention not to clean up the background and call out the virtual machine. At this time, HttpCanary will be in the lower right corner of the screen, as shown in the figure, and then click LAUNCH APP:

(3) At this time, Inspekage will wake up the treadmill APP, and the packet capture and monitoring work will start. As shown in the figure, you can see that HttpCanary has packet capture data:

(4) Then click the box navigation key of the real machine (gesture navigation is to press and hold the bottom of the screen to slide up), call out the real machine browser, and refresh the page just now. If App is running is true, the monitoring is successful. At this time, turn on the switch in the upper left corner to obtain the monitoring data. The successful interface is as follows:

So far, the packet capture and monitoring work has started, and the network request data after the start of the treadmill APP has been obtained, so we can start to analyze the data.

 

3, Data analysis

1. Httpcanary data section

(1) When you open HttpCanary in full screen, you can see that many requests have been captured. First, pull to the bottom and start from the initial request to see if there are any special requests that may meet our needs;

(2) As shown in the figure below, we can find that a request contains the Login keyword. We can subconsciously think that this may be a request related to user Login, which is an important part of APP packet capture

(3) Let's click to open this request, click request -- > click the preview in the lower right corner, as shown in the figure:

(4) We can see that there are several fields worthy of our attention: token, access_token,refresh_token,timestamp,nonce,jpush_id,sign:

I. The first three are token type fields. According to experience, such fields are generally generated by the server, so we will not consider them for the moment;

II. timestamp is the time stamp and nonce is the random string. They are all used to prevent replay attacks and can not be considered (see blogger)@ koastal My blog: https://blog.csdn.net/koastal/article/details/53456696);

To sum up, the most worthy consideration is the sign value. In fact, we should pay special attention to the sign value at the beginning, because it is a very common field of encryption algorithm, and we can also find that it is very similar to the md5 encrypted format, so we should write it down silently first;

(5) Then let's look at the response, as shown in the figure:

(6) Obviously, the data value in the response is base64 encoding, but when we decrypt it, we get garbled Code:

¶"Z¥ÆÍǠЩ<í/ÅtlÎÝοaó±8Êã½BV-0ÃúCÃæOÒ;Çÿ(^ò±°Î?t(:t1ÂTº	ådä0ãùºÆ^Ü~.KÝÜ[õõ»9+ÕI5ùÄs©îÁ^Çw[Ï8
Ïϼ#>,ÌeÔPÅ¿Vú2Êç7ZzÇF£ÙÈÊn©

So obviously, this field has been encrypted once before base64, which should arouse our interest;

After getting the above data, we can go to the inspectage website to find the corresponding encryption method.

2. Inspection data section

(1) Back to the webpage of inspectage, we need to know that Crypto (generally signature algorithm, such as AES) and Hash (Hash algorithm, such as md5) on the webpage are the sources of our search for encryption methods;

(2) Let's take a look at the data in Crypto first. Click Crypto, as shown in the figure (warm tip: please turn OFF the switch in the upper left corner first, and then analyze the data, otherwise the dynamic change of the web page will affect our data searching process)

(3) We can see that a lot of data has been monitored. As mentioned above, sign may be encrypted by md5, that is, Hash. Therefore, instead of looking for sign in Crypto, we first look for the data value. How do we find it

I. search according to the order of requests: we know that the data value is the response of the request containing the login field, and this request belongs to the first batch of requests sent in HttpCanary, so we should also pull to the bottom of the inspection webpage to search;

II. Use the search function of the browser, as shown in the figure:

Then input the data value to match and find. Note: you only need to copy the first few characters of the data value to find, because the page is not completely displayed. If you use the whole data value to match, you will not find the result;

(4) From the method in (3), we have successfully found the encryption algorithm of data, as shown in the figure:

After zooming in:

It's even more obvious if you copy it

(To protect privacy, Use of some data*Instead of)
SecretKeySpec(Wet2C8d34f62ndi3,AES) , Cipher[AES/CBC/PKCS5Padding] IV: K6iv85jBD8jgf32D (tiJapcbNx6AL0JmpPO0VL8V0bM7dEc4dvwth87E4HMrjE
L1CVi0ww5qO+kPD5k8L0jvH/xooXvKxsADOP5x0KAsSOpV0McJUugnlZOQwm+P5usZe3H4uS93cW/X1uzkQK4jVSY0eNfnEG3Op7hDBAl6PjccMd1uVzzgNz88VvCM+LMxl1FDFv1b6MsoC5zdaesdGoxiP******** , {"uid":"*******","access_token":"333C877C9429E26D4FCDC404******","refresh_token":"CC304EDBA40FF2F2AEA2D28C******","refresh_expire":1623509738})

In other words, the encryption algorithm of data before base64 is AES-CBC symmetric algorithm, and the string uses PKCS5Padding format, in which the SecretKey value is Wet2C8d34f62ndi3, the offset IV is K6iv85jBD8jgf32D, and the string before encryption is:

{"uid":"*******","access_token":"333C877C9429E26D4FCDC404******","refresh_token":"CC304EDBA40FF2F2AEA2D28C******","refresh_expire":1623509738}

data value successfully decoded!

(5) In the same way, we click on the Hash section to try to decipher the sign value

(6) Here's a trick. Because the default display of the web page is incomplete, some values can't be seen. We can click some > > symbols in the web page to display them completely to avoid matching failure

(7) Not surprisingly, the sign value algorithm and the string before encryption were also found

Copy it:

Algorithm(MD5) [access_token006C31A39AED1ECAAA28C32554******jpush_id1507bfd3f7684******mobileDeviceId185818969******mobileModelBRQ-AN00
mobileOsVersion7.1.2nonce320975ostype1refresh_token9EF86DB536CEB764B430EC2BA4******school_id***student_num******timestamp1620917736token006C31A39AED1ECAAA28C325****uid******version86version_name3.3.6rDJiNB9j7vD2 : 7aeb494fc0063ec5c60cfd7ef8373929]

So it's md5 encryption! The string before encryption is:

access_token006C31A39AED1ECAAA28C32554******jpush_id1507bfd3f7684******mobileDeviceId185818969******mobileModelBRQ-AN00
mobileOsVersion7.1.2nonce320975ostype1refresh_token9EF86DB536CEB764B430EC2BA4******school_id***student_num******timestamp1620917736token006C31A39AED1ECAAA28C325****uid******version86version_name3.3.6rDJiNB9j7vD2

So far, the simple reverse analysis has been completed! Other interfaces, other data encryption algorithms can use similar methods to find and decode! Can start coding!

 

4, Code implementation (Python)

1. AES-CBC-PKCS5 encryption and decryption (crypto.py)

(1) Let's first look at the installation and use of related libraries Blog Park: https://www.cnblogs.com/niuu/p/10107212.html

(2) Code implementation:

from Crypto.Cipher import AES
import base64
import re


# Encryption and decryption
class Crypto(object):
    def __init__(self):
        # pubkey value
        self.key = 'Wet2C8d34f62ndi3'.encode('utf-8')
        # Offset
        self.iv = b'K6iv85jBD8jgf32D'
        # AES-CBC symmetric encryption
        self.mode = AES.MODE_CBC
        # AES-CBC-PKCS5 format string
        self.bs = 16
        self.PADDING = lambda s: s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs)

    # AES-CBC encryption
    def AESEncrypt(self, text):
        generator = AES.new(self.key, self.mode, self.iv)
        crypt = generator.encrypt(self.PADDING(text).encode("utf-8"))
        # Convert to base64 after encryption
        crypted_str = base64.b64encode(crypt)
        result = crypted_str.decode()
        return result

    # decrypt
    def AESDecrypt(self, text):
        text = base64.b64decode(text)
        cryptos = AES.new(self.key, self.mode, self.iv)
        plain_text = cryptos.decrypt(text)
        data = bytes.decode(plain_text)
        # Transfer to Chinese
        data = data.replace(r'\/', '/').encode().decode('unicode_escape')
        # print(json.dumps(data))
        # format
        pat = re.compile(r'<html>(.*?)</html>')
        html = pat.findall(data)
        html = html[0] if html else ""
        html_ = html.replace('"', "'")
        # Turn to dictionary
        data = json.loads(data.replace(html, html_).strip('"').strip('\u000e').strip('\u0007').strip('\u0004'))
        return data

2. md5 encryption (Md5.py)

import hashlib


def Md5(text):
    text = text.encode()
    m = hashlib.md5()
    m.update(text)
    return m.hexdigest()

3. Request data build (data.py)

from crypto import *
import time


# The data marked with * involves privacy, please obtain it by yourself
data = {'version': '86', 'version_name': '3.3.6', 'mobileModel': 'BRQ-AN00', 'mobileDeviceId': '******', 'mobileOsVersion': '7.1.2', 'ostype': '1', 'student_num': '******', 'school_id': '***', 'uid': '******', 'token': '***************', 'timestamp': '', 'nonce': '******', 'access_token': '***************', 'refresh_token': '*****************', 'jpush_id': '*****************', 'sign': '*****************'}
timestamp = int(time.time())
token = '*************'
refresh_token = '************'
sign_data = 'access_token{}jpush_id*********mobileDeviceId**************mobileModelBRQ-AN00mobileOsVersion7.1.2nonce********ostype1refresh_token{}school_id***student_num********timestamp{}token{}uid******version86version_name3.3.6rDJiNB9j7vD2'.format(token, refresh_token, timestamp, token)
sign = Md5(sign_data)
data['token'] = token
data['access_token'] = token
data['refresh_token'] = refresh_token
data['timestamp'] = str(timestamp)
data['sign'] = sign
print(sign)
print(data)

4. Send request and parse response (req.py)

import urllib.parse
import requests


url = "https://api2.lptiyu.com/v3/api.php/Login/RefreshToken"
headers = {'Cookie': 'PHPSESSID=*******************', 'Connection': 'close', 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 'Content-Length': '299', 'User-Agent': 'Dalvik/2.1.0(Linux;U;Android7.1.2;BRQ-AN00Build/NZH54D)', 'Host': 'api2.lptiyu.com', 'Accept-Encoding': 'gzip'}
data = {'version': '86', 'version_name': '3.3.6', 'mobileModel': 'BRQ-AN00', 'mobileDeviceId': '***************', 'mobileOsVersion': '7.1.2', 'ostype': '1', 'student_num': '************', 'school_id': '***', 'uid': '*******', 'token': '*************', 'timestamp': '******', 'nonce': '*********', 'access_token': '**********', 'refresh_token': '***********', 'jpush_id': '***************', 'sign': '*************'}
data = urllib.parse.urlencode(data)
response = requests.post(url=url, headers=headers, data=data).json()
print(response)

5. Total code (including other interfaces and other data)

from Crypto.Cipher import AES
import urllib.parse
import requests
import hashlib
import random
import string
import base64
import time
import json
import re
import os


# Encryption and decryption
class Crypto(object):
    def __init__(self):
        # pubkey value
        self.key = 'Wet2C8d34f62ndi3'.encode('utf-8')
        # Offset
        self.iv = b'K6iv85jBD8jgf32D'
        # AES-CBC symmetric encryption
        self.mode = AES.MODE_CBC
        # AES-CBC-PKCS5 format string
        self.bs = 16
        self.PADDING = lambda s: s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs)

    # AES-CBC encryption
    def AESEncrypt(self, text):
        generator = AES.new(self.key, self.mode, self.iv)
        crypt = generator.encrypt(self.PADDING(text).encode("utf-8"))
        # Convert to base64 after encryption
        crypted_str = base64.b64encode(crypt)
        result = crypted_str.decode()
        return result

    # decrypt
    def AESDecrypt(self, text):
        text = base64.b64decode(text)
        cryptos = AES.new(self.key, self.mode, self.iv)
        plain_text = cryptos.decrypt(text)
        data = bytes.decode(plain_text)
        # Transfer to Chinese
        data = data.replace(r'\/', '/').encode().decode('unicode_escape')
        # print(json.dumps(data))
        # format
        pat = re.compile(r'<html>(.*?)</html>')
        html = pat.findall(data)
        html = html[0] if html else ""
        html_ = html.replace('"', "'")
        # Turn to dictionary
        data = json.loads(data.replace(html, html_).strip('"').strip('\u000e').strip('\u0007').strip('\u0004'))
        return data

    # md5 encryption
    @staticmethod
    def Md5(text):
        text = text.encode()
        m = hashlib.md5()
        m.update(text)
        return m.hexdigest()


# Generate sign/data value
class Md5Encrypt(object):
    def __init__(self, **kwargs):
        # Raw json data
        self.data = {
            # Customizable values
            "jpush_id": "",
            "mobileDeviceId": "",
            "mobileModel": "",
            "mobileOsVersion": "7.1.2",
            # Client version number, not recommended to modify
            "version": "86",
            "version_name": "3.3.6",
            "ostype": "1",
            # Flag value
            # School id, default - 1 is unknown
            "school_id": "-1",
            # time stamp
            "timestamp": "",
            # Six digit random number string
            "nonce": ""
        }
        # A fixed value at the end of sign
        self.sign_str = 'rDJiNB9j7vD2'
        # User uid
        self.uid = kwargs['uid'] if 'uid' in kwargs else None
        # Student number
        self.student_num = kwargs['student_num'] if 'student_num' in kwargs else None
        # Set other values
        for key, value in kwargs.items():
            self.data[key] = value

    # Login data value
    def loginData(self, code, phone):
        # Inherit data property
        data = self.data
        # Update flag value
        # Fixed to 1
        data['type'] = '1'
        # cell-phone number
        data['phone'] = str(phone)
        # The verification code can only be obtained once in 30 days
        data['code'] = str(code)
        # time stamp
        timestamp = str(int(time.time()))
        # Six digit random number string
        nonce = str(random.randint(100000, 999999))
        # Put in data
        data['timestamp'] = timestamp
        data['nonce'] = nonce
        # Generate the original sign value
        # Be sure to sort the dictionaries first
        sign_data = ''.join([(i + data[i]) for i in sorted(data)]) + self.sign_str
        # Encrypted sign value
        sign = Crypto().Md5(sign_data)
        # Put sign value into data
        data['sign'] = sign
        # Encrypt data
        data = Crypto().AESEncrypt(json.dumps(data))
        # The original json value of the transferred data
        data = {
            'key': data
        }
        # Convert to form data type
        data = urllib.parse.urlencode(data)
        # Returns the data value
        return data

    # Get user information
    def userData(self, token):
        # Inherit data property
        data = self.data
        # Delete jpush_id value
        del data['jpush_id']
        # Update flag value
        # User id
        data['uid'] = self.uid
        # token value
        data['token'] = token
        # time stamp
        timestamp = str(int(time.time()))
        # Six digit random number string
        nonce = str(random.randint(100000, 999999))
        # Put in data
        data['timestamp'] = timestamp
        data['nonce'] = nonce
        # Generate the original sign value
        # Be sure to sort the dictionaries first
        sign_data = ''.join([(i + data[i]) for i in sorted(data)]) + self.sign_str
        # Encrypted sign value
        sign = Crypto().Md5(sign_data)
        # Put sign value into data
        data['sign'] = sign
        # Convert to form data type
        data = urllib.parse.urlencode(data)
        # Returns the data value
        return data

    # Get cookie
    def ipData(self, token):
        # Inherit data property
        data = self.data
        # Update flag value
        # Fixed to 2
        data['type'] = '2'
        # Student number
        data['student_num'] = self.student_num
        # User id
        data['uid'] = self.uid
        # token value
        data['token'] = token
        # time stamp
        timestamp = str(int(time.time()))
        # Six digit random number string
        nonce = str(random.randint(100000, 999999))
        # Put in data
        data['timestamp'] = timestamp
        data['nonce'] = nonce
        # Generate the original sign value
        # Be sure to sort the dictionaries first
        sign_data = ''.join([(i + data[i]) for i in sorted(data)]) + self.sign_str
        # Encrypted sign value
        sign = Crypto().Md5(sign_data)
        # Put sign value into data
        data['sign'] = sign
        # Convert to form data type
        data = urllib.parse.urlencode(data)
        # Returns the data value
        return data

    # Refresh token value
    def refreshData(self, token, refresh_token):
        # Inherit data property
        data = self.data
        # Update flag value
        # Student number
        data['student_num'] = self.student_num
        # User id
        data['uid'] = self.uid
        # token value
        data['token'] = token
        # access_token value (same as token)
        data['access_token'] = token
        # refresh_token value
        data['refresh_token'] = refresh_token
        # time stamp
        timestamp = str(int(time.time()))
        # Six digit random number string
        nonce = str(random.randint(100000, 999999))
        # Put in data
        data['timestamp'] = timestamp
        data['nonce'] = nonce
        # Generate the original sign value
        # Be sure to sort the dictionaries first
        sign_data = ''.join([(i + data[i]) for i in sorted(data)]) + self.sign_str
        # Encrypted sign value
        sign = Crypto().Md5(sign_data)
        # Put sign value into data
        data['sign'] = sign
        # Convert to form data type
        data = urllib.parse.urlencode(data)
        # Returns the data value
        return data

    # Get the leaderboard
    def rankData(self, token, page):
        # Inherit data property
        data = self.data
        # Delete jpush_id value
        if 'jpush_id' in data:
            del data['jpush_id']
        if 'sign' in data:
            del data['sign']
        # Update flag value
        # Fixed to 1
        data['type'] = '1'
        # Fixed to 1
        data['category'] = '1'
        # Student number
        data['student_num'] = self.student_num
        # User id
        data['uid'] = self.uid
        # token value
        data['token'] = token
        # Page number
        data['page'] = str(page)
        # time stamp
        timestamp = str(int(time.time()))
        # Six digit random number string
        nonce = str(random.randint(100000, 999999))
        # Put in data
        data['timestamp'] = timestamp
        data['nonce'] = nonce
        # Generate the original sign value
        # Be sure to sort the dictionaries first
        sign_data = ''.join([(i + data[i]) for i in sorted(data)]) + self.sign_str
        # Encrypted sign value
        sign = Crypto().Md5(sign_data)
        # Put sign value into data
        data['sign'] = sign
        # Convert to form data type
        data = urllib.parse.urlencode(data)
        # Returns the data value
        return data


# Send request
class GetResponse(object):
    def __init__(self, **kwargs):
        # Request header
        self.headers = {
            'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
            'User-Agent': 'Dalvik/2.1.0(Linux;U;Android7.1.2;BRQ-AN00Build/NZH54D)',
            'Host': 'api2.lptiyu.com',
            'Accept-Encoding': 'gzip'
        }
        # Read user information
        userData = User().getUser()
        # Virtual client information
        jpush_id = userData['jpush_id']
        mobileDeviceId = userData['mobileDeviceId']
        mobileModel = userData['mobileModel']
        # User identification information
        # User uid
        if 'uid' not in userData:
            self.encrypter = Md5Encrypt(jpush_id=jpush_id, mobileDeviceId=mobileDeviceId, mobileModel=mobileModel)
            self.login(autoLogin=False)
        else:
            self.uid = userData['uid']
            self.token = userData['access_token']
            self.refresh_token = userData['refresh_token']
        # Student number
        if 'student_num' not in userData:
            print('Getting student number...')
            self.encrypter = Md5Encrypt(jpush_id=jpush_id, mobileDeviceId=mobileDeviceId, mobileModel=mobileModel, uid=self.uid)
            self.user()
        else:
            self.student_num = userData['student_num']
        # Overload encryptor
        self.encrypter = Md5Encrypt(jpush_id=jpush_id, mobileDeviceId=mobileDeviceId, mobileModel=mobileModel, uid=self.uid, student_num=self.student_num)
        print('Initialization complete!')

    # Log in again
    def login(self, code=None, phone=None, autoLogin=True):
        if not autoLogin:
            print('Please login again!')
            phone = input('Please input mobile phone number: ')
            code = input('Please enter the verification code: ')
            print('Signing in......')
        url = 'https://api2.lptiyu.com/v3/api.php/Login/quickLoginV300'
        data = self.encrypter.loginData(code=code, phone=phone)
        response = requests.post(url=url, headers=self.headers, data=data).json()
        if 'data' not in response:
            raise Exception("Login failed!")
        tokenData = Crypto().AESDecrypt(response['data'])
        self.uid = tokenData['uid']
        self.token = tokenData['access_token']
        self.refresh_token = tokenData['refresh_token']
        # Save token data
        User().saveUser(userData=tokenData)
        return tokenData

    # Get user information
    def user(self):
        url = 'https://api2.lptiyu.com/v3/api.php/User/User'
        data = self.encrypter.userData(token=self.token)
        response = requests.post(url=url, headers=self.headers, data=data).json()
        if 'data' not in response:
            raise Exception("Login failure!")
        user = Crypto().AESDecrypt(response['data'])
        self.student_num = user['student_num']
        User().saveUser(userData=user)
        return user

    '''
    # Get cookie
    def getIp(self):
        url = 'https://api2.lptiyu.com/v3/api.php/System/getIp'
        data = self.encrypter.ipData(self.token)
        response = requests.post(url=url, headers=self.headers, data=data)
        try:
            cookies = response.cookies
            cookies = requests.utils.dict_from_cookiejar(cookies)
            self.headers["Cookie"] = list(cookies.keys())[0] + '=' + list(cookies.values())[0]
        except:
            raise Exception("Login failure!")
        return cookies
    '''

    # Refresh token value
    def refreshToken(self):
        url = 'https://api2.lptiyu.com/v3/api.php/Login/RefreshToken'
        data = self.encrypter.refreshData(token=self.token, refresh_token=self.refresh_token)
        response = requests.post(url=url, headers=self.headers, data=data).json()
        if 'data' not in response:
            raise Exception("Login failure!")
        tokenData = Crypto().AESDecrypt(response['data'])
        self.token = tokenData['access_token']
        self.refresh_token = tokenData['refresh_token']
        # Save token data
        User().saveUser(userData=tokenData)
        return tokenData

    # Get the leaderboard
    def getTotalRank(self, page):
        url = 'https://api2.lptiyu.com/v3/api.php/Run/getTotalRank'
        data = self.encrypter.rankData(token=self.token, page=page)
        response = requests.post(url=url, headers=self.headers, data=data).json()
        if 'data' not in response:
            raise Exception("Login failure!")
        rankData = Crypto().AESDecrypt(response['data'])['rank_list']
        return rankData


# Operation user file
class User(object):
    def __init__(self):
        # token file path
        self.file = 'user.json'

    # Building client information
    def createInfo(self):
        s = string.ascii_letters + string.digits
        jpush_id = "".join(random.choice(s) for _ in range(0, 19))
        mobileDeviceId = "".join(random.choice(string.digits) for _ in range(0, 15))
        mobileModel = "".join(random.sample(string.digits, 3)) + '-' + "".join(random.sample(s, 4))
        user = {'jpush_id': jpush_id, 'mobileDeviceId': mobileDeviceId, 'mobileModel': mobileModel}
        with open(self.file, 'w') as fp:
            json.dump(user, fp)

    # Get User information locally
    def getUser(self):
        if not os.path.exists(self.file):
            self.createInfo()
        with open(self.file, 'r') as fp:
            userData = json.load(fp)
        return userData

    # Save User data
    def saveUser(self, userData):
        with open(self.file, 'r') as fp:
            userData_ = json.load(fp)
        for key, value in userData.items():
            userData_[key] = value
        with open(self.file, 'w') as fp:
            fp.write(json.dumps(userData_))


if __name__ == '__main__':
    handler = GetResponse()
    fp = open('lptiyu.csv', 'w', encoding='utf-8')
    fp.write('ranking,full name,Number of runs\n')
    for j in range(1, 4):
        rankData_ = handler.getTotalRank(page=j)
        for i in rankData_:
            score = i['score_num']
            rank = i['rank']
            name = i['name']
            fp.write('%s,%s,%s\n' % (rank, name, score))
    fp.close()

Well, that's all for today's summary and sharing. Thank you for reading!

Tags: Python Python crawler app

Posted by SemiApocalyptic on Fri, 14 May 2021 03:05:19 +0930