Skip to content
KitploitKITPLOIT
FerramentasExploitsBlog
Log in
Enviar
FerramentasExploitsBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
datadome-vm — Engenharia reversa da nova VM Datadome 🔥 | Kitploit
Ferramentas/GitHubGitHub/xkiian/datadome-vm
Análise Dinâmica (Sandboxing)Engenharia ReversaAnálise de BináriosAprendizado e EducaçãoAnti-BotBypass de CAPTCHA
GitHubxkiian/datadome-vm

datadome-vm

Engenharia reversa da nova VM Datadome 🔥

Ver Repositório
1251961há 7 mesesRevisado pelo Kitploit

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

Análise da VM do DataDome

Resumo

Este repositório documenta a primeira versão pública da máquina virtual (VM) JavaScript no navegador do DataDome, usada no fluxo de CAPTCHA/interstitial. Esta análise cobre:

  • Mecanismos de carregamento e decodificação de bytecode
  • Layout e arquitetura da memória da VM
  • Um desmontador de prova de conceito
  • Notas de análise de fluxo de controle

Nota: Este repositório cobre apenas uma versão (estática) da VM e tem como objetivo pesquisa e análise de segurança. Não inclui resolvedores dinâmicos ou implementações de resolvedores de produção.

Contexto

Em 14 de janeiro de 2026, o DataDome começou a distribuir um novo componente baseado em VM em sua tag do cliente.

Desofuscação

O código da VM foi extraído do desafio captcha e está disponível em vm.js (neste repositório).

O primeiro passo foi desofuscar o script:

A ofuscação é direta: avalie cada variável e substitua pelo seu valor real. Um script de desofuscação está disponível em deobf.js.

Análise Inicial

Executar o código desofuscado (out.js) no DevTools revela a saída esperada da VM:

A saída é um objeto JSON contendo dois números e uma string. Agora vamos mergulhar na implementação real da VM.

Decodificação do Bytecode

No início da função Q.exports, podemos ver como o bytecode é decodificado:

  1. A string de entrada é decodificada em base64
  2. Um array de comprimento 129.263 é criado
  3. Cada índice é verificado em relação a um intervalo específico:
    • Se o índice estiver dentro do intervalo, o valor é decodificado
    • Caso contrário, um número aleatório é retornado (usando B(), um gerador de números pseudoaleatórios) -> D contém o bytecode decodificado com algum "ruído" aleatório

Arquitetura da VM

Rolando para baixo revela o ponto de entrada da VM: uma função com dois parâmetros A (o bytecode) e Q (um dicionário vazio usado para tratamento de erros).

Layout da Memória

O aspecto mais interessante desta VM é sua arquitetura: tudo vive em um único array (A). Esse array contém:

  • A pilha
  • Registradores
  • Opcodes
  • O próprio bytecode
  • O ponteiro de instrução

Esse design espelha arquiteturas reais de computadores com regiões de memória distintas. O próximo passo é mapear cada offset para entender o que está armazenado onde:

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

Com esses offsets mapeados, a estrutura da VM fica clara.

Funções Auxiliares

A VM começa com uma coleção de funções auxiliares que lidam com:

  • Leitura de valores tipados da pilha
  • Movimentação de dados entre a pilha e "registradores"

Inicialização da VM

Após as funções auxiliares, a VM inicializa os valores centrais:

  • Todos os ponteiros (pilha, instrução, base do frame)
  • Flag de saída
  • Registrador de último resultado

Abaixo da inicialização estão todos os manipuladores de instrução.

O Loop do Despachante

O despachante é o loop principal da VM que executa até que `exit_flag` seja definido:
  • I representa a instrução atual
  • P é o offset real no array (considerando a ofuscação)
  • O loop define a instrução atual como current_opcode_handler e atualiza current_opcode_id

Implementação dos Opcodes

Como os Opcodes Funcionam

Aqui está um exemplo básico de um manipulador de opcode:

  1. Busca um valor imediato do bytecode
  2. Recupera o valor do topo da pilha
  3. Executa uma operação (ex.: %= ou ^=)
  4. Chama a função fetch() no final

Opcodes Interessantes

Opcode 4919: Criação de Função/Closure

Um dos opcodes mais complexos cria closures/funções:

A[4919] = function () {
    var Q = readUint8();  // Número de argumentos esperados
    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);  // Captura variáveis do escopo atual
    }
    var h = A[instruction_pointer] + 3;  // Salva endereço do corpo da função
    A[A[stack_pointer]++] = function (E) {
        // Configura um novo frame de pilha quando chamado
        var e = A[stack_pointer] - E;
        while (E < Q) {
            A[e + E++] = undefined;  // Preenche argumentos faltantes com undefined
        }
        A[stack_pointer] = e + Q;
        for (var D = 0; D < B.length; D++) {
            var g = B[D];
            A[A[stack_pointer]++] = g;  // Empurra variáveis capturadas
        }
        A[e - 2] = A[frame_base_pointer];  // Salva ponteiro do frame antigo
        A[e - 1] = A[instruction_pointer];  // Salva endereço de retorno
        A[frame_base_pointer] = e;
        A[instruction_pointer] = h;  // Salta para o corpo da função
    };
    fetch();
};

Este opcode:

  1. Lê a contagem de argumentos esperados
  2. Captura variáveis do escopo atual (closure)
  3. Cria uma função que configura um novo frame de pilha com convenções de chamada adequadas
  4. Lida com argumentos faltantes preenchendo com undefined
  5. Salva o endereço de retorno e o ponteiro do frame para retornos adequados

Opcode 5003: Chamada de Função Dinâmica

Este opcode cria um wrapper para chamadas de função que lida tanto com chamadas regulares quanto com construtor:

A[5003] = function () {
    var Q = A[--A[stack_pointer]];  // POP função
    var B = A[--A[stack_pointer]];  // POP contexto 'this'

    function E(e) {  // e = contagem de argumentos
        var D = A[stack_pointer];
        var g = A.slice(D - e, D);  // Obtém argumentos da pilha
        if (this instanceof E) {
            // Chamada de construtor (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 {
            // Chamada de função regular
            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();
};

Este opcode:

  1. Retira a função e o contexto da pilha
  2. Cria um wrapper que pode ser chamado com argumentos
  3. Detecta se é uma chamada de construtor (new) ou regular
  4. Aplica a função com contexto e tratamento de erro adequados
  5. Coloca o resultado de volta na pilha

Opcode 4961: Construção de Objeto Literal

Baixar ferramenta