Skip to content
KitploitKITPLOIT
ИнструментыЭксплойтыБлог
Log in
Отправить
ИнструментыЭксплойтыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
datadome-vm — Обратная разработка новой виртуальной машины Datadome 🔥 | Kitploit
Инструменты/GitHubGitHub/xkiian/datadome-vm
Динамический анализ (песочница)Обратная инженерияАнализ Бинарных ФайловОбучение и ОбразованиеАнти-БотОбход CAPTCHA
GitHubxkiian/datadome-vm

datadome-vm

Обратная разработка новой виртуальной машины Datadome 🔥

Репозиторий
12519617 месяцев назадПроверено Kitploit

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

Анализ виртуальной машины DataDome

Краткое описание

Этот репозиторий содержит первую публичную версию анализа встроенной в браузер JavaScript виртуальной машины (VM) DataDome, используемой в их CAPTCHA/промежуточном потоке. Анализ охватывает:

  • Механизмы загрузки и декодирования байткода
  • Структуру памяти и архитектуру ВМ
  • Прототип дизассемблера
  • Замечания по анализу потока управления

Примечание: Этот репозиторий охватывает только одну (статическую) версию ВМ и предназначен для целей исследования безопасности и анализа. Он не включает динамические решатели или реализации промышленных решателей.

Предыстория

14 января 2026 года DataDome начала внедрять новый компонент на основе ВМ в свой клиентский тег.

Деобфускация

Код ВМ был извлечён из задачи капчи в vm.js (доступен в этом репозитории).

Первым шагом была деобфускация скрипта:

Обфускация проста: оценить каждую переменную и заменить её фактическим значением. Скрипт деобфускации доступен в deobf.js.

Первоначальный анализ

Запуск деобфусцированного кода (out.js) в DevTools показывает ожидаемый вывод ВМ:

Вывод — это JSON-объект, содержащий два числа и строку. Теперь давайте углубимся в реализацию самой ВМ.

Декодирование байткода

В начале функции Q.exports видно, как декодируется байткод:

  1. Входная строка декодируется из base64
  2. Создаётся массив длиной 129 263
  3. Каждый индекс проверяется на соответствие определённому диапазону:
    • Если индекс попадает в диапазон, значение декодируется
    • В противном случае возвращается случайное число (с помощью B(), генератора псевдослучайных чисел) -> D содержит декодированный байткод с некоторым случайным «шумом»

Архитектура ВМ

Прокручивая вниз, можно найти точку входа ВМ: функция с двумя параметрами A (байткод) и Q (пустой словарь для обработки ошибок).

Структура памяти

Наиболее интересный аспект этой ВМ — её архитектура: всё хранится в одном массиве (A). Этот массив содержит:

  • Стек
  • Регистры
  • Опкоды
  • Сам байткод
  • Указатель инструкций

Такая конструкция отражает реальную архитектуру компьютера с отдельными областями памяти. Следующий шаг — нанести на карту каждое смещение, чтобы понять, что где хранится:

var stack_pointer = 4593
var instruction_pointer = 4635
var frame_base_pointer = 4674
var last_result = 4633
var exit_flag = 4656
var current_opcode_handler = 4685
var current_opcode_id = 4675
var stack_offset = 124482
var vm_start = 5258

После нанесения этих смещений структура ВМ становится ясной.

Вспомогательные функции

ВМ начинается с набора вспомогательных функций, которые обрабатывают:

  • Чтение типизированных значений из стека
  • Перемещение данных между стеком и «регистрами»

Инициализация ВМ

После вспомогательных функций ВМ инициализирует основные значения:

  • Все указатели (стек, инструкции, базовый кадр)
  • Флаг выхода
  • Регистр последнего результата

Ниже инициализации находятся все обработчики инструкций.

Цикл диспетчера

Диспетчер — это основной цикл ВМ, который выполняется до тех пор, пока не будет установлен `exit_flag`:
  • I представляет текущую инструкцию
  • P — фактическое смещение в массиве (с учётом обфускации)
  • Цикл устанавливает текущую инструкцию на current_opcode_handler и обновляет current_opcode_id

Реализация опкодов

Как работают опкоды

Вот базовый пример обработчика опкода:

  1. Извлекает непосредственное значение из байткода
  2. Получает верхнее значение из стека
  3. Выполняет операцию (например, %= или ^=)
  4. Вызывает функцию fetch() в конце

Интересные опкоды

Опкод 4919: Создание функции/замыкания

Один из самых сложных опкодов создаёт замыкания/функции:

A[4919] = function () {
    var Q = readUint8();  // Количество ожидаемых аргументов
    var B = [];
    for (var E = readUint8(), D = 0; D < E; D++) {
        var g = readUint8();
        var a = A[A[frame_base_pointer] + g];
        B.push(a);  // Захват переменных из текущей области видимости
    }
    var h = A[instruction_pointer] + 3;  // Сохранить адрес тела функции
    A[A[stack_pointer]++] = function (E) {
        // Настройка нового кадра стека при вызове
        var e = A[stack_pointer] - E;
        while (E < Q) {
            A[e + E++] = undefined;  // Заполнить недостающие аргументы значением undefined
        }
        A[stack_pointer] = e + Q;
        for (var D = 0; D < B.length; D++) {
            var g = B[D];
            A[A[stack_pointer]++] = g;  // Поместить захваченные переменные
        }
        A[e - 2] = A[frame_base_pointer];  // Сохранить старый указатель кадра
        A[e - 1] = A[instruction_pointer];  // Сохранить адрес возврата
        A[frame_base_pointer] = e;
        A[instruction_pointer] = h;  // Переход к телу функции
    };
    fetch();
};

Этот опкод:

  1. Считывает количество ожидаемых аргументов
  2. Захватывает переменные из текущей области видимости (замыкание)
  3. Создаёт функцию, которая настраивает новый кадр стека с правильными соглашениями вызова
  4. Обрабатывает недостающие аргументы, заполняя их undefined
  5. Сохраняет адрес возврата и указатель кадра для корректного возврата

Опкод 5003: Динамический вызов функции

Этот опкод создаёт обёртку для вызовов функций, которая обрабатывает как обычные вызовы, так и вызовы конструктора:

A[5003] = function () {
    var Q = A[--A[stack_pointer]];  // POP функции
    var B = A[--A[stack_pointer]];  // POP контекста 'this'

    function E(e) {  // e = количество аргументов
        var D = A[stack_pointer];
        var g = A.slice(D - e, D);  // Получить аргументы из стека
        if (this instanceof E) {
            // Вызов конструктора (new E(...))
            g.unshift(null);
            var h = Function.prototype.bind.apply(Q, g);
            A[stack_pointer] -= e;
            try {
                a = new h();
            } catch (A) {
                a = A.message;
            }
            A[A[stack_pointer]++] = a;
        } else {
            // Обычный вызов функции
            var t;
            try {
                t = Q.apply(B, g);
            } catch (A) {
                t = A.message;
            }
            A[stack_pointer] -= e + 2;
            A[A[stack_pointer]++] = t;
        }
    }

    A[A[stack_pointer]++] = E;
    fetch();
};

Этот опкод:

  1. Извлекает функцию и контекст из стека
  2. Создаёт обёртку, которую можно вызывать с аргументами
  3. Определяет, является ли вызов конструктором (new) или обычным
  4. Применяет функцию с правильным контекстом и обработкой ошибок
  5. Помещает результат обратно в стек

Опкод 4961: Построение литерала объекта

Скачать инструмент