Life without Hook is not worth jsHook and simulation execution

1, Target

Boss Li: Fenfei, the App analyzed last time http://91fans.com.cn/post/bankdataone/ Light energy debugging is not enough, and the js in the web page can't use Frida. I also want to Hook its function. What's the matter? Another App can use RPC to execute signature. How can I use this js? You can't change the code to js to make requests, can you?

Fenfei: boss, don't you make so many demands for us to work overtime? The overtime pay can be doubled this time.

2, Steps

The simplest JS hook - console log

Our goal is to Hook this encryptSm4ECB function, and then print out its input parameter and return value.

Break points (usually function entries and exits) at appropriate locations. Then right-click the breakpoint - > modify the breakpoint, and then enter the variable to be printed in the pop-up window.

TIP: actually, this function is a conditional breakpoint. It can trigger the breakpoint when the conditions are met, but it can be used to print the variable value. After the modification is successful, the breakpoint icon will change color.

Run, we want to participate in results are printed out.

TamperMonkey injection

TamperMonkey is commonly known as oil monkey. You can understand that it is the Frida of browser. However, I can't find how to Hook the encryptSm4ECB in this sample, but it can be used to Hook the global function successfully. Oil monkey Hook is successful. This brother of encryptSm4ECB can leave me a message.

Fiddler plug-in injection

Fiddler can use plug-ins to inject js code while capturing packages. This seems complicated and I don't know what to do

Chrome enables local substitution

If you can directly in this articledetail JS up to modify and add the code of printing variables, isn't it fast.

Chrome actually provides this function. It is a file level Hook, which is executed to articledetail js this request, do not send a request to the server, but directly use your local replacement js. So you can change it as you want.

Select replace on the source code page, and then check enable local replacement. At this time, the browser will prompt you to give permission, and then select a local directory to store the js to be replaced.

Go back to the web page, select the js you want to replace, right-click - > save and overwrite.

Go back to the source code page and find the js file. In fact, it has been saved in the directory we started to specify.

At this time, find the specified function location and write hook code.

TIP: xxx.js this kind of link replacement is no problem, and the hook code can also be activated. ArticleDetail.js?v=ab4f0b37a4a90050d429 JS in this mode were not replaced successfully. The reason is unknown. Successful brothers also leave messages.

The first step of simulation execution is to run through with Nodejs

Zi once said: reverse is miscellaneous, and you should know a little about A-Z language. js was originally running on the server side. When Nodejs came out, who would compete.

Ask Du Niang and Gu Ge, match VSCode + NodeJs, and Hello World will run through and work.

ArticleDetail.js this sample code is still very kind, basic wood is confused, at a glance.

The eight character mantra of running through the code is to step by step, divide and rule.

Code by code and function by function. Don't copy the whole code as soon as you come up, and then give up treatment after looking at a pile of errors.

encryptSm4ECB: function(t) {
	var e = s("string" == typeof t ? t : JSON.stringify(t))
  ...
}

First execute the value of E, e calls the function s, and the parameter is t, but judge whether T is a string. When we used Hook, we directly printed the console log(JSON.stringify(t));

So the code here can be written in Nodejs:

var n = "dro";
var o = [20320, 25105, 20182, 30340, 22320, 30334, 21315, 19975, 20986, 20837, 19978, 19979, 21069, 21518, 25307, 38134, 22269, 26085, 26376, 23545, 38169, 22909, 22351];

function s(t) {
    var e, i, n = new Array;
    e = t.length;
    for (var r = 0; r < e; r++)
        (i = t.charCodeAt(r)) >= 65536 && i <= 1114111 ? (n.push(i >> 18 & 7 | 240),
            n.push(i >> 12 & 63 | 128),
            n.push(i >> 6 & 63 | 128),
            n.push(63 & i | 128)) : i >= 2048 && i <= 65535 ? (n.push(i >> 12 & 15 | 224),
                n.push(i >> 6 & 63 | 128),
                n.push(63 & i | 128)) : i >= 128 && i <= 2047 ? (n.push(i >> 6 & 31 | 192),
                    n.push(63 & i | 128)) : n.push(255 & i);
    return n
}

var t = '{"parentId":"f6be7358-f906-4087-b387-69cc17a9ebf8","parentType":"ARTICLE","pageIndex":1,"time":"2022-02-23T10:05:34.760","pageSize":5}';
var e = s(t);
console.log(e);

Here, the values of n, t and e can be printed through the previous hook scheme. By comparison, the value of e is ok, indicating that the s function is available.

