1

I don't know much about JavaScript, but I found this code as a part of some game engine code. I tried to inspect it, because I noticed this part of code alerts a message and I really cannot figure out how. Here is the minimal code (I reduced it and extracted from original script and I changed variable names to single letters):

var a = '͏‪͏‪‪‪‪‪͏͏‪‪‪‪͏‪͏͏‪͏͏‪‪‪͏‪͏‪‪͏‪‪͏‪‪‪‪‪‪͏͏‪͏‪‪͏‪‪͏͏‪͏‪͏͏͏͏‪‪‪͏͏͏͏͏‪‪͏‪‪͏‪͏‪‪‪͏͏͏‪͏‪‪‪͏‪‪‪͏‪‪‪͏‪͏͏͏‪‪‪‪͏‪‪͏‪‪͏‪‪‪͏͏‪‪‪‪͏‪‪͏‪‪‪‪‪͏͏͏‪‪‪‪‪͏‪͏‪‪‪‪‪͏͏͏‪‪‪‪͏‪‪͏‪‪‪͏‪͏͏͏‪‪‪‪‪͏‪͏‪‪‪‪͏͏‪͏‪‪‪͏͏͏͏͏‪‪‪‪‪͏͏͏‪‪‪‪‪͏‪͏‪‪͏‪‪͏‪͏‪‪‪͏͏͏‪͏‪‪‪͏‪‪‪͏‪‪‪‪‪͏͏͏‪‪‪‪͏‪‪͏‪͏‪‪‪͏‪͏‪͏‪‪‪͏‪͏͏‪͏‪͏͏͏͏͏‪͏‪͏͏͏͏‪‪‪͏‪͏‪͏‪‪‪͏͏͏‪͏‪‪͏‪‪‪͏͏‪‪‪͏͏‪͏͏‪‪‪‪‪͏͏͏‪‪‪‪‪͏‪͏‪‪‪‪͏͏‪͏‪‪‪͏‪‪͏͏‪‪‪‪͏‪͏͏‪‪‪͏‪‪‪͏‪͏‪‪‪͏‪͏͏‪͏‪͏‪‪͏‪‪‪͏͏͏‪͏‪‪‪͏͏‪‪͏‪‪‪͏͏‪‪͏‪‪‪‪͏‪‪͏‪‪‪͏͏‪‪͏‪‪‪‪͏‪‪͏‪‪‪‪͏‪‪͏‪‪‪͏͏‪‪͏‪‪‪‪͏‪‪͏‪‪‪‪͏‪‪͏‪‪‪‪͏‪‪͏‪‪͏‪‪͏‪͏‪‪‪‪͏‪͏͏‪‪‪͏‪‪‪͏‪͏‪‪‪͏‪͏͏‪͏‪͏͏‪͏͏‪͏‪͏͏͏͏͏‪͏‪͏͏‪͏‪‪‪‪‪‪͏͏‪͏‪‪‪͏‪͏‪͏‪‪͏‪‪͏͏‪͏‪͏͏͏͏‪‪‪͏‪͏‪͏‪͏‪‪͏‪‪͏‪‪‪‪‪͏‪͏‪‪‪‪͏͏‪͏‪͏‪‪͏‪‪͏‪‪‪‪͏͏͏͏‪͏‪‪‪͏‪͏‪‪‪‪‪͏‪͏‪͏‪‪͏‪‪͏‪‪͏‪‪‪‪͏‪͏‪‪‪͏‪͏͏‪͏‪͏͏‪͏‪‪‪͏͏͏‪͏‪‪‪͏‪‪‪͏‪‪͏‪‪‪͏͏‪‪‪‪‪͏͏͏‪‪‪‪‪͏‪͏‪‪‪͏͏‪͏͏‪͏‪‪‪͏‪͏‪͏‪‪‪͏‪͏‪‪‪‪‪͏‪͏͏‪͏‪͏‪‪͏‪‪͏‪‪‪‪͏‪‪‪‪‪͏͏͏‪‪‪‪͏͏‪͏͏‪͏͏͏͏‪͏‪‪‪͏‪͏‪͏‪‪‪͏͏͏‪͏͏‪͏‪͏‪‪͏͏‪͏͏͏͏‪͏‪‪‪‪‪͏‪͏‪‪‪‪͏‪͏͏‪͏‪‪͏‪‪͏͏‪͏͏͏͏‪͏‪‪‪‪‪͏‪͏‪‪‪‪͏‪‪͏‪‪‪‪͏͏‪͏‪͏‪‪‪͏‪͏͏‪͏‪͏‪‪͏͏‪͏‪͏͏͏͏‪‪‪‪͏͏͏͏͏‪͏͏͏͏‪͏‪‪͏‪‪‪‪͏‪‪‪͏‪‪‪͏͏‪͏‪͏‪‪͏‪͏‪‪͏‪‪͏‪‪‪‪‪͏‪͏‪͏‪‪‪͏‪͏‪‪‪͏‪‪͏͏‪‪‪‪͏͏͏͏͏‪͏‪͏‪‪͏‪͏‪‪͏‪‪͏‪‪‪‪‪‪͏͏͏‪͏‪͏͏‪͏‪‪‪͏‪͏‪͏‪‪‪‪͏͏͏͏‪‪‪‪‪͏‪͏͏‪͏‪͏͏‪͏‪‪‪͏‪͏͏͏‪‪‪‪͏͏‪͏‪‪͏‪‪‪‪͏‪͏‪‪͏‪‪͏‪‪͏‪‪‪‪͏‪͏‪‪‪͏‪͏‪‪‪‪‪͏‪͏͏‪͏‪͏͏‪͏‪͏‪‪͏‪‪͏‪‪‪͏͏‪‪͏‪͏‪‪‪͏‪͏͏‪͏‪͏‪‪͏‪‪‪‪͏‪͏͏‪‪‪͏͏‪͏͏‪‪‪‪‪͏͏͏͏‪͏͏͏͏‪͏‪‪‪‪‪͏‪͏‪‪‪‪͏‪‪͏‪‪‪‪͏‪‪͏‪‪‪‪‪͏͏͏‪‪‪‪͏‪‪͏‪‪‪͏͏͏‪͏‪͏‪‪͏‪‪͏‪‪‪‪͏‪‪͏‪‪‪͏͏͏͏͏‪͏‪‪͏‪‪͏‪‪‪‪‪‪͏͏‪‪‪‪‪͏‪͏͏‪͏‪͏͏‪͏‪‪‪‪͏‪͏͏‪‪‪‪͏‪‪͏‪‪͏‪‪‪‪͏‪͏‪‪͏‪‪͏‪͏‪‪‪͏‪͏‪‪͏‪‪‪͏͏‪‪‪‪͏͏‪͏‪‪‪͏‪͏‪͏‪‪‪‪͏‪͏͏‪‪͏‪‪͏‪͏‪‪‪‪͏͏‪͏͏‪͏͏͏͏‪͏‪‪‪͏‪‪͏͏͏‪͏͏‪‪‪͏͏‪‪‪͏‪‪͏‪‪͏͏‪‪͏͏‪‪͏‪‪‪‪͏‪‪‪͏͏‪͏͏͏‪͏‪͏͏͏͏‪‪͏‪͏͏‪͏͏‪͏͏͏͏͏͏‪‪͏‪‪‪‪͏‪‪͏͏‪‪͏͏͏‪͏͏‪‪‪͏‪‪͏‪‪͏‪͏‪‪͏‪‪‪͏͏‪‪͏‪‪‪‪͏‪‪‪͏͏͏͏͏‪‪‪͏͏͏‪͏‪‪‪͏͏‪͏͏‪‪‪͏͏‪‪͏‪‪‪͏‪͏͏͏‪‪‪͏‪͏‪͏‪‪‪͏‪‪͏͏‪‪‪͏‪‪‪͏‪‪‪‪͏͏͏͏‪‪‪‪͏͏‪͏‪‪‪‪͏‪͏͏‪‪‪‪͏‪‪͏‪‪‪‪‪͏͏͏‪‪‪‪‪͏‪͏‪‪‪‪‪‪͏͏͏‪͏͏‪‪‪͏͏‪͏‪͏͏‪͏‪‪‪͏‪‪‪͏‪‪͏‪͏͏‪͏‪‪‪͏‪͏͏͏‪‪͏‪͏͏͏͏͏‪͏‪͏͏͏͏‪͏‪‪‪‪‪͏͏‪͏‪‪‪͏͏‪‪‪͏͏‪‪͏‪‪‪͏͏͏͏͏‪‪͏‪‪͏͏͏‪‪͏‪͏͏‪͏‪‪‪͏‪͏͏͏͏‪͏‪͏͏͏͏‪‪͏‪͏͏‪͏͏‪͏‪͏͏‪͏͏‪͏‪͏͏‪͏‪͏‪‪‪‪‪͏͏‪‪‪‪͏‪͏‪‪͏‪͏‪͏͏‪‪͏‪‪‪‪͏‪‪͏‪͏͏‪͏‪‪͏‪‪‪͏͏͏‪͏‪͏͏͏͏‪‪‪͏͏͏͏͏‪‪͏‪‪‪‪͏‪‪‪͏͏͏͏͏͏‪͏‪͏͏͏͏͏‪͏‪͏͏‪͏͏‪͏‪͏͏‪͏͏‪‪‪͏‪‪͏‪‪͏͏‪͏‪͏‪‪‪͏‪‪͏͏‪‪͏͏͏͏‪͏‪‪͏‪‪͏͏͏͏‪͏‪͏͏͏͏‪͏‪‪‪‪‪͏͏‪͏‪͏͏‪';
var b = a.match(/.{8}/g);
var c = b.map(a => [...a].map(a => a == '‪' | 0));
var d = c.map(a => parseInt(a.join``, 2).toString(16));
var e = d.map(a => eval(`'\\x${a.padStart(2, 0)}'`));
var f = eval(e.join``);

