Write any javascript code with just these characters: ()[]{}+
patriciopalladino.com
patriciopalladino.com
Since you've beat me to it, let me offer up a couple additional tricks you might want to use. If you want to make this completely independent of browser API's, you can eliminate the dependence on window.location (or atob/btoa as the sla.ckers.org poster did).
Trick #1 is to get the letter "S".
You can extract this from the source code of the String constructor, but you want to be careful to make this as portable as possible. The ES spec doesn't mandate much about the results of Function.prototype.toString, although it "suggests" that it should be in the form of a FunctionDeclaration. In practice you can count on it starting with [whitespace] "function" [whitespace] [function name]. So how to eliminate the whitespace?
For this, we can make use of JS's broken isNaN global function, which coerces its argument to a number before doing its test. It just so happens that whitespace coerces to NaN, whereas alphabetical characters coerce to 0. So isNaN is just the predicate we need to strip out the whitespace characters. So we can reliably get the string "S" from:
[].slice.call(String+"").filter(isNaN)[8]
Of course, to get isNaN you need the Function("return isNaN")() trick, and you know how the rest of the encoding works.
Trick #2 then lets you get any lowercase letter, in particular "p".
For this, we can make use of the fact that toString on a number allows you to pick a radix other than 2, 8, 10, or 16. Again, the ES spec doesn't mandate this, but in practice it's widely implemented, and the spec does say that if you implement it its behavior needs to be the proper generalization of the other radices. So we can get things like:
(25).toString(26) // "p"
(17).toString(18) // "h"
(22).toString(23) // "m"
and other hard-to-achieve letters.
But once you've got "p", you're home free with escape and unescape, as you said in your post.
Dave
For example:
(1.1536999999997645e-10).toString(33).match(/[a-z]+/g)[81]; // 'oops'
More here: https://gist.github.com/1153826Dave
The slice & isNaN trick is brilliant!
Is there some reason not to use 36 as a radix and access the whole lowercase alphabet like
(10).toString(36) // "a"
...
(36).toString(36) // "z"
? I'm curious why you use varied combinations of radixes & base numbers.EDIT: Friend pointed out that you are only extending the number set out to what's required for that one character. Makes sense now. :)
And because I felt it was appropriate, I created this extraction script in an obfuscated form!
Use this to extract obfuscated scripts: http://pastebin.com/raw.php?i=Q9TB4wEF
Just save your obfuscated script in a variable called "original" and then run my code. It'll return with the extracted script.
Oh, and it won't work on itself. That's because I didn't use the obfuscation tool to create it. I made it mostly by hand: http://pastebin.com/9LBWCSJs
http://esolangs.org/wiki/Main_Page
(Especially true if you're developing with a Javascript interpreter hosted in Javascript. Really, it's esolangs all the way down.)
[1]www.staff.science.uu.nl/~fokke101/article/combinat/combinat.ps
I went to http://patriciopalladino.com/files/hieroglyphy/ and put in a script "alert(1);". This provided me with a script of about 8300 characters.
I created a web page to execute the script:
<body onload="
[][(![]+[])[!+[] ...
</body>
Firebug reports: ReferenceError: Unescaee is not defined.
Looks like it's having trouble picking up a "p".http://utf-8.jp/public/aaencode.html
Encode any JavaScript program to Japanese style emoticons (^_^)
And of course jjencode:
http://utf-8.jp/public/jjencode.html
(hint: have a look at "palindrome")
[].sort.call()
which I believe used to return the global object but now throws an exception.AFAICT, you need to add {} to make this work in current JS engines.
Dave
That said, this was a very small test; the original file was a random snippet of JS code less than 500 bytes, and that itself took a considerable amount for hieroglyphy to chew on, so I can't really do a proper test of a larger input file.
One technique would be to store verbose or commonly-used string constants in accessible locations like Array.prototype.f. Then you could access, say, the string "prototype" by simply writing
[][(![]+[])[+[]]]
Once you build up a little scratch storage of the most common or hard-to-encode strings, everything starts getting orders of magnitude smaller.(Technically, this means that you're polluting the space shared with the program being encoded, so for everything to work the program can't make use of it. But that's a pretty simple invariant to ask of the input program: "don't get or set the 'f' property of arrays.")
Another technique would be to break up large statements into smaller substatements, to avoid fixed limits of JS engines on statement size. You can always avoid semicolons, since ASI is guaranteed to work if you start your statement with a !.
Dave
That's from a <0.5KB test input, so the expansion might be mitigated a little more for larger files. I was going to test on a 3KB microlibrary, but gave up after about 10 minutes of waiting for the conversion to finish.
EDIT: I didn't check properly. You only use {} for a minor detail.
> "[object Object]" with {}+[]
I believe it should be []+{}
If someone can prove me wrong, I'd be very happy though. Writing a program using just '0' and '1' (the ASCII characters) would be awesome. (in an established programming language, and no homomorphisms. :) )