JS reverse webpack decryption

Disclaimer: This article is only for study and research, and it is prohibited to use it for illegal purposes, otherwise you will be responsible for the consequences. If there is any infringement, please inform and delete it, thank you!

Foreword: My technology may not be very good, but I am very lazy, how to do it as simple as possible, so if there is a better decryption method, you can comment in the comment area~

Target website: This website is very poor, the verification field cannot be found, and the XHR breakpoint cannot be broken. It is very strange, but I dare to write this article, which means I have passed it.

Here is the website:

aHR0cHM6Ly90b29scy5taWt1LmFjL25vdmVsYWkv

Regarding JS reverse engineering, it is nothing more than:

        1. Find parameters

        2. The location where the positioning parameter is generated

        3. Analyze the encryption logic of the parameters

        4. Deduction code call

Regarding webpack, if you don’t understand it, you can read the following article:

https://app.yinxiang.com/fx/970ae39c-9964-4aae-aa96-7e81fee4ef8f

         Simply put, after being packaged by webpack, all resources become modules, which are called through the "loader (distributor)".

Let's start the reverse process:

Open the target website, click the "Start Generation" button of the target website more times, and wait for the generation to succeed. After comparing several requests, it can be found that the request parameters are the same, but the authsign in the request header is different every time, so judge the The parameter is the validation parameter (actually it is this).

The first step, the old rules first search globally,

It seems that there is no assignment, which is very strange,

The second step is to locate the parameter position. In short, first look at the location where the request was initiated (find it in the launcher) (in fact, there is another method: Js Hook, which will be discussed later)

 Jump here, hit a breakpoint here.

 Click "Start Build" again

 It can be found that there is a t here, and the headers inside do not know what it is (general headers are request headers, so this is very suspicious, remember this t first), in short, press F10 to skip the function and continue to execute.

e is an array, it seems to be traversing this array, don't care about it, I can't understand it anyway, then press F10 to skip.

After arriving here, you can find the request parameters here, but there is no "authsign" we want, then press F10 to skip until you see the parameters we need (Authsign).

It can be found that the t appears here again, and the headers inside are still those values. Press F10 to skip the function

Well, after skipping, you can find that there is an extra Authsign in the headers in this t here. So this Authsign is generated here.

We set a breakpoint here, cancel other breakpoints, and click "Start Generation" again,

After stopping here, we press F9 to execute step by step (F10 is to execute across functions),

Step by step execution here, print it, you can find that this is the place where the encryption parameters are generated, and now it has been located.

Let’s talk about another method: JS Hook

