Javascript-подорож з шістьма символами


Javascript – це дивний і прекрасний мову, який дозволяє писати божевільний, але все ще валідний код. Він намагається допомогти нам, конвертуючи одні штуки в інші в залежності від того, як ми працюємо з ними.
Якщо додати рядок до чогось, то він допустить, що ми хочемо отримати текст, тому сконвертирует всі в рядок.
Якщо ми додаємо префікс "плюс" або "мінус", то він допустить, що нам потрібно числове подання та сконвертирует рядок на число, якщо зможе.
Якщо ми заперечуємо, то він сконвертирует це логічне значення.
Ми можемо використовувати ці особливості мови і створити трохи магії зі всього-лише шістьма символами:
[
,
]
,
(
,
)
,
!
та
+
. Якщо ви читаєте це на десктопі, то можете відкрити консоль в вашому браузері (developer tools, наприклад) і запускати код. Просто копіюйте будь-який код з прикладів нижче в консоль, і він повинен виповниться та повернути true.
Давайте почнемо з простого. Ось головні правила:
  1. Префікс
    !
    конвертує в Boolean
  2. Префікс
    +
    конвертує в Number
  3. Додати
    []
    конвертує String
Ось вони в дії:
![] === false
+[] === 0
[]+[] === ""

Ще один важливий момент, про який варто пам'ятати — з допомогою квадратних дужок можна отримувати конкретний символ (букву) з рядка ось так:
"привіт"[0] === "h"

Також пам'ятайте, що можна брати кілька цифр і складати їх у строковому поданні, а потім конвертувати це назад в число.
+("1" + "1") === 11

Добре, давайте спробуємо поєднати ці трюки і отримати букву
a
.
![] === false
![]+[] === "false"
+!![] === 1
------------------------
(![]+[])[+!![]] === "a" // same as "false"[1]

Прикольно!
Цієї досить простої комбінацією можна отримати всі літери зі слів
true
та
false
.
a
,
e
,
f
,
l
,
r
,
s
,
t
,
u
. Окей, можна отримати букви ще звідки-небудь?
Ну, тобто
undefined
, який можна отримати з допомогою дивною нісенітниці на кшталт
[][[]]
. Сконвертируем його в рядок використовуючи одне з наших головних правил і отримаємо додаткові літери
d
,
i
and
n
.
[][[]] + [] === "undefined"

З допомогою літер, які у нас поки є, можна написати слова
fill
,
filter
та
find
. Звичайно, є й інші слова, але нам цікаві саме ці три слова, тому що це методи масиву Array). Тобто вони є частиною об'єкта
Array
і їх можна викликати безпосередньо в примірника масиву. Наприклад,
[2,1].sort()
.
Важлива особливість Javascript: властивості об'єкта доступні через точки (dot notation) або квадратні дужки (square bracket notation). Так як методи масиву — це властивості самого об'єкта Array, можна викликати ці методи за допомогою квадратних дужок замість точки.
[2,1]["sort"]()
— це те ж саме, що
[2,1].sort()
.
Давайте подивимося, що буде, якщо використовувати один з методів масиву з допомогою доступних букв, але не будемо викликати його:
[]["fill"]

Це дає
function fill() { [native code] }
. Можна сконвертувати цей заголовок методу в рядок відомим правилом:
[]["fill"]+[] === "function fill() { [native code] }"

Ось, тепер у нас є додаткові символи:
c
,
o
,
v
,
(
,
)
,
{
,
[
,
]
,
}
,.
З допомогою літер
c
та
o
ми тепер можемо написати
constructor
.
constructor
— це метод, доступний у всіх об'єктах Javascript, він просто повертає функцію-конструктор.
Давайте отримаємо рядкове представлення функції-конструктора для всіх доступних нам зараз об'єктів:
true["constructor"] + [] === "function Boolean() { [native code] }" 
0["constructor"] + [] === "function Number() { [native code] }" 
""["constructor"] + [] === "function String() { [native code] }"
[]["constructor"] + [] === "function Array() { [native code] }"

