Почему (! [] + []) [+!! [] + []] создает "a"
Мне интересно понять внутренности JavaScript. Я попытался прочитать источник для SpiderMonkey и Rhino, но это довольно сложно обернуть вокруг меня.
Я спрашиваю: почему что-то вроде
-
(![]+[])[+!![]+[]]
произведите "a"
-
(Å=[],[µ=!Å+Å][µ[È=++Å+Å+Å]+({}+Å)[Ç=!!Å+µ,ª=Ç[Å]+Ç[+!Å],Å]+ª])()[µ[Å]+µ[Å+Å]+Ç[È]+ª](Å)
произведите alert(1)
?
Источник: http://sla.ckers.org/forum/read.php?24,32930,page=1.
В этом форуме есть еще много примеров особенностей JavaScript, и я хотел бы знать, как это работает с точки зрения программирования в отношении безопасности веб-приложений.
Ответы
Ответ 1
Почему (![]+[])[+!![]+[]]
создает "a"
шаг за шагом: это анализируется в: (![]+[])
и [+!![]+[]]
. Первый бит уже был объяснен artemb: []
- это массив. Отрицая его, ![]
оценивает логическое значение, false
- то, как работает !
, когда оно применяется к тому, что не является null
или undefined. Опять же, как указано artemb, добавление этого +[]
заставляет логическое преобразование в строку. Это потому, что +
является оператором конкатенации строк. Булевский false
затем преобразуется в его строковое представление, "false"
.
Затем второй бит [+!![]+[]]
. Прежде всего, внешние [
и ]
служат для обработки предыдущей строки, которую мы просто поддерживаем, равным "false"
как массив символов. Поместив целочисленный индекс внутри [
и ]
, вы получите символ в определенном индексе. Итак, остается +!![]+[]
Это состоит из 4 частей: +
, !![]
, +
и []
. Оценивается первый !![]
. Мы уже видели, что ![]
является булевым false
, поэтому новый !
отрицает его и дает true
. Следующее, что происходит, это то, что применяется +
in +!![]
, и, применяя +
, он преобразует логическое true
в числовое представление, которое является 1
(so +true
is 1
) Следующий ниже +[]
возвращает строку из этого 1
, давая "1"
, но это не имеет смысла, более короткое выражение (![]+[])[+!![]]
уже создает a
. Добавление +[]
тоже не повредит, получившееся выражение просто ["1"]
вместо [1]
. Моя догадка заключается в том, что когда []
применяется к массиву, все, что находится внутри []
, будет принудительно введено в число, которое для "1"
дало бы 1
снова. Таким образом, в любом случае +!![]+[]
оценивается до 1
, делая окончательное выражение: "false"[1]
, который говорит: gimme символ в индексе 1 из строки "false"
, и поскольку по умолчанию массивы начинаются с 0
в javascript, это второй символ "false"
и a
.
Ответ 2
Если вы хотите понять, почему эти странные выражения работают так, как они, вы можете открыть консоль firebug и провести эксперимент самостоятельно. Я сделал, и я получил, что ![]
is false
, !![]
is true
, добавление массива к логическому значению (false+[]
или true+[]
) создает строку-версию этого значения (false+[]="false"
).
Таким образом, выражение сводится к следующему:
"false"["1"]
который, очевидно, "a"
Ответ 3
Почему (! [] + []) [+!! [] + []] создает "a"
-
!expr
- вызывает ToBoolean на expr и переворачивает логическое значение. Другими словами, значения правды, такие как пустой массив, будут выдавать false при использовании с оператором not.
-
a + b
- Оба выражения запускаются через внутренний ToPrimitive. Если результирующее значение является строкой, выполняется конкатенация строк. В противном случае примитивы запускаются через ToNumber и добавляются. ToPrimitive для объектов (включая массивы) будет пытаться toString и valueOf. Array.prototype.toString действует как вызов соединения без параметров. Таким образом, ![] + [] = false + "" = "false"
-
!![] == true
, унарный оператор plus преобразует выражение в число, поэтому 1
. И снова массив преобразуется в ""
, поэтому +!![]+[] == "1"
.
- Выражение сводится к
("false")["1"] == "a"
Другое выражение можно сварить аналогичным образом. Он использует строки unicode, чтобы испортить его, и это длиннее, но так же просто, как "разобрать".
Ответ 4
Я рекомендую вам получить и прочитать:
- Стандарт ECMAScript (ECMA 262), 5-е издание
- Документ Adobe под названием "Обзор AVM 2", который объясняет архитектуру виртуальной машины AVM2, на которой выполняется Adobe Flash и ее ActionScript.