Jump to content
  • Sign in to follow this  

    Artigos


    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!

    Fernando Mercês
    Essa é uma história verídica: Tive o celular (um iPhone 7) furtado no último fim de semana e houve tentativas, afortunadamente sem sucesso, de hackear minhas contas. Nesse artigo vou explicar como me protegi, na esperança de que você se proteja também. Isso antes que tenha o celular roubado, furtado ou perdido. 😌
    Após perceber que meu celular não estava no meu bolso comecei a dar valor a todas as ações de proteção que tomei. Consegui ligar para o Nubank, que cancelou o cartão (pois é, eu colei aquele lindo porta cartão roxo que eles mandam na traseira do celular e por isso foi tudo junto: celular e cartão de crédito). Por um momento pensei que para cancelar o cartão do Nubank eu precisaria do aplicativo do mesmo, mas eles possuem um número que podemos ligar. E você consegue cancelar seu cartão após confirmar alguns dados. Nota importante: você precisa estar sóbrio o suficiente para confirmar seus dados. Não entrarei em detalhes aqui como fiz.
    O telefone estava bloqueado com um código numérico de 6 dígitos (padrão nas últimas versões do iOS). E não era nada do tipo "000000" ou "123456". Dá um negócio escrever isso. Na verdade, gostaria de escrever que meu código era alfanumérico (que é mais seguro, em geral), mas não era. Mesmo assim, era "aleatório" o suficiente, creio eu, então confiei que o ladrão não conseguiria desbloquear o aparelho. Como todos os dedos de ambas as minhas mãos estavam no lugar, achei que o ladrão também não tinha minhas digitais para tentar o Touch ID. 🙂
    Ao chegar em casa, fiz login no iCloud utilizando meu Apple ID para usar o Find My iPhone, que estava ativado. A última localização conhecida foi onde eu estava mesmo, ou seja, o danado desligou o telefone, removeu o chip ou fez alguma outra coisa para que este ficasse sem sinal.
    Também recebi o seguinte e-mail do Facebook:

    E-mail do Facebook para confirmação de alteração de senha
    Ou seja, o abençoado tentou resetar minha senha do Facebook. Como? Bom, o aplicativo do Facebook para iOS não pede senha ao abrir, então deduzi que o ladrão não tinha conseguido desbloquear o telefone. Cliquei no link "avise-nos" destacado no fim da mensagem, verificando que era do Facebook mesmo, só para bloquear novas tentativas.
    Daí vem minha primeira hipótese: o ladrão queria tomar minha conta do Facebook. Como no Facebook é possível logar via número de telefone, imagino que ele tenha removido o chip do telefone, posto em outro para ver o número e tentado resetar a senha pelo site do Facebook, bastando apenas para isso ter o meu número:

    Tela de recuperação de senha por número de telefone no Facebook (não é exigido nome de usuário)
    Não sei como pretendiam clicar no link que chega no meu e-mail. Talvez esperassem que a confirmação chegasse via SMS. 🤷‍♂️
    Fiz um boletim de ocorrência no site da Polícia Civil e marquei a caixa que os autoriza a bloquear o IMEI do telefone, para que este se torne inútil. Pois é, eu tinha o IMEI e o número de série (ambos necessários para o bloqueio pela polícia) anotados. Também liguei para a operadora e pedir para bloquear o chip.
    O golpe comum no Brasil de colocar o chip em outro aparelho e começar a falar no WhatsApp com seus amigos e familiares pedindo dinheiro se passando por você também não rolou, pois minha conta do WhatsApp era protegida por um PIN, que é a autenticação de dois fatores do WhatsApp.
    Vamos às dicas para cada momento:
    Enquanto seu celular está com você:
    Anote marca, modelo, IMEI e número de série do seu aparelho em algum lugar. Também o PIN e PUK do chip (vem no cartão). Pode ser mandando um e-mail para você mesmo e deixando-o lá para sempre. Utilize um bom código alfanumérico (letras e números). Nada de padrões de desenho (em Android) ou códigos numéricos como "1234", "0000" ou afins. Jamais use um telefone sem código de bloqueio. Utilize autenticação de dois fatores em todas as suas contas de redes sociais e serviços de Internet (Facebook, Instagram, Gmail, etc), mas nunca por SMS, pois eles têm acesso ao chip quando obtêm seu telefone! Use sempre por aplicativo (recomendo o Authy porque você pode usar no desktop e no celular (e em outros dispositivos) - aí se perder o celular você ainda tem o app no desktop pra te dar os tokens). Imprima os códigos de recuperação do aplicativo de autenticação de dois fatores escolhido no passo acima e guarde em casa, porque com o celular você perde o aplicativo de autenticação de dois fatores junto! Habilite a autenticação de dois fatores (via PIN) no aplicativo do WhatsApp (Ajustes -> Conta -> Confirmação em duas etapas). Do contrário você pode ser vítima do golpe de falsidade ideológica que comentei no texto. Sempre habilite o Touch ID ou senha para todos os aplicativos que suportam. Normalmente os de banco suportam. Para e-mails, sei que o Outlook suporta. Dessa forma, mesmo que o gatuno roube seu telefone desbloqueado, não verá seu e-mail e não acessará os apps dos bancos. Habilite o PIN do chip (no iPhone esta configuração fica em Ajustes -> Celular -> PIN do SIM). Dessa forma, será solicitada uma senha quando o meliante colocar seu chip em outro aparelho. Lembrando que o chip já vem com um PIN da operadora, que você vai precisar para alterar. Os padrões são Vivo: 8486; TIM: 1010; Claro: 3636; Oi: 8888 mas o correto mesmo é ver no cartão do qual o chip é destacado quando vem. Todo cuidado é pouco aqui, pois é muito fácil bloquear o chip ao tentar desbloqueá-lo incorretamente 3 vezes. Aí só ligando pra operadora, ou usando o PUK para desbloquear. Desabilite a visualização de SMS na tela bloqueada (para que o conteúdo do SMS não seja mostrado). É uma boa ideia desabilitar quaisquer outras notificações também, ou pelo menos previnir que seu conteúdo seja exibido na tela bloqueada. Desabilite a Siri (iOS) na tela bloqueada (Ajustes -> Siri -> Permitir Quando Bloqueado). Do contrário, basta que alguém pergunte "Qual o meu nome?" para saber seu nome, telefone, empresa (dependendo do que constar no seu contato), como mostra a captura de tela abaixo:
    Siri entregando seus dados pra qualquer um
    Também é possível pedir à Siri para "Mostrar ligações perdidas". A ordinária vai mostrar a última ligação perdida somente, mas é possível inclusive retornar a ligação, mesmo com a tela bloqueada:

    Siri mostrando quem te ligou e permitindo retornar 
    As medidas acima você deve tomar agora que tem seu telefone com você, pois todo cuidado é pouco. Vale ressaltar que os ladrões de rua, os "mão leve" podem não ser especialistas em TI, mas certamente existe um "departamento de TI do crime" onde eles levam estes celulares roubados e lá pessoas que entendem do assunto tentam hackear suas contas.
    Se todas essas medidas forem tomadas, acho que você pode ficar tranquilo como eu fiquei. Pra ser honesto, eu não segui a recomendação de número 7, sobre o PIN do chip e só por isso conseguiram meu número pra tentar resetar a senha do Facebook. Mesmo assim não rolou, pois meu 2FA no Facebook era via app, não SMS (isso porque segui bem a recomendação de número 3) mas serviu pra ver onde vacilei.
    Depois de ser roubado/furtado ou perder o celular:
    Se você desconfia que perdeu o celular (não é roubo, nem furto), pode tentar localizar o aparelho, mas tenha em mente que quanto mais tempo você deixar o chip habilitado, mais tempo os bandidos têm para tentar as fraudes. Já aconteceu de eu perder o celular e só conseguir ligar pra ele dois dias depois, pois quem achou não tinha um carregador de iPhone à mão. Se eu tivesse bloqueado o aparelho ou o chip, não conseguiria ligar. Então aqui é sempre delicado. Depende do que rolou e o do nível de risco que você quer assumir (sinta em seu coração), mas:
    Ligue para a sua operadora e informe o ocorrido. Eles vão bloquear o chip. Faça um boletim de ocorrência na delegacia mais próxima ou pela Internet. Você vai precisar dos dados lá do passo 1 do que fazer enquanto seu celular está com você. Desconecte o dispositivo das suas contas de e-mail, redes sociais e outros serviços (é impossível enumerar todos, mas serviços comuns são Gmail, Facebook, Instagram, Twitter, Spotify, etc). Programe a deleção dos dados pelo site do fabricante (Google, Apple, etc). No novo telefone:
    Se não teve jeito e você comprou um telefone novo, tudo bem. Vida nova. Só fica esperto porque se você fizer mantiver o mesmo número, os criminosos podem tentar entrar em contato via ligação, SMS, WhatsApp, iMessage, etc se passando pela fabricante do telefone, dizendo que o acharam e pedindo as credenciais do iCloud, por exemplo. Com elas, é possível desbloquear o telefone roubado. Não caia nessa. 😉
    Roubo, perda e furto de telefones são muito comuns em grandes cidades. É importante que você seja precavido. Quem tiver outras dicas, só postar nos comentários que incorporo aqui. Se quiser me contar como fazer pra Android as configurações acima, comenta aí também. Ah, depois de fazer tudo isso no seu telefone, seja um bom amigo e compartilhe este artigo com todo mundo que tem um smartphone pelo bem da nação. 🙏
     
    Atualização em 16/5/2019 - Adicionei uma captura de tela da Siri entregando quem te ligou e recomendei o Authy expressamente, pois parece ser realmente melhor que Duo Mobile e Google Authenticator, já que pode ser utilizado em vários dispositivos (incluindo no desktop) e possui suporte ao Touch ID.
     

    Candeer
    Olá! No artigo anterior falamos sobre Signals, que é de suma importância para a comunicação entre processos, mas para construir o nosso debugger precisamos muito mais do que apenas isso, precisamos de fato ter total controle sobre um dado processo e se possível controlar até o seu própio início.
    Neste artigo será explicado o que são forks e seu uso em desenvolvimento de aplicações em sistemas UNIX. Sem mais delongas, vamos prosseguir!!!😀
    Resumidamente a syscall fork é usada para a duplicação e criação de um processo. Quando um dado processo chama a função fork(), é criada uma cópia idêntinca de seus dados. Note que apenas uma cópia é feita, o processo filho não compartilha o mesmo espaço de memória do pai.
    A syscall fork retorna um PID que é usado para indetificar em qual processos estamos e também dar acesso ao ID do processo filho. Caso o PID seja 0 estamos executando no filho, caso seja qualquer outro somos o processo pai, isso ocorre pois o pai precisa saber o PID do filho, mas o filho não necessariamente precisa saber o seu própio (da mesma maneira que o seu processo não sabe o própio PID ao menos que o mesmo peça).
    Algo interessante de se notar é que os Init System usados para subir e gerenciar serviços de sua máquina trabalham dessa mesma maneira, você pode checar sua árvore de processo usando comando pstree:
    $ pstree Dessa maneira você tem uma representação bem visual de como está dividida a sua estrutura de processos 😀. Note que todos os processos são filhos do seu Init system (seja ele SystemV, Systemd, etc). Aconselho você explorar o comando pstree para uma visão bem mais detalhada do seu sistema! Outra abordagem é usar o própio comando ps:
    $ ps -ef Rode o comando acima (dependendo da quantidade de processos use um pipe para o less 😉) e com ele teremos uma visão mais detalhada. A coluna PID representa o ID do processo em si e a coluna PPID representa o "Parent Process ID", que nada mais é que o ID do processo pai. Note que o PID 1 é o seu Init System e os seus processos rodam como filho dele!

    Vale notar que o processo Pai do própio init é o PID 0, que é conhecido como "swapper" ou "scheduler", que é o processo responsavel para realização de paging. Paging é o sistema de gerenciamento de memória que salva os dados da RAM em uma memória secundária (HD, SSD e etc) e recupera em formato de páginas (outros PID também são filhos do propio PID 0 como PID 2 que gerencia todas as threads que rodam em Kernel Land(KThread) etc).
     
    Programando Forks
    A syscall fork está na lib  <unistd.h> (Unix Standard library) e tem a seguinte construção:
    #include <sys/types.h> #include <unistd.h> pid_t fork(void); Precisamos incluir a lib <sys/types.h> para que seja possivel acessar o tipo pid_t. A função fork não espera nenhum parâmetro para a sua construção e o código abaixo demonstra o quão simples é cria um fork.
    #include <stdio.h> // Acesso a syscall #include <unistd.h> // Acesso ao tipo variavel pid_t #include <sys/types.h> int main(void) { int x; printf("Processo normal...\n"); printf("Forking...\n"); sleep(5); pid_t pid = fork(); x = 40; if (pid == 0) { printf("Eu sou o processo filho meu PID: %d\n", pid); } else { printf("Eu sou o processo pai de %d\n", pid); } sleep(5); return 0; } Compile o código acima da seguinte forma:
    $ gcc -o fork fork.c $ ./fork Note que o código se "divide" a partir da chamada fork e um if  é usado para saber se estamos executando no pai ou no filho, note também que o pai sabe o PID e o filho não.
    Para melhor visualização o código acima roda por 10 segundos (por conta da chamada ao sleep com esse tempo de espera). Abra um outro terminal e rode o comando:
    $ watch -n1 pstree O comando acima vai executar o pstree a cada 1 segundo, desta forma você verá o exato momento da criação do fork.

    Comunicando-se com o processo fork
    Agora imagine que um  processo precisa esperar o seu filho terminar algum trabalho e dependendo do seu sinal o processo pai realiza alguma ação. A comunicação entre o processo pai e o filho se da por signals. O pai pode saber exatamente o estado do seu processo filho usando a syscall wait e waitpid, ambas na lib <sys/wait.h>:
    #include <sys/types.h> #include <sys/wait.h> pid_t wait(int *status); pid_t waitpid(pid_t pid, int *status, int options); A syscall wait espera que ao menos 1 de seus processos filho troque de estado, já a waitpid espera por um processo específico. Como sabemos exatamente qual processo queremos rastrear iremos usar esta call 😀:
    #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> int main(void) { printf("Spliting work...\n"); pid_t pid = fork(); if (!pid) { int a = 0; for(int i = 0; i < 100000000; i++ ) { a += i*2 + 10 *i; } return 9; } int status; int signal; printf("Waiting child finish work...\n"); waitpid(pid, &status, 0); if (WIFEXITED(status)) { signal = WEXITSTATUS(status); printf("Child exited, status = %s\n", strsignal(signal)); } return 1; } Compile o código acima e execute:
    $ gcc -o work work.c $ ./work Spliting work... Waiting child finish work... Child exited, status = Killed Veja que após a chamada de fork nosso processo filho executa várias iterações e realiza um cálculo (um cálculo totalmente randômico) e após isso retorna 9. Este retorno em questão é apenas por motivos educativos (no artigo anterior falamos de sinais e como eles funcionam). O processo pai usa a syscall waitpid para esperar que qualquer signal seja enviada do pid especificado. Após receber um status é verificado se o fork saiu (WIFEXITED) e se sim, pegamos o signal enviado usando WEXITSTATUS(status da saída) e usamos a chamada strsignal(provida pela string.h) para recuperar uma versão em texto do signal. Nesse caso iremos recuperar o signal "KILLED", pois colocamos 9 apenas por razões educativas.
    Normalmente se tudo ocorreu bem colocamos 0 (inclusive é dessa maneira que sua shell avalia se o programa rodou certo).
    $./work && echo "Filho saiu com 0, tudo certo..." || echo "Filho saiu com 1, algo errado..." No caso acima a nossa shell irá criar um fork do nosso work, executar o nosso programa (que por sua vez também executa um fork mas não entra em questão aqui) e se o signal retornado pelo fork for 0 ele imprime uma mensagem, caso contrario ele imprime uma mensagem de erro, dessa maneira você pode orquestrar um shell scripting usando o própio retorno do processo 😉
    Tente mudar o retorno do fork acima e verifique seu status usando funções providas pela <sys/wait.h>. No exemplo acima usamos apenas a call WIFEXITED e WEXITSTATUS, mas existem várias outras.
    Forks são de extrema importância para criação e gerenciamento de processos e iremos usar forks para que seja possível executar o programa que queremos debugar, dessa maneira o software em questão vai ser filho do nosso debugger, o que nós da total controle sobre o mesmo.
    Comentarios são todos bem vindos e todos os códigos usados estão disponíveis no github! 😀

    Links úteis:
        Process Control
        fork
        wait
        Process State
        Fork Bomb - Cuidado com isso

    Candeer
    Olá, neste artigo compartilharei um pouco da minha pesquisa no desenvolvimento de debuggers. No momento estou trabalhando em um protótipo de debugger para Linux, mas nada tão avançado quanto um gdb ou radare (muitas coisas são necessárias para chegar neste nível de maturidade de software).
    O desenvolvimento de debuggers é uma atividade muito interessante, já que, em sua forma mais básica, pode ser resumido em uma série de chamadas de sistema (syscalls) para que seja possível o controle do processo a ser depurado (muitas vezes chamado de debuggee) e de seus recursos, mas não vamos colocar a carroça na frente dos cavalos e vamos em partes.
    Antes de começarmos a discutir detalhes mais específicos acerca da depuração de processos, é necessário um entendimento básico de como os mesmos se comunicam na plataforma que vamos desenvolver o tal debugger, no nosso caso, UNIX-like.
    Inter-process communication (IPC)
    IPC é uma forma que processos podem utilizar para se comunicar dentro de um sistema operacional. Existem diversas maneiras de comunicação: via sinais (signals), sockets, etc, mas para a criação de um debugger é apenas necessário usar sinais para a execução.
    Sinais funcionam como uma notificação que pode ser enviada à um processo específico para avisar que algum evento ocorreu.
    É possível também programar um processo para reagir aos sinais de maneira não padrão. Se você já teve um uso razoável de Linux, você provavelmente já enviou sinais à um processo. Por exemplo, quando você aperta Ctrl+C para interromper a execução de um processo, é enviado um sinal do tipo SIGINT, que nada mais é que uma abreviação para Signal Interruption. Se o processo em questão não está preparado para reagir a este sinal, o mesmo é terminado. Por exemplo, considere o seguinte código:
    #include <stdio.h> int main(void) { while(1) printf("hi\n"); return 0; } Ao compilar e executar o código acima e apertar Ctrl+C, o mesmo encerra como esperado, porém podemos verificar que um SIGINT foi enviado usando a ferramenta ltrace, que além de listar chamadas a bibliotecas também mostra os sinais enviados ao processo:
    $ gcc -o hello hello.c $ ltrace ./hello Rode o comando acima e aperte Ctrl+C para verificar o sinal enviado!
    Programando reações a sinais
    A capacidade de enviar sinais a um processo nos dá a possibilidade de saber o que esta acontecendo com algum processo específico que estejamos depurando.
    Para programar reações a algum tipo de sinal, podemos incluir a biblioteca signal, para que possamos usar a função e estrutura (struct) sigaction:
    struct sigaction { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void); };  
    int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); A struct sigaction nos permite adicionar handlers (tratadores) para nossos sinais, enviando o endereço de nossa função que realiza algum tipo de ação baseada no sinal enviado para o campo sa_handler(sigaction handler).
    Um handler neste contexto nada mais é que uma função que sempre vai ser chamada quando um dado sinal for enviado, dessa maneira podemos executar alguma ação quando recebermos um sinal.
    Já a função sigaction recebe o número do sinal, porém uma série de macros já são pré-definidas e podemos passar como argumento apenas o nome do sinal, como SIGINT por exemplo. A função recebe também a referência da struct previamente definida (struct sigaction) e, caso precise trocar um handler por outro, também recebe no último argumento (oldact) o handler anterior, para que possa ser feita a troca pelo novo. Como não é o nosso caso, vamos passar NULL neste último argumento.
    O código abaixo simula um uso de handlers de sinais, que imprime uma mensagem quando um sinal é enviado:
    #include <stdio.h> #include <signal.h> #include <unistd.h> // sleep void simple_handler(int sig) { printf("Hello SIGINT\n"); } int main() { struct sigaction sig_handler = { simple_handler }; sigaction(SIGINT, &sig_handler, NULL); sleep(1000); return 0; } Ao executar o código acima, aperte Ctrl+C e veja que será imprimido a mensagem do nosso handler!
    O manual da signal contém uma tabela com todos os sinais usados por sistemas POSIX.
    Para enviarmos sinais facilmente em sistemas UNIX podemos usar o comando kill:
    $ kill -l O comando acima mostra todos os sinais e seus respectivos números, com isso podemos fazer algo interessante. Por exemplo, rode o código acima em um terminal separado e use o kill para se comunicar com o seu processo, assim:
    $ ps ax | grep simple_signal $ kill -2 <pid> Primeiro buscamos o PID do nosso processo então usamos o kill que espera como primeiro argumento numero do sinal (listado em kill -l) e o segundo o PID do processo alvo.
    Ao enviar o sinal, podemos ver que o nosso código reage aos sinais que foram associados a um handler especifico! Tente criar handlers para vários sinais e teste usando o comando kill. 😃
    Abaixo um código para demonstrar um uso real de um software que escreve dados aleatórios nos arquivos temporários e antes de uma finalização abrupta, é deletado o que foi usado:
    #include <stdio.h> #include <signal.h> #include <unistd.h> // Log errors void fatal(const char* err_msg) { fprintf(stderr, "Error: %s\n", err_msg); } // Escreve algo random em um arquivo void random_work() { FILE* temp_files = fopen("/tmp/foo", "w"); if (!temp_files) { fatal("Cant open foo!"); } else { fprintf(temp_files, "%s", "Random random random!\n"); fclose(temp_files); } } // Handler para deleta arquivos criados void handler_termination(int sig) { // Verifica se existe usando a function access // Caso existe usa a syscall unlink para remover o arquivo if (access("/tmp/foo", R_OK) < 0) return; unlink("/tmp/foo"); printf("All clean! closing...\n"); } int main() { //struct sigaction que recebe a function handler_termination como valor do seu handler struct sigaction interruption_handler; interruption_handler.sa_handler = handler_termination; // Syscall sigaction que associa o nosso handler para um sinal especifico // O ultimo campo NULL, espera o handler anterior para que posso tornar o novo handler o default sigaction(SIGINT, &interruption_handler, NULL); random_work(); sleep(1000); handler_termination(0); return 0; } Dica: Dê uma olhada na tabela de sinais e crie handlers para o mesmo código acima!
    Para a construção do nosso debugger iremos focar mais no signal SIGTRAP, para que seja possível detectar se o nosso processo sofreu uma "trap" da CPU. Uma trap ocorre quando acontece alguma interrupção síncrona na execução, que faz o processo ficar parado até que o sistema operacional execute alguma ação. Isto será usado para implementar e interpretar breakpoints. Veremos tudo isso com mais detalhes em breve!
    Sinta-se livre para comentar e sugerir correções e melhorias. Até o próximo artigo!
    Links úteis:
    Syscall IPC CERO 11 – Linux Syscalls Syscalls, Kernel mode vs User mode Programação em C

    Fernando Mercês
    Saudações, leitores do Mente Binária! Hoje me deu vontade de falar sobre uma tarefa que eventualmente preciso fazer na empresa onde trabalho, que é a de verificar as diferenças entre arquivos executáveis, normalmente de Windows, também conhecidos por executáveis PE.
    Há vários usos ao comparar binários. É possível avaliar o que mudou na versão atual de um software em relação à anterior, descobrir o que muda em cada sample diferente de uma mesma família de malware, etc. Esses dias mesmo me foi pedido que verificasse a diferença entre 6 arquivos maliciosos, que compartilho abaixo como fiz.
    Reconhecimento básico
    Os arquivos que recebi tinham seu hash SHA-256 como nome. A primeira coisa que fiz foi checar seu tipo (usando comandos do macOS, mas o Linux tem comandos similares):
    $ file * fdba340bb35635934aa43b4bddd11df31f2204e73394b59756931aa2f7f59e04: PE32 executable (GUI) Intel 80386, for MS Windows fdf3060eb9c39b1a2be168b1ac52c2f80171394e73fe03c4e0c57911cb9358a9: PE32 executable (GUI) Intel 80386, for MS Windows fedf9d9815b3d0ad28e62f99d5dcf92ec0f5fcb90135b4bdc30bb5709ab9ff05: PE32 executable (GUI) Intel 80386, for MS Windows ff2f1be6f64c91fa0a144cbc3c49f1970ba8107599d5c66d494ffb5550b0f7fd: PE32 executable (GUI) Intel 80386, for MS Windows ff53c7ba285ffdc2c29683bb79bb239ea59b3532f8b146523adf24d6d61fc640: PE32 executable (GUI) Intel 80386, for MS Windows ffee504e292a9f3ae6c439736881ebb314c05eac8b73d8b9c7a5a33605be658e: PE32 executable (GUI) Intel 80386, for MS Windows Só para garantir, também chequei o SHA-256 deles e realmente bateu com o nome, o que era esperado:
    $ shasum -a256 * fdba340bb35635934aa43b4bddd11df31f2204e73394b59756931aa2f7f59e04 fdba340bb35635934aa43b4bddd11df31f2204e73394b59756931aa2f7f59e04 fdf3060eb9c39b1a2be168b1ac52c2f80171394e73fe03c4e0c57911cb9358a9 fdf3060eb9c39b1a2be168b1ac52c2f80171394e73fe03c4e0c57911cb9358a9 fedf9d9815b3d0ad28e62f99d5dcf92ec0f5fcb90135b4bdc30bb5709ab9ff05 fedf9d9815b3d0ad28e62f99d5dcf92ec0f5fcb90135b4bdc30bb5709ab9ff05 ff2f1be6f64c91fa0a144cbc3c49f1970ba8107599d5c66d494ffb5550b0f7fd ff2f1be6f64c91fa0a144cbc3c49f1970ba8107599d5c66d494ffb5550b0f7fd ff53c7ba285ffdc2c29683bb79bb239ea59b3532f8b146523adf24d6d61fc640 ff53c7ba285ffdc2c29683bb79bb239ea59b3532f8b146523adf24d6d61fc640 ffee504e292a9f3ae6c439736881ebb314c05eac8b73d8b9c7a5a33605be658e ffee504e292a9f3ae6c439736881ebb314c05eac8b73d8b9c7a5a33605be658e PS.: No Linux o comando seria sha256sum ao invés de shasum -a256.
    O próximo passo foi checar o tamanho deles:
    $ wc -c * 396973 fdba340bb35635934aa43b4bddd11df31f2204e73394b59756931aa2f7f59e04 396973 fdf3060eb9c39b1a2be168b1ac52c2f80171394e73fe03c4e0c57911cb9358a9 396973 fedf9d9815b3d0ad28e62f99d5dcf92ec0f5fcb90135b4bdc30bb5709ab9ff05 396973 ff2f1be6f64c91fa0a144cbc3c49f1970ba8107599d5c66d494ffb5550b0f7fd 396973 ff53c7ba285ffdc2c29683bb79bb239ea59b3532f8b146523adf24d6d61fc640 396973 ffee504e292a9f3ae6c439736881ebb314c05eac8b73d8b9c7a5a33605be658e 2381838 total Aqui apresentou-se um caso atípico: os binários possuem exatamente o mesmo tamanho! Já pensei que havia grandes chances de as diferenças entre eles serem mínimas: provavelmente algo usado pelo autor do malware só para "mudar o hash" na tentativa de evitar que os antivírus detectem os arquivos idênticos, por exemplo. Essa tentativa é na verdade frustrada visto que, ao contrário do que muitos pensam, os antivírus não detectam malware por hash normalmente, já que isso seria muito custoso do ponto de vista do desempenho (seria preciso ler todos os bytes do arquivo!) e também seria muito fácil tornar um novo arquivo indetectável - bastaria alterar um único byte para um hash final completamente diferente.
    Comparação de estrutura
    Se estivéssemos tratando arquivos de texto, poderia simplesmente usar o comando diff, mas o assunto aqui é PE, então algo interessante de verificar é sua estrutura, que consiste basicamente em cabeçalhos, localizados antes das seções. Se você não sabe do que estou falando, recomendo os seguintes recursos:
    Posts do @Leandro Fróes sobre o formato PE e suas referências. Capítulo sobre PE do livro Fundamentos de Engenharia Reversa. Aulas 5 e 6 do CERO, nosso Curso de Engenharia Reversa Online em vídeo. Digitar "PE executable" no Google ler o que curtir. Depois dessa imersão no mundo dos executáveis PE, não tenho dúvidas de que você vai se apaixonar por eles também! 😍
    Voltando à comparação, o que eu quero dizer com estrutura? Bem, os valores dos campos dos cabeçalhos. Por exemplo, para ver o cabeçalho COFF de um arquivo PE, usei o readpe, parte do kit de ferramentas pev:
    $ readpe -h coff fdba340bb35635934aa43b4bddd11df31f2204e73394b59756931aa2f7f59e04 COFF/File header Machine: 0x14c IMAGE_FILE_MACHINE_I386 Number of sections: 5 Date/time stamp: 1401620468 (Sun, 01 Jun 2014 11:01:08 UTC) Symbol Table offset: 0 Number of symbols: 0 Size of optional header: 0xe0 Characteristics: 0x102 Characteristics names IMAGE_FILE_EXECUTABLE_IMAGE IMAGE_FILE_32BIT_MACHINE Mas não, não usei o pev por saudosismo! A ideia de ter uma saída em texto da estrutura desses binários é depois usar o comando diff para compará-las. A primeira coisa que precisei então foi gerar um .txt contendo toda a estrutura, e não só o cabeçalho COFF, para cada um dos arquivos. Uma repetição em bash dá conta do recado:
    $ ls -1 readpe_output_* readpe_output_fdba340bb35635934aa43b4bddd11df31f2204e73394b59756931aa2f7f59e04.txt readpe_output_fdf3060eb9c39b1a2be168b1ac52c2f80171394e73fe03c4e0c57911cb9358a9.txt readpe_output_fedf9d9815b3d0ad28e62f99d5dcf92ec0f5fcb90135b4bdc30bb5709ab9ff05.txt readpe_output_ff2f1be6f64c91fa0a144cbc3c49f1970ba8107599d5c66d494ffb5550b0f7fd.txt readpe_output_ff53c7ba285ffdc2c29683bb79bb239ea59b3532f8b146523adf24d6d61fc640.txt readpe_output_ffee504e292a9f3ae6c439736881ebb314c05eac8b73d8b9c7a5a33605be658e.txt Eu usei o readpe sem nenhuma opção, assim ele imprime todos os cabeçalhos, incluindo os de seções. Só pra começar fiz um diff do primeiro para o segundo e não houve qualquer saída, ou seja, a estrutura dos arquivos eram idênticas! E eram mesmo:
    $ wc -c readpe_output_* 21627 readpe_output_fdba340bb35635934aa43b4bddd11df31f2204e73394b59756931aa2f7f59e04.txt 21627 readpe_output_fdf3060eb9c39b1a2be168b1ac52c2f80171394e73fe03c4e0c57911cb9358a9.txt 21627 readpe_output_fedf9d9815b3d0ad28e62f99d5dcf92ec0f5fcb90135b4bdc30bb5709ab9ff05.txt 21627 readpe_output_ff2f1be6f64c91fa0a144cbc3c49f1970ba8107599d5c66d494ffb5550b0f7fd.txt 21627 readpe_output_ff53c7ba285ffdc2c29683bb79bb239ea59b3532f8b146523adf24d6d61fc640.txt 21627 readpe_output_ffee504e292a9f3ae6c439736881ebb314c05eac8b73d8b9c7a5a33605be658e.txt 129762 total $ md5 !$ md5 readpe_output_* MD5 (readpe_output_fdba340bb35635934aa43b4bddd11df31f2204e73394b59756931aa2f7f59e04.txt) = 05b36b89b1165b3d619bee16f8a1d7f7 MD5 (readpe_output_fdf3060eb9c39b1a2be168b1ac52c2f80171394e73fe03c4e0c57911cb9358a9.txt) = 05b36b89b1165b3d619bee16f8a1d7f7 MD5 (readpe_output_fedf9d9815b3d0ad28e62f99d5dcf92ec0f5fcb90135b4bdc30bb5709ab9ff05.txt) = 05b36b89b1165b3d619bee16f8a1d7f7 MD5 (readpe_output_ff2f1be6f64c91fa0a144cbc3c49f1970ba8107599d5c66d494ffb5550b0f7fd.txt) = 05b36b89b1165b3d619bee16f8a1d7f7 MD5 (readpe_output_ff53c7ba285ffdc2c29683bb79bb239ea59b3532f8b146523adf24d6d61fc640.txt) = 05b36b89b1165b3d619bee16f8a1d7f7 MD5 (readpe_output_ffee504e292a9f3ae6c439736881ebb314c05eac8b73d8b9c7a5a33605be658e.txt) = 05b36b89b1165b3d619bee16f8a1d7f7
    Os hashes MD5 da saída em texto da estrutura de todos os arquivos batem. Eles são mesmo iguais estruturalmente!
    Passado o choque, percebi que teria que comparar o conteúdo das seções (código, dados, talvez resources, etc). Aí fui obrigado a inicializar minha VM do Janelas mesmo...
    Comparação do conteúdo das seções
    Existem alguns softwares que trabalham com PE e possuem funções de comparação de dois executáveis. Eu costumava usar o Cold Fusion (um antigo gerador de patch) pra isso, mas ele tem alguns bugs que me impediram. Achei a mesma função no Stud_PE, mas ele localiza arquivos por extensão na janela de comparação, então renomeei o primeiro e o segundo arquivo que tinha para a.exe e b.exe respectivamente.
    Ao abrir o a.exe no Stud_PE, usei o botão "File Compare", selecionei o método "Binary", setei o "Starting from" pra "Raw" e cliquei em "Compare":

    Se você não entendeu por que fiz isso, volte uma casa ou leia os tutorias de PE que indiquei. Ou pergunte que eu falo. 😍
    Bem, entre esses dois caras então havia 9 bytes que o diferenciavam e eu já tinha os offsets a partir do início do arquivo. Agora é descobrir em que seção eles estavam no PE, o que são, o que comem e como eles vivem. 😎
    Descobrindo como as diferenças são usadas
    Abri o executável no x64dbg (na verdade no x32dbg, já que este binário é de 32-bits) mas percebi que o entrypoint estava no endereço 013706AA. Como o ImageBase deste binário é 00400000, percebi que o ASLR estava habilitado e, antes de continuar , desabilitei-o com o DIE, como mostro neste vídeo rápido no canal Papo Binário:
    Antes de reabrir o binário no x32dbg, convém lembrar que eu tinha um offset e precisava convertê-lo para endereço virtual (VA). Isso é feito com o que alguns analisadores de PE chamam de FLC (File Location Calculator). O DIE tem, o Stud_PE tem e o pev também tem, com a ferramenta ofs2rva:
    $ ofs2rva 0x4c451 a.exe 0x4dc51 Mas pra não você não me acusar de saudosismo de novo, vou mostrar no Stud_PE 😄

    Percebe que o Stud_PE já diz que este byte pertence à seção .rdata, o que à esta altura você já sabe, caso tenha feito o trabalho de casa de estudo do PE, que é provavelmente uma seção de dados somente-leitura, então há grandes chances de nossa sequência diferentona pertencer à uma string constante, por exemplo. Fui ver no debugger como é que tava a festa. Abri o a.exe lá e dei um Ctrl+G no Dump pra ir pro endereço 44DC51:

    De fato tinha uma string lá: zuk0KRrGrP, mas ela na verdade começava em 44DC44 e pra saber quando ela era usada no malware, coloquei um breakpoint de hardware no acesso ao byte, que é o primeiro da string e cheguei à conclusão de que, como o nome sugere, é realmente uma string de identificação da campanha do malware, sempre no mesmo offset (calculei de novo usando FLC).  Agora foi só ver a dos outros e novamente recorri à uma ferramenta do pev (💚), a pestr:
    $ for i in *; do echo $i; pestr -so $i | grep 0x4c444; echo; done fdba340bb35635934aa43b4bddd11df31f2204e73394b59756931aa2f7f59e04 0x4c444 .rdata identifierStrzuk0KRrGrP fdf3060eb9c39b1a2be168b1ac52c2f80171394e73fe03c4e0c57911cb9358a9 0x4c444 .rdata identifierStrAR0U4hr1wW fedf9d9815b3d0ad28e62f99d5dcf92ec0f5fcb90135b4bdc30bb5709ab9ff05 0x4c444 .rdata identifierStrswEYVkFWeg ff2f1be6f64c91fa0a144cbc3c49f1970ba8107599d5c66d494ffb5550b0f7fd 0x4c444 .rdata identifierStrKXaUzlBDIj ff53c7ba285ffdc2c29683bb79bb239ea59b3532f8b146523adf24d6d61fc640 0x4c444 .rdata identifierStrv91TJ5c3Lr ffee504e292a9f3ae6c439736881ebb314c05eac8b73d8b9c7a5a33605be658e 0x4c444 .rdata identifierStrOzJnvFQy2U Bom, daí o céu é o limite. Dá pra criar assinatura, criar um script pra extrair esse ID da campanha, enfim, missão cumprida.

    FAQ
    1. Por que você não utilizou só um comparador de arquivos qualquer, que compara os bytes em hexadecimal?
    Eu queria saber exatamente onde estavam as diferenças entre os arquivos, se na estrutura ou não. Em caso negativo, é código? Se sim, que código? Que faz o que? São dados? Usados onde? Em qual seção? Um editor hexadecimal ignorantão não me daria isso. Além disso, se os arquivos fossem diferente estruturalmente, ou em tamanho, eu queria saber antes, pra não perder tempo analisando diferenças de bytes hexa que eu não sei o que é.
    2. Existem softwares para comparar binários PE muito mais poderosos, como o BinDiff. Por que caralhas você não o usou?
    O BinDiff é pra comparar código. Minha diferença estava nos dados. Além disso, o BinDiff e seus amigos traduzem o Assembly original do binário para uma linguagem intermediária própria e comparam lógica, não instruções. É bem fodão, mas não me atendia neste caso, afinal eu já sabia que os binários eram idênticos em funcionalidade. Só queria saber onde estava a diferença exata.
    3. Percebi pela screenshot do Stud_PE que ele também compara a estrutura dos arquivos PE, então todo aquele processo com o readpe foi à toa?
    Sim, foi só pra Inglês ver. Não, brincadeira! O Stud_PE compara os cabeçalhos COFF, Optional e os diretórios de dados somente. O readpe imprime todos os cabeçalhos, incluindo todas as seções mais os imports. É outro nível, moleque! 😏

    4. E quanto à executáveis ELF?
    O título não fala somente de PE propositalmente, já que a mesma técnica pode ser usada para arquivos ELF, só mudando os programas (readelf, etc).
    Por hora é só. Se você deixar sua análise abaixo ou quiser fazer um comentário/pergunta, ficarei muito grato. Considera apoiar a gente também vai. 💚

    Leandro Fróes
    Após ver sobre o comando find no nosso canal Papo Binário decidi estudar um pouco mais sobre o mesmo. Revisando estas anotações pensei que seria interessante compartilhá-las, tendo em vista que o find é um comando extremamente poderoso. Alguns dos parâmetros já foram abordados no vídeo, mas vou repassar alguns aqui, não custa nada, não é mesmo?!
    Este comando pode ser útil para diversas tarefas, dentre elas investigação, administração ou mesmo aprendizado sobre o sistema.
    Indo direto ao ponto, o find é um comando para procurar itens no filesystem (arquivos, links, diretórios, etc). O que o difere de outros programas que fazem isto é a quantidade de opções que a ferramenta possui e o fato de não depender da variável $PATH para encontrar um binário. O comando leva como principal parâmetro um path, ou seja, um caminho para procurar algo. Se não passarmos nada ele entenderá que o path é o diretório atual:
    find find /etc Se não especificarmos exatamente o que queremos buscar o find simplesmente nos mostra tudo o que achar pois ele varre o filesystem recursivamente na hora de procurar algo, mas não queremos isso tudo, até porque não seria muito útil. 🙄
    Vamos tentar entender alguns filtros interessantes... Imagine que você é um administrador e precisa verificar todos os arquivos que pertencem a um usuário em específico:
    find / -type f -user leandro O que fizemos aqui? Utilizamos 2 tipos de filtros, um deles foi o -user, que busca arquivos que pertencem apenas à aquele usuário. O -type filtra pelo tipo de item no filesystem e suporta os seguintes tipos:
    d -> diretório f -> arquivo regular l -> link simbólico s -> socket     Procurando por arquivos perdidos:   Imagine agora que seu sistema está uma bagunça e você não faz ideia onde está um arquivo em específico, pense que você tem no mínimo 8 subdiretórios lotados de arquivos e você não lembra onde está o que você está procurando, só lembra que existe a palavra "mentebinaria" no nome dele. Além disso, você também sabe que não está nos primeiros 2 subdiretórios. Podemos resolver com: find . -mindepth 2 -name "*mentebinaria*" -type f A primeira coisa que fizemos foi utilizar a opção -mindepth, que especifica quantos níveis na hierarquia o find deve olhar no mínimo (a opção -maxdepth especifica o máximo). A outra opção foi a -name, que procura por um nome completo ou parte dele como fizemos no exemplo utilizando o wildcard * (asterisco) para bater com qualquer string antes de depois da palavra "mentebinaria".   Executando comandos:
    Na minha opinião uma das opções mais interessantes do find é a -exec, que praticamente executa comandos em cima do que o find encontrar. Não entendeu? Vamos lá... supondo que queiramos ver qual o tipo de arquivo de todos os arquivo que encontrarmos em um diretório em específico com o comando file:
    find . -type f -exec file {} \; Temos muita coisa pra entender nesta linha. Primeiro, o -exec trabalha com o conceito de targets (as chaves {} ) e isto significa: coloque tudo o que o find devolver no local da chave. Para cada arquivo que o find achar ele rodará o comando file naquele arquivo. Incrível, não?
    Sim, mas com isto estaremos executanto o mesmo comandos múltiplas vezes, por exemplo:
    leandro@teste:~$ find . -type f | wc -l 295 Imagine rodar isto 295 vezes, muita coisa, não? Se notarmos no primeiro exemplo do -exec vemos que no fim da linha tem um ponto de vírgula e este indica o fim do -exec para o find (e não para o shell). Temos que usar a contra barra para escapar e o shell não pensar que é para ele.
    Ok, mas até agora não vimos como melhorar isto. Concordam que o comando file aceita mais de um parâmetro?
    file arq1 arq2 arq3 E se pudéssemos pegar tudo que o find achar e, ao invés de rodar um comando do -exec por vez passamos tudo um atrás do outro? É exatamente isto o que o + faz e para ele não precisamos escapar:
    find . -type f -exec file {} + Este exemplo é a mesma coisa do anterior, mas de forma mais automatizada. Vamos medir a velocidade dos dois comandos:
    root@teste:~# time find / -type l -exec file {} \; ... real 0m15,127s user 0m0,336s sys 0m1,640s root@teste:~# time find / -type l -exec file {} + ... real 0m1,119s user 0m0,212s sys 0m0,396s Bem mais rápido com o +, não acham? 😉
     
    Investigando o sistema:
    Seu servidor foi atacado, você não sabe exatamente o que aconteceu e como aconteceu, só sabe que nem tudo está funcionando do jeito que deveria. Uma coisa interessante à se fazer é tentar olhar para o que exatamente foi alterado desde o ataque. Imagine que isto ocorreu à 2 dias: find / -mtime -2 Aqui estamos dizendo que a partir da hora que rodarmos o comando olhar para tudo que foi modificado 48 horas atrás. Podemos também verificar se algo foi acessado com -atime.
    E se você não sabe exatamente quando foi o ataque? A única coisa que você sabe é que a última coisa que você fez foi adicionar novas funcionalidades à um script que você tem. Podemos procurar por tudo que foi modificado após este arquivo com a opção -newer:
    find /etc -newer <arquivo_velho> Mas como isto? O Linux guarda um tipo de informação chamada MAC no inode de cada arquivo, resumindo é simplesmente a data da última modificação, acesso e criação do arquivo ao qual aquele inode se refere. Apenas como curiosidade, o comando stat lê essas informações também. 😋
     
    Mais algumas informações:
    Ok, agora você não teve nenhum problema, só quer algumas informações sobre os arquivos que o find encontrar. A opção -size <n> pode ajudar a procurar por arquivos maiores (+) ou menores (-) que o especificado:
    find /var -size +20k Podemos trabalhar com os seguintes formatos:
    c -> bytes k -> KB 0 ou -empty -> vazio find . -empty Não está satisfeito? Ok, a opção -ls ti da muito mais informações (praticamente aplica um ls -lids em cima de tudo que o find achar)
    find . -user leandro -type d -ls  
    Facilitando o parsing:
    Achou as opções de informações fracas? De fato a saída fica bem poluída. E se você precisasse todo dia monitorar informações específicas sobre arquivos específicos e criasse um script para isso, como você faria para obter estas informações? O find ti ajuda nisso também!!! Se você está familiarizado com a linguagem C (se não está veja isto) a função printf do C pode imprimir uma saída formatada de acordo com o que você escolher (string, inteiro, inteiro sem sinal, etc).
    Assim como em C, a opção -printf possui uma série de diretivas para formatarmos a saída do find como quisermos, algumas delas são:
    %f -> nome do arquivo %p -> path completo %i -> inode %M -> permissões %n -> número de hard links find / -type f -atime -1 -printf '%p %i %M \n' O único detalhe aqui é que por padrão o -printf não coloca um caractere de nova linha, devemos adicionar como no exemplo. Com isto a saída fica bem mais interesante para um script ler, não acham?! Aqui está o exemplo de uma saída:
    file1 262295 -rw-r--r-- file2 262283 -rw-r--r-- file3 262296 -rw-r--r-- Estas foram algumas dicas sobre o comando find. Com certeza informações mais completas podem ser encontradas no manual do comando, este tutorial tem como objetivo simplesmente compartilhar minhas anotações sobre o que acho bem interessante e usual sobre o comando find.
    Qualquer dúvida, crítica ou sugestão, por favor, sinta-se à vontade para comentar e obrigado! 😄

    barbieauglend
    No artigo anterior, discutimos como encontrar informações importantes para se comunicar com seu dispositivo. Neste, vamos falar sobre uma abordagem genérica antes de reverter o código do firmware de fato.
    Objetivos
    A coisa mais importante durante o processo a partir de agora  é saber quais perguntas você quer responder. Se você começa a querer entender tudo, sem focar, no final acaba perdido numa montanha de informações sem sentido.
    Dê uma de Jack, O Estripador clássico: entenda tudo, mas por partes. Eu normalmente começo procurando o protocolo de comunicação no caso dele não estar documentado. Após isso quero entender geralmente o algoritmo usado para autenticação ou o gerador de senhas ou algo que me dê acesso a dados interessantes que possam ser usados em outros $hardware iguais. Normalmente a segurança de sistemas embarcados não é muito boa nisso: todos os hardware precisam de alguma forma de identificação única presente no firmware. Como você solucionaria o problema pensando em larga escala?
    Conversando com o seu $sistema
    A melhor parte e o principal diferencial da análise de hardware é ter o bare metal (o equipamento físico) nas suas mãos. Ter acesso aos sinais eletrônicos, poder medir frequências e VER como o sistema trabalha (adicionar LEDs em todos os circuitos possíveis e adaptar a frequência por exemplo, sempre lindo!) são coisas que fazem o coração palpitar bem mais do que algumas linhas de código. Acredite 😉 Sem muita informação prévia e com alguns equipamentos baratos, é possível obter dados interessantes para a análise. Poderíamos começar a controlar o tráfego de dados usando o analisadores lógicos simples e ao mesmo tempo podemos usar equipamentos mais avançados para medir o consumo de energia no processo de inicialização do hardware com tanta precisão, que poderíamos deduzir possíveis valores de chaves privadas - é pura ciência, física 💚 - e claro que funciona em condições ideais, como aprendemos na escola.
    Fluxo de dados na PCB
    Não adianta muito ter dados se você não pode ler, escrever ou transferi-los de alguma maneira. Olhar a placa deve ser suficiente para entender como o fluxo de dados ocorre, simplesmente pensando na posição do CI (Circuito Integrado) e das marcas na PCB, ou seja, na placa. Outra coisa importante: quase todas as plaquinhas têm uma datasheet voando na !nternetz e acessível publicamente, contendo toda a informação técnica (da pinagem, voltagem e o PROTOCOLO DE COMUNICAÇÃO). Sempre vale a pena usar o DuckDuckGo antes de começar a ter dores de cabeça por algo que esta documentado, certo?
    Vamos começar sem gastar muito $$$:
    Procure a fonte!
    Quem esta começando agora pode não ter todas as ferramentas disponíveis ou não ter acesso a uma oficina / laboratório. Por isso, vamos dump o código direto do hardware e abrir mao do contexto - que teríamos no caso da leitura dos dados no hardware - mas economizaremos dinheiro e teremos acesso a toda informação possível do $device. 
    Acesso ao firmware e memória
    Dumpe o código do CI e o descomprima. Essa é a parte mais fácil e mais difícil de todo o processo, mas para isso você não precisa de nenhum equipamento e pode usar várias ferramentas de graça (algumas inclusive de código aberto).
    Como eu falei anteriormente, é possível achar na Internet os datasheets (documentação completa, normalmente em PDF) de quase todos os dispositivos. Ache a datasheet para o seu CI e assim não terá necessidade nem de identificar a pinagem nem de reverter o conjunto de instruções necessárias para a comunicação. Algo também importante é saber como parar a comunicação entre o seu CI e os outros pontos de comunicação da placa, pois isso pode interferir na leitura dos dados. Como interromper o fluxo de dados depende bastante do circuito que você esta analizando.
    Mas eu preciso desoldar?
    Esse seria o caminho mais óbvio certo? Não quer interferência, desconecte seu CI da placa e vai para o abraço. Esse método te dá controle total sobre o seu CI 🙂 O problema aqui é que esse processo requer experiencia e TEMPO.
    A ideia deste artigo é para ser algo mais simples e barato, então tente evitar essa situação e pense em ideias mais "criativas". Normalmente é possível conectar os dispositivos num osciloscópio ou voltímetro para monitorar e ter certeza que não há interferência dos sinais de cada pino do CI. Fique atento nos outros componentes, se algum tráfego de dados está ocorrendo (instale por exemplo um monitor na outra porta UART). Se algum tráfego ocorrer, daí não tem outro jeito a não ser desconectar o seu CI - use um fim de semana. 🙂
    Assim que o seu CI estiver isolado, conecte ele a qualquer aparelho que "fale" o mesmo protocolo e comece a ler a memória bloco por bloco. Isso pode demorar, por isso aconselho quando começar a leitura, vá a cozinha e faça um café! 🙂
    Dumping os dados
    Criar suas próprias ferramentas pode ser super divertido, mas custa tempo e é bem mais fácil quando você sabe o que está procurando. Por enquanto, podemos usar vários projetos de código aberto disponíveis:
    Para fazer o dump, flashrom é uma das ferramentas populares. É facil de usar, cheia de bugs mas tem suporte para várias arquiteturas diferentes. Funciona muito bem no Rasperry Pi. 🙂
    Normalmente vale a pena usar o comando file para ter uma ideia do tipo de arquivo que você esta lidando. Se ele não te ajudar em nada, tem o famoso binwalk.
    Os logs do binwalk the darão os endereços importantes. Então agora podemos usar o dd e dividir o seu binário em segmentos específicos. O comando dd precisa dos parâmetros bs (block size), do skip (offset) e o count (que aqui signifca quantos blocos você tem/quer copiar). Normalmente você terá pelo menos 3 blocos:
    O bootloader.bin: geralmente não esta criptografado pelo fato do microcontrolador não poder descriptografar. O mainkernel.bin: se você tiver sorte será algum kernel Linux. Esse é o firmware que controla o bare metal. 🙂 Geralmente o mais divertido de ler e várias vezes comprimido - use o file novamente para saber como descomprimir. O mainrootfs.bin: para quem entende um pouco de Linux e sistemas BSD, esse é o sistema de arquivos, contendo todos os arquivos com configurações, os binários do sistema, etc. Use novamente o file para verificar se está comprimido. No caso da imagem estar criptografada, é possível quebrá-la utilizando o fcrackzip. 
    No próximo artigo eu vou tentar entrar em detalhes desses três binários - vamos ver se eu acho algum hardware interessante. 🙂

    fnkz
    Ataques cibernéticos estão cada vez mais presentes no nosso dia-a-dia, sendo assim, as empresas devem fortalecer seus mecanismos de defesa e capacitar seus usuários para que informação privilegiada não caia nas mãos erradas.
    Depois do acesso remoto aberto para a internet, justificar o orçamento para diretoria é o problema mais comum de 9 a cada 10 profissionais da área de segurança. Essa dificuldade muitas vezes está relacionada à falta de um planejamento ou estratégia que justifique o projeto, a diretoria precisa enxergar retorno ou resultado palpável que aquela despesa vai trazer.
    Ai você se pergunta: Como eu vou construir defesas, se não tenho orçamento?
    O primeiro passo é entender que nem todo mecanismo de defesa consiste na aquisição de uma ferramenta ou despesa financeira. Uma mudança de processo e otimização no uso de recursos que já estão implementados, na maioria das vezes, trás um retorno bem legal.
    Para te ajudar nessa jornada, alguns frameworks foram desenvolvidos com estratégias para que você, como profissional de segurança da informação, possa elaborar um plano bem definido e endereçar de forma estruturada as demandas de sua companhia. Neste artigo, vou te apresentar três frameworks  que são amplamente utilizados pelo mercado e ao final compartilho minha experiência sobre a adoção destes modelos.
    Familia de normas ISO 27000 As normas da família ISO/IEC 27000, podendo citar como as mais conhecidas as ISO 27001 e ISO 27002, estão diretamente relacionadas à segurança de dados digitais e sistemas de armazenamento eletrônico.
    Como destaque, posso indicar a ISO/IEC 27032 que literalmente oferece um guia de como estruturar sua operação em quesitos como segurança da informação e segurança de redes.
    Nesta norma, você também encontra uma literatura bacana sobre o relacionamento da área de segurança com o restante dos setores da companhia e te apresenta dicas bacanas de como trazer a nossa matéria para realidade do restante da galera.
    Conteúdo complementar: http://abntcatalogo.com.br/norma.aspx?ID=91527
    NIST Cybersecurity Framework Este framework é bastante reconhecido nos Estados Unidos pela sua presença em empresas públicas e privadas. Para equipes pequenas, pode ser complicado implementar seus controles devido à complexidade considerada no momento de sua concepção.
    O core deste framework é organizados em etapas, executadas de forma sequencial para estabelecer um ciclo de vida para as operações relacionadas a segurança. As etapas são: Identificar, Proteger, Detectar, Responder e Recuperar.
    Conteúdo complementar:  https://nvlpubs.nist.gov/nistpubs/CSWP/NIST.CSWP.04162018.pdf
    COBIT A Associação de Auditoria e Controle de Segurança da Informação (ISACA), produziu uma estrutura de controles para a tecnologia da Informação, o COBIT. Em sua revisão mais recente ele evoluiu para abordar as melhores práticas buscando alinhar funções, processos e tecnologia à estratégia de negócios.
    Apesar deste framework não ser diretamente ligado a segurança da informação, perceba o link entre o problema exposto na introdução deste artigo e o objetivo final do COBIT. Os investimentos de segurança só fazem sentido se minimizam riscos de negócio, independentemente se estão atrelados à correção de uma vulnerabilidade técnica ou conscientização de usuários. Estabelecer estes links é fundamental.
    Conteúdo complementar: http://www.isaca.org/COBIT/Pages/default.aspx
    Conclusão
    Logo de cara, depois de pesquisar um pouco melhor, você vai pensar.. Putz! É coisa para caramba.
    Quando estamos aprendendo assuntos de viés mais técnico, é um grande erro tentar atacar todos as materáis ou tecnologias de uma só vez, com gestão não é diferente.  
    No inicio é importante elaborar um plano de estudo e aos poucos entender qual é a função de cada um destes frameworks na estratégia de segurança.
    Não encare a documentação como uma receita de bolo, mas sim como um mapa que vai te ensinar percorrer o caminho de pedras.
    Ter um norte a seguir, facilita a caminhada.
    Grande abraço e até o próximo artigo!

    barbieauglend
    barbie's hardware 101
    Primeiro passo: Procure por portas de Debug
        
    Na verdade, esse deveria ser o passo zero, já que a primeira coisa que devemos fazer antes de começar qualquer outra coisa é dar uma boa olhada no hardware que você tem na mão.
    Nesse processo, faça uma lista das portas físicas que você tem acesso, conte os pinos que você vê, etc. O seu foco aqui é achar a porta de debug que geralmente é deixada na placa para reparos e suporte técnico.
    Essa porta está disponível em qualquer dispositivo (estamos falando de sistemas embarcados), ou seja, as queridas impressoras, câmeras de segurança, a torradeira no canto da cozinha, o roteador, etc. Outro fato importante: todo dispositivo mais complexo é um pouco mais fácil, pois estará provavelmente usando alguma versão do kernel Linux (que é open source :D). Se você não ver nenhuma porta de Debug diretamente, não desista. Muitas vezes elas estão um pouco escondidas e você terá que abrir o aparelho ou até romper algum lacre. 😉
    As portas de debug são normalmente do tipo serial, UART, possuem de 4 a 6 pinos, e são literalmente desenhadas na placa. Sim, na PCB (placa) geralmente há uma marcação para a porta de debug, contornada, esperando para ser achada. 🙂 Como elas são feitas para o suporte técnico usar, elas não são cobertas, não tem um plug bonitinho esperando por você... Os conectores ou as ilhas (os buraquinhos na placa onde os fios ou terminais de componentes são soldados) vão estar soltos, sem proteção e não estarão conectados a lugar nenhum no dispositivo.
    Aqui o exemplo da placa do roteador FritzBox 4020:

    Localize as ilhas/conectores que não estão em uso e solde os pinos. Se a sua placa tem mais do que um CI (Circuito integrado), procure pelo principal e solde os pinos na porta que esta conectada a ele (geralmente a parte mais interessante está rodando lá de qualquer maneira!). Eu prefiro sempre soldar pinos para que fique mais fácil o acesso.
    Continuando
    É importante entender como o protocolo de transmissão da porta funciona. O protocolo de comunicação UART (Universal Asymchtonous Receiver / Transmitter) difere de outros protocolos de comunicação como o I2C, basicamente por ser um circuito físico do microcontrolador com a única função de transmitir e receber dados de forma serial. A parte boa é que por não ser de propósito geral como o USB, é possível transmitir dados usando apenas dois cabos entre dois dispositivos. Esse tipo de comunicação pode ser chamada de comunicação direta.
    Os dados são transmitidos do pino Tx (transmissor) da origem para o pino Rx (receptor) do destino.
    +-------+                      +-------+
    |                |                       |                |
    |          Tx +--           --> Tx            |
    |                |     `   . .  '         |                |
    |         Rx +-- .  ' `  .  --> Rx            |
    |                |                       |                |
    |      GND +----------+ GND      |
    |                |                       |                |
    +-------+                      +-------+
    Como o próprio nome diz, a transmissão de dados usando o protocolo UART é assíncrona, o que significa que não existe um relógio (sinal de clock) que sincronize os dados de saída do UART de origem com o os dados recebidos pelo UART de destino. Ao invés disso, o UART de origem adiciona bits de sinalização de "início" e  "fim" em cada pacote de dados transmitido. Esses bits irão então definir o começo e o final de um pacote de dados de uma maneira que o UART de destino sabe onde começar e onde terminar a leitura.
    Quando o UART de destino recebe um "bit de início", ele começa a ler os dados recebidos na frequência chamada de baud rate (taxa de transferência). Essa frequência é a velocidade de transmissão de dados em bps (bits por segundo) e os dois UART devem estar operando na mesma frequência (ou com um erro de no máximo 10%) e a estrutura dos dados deve ser a mesma para que a comunicação ocorra sem problemas.
    Achando o output de cada pino:
    Até agora sabemos que as portas seriais possuem entre 4 e 6 pinos. Algo importante é saber que eles operam de acordo com a seguinte especificação ou "pinagem":
    Tx ou Transmitting: esse pino será conectado com o Rx do nosso leitor. Rx ou Receiving: esse pino será conectado com o Tx do nosso leitor. Vcc: POWER \o/ NÃO CONECTE! Geralmente 3.3V ou 5V. GND ou Ground (terra): esse será conectado com o GND do nosso leitor. (DTR e CTS) são dois pinos que não são obrigatórios e normalmente não são necessários. Talvez escreva mais sobre eles na próxima série! 🙂 Tx e Rx possuem o valor 1 / verdadeiro / ligado (por padrão). Proximo passo: Multímetro!
    Claro que você pode simplesmente olhar as conexões desenhadas na sua PCB e usar a técnica da "tentativa & erro", mas dependendo do dispositivo que você está analisando, pode acabar ficando caro 😉 Então o melhor é analisar um pouco mais a sua PCB antes de mandar sinais aleatórios e por fogo em tudo - been there, done that! not fun!
    E não é tão dificil assim 🙂 Geralmente um multímetro simples é o suficiente para nos oferecer a maioria das informações necessárias para dizer quem é quem na nossa plaquinha! Mas é claro que eu nunca direi não a um osciloscópio! 🙂
    O osciloscópio não só nos dá "informações suficientes para deduzir" quem é quem, mas nos mostra exatamente quem é quem, de acordo com as especificações esperadas:
    O pino com valor 0V constante é o GND. O pino com valores constantes 3.3V ou 5V deverá ser o Vcc. Um dos pinos com valor flutuante próximo de 0V deve ser o Rx (faz sentido? Não? Pensa assim, Rx esta "escutando" mas não está conectado a nada ainda!) Yay! Agora que sabemos qual pino é qual, só falta saber a nossa taxa de transferência (baud rate) para montar o nosso leitor. Para descobri-la, a maneira mais facil que conheço é usando um analisador lógico: Conecte seus pinos, ligue (acione) a opção "análise de protocolo" e vai na adivinhação, simplesmente mudando o baud rate até você ver texto na saída de dados serial.
    Tendo o layout dos pinos e a taxa de transferência, nós podemos começar a nos comunicar com o dispositivo. \o/

    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

    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
    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)
    Clique no menu  Selecione Reiniciar ... Mantenha pressionado o ⌘comando-R para inicializar no sistema de recuperação. Clique no menu Utilitários e selecione Terminal. Digite csrutil disable e pressione Enter. Feche o aplicativo Terminal. Clique no menu  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.
     
    Imagem 3: ferramentas objective-see
     Destaco três ferramentas especificas para quem estiver analisando binários do tipo Mach-O:

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

    Imagem 5: ferramenta jtool exibindo o endereço da função main() do binário Mach-O
     
    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  

×
×
  • Create New...