Desvendando o STL: Identificando std::vector em Código Revertido
Aprenda a reconhecer o contêiner std::vector em código compilado através de padrões de memória e chamadas de função. Este guia detalha como identificar a estrutura e o comportamento do std::vector em engenharia reversa.
MundiX News·02 de maio de 2026·4 min de leitura·👁 4 views
Em processos de engenharia reversa, é comum nos depararmos com código STL (Standard Template Library) que, à primeira vista, pode parecer complexo e demorado de analisar. Um olhar inexperiente pode gastar tempo valioso tentando decifrar construtores ou funções genéricas, quando na verdade, o objetivo deveria ser identificar rapidamente os contêineres STL, entender a disposição dos dados e tipificá-los para prosseguir com a análise.
Nesta série de publicações, abordaremos os contêineres STL mais comuns, começando pelo std::vector. Este contêiner é um dos mais simples e, em compilações de 64 bits, ocupa 24 bytes na memória. Ao inspecionar o código fonte do std::vector no MSVC STL, encontramos a classe _Vector_val, que armazena exatamente três ponteiros: _Myfirst (início dos dados), _Mylast (fim dos dados) e _Myend (fim da memória alocada). Essa tríade define o esquema de memória do vetor. O compilador os alinha sequencialmente na memória (com offsets de 0, 8 e 16 bytes em código de 64 bits), o que se traduz em acessos a esses offsets no código assembly, servindo como nosso principal indicador.
Um padrão comum a ser observado é a inicialização de um vetor vazio. Em vez de chamar um construtor padrão, o compilador pode inicializar os três ponteiros com zero. Isso se manifesta no código assembly como acessos a offsets específicos (por exemplo, var_68, var_60, var_58 em relação a um endereço base) e a escrita de valores nulos. A soma dos tamanhos desses campos (16 + 8 = 24 bytes) confirma a estrutura de um vetor vazio na pilha. Identificar essa sequência de inicialização nos permite inferir a presença de um std::vector.
Outro padrão crucial envolve a operação push_back(). Em C++, push_back() é um método de instância que adiciona um novo elemento ao final do vetor, redimensionando-o automaticamente se necessário. Embora não seja a única forma de popular um vetor, é uma operação que revela o padrão de verificação de espaço. Em pseudocódigo, a lógica seria algo como: if (_Mylast == _Myend) { push_back_func(); } else { construct(_Mylast, value); ++_Mylast; }. O código verifica se há espaço para um novo elemento. Se não houver, um redimensionamento é acionado (push_back_func()). Caso contrário, o novo valor é construído no endereço apontado por _Mylast, e _Mylast é incrementado. No código assembly, isso se traduz em uma comparação entre os ponteiros _Mylast e _Myend, seguida por uma chamada a uma função (como sub_140002860) que, internamente, calculará o novo tamanho do vetor (size() = (_Mylast - _Myfirst) / sizeof(T)) para determinar se um novo bloco de memória precisa ser alocado. A presença dessa comparação e a subsequente chamada de função são fortes indicativos de que estamos lidando com uma operação de push_back em um std::vector.
Ao reconhecer esses padrões – a tríade de ponteiros com offsets específicos e a lógica de push_back com verificação de capacidade – podemos reconstruir a estrutura do vetor e as estruturas que o utilizam com maior clareza. Mesmo com a existência de outros construtores e métodos para std::vector, a compreensão de sua disposição interna na memória é fundamental para a identificação rápida em código compilado. Continuaremos explorando outros contêineres STL populares em publicações futuras. Fique atento e seguro!
🛡️⚡
Pare de pesquisar. Comece a hackear.
O MundiX é seu copiloto de pentest com IA: comandos exatos, análise de outputs e próximo passo na kill chain — em segundos.
Sem cartão para começar · Planos a partir de R$49/mês
Em processos de engenharia reversa, é comum nos depararmos com código STL (Standard Template Library) que, à primeira vista, pode parecer complexo e demorado de analisar. Um olhar inexperiente pode gastar tempo valioso tentando decifrar construtores ou funções genéricas, quando na verdade, o objetivo deveria ser identificar rapidamente os contêineres STL, entender a disposição dos dados e tipificá-los para prosseguir com a análise.
Nesta série de publicações, abordaremos os contêineres STL mais comuns, começando pelo std::vector. Este contêiner é um dos mais simples e, em compilações de 64 bits, ocupa 24 bytes na memória. Ao inspecionar o código fonte do std::vector no MSVC STL, encontramos a classe _Vector_val, que armazena exatamente três ponteiros: _Myfirst (início dos dados), _Mylast (fim dos dados) e _Myend (fim da memória alocada). Essa tríade define o esquema de memória do vetor. O compilador os alinha sequencialmente na memória (com offsets de 0, 8 e 16 bytes em código de 64 bits), o que se traduz em acessos a esses offsets no código assembly, servindo como nosso principal indicador.
Um padrão comum a ser observado é a inicialização de um vetor vazio. Em vez de chamar um construtor padrão, o compilador pode inicializar os três ponteiros com zero. Isso se manifesta no código assembly como acessos a offsets específicos (por exemplo, var_68, var_60, var_58 em relação a um endereço base) e a escrita de valores nulos. A soma dos tamanhos desses campos (16 + 8 = 24 bytes) confirma a estrutura de um vetor vazio na pilha. Identificar essa sequência de inicialização nos permite inferir a presença de um std::vector.
Outro padrão crucial envolve a operação push_back(). Em C++, push_back() é um método de instância que adiciona um novo elemento ao final do vetor, redimensionando-o automaticamente se necessário. Embora não seja a única forma de popular um vetor, é uma operação que revela o padrão de verificação de espaço. Em pseudocódigo, a lógica seria algo como: if (_Mylast == _Myend) { push_back_func(); } else { construct(_Mylast, value); ++_Mylast; }. O código verifica se há espaço para um novo elemento. Se não houver, um redimensionamento é acionado (push_back_func()). Caso contrário, o novo valor é construído no endereço apontado por _Mylast, e _Mylast é incrementado. No código assembly, isso se traduz em uma comparação entre os ponteiros _Mylast e _Myend, seguida por uma chamada a uma função (como sub_140002860) que, internamente, calculará o novo tamanho do vetor (size() = (_Mylast - _Myfirst) / sizeof(T)) para determinar se um novo bloco de memória precisa ser alocado. A presença dessa comparação e a subsequente chamada de função são fortes indicativos de que estamos lidando com uma operação de push_back em um std::vector.
Ao reconhecer esses padrões – a tríade de ponteiros com offsets específicos e a lógica de push_back com verificação de capacidade – podemos reconstruir a estrutura do vetor e as estruturas que o utilizam com maior clareza. Mesmo com a existência de outros construtores e métodos para std::vector, a compreensão de sua disposição interna na memória é fundamental para a identificação rápida em código compilado. Continuaremos explorando outros contêineres STL populares em publicações futuras. Fique atento e seguro!
📤 Compartilhar & Baixar
📩 Newsletter MundiX
Receba novidades de cibersegurança + um checklist de pentest grátis. Sem spam.
Ao assinar você concorda em receber e-mails. Cancele quando quiser.