Еще раз об обфускации JavaScript или сказ о том, как я обфускатор писал. Часть 1

Возникла тут задача, написать код на php, который принимает js-код и обфусцирует его по самое не балуй. После курения гугла и хабра (в том числе, как правильно подсказывает monolithed, вот этой статьи) я приступил к работе. Как и следовало, я начал того, что запустил консоль JS в хроме и начал пытаться получать строки из месива символов, вот что получилось (с пояснениями):
(![]+[])

«false»
[] — пустой массив
! — операция логическое НЕ, особенность js (и не только), в том, что при булевых операциях если операнд равен 0, false, undefined, null, '' (пустой строке), то он приводится к false. Во всех остальных случаях — к true. (спасибо oshibka404 за поправки)
То есть (![]) = false
+ — операция сложения и объединения строк, если оба операнда числа, то произойдет сложения, иначе это будет объединение строк с приведением типов. Кстати, тут есть особая магия типов, но это позже.

(!(![])+[]) 

«true»
Абсолютно тот же принцип, только добавилось двойное отрицание array=>false=>true=>«true»

Теперь нам надо получить два числа, в свое распоряжение, кто угадает какие?

([]|[])

0
Тут у нас снова идут пустые массивы пропущенные через побитовое ИЛИ, суть проста пропускаем любые пустые элементы — получаем ноль

(-~[])

1
А вот тут все малость по сложнее, как мы помним [] — пустой массив, а мы берем его и отрицаем, да не просто так, а побитово, но [] в битовом представлении это будет что-то типа 00000000, а если инвертировать 11111111, казалось бы должно получиться 255, однако это не так, не вдаваясь в подробности представления чисел и всякие дебри (мантиссы, знаковые разряды), достаточно сказать, что в битовом представлении в памяти машины хранят не только число, но и знак, хотя, если надо, почитаете, что это я объясняю, роскомнадзор еще не запретил гугл. Таким образом из 0 мы получаем -1, а так, как у на уже есть один знак минус, то получается --1=1
Уважаемый vitvad в комментариях любезно дал ссылку на видео, где объясняют «магию» чисел

Хм, мы конечно получили уже не мало, но чем больше, тем лучше!

([]+{})

"[object Object]"
Тут опять все просто, до зубовного скрежета, пустой массив плюс пустой объект, объединение строк. А! Стоп! Я же хотел немного рассказать по этому поводу — если при объединении строк один из операндов является объектом, то за место него возвращается "[object %object_name%]"

([]/[]+[])

«NaN»
Сколько будет ноль делить на ноль? А хрен его знает, вот и JavaScript тоже не знает, но те, кто его придумывали были люди умные (впрочем, создатели других языков ни разу не глупее, но зачастую имеют меньше склонностей к мазохизму) и ввели в язык специальное число (и не одно, но об этом чуть ниже), так, что NaN это число, при чем ничему не равное, даже самому себе и обозначает «а хрен его знает». Что же мы тут сделали? Мы разделили пустой массив, на пустой массив, сработало приведение типов, и это получило равно делению 0 на 0. Дальше как обычно

(~[]/-[]+[])

«Infinity»
Давайте еще подумаем, сколько будет -1/-0? Бесконечность и для обозначения бесконечности в JS так же есть свое число. В принципе, я думаю вы уже догадались как это работает…

([][[]]+[])

«undefined»
Тут дело принимает интересный оборот, как мы помним [] — пустой массив, но обратившись к нему, мы выделили под него место, и мы можем обращаться к его элементам, чем мы и не замедлили воспользоваться, обратившись к элементу массива по индексу… пустого массива, но, так, как до нашего обращения там ничего не было нам возвращается undefined, специальный тип, обозначающий «здесь что-то могло быть, но его не было, пока я не посмотрел, а сейчас здесь оно есть, но внутри пусто» или короче «не заданно».

Ну, давайте подведем итог первой части, изыскательной и напишем простой скрипт:
alert('cool')


_=[][(![]+[])[(-~[])+(-~[])+(-~[])]+([]+{})[(-~[])]+(!(![])+[])[(-~[])]+(!(![])+[])[([]|[])]];
_=_();
_[(![]+[])[(-~[])]+(![]+[])[(-~[])+(-~[])]+(![]+[])[(-~[])+(-~[])+(-~[])+(-~[])]+(!(![])+[])[(-~[])]+(!(![])+[])[([]|[])]](([]+{})[(-~[])+(-~[])+(-~[])+(-~[])+(-~[])]+([]+{})[(-~[])]+([]+{})[(-~[])]+(![]+[])[(-~[])+(-~[])]);

Разберем по строкам

