Crack and Research on verification code of polar test slider: AST reduction confusion JS

Cracking and Research on the verification code of polar test slider (I): AST reduction confusion JS

statement

Original articles, please do not reprint!

The content of this paper is limited to security research and does not disclose the specific source code. Everyone is responsible for maintaining network security.

Hyperlinks to articles related to this article:

  1. Cracking and Research on the verification code of polar test slider (I): AST reduction confusion JS
  2. Crack and Research on verification code of polar test slider (II): restoration of notch image
  3. Crack and Research on verification code of extreme test slider (3): slider gap identification
  4. Decoding and research of polar slider verification code (4): slider trajectory construction
  5. Decoding and Research on the verification code of polar test slider (5): request analysis and encryption parameter cracking

1, Environmental installation

1. node installation

1.1. node Download

Click here Official download link of node Download the installation package and install

1.2. Configure environment variables

1.3. node installation detection

node --version  # v14.16.0

1.4. pycharm configuring node environment

2. babel library installation

2.1. babel library installation command

npm install @babel/core --save-dev

2.2. babel library installation detection

node  # Enter the node environment
require("@babel/parser")  # Introducing babel Library

2, AST restore confusion JS

Warm tip: polar test JS all adopt the same confusion method. Fullpage used in later articles 9.0.7. js ,click.3.0.2.js, etc. are restored in this way
If you don't understand AST syntax tree, you can ask Du Niang
This article is based on slide 7.8.4. JS file as an example, Online AST syntax tree conversion tool

aHR0cHM6Ly9zdGF0aWMuZ2VldGVzdC5jb20vc3RhdGljL2pzL3NsaWRlLjcuOC40Lmpz

1. Module import

Import and restore the function blocks required by JS

const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
const fs = require("fs");

2. Copy and restore the js source code needed

The blogger is too lazy. Let's copy it by ourselves. (the source code is too long, reduce the length...)
This part of the code is slide 7.8.4. Functions in JS files as like as two peas are copied, and will be used in restore.

AaWgt.Bq_ = function () {}();
AaWgt.CyZ = function () {}();
AaWgt.Dvn = function () {};
AaWgt.EeS = function () {};
function AaWgt() {}

3. Detailed explanation of AST function

3.1. replace_unicode

There are many binary and Unicode codes for characters in js code. This function converts other character codes in the code into utf-8 codes

// Delete the extra attribute in the node (binary, Unicode and other encoding - > UTF-8)
function replace_unicode(path) {
    let node = path.node;
    if (node.extra === undefined)
        return;
    delete node.extra;
}

Before restore

After restore

3.2. replace_unicode, replace_name_array, replace_Dvn

A lot of code like var avml = axzpo Expression for DVN, axzpo DVN is actually a large array. The following function replaces it with specific characters

// Define a global variable to store the name of the variable to be replaced
let name_array = [];

function get_name_array(path) {
    let {kind, declarations} = path.node
    if (kind !== 'var'
        || declarations.length !== 3
        || declarations[0].init === null
        || declarations[0].init.property === undefined)
        return;
    if (declarations[0].init.property.name !== "Dvn")
        return;
    // Get the variable name of the node to be replaced
    let name1 = declarations[0].id.name
    // Get the name of the variable to be output
    let name2 = declarations[2].id.name
    // Store variable names in an array
    name_array.push(name1, name2)

    // Delete next node
    path.getNextSibling().remove()
    // Delete next node
    path.getNextSibling().remove()
    // Delete path node
    path.remove()
}

function replace_name_array(path) {
    let {callee, arguments} = path.node
    if (callee === undefined || callee.name === undefined)
        return;
    // Not in name_ The nodes in the array are not replaced
    if (name_array.indexOf(callee.name) === -1)
        return;
    // Call aawgt Get the result with DVN function
    let value = AaWgt.Dvn(arguments[0].value);
    // Create nodes and replace results
    let string_node = t.stringLiteral(value)
    path.replaceWith(string_node)
}

function replace_Dvn(path) {
    let {arguments, callee} = path.node
    // Parse arguments parameter
    if (arguments.length !== 1) return;
    if (arguments[0].type !== 'NumericLiteral') return;

    // Parsing callee
    if (callee.type !== 'MemberExpression') return;
    let {object, property} = callee;
    if (object.type !== 'Identifier' || property.type !== 'Identifier') return;

    if (property.name === 'Dvn') {
        // Calculated value
        let value = AaWgt.Dvn(arguments[0].value);
        // Create node and replace
        let string_node = t.stringLiteral(value)
        path.replaceWith(string_node)
    }
}

Before restore

After restore

3.3. replace_ForStatement

There are a lot of switch case structures in js code, which is not conducive to code logic analysis, and the control flow needs to be flattened

