Replace array-mapped variables with the actual var

2019-02-06 15:40发布

问题:

I am trying to edit a Greasemonkey/jQuery script. I can't post the link here.
The code is obfuscated and compressed with minify.
It starts like this:

var _0x21e9 = ["\x67\x65\x74\x4D\x6F\x6E\x74\x68", "\x67\x65\x74\x55\x54\x43\x44\x61\x74\x65", ...

After "decoding" it, I got this:

var _0x21e9=["getMonth","getUTCDate","getFullYear", ...   

It is a huge list (500+ ). Then, it has some variables like this:

 month = date[_0x21e9[0]](), day = date[_0x21e9[1]](), ...

_0x21e9[0] is getMonth, _0x21e9[1] is getUTCDate, etc.

Is it possible to replace the square brackets with the actual variable name? How?
I have little knowledge in javascript/jQuery and can not "read" the code the way it is right now.
I just want to use some functions from this huge script and remove the others I do not need.

Update: I tried using jsbeautifier.org as suggested here and in the duplicated question but nothing changed, except the "indent".

It did not replace the array variables with the decoded names.
For example:

  1. jsbeautifier still gives: month = date[_0x21e9[0]]().
  2. But I need: month = date["getMonth"]().

None of the online deobfuscators seem to do this, How can I?


Is there a way for me to share the code with someone, at least part of it? I read I can not post pastebin, or similar here. I can not post it the full code here.

Here is another part of the code:

$(_0x21e9[8] + vid)[_0x21e9[18]]();    

[8] is "." and [18] is "remove". Manually replacing it gives a strange result.

回答1:

I haven't seen any online deobfuscator that does this yet, but the principle is simple.
Construct a text filter that parses the "key" array and then replaces each instance that that array is referenced, with the appropriate array value.

For example, suppose you have a file, evil.js that looks like this (AFTER you have run it though jsbeautifier.org with the Detect packers and obfuscators? and the Unescape printable chars... options set):

var _0xf17f = ["(", ")", 'div', "createElement", "id", "log", "console"];
var _0x41dcx3 = eval(_0xf17f[0] + '{id: 3}' + _0xf17f[1]);
var _0x41dcx4 = document[_0xf17f[3]](_0xf17f[2]);
var _0x41dcx5 = _0x41dcx3[_0xf17f[4]];
window[_0xf17f[6]][_0xf17f[5]](_0x41dcx5);

In that case, the "key" variable would be _0xf17f and the "key" array would be ["(", ")", ...].

The filter process would look like this:

  1. Extract the key name using text processing on the js file. Result: _0xf17f
  2. Extract the string src of the key array. Result:

    keyArrayStr = '["(", ")", \'div\', "createElement", "id", "log", "console"]';
    
  3. In javascript, we can then use .replace() to parse the rest of the JS src. Like so:

var keyArrayStr = '["(", ")", \'div\', "createElement", "id", "log", "console"]';
var restOfSrc   = "var _0x41dcx3 = eval(_0xf17f[0] + '{id: 3}' + _0xf17f[1]);\n"
                + "var _0x41dcx4 = document[_0xf17f[3]](_0xf17f[2]);\n"
                + "var _0x41dcx5 = _0x41dcx3[_0xf17f[4]];\n"
                + "window[_0xf17f[6]][_0xf17f[5]](_0x41dcx5);\n"
                ;
var keyArray    = eval (keyArrayStr);
//-- Note that `_0xf17f` is the key name we already determined.
var keyRegExp   = /_0xf17f\s*\[\s*(\d+)\s*\]/g;

var deObsTxt    = restOfSrc.replace (keyRegExp, function (matchStr, p1Str) {
    return '"' + keyArray[ parseInt(p1Str, 10) ] + '"';
} );
console.log (deObsTxt);

if you run that code, you get:

var _0x41dcx3 = eval("(" + '{id: 3}' + ")");
var _0x41dcx4 = document["createElement"]("div");
var _0x41dcx5 = _0x41dcx3["id"];
window["console"]["log"](_0x41dcx5);

-- which is a bit easier to read/understand.


I've also created an online page that takes JS source and does all 3 remapping steps in a slightly more automated and robust manner. You can see it at:

jsbin.com/hazevo

(Note that that tool expects the source to start with the "key" variable declaration, like your code samples do)



回答2:

@Brock Adams solution is brilliant, but there is a small bug: it doesn't take into account simple quoted vars.

Example:

var _0xbd34 = ["hello ", '"my" world'];
(function($) {
  alert(_0xbd34[0] + _0xbd34[1])
});

If you try to decipher this example, it will result on this:

alert("hello " + ""my" world")

To resolve this, just edit the replacedSrc.replace into @Brock code:

replacedSrc     = replacedSrc.replace (nameRegex, function (matchStr, p1Str) {
    var quote = keyArry[parseInt (p1Str, 10)].indexOf('"')==-1? '"' : "'";
    return quote + keyArry[ parseInt (p1Str, 10) ] + quote;
} );

Here you have a patched version.



回答3:

for (var i = 0; i < _0x21e9.length; i++) {
  var funcName = _0x21e9[i];
  _0x21e9[funcName] = funcName;
}

this will add all the function names as keys to the array. allowing you to do

date[_0x21e9["getMonth"]]()