Reverse case of X car network

Reverse case of X car network

***Used knowledge points:

(1) requests module and session module, initiate a request

  • What is a session object?
    • The usage of this object is almost the same as that of the requests module.
    • For requests that generate cookies during the request
    • If the request is initiated using a session, the cookie will be automatically stored in the session.
session = requests.Session()

See the blog for details: https://www.cnblogs.com/dream-ze/p/17176494.html

(2) headers header camouflage, random UA camouflage

# UA camouflage random headers module
from fake_useragent import UserAgent

headers = {'User-Agent': UserAgent().random,} # The random method here does not require random module support

(3) md5 encryption algorithm, encrypts data

# md5 encryption module
from hashlib import md5
# If you need to convert between hexadecimal results and binary results, the required modules
import binascii

# [1] Prepare data
data = 'hello' #here is a string type
# String to binary data method 1
encode_data = s. encode()
# String to binary data method 2
encode_data = b'hello'

# [2] Data encryption
# build md5 object
md5_obj = md5()
# Update the data to the md5 algorithm for data encryption (parameters are plaintext data of binary data)
#(Method 1): transcoding directly in the encryption algorithm
md5_obj.update("Hello".encode("utf-8"))
md5_obj.update(data.encode("utf-8"))
#(Method 2): Transcode the plaintext data first, and then pass it into the encryption algorithm
md5_obj. update(encode_data)


# [3] Data extraction
# get encrypted string # hexadecimal result
result_16 = obj.hexdigest()
# get encrypted string # binary result
result_2 = obj.digest()
# Get the encrypted string # Conversion between the hexadecimal result and the binary result (the parameter is result_16 or result_2)
result_change = binascii. unhexlify(result_16)

See blog for details: https://www.cnblogs.com/dream-ze/p/17362507.html

(4) json module, serialization and deserialization

dataDict = json.loads(decrypt_data.decode())

See the blog for details: https://www.cnblogs.com/dream-ze/p/17337544.html

(5) separators

of json.dumps() parameter

# is the meaning of the separator
# The meaning of the parameter is the separator between different dict items and the separator between key and value in the dict item
# - Remove the spaces after : and .

import json

data = {"a": 1, "b": 2}

ret = json.dumps(data, separators=(',', ':'))
print(ret)

# {"a":1,"b":2}

See blog for details: https://www.cnblogs.com/dream-ze/p/17418963.html

(6) URL encoding and decoding

  • coding
# -*-coding: Utf-8 -*-
# @File : URL-encoded .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time: 2023/5/21

# import parse module
from urllib import parse

# Build query string dictionary
query_string = {
    'wd': 'Crawler'
}
# Call the urlencode() of the parse module to encode
result = parse.urlencode(query_string)
# Use the format function to format the string and splice the url address
url = 'http://www.baidu.com/s?{}'.format(result)
print(url)

# http://www.baidu.com/s?wd=crawler
# -*-coding: Utf-8 -*-
# @File : URL-encoded .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time: 2023/5/21

from urllib import parse

# Pay attention to the writing format of url, which is different from urlencode
url = 'http://www.baidu.com/s?wd={}'
word = input('Please enter the content you want to search:')
# quote() can only encode strings
query_string = parse. quote(word)
print(url. format(query_string))

# Please enter what you want to search for: crawler
# http://www.baidu.com/s?wd=crawler
  • decoding
# -*-coding: Utf-8 -*-
# @File : URL-encoded .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time: 2023/5/21

from urllib import parse

string = 'crawler'
result = parse. unquote(string)
print(result)

# crawler

See blog for details: https://www.cnblogs.com/dream-ze/p/17418964.html

【1】Analysis website

Reverse URL: https://car.yiche.com/baomax5-8108/peizhi/

  • As can be seen from the URL, this is a URL that introduces car parameters

  • Then we reverse it, that is, for its parameter data

  • First of all, we still open the developer tool to capture the package

  • Judging from the number of captured packets, there are many, and we don’t know which packet the data is in without trying.
  • We analyze package by package
  • It is found that the data we want is in the package config_new_param?......
  • The information returned by this package is exactly what we want

  • By convention we check the request header and payload of this packet

  • Through observation, we found that this request is a get request, and the request parameters of the get request are generally spelled directly at the end of the url

  • Let’s observe his request header again
