Jump to content
  • Montando sua máquina virtual para engenharia reversa em macOS

       (0 reviews)

    l0gan

    Complementando os artigos criados sobre máquina virtual para ambiente Windows e Linux, este tutorial tem como finalidade auxiliar na criação de uma máquina virtual para análise de binários, possivelmente maliciosos, em ambiente macOS

    Configurações da Máquina Virtual

    • 2 processadores (cores).
    • 2GB de RAM.
    • Placa de rede em modo NAT (em casos aonde você realmente precisa de comunicação com um C&C).
    • Placa de rede em modo Host-Only.
    • Compartilhamento de Pastas desativado ou Host-Only (com host local).

    Aqui vem um ponto interessante: como tenho receio de malwares que detectam o ambiente de virtualização (ex: VMware Fusion) e tentam escapar do guest pro host, rodo sempre o SO guest num SO host diferente. No caso, rodo a máquina virtual com macOS mas o SO host é Linux.

    Sistema Operacional virtual

    • 10.10.1 (Yosemite) publicado em 2014
    • 10.13.4 (High Sierra) Versão Atual

    Obs.:  As duas versões do macOS mencionados acima são para demonstrar a tela de configuração do Gatekeeper de cada versão. A importância das versões está nos diferentes tipos de malware  que podem se propagar em versões específicas. No entanto, basta escolher uma.

    O Gatekeeper é um componente de proteção para o macOS existente desde a edição Mountain Lion. A responsabilidade deste constituinte é encontrar a identificação do desenvolvedor (Developer ID, também conhecido como “assinatura de autenticidade”), que é fornecido pela própria Apple. Quando em conformidade, o Gatekeeper mantém-se adormecido até o momento do cujo arquivo executável ou instalador ser flagrado com a assinatura de autenticidade ausente ou por ser reconhecido pela semelhança de algum tipo de malware.

    Uma vez que você utiliza softwares baixados através da App Store e ou assinados pela Apple você já possui uma certa segurança. Tendo consciência que boa parte dos últimos malwares para macOS dependiam que este recurso estivesse desativado, consequentemente, permitindo o download e instalação de qualquer software não identificado.

    Desativando o Gatekeeper

    gatekeeper_yosemite.thumb.png.922cb2f9a8cc0d5579c7a51b3732bee2.png

    Imagem 1: versão Yosemite

     No macOS Sierra e posterior a opção Anywhere não aparece mais, agora o sistema operacional perguntará para o usuário se ele deseja permitir que o software realmente seja instalado / executado no sistema. Porém há maneira de desabilitar o Gatekeeper e voltar com a opção como mostra na Imagem 1, usando o spctl (SecAssessment system policy security), via Terminal:

    $ sudo spctl --master-disable

    gatekeeper_high-sierra.thumb.png.066fff24592d56a755b41c4341605468.png

    Imagem 2: versão high-sierra

    Outro sistema de segurança é o SIP (System Integrity Protection). Eu ainda não vi nenhuma necessidade de desativar para rodar malware porem caso precisem:

    SIP (proteção de integridade do sistema)

    1. Clique no menu apple.png.5c7e8390fe4b6f267080d165316a3c39.png
    2. Selecione Reiniciar ...
    3. Mantenha pressionado o comando-R para inicializar no sistema de recuperação.
    4. Clique no menu Utilitários e selecione Terminal.
    5. Digite csrutil disable e pressione Enter.
    6. Feche o aplicativo Terminal.
    7. Clique no menuapple.png.5c7e8390fe4b6f267080d165316a3c39.png  e selecione Reiniciar ....

    Ferramentas

    No macOS além da própria Apple Store (que com certeza neste caso não terá as principais ferramentas que precisamos), também temos algumas boas fontes de ferramentas. O MacPorts, sistema de pacotes muito utilizado, e também o Homebrew suprem muito bem nossas necessidades quanto aos pacotes.

    Abaixo deixei um lista de ferramentas tanto para análise estática quanto dinâmica, claro que em alguns casos a mesma ferramenta pode ser utilizada em ambos os tipos de análise.

     

    Analise Estática

    xxd                 -> Cria um dump a partir de um binário, parecido com o hexdump.
    strip               -> Remove e ou modifica a tabela de símbolos de um binario.
    hexEdit         -> Editor hexadecimal.
    lipo                 -> Cria ou modifica arquivos multi-arquitetura, na imagem 6 tempos um exemplo da sua funcionalidade.
    otool              -> Exibe informações sobre binários Mach-O (tipo um objdump).
    jtool                -> Versão melhoradas do otool.
    nm                   -> Exibe a tabela de símbolos.
    codesign       -> Usado para criar, verificar e exibir assinaturas de código.
    machOView -> Interface visual para edição de binários mach-o. 
    class-dump -> Usado para examinar informações em tempo de execução do Objective-C armazenadas em arquivos Mach-O.
    dtrace             -> Ferramenta usada para analisar comportamento do Sistema Operacional e dos programas em execução.
    fs_usage       -> Exibe informações sobre chamadas de sistemas, executa rastreamento do kernel e processos, tudo em real-time.
    xattr                 -> Usado para exibir, modificar e ou remover atributos(metadados) de arquivos, diretórios e links simbólicos.

    Analise Dinâmica

    xcode                         -> IDE de desenvolvimento de software oficial da apple, possui recursos internos para testes de perfomance de sistema.
    hopper                      -> Ferramenta usada para disassemble e decompile de arquivos mach-o 32/64bits.
    lldb                             -> Debugger utilizado para depurar programas C, C ++, Objective-C, Objective-C ++ e Swift.
    fseventer                 -> Ferramenta gráfica usada para verificar atividades em disco e execução de processos de forma visual.
    open snoop             -> Usado para rastrear acessos de arquivos, aplicativos, processos e também monitoramento do 
                                              filesystem, você pode utilizar ele em conjunto com o Dtrace.
    activity Monitor    -> Exibe processos que estão sendo executados no macOS.
    procexp                    -> Ferramenta exibe informações acessíveis pelo proc_info para exibição de processos, parecido com o top e htop.
    lsock                          -> Baseado no PF_SYSTEM o lsock e usado para visualização em real time das conexões (Sockets) no sistema, similar ao netstat.

    Por se tratar de máquina virtual para pesquisas em macOS, não poderia deixar de mencionar as ferramentas do pessoal da Objective-See. Vale a pena testar e acompanhar os artigos deles.

     Objective-see.thumb.png.91b9fe1c3a2b4615ce1d14c65d9c1b25.png

    Imagem 3: ferramentas objective-see

     Destaco três ferramentas especificas para quem estiver analisando binários do tipo Mach-O:

    otool.thumb.png.bde548a20ee70720c53befa4936af166.png

    Imagem 4: ferramenta otool exibindo o magic number do binário.

    jtool.thumb.png.ceb11cf8c56559cfe3ea6a5b1f1f5b68.png

    Imagem 5: ferramenta jtool exibindo o endereço da função main() do binário Mach-O

     lipo.thumb.png.15744ec57a72edc389da48836cbcfacb.png

    Imagem 6: ferramenta lipo extraindo o suporte a uma arquitetura especifica em binários do tipo fat.

    Considerações finais

    • Criar um snapshot da instalação default;
    • Ficar atento:
      • Anti-Disasssembly
      • Anti-Debugging
      • Sistemas de Ofuscação
      • Anti-VM
      • Ficar atento às falhas publicadas

     

    • Agradecer 1
    • Curtir 1


    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 lucass
      Vou começar agradecendo ao @Fernando Mercês pela oportunidade e por ter sugerido este artigo, que também me motivou bastante a escrevê-lo!
      Introdução
      Não sou conhecido internet a dentro, apenas acompanho alguns canais no Discord (tal como o do Mente Binária). Meu nível de programação e engenharia reversa não é algo admirável ainda. Em um grupo especifico intitulado "Terra do 1337", que é um grupo fechado de amigos com finalidade de estudar engenharia reversa, programação e descontrair, eu surgi com uma idéia de escrever uma ferramenta que iria facilitar a vida de muitos nesta área de engenharia reversa e achei de API Inspector.
      A seguir um spoiler de como foi o início do projeto, para quem se interessar. 😉
      O que é o API Inspector
      É uma ferramenta de código-aberto voltada para área de engenharia reversa, que irá auxiliar na análise de funções correspondentes a certas API's do Windows, retornando informações obtidas dos argumentos caso a função seja chamada pela aplicação.
      O que ele faz
      Ele faz um hook (do Inglês "gancho"), que consiste num desvio na função original da API solicitada para nossa própria função e com isso podemos obter os dados (argumentos/parâmetros) que foram passados para tal função.
      Como ele funciona
      O princípio de um hook é simples: você insere no inicio da função um salto que irá levar para a sua função (que é uma cópia da função original) e depois de efetuar o que quiser, irá retornar para a função original prosseguir.
      Talvez mais fácil visualizar o que expliquei com código:
      //Aqui está a função //ZwWriteVirtualMemory | NtWriteVirtualMemory, originada do binário: ntdll.dll //créditos ao https://undocumented.ntinternals.net/ NTSYSAPI NTSTATUS NTAPI //WINAPI NtWriteVirtualMemory( IN HANDLE ProcessHandle, IN PVOID BaseAddress, IN PVOID Buffer, IN ULONG NumberOfBytesToWrite, OUT PULONG NumberOfBytesWritten OPTIONAL ); //Sua versão assembly 777F2110 mov eax,0x3A 777F2115 mov edx,ntdll.77808D30 777F211A call edx 777F211C ret 0x14 //O que nós vamos fazer é criar uma função similar á ela com o nome que decidirmos //Então vamos inserir um jmp no início da função original para nossa função, ficando assim: 777F2110 jmp api inspector.573523EC 777F2115 mov edx,ntdll.77808D30 777F211A call edx 777F211C ret 0x14 //Usei como exemplo minha próprio ferramenta! //Então quando ocorrer a chamada desta função ela será jogada em nossa função! Depois de nós fazermos que desejar vamos retorna-la, porém para uma região que aloquei onde contém //Um buffer dos bytes que foram sobrescritos da função original: 03610000 mov eax,0x3A 03610005 jmp ntdll.777F2115 //Ela irá retornar depois do jmp que existe na função original e continuar o código.... Vantagens de se utilizar o API Inspector ao invés de um debugger
      Imagine que você está visualizando as chamadas intermodulares (para bibliotecas externas, no caso) que um programa faz, utilizando um debugger (o x64dbg por exemplo) e notou que uma certa função que deseja inspecionar é chamada em diversos pontos do programa. Vejo duas opções neste caso: colocar vários breakpoints, um em cada chamada à função, no código do programa ou colocar um único breakpoint função em si, no código dela, na DLL.
      Em ambos os casos, você vai precisar analisar chamada por chamada, parâmetro por parâmetro. E se a função for chamada 20 vezes consecutivamente? O tempo que você levaria para visualizar apenas o primeiro parâmetro da chamada é o tempo que a ferramenta iria levar para exibir todas as 20 chamadas, com os argumentos formatados bonitinhos ao seu dispor. Entende a vantagem? 🙂
      E as desvantagens?
      Por hora, uma desvantagem é a quantidade de funções e API's suportadas. De fato, a primeira release não possui uma quantidade significativa que vá fazer você utilizar a ferramenta e nem uma quantidade de recursos interessantes na ferramenta. Mas é ai que vem o ponto chave, o fato de deixar ela pública remete ao próprio crescimento da mesma, no primeiro momento é necessário uma orientação da parte de vocês para me ajudar a melhorar o código visual. O segundo passo é eu e vocês começarem a fornecerem mais recursos para ela. Eu irei adicionar todo ou qualquer recurso que seja significativo para a mesma, e para isso eu já tenho mais funcionalidades para implementar na ferramenta que são excelentes.
      Interface gráfica
      Na imagem abaixo, utilizei o API Inspector para hookar a função MessageBoxW() da USER32.DLL. Depois disso, escrevi um texto num novo arquivo no Notepad++ e tentei fechar o programa. Ao fazer isso, o Notepad++ perguntou se eu queria salvar o arquivo e ele faz isso através de uma chamada à MessageBoxW(), que o API Inspector interceptou prontamente.

      Na imagem acima, a janela à esquerda mostra o que está atualmente passando pelas funções hookadas. Na janela a direita, temos um log.
      Como utilizar o API Inspector
      A única coisa que você precisa fazer é anexar a DLL do API Inspector ao processo desejado e para isso existem os softwares chamados "Injetores de DLL" que podem ser achados na internet.
      Você também pode criar o seu próprio injetor. Uma dica é pesquisar sobre injeção com a função LoadLibrary(), mas no exemplo a seguir eu vou mostrar como utilizar o Process Hacker para fazer a injeção.
      1 - Abra o Process Hacker e identifique no mesmo o processo no qual você quer injectar a DLL do API Inspector. No exemplo, usei o processo do Notepad++.

      2 - Clique com o botão direito sobre o processo e escolha Miscellaneous > Inject DLL.

      3 - Selecione a DLL API-Inspector.dll e clique em Abrir.

      4 - Se o Process Hacker possuir privilégios suficientes a ferramenta irá ser carregada, caso contrário, não.

      Após isso você precisa selecionar a API desejada, a função desejada e clicar em GO Hook!
      O step call é uma funcionalidade que vai fazer a ferramenta aguardar o pressionamento da tecla ENTER para retornar para a função original. Pronto, o seu hook está feito e você já poderá inspecionar a função desejada.
      Download e código
      No repositório do API Inspector no Github você pode baixar a versão compilada e ter acesso ao código-fonte também. Contribuições são muito bem vindas!
      Bom, eu nunca tinha escrito um artigo. Se faltou informação ou coloquei informação demais me desculpe. Estou aberto pra ler os comentários. Ah, e participem deste projeto! Eu quero fazer ele crescer muito. Caso precise de referências de como cheguei a este projeto, tem tudo na página inicial do projeto no Github.
      Agradecimentos
      Obrigado novamente ao Fernando Mercês, ao pessoal do Terra 1337 que me incentiva cada vez mais e em especial para o iPower e Luan que são colaboradores do projeto.
      Referências
      Dear ImGui Programming reference for the Win32 API NTAPI Undocumented Functions C++ 3D DirectX Programming
    • By ncaio
      ====== Bem-vindo a bordo ======

      Este é um repositório/espaço aberto/livre de conteúdo referente a hardware hacking em geral. Sinta-se a vontade para contribuir e retirar suas dúvidas. Assim como em outros espaços de conhecimento compartilhado na Internet, este Fórum tem regras. Algumas delas, são:
        * Seja educado(a) e respeitoso(a);
        * Pesquise antes;
        * Seja claro(a) e descritivo(a);
        * Esteja preparado(a) para compartilhar informações relevantes a sua dúvida;
        * Não fuja do foco;
        * Referencie autores;
        * E etc.
    • By Fabiano Furtado
      Pessoal...
      Ontem achei um artigo na Internet bem escrito, interessante e detalhado sobre Engenharia Reversa em ELF.
      É um reversing básico, mas não tããããão básico assim. Acho que vale a pena conferir.
      http://manoharvanga.com/hackme/
      Valeu!
    • By Ciro Moises Seixas Dornelles
      Olá a todos, existe alguma maneira de se extrair o conteúdo do livro de engenharia reversa para que eu posso lê-lo em um dispositivo kindle?

       
    • By Candeer
      Olá, já faz um bom tempo desde do ultimo artigo sobre a construção de debuggers mas, sem mais delongas, vamos dar continuidade a esta série! 😀 
      Neste artigo iremos falar um pouco sobre uma chamada de sistema que é capaz de controlar quase todos os aspectos de um processo: a syscall PTRACE (process trace). Antes de continuarmos, vale ressaltar que todo o código utilizado neste artigo está disponível no repositório do Github.
      De acordo com o manual do Linux (man ptrace), a syscall ptrace é definida assim:
      "A syscall ptrace provê meios para que um processo (denominado "tracer") possa observar, controlar a execução de um outro processo (denominado "tracee"), examinar e modificar a memória e registradores do "tracee". É primariamente utilizado para a implementação de 'breakpoint debugging' e para rastreamento de syscalls".
      Em outras palavras, podemos utilizar a ptrace para controlar um outro processo sobre o qual termos permissões sobre!
      Por exemplo, execute:
      strace /bin/ls O comando "strace" acima, é utilizado para que se possa rastrear todas as syscalls que um programa realiza. Vale lembrar que toda a técnica utilizada para o rastreamento de syscalls envolve o conteúdo abordado nos artigos anteriores, então é de suma importância que você tenha lido (ou saiba) o primeiro artigo sobre Sinais e o segundo sobre Forks.
      Antes de começar a rastrear um dado comando, o strace precisa ter controle total sobre a execução do processo alvo, para isso é feito um fork do processo em questão e o mesmo é "traceado". Voltaremos neste assunto em breve.
      A wrapper da ptrace é definida em <sys/ptrace.h> e tem o seguinte protótipo:
      #include <sys/ptrace.h> long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data); Onde o primeiro argumento request é um enum onde cada valor define uma ação em cima do "tracee", tais como TRACEME, GETEREGS, SETREGS e etc. O segundo argumento, pid, é o PID (Process Identification) do processo que queremos "tracear", o terceiro argumento addr é um endereço para alguma interação que a ser realizada da memória do processo "traceado" e o quarto e último argumento data é algum tipo de dado passado para o processo.
      Agora que você ja conhece o formato desta syscall, vamos fazer um pequeno breakdown do comando "strace".
      Execute:
      strace strace /bin/ls 2>&1 | grep -A2 clone Por mais bizarro que o comando acima pareça, o que vamos fazer aqui é rastrear todas as syscalls que o strace faz usando o próprio strace! Como a saída padrão do strace não é o stdout (dê uma lida em standart streams, caso esteja confuso) então é primeiro redirecionar a saída de erro para a saída padrão, para que seja possível rodar o grep no que queremos.
      Estamos buscando aqui, alguma chamada a syscall clone, que é sempre chamada quando é feito um fork. A chamada à ptrace vem logo em seguida:
      clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f7c4aa8ea10) = 16203 ptrace(PTRACE_SEIZE, 16203, NULL, 0) = 0 Nesse caso, o strace cria um processo filho e em seguida usa o ptrace com o argumento SEIZE para iniciar o rastreamento (tracing) de um processo sem interrompê-lo, como analisaremos em seguida. Dessa maneira o strace é capaz de interceptar cada chamada de sistema feita pelo processo!
      Dê uma olhada no comando ltrace, que diferente do strace, rastreia todas as chamadas à bibliotecas (libraries trace) e tente fazer o mesmo que fizemos acima!
      Algumas ações notáveis que podemos fazer com a ptrace:
      PTRACE_PEEKTEXT, PTRACE_PEEKDATA Ler uma word em um dado endereço. PTRACE_POKETEXT, PTRACE_POKEDATA Copiar uma word para um determinado endereço (injete dados na memória). PTRACE_GETREGS Ler os registradores de um processo, que será guardado na struct user_regs_struct em <sys/user.h>. PTRACE_SETREGS Escrever nos registradores de um processo (também no formato da struct acima). Execute "man ptrace" para uma abordagem mais detalhadas de todos os valores disponíveis. 👍
       
      Implementando um simples tracer
      Agora que já temos uma base de forks e uma ideia de como o ptrace funciona, podemos unificar os dois e tenho certeza que o ptrace irá ficar mais claro. A partir de agora ele é fundamental para a implementação do nosso debugger.
      O primeiro passo é definir o escopo de como será feito o nosso "tracer": vamos rastrear um processo que já esta sendo executado ou vamos criar um novo? Para o nosso debugger, iremos apenas criar um fork e trocar sua imagem de execução para a do programa que queremos debugar, usando uma das funções da família exec.
      Primeiro vamos usar a função execl, que faz parte do leque de funções exec (man 3 exec) que trocam a imagem do nosso processo por outra, ou seja, o nosso programa é realmente trocado por outro em uma execução.
      A função execl é definida como:
      #include <unistd.h> int execl(const char *pathname, const char *arg, ... /* (char *) NULL */); Onde o primeiro argumento pathname é caminho completo do nosso executável alvo e os demais argumentos, que podem ser vários, são os argumentos para o programa que será executado.
      Para seguir um padrão, o primeiro argumento que geralmente colocamos é o caminho do programa em questão (lembrem que no array argv a posição 0 guarda o nome do programa em si), o resto dos argumentos são opcionais e seguem no modelo de lista de argumentos que são delimitados por um argumento NULL, que geralmente usamos para finalizar a lista.
      Agora considere o seguinte exemplo:
      #include <unistd.h> #include <stdio.h> int main(int argc, char* const* argv) { if (argc < 3) { printf("Usage: %s <command> <args>\n", argv[0]); return 1; } const char* command = argv[1]; char* const* args = &argv[1]; printf("First arg => %s\n", args[0]); execv(command, args); puts("Continua?\n"); return 0; } Compile com
      $ gcc -o exec exec.c $ ./exec /bin/ls -lah Este programa bem simples demonstra como a exec funciona.
      O que acabamos de criar aqui foi uma espécie de wrapper para qualquer comando: ele irá pegar o nome do comando e os seus respectivos argumentos e trocar sua execução atual pela a que você especificou.
      Note também a string "Continue?" que deveria ser impressa na tela. Esta nunca será impressa pois o nosso programa virou de fato, outro.
      Interessante, não? Usando um pouco de criatividade, podemos criar novos processos filhos combinando forks + exec, ou seja, criamos um fork do nosso processo e trocamos sua imagem por outra! Dessa maneira, por exemplo, temos total controle sobre o comando ls.
      Modificando um pouco o código acima e seguindo a ideia de forks, temos:
      #include <stdio.h> #include <sys/types.h> #include <sys/ptrace.h> #include <unistd.h> int main(int argc, char* const* argv) { if (argc < 3) { printf("Usage: %s <command> <args>\n", argv[0]); return 1; } const char* command = argv[1]; char* const* args = &argv[1]; pid_t child_pid = fork(); // Neste ponto, todas as variaveis sao copiadas para o nosso fork // o fork NAO recebe as mesmas variaveis, apenas uma cópia ;) if (!child_pid) { // Hora de transformar nosso fork em outro programa ptrace(PTRACE_TRACEME, NULL, NULL, NULL); execv(command, args); } char in; do { puts("Iniciar processo ? [y/n]: "); in = getchar(); } while (in != 'y'); ptrace(PTRACE_CONT, child_pid, NULL, NULL); return 0; } Compile
      $ gcc -o fork_exec fork_exec. $ ./fork_exec /bin/ls O programa acima realiza os primeiros passos do nosso tracer: é passado o caminho de um programa e os argumentos para o mesmo. Com isso criamos um fork e usamos o ptrace no própio fork com o argumento TRACEME. Este parâmetro indica que o este processo será "traced" pelo seu processo pai. Em seguida trocamos a nossa execução para o nosso programa alvo. Neste momento temos total controle sobre a execução, no exemplo acima, do comando ls.
      Quando um processo inicia sua execução com TRACEME + exec, o mesmo recebe um sinal de interrupção (SIGTRAP) até que o seu processo pai indique que ele deve continuar sua execução. Por isso, o nosso processo pai, que retém o PID do processo filho, usa o ptrace com o argumento CONT para que seja enviado o signal para dar continuidade de execução.
      E depois?
      Agora toda a comunicação entre os processos pai e o filho se dará via sinais e usaremos a syscall wait constantemente.
      Lembra que definimos acima algumas funções que podemos usar em conjunto com a ptrace? Para já irmos adiantando alguns artigos, vamos fazer um programa que mostra o estado dos registradores para um processo, passo a passo. Vamos usar dois parâmetros para a ptrace: GETREGS e STEP. Segue o código:
      #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/ptrace.h> #include <sys/user.h> #include <sys/wait.h> void display_regs(struct user_regs_struct* regs) {     printf("RIP: 0x%x\n", regs->rip);     printf("RBP: 0x%x\n", regs->rbp);     printf("RSP: 0x%x\n", regs->rsp); } int main(int argc, char* const* argv) {     if (argc < 2) {         fprintf(stderr, "Usage: %s <program_path>\n", argv[0]);         return 1;     }     const char* progName = argv[1];          pid_t child = fork();     if (!child) {         ptrace(PTRACE_TRACEME, NULL, NULL, NULL);         execl(progName, progName, NULL);     }          int status;     int options = 0;     int signal;     // Estrutura que mantem os registradores     struct user_regs_struct regs;     /// Capta primeiro sinal de parada do filho     waitpid(child, &status, 0);     signal = WSTOPSIG(status);     if (signal == SIGTRAP) {         printf("Processo alvo %s esperando pronto para iniciar\n\n", progName);     }          printf("Executando 10 instruções\n");     for (int i = 0; i < 10; ++i) {         printf("Passo: %d\n", i+1);         // Executa uma instrução         ptrace(PTRACE_SINGLESTEP, child, NULL, NULL);         // Espera sinal do filho         waitpid(child, &status, 0);         // Copia o estado atual dos registradores         ptrace(PTRACE_GETREGS, child, NULL, &regs);         // Função local para imprimir os principais registradores         display_regs(&regs);         puts("\n\n");     }     puts("Continuando...\n");     /// Continua execução     ptrace(PTRACE_CONT, child, NULL, NULL);     waitpid(child, &status, 0);     printf("Filho saiu com %d\n", WIFEXITED(status));     return 0; }  
      Compile:
      $ gcc -o tracer tracer.c $ ./tracer /bin/ls O código acima, além de criar e rastrear o processo, executa as primeiras 10 instruções e copia os estados dos registradores em cada passo. Logo após, continua a execução do programa normalmente.
      A estrutura user_reg_struct, definida em <sys/user.h>, contém todos os registradores que estão disponíveis na sua arquitetura. O código foi escrito considerando um ambiente x86-64.
      Com o estudo da ptrace, fechamos toda a introdução para construirmos o nosso debugger de fato, que vamos começar a desenvolver no próximo artigo, incialmente com capacidade de por breakpoints, imprimir o atual estado dos registrados e executar instrução por instrução do processo.
      Qualquer dúvida ou correção sinta-se livre de por nos comentários!  😁
      Links úteis:
      Process control Process relationship Code injection with ptrace Sinais Fork Até a próxima!
×
×
  • Create New...