1) _=[][«sort»] — сохраняем в переменную ссылку на метод sort массив
2) _=_(); — выполняем метод sort, получаем ссылку на объект Window
3) _['alert']('cool') — используя в объекте Window ссылку на метод alert выполняем его с аргументом 'cool'
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 37

    +4
    Спасибо за статью, но…
    А где же код обфускатора?
      +2
      Извиняюсь, будет в следующей статье, над которой я в данный момент работаю, думаю стоит подправить название статьи добавив «Часть 1». Fixed.
        +2
        Это будет познавательно.
        Попробую на его основе сделать реобфускатор (вдруг столкнусь с Вашим творением).
          0
          В принципе, по завершению работ код будет доступен на github, а что касается деобфускатора — я Вам даже помогу, самому интересно =)
      +5
      Можно узнать о задаче из-за которой возникла такая необходимость? Интересуюсь потому как подобный обфускатор может работать только с примитивными JS конструкциями, да и деофусцируется руками достаточно просто, что в свою очередь ставит под сомнения оправданность его создания кроме как for fun.
        –1
        Задача = заказ. Что касается деобфускации кода, на мой взгляд Вы несколько торопитесь с выводами, ведь сам обфускатор еще не представлен (что будет во второй части), как и код, который он генерирует, при этом не стоит забывать, что пример в статье синтетический, созданный руками.
          +8
          Готов поспорить, что напишу такой JS код, который сломается после подобной обфускации. Ждем релиза.
            +8
            Вызов принят, сэр.
              0
              Окей, жду новостей в этой ветке/топике.
        0
        Вообще прикольно. Я получил удовольствие прочитав статью.

        С помощью closure compiler Ваш код превращается в удобочитаемый (да и вообще — слабая защита от firebug).
        По эффективности и сложности деобфускации — не самое эффективное решение.

          +1
          Насчет деобфускации — ответил выше. Ну а так, рад, что Вам понравилось, мне самому было интересно писать эту статью.
            0
            Есть предположение что сам closure compiler явно лучше справится с обфускацией кода.
            Его же можно было выполнить как раз из самого php ;-)
          0
          Интересно, а как с производительностью у этого обфусцированного кода?
          Вы проводили какие-нибудь тесты?
            +1
            Тесты не проводились, но производительность кода гарантированно упадет.
            +1
              0
              Да, с изучения этого и подобных обфускаторов я и начинал.
              0
              Можно еще собрать слово constructor:

              []['constructor']['constructor']('alert(this)')();
              
                0
                Да, а еще можно использовать функции atob и bota
                +1
                … однако это не так, не вдаваясь в подробности представления чисел и всякие дебри (мантиссы, знаковые разряды) ...

                Видео объясняет «магию» с числами в JS и не только (на английском)
                это как дополнение к посту
                  0
                  Сейчас добавлю в пост, благодарю
                  0
                  Можете еще добавить ссылку на статью Обфускация JavaScript
                    0
                    Сейчас добавлю, натыкался на неё, когда собирал материал. Благодарю.
                      0
                      особенность js (и не только), в том, то при булевых операциях если операнд не является булевым типом и не равен 0 то он признается за true

                      Не совсем так. В булевых операциях если операнд равен 0, false, undefined, null, '' (пустой строке), то он приводится к false. Во всех остальных случаях — к true.
                        0
                        Спасибо, сейчас исправлю.
                          0
                          Когда меня попросили сделать защиту от ботов для тизерной сети, я ограничился генерацией кода типа var hash = function(){var _=substr(1, 2, substr(2, 4, 'ngveifq3dm3129ejf'))(); return substr(5, 3,_)+substr(6, 8,_).... деталей сейчас уже не вспомню.
                          Человеку в принципе и такое достаточно сложно разобрать. А если к боту добавлен JS интерпретатор, то более-менее защитить может только динамическое обращение к окружению браузера (типа if(window.alert) return true; else return false в браузере вернёт true а в голом интерпретаторе false)
                            0
                            Давайте еще подумаем, сколько будет -1/-0? Бесконечность


                            Неопределённость — как математик говорю.
                              0
                              Хм, я всегда считал, что 0/0 = неопределенность, так же как и ∞/0, а вот если делить на ноль вещественное число, то будет бесконечность. Или я путаю просто ноль и стремление к нулю?
                                0
                                x / 0 — согласно стандарту IEEE 754, если знаменатель будет стремиться к нулю или числитель к бесконечности, то результат будет равен +/Infinity.
                                  0
                                  Спасибо за разъяснения, поставил бы плюс, но кармы не хватает
                              0
                              А зачем вы всё это делаете? Материал изучен (есть статьи), обсускаторы по этой методике написаны, а вы изобретаете велосипед, да ещё и плохой. Например ваше «([]|[])» заменяется просто на «+[]».
                                0
                                Да, а еще можно заменить на ((![[]]+![[]])) или ([[]]|[[]]). Извиняюсь, конечно, но Вы судите о том, что еще не видели. К тому же, что Вам мешает игнорировать очередной «велосипед», если Вы его таковым посчитаете. Однако, если у Вас есть конструктивная критика, то я с удовольствием её выслушаю.
                                  0
                                  Если вы хотели критики, то вы неправильно статью начали. Вы дипломную работу сдавали когда-нибудь? Там для начала спрашивают — если есть аналоги, то чем ваше решение лучше? Вот и рассказали бы для чего вы начали писать свой велосипед, если есть аналоги.
                                0
                                Это не обфускатор, это игрушка.
                                Даже если не обращать внимание на объем или производительность. Символы закодированные таким методом одназначно восстанавливаются в исходные. Это примерно такой же уровень как кодировать урлы в base64.
                                Тоесть всю сложность для деобфускации представляют те шаги которые были сделанны до или после самой зрелищной части со скобочками.
                                  0
                                  Согласен, сам по себе такой метод не эффективен, однако в комбинации с другими методами может получиться вполне не плохо.
                                  Если проводить аналогию с URL дальше, то закодировав в Base64 мы ничего не добьемся, а вот если перед этим URL зашифруем любым криптоалгоритмом, то получается совсем другая песня.
                                    +1
                                    Да, но зачем тогда вообще кодировать в base64?
                                      0
                                      Как вариант — психологический эффект, однако тут есть дополнительный бонус, это отсев интерпретаторов
                                  0
                                  Но в чём смысл обусфицировать javascript?
                                  Уже сходу можно будет понять, как примерно всё работает, просто запустив пошаговое выполнение.

                                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                  Самое читаемое