:authority: mapi.yiche.com
:method: GET
:path: /web_api/car_model_api/api/v1/car/config_new_param?cid=508 &param={"cityId":"2401","serialId":"8108"}
:scheme:https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: zh-CN,zh;q=0.9
cache-control: no-cache
content-type: application/json;charset=UTF-8
cookie: CIGUID=93f5a6aa4597abac03dde7ab96b3e3c9; isWebP=true; locatecity=310100; bitauto_ipregion=180.164.67.62: Shanghai; 2401, Shanghai, shanghai; auto_id=044e8069776834cba1d5d72 375d6423c; UserGuid=93f5a6aa4597abac03dde7ab96b3e3c9; Hm_lvt_610fee5a506c80c9e1a46aa9a2de2e44=1684637884; ectcityName = Shanghai; Hm_lpvt_610fee5a506c80c9e1a46aa9a2de2e44=1684659764
origin: https://car.yiche.com
pragma: no-cache
referer: https://car.yiche.com/baomax5-8108/peizhi/
sec-ch-ua: "Chromium";v="110", "Not A(Brand";v="24", "Google Chrome";v="110"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: same-site
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36
x-city-id: 2401
x-ip-address: 180.164.67.62
x-platform: pc
x-sign: 5272f564ad0e326b85701320d17eb1bc
x-timestamp: 1684659763986
x-user-guid: 93f5a6aa4597abac03dde7ab96b3e3c9

  • Through observation, we found that his request header carries the common sign value we used to request in post and the familiar time timestamp value

  • Let’s look at his load data

cid: 508
param: {"cityId":"2401","serialId":"8108"}

  • We found out it was a bunch of numbers, we couldn’t understand it, but it doesn’t matter

  • Here is one way to encode the url

    • The purpose is to prevent ambiguity caused by parameters carrying special symbols during URL transmission
  • We click to view the view next to the source code is decoded

cid: 508
param: {"cityId":"2401","serialId":"8108"}

  • Then we can see the parameters he carries.
  • We can also compare the parameters of the two places and find that
  • They all have a common parameter 2401

【2】Reverse search parameters

  • After the above analysis, we can basically confirm that the values that will change are the two parameters x-sign and x-timestamp in our request header
  • We still have two ways to find these two parameters
    • Search keywords directly
    • Through the starter, step by step to find the method of calling each function that generates data
  • Here we use the method of reversing the function (starter looking back)
    • This method is basically error-free. Global search is possible for short-answer website reverse, including those with certain experience.
    • I used the reverse method this time

  • We find the launcher, then find the first function, click in to find his calling function

  • That is, this function, we put a breakpoint on it
  • The second request to observe whether it is interrupted

  • successful break
  • Here, we have to pay special attention to one thing. In the past, we used to directly locate the url keyword to locate the related functions, but this time it is different. We will find that every time the URL request is requested, it is always continuous Variety
  • Then we have to pay special attention to the request url where we found the data packet
  • that is
https://mapi.yiche,com/web api/car model api/api/v1/car/config ne param?

  • By continuously clicking to let the script continue to execute, we have come to this step of the key parameter we want.

  • Then we follow this request to find the parameters we want

  • The idea is

    • The encryption parameters we want to find reversely must have obvious changes before and after
    • If it is a fixed value that does not change, then it is of little significance for us to reverse it
  • Following this line of thought, we switch up the call stack step by step

  • At the same time, pay attention to setting breakpoints in possible functions to view data changes

  • We looked up the call stack step by step and found that there is a very suspicious place here. We all know that the website will send Ajax requests, and then return to get the data, so let’s first see what the passed s is.

  • We can find out by printing s on the console that it is such a string of data, among which we should focus on the two parameters of data and headers
    • data is generally used to store data
    • headers because we just discovered that the request parameters we need are in our request headers
  • We opened the headers and found that the encrypted sign value was already carried in it
  • Then let’s look forward to a calling function to see if the value of sign changes

  • We found in the previous calling function that the sign value in the parameter it carries has changed, so we have reason to suspect that this clientAxios.axios(e, n) function is to encrypt the data and return the function of

  • we enter this function

  • Enter this function by hovering to view the calling function
