Cocos2d JS mobile game decryption jsc and aes encryption and decryption analysis of http data ①

Posted by Phsycoslaya on Sat, 25 Dec 2021 02:03:03 +0100

Analysis process

1. View game engine types

2. Install the game

3. Packet capture analysis

4.ida analysis so file

5. Obtain key decryption

6. Analyze the decrypted source code

7. Verify the analysis results

Decryption tool: jsc decryption 1.44 Zip other document resources CSDN download cocos jsc decryption encryption program. You need to know the key. These can be obtained through ida or Frida. Support the latest and more download resources and learning materials. Please visit CSDN download channelhttps://download.csdn.net/download/qq_21051503/70268217

Test App: Happy fish_ Android and apple zip - other document resources - CSDN download happy fish attachment. For more download resources and learning materials, please visit CSDN download channelhttps://download.csdn.net/download/qq_21051503/70274213

Download the game. This game is a cocos2d JS game. The encrypted file is jsc suffix.

Install the App to capture the package. When you open the game, you can request the package and obtain the game configuration information, but the data is encrypted.

>>>>>>>>>>>>>>>>>>>Then start working v_v

 

Find libcos2djs. In the \ lib\arm64-v8a directory So file, pull in ida analysis

Find} xxtea_ The ida position of decrypt is 0x6FAED4

 

I use Frida hook

frida -U com.yoyo.happyfish -l klby.js --no-pause

Package name: com yoyo. happyfish

Get jsc decryption key

Here we get the decryption key of jsc. The rest is to decrypt jsc into js. Analyze the js code to see how to encrypt and decrypt the data

 

hook code klby js

    // Find base address 
    var baseAddr = Module.findBaseAddress('libcocos2djs.so');
    console.log('libcocos2djs.so baseAddr: ' + baseAddr);

    var xxtea_decrypt =  resolveAddress(0x6FAED4);//Decrypt jsc
    Interceptor.attach(xxtea_decrypt, { 

        onEnter: function (args) {

                console.log('[+] Called xxtea_decrypt ' + xxtea_decrypt);
                console.log('[+] args0,r0: ' + args[0]);//Data data
                console.log('[+] args1,r1: ' + args[1]);//data length
                console.log('[+] args2,r2: ' + args[2]);//secret key
                console.log('[+] args2,r3: ' + args[3]);//Key length

                dumpAddr('args0', args[0], 64);
                dumpAddr('args2', args[2], 64);
        },

        onLeave: function (retval) {
            console.log('[+] Returned from  xxtea_decrypt: ' + retval);
        }
    });

    function dumpAddr(info, addr, size) {
        if (addr.isNull())
            return;

        console.log('Data dump ' + info + ' :'+addr);
        var buf = addr.readByteArray(size);

        // If you want color magic, set ansi to true
        console.log(hexdump(buf, { offset: 0, length: size, header: true, ansi: false }));
    }

    function resolveAddress(addr) {
        var idaBase = 0x0; // Enter the base address of jvm.dll as seen in your favorite disassembler (here IDA)
        var offset = ptr(addr).sub(idaBase); // Calculate offset in memory from base address in IDA database
        var result = baseAddr.add(offset); // Add current memory base address to offset of function to monitor
        console.log('[+] New addr=' + result); // Write location of function in memory to console
        return result;
    }

Start decrypting jsc files. I use the fool decryption method. Just fill in the key and a software will do it automatically.

A decrypted compressed package. Open the compressed package and drag the file out.  

 

Open the file and search for m-urlencoded;charset=utf - locate where the Http request is sent.

If aesEncryptString data encryption and HttpEncryptKey are found, we will print the HttpEncryptKey.

I'll be lazy and add it directly to the http request header. I can see the key from the packet capture.

Select the new encryption and put it The js file is encrypted back to the jsc file to replace the original file.  

Rendering, get the aes encryption key.

I also printed the POST data, content params: {"channelid": "0"}

Then I'll test whether the key is correct. The test results are completely consistent

Then I'll see how to decrypt it.

Search aesEncryptString. The following is the decryption method aesDecryptString

Search for aesDecryptString, find the http data return, call aesDecryptString, and decrypt the data with t.config Httpencryptkey. The description is the same as that of encryption.

The test and decryption of the returned data are also successful, so our first step ① is completed.

Decrypt part of js code