I'm trying to understand how they succeed to alert a message. It alerts number 12345, but how? I see some evals here, so I suppose they are making code on the fly, but still I tried using debugger but I couldn't find explanation. They are somehow generating code and executing it, I'm still unable to see how.

I tried this code in jsFiddle and it still works and I tried in Node.js and it throw error alert is not defined, so I am pretty sure everything this code does is to alert a message.

What trick did they use here? How are they making and evaling code and how do they succeed to alert a message? Is this some sort of encription or what?

My question has absolutely nothing to do with this question.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • Use a debugger and step through it. Whatever is `eval`'d on the last line obviously contains an `alert`. – user229044 Jan 12 '18 at 22:12
  • @meagar. No it doesn't contain alert - that is the problem. –  Jan 12 '18 at 22:12
  • 2
    @9098conadygawu you don't know they downvoted you, there are many others that use the site anyone of them could be the one that downvoted. – Patrick Evans Jan 12 '18 at 22:14
  • @PatrickEvans. When you downvote a post it says "please leave a comment", so I am assuming each downvote should correspond to at least one comment. But you are right, I cannot really tell anything for sure. –  Jan 13 '18 at 00:22
  • @HovercraftFullOfEels. When I said to him "would you reconsider your vote", I didn't only mean "retract your downvote", I also mean "give a +1 if the question is good"..*"Please delete your comment"* - Ok, i deleted it. –  Jan 13 '18 at 00:32
  • @HovercraftFullOfEels. No, I am not asking for votes, because as I explained on meta, I will delete this account soon, but I am asking for explanation why he didnt leave upvote –  Jan 13 '18 at 00:35
  • 2
    Possible duplicate of [How does this magic Javascript work?](https://stackoverflow.com/questions/22588223/how-does-this-magic-javascript-work) – Alexei Levenkov Jan 13 '18 at 00:55
  • @AlexeiLevenkov. No, it is just similar, but really has nothing to do with my question. –  Jan 13 '18 at 00:58
  • 1
    If you don't understand how your code works, how can you categorically state that your code and question have nothing to do with the other? – Hovercraft Full Of Eels Jan 13 '18 at 01:02
  • @HovercraftFullOfEels. Very simple: because the linked question has so many upvotes. If it had anything with my question, I would receive probably approximately same amount of votes. –  Jan 13 '18 at 01:03

1 Answers1

1

The code is all there, hidden in the variable a. No, it's not an empty string, its a string consisting of 1888 invisible characters - either \u034f or \u202a to be precise. So this is in fact just a disguised binary encoding.

The code part

var b = a.match(/.{8}/g);
var c = b.map(a => [...a].map(a => a == '‪' | 0));
var d = c.map(a => parseInt(a.join``, 2).toString(16));

breaks them in chunks of 8, then converts each chunk from an array of characters to an array of booleans (or rather, the integers 0 and 1) - notice that it compares the character against the invisible \u202a, and then converts each array-of-8-booleans (oh look, an octet!) into an actual byte and gets a hex representation of it. Here's the hex string (d.join('')):

5f3d275b7e5b28706d7177747b6e7b7c7d7c7b747d79707c7d6d71777c7b5d5d282875716e727c7d79767a775d2b7173737b737b7b737b7b7b6d7a775d2928297e5d5b28755b7d795b785d7d5b6f5d2971776e7c7d725d5d7d2b6f7c792175712b217d7a5b217d7b795d2b2878216f772b5b7d5d76782b5b7e2975787d2974796f5b6f5d7d295b735d2b7a727c217d7b7b7c7b715b7b705b7e7d297a7b6f5b5d6e79757a6d792176273b666f722869206f66276d6e6f707172737475767778797a7b7c7d7e272977697468285f2e73706c6974286929295f3d6a6f696e28706f702829293b6576616c285f29

The part

d.map(a => eval(`'\\x${a.padStart(2, 0)}'`));

has each of them parsed into a character, using a backslash escape. String.fromCharCode would have been the simpler choice. Also the padStart is not even required here, given that none of the bytes is a control character with a byte value less than 16. Maybe this would've been more familiar:

"\x5f\x3d\x27\x5b\x7e\x5b\x28\x70\x6d\x71\x77\x74\x7b\x6e\x7b\x7c\x7d\x7c\x7b\x74\x7d\x79\x70\x7c\x7d\x6d\x71\x77\x7c\x7b\x5d\x5d\x28\x28\x75\x71\x6e\x72\x7c\x7d\x79\x76\x7a\x77\x5d\x2b\x71\x73\x73\x7b\x73\x7b\x7b\x73\x7b\x7b\x7b\x6d\x7a\x77\x5d\x29\x28\x29\x7e\x5d\x5b\x28\x75\x5b\x7d\x79\x5b\x78\x5d\x7d\x5b\x6f\x5d\x29\x71\x77\x6e\x7c\x7d\x72\x5d\x5d\x7d\x2b\x6f\x7c\x79\x21\x75\x71\x2b\x21\x7d\x7a\x5b\x21\x7d\x7b\x79\x5d\x2b\x28\x78\x21\x6f\x77\x2b\x5b\x7d\x5d\x76\x78\x2b\x5b\x7e\x29\x75\x78\x7d\x29\x74\x79\x6f\x5b\x6f\x5d\x7d\x29\x5b\x73\x5d\x2b\x7a\x72\x7c\x21\x7d\x7b\x7b\x7c\x7b\x71\x5b\x7b\x70\x5b\x7e\x7d\x29\x7a\x7b\x6f\x5b\x5d\x6e\x79\x75\x7a\x6d\x79\x21\x76\x27\x3b\x66\x6f\x72\x28\x69\x20\x6f\x66\x27\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x27\x29\x77\x69\x74\x68\x28\x5f\x2e\x73\x70\x6c\x69\x74\x28\x69\x29\x29\x5f\x3d\x6a\x6f\x69\x6e\x28\x70\x6f\x70\x28\x29\x29\x3b\x65\x76\x61\x6c\x28\x5f\x29"

This string is the one evaled in the last line. But surprise, the contents of that string are just

_='[~[(pmqwt{n{|}|{t}yp|}mqw|{]]((uqnr|}yvzw]+qss{s{{s{{{mzw])()~][(u[}y[x]}[o])qwn|}r]]}+o|y!uq+!}z[!}{y]+(x!ow+[}]vx+[~)ux})tyo[o]})[s]+zr|!}{{|{q[{p[~})z{o[]nyuzmy!v';for(i of'mnopqrstuvwxyz{|}~')with(_.split(i))_=join(pop());eval(_)

So what does - still obfuscated - code do?

var _='[~[(pmqwt{n{|}|{t}yp|}mqw|{]]((uqnr|}yvzw]+qss{s{{s{{{mzw])()~][(u[}y[x]}[o])qwn|}r]]}+o|y!uq+!}z[!}{y]+(x!ow+[}]vx+[~)ux})tyo[o]})[s]+zr|!}{{|{q[{p[~})z{o[]nyuzmy!v';
for (var i of 'mnopqrstuvwxyz{|}~')
  with (_.split(i))
    _=join(pop());
eval(_)

Removing the with magic, we get

for (var i of 'mnopqrstuvwxyz{|}~') {
  let temp = _.split(i);
  _ = temp.join(temp.pop());
}

So for all of these characters from m to z, it splits _ by that, takes the last part out, and joins it back together, effectively

  • replacing m by y!v,
  • replacing n by yuz,
  • replacing o by [],
  • replacing p by [~})z{,
  • replacing q by [{,
  • replacing r by |!}{{|{,
  • replacing s by ]+z,
  • replacing t by y[][[]]})[,
  • replacing u by x}),
  • replacing v by x+[~),
  • replacing w by +[}],
  • replacing x by ![],
  • replacing y by ]+(,
  • replacing z by [!}{,
  • replacing { by +!},
  • replacing | by ]+(!![]})[,
  • replacing } by +[],
  • replacing ~ by ][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]

and after all that we get for _ to be evaled the code

[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+[!+[]+!+[]]+[!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])()

Now doesn't that look familiar? It's good old jsfuck!

I found this code as a part of some game engine code

I doubt it. Looks much more like a submission to a code obfusciation context. However, it doesn't appear to be hand-crafted, more likely someone just blindly chained multiple obfusciation tools together.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375