function axios(e, t) {
            var n, i = {
                cid: "601",
                ver: clientAxios. getVer(),
                timestamp: (new Date). getTime()
            };
            t = window.yicheUtils.extend(t, i);
            var o = (n = {
                headers: {
                    "x-platform": "phone"
                },
                method: "GET",
                data: {},
                withCredentials: "true",
                async: "true",
                isParam: "true",
                dataType: "json",
                defaultContentType: "true",
                okFun: function(e) {},
                errFun: function(e) {},
                encryptType: "headers",
                isEncrypt: "false"
            },
            _defineProperty(n, "defaultContentType", "true"),
            _defineProperty(n, "isBrush", "false"),
            _defineProperty(n, "proxy", "false"),
            n);
            if (e = window. yicheUtils. extend(e, o),
            !e.url)
                return console.log("The request URL address cannot be empty");
            var r = new Date
              , a = {
                url: e.url,
                lgin: e.data || "",
                inbyte: e.data ? JSON.stringify(e.data).length : 0,
                st: r.toLocaleString()
            }
              , s = {
                dataType: e.dataType,
                method: e.method,
                headers: clientAxios. getHeaders(e, t),
                url: "true" == e.proxy ? e.url : window.yicheUtils.urlDispose(e.url),
                async: e.async,
                xhrFields: {
                    withCredentials: e.withCredentials
                },
                data: clientAxios. getData(e, t),
                success: function(t) {
                    return t & amp; & amp; 1 == t.status ? e.okFun & amp; & amp; e.okFun(t) : e.errFun & amp; & amp; e.errFun(t)
                },
                error: function(t) {
                    e.errFun & amp; & amp; e.errFun({
                        status: 0,
                        message: "Request exception" + t,
                        data: null
                    })
                },
                complete: function(t, n) {
                    var i = e. headers;
                    if (window._reportLog)
                        if (a.dur = Date.now() - r.getTime(),
                        a.lgout = t.responseText || "",
                        a.outbyte = a.lgout.length,
                        "success" == n)
                            try {
                                var o = JSON. parse(t. responseText);
                                window._reportLog(a, "info", i),
                                (!o || "" !== o.ercd & amp; & amp; null !== o.ercd & amp; & amp; void 0 !== o.ercd) & amp; & amp; (a. reason = "status: " + o.status,
                                window._reportLog(a, "err", i))
                            } catch(s) {
                                window._reportLog(a, "info", i),
                                a.reason = "responseText: " + t.responseText,
                                window._reportLog(a, "err", i)
                            }
                        else
                            window._reportLog(a, "info", i),
                            a.reason = n || "",
                            window._reportLog(a, "err", i)
                }
            };
  • That’s all for this big list of functions

  • We analyze this function

  • It is normal to add some request parameters before, when we read this line of code, we need to pay special attention

    headers: clientAxios.getHeaders(e, t),
  • Because the request parameters we need are in the headers, we need to focus on where the headers appear

  • Through this code, we can understand that the meaning of this line of code is to get the headers parameter from this function and assign it to the headers variable.

  • We can print out this function on the console, or we can select all and hover to view its data
  • We found that this data carries our official encryption function, so our purpose is to go to this function to see how it is generated
  • In the same way we enter the function

function i(e, t) {
            var n = {
                cid: "601",
                timestamp: (new Date). getTime(),
                gradeParam: {},
                uid: window.__uid__ || "",
                ver: window.__appver__ || null,
                headerEncryptKeys: [{
                    name: "pc",
                    value: "19DDD1FBDFF065D3A4DA777D2D7A81EC",
                    cid: "508"
                }, {
                    name: "phone",
                    value: "DB2560A6EBC65F37A0484295CD4EDD25",
                    cid: "601"
                }, {
                    name: "h5",
                    value: "745DFB2027E8418384A1F2EF1B54C9F5",
                    cid: "601"
                }, {
                    name: "business_applet",
                    value: "64A1071F6C3C3CC68DABBF5A90669C0A",
                    cid: "601"
                }, {
                    name: "wechat",
                    value: "AF23B0A6EBC65F37A0484395CE4EDD2K",
                    cid: "601"
                }, {
                    name: "tencent",
                    value: "1615A9BDB0374D16AE9EBB3BBEE5353C",
                    cid: "750"
                }],
                paramsKey: "f48aa2d0-31e0-42a6-a7a0-64ba148262f0"
            };
            t = window.yicheUtils.extend(t, n),
            t.cid = f(e, t),
            t.timestamp = (new Date).getTime();
            var i = yicheUtils.deepClone(e.headers);
            i["x-timestamp"] = t.timestamp,
            o("Content-Type", e.headers) || "true" != e.defaultContentType || (i["Content-Type"] = "application/json;charset=UTF-8"),
            "headers" == e.encryptType & amp; & amp; (i["x-sign"] = s(e, t)),
            "true" == e.isEncrypt & amp; & amp; yicheUtils.switchEncrypt() & amp; & amp; (i.encryptType = 2);
            var r = l()
              , a = c()
              , d = u();
            return o("x-city-id", e. headers) || (i["x-city-id"] = r),
            o(" ", e. headers) || (i["x-ip-address"] = a),
            o("x-user-guid", e. headers) || (i["x-user-guid"] = d),
            window.__getAjaxHeader & & (i = yicheUtils.extend(i, window.__getAjaxHeader())),
            "true" == e.isBrush & amp; & amp; (i = yicheUtils.setEncryptParams(i, t.timestamp)),
            i
        }
  • That’s all the methods of that function
  • We observe the calling method of this function, we can find such a string of data
headerEncryptKeys: [{
                    name: "pc",
                    value: "19DDD1FBDFF065D3A4DA777D2D7A81EC",
                    cid: "508"
                }, {
                    name: "phone",
                    value: "DB2560A6EBC65F37A0484295CD4EDD25",
                    cid: "601"
                }, {
                    name: "h5",
                    value: "745DFB2027E8418384A1F2EF1B54C9F5",
                    cid: "601"
                }, {
                    name: "business_applet",
                    value: "64A1071F6C3C3CC68DABBF5A90669C0A",
                    cid: "601"
                }, {
                    name: "wechat",
                    value: "AF23B0A6EBC65F37A0484395CE4EDD2K",
                    cid: "601"
                }, {
                    name: "tencent",
                    value: "1615A9BDB0374D16AE9EBB3BBEE5353C",
                    cid: "750"
                }],
                paramsKey: "f48aa2d0-31e0-42a6-a7a0-64ba148262f0"
            };
  • We are familiar with this string of data, and we need to pay special attention to the corresponding data corresponding to the PC. Its function is probably to identify the device.