Звідси ми одержуємо нові літери для арсеналу:
B
,
N
,
S
,
A
,
m
,
g
,
y
.
Тепер можна зібрати слово
"toString"
. Це функція, яку можна викликати з квадратними дужками. Так, на цей раз ми насправді викличемо її:
(10)["toString"]() === "10"

Але ж ми й так могли конвертувати все що завгодно в рядок за допомогою одного з основних правил. У чому користь?
Ну, а якщо я скажу, що метод
toString
у типу
Number
має секретним аргументом з назвою
radix
, який змінює підставу системи числення повертається числа перед конвертацією в рядок. Дивіться:
(12)["toString"](10) === "12" // підстава 10
(12)["toString"](2) === "1100" // основа 2, бінарна система
(12)["toString"](8) === "14" // підстава 8, вісімкова система
(12)["toString"](16) === "с" // шістнадцяткова система 12

Але навіщо зупинятися на 16? Максимум це 36, що включає в себе всі цифри
0
9
і букви
a
z
. Відтепер можна отримати будь-який символ:
(10)["toString"](36) === "a"
(35)["toString"](36) === "z"

Круто! Але що робити з іншими символами начебто знаком пунктуації і великими літерами? Пірнаємо ще глибше в кролячу нору!
В залежності від того, де ви запускаєте Javascript, у вас може бути або не бути доступ до деяких pre-defined об'єктами і даними. Якщо ви працюєте в браузері, то вам швидше за все доступні обгорткові методи HTML.
Наприклад,
bold
— це метод рядка, який додає теги полужирности:
"test"["bold"]() === "<b>test</b>"

Звідси можна дістати
<
,
>
та
/
.
Ви, напевно, чули про функцію
escape
. Вона, грубо кажучи, перетворює рядок у формат URI, щоб прості браузери могли інтерпретувати її. Якщо передати їй пробіл, то отримаємо
%20
. Якщо передати їй
<
, то отримаємо
%3C
. Ця заголовна
C
дуже важлива якщо потрібно отримати всі залишилися відсутні символи.
З допомогою цієї букви можна написати
fromCharCode
. Ця функція повертає символ Юнікоду на основі заданого десятеричного числа. Вона – частина об'єкта String, який можна отримати викликом конструктора, як ми вже робили раніше.
""["constructor"]["fromCharCode"](65) === "A"
""["constructor"]["fromCharCode"](46) === "."

Unicode lookup з легкістю знайти код для будь-якого символу Unicode.
Так, тепер ми можемо написати що завгодно у вигляді рядка, і можемо запустити будь-яку функцію у типів Array, String, Number, Boolean і Object через їхні конструктори. Пристойна потужність всього лише шести символів. Але це ще не все.
Що таке конструктор будь-якої функції?
Відповідь
function Function() { [native code] }
, тобто сам об'єкт Function.
[]["fill"]["constructor"] === Function

Використовуючи це можна передати рядок коду та створити справжню функцію.
Function("alert('test')"); 

Отримуємо:
Function anonymous() { 
alert('test')
}

І її можна відразу ж викликати додавши
()
в кінець. Так, тепер ми можемо запускати цей код!
Уфф! Все!
Тепер у нас є доступ до всіх символів, можна писати ними будь-код і запускати його. Так що Javascript Тьюринг-повний з усього лише шістьма символами
[
,
]
,
(
,
)
,
+
та
!
.
Хочете доказів? Запустіть цей код в консолі.
Є інструмент, який автоматизує конвертацію, і ось як він переводить кожен символ.
У чому користь?
Ні в чому. eBay робив нехороші штуки ще зовсім недавно, що дозволяло продавцям вставляти виконуваний JS в свої сторінки використовуючи ці символи, але це не зовсім типовий вектор атаки. Деякі люди піднімають тему обфускации, але, чесно кажучи, для цього є методи краще.
Вибачте.
Але, сподіваюся, вам сподобалося це подорож.
Джерела:
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.