// Control flow flattening
function replace_ForStatement(path) {
    var node = path.node;

    // Get the previous node, that is, VariableDeclaration
    var PrevSibling = path.getPrevSibling();

    // Judge the attributes of the previous node to prevent error reporting
    if (PrevSibling.container === undefined
        || PrevSibling.container[0].declarations === undefined
        || PrevSibling.container[0].declarations[0].init === null
        || PrevSibling.container[0].declarations[0].init.object === undefined
        || PrevSibling.container[0].declarations[0].init.object.object === undefined)
        return;
    if (PrevSibling.container[0].declarations[0].init.object.object.callee.property.name !== 'EeS')
        return;

    // SwitchStatement node
    var body = node.body.body;
    // Judge the body[0] attribute and body[0] of the current node Whether discriminator exists
    if (!t.isSwitchStatement(body[0]))
        return;
    if (!t.isIdentifier(body[0].discriminant))
        return;

    // Gets the initial value of the control flow
    var argNode = PrevSibling.container[0].declarations[0].init;
    var init_arg_f = argNode.object.property.value;
    var init_arg_s = argNode.property.value;
    var init_arg = AaWgt.EeS()[init_arg_f][init_arg_s];

    // Extract the value of the if judgment parameter in the for node as the judgment parameter
    var break_arg_f = node.test.right.object.property.value;
    var break_arg_s = node.test.right.property.value;
    var break_arg = AaWgt.EeS()[break_arg_f][break_arg_s];

    // Extract all case s under switch
    var case_list = body[0].cases;
    var resultBody = [];

    // Traverse all case s
    for (var i = 0; i < case_list.length; i++) {
        for (; init_arg != break_arg;) {

            // Extract and calculate the value of conditional judgment after case
            var case_arg_f = case_list[i].test.object.property.value;
            var case_arg_s = case_list[i].test.property.value;
            var case_init = AaWgt.EeS()[case_arg_f][case_arg_s];

            if (init_arg == case_init) {
                //All nodes under the current case
                var targetBody = case_list[i].consequent;

                // Delete the break node and some useless code of the previous node of the break node
                if (t.isBreakStatement(targetBody[targetBody.length - 1])
                    && t.isExpressionStatement(targetBody[targetBody.length - 2])
                    && targetBody[targetBody.length - 2].expression.right.object.object.callee.object.name == "AaWgt") {

                    // Extract the previous node ajgjj of the break node Two index values after emf()
                    var change_arg_f = targetBody[targetBody.length - 2].expression.right.object.property.value;
                    var change_arg_s = targetBody[targetBody.length - 2].expression.right.property.value;

                    // Modify the initial value of control flow
                    init_arg = AaWgt.EeS()[change_arg_f][change_arg_s];

                    targetBody.pop(); // Delete break
                    targetBody.pop(); // Delete the previous node of the break node
                }
                //Delete break
                else if (t.isBreakStatement(targetBody[targetBody.length - 1])) {
                    targetBody.pop();
                }
                resultBody = resultBody.concat(targetBody);
                break;
            } else {
                break;
            }
        }
    }
    //Replace the for node, replace one node with multiple nodes, and use replaceWithMultiple
    path.replaceWithMultiple(resultBody);

    //Delete previous node
    PrevSibling.remove();
}

Before restore

After restore

3.4. delete_func

The restore process is over. It's time to delete some irrelevant functions.

// Delete irrelevant functions
function delete_func(path) {
    let {expression} = path.node
    if (expression === undefined
        || expression.left === undefined
        || expression.left.property === undefined)
        return;
    if (expression.left.property.name === 'Bq_'
        || expression.left.property.name === 'Dvn'
        || expression.left.property.name === 'CyZ'
        || expression.left.property.name === 'EeS'
    ) {
        path.remove()
    }
}

4. AST reduction process

4.1. AST restore overall process

// File location to decode
let encode_file = "slide.7.8.4.js"
// Decoded file location
let decode_file = "ast_slide.7.8.4.js_init.js"

// Read the js file to be decoded. Note that the file is encoded in utf-8 format
let jscode = fs.readFileSync(encode_file, {encoding: "utf-8"});

// Modify js code into AST syntax tree
let ast = parser.parse(jscode);
// AST structure modification logic
const visitor = {
    StringLiteral: {
        enter: [replace_unicode]
    },
    VariableDeclaration: {
        enter: [get_name_array]
    },
    CallExpression: {
        enter: [replace_name_array, replace_Dvn]
    },
    ForStatement: {
        enter: [replace_ForStatement]
    },
    ExpressionStatement: {
        enter: [delete_func]
    },
}

// Traverse the syntax tree node and call the modification function
traverse(ast, visitor);

// Convert ast into js code, {jsescoption: {"minimal": true}} Unicode - > Chinese
let {code} = generator(ast, opts = {jsescOption: {"minimal": true}});
// Save js code to a file
fs.writeFile(decode_file, code, (err) => {
});

4.2. Polar test different js or different version restore methods

js version used in this series of articles:

fullpage.9.0.7.js: https://static.geetest.com/static/js/fullpage.9.0.7.js
slide.7.8.4.js: https://static.geetest.com/static/js/slide.7.8.6.js
click.3.0.2.js((to be used in the later selected articles): https://static.geetest.com/static/js/click.3.0.2.js

Little friends can find that the pole test fullpage 9.0. x.js,slide.7.8.x.js,click.3.0.x.js all use the same confusion. Therefore, the AST restore method in this article can be used to restore the latest version of JS. The following is a case that will apply to slide 7.8.4. JS to replace the restored AST code with the applicable fullpage 9.0.7. JS

Step 1: add fullpage 9.0.7. JS part of the source code is replaced in the AST file.

Step 2: use the replacement function of the compiler to replace all the function names in the AST code.

Step 3: finally, remember to change the file to be decoded into fullpage 9.0.7. js

// File location to decode
let encode_file = "fullpage.9.0.7.js"
// Decoded file location
let decode_file = "ast_fullpage.9.0.7.js_init.js"

3, Conclusion

Links: Crack and research of polar slider verification code (II): restoration of notch image

This issue is over. If it helps you, remember to collect and pay attention to it. Later articles will be updated continuously~~~

Keywords: Front-end Vue.js html Mini Program

Added by backslash on Mon, 07 Mar 2022 03:07:56 +0200