Заявление об ограничении ответственности: на самом деле это не рецензия, это просто я делаю заметки. Контент может быть хаотичным, неправильным или могут быть более эффективные способы. Если вы заметили это, укажите на это.

вступление

До сих пор я искал ошибки в основном в веб-приложениях, но чем больше я это делал, тем больше мне казалось, что я просто касаюсь поверхности. Я хочу глубже изучить уровни абстракции и перейти к бинарному материалу.

Бинарная эксплуатация - это огромная тема, и, посмотрев / прочитав некоторое время, я решил, что мне нужно сосредоточиться на чем-то. Чтобы перейти к сути, я выбрал веб-браузеры или, точнее, движки Javascript. Примерно в то же время LiveOverflow начал публиковать отличные видео на эту тему:

Https://www.youtube.com/watch?v=5tEdSoZ3mmE (часть 0x00)

Https://www.youtube.com/watch?v=yJewXMwj38s (часть 0x01)

Моя цель - продолжить, но не с упором на JavaScriptCore, а на движок V8, и я буду использовать Windows / WinDbg вместо Mac / lldb.

Компиляция V8

Первым шагом будет установка работающего V8 на нашем компьютере. Следующие ссылки помогут:

Https://blog.angularindepth.com/how-to-build-v8-on-windows-and-not-go-mad-6347c69aacd4

Http://www.lfdm.net/development/5-how-to-compile-v8-on-windows.html

Это не совсем простой процесс, и не беспокойтесь, если он займет у вас время. Оба руководства компилируют его с is_debug = false, но вы должны установить для него значение true, потому что нам нужна отладочная сборка.

Примечание: Как указывает Geluchat, отладочная сборка также имеет недостатки: медленные и ошибки, которые могут возникать при выпуске сборки, могут иногда не запускаться при отладочной сборке. Использование use_jumbo_build = true и is_compoment_build = false ускорит процесс сборки.

В какой-то момент я застрял на последней команде компиляции с «ниндзя», которая возвращала ошибку «Permission denied» (даже при запуске от имени администратора). Перестройка V8 в C: \ V8 вместо «Мои документы» решила проблему (не знаю почему). Простое перемещение сборки из одной папки в другую было бы плохой идеей, потому что это испортило бы ваши символы отладки и исходные пути.

На данный момент я скомпилировал последнюю версию, но в какой-то момент будет полезно научиться компилировать более старые версии для анализа прошлых ошибок.

Запуск V8

Оболочка / отладчик V8 называется D8. Он будет находиться здесь:

[path_to_v8_folder]\out.gn\[build_name]\d8.exe

Вы должны запустить его с флагом --allow-natives-syntax, чтобы получить доступ к встроенным функциям, таким как DebugPrint (который похож на функцию «описать», которую LiveOverflow использует в своих видео):

Расположение объектов в памяти и внутренние концепции в V8 отличаются от JavaScript Core. Такие концепции, как Butterfly или Structure ID, которые LiveOverflow описывает в своих видео и которые также описаны здесь, являются концепциями ядра JavaScript и не совпадают в V8 (я так считаю). Сборщик мусора V8, похоже, тоже работает иначе.

Внутренние типы также выглядят по-разному (т.е. массив только с целыми числами - это PACKED_SMI_ELEMENTS, а массив с целыми числами и двойными числами - это PACKED_DOUBLE_ELEMENTS - и массив станет HOLEY вместо PACKED, если в нем будет пустое пространство):

DebugPrint - не единственная внутренняя функция, и есть еще много чего:

Https://cs.chromium.org/chromium/src/v8/src/runtime/runtime.h?rcl=05720af2b09a18be5c41bbf224a58f3f0618f6be&l=574

Отладка V8

LiveOverflow использовал lldb в качестве отладчика. Я попробую использовать WinDbg (не знаю, имеет ли это вообще смысл).

Первые шаги относительно просты. Вы можете либо запустить D8 и подключиться к этому процессу из WinDbg, либо вы можете запустить исполняемый файл D8 напрямую из WinDbg. После этого вы можете нажать F5, чтобы продолжить выполнение программы. После этого можно приступать к экспериментам.

Давайте попробуем создать массив из пары строк и сделать на нем DebugPrint:

В разделе «элементы» мы видим адрес, по которому хранятся элементы массива - в этом случае элементы являются двумя указателями на строки (мы также видим значения указателей). Мы можем попробовать посмотреть адрес элементов массива в WinDbg (окно памяти - нам нужно приостановить выполнение, чтобы иметь возможность искать адрес).

Там мы увидим указатель на первую строку. Не забывайте, что память имеет прямой порядок байтов. Если бы мы пошли по этому адресу, мы бы нашли строку:

Если мы повторим то же самое с массивом целых чисел (ну, в JS это числа, а не целые числа), массив будет состоять не из указателей, а из целых чисел, которые мы определили:

Если мы перейдем к адресу «элементов», то увидим их:

Интересно отметить, что целые числа в примерах, которые LiveOverflow демонстрировал в своем видео 0x01, имели некоторые старшие байты, установленные в FFFF. Это потому, что JS Core использует NaN-бокс, а V8 - нет. Способ хранения массивов отличается от. Нет упоминаний о бабочках или идентификаторах структур. Похоже, это концепции JS Core. Но нам еще предстоит выяснить, как работают внутренние компоненты V8.

Еще одна вещь, которую стоит попробовать, прежде чем мы завершим часть 1: можем ли мы установить точку останова на Math.max, как это сделал LiveOverflow, и пройти через исходный код этой функции? Сначала нам нужно найти эту функцию, чтобы установить точку останова. Мы можем искать такие символы в WinDbg:

X v8!*Mathmax*

Результат:

Затем мы можем установить точку останова:

bp v8!Builtins_MathMax

Затем мы запускаем Math.max в D8 и достигаем точки останова:

Но я не вижу исходного кода? Если я открываю окно стека вызовов, я вижу, что исходный код доступен для большинства функций в стеке вызовов, но не для функций v8! Builtins_ *, которые находятся на вершине стека. Но почему?

Немного погуглив, я наткнулся на это:

Https://v8.dev/docs/csa-builtins

Оказывается, многие встроенные функции не выполняются в C ++, а либо жестко запрограммированы в сборке, либо реализованы с использованием инфраструктуры CodeStubAssembler. Но некоторые по-прежнему написаны на C ++. Немного просмотрев исходный код, я обнаружил, что Math.hypot должен быть одной из таких функций. Итак, я попытался установить точку останова на bp v8! Builtins_MathHypot. Я запускаю все снова, попадаю в точку останова, но результат тот же. Нет исходного кода. ЗАЧЕМ???!!!

Я спросил на StackOverflow:

Https://stackoverflow.com/questions/56443262/how-to-debug-v8-with-source-code-view-using-windbg

Я не ожидал ответа, но получил. Как и было предложено, я установил точку останова прямо в исходном коде, и когда я нажал на нее, я смог пройти через исходный код! Я также заметил, что моя предыдущая ошибка заключалась в том, что я устанавливал точку останова на v8! Builtin_MathHypot вместо v8! V8 :: internal :: Builtin_MathHypot.

Хорошо, я доволен прогрессом. Теперь я собираюсь посмотреть видео LiveOverflow 0x02 и выяснить, что изучать дальше :)

V8 Bug Hunting. Часть 2: Представление в памяти типов JS ›››