[JS reverse hundred examples] actual combat of SM2+SM4 domestic encryption algorithm of Medical Insurance Bureau

WeChat official account: brother K crawler, QQ exchange group: 808574309, keep sharing crawler advance, JS/ Android reverse technology dry cargo!

statement

All contents in this article are for learning and communication only. The packet capturing content, sensitive website and data interface have been desensitized. It is strictly prohibited to use them for commercial and illegal purposes, otherwise all the consequences have nothing to do with the author. If there is infringement, please contact me and delete them immediately!

Reverse target

  • Objective: public inquiry of Medical Security Bureau
  • Home page: aHR0cHM6Ly9mdXd1Lm5oc2EuZ292LmNuL25hdGlvbmFsSGFsbFN0LyMvc2VhcmNoL21lZGljYWw=
  • Interface: aHR0cHM6Ly9mdXd1Lm5oc2EuZ292LmNuL2VidXMvZnV3dS9hcGkvbnRobC9hcGkvZml4ZWQvcXVlcnlGaXhlZEhvc3BpdGFs
  • Reverse parameters: encData and signData of Request Payload, x-tif-nonce and x-tif-signature of Request Headers

Reverse process

Packet capture analysis

Go to the public query page and click page turning to see a POST request. The parameters of the Request Payload are encrypted, mainly including appCode, encData and signData parameters. Similarly, the returned data also has these parameters. The encryption and decryption methods are the same. The encType and signType are SM4 and SM2 respectively, so it is probably a state secret algorithm, Previous articles on the state secret algorithm K include: Fundamentals of crawler reverse, understanding SM1-SM9 and ZUC national secret algorithm In addition, the request header also has x-tif-nonce and x-tif-signature parameters, as shown in the following figure:

Parameter inversion

Directly search encData or signData globally. The search results are only available in app.1634197175801.js. Obviously, there is a place to set the header. All parameters are here. Bury breakpoints. You can see that this is the place for encryption, as shown in the following figure:

The encryption functions here mainly pass in an e parameter. We can take a look at this e first. The meanings of the parameters are as follows:

  • addr: detailed address of medical institution; blank by default;
  • medinsLvCode: medical institution grade code; empty by default;
  • medinsName: medical institution name; empty by default;
  • medinsTypeCode: medical institution type code; empty by default;
  • pageNum: number of pages, 1 by default;
  • pageSize: number of data pieces per page, default 10;
  • regnCode: medical institution location code, 110000 by default (Beijing);
  • sprtEcFlag: its meaning is unknown for the time being. It is empty by default.

The level code, type code and location code are obtained through the request encryption interface. Their encryption and decryption methods are the same. They are shared in the final complete code, which will not be repeated here. Other parameters, such as appCode, are written in JS.

Let's take another look at the entire JS file. In the header, we can see the. call statement and the exports keyword. It is obviously written in the form of webpack.

Let's go back to the encryption place. From top to bottom, the whole function refers to many other modules. If you want to deduct the whole function, the time must be huge. If you want to directly take the whole JS and export the parameters, this violent method is OK, but the whole JS has more than 70000 lines, and the operation efficiency must be affected, so observe the function, It's better to remove the unused functions and leave the useful ones. Observe function d, the first line var t = n("6c27").sha256, and click to go to the createOutputMethod method. Here is a SHA256 algorithm, which can be copied down from this method, as shown in the following figure:

It should be noted here that the exported sha256 after this function actually calls the createMethod method method, so the method we copy can directly call createMethod, that is, var t = createMethod(), and these exports are not required.

In addition, some variables need to be defined. The structure of the whole copy is as follows:

Then continue to look down. There is another sentence o = Object(i.a)(). Similarly, click in and directly copy down. There is nothing to pay attention to here.

When you look down, you will come to e.data.signData = p(e). Click function p and copy the whole function. At this time, you will find no errors when debugging locally. In fact, he uses a try catch statement here. After catching the exception, there is no processing. You can add a console.log(e) to output the exception. In fact, he will print the exception in o.doSignature e. The prompt from two places is undefined. Similarly, we can click in to deduct the function, but later we will encounter that the function will constantly reference other functions. For convenience, we can write them into the webpack, and the same is true for e.from below.