Need to install an extension  in the browser : oil monkey (Violentmonkey)

        Open the plug-in to create a new script: the function of this script is to enter the debugger state when the specified request header is set.

        More scripts: ( https://www.jb51.net/article/241736.htm )

// ==UserScript==
// @name        tools.miku
// @namespace   Violentmonkey Scripts
// @match       https://*/*
// @grant       none
// @version     1.0
// @author      -
// @description 2022/11/4 08:39:59
// ==/UserScript==
var code = function(){
var org = window.XMLHttpRequest.prototype.setRequestHeader;
window.XMLHttpRequest.prototype.setRequestHeader = function(key,value){
    if(key=='Authsign'){
        debugger;
    }
    return org.apply(this,arguments);
}
}
var script = document.createElement('script');
script.textContent = '(' + code + ')()';
(document.head||document.documentElement).appendChild(script);
script.parentNode.removeChild(script);

Then refresh the page and start the script. Click "Start Generation" again, and you will find that it will stop here, indicating that "Authsign is set to Header" at this time.

 Then we started to trace the stack. The purpose of the stack trace is also to find the location where the encrypted parameters are generated.

The call stack is the caller of the current code, and the caller of the caller . To put it bluntly, the program executes all the operations before here, and the "Authsign" value is set here, and the stack is traced from top to bottom. The top is the current position, and the bottom is the previous call position. If you don't know how to do it, you can only find a tutorial on stack chasing by yourself.

Finally, after chasing this h.request, it is the same as the one entered by the launcher. Press F10 to see step by step, where "Authsign" is added, follow it, and locate the encrypted parameter position.

Then, as I said just now, locate after the encryption parameters.

Through observation, it can be found that this is a webpack package. The packaging of webpack is very characteristic, and there is a distributor.

f is , and y is defined above,  y = nn(f), good.

Next, we start to buckle the code, first set a breakpoint here at y=nn(f), and refresh the page (the modules are loaded at the beginning, so we refresh the page instead of clicking "Start Generation")

After breaking it, jump into n. You can hover the mouse over n and jump into this method, or you can print n directly on the console, and then double-click to jump into this method.

 

 You can see that this is the so-called loader (distributor) of webpack

We directly copy the entire js file. Some tutorials may say that only copying the loader is enough. In fact, it is not the case. We can find that our y=nn(f) is nn, and n is the o function, which is equivalent to on (f), we search this js file, we can find that on is defined below, so we need to copy all of them instead of just copying the loader.

 Create a new js file and paste it here.

Define a global variable lorder. Change the last d() to lorder=o, the last d() is an entry function, we don't need it, so we can use lorder to do o outside. Then we changed the array form to object form , and built two methods to test it.

 Something went wrong. window is not defined. window is not defined, let's define a var window={} at the beginning; 

 Then run it again and output successfully, indicating that there is no problem with our loader.

Ok, let's copy the method we need, f=n(267), y=nn(f), the same, refresh the page to jump in, and then copy the method. We can see that n(267) is not a method.

 At this time, we jump into n, which is the loader, hit a breakpoint, refresh the page, and then print the function subscripted 267 on the console, e is the function array, that is, e[267].

After jumping in, I found that the entire js module inside is different from the others (this is in the form of an array), and there are a bunch of functions called in it. I don’t care about them at this time. Copying one by one is too tiring. Of course, if you like one One copy is also okay, but remember to change the object form to the corresponding subscript, that's it

I won’t demonstrate it here. We use the lazy method to spit it out with one click. First locate the beginning of the js where e[267] is located. All modules starting with this are webpack packaged module files.

We also copy the js module and put it in our loader (note that our loader js needs to be copied from the browser again, it must be unchanged, that is, without the Lorder).

 Then you need to use the ast reverse of the fisherman. This is the project address of the big guy.

Docs · master · fish / webpack_ast · GitCode

Lack of dependencies to supplement dependencies , I created a a.js file in this ast folder, and then copied the js we wrote (loader and array module) to a.js.

Then open cmd to enter the ast folder and execute the command:

node webpack_mixer.js -l a.js -o webpack_out.js

 If an error is reported, the lack of dependencies will be supplemented , and after the successful operation, there will be an extra webpack_out.js file, which is a module in the form of an object.

Then copy the js back to our own js, and the boss also thoughtfully helped us generate global variables for us to call (export_function = o;), here is another thumbs up for the boss.

 The n in the code is equivalent to the o function, and o is assigned to the global variable export_function, that is to say, n(267)=o(267)=export_function(267), there is method 267, and then look down ,

We also define n(267), and y,

Then there is the m function, which is defined above, and we copy it over. In short, it is to make up for what is missing.

After running, there is an exception again. When we see this exception, we know that there is a missing function, and we don’t know which function is missing, so we add the code console.log(c) to the loader, where c is the subscript, and It can be said to be the name of the object, so that you know which function he reported an error in, and which function is missing. 

 emmm, function 51

The old rules, enter the decorator and set a breakpoint 

Then jump into method No. 51 and copy the entire js module to the loader. Note that our loader js needs to be copied in the browser again. Like the one above, it is also cmd to enter the ast folder and execute the command :

 node webpack_mixer.js -l a.js -o webpack_out.js

 Then, just copy the modules in the output js file to our own js loader module.

 Remember to separate methods with commas!

 After putting it away, save and run, and report an error again. e is not defined, 

 Let's find out where e comes from. Looking up, you can find that e is here, which is the parameter of the method.

 

 Then we look at how the parameters of this method come from. Looking back, we can find that it is .call(this, n(262), n(51)), that is to say, e is n(262), o is n( 51).

Ok, then we also define an e=n(262), save it and run it again, and report an error again, r is undefined, the same, find where r is defined

 Found right at the beginning,

Let's also define r, r=n(56), save and run and report an error, o is undefined, o is the n(51) just now, let's define it, and then save and run and report an error. .

In e = decodeURIComponent(l); an error is reported here, URIError: URI malformed, this is very strange, see if something is missing. Well, we can see that there are indeed some parameter definitions above this m, we don't have it, so add it.

 Well, after adding it, it still saves and runs and reports an error, but it’s not the mistake just now, this time it’s d(...)(...)[m(...)] is not a function, see where d is Which definition, is there any redundant operation.

Then you can see that there is an operation after the definition of d, daextend(_.a), and _ = nn(m), m = n(568),

We also added these definitions, still save and run and report an error, document is not defined,

 This exception is different from window, and it doesn’t work if it’s defined above. In short, let’s try to define above, var document={}, you can see that f is printed, but there is still a problem, prompting that the attribute “words” is not defined , and words is an attribute of f,

 In short, print it and compare it with the browser, m("8", "fUV&") is domain, which is the domain name, that is to say, the output of document[domain] is undefined,

 Look at the browser's,

 Therefore, since our program cannot obtain the domain name of the browser document, we can just use the dead value directly. After changing it, save and run it, and report an error again. There is a problem with the encryption module.

 Let's print to see what encryption is, and we can see that it is the AES encryption method,

The parameters already exist, we refer to the encryption library of nodejs,

var CryptoJS = require("crypto-js");

Crypto-js is installed by itself, and then the following encryption is also changed, save and run 

 The hard work paid off, and finally the encryption was successful! Put it on apipost and try it. It's strange that there is no data. Is there a problem with the encrypted data?

 After unremitting efforts, it was finally found that

 There is a problem with the document[m("3", "jx9[")] of this f. I said before that our program cannot access the document of the website, so let's print it to see what value is needed.

It can be seen that it is also a domain name, so our document[m("3", "jx9[")] should also be replaced with 'tools.miku.ac', save and run,

Put the encrypted data in Apipost again, this time the request is longer (great joy),

You can see that the website returns the image in base64 format, and it is finally a success!

I paste the core decryption code, but I won’t paste the modules in the middle (too many),

var CryptoJS = require("crypto-js");
var document = {}
var export_function;
!function (e) {
    //这里是加载器代码
}({
    //这里是模块代码
});
module.exports = export_function;
e = export_function(262)
o = export_function(51)
r = export_function(56)
f = export_function(267)
y = export_function.n(f)
h = (export_function(37), export_function(91), export_function(92), export_function(25), export_function(123), export_function(49))
d = export_function.n(h)
m = export_function(568)
_ = export_function.n(m)
d.a.extend(_.a)
var n, l, h = ["jsjiami.com.v6", "jsjSiamtirN.coEmh.Fulv6YWfbWzOB==", "bALCr8KoHEA=", "w7BQDcKBPMO7", "wqw8W8OFT1XDnw==", "TcKFbsOIw7oTwqnDrQ==", "wqM3w7c=", "w6TCqsKDw6PCsMOaRA==", "wq9Qw7rDpcKRZg==", "w7Msw5zDtsORCsOJIw==", "OlPDoQ==", "IsOIWMOowpvCtHdD"];
n = h,
    l = 448,
    function (e, t, o, r) {
        if ((t >>= 8) < e) {
            for (; --e;)
                r = n.shift(),
                    t === e ? (t = r,
                        o = n.shift()) : o.replace(/[StrNEhFulYWfbWzOB=]/g, "") === t && n.push(r);
            n.push(n.shift())
        }
    }(++l, 114688);
var m = function t(n, c) {
    n = ~~"0x".concat(n);
    var l = h[n];
    if (void 0 === t.RVAulw) {
        !function () {
            var t = "undefined" != typeof window ? window : "object" === (void 0 === e ? "undefined" : Object(r.a)(e)) && "object" === (void 0 === o ? "undefined" : Object(r.a)(o)) ? o : this;
            t.atob || (t.atob = function (e) {
                for (var t, n, o = String(e).replace(/=+$/, ""), r = 0, c = 0, l = ""; n = o.charAt(c++); ~n && (t = r % 4 ? 64 * t + n : n,
                    r++ % 4) ? l += String.fromCharCode(255 & t >> (-2 * r & 6)) : 0)
                    n = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(n);
                return l
            }
            )
        }();
        t.hRdDHj = function (e, t) {
            for (var n, o = [], r = 0, c = "", l = "", h = 0, d = (e = atob(e)).length; h < d; h++)
                l += "%" + ("00" + e.charCodeAt(h).toString(16)).slice(-2);
            e = decodeURIComponent(l);
            for (var m = 0; m < 256; m++)
                o[m] = m;
            for (m = 0; m < 256; m++)
                r = (r + o[m] + t.charCodeAt(m % t.length)) % 256,
                    n = o[m],
                    o[m] = o[r],
                    o[r] = n;
            m = 0,
                r = 0;
            for (var _ = 0; _ < e.length; _++)
                r = (r + o[m = (m + 1) % 256]) % 256,
                    n = o[m],
                    o[m] = o[r],
                    o[r] = n,
                    c += String.fromCharCode(e.charCodeAt(_) ^ o[(o[m] + o[r]) % 256]);
            return c
        }
            ,
            t.gjhasZ = {},
            t.RVAulw = !0
    }
    var d = t.gjhasZ[n];
    return void 0 === d ? (void 0 === t.PPSXqK && (t.PPSXqK = !0),
        l = t.hRdDHj(l, c),
        t.gjhasZ[n] = l) : l = d,
        l
}
_ = d()()[m("0", "ZLEd")]()[m("1", "1)V^")]()
f = y.a[m("2", "ise7")]("" + _ + 'tools.miku.ac');
authsign=f + "." + CryptoJS.AES.encrypt(_, 'tools.miku.ac')[m("9", "6lDQ")]()
console.log(authsign)

This article is to make a record for myself, and of course everyone is welcome to learn and communicate.

Guess you like

Origin blog.csdn.net/qq_51502150/article/details/127879654