var i = function() {
function t() {}
t.randomAesKeyBuffer = function() {
return n(16);
};
t.randomAesIvBuffer = function() {
return n(16);
};
t.stringToBuffer = function(t) {
for (var e = [], o = t.length, n = 0; n < o; n++) e[n] = t.charCodeAt(n);
return new Uint8Array(e);
};
t.bufferToString = function(t) {
for (var e = "", o = Array.from(t), n = 0, r = o.length; n < r; n++) e += String.fromCharCode(o[n]);
return e;
};
t.aesEncryptString = function(t, e) {
if (e.length > 0) {
var o = by.CryptoJS, n = o.enc.Utf8.parse(t), r = o.enc.Utf8.parse(e);
return by.CryptoJS.AES.encrypt(r, n, {
mode: o.mode.ECB,
padding: o.pad.Pkcs7
}).toString();
}
return "";
};
t.aesDecryptString = function(t, e) {
var o = by.CryptoJS, n = o.enc.Utf8.parse(t), r = o.AES.decrypt(e, n, {
mode: o.mode.ECB,
padding: o.pad.Pkcs7
});
return o.enc.Utf8.stringify(r).toString();
};
t.aesEncryptBuffer = function(t, e, o) {
var n = by.CryptoJS, i = n.lib.WordArray.create(t), a = n.lib.WordArray.create(e), s = n.lib.WordArray.create(o);
return r(n.AES.encrypt(s, i, {
iv: a,
mode: n.mode.CBC,
padding: n.pad.ZeroPadding
}).ciphertext);
};
t.aesDecryptBuffer = function(t, e, o) {
var n = by.CryptoJS, i = n.lib.WordArray.create(t), a = n.lib.WordArray.create(e), s = n.lib.WordArray.create(o), c = n.enc.Base64.stringify(s);
return r(n.AES.decrypt(c, i, {
iv: a,
mode: n.mode.CBC,
padding: n.pad.NoPadding
}));
};
t.rsaEncryptWithPublicKey = function(t) {
var e = by.forge.pki.publicKeyFromPem("-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEA3svz/3o/IBJGL3Lml5zH1jTpey+Z1fZraHF5dOj0FA1f8eJM7naE\ncO4tDuIgbICXfNW4Ln+hTi33EMzulpZpehigYFd9aPinJ6ynhNXE4BL/2NauYIRq\nvTy+72PtL4gswT0l0tWWbCX2yNco0G8J4fk92ZivuO+/osTtmYLMmNJ/hjPi4u86\njRtLkoSQ+6X55OJmLA1enWiu5hxDQQtue3Yw96Wo7fUJh8BnTBw7lTUwTHJhadoK\nt6mJqQfv0DnnHaOERkwxNgL/c6u04R2ruywyKrC5aaFRqZRrGA3zthUZS5K3/YLq\nU8tf+UBiBI1hEyroAWe8Q8/syBJ0cYOCOQIDAQAB\n-----END RSA PUBLIC KEY-----"), o = this.bufferToString(t), n = e.encrypt(o);
return this.stringToBuffer(n);
};
t.hex_md5 = function(t) {
var e = by.CryptoJS;
return e.MD5(t).toString(e.enc.Hex);
};
t.LogTag = "[CryptoTool]";
t.strEncryptKey = "thndwdhbqdyydsy5";
return t;
}();

 

>>>>Colored egg<<<<

Careful people may have found that the key is saved in t.Config Httpencryptkey, Config keyword, the description is saved to a certain place, so dig again.

Just find a gameconfig. In the assets directory Config file,

Open file found to be encrypted

Then look in the js file to see if there is a decryption key and search gameconfig config

 

Find the following key code: aesDecryptString

cc.loader.load({ 
    url: e, 
    type: "text" }, 
    function(e, o) {
         //Key decryption key strEncryptKey found is thndwdhbqdyydsy5
        var n = i.CryptoTool.aesDecryptString(i.CryptoTool.strEncryptKey, o);
        r = JSON.parse(n);
        by.extend("by", "Config", r); 
});
t.loadConfig = function(t) {
if (void 0 == by.Config) {
var e = "";
if (cc.sys.isNative) e = cc.path.join(r.default.pkgInsideRootPath, "GameConfig.config"); else {
e = globalThis.location.origin + globalThis.location.pathname;
var o = globalThis.location.search, n = "TestChannel";
if ("" != o) {
var a = o.substr(1, o.length).split("&");
a && (n = a[0]);
}
e += n + ".config";
}
cc.loader.load({
url: e,
type: "text"
}, function(e, o) {
if (e) t(e); else {
var n = i.CryptoTool.aesDecryptString(i.CryptoTool.strEncryptKey, o), r = JSON.parse(n);
by.extend("by", "Config", r);
t && t(e, r);
}
});
} else t(void 0, by.Config);
};

The decrypted local configuration file contains the HttpEncryptKey field, which is the same as that obtained above.

This time only analyzes the encryption at the http level. This game also has a websocket long connection. The specific decryption analysis will be written next time.

Topics: Javascript http cocos2d