Write the module in the form of webpack, call it in the self executing method, define the global variable to receive it, and then replace the original O and e with global variables. Another thing to pay attention to here is that the h passed in by o.doSignature is a fixed value, which needs to be defined, otherwise the subsequent decryption will fail. As shown in the figure below:

When deducting the webpack module here, you should also pay attention not to deduct all the modules in the original method. Some modules are not used at all and can be commented out directly. This process requires patience. If you deduct all the modules, it will be endless. You might as well directly use the whole JS file. All useful modules are as follows (there may be more but not less):

Then, in the original, encData: v("SM4", e) uses function v, and V uses functions such as A and g, which can be deducted. At the same time, it should be noted that e mentioned above is also used in function A, which also needs to be replaced with the global variables defined by ourselves, as shown in the following figure:

All the functions used for this encryption are deducted. At this time, we can write a method to encapsulate the encryption process. When using, we only need to pass in the following parameters:

{
    "addr": "", 
    "regnCode": "110000", 
    "medinsName": "", 
    "sprtEcFlag": "", 
    "medinsLvCode": "", 
    "medinsTypeCode": "", 
    "pageNum": 1, 
    "pageSize": 10
}

As shown in the following figure, getEncryptedData is the encryption method:

What about the decryption method? Obviously, the returned data is encData. If you search encData directly, there are only three results. It's easy to find function y. similarly, pay attention to changing e.from to our customized e_ Buffer.from. In addition, we can also encapsulate the generation method of header parameters into a function for easy calling.

Complete code

GitHub pays attention to brother K crawler and continues to share crawler related codes! Welcome, star! https://github.com/kgepachong/

The following only demonstrates part of the key code and cannot be run directly! Full code warehouse address: https://github.com/kgepachong...

JavaScript encryption key code architecture

var sm2, sm4, e_;
!function (e) {
    var n = {},
        i = {app: 0},
        r = {app: 0};

    function o(t) {}

    o.e = function (e) {}
    o.m = e
    o.c = n
    o.d = function (e, t, n) {}
    o.r = function (e) {}
    o.n = function (e) {}
    o.o = function (e, t) {}

    sm2 = o('4d09')
    e_ = o('b639')
    sm4 = o('e04e')

}({
    "4d09": function (e, t, n) {},
    'f33e': function (e, t, n) {},
    "4d2d": function (e, t, n) {},
    'b381': function (e, t, n) {},
    // N modules are omitted here
})

// N variables are omitted here

var createOutputMethod = function (e, t) {},
    createMethod = function (e) {},
    nodeWrap = function (method, is224) {},
    createHmacOutputMethod = function (e, t) {},
    createHmacMethod = function (e) {};

function Sha256(e, t) {}

function HmacSha256(e, t, n) {}

// N methods are omitted here

function i() {}

function p(t) {}

function m(e) {}

var c = {
    paasId: undefined,
    appCode: "T98HPCGN5ZVVQBS8LZQNOAEXVI9GYHKQ",
    version: "1.0.0",
    appSecret: "NMVFVILMKT13GEMD3BKPKCTBOQBPZR2P",
    publicKey: "BEKaw3Qtc31LG/hTPHFPlriKuAn/nzTWl8LiRxLw4iQiSUIyuglptFxNkdCiNXcXvkqTH79Rh/A2sEFU6hjeK3k=",
    privateKey: "AJxKNdmspMaPGj+onJNoQ0cgWk2E3CYFWKBJhpcJrAtC",
    publicKeyType: "base64",
    privateKeyType: "base64"
    },
    l = c.appCode,
    u = c.appSecret,
    f = c.publicKey,
    h = c.privateKey,
    t = createMethod(),
    // t = n("6c27").sha256,
    r = Math.ceil((new Date).getTime() / 1e3),
    o = i(),
    a = r + o + r;

function getEncryptedData(data) {
    var e = {"data": data}
    return e.data = {
            data: e.data || {}
        },
        e.data.appCode = c.appCode,
        e.data.version = c.version,
        e.data.encType = "SM4",
        e.data.signType = "SM2",
        e.data.timestamp = r,
        e.data.signData = p(e),
        e.data.data = {
            encData: v("SM4", e)
        },
        // e.data = JSON.stringify({
        //     data: e.data
        // }),
        e
}