name: "pc",
value: "19DDD1FBDFF065D3A4DA777D2D7A81EC",
cid: "508"

  • Through these codes, we can find that this is the keyword of the data we want

    • x-sign
      • changing sign value
    • x-city-id
      • presumably the city ID
    • x-ip-address
      • presumably the IP address
    • x-user-guid
      • Probably related to user login
  • When we analyze the above parameters, we need to pay special attention to whether the changed sign value and the login parameters have changed

    • Don’t think about it, the sign will definitely change
    • Then let’s look at x-user-guid first
  • We find that this parameter = d then we find out how d comes from

    • He is actually above us d = u()
    • We look at the u function
function u() {
            var e = yicheUtils.getCookie("UserGuid") || yicheUtils.getCookie("CIGUID");
            if (e)
                return e;
            var t = window.yicheUtils.createGuid();
            return window.yicheUtils.setCookie("CIGUID", t, 43800, ".yiche.com"),
            t
        }
  • We analyze this code and find that it has two branches

    • one is established
      • returns a parameter
    • one is a failure
      • After the establishment, a new ID will be generated, and the dye will return this ID
  • Then we guess that it is the logo that identifies whether the user has visited this web page. Make a mark if visited, otherwise create a new logo for the new user

  • Now that this parameter is resolved, we are left with the last parameter sign

  • We enter this function that generates sign
function s(e, t) {
            var n = "";
            if ("headers" == e.encryptType) {
                var i = e.data ? JSON.stringify(e.data) : "{}"
                  , o = r(e, t);
                n = "cid=" + t.cid + " &param=" + i + o + t.timestamp
            } else {
                var a = [];
                a. push("cid=" + t. cid),
                a.push("uid=" + t.uid),
                a.push("ver=" + t.ver),
                a.push("devid=" + (e.deviceId || "")),
                a.push("t=" + t.timestamp),
                a.push("key=" + t.paramsKey),
                n = a.join(";")
            }
            var s = yicheUtils.md5(n);
            returns
        }
  • By analyzing this code, we have two branches
    • If this header is equal to the header in our e (actually an md5 signature verification operation)
      • Then we must go the right way and let him generate the data we want for us
    • The other is that the signature verification failed.
      • The one in front is Gagaga
      • Then it’s useless to ignore him
  • Next, let’s look at the following s (because its return value is s)
    • He is an md5 encrypted operation and then gets his encrypted value
  • We need to understand what the parameters of this encryption are
    • Let’s take the function so far to see what n actually is

  • We can find out by printing out this function on the console, or by analyzing the function after the above conditions are established.
    • He is actually a string concatenation operation
