Hacker News new | past | comments | ask | show | jobs | submit login
JSFuck – esoteric JavaScript (jsfuck.com)
118 points by BenjaminRH on May 3, 2015 | hide | past | favorite | 38 comments



For those wondering how you get numbers, strings and other primitives from a whole bunch of empty arrays and objects in JavaScript, here's what happens when you do arithmetic and other operations on arrays and objects:

> [] + []

"" (An empty string)

> {} + []

0 (The number zero)

> [] + {}

"[object Object]" (.toString() called on a plain object).

> ![]

false (!{} is the same)

> !![]

true (not false)

> +[]

0 (the number zero)

> -[]

-0 (negative zero)

> +{}

NaN (Not a number, same goes for -{})

> "" + []

"" (empty string)

> "" - []

0 (number zero, not empty string)

And the one that gets more people than the previous list:

> typeof [] === typeof {}

true

Incidentally, some of these things can be useful. For example, +(x) will always evaluate to a double (unless it is preceded by a string), while (x|0) will always evaluate to a 32bit integer. asm.js abuses (to an extent) this to have more control over types, and most JavaScript engines would now store the result of (x|0) as an integer internally instead of a double.


Does knowing any of this make you a better programmer? I'd say no.

Should you be using any of this in production code? No

The next programmer even if he is a really good one might not know that particular esoteric trick.

I'm not trying to vote down or anything, but what is the point? Most of it look like language design warts to me. Most of them should have thrown exceptions and errored out.

Now I'm loving the idea of static typing a lot more.


Most of it is warts and is completely useless. Sometimes things like knowing that an array is an abject are pretty important if you're writing production code.

The bit at the end (integer and float casting) is almost essential knowledge now for anyone writing JavaScript code that does anything mildly intensive.


The problem is the strong-typing/weak-typing distinction, not the static-typing/dynamic-typing distinction. For example, Python is about as strongly typed as C++ yet they're on opposite sides wrt. how dynamic their types are.


Using +x to convert things to numbers and x|0 to convert things to 32-bit integers are useful and common operations.


> Does knowing any of this make you a better programmer? I'd say no.

Yes it does, it makes sure you never do this ;P

Adding arrays sounds like something I'd totally try in JS, had I not known that it causes magic unicorn sparkles to happen)


Useful for obfuscating executable JS for security reasons.


Security through obscurity is not security. This would be easy to translate back into the original code anyway. This is also not efficient from a file size perspective which is cancer on the web.


It'd have to be something that really had to be hidden, because most of these edge cases are very very slow to run.


Why? I believe you have more sophisticated ways of obfuscating code. Those tricks seem to be easy to reverse.


Here's a slightly more entertaining version of the discrepancies/oddities found in javascript:

https://www.destroyallsoftware.com/talks/wat


A bunch of that is wrong or at least misleading.

> {} + [] > 0 (The number zero)

This is wrong, it is only true in the weird scenario that you type it into the jsconsole and don't use the result. It parses as an empty block ({}) and then an unrelated unary +[] after that. var x = {} + []; gives you a string and it is the same as var x = [] + {}.

There is more logic than it seems here:

  Unary plus:
  > always gives a number (a double: NaN is a double)
  > on array: +a -> if len>=2: NaN else +a[0] (recursively)
  > on object: NaN
  > on string: parseNumber, or NaN if that fails
  > on bool: 0 for false, 1 for true
  > on null: 0
  > on undefined: NaN

  binary plus (addition):
  > order doesn't matter except for the jsconsole pseudo-bug
  > if both sides are number or boolean, add them
  > else, toString() both sides and concat them

  boolean negate (not):
  > false for 0, false, "", null, undefined
  > true otherwise

  binary - (subtraction)
  > x-y coerces x and y to number and subtracts them.
  > Most mainstream languages that use + for string concat don't use - for some sort of string unconcat so its not too crazy.

  > typeof [] === typeof {}

  This isn't so crazy, typeof any non-primitive gives you "object".
  [] is pretty much just an object plus magic .length property,
  var x = []; x.blah = 7; console.log(x.blah) works. It doesn't work on number or bool.



It was on HN 2 years ago with some interesting comments: "JSFuck – Write any JavaScript with 6 Characters: []()!+": https://news.ycombinator.com/item?id=6379732

One interesting question was about the "performance impact"? One reply was: "Vanilla I got 225k ops/sec. JSFuck, 4.5 ops/sec. So about 50000 times slower."


Wow, its amazing that you developed this. A healthy mixture of too much free time and being clever.


Great abuse of the implicit type conversion system.

Amazing that a simple alert("Hello world!") expands to over 16KB.


Yeah, it wasn't the greatest idea to copy and paste the minified version of jQuery in there...


Yes, but if you gzip it, you should get a much smaller file.


A good test for compression algorithms.


For shits and giggles I tried to use the minified version of my open source library. After about 30 minutes of Chrome being frozen I gave up, lol. I was curious how large it would balloon 17kb of JavaScript.


I seem to remember CloudFlare using this approach (for obfuscation purposes) when users enable the 'under attack' mode on their sites. I was pretty surprised something like this was possible when I first saw the code.


with eval checked,

   alert(1)
will produce 1227 chars while

   alert(1);
will produce 9535 chars. Interesting.


Well ; produces 8307 chars on its own (without eval checked) -- which seems kind of inefficient (for such a common character in idiomatic javascript). Looking that their encoder, ';' actually has a specific encoding (which itself has to be encoded) so it looks like there's some inefficient expansion taking place (e.g. '.' has a specified encoding that does not require recursive encoding). Encoding the string "link" in the expansion of ';' appears to be very expensive -- alert is cheap in comparison because you can obtain its letters from Javascript return values (e.g. "l" is pulled out of "false" which is obtained by (![]+"")[2])

I'd imagine that if you were serious about this, you'd implement, say, e=String.fromCharCode (12k chars) and use that to dig yourself out of a lot of this expensive stuff if you need more than one hard-to-encode character.


hah. I tried to, eh, encrypt the source code of JSFuck itself at https://raw.githubusercontent.com/aemkei/jsfuck/master/jsfuc...


Just like vanilla JS: http://wtfjs.com


That's the joke. It's a strict subset of JS, not a transpiled language.


Amazing! I wonder if it's difficult to "disassemble" the JSFuck code?


This article is pretty good on piecing it stuff like this together: https://blog.korelogic.com/blog/2015/01/12/javascript_deobfu...


Once you have partial evaluation, it's really easy. I've done this (and with jsfuck too!) http://m1el.github.io/jsdeobfuscate/


Just remove the () and you'll get the code in a string.


Not useful for obfuscation :( Just remove last ()


so js is a cipher code.


I know this will most likely come as a surprise to most Javascript programmers but all code is represented using only two "characters" deep inside your little computers!


What do you mean two characters? What are they exactly? I'm guessing $ and ; because it's like jQuery but some operations seem impossible, like how does it even assign variables?


Its quite simple. The "$" and ";" are used in groups of three to signify other characters.

    "$$$" = "["
    "$$;" = "]"
    "$;$" = "("
    "$;;" = ")"
    ";$$" = "+"
    ";$;" = "!"


I think he meant 1 and 0


yes


Most JavaScript developers have tiny computers.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: