[JS reverse hundred examples] Introduction to anti confusion, a Peng education JS confusion restoration

Keywords: Python

Focus on official account dry cargo WeChat public: K brother crawler, keep sharing crawler advance, JS/ Android reverse technology dry goods!


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: a Peng education login interface is encrypted with simple JS confusion
  • Home page: aHR0cHM6Ly9sZWFybi5vcGVuLmNvbS5jbi8=
  • Interface: aHR0cHM6Ly9sZWFybi5vcGVuLmNvbS5jbi9BY2NvdW50L1VuaXRMb2dpbg==
  • Reverse parameter: Form Data: black_box: eyJ2IjoiR01KM0VWWkVxMG0ydVh4WUd...

Reverse process

The goal of this reverse is also a login interface. The encrypted JS uses simple confusion, which can be used as an entry-level tutorial for confusion restoration. Come to the login page and enter the account and password to log in. In the login POST request, Form Data has an encryption parameter black_box, that is, the goal of this reverse, the packet capture is as follows:

Search black directly_ Box, you can easily find the encrypted place in login.js, as shown in the following figure:

Take a look_ The method fmOpt.getinfo() calls the OO0O0() method in fm.js. It is both 0 and O, which is mostly confused, as shown in the following figure:

Click in to see that the whole fm.js is confusing code. We select a code similar to OQoOo[251], and you can see that it is actually a string object, or you can directly output its actual value in the Console. The oooooo0 [oqooo [448]] (JSON [oqooo [35]] (o0ooo [oqooo [460]]) returned by this OO0O0 method is black_box, as shown in the following figure:

After careful observation, you can find that OQoOo should be something similar to an array. You can get its real value in turn by passing in the element subscript. If you search for a value arbitrarily, you can find an array at the end of the code. This array is actually OQoOo. You can pass in the subscript to verify it, as shown in the following figure:

In fact, we know the general confusion principle here. We can take down the JS and write a small script locally to replace these values:

# ==================================
# --*-- coding: utf-8 --*--
# @Time    : 2021-11-09
# @Author: WeChat official account: K brother crawler
# @FileName: replace_js.py
# @Software: PyCharm
# @describe: obfuscate restore script
# ==================================

# Values to be replaced (too many, only a few are listed)
# Subject to the actual list, and FM_ The list in old.js is consistent
item = ['referrer', 'absolute', 'replace',...]

# JS after confusion
with open("fm_old.js", "r", encoding="utf-8") as f:
    js_lines = f.readlines()

js = ""
for j in js_lines:
    js += j

for i in item:
    # Qo00o needs according to your fm_old.js is replaced by a specific string
    str_old = "Qo00o[{}]".format(item.index(i))
    js = js.replace(str_old, '"' + i + '"')

# Restored JS
with open("fm_new.js", "w", encoding="utf-8") as f:

After replacing with this script, you may find that JS will report errors because of some line breaks, slash parsing errors, and the repeated use of double quotation marks. You can modify them manually.

One thing to note here is that fm.js is followed by a suffix, such as t=454594, t=454570, etc. the JS contents obtained by different suffixes are also different. The order of various function variable names is different from that list element. In fact, the method called is the same, so it has little impact. Just pay attention to the list contents during replacement The string to be replaced is consistent with the one in the JS file you downloaded.

After restoring JS, we can replace the restored JS with the confused JS of the website itself. There are many replacement methods, such as replacing the response with packet capture tools such as fiddler, using plug-ins such as ReRes, using the Overrides function of the browser developer tool (functions only after Chrome 64), etc, Here we use Fiddler's Autoresponder function to replace.

Actually, the suffix of fm.js will not change in a short time, so we can directly copy its full address to replace it. To be more rigorous, we can match the t value with a regular expression, select AutoResponder in Fiddler, click Add Rule to add the replacement rule, The regular expression is written as follows: regex:https:\/\/static\.tongdun\.net\/v3\/fm\.js\?t=\d +. Note that the regex prefix is essential. Select Enable rules, accept all connections, Unmatched requests passthrough from the top. Enable Latency is to set the delay effective time without checking it, as shown in the following figure:

After login, the next breakpoint is replaced. You can see that the JS is clear now. Then look at the last return statement of the function. oQOQ0["blackBox" contains three parameters, it, os, t and v, and converts it to string using JSON's stringify method, then calls the QQo0 method to encrypt it, as shown in the following figure:

Let's first look at the four parameters in oQOQ0["blackBox"], of which it, os and v have been defined at the beginning of this function. v is Q0oQQ["version"], which is a fixed value. Direct search can find that this value is in the first large list. os is a fixed value, it is the value subtracted by two timestamps, and O000o is the method of subtracting two values, oQOQo this timestamp can search var oQOQo, which is the timestamp generated at the beginning of loading. It takes about one minute for JS to click login to enter the encryption function, so here we can directly generate a five digit random number (the difference between one minute and milliseconds is about five digits).

Now there is only one t parameter left. Looking down, t is actually Q0oQQ["tokens"]. In the middle, an if else statement is passed, and the breakpoint can be buried for debugging. It is found that only else statement is executed, and the assignment to t is just this sentence, so the remaining code can be deleted during deduction.

The tokens have been tested for many times and found to be unchanged. Try to search the token keyword directly to find the place where it is assigned. Divide the id according to the | symbol. Take the first index value as tokens, and then look at the value of id. there is no obvious generation logic. Copy its value and search. It is found that it is returned through an interface and can be written to death directly, You can also request this interface first and get the returned value, as shown in the figure below:

Since then, all the parameters have been found. When you return to the original return position, there is still an encryption function, ooOoO["encode"] (). You can directly follow in and deduct this method. You can supplement what is missing in local debugging and complete the functions used.

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

function oQ0OQ(Q0o0, o0OQ) {
    return Q0o0 < o0OQ;

function O000O(Q0o0, o0OQ) {
    return Q0o0 >> o0OQ;

function Qo0oo(Q0o0, o0OQ) {
    return Q0o0 | o0OQ;

function OOO0Q(Q0o0, o0OQ) {
    return Q0o0 << o0OQ;

function OooQo(Q0o0, o0OQ) {
    return Q0o0 & o0OQ;

function Oo0OO(Q0o0, o0OQ) {
    return Q0o0 + o0OQ;

var oQoo0 = {};
oQoo0["_keyStr"] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
oQoo0["encode"] = function QQQ0(Q0o0) {
        var o0OQ = 62;
        while (o0OQ) {
            switch (o0OQ) {
                case 116 + 13 - 65: {}
                case 118 + 8 - 63: {}
                case 94 + 8 - 40: {}
                case 122 + 6 - 63: {}
oQoo0["_utf8_encode"] = function oOQ0(Q0o0) {}

function OOoO0() {
    var tokens = "e0ia+fB5zvGuTjFDgcKahQwg2UEH8b0k7EK/Ukt4KwzyCbpm11jjy8Au64MC6s7HvLRacUxd7ka4AdDidJmYAA==";
    var version = "+X+3JWoUVBc12xtmgMpwzjAone3cp6/4QuFj7oWKNk+C4tqy4un/e29cODlhRmDy";
    var Oo0O0 = {};
    Oo0O0["blackBox"] = {};
    Oo0O0["blackBox"]["v"] = version;
    Oo0O0["blackBox"]["os"] = "web";
    Oo0O0["blackBox"]["it"] = parseInt(Math.random() * 100000);
    Oo0O0["blackBox"]["t"] = tokens;
    return oQoo0["encode"](JSON.stringify(Oo0O0["blackBox"]));

// Test sample

Python login key code

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

import time
import execjs
import requests

login_url = "Desensitization, complete code attention GitHub: https://github.com/kgepachong/crawler"

def get_black_box():
    with open('get_black_box.js', 'r', encoding='utf-8') as f:
        exec_js = f.read()
    black_box = execjs.compile(exec_js).call('OOoO0')
    return black_box

def login(black_box, username, password):
    params = {"bust": str(int(time.time() * 1000))}
    data = {
        "loginName": username,
        "passWord": password,
        "validateNum": "",
        "black_box": black_box
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36"
    response = requests.post(url=login_url, params=params, data=data, headers=headers)

def main():
    username = input("Please enter login account: ")
    password = input("Please enter the login password: ")
    black_box = get_black_box()
    login(black_box, username, password)

if __name__ == '__main__':

Posted by texelate on Mon, 29 Nov 2021 23:33:57 -0800