'cid=508 & amp;param={"cityId":"2401","serialId":"8108"}19DDD1FBDFF065D3A4DA777D2D7A81EC1684663988312'
  • The first one is obviously part of the request parameters we carry
  • As for the latter, it is actually the value corresponding to the part where we identify the device
  • Finally, the md5 encryption function generates the sign value

【3】Reverse code

【1】Reverse of timestamp

  • Timestamps are basically fixed
    • 13-bit timestamp
import time
x_timestamp = str(int(time.time() * 1000))

【2】Reverse of sign parameter

  • Generate md5 encrypted parameters
import time
import json
import hashlib
x_timestamp = str(int(time.time() * 1000))

# One of the parameters inside
p = {"cityId": "2401", "serialId": "8108"}
# convert the string to json format
p_str = json. dumps(p)
n = f'cid=508 &param={p_str}19DDD1FBDFF065D3A4DA777D2D7A81EC{x_timestamp}'
# create md5 object
mad5 = hashlib.md5()
# The parameters passed in here must be binary data
mad5. update(n. encode())
# Take the md5 encrypted value
sign = mad5.hexdigest()
  • There is a bug that requires extra care, that is, the parameters you pass to md5 encryption must be the same as the request parameters you carry
    • If you do not want to wait for md5 signature verification to fail, you will not be able to request data
    • The author of this encryption is really a talent, if you don’t pay attention, you may really be cheated!

【3】Complete Python code

# -*-coding: Utf-8 -*-
# @File : 01.py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time: 2023/5/21

import requests
import os
from fake_useragent import UserAgent
import time
import json
import hashlib

x_timestamp = str(int(time.time() * 1000))

# 'https://mapi.yiche.com/web_api/car_model_api/api/v1/car/config_new_param?cid=508 & amp;param={"cityId":"2401","serialId":"8108"}'

page_url = 'https://mapi.yiche.com/web_api/car_model_api/api/v1/car/config_new_param?'
p = {"cityId": "2401", "serialId": "8108"}
p_str = json. dumps(p)
n = f'cid=508 &param={p_str}19DDD1FBDFF065D3A4DA777D2D7A81EC{x_timestamp}'
mad5 = hashlib.md5()
mad5. update(n. encode())
sign = mad5.hexdigest()
params = {
    'cid': '508',
    'param': p_str,
}
headers = {

    'cookie': 'CIGUID=93f5a6aa4597abac03dde7ab96b3e3c9; isWebP=true; locatecity=310100; bitauto_ipregion=180.164.67.62: Shanghai; 2401, Shanghai, shanghai; auto_id=044e8069776834cba1d5 d72375d6423c; UserGuid=93f5a6aa4597abac03dde7ab96b3e3c9; Hm_lvt_610fee5a506c80c9e1a46aa9a2de2e44=1684637884; selectcity=310100; selectcityid= 2401; selectcityName=Shanghai; Hm_lpvt_610fee5a506c80c9e1a46aa9a2de2e44=1684637896',
    'origin': 'https://car.yiche.com',
    'referer': 'https://car.yiche.com/baomax5-8108/peizhi/',
    'User-Agent': UserAgent(). random,
    'x-city-id': '2401',
    'x-ip-address': '180.164.67.62',
    'x-platform': 'pc',
    'x-sign': sign,
    'x-timestamp': x_timestamp,
    'x-user-guid': '93f5a6aa4597abac03dde7ab96b3e3c9',
}

response = requests.get(page_url, params=params, headers=headers)

# Convert the returned data to json format for easy value retrieval
# Get the data inside
data_list = json. loads(response. text)['data']

for data in data_list:
    print('This is name', data['name'])
    # print('--------------dividing line data------------------')
    items = data['items']
    print('This is items', items)

    for item in items:
        print('This is item', items)
  • If you are interested, go to the next step to get the value. I am too lazy to do it. There are too many data and nesting layer by layer is very troublesome.

The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge. Network skill treeHomepageOverview 33504 people are studying systematically