function getDecryptedData(t) {
    if (!t)
        return null;
    var n = e_.Buffer.from(t.data.data.encData, "hex")
      , i = function(t, n) {
        var i = sm4.decrypt(n, t)
          , r = i[i.length - 1];
        return i = i.slice(0, i.length - r),
        e_.Buffer.from(i).toString("utf-8")
    }(g(l, u), n);
    return JSON.parse(i)
}

function getHeaders(){
    var headers = {}
    return headers["x-tif-paasid"] = c.paasId,
        headers["x-tif-signature"] = t(a),
        headers["x-tif-timestamp"] = r.toString(),
        headers["x-tif-nonce"] = o,
        headers["Accept"] = "application/json",
        headers["contentType"] = "application/x-www-form-urlencoded",
        headers
}

Python get data key code

# ==================================
# --*-- coding: utf-8 --*--
# @Time    : 2021-11-03
# @Author: WeChat official account: K brother crawler
# @FileName: nhsa.py
# @Software: PyCharm
# ==================================


import execjs
import requests


regn_code_url = "Desensitization, complete code attention GitHub: https://github.com/kgepachong/crawler"
lv_and_type_url = "Desensitization, complete code attention GitHub: https://github.com/kgepachong/crawler"
result_url = "Desensitization, complete code attention GitHub: https://github.com/kgepachong/crawler"
UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36"

with open('nhsa.js', 'r', encoding='utf-8') as f:
    nhsa_js = execjs.compile(f.read())


def get_headers():
    """obtain header Parameters, changed each time a request is made"""
    headers = nhsa_js.call("getHeaders")
    headers["User-Agent"] = UA
    headers["Content-Type"] = "application/json"
    headers["Host"] = "Desensitization, complete code attention GitHub: https://github.com/kgepachong/crawler"
    headers["Origin"] = "Desensitization, complete code attention GitHub: https://github.com/kgepachong/crawler"
    headers["Referer"] = "Desensitization, complete code attention GitHub: https://github.com/kgepachong/crawler"
    # print(headers)
    return headers


def get_regn_code():
    """Get the city code, and the returned result is unencrypted"""
    payload = {"data": {"transferFlag": ""}}
    response = requests.post(url=regn_code_url, json=payload, headers=get_headers())
    print(response.text)


def get_medins_lv_or_type_code(key):
    """Get medical institution level (LV) or type (TYPE) code"""
    if key == "LV":
        payload = {"type": "MEDINSLV"}
    elif key == "TYPE":
        payload = {"type": "MEDINS_TYPE"}
    else:
        print("Incorrect input!")
        return
    encrypted_payload = nhsa_js.call("getEncryptedData", payload)
    encrypted_data = requests.post(url=lv_and_type_url, json=encrypted_payload, headers=get_headers()).json()
    decrypted_data = nhsa_js.call("getDecryptedData", encrypted_data)
    print(decrypted_data)


def get_result():
    addr = input("Please enter the detailed address of the medical institution(Default none): ") or ""
    medins_lv_code = input("Please enter the medical institution grade code(Default none): ") or ""
    medins_name = input("Please enter the name of the medical institution(Default none): ") or ""
    medins_type_code = input("Please enter the medical institution type code(Default none): ") or ""
    regn_code = input("Please enter the medical institution location code(Default Beijing): ") or "110000"
    page_num = input("Please enter the number of pages to crawl(Default 1): ") or 1

    for page in range(1, int(page_num)+1):
        payload = {
            "addr": addr,
            "medinsLvCode": medins_lv_code,
            "medinsName": medins_name,
            "medinsTypeCode": medins_type_code,
            "pageNum": page,
            "pageSize": 10,
            "regnCode": regn_code,
            "sprtEcFlag": ""
        }
        page += 1
        encrypted_payload = nhsa_js.call("getEncryptedData", payload)
        encrypted_data = requests.post(url=result_url, json=encrypted_payload, headers=get_headers()).json()
        decrypted_data = nhsa_js.call("getDecryptedData", encrypted_data)
        print(decrypted_data)


def main():
    # Get city code
    # get_regn_code()
    # Get medical institution grade code
    # get_medins_lv_or_type_code("LV")
    # Get medical institution type code
    # get_medins_lv_or_type_code("TYPE")
    # Get search results
    get_result()


if __name__ == "__main__":
    main()

Keywords: Python crawler Data Mining

Added by Chicken Little on Fri, 12 Nov 2021 00:56:56 +0200