[JS reverse hundreds of cases] cross domain error analysis of UA and ob anti aliasing and packet capturing replacement CORS in W store

Time:2022-6-18

Follow wechat official account: brother K crawler, and continue to share advanced crawler, js/ Android reverse and other technical dry goods!

statement

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

Reverse target

  • Objective: the UA parameters of the w store login interface are encrypted, and the JS code has been obfuscated
  • Home page:aHR0cHM6Ly9kLndlaWRpYW4uY29tLw==
  • Interface:aHR0cHM6Ly9zc28xLndlaWRpYW4uY29tL3VzZXIvbG9naW4=
  • Reverse parameter: form data:ua: H4sIAAAAAAAAA91ViZUbMQhtiVOIcnRRxRafr%2FGuN5ukgoyfLUZC...

Introduction to ob confusion

Obfuscator is the full name of obfuscator. Obfuscator actually means obfuscator. Official website:https://obfuscator.io/The author is a Russian JavaScript development engineer named timofey kachalov, who released the first version as early as 2016.

A normal code is as follows:

function hi() {
  console.log("Hello World!");
}
hi();

Results after ob confusion:

function _0x3f26() {
    var _0x2dad75 = ['5881925kTCKCP', 'Hello\x20World!', '600mDvfGa', '699564jYNxbu', '1083271cEvuvT', 'log', '18sKjcFY', '214857eMgFSU', '77856FUKcuE', '736425OzpdFI', '737172JqcGMg'];
    _0x3f26 = function () {
        return _0x2dad75;
    };
    return _0x3f26();
}

(function (_0x307c88, _0x4f8223) {
    var _0x32807d = _0x1fe9, _0x330c58 = _0x307c88();
    while (!![]) {
        try {
            var _0x5d6354 = parseInt(_0x32807d(0x6f)) / 0x1 + parseInt(_0x32807d(0x6e)) / 0x2 + parseInt(_0x32807d(0x70)) / 0x3 + -parseInt(_0x32807d(0x69)) / 0x4 + parseInt(_0x32807d(0x71)) / 0x5 + parseInt(_0x32807d(0x6c)) / 0x6 * (parseInt(_0x32807d(0x6a)) / 0x7) + -parseInt(_0x32807d(0x73)) / 0x8 * (parseInt(_0x32807d(0x6d)) / 0x9);
            if (_0x5d6354 === _0x4f8223) break; else _0x330c58['push'](_0x330c58['shift']());
        } catch (_0x3f18e4) {
            _0x330c58['push'](_0x330c58['shift']());
        }
    }
}(_0x3f26, 0xaa023));

function _0x1fe9(_0xa907e7, _0x410a46) {
    var _0x3f261f = _0x3f26();
    return _0x1fe9 = function (_0x1fe950, _0x5a08da) {
        _0x1fe950 = _0x1fe950 - 0x69;
        var _0x82a06 = _0x3f261f[_0x1fe950];
        return _0x82a06;
    }, _0x1fe9(_0xa907e7, _0x410a46);
}

function hi() {
    var _0x12a222 = _0x1fe9;
    console[_0x12a222(0x6b)](_0x12a222(0x72));
}

hi();

OB confusion has the following characteristics:

1. Generally, it consists of a large array or a function containing a large array, a self executing function, a decryption function and an encrypted function;

2. Function and variable names are usually_0xperhaps0xBeginning with 1~6 digits or letter combination;

3. The self executing function performs shift operations with obvious push and shift keywords;

For example, in the above example,_0x3f26()Method defines a large array. The self executing function contains the push and shift keywords, which are mainly used to shift the large array,_0x1fe9()Is the decryption function,hi()Is the encrypted function.

Packet capturing analysis

Click log in to capture packets. You can see that there are UA parameters that are encrypted and will change each time you log in, as shown in the following figure:

01.png