var encryptSm4ECB = function (t) {
    var e = s(t)
    , i = (new Date).getTime()
    , r = (i + "").split("")
    , o = [r[5], r[10]].join("")
    , c = s("CFKt03X9Ufk" + n + o);	

The value of c is a little complicated, but when we Hook, we can print out the values of n and o. in fact, when debugging, we can write c to death first, which is equivalent to

var cStr = 'CFKt03X9Ufkdro88';
var c = s(cStr);

TIP: in fact, there is a pit buried here. The value of c is related to the last timestamp, which should be corresponding to it.

It's going on

var CMBSM4EncryptWithECB = function (t, e) {
        // if (!e || !t)
        //    return y.failed(c);
    // if ("object" != s(e) || "object" != s(t))
    //    return y.failed(F);

    // if (e.length <= 0)
    //    return y.failed(h);

    // if (16 != t.length)
    //    return y.failed(f);
    var i = encodeWithPKCS5(e, 16)
        , n = encryptWithECB(i, t);
    return n;

    // , r = new C;
    // return r.set("result", n),
    // y.success(r)
}

It seems that the class y is just to output error prompts, so don't use it at all.

The return value r encapsulates n, which is not elegant enough. Let's return n directly.

var encryptWithECB = function (t, e) {
    // l(void 0 !== t && t.length % 16 == 0, "illegal plaintext:the length of plaintext must be the multiple of 16."),
    // l(void 0 !== e && 16 === e.length, "illegal key:the length of sm4Key must be 16 bytes.");
    for (var i = vt(e), n = t.length, r = new Array(n), a = 0; a < n;)
        bt(t, a, r, a, i, 0),
            a += 16;
    return r
}

This l function seems to be an error prompt. Kill it.

Then copy the dependent vt, bt and other functions, and it seems that you can run. Another error is the return value.

Since we directly returned n, we need to change it

var encryptSm4ECB = function (t) {
    var e = s(t)
    , i = (new Date).getTime()
    , r = (i + "").split("")
    , o = [r[5], r[10]].join("")
    , c = s("CFKt03X9Ufk" + n + o);

    // var cStr = 'CFKt03X9Ufkdro88';
    // var c = s(cStr);

    try {
        var l = CMBSM4EncryptWithECB(c, e);

        for (var u = "", h = 0; h < l.length; h++)
            u += String.fromCharCode(l[h]);

        console.log(i);   
        return base64encode(u);    
        /*    
        return {
					      data: window.btoa(u),
            timestamp: i
        }
        // */

    } catch (d) { }
    return t instanceof Object ? null : ""
}

This window Btoa gave the pit and asked brother Gu. Brother Gu said that this is the base64 transcoding provided by the browser. NodeJs also provides a Base64 function, but it turns out differently

Fortunately, brother Gu is still reliable. He found a Base64 written in js

var base64EncodeChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
    base64DecodeChars = new Array((-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), 62, (-1), (-1), (-1), 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, (-1), (-1), (-1), (-1), (-1), (-1), (-1), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, (-1), (-1), (-1), (-1), (-1), (-1), 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, (-1), (-1), (-1), (-1), (-1));
var base64encode = function (e) {
    var r, a, c, h, o, t;
    for (c = e.length, a = 0, r = ''; a < c;) {
        if (h = 255 & e.charCodeAt(a++), a == c) {
            r += base64EncodeChars.charAt(h >> 2),
                r += base64EncodeChars.charAt((3 & h) << 4),
                r += '==';
            break
        }
        if (o = e.charCodeAt(a++), a == c) {
            r += base64EncodeChars.charAt(h >> 2),
                r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),
                r += base64EncodeChars.charAt((15 & o) << 2),
                r += '=';
            break
        }
        t = e.charCodeAt(a++),
            r += base64EncodeChars.charAt(h >> 2),
            r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),
            r += base64EncodeChars.charAt((15 & o) << 2 | (192 & t) >> 6),
            r += base64EncodeChars.charAt(63 & t)
    }
    return r
}

After comparison, the first-class bar is consistent with the result of Chrome Hook.

So how to use this result? You can use NodeJs to start a web server and then rpc to execute it.

Next, let's introduce an elegant method to execute js directly in python

Introduction to Js simulation library

There are many JavaScript execution engines written in Python in the Jianghu.

PyV8

https://pypi.org/project/PyV8

It is said that it is old and in disrepair. The latest version is in 2010, which is not recommended by the bosses.

But in fact, in 2013, it also updated the general, honest and quite old, can you still eat? I think the name V8 is worth trying.

Js2Py

https://github.com/PiotrDabkowski/Js2Py

I also think it's old. In fact, it was updated five months ago. We can't underestimate the potential of older programmers.

PyExecJS

https://pypi.org/project/PyExecJS/

A library originally born in Ruby was later transplanted to Python.

It is quite active. The latest update is 2018. There are many examples of its use in the Jianghu. Many people recommend it

PyminiRacer

https://github.com/sqreen/PyMiniRacer

The author claims that this is a library to succeed PyExecJS. It's relatively new. It depends on fate. Brother Fei found it for the first time, so I'll use it today.

Pyppeteer

https://github.com/pyppeteer/pyppeteer

You can also try this. In fact, many old libraries that are despised by people are still trying to update.

Selenium

https://www.selenium.dev/

  • A web automation testing framework, which can drive various browsers to simulate manual operation
  • It is used to render the page to facilitate the extraction of data or verification code
  • You can also directly drive the browser to execute JS

Selenium can drive the browser, so it's no problem to execute js. This is the last mace.

PyminiRacer simulates the execution of encryptSm4ECB

Let's start with Hello World

from py_mini_racer import py_mini_racer
jsSource = '''
var ffdemo = function(str){
	return str;
}

'''
ctx = py_mini_racer.MiniRacer()
ctx.eval(jsSource)
print(ctx.call("ffdemo", "Hello World"))

Yes, it's so handsome. It's done in three lines of code.

Copy the code that NodeJs ran through just now and execute print(ctx.call("encryptSm4ECB", strFF))

The result came out.

3, Summary

After NodeJs is executed, don't copy the whole page of code at the beginning. Divide and conquer it and run through one function by one.

JavaScript protection has only one way to go, that is confusion. Next time we find a suitable sample, we'll analyze it together.

Lian Po is old. He is still a bucket of rice and ten kilograms of meat. He has more than life and coding.

Keywords: Javascript Front-end

Added by l053r on Mon, 28 Feb 2022 03:14:01 +0200