What are JavaScript's builtin strings?
Solution 1:
First of all, I would like to thank Jason and all the contributors for playing with that funny snippet. I have written that piece of code just for fun in order to send it to my wife on February 14 :) Having only Chrome installed on the laptop I had no options to check how it works in Firefox and IE. Moreover, I haven't really expected that toString()
representation of build-in methods might look differently in other browsers.
Now, moving to the real problem, let's precisely have a look at the code. Yes, "v"
was the real "problem" here. I found no other ways of getting this letter except parsing [native code]
string, which can be taken from any built-in method. Since I limited myself with having no strings and no numbers except 1
used, I needed to exploit some method that has only available characters in its name.
Available characters can be obtained from existing keywords and string representations, i.e. from the start we had NaN
, null
, undefined
, Infinity
, true
, false
, and "[object Object]"
. Some of them can be easily converted to strings, e.g. 1/!1+[]
gives "Infinity"
.
I have analyzed different build-in methods for arrays []
, objects {}
, regular expressions /(?:)/
, numbers 1.1
, strings "1"
, and discovered one beautiful method of RegExp
object called test()
. Its name can be assembled from all available characters, e.g. "t"
and "e"
from true
, and "s"
from false
. I have created a string "test"
and addressed this method using square brackets notation for regex literal /-/
, correctly identified in this line:
/-/[(!!1+[])[1>>1]+(!!1+[])[1<<1^1]+(!1+[])[1|1<<1]+(!!1+[])[1^1]]
As was already discussed, this piece of code is evaluated in Chrome as:
function test() { [native code] }
in Firefox as:
function test() {
[native code]
}
and in IE as:
function test() { [native code] }
(in the latter pay special attention to the space before function
keyword)
So, as you clearly see, my code was getting the 24th character from the presented string, which in Chrome was "v"
(as was planned), but unfortunately in Firefox and IE -- "n"
and "["
respectively.
In order to make the same output in all the browsers, I have used different approach than illustrated in the other answers. Now the modified version looks like that:
javascript:[[]+1/!1][1^1][1>>1]+({}+[])[1<<1^11>>1]+([]+!!-
[])[1<<1]+[/~/+{}][+!1][-~1<<1]+/\[[^1]+\]/[([]+![])[1<<1<<
1]+(/|/[(1+{})[1+11>>>1]+[[]+{}][+!1][1]+([]+1/[])[1<<1>>1]
+([1<1]+[])[1+11>>>1+1]+[[!!1]+1][+[]][1-1]+([]+!!/!/)[1|1]
+(/1/[1]+[])[!1%1]+(-{}+{})[-1+1e1-1]+(1+[!!1])[1]+([]+1+{}
)[1<<1]+[!!/!!/+[]][+[]][1&1]]+/=/)[1e1+(1<<1|1)+(([]+/-/[(
!!1+[])[1>>1]+(!!1+[])[1<<1^1]+(!1+[])[1|1<<1]+(!!1+[])[1^1
]])[1^1]==+!1)]+(!![]+{})[1|1<<1]+[1+{}+1][!1+!1][(11>>1)+1
]](([]+/-/[(!!1+[])[1>>1]+(!!1+[])[1<<1^1]+(!1+[])[1|1<<1]+
(!!1+[])[1^1]]))[1&.1][11>>>1]+([,][~1]+[])[1-~1]+[[]+{}][!
1.1%1][11111.1%11.1*111e11|!1]+(/1/+1/[1<1][1%1])[1^11]+[[]
,[]+{}][1<<1>>>1][1||1]+(/[<+>]/[1&1|1]+[1.1])[1/11.1&1.11]
However, in order to intrigue the readers I won't provide a solution for that. I honestly believe that you will easily understand how it works... and some can even surprise their beloved in cross-browser way ;)
P.S. Yet Another Obfuscator
Inspired by Jason's idea to create a universal obfuscating tool, I have written one more. You can find it at JSBin: http://jsbin.com/amecoq/2. It can obfuscate any text that contains numbers [0-9]
, small latin letters [a-z]
, and spaces. The string length is limited mostly with your RAM (at least the body of my answer was successfully obfuscated). The output is supported by Chrome, Firefox, and IE.
Hint: the tool uses different obfuscation approach than was presented above.
Solution 2:
Why isn't the native code
bit from the question being used? This one gives a 'v'
in both Chrome and Firefox:
([]+/-/[(!!1+[])[1>>1]+(!!1+[])[1<<1^1]+(!1+[])[1|1<<1]+(!!1+[])[1^1]])[1^11<<1]>([]+/-/[(!!1+[])[1>>1]+(!!1+[])[1<<1^1]+(!1+[])[1|1<<1]+(!!1+[])[1^1]])[1^(11+1+1)<<1]?([]+/-/[(!!1+[])[1>>1]+(!!1+[])[1<<1^1]+(!1+[])[1|1<<1]+(!!1+[])[1^1]])[1^11<<1]:([]+/-/[(!!1+[])[1>>1]+(!!1+[])[1<<1^1]+(!1+[])[1|1<<1]+(!!1+[])[1^1]])[1^(11+1+1)<<1]
Edit to support IE and do it without the ternary operator:
This one works in Chrome, IE, and FF. Builds an array and uses ==
to determine browser.
[([]+/-/[(!!1+[])[1>>1]+(!!1+[])[1<<1^1]+(!1+[])[1|1<<1]+(!!1+[])[1^1]])[1+(1^(11+1+1)<<1)],([]+/-/[(!!1+[])[1>>1]+(!!1+[])[1<<1^1]+(!1+[])[1|1<<1]+(!!1+[])[1^1]])[1^11<<1],([]+/-/[(!!1+[])[1>>1]+(!!1+[])[1<<1^1]+(!1+[])[1|1<<1]+(!!1+[])[1^1]])[1^(11+1+1)<<1]][((([]+/-/[(!!1+[])[1>>1]+(!!1+[])[1<<1^1]+(!1+[])[1|1<<1]+(!!1+[])[1^1]])[(1<<1<<1<<1)+1<<1]==({}+[])[1^1])*1)+((([]+/-/[(!!1+[])[1>>1]+(!!1+[])[1<<1^1]+(!1+[])[1|1<<1]+(!!1+[])[1^1]])[(1^11<<1)-1]==({}+[])[1^1])<<1)]
Readable:
[
//ie
([]+/-/[(!!1+[])[1>>1]+(!!1+[])[1<<1^1]+(!1+[])[1|1<<1]+(!!1+[])[1^1]])[1+(1^(11+1+1)<<1)],
//ch
([]+/-/[(!!1+[])[1>>1]+(!!1+[])[1<<1^1]+(!1+[])[1|1<<1]+(!!1+[])[1^1]])[1^11<<1],
//ff
([]+/-/[(!!1+[])[1>>1]+(!!1+[])[1<<1^1]+(!1+[])[1|1<<1]+(!!1+[])[1^1]])[1^(11+1+1)<<1]
]
[
//ch?
((([]+/-/[(!!1+[])[1>>1]+(!!1+[])[1<<1^1]+(!1+[])[1|1<<1]+(!!1+[])[1^1]])[(1<<1<<1<<1)+1<<1]==({}+[])[1^1])*1)+
//ff?
((([]+/-/[(!!1+[])[1>>1]+(!!1+[])[1<<1^1]+(!1+[])[1|1<<1]+(!!1+[])[1^1]])[(1^11<<1)-1]==({}+[])[1^1])<<1)
]
Solution 3:
This is about as close as I could get, unfortunately it violates the convention of the original obfuscation by making a call to unescape()
:
unescape((/%/+[])[1]+(/1/[1]+[])[1%1]+(+!1)+(+!1)+(1e1+(11*(1-~1)<<1)))
Teardown:
(/%/+[])[1] => "%"
(/1/[1]+[])[1%1] => "u"
(+!1) => "0"
(+!1) => "0"
(1e1+(11*(1-~1)<<1)) => "76"
===========================
unescape("%u0076") => "v"
Other ideas:
- Somehow get to
unescape("\x76")
- Somehow convert
118
without callingString.fromCharCode()
- Get the text from an exception with the word "Invalid" in it
Updates:
I started playing code golf and have been making it shorter, replacing parts with more 1
s, etc.