If you directly search UA, there are too many results to filter. It is easier to find the encrypted location through the XHR breakpoint, as shown in the following figure. The last submitted R parameter contains the UA value. Looking up, you can see that the value of I has been URL encoded. Looking up, you can see that the value of I passedwindow.getUa()Get, this is actually uad JS inside an anonymous function.

02.png

Follow up to uad JS, you can see the callwindow[_0x4651('0x710')]This method, the last returned_0x261229It is the encrypted UA value, which is similar to_0x4651('0x710')_0x4651('0x440')If you select the value of, you can see that it is actually some strings. Through direct search, you can find that these strings are in a large array in the header, as shown in the following figure:

03.png

04.png

Confusing restore and replace

A large array and a self executing function for shift operation with obvious push and shift keywords are undoubtedly obfuscated. So how should we deal with it to make it look more pleasing to the eye?

You can manually check the values in the browser and replace them locally. Of course, you don’t need to replace them all. Just follow the stack and replace them where they are used. Don’t be silly and replace them all manually one by one. This method is applicable to less complex code.

If you encounter a lot of code, it is recommended to use the anti obfuscation tool to handle it. The domestic one is recommended hereApe anthropology ob confusion solving toolAnd foreignde4js, the ape anthropological tools have a high degree of restoration, but some ob confusions will report errors when they are restored. The OB confusions measured in this case cannot operate normally after being processed by the ape anthropological tools, and may need to be handled in advance. De4js is an open source tool developed by a Vietnamese manufacturer. You can deploy it to your own machine. It supports a variety of obfuscation restoration, including Eval, ob, jsfuck, AA, JJ, etc, You can paste the code directly to automatically identify the confusion mode. De4js is recommended for this case, as shown in the following figure:

05.png

We copy the restored results to the local file and use Fiddler’s autoresponder function to replace the response, as shown in the following figure:

06.png

If you start capturing packets and refresh the page at this time, you will find that the request status shows CORS error, JS replacement is unsuccessful, and you can also see an error in the consoleNo 'Access-Control-Allow-Origin' header is present on the requested resource.As shown in the following figure:

14.png

CORS cross domain error

CORS (cross origin resource sharing) is a W3C standard. The standard uses additional HTTP headers to tell browsers that web applications running on one source can access resources from another source. Any difference between the protocol, domain name and port of a request URL and the current page address is cross domain. The common cross domain problem is that the browser prompts that the API of domain B cannot be accessed under domain A. for further understanding of CORS, please refer toW3C CORS Enabled

The brief process is as follows:

1. The consumer sends an origin header to the provider: origin:http://www.site.com
2. Provider sends aAccess-Control-Allow-OriginRespond to the header to the consumer if the value is*Or the site corresponding to origin, it means that resources are allowed to be shared with consumers. If the value is null or does not exist, it means that resources are not allowed to be shared with consumers;
3. ExceptAccess-Control-Allow-OriginIn addition, some sites may also detectAccess-Control-Allow-Credentials, true means allowed;
4. The browser judges whether to allow consumers to access the provider source across domains according to the provider’s response message;

According to the previous error messages on the console, we can know that the response header is missingAccess-Control-Allow-OriginAs a result, there are two methods in fiddler to add this parameter to the response header, as described below:

The first is to use the filter function of fiddler to set it in response headers, and fill in access control allow origin and allowed domain names respectively, as shown in the following figure:

15.png

The second is to modify customrules JS file, select rules – > Customize rules in thestatic function OnBeforeResponse(oSession: Session)Add the following code under module:

If (osession.uricontains ("URL to process"){
    oSession. Oresponse["access control allow origin"] = "allowed domain names";
}

16.png

Choose one of the two methods. After setting, it can be replaced successfully. After refreshing and debugging again, you can see the restored JS, as shown in the following figure:

07.png

backward analysis

Obviouslywindow.getUaIs the main encryption function, so let’s first analyze this function:

window.getUa = function() {
    var _0x7dfc34 = new Date().getTime();
    if (_0x4a9622) {
        _0x2644f4();
    }
    _0x55b608();
    var _0x261229 = _0x1722c3(_0x2e98dd) + '|' + _0x1722c3(_0x420004) + '|' + _0x7dfc34.toString(0x10);
    _0x261229 = btoa(_0x570bef.gzip(_0x261229, {
        'to': 'string'
    }));
    return _0x261229;
};

_0x7dfc34It is a timestamp, followed by an if judgment. We can mouse into the judgment to find the judgment_0x4a9622False, then_0x2644f4()Will not be executed, and then executed_0x55b608()method,_0x261229The value of the_0x1722c3()Method, passed in sequence_0x2e98ddand_0x420004It is obvious that these two values are key. Search them respectively and you can find:

_0x2e98ddSome header, browser information, screen information, system font information, etc. are defined, which can be directly transmitted as fixed values, as shown in the following figure:

09.png

10.png

_0x420004The useful result of the search is that only one empty object is defined. You can see that the data actually contains some keyboard and mouse click and move data in the console output. In fact, it is found through the test,_0x420004The value of is not strongly verified. It can be generated by random number simulation, or a fixed value can be copied directly.

11.png

_0x2e98ddand_0x420004These two parameters are not subject to strong verification. They can be passed in as fixed values. These two values are in JSON format and can be used directly on the consolecopyStatement to copy its value, or use theJSON.stringify()Statement output results are copied manually.

12.png

Local joint commissioning

There are many functions that call each other. You can directly copy the entire JS. We notice that the entire function is a self executing function. When calling locally, we can define a global variable, and thenwindow.getUaIn the function, the_0x261229Assign the value of to the global variable, which is equivalent to exporting the value. Finally, take this global variable. Another way is to prevent it from self executing, rewrite it into a normal function, and then callwindow.getUaMethod to get the UA value.

First we put_0x2e98ddand_0x420004The values of the two values are defined locally. Here is a small detail. You need to comment out the definitions of the two values in the original JS code to prevent conflicts.

When debugging locally, you will be promptedwindowlocationdocumentUndefined, define it as an empty object, and then promptattachEventUndefined, search, yes_0x13cd5aA prototype object of, exceptattachEventBesides, there is anotheraddEventListeneraddEventListener()Method is used to add an event handle to the specified element, which is used in IEattachEvent()Method. We bury a breakpoint in Google Chrome for debugging, and refresh the page to directly enteraddEventListener()Method, where the event iskeydown, that is, when the keyboard is pressed, the following_0x5cec90Method to output this returned later. In fact, no useful value is generated, so_0x13cd5a.prototype.bindMethod we can comment it out directly, and the actual test has no impact.

08.png

After local debugging, you will be promptedbtoaUndefined,btoaandatobAre two functions of the window object, wherebtoaBinary to ASCII is used to represent binary data with ASCII code, that is, the encoding process of Base64, andatobIs ASCII to binary, which is used to parse ASCII codes into binary data, that is, the decoding process of Base64.

In nodejs, aBuffer, which can be used to perform Base64 encoding and decoding. It will not be described in detail here. You can baidu by yourself,window.getUaOriginal in methodbtoaThe statement is as follows:

_0x261229 = btoa(_0x570bef.gzip(_0x261229, {'to': 'string'}));

In nodejs, we can write as follows:

_0x261229 = Buffer.from(_0x570bef.gzip(_0x261229, {'to': 'string'}), "latin1").toString('base64');

be careful:Buffer.from()Passed in alatin1Parameter because_0x570bef.gzip(_0x261229, {'to': 'string'})The result of is Latin1 (alias of iso-8859-1). If you do not pass or pass in other parameters, the final result may be the same asbtoaThe results are different!

Since then, the correct UA value can be obtained after the local joint commissioning is completed!

Full code

GitHub follows brother K’s crawlers and continues to share crawler related codes! Welcome star!https://github.com/kgepachong/

The following only demonstrates some key codes, which cannot be run directly!Full code warehouse address:https://github.com/kgepachong/crawler/

JavaScript encryption key code architecture

var window = {};
var location = {};
var document = {};
var _0x5a577d = function () {}();
var _0xe26ae = function () {}();
var _0x3204b9 = function () {}();
var _0x3c7e70 = function () {}();
var _0x4a649b = function () {}();
var _0x21524f = function () {}();
var _0x2b0d61 = function () {}();
var _0x53634a = function () {}();
var _0x570bef = function () {}();
var _0xd05c32 = function (_0x5c6c0c) {};
window.CHLOROFP_STATUS = 'start';

//N functions are omitted here

var _0x2e98dd = {
    //Object specific value omitted
    "basic": {},
    "header": {},
    "navigator": {},
    "screenData": {},
    "sysfonts": [],
    "geoAndISP": {},
    "browserType": {},
    "performanceTiming": {},
    "canvasFp": {},
    "visTime": [],
    "other": {}
}
var _0x420004 = {
    //Object specific value omitted
    "keypress": true,
    "scroll": true,
    "click": true,
    "mousemove": true,
    "mousemoveData": [],
    "keypressData": [],
    "mouseclickData": [],
    "wheelDeltaData": []
}

window.getUa = function () {
    var _0x7dfc34 = new Date().getTime();
    if (_0x4a9622) {
        _0x2644f4();
    }
    _0x55b608();
    var _0x261229 = _0x1722c3(_0x2e98dd) + '|' + _0x1722c3(_0x420004) + '|' + _0x7dfc34.toString(0x10);
    // _0x261229 = btoa(_0x570bef.gzip(_0x261229, {'to': 'string'}));
    _0x261229 = Buffer.from(_0x570bef.gzip(_0x261229, {'to': 'string'}), "latin1").toString('base64');
    return _0x261229;
};

//Test output
// console.log(window.getUa())

Python login key code

# ==================================
# --*-- coding: utf-8 --*--
# @Time    : 2021-11-15
#@author: wechat official account: brother K crawler
# @FileName: weidian_login.py
# @Software: PyCharm
# ==================================


import execjs
import requests
from urllib import parse


index_ Url = "desensitization processing, full code attention GitHub: https://github.com/kgepachong/crawler "
login_ Url = "desensitization processing, full code attention GitHub: https://github.com/kgepachong/crawler "
UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36"
session = requests.session()


def get_encrypted_ua():
    with open('get_encrypted_ua.js', 'r', encoding='utf-8') as f:
        uad_js = f.read()
    ua = execjs.compile(uad_js).call('window.getUa')
    ua = parse.quote(ua)
    return ua


def get_wd_token():
    headers = {"User-Agent": UserAgent}
    response = session.get(url=index_url, headers=headers)
    wd_token = response.cookies.get_dict()["wdtoken"]
    return wd_token


def login(phone, password, ua, wd_token):
    headers = {
        "user-agent": UserAgent,
        "Origin": "desensitization processing. The complete code focuses on GitHub: https://github.com/kgepachong/crawler ",
        "Referer": "desensitization processing, full code attention GitHub: https://github.com/kgepachong/crawler ",
    }
    data = {
        "phone": phone,
        "countryCode": "86",
        "password": password,
        "version": "1",
        "subaccountId": "",
        "clientInfo": '{"clientType": 1}',
        "captcha_session": "",
        "captcha_answer": "",
        "vcode": "",
        "mediaVcode": "",
        "ua": ua,
        "scene": "PCLogin",
        "wdtoken": wd_token
    }
    response = session.post(url=login_url, headers=headers, data=data)
    print(response.json())


def main():
    Phone = input ("please enter the login phone number:")
    Password = input ("please enter the login password:")
    ua = get_encrypted_ua()
    wd_token = get_wd_token()
    login(phone, password, ua, wd_token)


if __name__ == '__main__':
    main()