Jump to content
  • Editando executáveis com o GNU poke - Parte 1

       (1 review)

    Ano passado eu assisti à uma palestra sobre esse novo utilitário da suíte GNU chamado poke. Ele é um editor de dados binários de linha de comando bem diferente dos que costumo usar (HT Editor, Hiew, etc). Hoje decidi testá-lo e curti bastante. Tá em mega beta, então não tá nem perto de ter pacote disponível nos repositórios oficiais das distros Linux, mas consegui compilar e neste artigo vou dar as instruções, que podem variar em cada ambiente, até porque o poke está em constante desenvolvimento. Usei um ambiente Debian testing aqui.

    Instalando as dependências

    A dependência mais chatinha de instalar foi a gettext, porque o pacote pronto dela não foi suficiente. Então tive que clonar e compilar:

    $ sudo apt install perf fp-compiler fp-units-fcl groff build-essential git
    $ git clone https://git.savannah.gnu.org/git/gettext.git
    $ cd gettext
    $ ./gitsub.sh pull
    $ ./autogen.sh
    $ ./configure
    $ make
    $ sudo make install

    Com a gettext instalada, agora podemos partir para as demais dependências do poke:

    $ sudo apt install build-essential libgc-dev libreadline-dev flex libnbd-dev help2man texinfo

    Só então podemos seguir para a compilação do poke.

    Compilando o poke

    $ git clone git://git.savannah.gnu.org/poke.git
    $ cd poke
    $ ./bootstrap
    $ ./configure
    $ make
    $ sudo make install

    Criando links para as bibliotecas

    Como instalei as bibliotecas do poke em /usr/local e o meu sistema não tinha este diretório configurado para que o loader busque as bibliotecas, precisei criar dois links para elas em /usr/lib:

    $  sudo ln -s /usr/local/lib/libpoke.so.0 /usr/lib/libpoke.so.0
    $  sudo ln -s /usr/local/lib/libtextstyle.so.0 /usr/lib/libtextstyle.so.0

    Sei que há outras maneiras de resolver isso, mas fiz assim pra acelerar, afinal eu queria mexer no poke logo! ?

    Abrindo um binário PE no poke

    Baixei o executável do PuTTY para brincar um pouco e abri assim:

    $ poke putty.exe
         _____
     ---'   __\_______
                ______)  GNU poke 0.1-beta
                __)
               __)
     ---._______)
    
    Copyright (C) 2019, 2020 Jose E. Marchesi.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.
    
    Powered by Jitter 0.9.212.
    Perpetrated by Jose E. Marchesi.
    
    hserver listening in port 47209.
    
    For help, type ".help".
    Type ".exit" to leave the program.
    (poke)

    Gerenciando os arquivos abertos

    O poke permite trabalhar com múltiplos arquivos de uma vez. Você pode ver a lista de arquivos abertos com o seguinte comando:

    (poke) .info ios
      Id	Mode	Size		Name
    * #0	rw	0x0010b990#B	./putty.exe

    ios signifca "IO Spaces". Não tem nada a ver com o SO da Cisco ou com o da Apple. hehe

    Se quiser abrir outro arquivo, pode usar o comando .file <arquivo> e aí pode selecionar em qual você quer trabalhar com o comando .ios #n onde n é o número que identifica o arquivo, mas vou seguir o artigo com somente um arquivo aberto mesmo, então só teremos a tag #0.

    Dumpando dados

    Um dos principais comandos do poke é o dump (perceba este não começa com um ponto) que basicamente visualiza o conteúdo do arquivo, mas este tem várias opções. Vamos à mais básica:

    poke_dump.png.a306fee9e691569c325d5357f46cffc3.png

    A primeira linha na saída acima é só uma régua pra te ajudar a encontrar os bytes.

    Fiz questão de colar uma captura de tela aí acima pra você ver que o poke colore a saída, mas nos exemplos seguintes vou colar a saída em texto pelo bem da sua largura de banda. ?

    Por padrão, o dump exibe 128 bytes do arquivo, começando do seu primeiro byte. O número de bytes pode ser alterado na própria linha de comando:

    (poke) dump :size 64#B
    76543210  0011 2233 4455 6677 8899 aabb ccdd eeff  0123456789ABCDEF
    00000000: 4d5a 7800 0100 0000 0400 0000 0000 0000  MZx.............
    00000010: 0000 0000 0000 0000 4000 0000 0000 0000  ........@.......
    00000020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00000030: 0000 0000 0000 0000 0000 0000 7800 0000  ............x...

    A sintaxe pode parecer um pouco estranha no início, mas você acostuma rápido. O sufixo #B diz que a unidade usada é bytes. Você pode testar outros valores como 2#KB ou 1#MB por exemplo.  ?

    Dumpando a partir de posições específicas

    Para dumpar a partir de uma posição específica, podemos usar a opção :from do comando dump:

    (poke) dump :from 0x30#B :size 32#B
    76543210  0011 2233 4455 6677 8899 aabb ccdd eeff  0123456789ABCDEF
    00000030: 0000 0000 0000 0000 0000 0000 7800 0000  ............x...
    00000040: 0e1f ba0e 00b4 09cd 21b8 014c cd21 7468  ........!..L.!th

    No comando acima eu pedi para o poke me mostrar 32 bytes a partir da posição 0x30. Seria o equivalente a fazer hd -n 32 -s 0x30 <arquivo>.

    O poke mantém um ponteiro de leitura no arquivo, por isso se você comandar somente dump novamente, o dump ocorrerá a partir da última posição lida (no caso, 0x30). Se quiser voltar o ponteiro para a posição zero, é a mesma sintaxe: dump :from 0#B.

    Interpretando dados

    O dump sempre te entrega uma saída em hexadecimal, mas e se quisermos interpretar os dados e exibi-los de maneiras diferentes? Para  isso a gente larga de mão o comando dump e começa a operar com o jeito do poke de ler e interpretar especificamente, assim:

    (poke) byte @ 0#B
    77UB

    O sufixo UB significa Unsigned Byte.

    Se eu quiser a saída em hexa por exemplo, basta eu setar a variável obase (output base):

    (poke) .set obase 16
    (poke) byte @ 0#B
    0x4dUB

    Eu poderia querer ler 2 bytes. Tranquilo:

    (poke) byte[2] @ 0#B
    [0x4dUB,0x5aUB]

    Posso interpretar o conteúdo como número também:

    (poke) uint16 @ 0#B
    0x4d5aUH

    O prefixo UH significa Unsigned Half (Integer). Perceba que o poke sabe que um uint16 tem 2 bytes e por isso os lê sem a necessidade que especifiquemos o número de bytes a serem lidos.

    À essa altura você já sacou que equivalentes aos tipos padrão da linguagem C (da inttypes.h na real) estão disponíveis para uso né? Fique à vontade pra testar off64, int64, int32, etc.

    Lendo strings

    Além dos tipos numéricos, o poke tem o tipo string, onde ele lê até encontrar um nullbyte:

    (poke) dump
    76543210  0011 2233 4455 6677 8899 aabb ccdd eeff  0123456789ABCDEF
    00000000: 4d5a 7800 0100 0000 0400 0000 0000 0000  MZx.............
    00000010: 0000 0000 0000 0000 4000 0000 0000 0000  ........@.......
    00000020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00000030: 0000 0000 0000 0000 0000 0000 7800 0000  ............x...
    00000040: 0e1f ba0e 00b4 09cd 21b8 014c cd21 5468  ........!..L.!Th
    00000050: 6973 2070 726f 6772 616d 2063 616e 6e6f  is program canno
    00000060: 7420 6265 2072 756e 2069 6e20 444f 5320  t be run in DOS
    00000070: 6d6f 6465 2e24 0000 5045 0000 4c01 0700  mode.$..PE..L...
    
    (poke) string @ 0x4d#B
    "!This program cannot be run in DOS mode.$"

    Patch simples

    Vamos fazer um patch simples: alterar o "T" desta string acima de maiúsculo para minúsculo. Basicamente é só colocar à esquerda o jeito que acessamos uma determinada posição do arquivo e igualar ao que a gente quer. Sabendo que para converter maiúsculo para minúsculo na tabela ASCII basta somar 32 (0x20), podemos fazer:

    (poke) byte @ 0x4e#B = 0x74

    Perceba que fui na posição 0x4e, porque na 0x4d temos o '!' e não o 'T'. Só pra checar se funcionou:

    (poke) string @ 0x4d#B
    "!this program cannot be run in DOS mode.$"
    (poke)

    Legal né? Mas dá pra ficar melhor. O poke suporta char, então podemos meter direto:

    (poke) char @ 0x4e#B = 't'
    (poke) string @ 0x4d#B
    "!this program cannot be run in DOS mode.$"

    Por hora é só. Fica ligado aí que postarei a parte 2 em breve, onde vou mostrar mais recursos do poke que tô achando bem úteis para engenharia reversa. Até lá! ?


    User Feedback

    Join the conversation

    You can post now and register later. If you have an account, sign in now to post with your account.

    Guest


  • Similar Content

    • By paulosgf
      Pessoal,
      recebi recentemente esta referência de técnicas anti-debug atualizadas por uma fonte confiável, que é a empresa Check Point.
      Não cheguei a olhar ainda, mas achei interessante de compartilhar logo com os colegas, por ser um tema de grande relevância na área de engenharia reversa.
      Abraços!
      https://research.checkpoint.com/2020/cpr-anti-debug-encyclopedia-the-check-point-anti-debug-techniques-repository/
       
    • By Leandro Fróes
      Se você é da área de Segurança da Informação ou simplesmente tem interesse pelo assunto já deve ter notado que todo dia temos notícias de novos malwares surgindo, sejam eles malwares completamente novos ou variantes de um malware já conhecido. Com isto em mente, faz algum tempo que as empresas de segurança, inteligência e até mesmo pesquisadores independentes passaram a buscar métodos de automatizar não só a análise destes malwares, mas também a administração e armazenamento do arquivo em si, suas características e relacionamentos com outros arquivos demais entidades (domínios, campanhas, endereços IP, etc). Obviamente a análise automatizada não substitui a análise humana, mas já é uma ajuda e tanto considerando o número de malwares surgindo diariamente.
      Para cada uma destas necessidades descritas anteriormente existe uma ou mais ferramentas/plataformas que podem ser utilizadas para cumprir estes objetivos. Dentre elas estão plataformas de sandboxing  como Hybrid-Analysis e AnyRun, ferramentas de análise estática de arquivos como o DIE (Detect It Easy), pev, yara, capa, e também repositórios de malware como o VirusShare e o Malware Bazaar.
      Não podemos negar que todas estas ferramentas/plataformas ajudam e muito no nosso dia a dia, mas ainda assim não conseguiríamos organizar nossas informações e centralizá-las em um único lugar de forma automática, tendo em vista que as as soluções descritas acima são isoladas e não conectam umas com as outras de forma nativa. A plataforma que chegou mais próximo de atingir as quatro exigências (isto é: análise automatizada, administração, armazenamento, relacionamento com demais entidades) foi uma plataforma chamada Virus Total, também conhecido como VT, atualmente administrado pelo Google.
      Virus Total
      O Virus Total trouxe para a comunidade uma forma simples e rápida de análise de IoCs (Indicator of Compromise) e também uma API bem simples de se utilizar para fins de automação. Dentre as diversas funcionalidades da plataforma estão inclusas análise estática, checagem de assinatura utilizando uma lista gigantesca de Anti-Virus, descrição das características gerais do IoC e comentários da comunidade. Além disso, ele também possui uma versão paga (bem cara por sinal) onde você pode fazer hunting de malwares utilizando regras de Yara, download de arquivos, buscas baseadas em histórico, visualização gráfica e uma API bem mais robusta e permissiva.
      É importante deixar claro que o termo IoC não se refere apenas à arquivos e seus hash, mas também à URL, domínios e IP. Ou seja, o VT realmente acaba se tornando uma opção super viável para começar qualquer tipo de investigação.
      O cenário atual de Segurança da Informação
      Com o passar do tempo não só a comunidade, mas também o mercado de Segurança da Informação no geral passou a notar que a única forma de se posicionar contra os ataques atuais é através de contribuição. Pelo mesmo motivo que gerou a necessidade de se criar formas automatizadas de análise, a contribuição se mostra cada dia mais que necessária pois ela não impõe limites, muito pelo contrário, ela dá liberdade o suficiente para você contribuir da forma que quiser.
      Um ótimo exemplo que mostra o exercício da contribuição e o quão valioso isto pode ser é o próprio Linux, que desde sua primeira versão foi liberado para receber contribuições e hoje é um dos maiores projetos existentes na área de tecnologia, com milhares de contribuidores ao redor do mundo.
      Com isto em mente, podemos notar uma desvantagem no VT: o espaço para contribuição é limitado.
      Desafios
      Como já comentado anteriormente, as principais funcionalidades são suportadas apenas na versão paga e infelizmente não são todos que podem pagar pelo valor do serviço.
      Um dos principais motivos dessa limitação é fato do código não ser aberto, isto é, estamos presos às funcionalidades que o time do VT disponibiliza. Se o código fosse disponível para a comunidade, resolveríamos tanto o problema monetário quanto a limitação de funcionalidades disponíveis.
      Uma outra porta que seria aberta no cenário descrito acima é a seguinte: Imagine que você, sua empresa, seu time ou um grupo de amigos estão com um projeto em mãos que envolve análise, classificação, categorização ou qualquer tipo de manipulação de malware. Com o código em mãos você teria liberdade de fazer a instalação da plataforma localmente ou em um servidor do qual você controla, limitando o acesso à quem você quiser e como quiser.
      A comunidade
      Tendo estes desafios em mente, a comunidade começou a criar alternativas para resolver alguns problemas encontrados no cenário atual. A ideia do artigo não é de forma alguma dizer que uma plataforma é melhor que outra ou que o Virus Total está errado em trabalhar no modelo que trabalha, muito pelo contrário, o objetivo aqui é mostrar as várias formas que temos de se chegar no mesmo objetivo. Uns mais flexíveis, outros com mais conteúdo disponível, mas todos conseguem te ajudar a chegar no mesmo lugar:
      Saferwall: Este é o projeto mais maduro que temos atualmente quando o assunto é análise automatizada e contribuição da comunidade. Robusto e flexível para ser instalado em  diversos ambientes, o Saferwall consegue entregar informações estáticas de arquivos, detecções baseadas em assinaturas de alguns antivírus, identificações de packers e download dos arquivos submetidos anteriormente. Além disso, o Saferwall possui uma plataforma aberta e que aceita colaboração, além de disponibilizar o código para você instalar onde e como bem entender. Dentre as formas de instalação estão inclusas o minikube (indicado para ambientes de testes), em nuvem utilizando AWS e On-Premise.


      Freki: O projeto Freki foi criado por uma única pessoa, mas não deixa a desejar quando o assunto é funcionalidade e fácil instalação. Com possibilidade de ser instalado utilizando Docker, este projeto possui não só análise estática dos arquivos PE submetidos, mas também disponibiliza sua própria API e puxa informações do VT para garantir que não falte nada.


      Aleph: focando bastante na parte de inteligência, o projeto Aleph entrega para você não só informações estáticas dos arquivos submetidos, mas também análise dinâmica utilizando sandbox, visualização gráfica dos resultados e uma saída em JSON formatada para ser utilizada em backends como Elasticsearch, por exemplo. Além disso, o Aleph também consegue mapear as técnicas utilizadas pelo malware utilizando o MITRE ATT&CK Framework. Eu realmente aconselho você dar uma olhada na palestra da MBConf v3 sobre o Aleph para saber mais sobre o projeto.
       


      A tabela à seguir foi criada para facilitar a visualização das funcionalidades descritas acima. É importante deixar claro que a versão do VT utilizada para a criação da tabela é a gratuita:
       
       
      VirusTotal
      Saferwall
      Freki
      Aleph
      Análise Estática
      ✔️
      ✔️
      ✔️
      ✔️
      Análise Dinâmica
       
      X
       
      ✔️
       
      X
       
      ✔️
       
      Suporte à múltiplos SO
      ✔️
       
      ✔️
       
      X
       
      ✔️
       
      Análise de IoC de rede
      ✔️
       
      X
       
      X
       
      X
       
      Código Aberto
      X
       
      ✔️
       
      ✔️
       
      ✔️
       
      Download de arquivos
       
      X
       
      ✔️
       
      ✔️
       
      ✔️
       
      Instalação local
      X
       
      ✔️
       
      ✔️
       
      ✔️
       
      Controle total do backend
      X
       
      ✔️
       
      ✔️
       
      ✔️
       
      API
       
      ✔️
       
      ✔️
       
      ✔️
       
      X
      Como podemos ver, todos estes projetos são de código aberto, o que permite a seus usuários livre contribuição. Caso você tenha interesse em contribuir para alguns desses projetos, aqui vai uma dica: nenhum deles possui ainda análise de URL/IP/domínio de forma isolada, isto é, independente do arquivo. Tenho certeza que uma contribuição deste tipo seria bem vinda. ?
      Conclusão
      Ajudando estes projetos nós não só melhoramos a ferramenta/plataforma em si, mas ajudamos todos que a utilizam e também construímos um sistema livre e aberto de análise, inteligência e investigação.
      Se você é da área ou simplesmente curte contribuir, não deixe de dar uma olhada em cada um destes projetos e, se possível, contribuir com eles. Lembrando que quando falamos de contribuição, não há limites. Pode ser um commit, uma ideia, ajuda monetária ou um simples OBRIGADO aos desenvolvedores e contribuidores por disponibilizarem projetos tão úteis para a comunidade.
    • By julio neves
      Livro do Julio Cezar Neves com dicas importantes (e raras de serem encontradas) sobre shell, incluindo sincronismo de processos, novidades do Bash 4.0, uso do ImageMagik e YAD (o melhor da categoria dos dialog da vida). Vale ler cada palavra. ?
       
      E se quiser ver se tem turma aberta do curso dele é só clicar aqui. ?
    • By l0gan
      Em todos os sistemas operacionais existem arquivos estruturados. Imagine um bloco segmentado em diversas partes e cada uma sendo uma área que armazena um tipo de dado específico (ex.: cabeçalho, área de código, área de dado inicializado, área de dado estático, área de dado não inicializado, área de referência de definições externas/outros objetos) servindo de referência para resguardar determinada classe de dado do respectivo arquivo binário para serem usados durante a execução do software ou até mesmo para fornecer informações que ajudam no processo de debugging. O conceito dessa formatação do arquivo (file format) é presente em todos os sistemas operacionais populares como Windows e Unix-like – isso inclui o macOS.
      Sabendo que o macOS é um sistema operacional do Unix é de se esperar que seus arquivos binários também tenham um “formato”, e estes são conhecidos como “arquivos de objeto do Mac” ou simplesmente Mach-O. Com esse entendimento o propósito deste artigo é dar uma visão técnica geral sobre a estrutura de arquivos construídos com este formato.
       
      Por que é importante conhecer o formato Mach-O?
      Algumas pessoas acreditam que o sistema operacional macOS (atualmente na versão denominada Catalina) é mais seguro que outros sistemas operacionais existentes pelo fato de não ser afetado por malware. Grande engano! Atualmente, vemos muitas publicações de vulnerabilidades relacionadas ao macOS, o que demonstra que este sistema operacional é, sim, um alvo em potencial.
      A grande pergunta que sempre faço é: “O que é mais interessante para um criminoso?”. Neste contexto, por “criminoso” me refiro à qualquer indivíduo que se utiliza dos meios eletrônicos para cometer fraudes. Deixando dispositivos móveis de lado, minha opinião é que hajam duas alternativas principais:
      Infectar o maior número de hosts possível (Windows ou Linux); Infectar um número mais restrito de hosts, porém algo mais direcionado a usuários, em geral, de cargos executivos, por exemplo: Diretores, CSO, etc. ou usuários domésticos, que muitas vezes permitem que softwares de fonte desconhecida sejam executados livremente em seu sistema operacional, ao desativar mecanismos de segurança como o gatekeeper; Se eu fosse um criminoso, optaria pela segunda opção; pois, atualmente o MacBook está se tornando cada vez mais popular.
      A imagem abaixo nos mostra a grande quantidade de arquivos Mach-O que foram analisados no VirusTotal nos últimos 7 dias desde a escrita deste artigo:

      Estes são os tipos de arquivos submetidos ao VirusTotal nos últimos 7 dias, obtidos em 25/julho/2020.
      Repare que a imagem não reporta arquivos infectados, mas sim os binários de cada tipo analisados. Bom, é perceptível que Mach-O está ganhando uma certa predominância hoje em dia, embora ainda seja bem inferior ao número do arquivo executável do Windows (Win32.exe).
      Apenas a título de curiosidade, o Mach-O tem um formato multi arquitetura, também conhecido como “fat binary” (conforme podemos ver na imagem abaixo)  aonde ele suporta 3 tipos de arquiteturas diferentes: x86_64, i386 e ppc7400:


      Aqui temos uma tabela com todos os “Magic Number” (valor numérico de texto usado para identificar um formato de arquivo) referentes à binários do tipo Mach-O:

      Ainda nesta linha de pesquisa, a técnica utilizada para gerar um binário suportado com várias plataformas (cross-compiling) é demonstrada na imagem  abaixo utilizando o compilador gcc:

      Usando o comando file do macOS vemos o tipo do arquivo e a arquitetura da plataforma que é suportado:

      O formato Mach-O de 64-bits
      Conforme observado anteriormente os binários Mach-O tem três regiões principais: Cabeçalho (Header); Comandos de carregamento (Load Commands); e, Dados (Data). A imagem abaixo representa a estrutura básica dos arquivos Mach-O 64-bit:

      No Header, encontram-se especificações gerais do binário, como seu magic number e a arquitetura alvo. Podemos encontrar este header em /usr/include/mach-o/loader.h:

      Conhecendo um pouco mais a estrutura do mach header podemos notar que ela é composta por 8 membros, cada um possuindo 4 bytes, ou seja: 4 * 8 = 32. Podemos ver os primeiros 32 bytes do binário, isto é, os valores do header abaixo:


      A região Load Commands especifica a estrutura lógica do arquivo e informações para que o binário possa ser carregado em memória e utilizado pelo sistema. Ela é composta por uma sequência de diversos modelos de commands numa tupla, por exemplo: “[load_command, specific_command_headers]” -- definindo as diferentes “seções lógicas” (commands) do binário. Cada command necessita de um ou mais cabeçalhos específicos, por isso, o segundo membro da tupla (specific_command_headers) pode variar de acordo com o tipo de command da mesma em questão:

      A título de exemplo, podemos ver também o command LC_SEGMENT_64  do cabeçalho do binário Mach-O:

      Neste mesmo contexto, podemos ver que as bibliotecas dinâmicas (dylib) "libncurses" e "libSystem" foram carregadas nos commands 12 e 13, que pertencem ao cabeçalho LC_LOAD_DYLIB.
      Deste jeito, o kernel consegue mapear as informações do executável para um espaço de memória que pode ser acessado simultaneamente por múltiplos programas na finalidade de prover comunicação entre eles ou para evitar compartilhamento de dados supérfluos – tal conceito é conhecido como memória compartilhada:

      Podemos ver também que a section __text contém o segmento __TEXT:

      E por fim temos a Data, onde temos instruções armazenadas logo após a região LOAD_Commands. Na região Data é que são definidas as permissões de leitura e gravação. Dependendo do tipo de Mach-O a maneira como essa região é usada varia.
      Quando analisamos um binário um dos primeiros pontos para o início dos testes é a inspeção do binário em um debugger a partir de seu entrypoint. No caso do deste Mach-O que estamos analisando percebemos que o código é colocado na seção __TEXT, as bibliotecas são carregadas no cabeçalho LC_LOAD_DYLIB e o LC_MAIN é o cabeçalho que aponta para o ponto de entrada (entrypoint) :


      Por enquanto já temos uma noção básica da estrutura dos binários Mach-O. Em um próximo artigo, iremos detalhar melhor este binário com foco em engenharia reversa para identificar ações de software malicioso.

      Para ajudar, recomendo a você artigos da H2HC Magazine sobre pilhas, registradores etc., dos colegas Fernando Mercês, Ygor da Rocha Parreira, Gabriel Negreiros, Filipe Balestra e Raphael Campos nas edições 7, 8, 9, 10 e 11. Outra referência para auxiliar nesta análise é o artigo "Montando sua máquina virtual para engenharia reversa em macOS"[11].

      Até lá!

      Referências
      Palestra H2HC University Vídeo Demo Malware Keranger Mach-O Vídeo Demo Crackme Mach-O Calling Conventions OS X ABI Mach-O File Format Revista H2HC ed7 Revista H2HC ed8 Revista H2HC ed9 Revista H2HC ed10 Revista H2HC ed11 Montando sua máquina virtual para engenharia reversa em macOS
×
×
  • Create New...