Jump to content

Fernando Mercês

Administradores
  • Content Count

    640
  • Joined

  • Last visited

Everything posted by Fernando Mercês

  1. Essas são algumas gafes que vez ou outra flagramos a galera de TI caindo. Na prática são termos confusos que já foram utilizado de forma inadequada muitas vezes e a intenção deste artigo é desmistificar, de uma vez por todas, alguns conceitos. 😌 Programação em HTML HTML (HyperText Markup Language), como o próprio nome sugere, é uma linguagem sim, mas não é de programação. Para ser, esta precisaria expressar um algoritmo que pudesse utilizar-se de estruturas de controle e loop, por exemplo, recursos fundamentais para qualquer linguagem de programação. Logo, quem sabe HTML sabe criar páginas em HTML. Programar é outra história… Estabilizador não serve pra nada Infelizmente ele serve pra estragar sua fonte. Isso mesmo, o trabalho de estabilização feito por estes componentes é tão mal feito e lento, que ao tentar corrigir uma anomalia na energia ele atrapalha a fonte, que seria capaz de corrigir sozinha, mas é obrigada a esperar o estabilizador fazer seu trabalho porco e corrigir a anomalia duas vezes! O investimento correto deve ser feito na fonte do computador, sem economia porca. Um filtro de linha pode ser prático no caso de um spike (grandes descargas de curto prazo, como as geradas por raios) porque o fuzível dele pode abrir e proteger o da fonte (que é mais chato de trocar porque tem que tirá-la do computador e abri-la), mas proteção mesmo é na fonte e por isso é interessante adquirir uma boa, mas só faz sentido para desktops montados. No mundo atual, onde os laptops dominam, estabilizadores não servem para nada mesmo. 😝 O positivo e negativo da tomada… O tipo de corrente que chega em nossas tomadas é chamado de alternada. Neste tipo não há pólos positivo ou negativo, mas sim fase e neutro. Na fase há a tal alternagem de polaridade, entre positivo e negativo, cerca de 60 vezes por segundo no caso das capitais brasileiras. O neutro é ligado à terra em algum momento e ao conectar algum componente (como uma lâmpada) entre este caminho fase-neutro você faz com que energia passe pelo componente. No caso da lâmpada, ela acende. 💡 Vale a pena estudar o básico sobre corrente contínua e corrente alternada porque o assunto é longo. Baixei o drive da placa de… Você baixou o driver, filhão. Com ‘r’ no final. Drive, sem ‘r’, diz respeito aos dispositivos físicos como drive de CD-ROM, drive de disquete, ZIP drive etc. Já driver, com ‘r’, é um programa responsável por controlar e permitir o uso de um determinado dispositivo em um sistema operacional. Essa placa é show, nem precisa de driver! Ah precisa. 🙏 O que ocorre é que seu sistema já vem com drivers para vários dispositivos e você pode notar o dispositivo funcionando sem precisar instalar nada além do próprio sistema. Na verdade o driver para aquele dispositivo não estava em uso, mas foi instalado e agora está em uso quando da inserção do dispositivo no PC. Vou formatar essa HD aqui Formatar é criar um formato. Não se formata HDs, mas sim partições. Dizer “vou formatar uma partição como NTFS” é correto. Tanto é que pra formatar você precisa especificar um sistema de arquivos desejado. Adicionalmente, o HD (Hard Disk), já que é um disco, possui gênero masculino, então o correto é dizer “o HD”, como em “o disco”. Meu HD não tá particionado! Em geral para instalar um sistema operacional é preciso, sim, criar nem que seja uma única partição e depois disso formatá-la com um filesystem para o qual o sistema operacional tenha driver e saiba trabalhar. Logo, seu disco deverá ter, no mínimo, uma partição e por isso ele está particionado sim. Uma partição. ☝️ Formatar não apaga tudo, rapá! Formatar é criar um formato. A ilusão de que a partição está vazia após a formatação deve-se ao fato de que a estrutura do filesystem presente naquela partição foi recriada e a referência aos arquivos é perdida, por isso os arquivos não são mais exibidos, mas nenhum foi apagado. Lembrando que nos dias atuais apagar de um disco via software é impossível. Uma vez que ele tenha sido escrito/magnetizado, por assim dizer, o que se pode fazer é substituir os bytes contidos nele por exemplo, para simular deleção. Remover os bits (energia) não é impossível, mas não é comum. Põe o HD no congelador que ele volta a funcionar! Embora haja um agrupamento de moléculas sob baixas temperaturas, que são responsáveis inclusive pela falsa sensação de carga de pilhas também colocadas no congelador, o efeito não é comprovado e, caso haja algum benefício, será fulminante. Duvido muito que dê tempo para recuperar algo. HD com problema na placa lógica pode ter a placa lógica substituída, mas HDs com problemas físicos, em geral, não têm conserto. Faz uma formatação de baixo nível que tira os bad blocks! Bad blocks (ou setores defeituosos) caracterizam um problema físico, um câncer que se espalha. Não se resolve hardware com software. É como tentar anexar um suricate num e-mail. Além disso, já parou para se perguntar o que significa formatação de baixo nível, também conhecida como LLF – Low Level Formatting? Uma dica: ZeroFill (famoso programa que preenche com zeros todos os setores do disco) não é. É possível recuperar dados de um HD formatado até X vezes! Repita comigo: HD não se formata (pelo menos não mais, com a extinção da LLF). NINOVO, a formatação de uma partição dá a ela um formato. Se você formatar 4, 7, 12 ou 7038 vezes, dá no mesmo. Os dados continuam íntegros. Agora, se você fizer um zerofill uma vez, já f*** tudo. Se ao invés de zeros, escrever bytes randômicos, piorou. Como comento na gafe 8, não se apaga nada de HD, só se sobrescreve. Esse barulho é a agulha do HD! Quem tem agulha é sua avó! HD tem cabeça de leitura e gravação e ela, pasme, não enconsta na superfície do prato. É eletromagnetismo. Favor esquecer a analogia com toca-discos. Venho por meio deste… Segue em anexo… Não precisa tentar enfeitar. Se você tá escrevendo um e-mail, é óbvio que é por meio dele que vai informar alguma coisa. Difícil seria informar algo “por meio de outro e-mail”. E o que tá anexo, tá anexo. De onde vem esse “em”!? Nunca minha rede foi atacada, nunca pegamos vírus! O ataque bem sucedido não faz ‘barulho’. Quando você não sabe é justamente quando dá certo. 😈 Instala um antivírus gratuito! Esse aqui é muito bom! Trabalhando na indústria de AV eu vi coisas horríveis. O esforço pra tentar ficar na frente dos criadores de vírus é gigante, caro e por vezes frustrante. Todos os antivírus mais caros das companhias com mais dinheiro sofrem para tentar chegar lá. Desculpe, mas os antivírus gratuitos não têm a menor chance de oferecer qualquer segurança. Quem não investe num antivírus pago hoje para computador e smartphone, pra mim, é maluco(a). Não existe vírus pra Linux Este sistema é o mais utilizado em webservers no mundo. Você acha mesmo que não haveria vírus capazes de se propagar neles? Faz uma busca por “linux malware” só. Ah, e o Android, sabia que já atingimos a marca de 1 milhão de vírus diferentes para esta plataforma (que é Linux)? Guarde isso: o crime segue o dinheiro. Não importa o sistema utilizado, se o cibercrime ver valor em ameaças para ele, vão surgir. Gateway da placa rede O Windows é craque em ensinar errado. O que ele chama de gateway na verdade é a rota padrão para pacotes fora da rede local. É Highlander: só pode haver um! Essa zona que ele cria colocando a configuração de gateway por placa de rede só confunde. E mais, tem gente que acha que sempre precisa de gateway ao configurar uma rede no Windows, o que não é verdade. Se você não vai sair da rede local (para internet ou outra rede), não precisa definir uma rota para fora dela. Hackers roubaram… Quem comete crime é criminoso. Quem faz hacking de qualquer coisa é hacker. Uma coisa não implica a outra. Tá fora do ar! ‘Pingei’ e ele não respondeu. O ping dispara pacotes ICMP. Se o alvo ignorar pacotes ICMP, você não vai ter resposta e mesmo assim ele pode estar na rede funcionando normalmente. Com o nmap você tem mais chances de saber se um host está realmente no ar ou não. Quebrei o SSL! Parece coisa de maluco mas já ouvi umas vezes. Não que seja impossível, mas não confunda man-in-the-middle com quebra da criptografia. Tem um abismo entre estes tópicos! Não uso software livre porque ninguém me dá suporte! Primeiro: sim, você já usa, mas não sabe. Segundo: várias empresas nacionais e internacionais oferecem suporte, é só procurar. Terceiro: Muitas das próprias organizações que fazem software livre oferecem versões pagas destes softwares, com suporte. Pois é, livre é diferente de gratuito. Um software pode ser gratuito e proprietário enquanto outro pode ser livre e pago. Legal né? Não dá pra ganhar dinheiro com software livre! Errou. Errou feio, errou rude. O modelo de negócios muda. Há várias empresas e investidores, grandes clientes como bancos e governos. Se você ainda pensa assim, não deve estar ganhando dinheiro no mundo proprietário também, porque o problema está em você! Linux é difícil Outro mito. Há vários casos de crianças que sempre usaram Linux dentro de casa e acharam o Windows difícil quando foram usar na casa de amigos. É questão de costume. MP3 com qualidade de CD Se existisse já seria ruim. Ao ripar de um CD há perda de qualidade, sempre. Na conversão de analógico para digital já há, por isso ainda há tantos amantes do vinil. Lembre-se que MP3 é um codec (compressor/decompressor), e se na maioria dos casos de compressão de áudio há perda, mesmo que seu ouvido não note! Java roda em qualquer lugar! Java só roda em “lugares” que possuem a JVM (Java Virtual Machine). Além disso, há várias máquinas virtuais Java diferentes, com suportes diferentes. Linguagem C é velha Velho é o seu professor que ensina como calcular a média de três alunos no Turbo C. A última atualização da linguagem C foi em 2011 e as principais suítes de compiladores como gcc, llvm, Visual Studio, continuam em constante desenvolvimento. Mais da metade do universo é escrito em C. Portanto, vá estudar C! 👨🏿‍💻 Aperta o Del pra entrar na BIOS! Pelo sangue de Chessus, não dá pra entrar lá não. Pressionando a tecla Del, F2, F10 ou qualquer que seja no seu computador, você acessa o SETUP, que é um software de configuração do BIOS. E é “o” BIOS porque ele é um sistema (Basic Input/Output System), logo, gênero masculino. A voltagem e a amperagem… Talvez de tão faladas já sejam aceitas, mas expressões como essas não soam muito elegantes. Em português, o que é medido em Volts (V) é a tensão elétrica (às vezes chamada de força eletro-motriz ou diferença de potencial). Já o ampère (A) é usado para intensidade de corrente [elétrica]. Espero que não criem ohmagem, wattagem, metragem (opa… essa já criaram!), quilagem, hertzagem… Senha forte: números, letras maiúsculas e minúsculas, caracteres espaciais plunct plact zum! Ok, ok. Aí vem o usuário danadinho e põe “Senha1”. Assim que expira ele muda: “Senha2”. O problema dessa gafe não é a validade técnica mas a efetividade de jogar essa responsa para o coitado do usuário que mal sabe o motivo de ele ter uma senha tão chata. Se você facilita, fica fácil pra força bruta, se dificulta, ele cola no monitor. Solução: PENSA. O sucesso de um ataque de força bruta depende de duas coisas: a complexidade da senha, o que inclui tamanho, não possuir palavras de dicionário, conter alta entropia etc e o poder computacional do atacante. Mas não precisa complicar muito. Uma senha “Meng0==========” pode ser mais forte que “5!8@6_(hiKij9” (que é bem ruim de decorar). Não é feitiçaria, o nome é entropia. Como o link enaltece, o padding (sinais de igual neste caso) deve ser criado pelo usuário, ou seja, se você ensinar seu usuário a criar algo pra colocar “antes e/ou depois” da senha várias vezes, ele vai se lembrar e ainda assim terá uma senha forte. Assim, uma pessoa sensível que queria usar a senha “Amor” agora pode.. Basta adicionar os amantes felizes e saltitantes: “\0/ Amor \0/”. Mais forte, mais fácil de lembrar, mais amor pro seu coração! Curso de hacker Você consegue ensinar alguém a ter ideias? Há vários picaretas no Brasil divulgando e ministrando “cursos hacker”. Todos não passam de leitura de slides e apostilas copiadas de páginas de manuais e tutoriais em sites na Internet. Na sala de “aula” o aluno vê e faz mágica, mas no cenário real a grama muda de cor. 🐴 No entanto, não fique triste, ainda dá tempo de aprender segurança de verdade. Minha internet é de 10 megabaites! O mercado (sempre ele) cria estas babaquices pra vender. A unidade de medida aqui é o Mb (megabit) e não MB (megabyte). Pois é, muda tudo. Se a sua internet é de 10 “megas”, ou seja, 10 Mbps (megabit por segundo) você baixaria aproximadamente 1,25 MB em um segundo, não 10. O macete é dividir por 8 já que 8 bits formam um byte. O mesmo se aplica às placas de rede de 100 Mbps, 1 Gbps, ou as sem fio de 54 Mbps, 108 Mbps, 3G de 1 Mbps etc: é tudo bit, não byte.
  2. Configurar um servidor de mídia pode ser uma boa maneira de aproveitar um PC de baixo desempenho (mas de razoável capacidade de armazenamento). Além de centralizar suas músicas, fotos e vídeos, torná-los acessíveis a dispositivos como smart TVs, smartphones, tablets, video games e outros que possam atuar como clientes de streaming é realmente interessante. Tudo que precisamos para configurar este servidor é de uma LAN ou WLAN e o Debian GNU/Linux. Sem complicação, sem custo. Configurar o Debian com IP fixo No media server, configuramos um Debian Wheezy com IP fixo. A máquina que eu utilizei acessa a WLAN através de um adaptador wireless USB, então eu coloquei uma reserva de IP (através do MAC address) no próprio roteador wireless, fazendo com que o mesmo IP seja entregue ao servidor, mas você configurar de acordo com a topologia da sua rede. A configuração de IP no Debian fica em /etc/network/interfaces. Lembrando que não precisamos de ambiente gráfico. Criando o diretório de mídia Tendo o IP, é hora de criar um diretório que mídia que vai armazenar seus arquivos. Escolhi /home/share, mas poderia ser qualquer outro. Dentro dele vamos criar diretórios separados para, por exemplo, filmes, shows e músicas: $ sudo mkdir -p /home/share/{filmes,shows,mp3} Eu optei por não compartilhar fotos por questões de privacidade. Agora é hora de colocar arquivos nos diretórios, de modo a popular o media server. Para poder gerenciar os arquivos sem privilégios de root, é interessante tornar seu usuário comum dono do diretório share. No meu caso, o nome de usuário é fernando: $ sudo chown -R fernando: /home/share Certifique-se de que os arquivos e diretórios possuem permissões para serem visualizados, mas nada de 777 hein. Se necessário, aplique as permissões padrão neles: $ find /home/share -type d -exec chmod 0755 {} ; $ find /home/share -type f -exec chmod 0644 {} ; Instalando e configurando o minidlna O minidlna [1] é um servidor de mídia para clientes DLNA/UPnP. Resumidamente, enquanto o UPnP é um conjunto de protocolos que permitem dispositivos compatíves se encontrarem numa rede (auto-discovery), o DLNA é um serviço que utiliza UPnP para streaming de mídia. Algumas pessoas preferem usar o MediaTomb, que tem mais recursos que o minidlna, mas este último é bem modesto e simplista. Para instalar é bem difícil: $ sudo apt-get install minidlna Após a instalação, vamos configurar três parâmetros em /etc/minidlna.conf: media_dir=/home/share root_container=B friendly_name=Debian Home Server A opção root_container=B faz com que os dispositivos vejam os três diretórios que criamos abaixo de /home/share, por padrão. Eu configurei assim porque não gosto de localizar mídia por artista, gênero, ano etc já que não mantenho tags IDv3 e similares atualizadas. Se não é o seu caso, pode comentar essa opção para que ela seja configurar com seu valor padrão. Em friendly_name você configura um nome pelo qual os dispositivos clientes reconhecerão seu media server. E por fim, a única opção realmente necessária para o servidor entrar em funcionamento, media_dir, define qual o diretório raiz de mídia. Após salvar o arquivo, é preciso forçar que o banco de dados de mídia (em /var/lib/minidlna) seja recriado: $ sudo /etc/init.d/minidlna force-reload Por padrão, as portas UDP 1900 e TCP 8200 são utilizadas. Se você configurou um firewall no servidor, deve liberá-las. Isto é tudo que você precisa fazer para ter um media server funcional. Vamos agora para o lado cliente. Usando clientes no Linux e Windows Em minha humilde opinião, tá pra nascer um player melhor que o VLC [2]. Além de fazer café, ele é multiplataforma (apt-get install vlc) e age como cliente UPnP tranquilamente. Para isso, instale-o numa máquina que está na mesma rede que o servidor e faça o seguinte: Abra o VLC e vá em “View -> Playlist”. Na navegação do lado esquerdo, expanda “Local Network” e vá em “Universal Plug’n’Play”. O friendly_name do seu media server deve aparecer do lado direito em alguns segundos e você já pode começar o streaming. Usando cliente no Android Usuários de smartphones Samsung já possuem um clinte instalado chamado AllShare. Na verdade ele é um servidor também, e você pode compartilhar os arquivos de mídia do seu aparelho na rede, se for de seu interesse. O VLC para Android [3] suporta UPnP, mas os testes foram feitos com uma alternativa para a época que ainda não havia suporte. A ferramenta utilizado foi o UPnPlay [4], que é gratuito e dá conta do recado. Depois de instalar, basta abri-lo e começar o streaming: Outros clientes Qualquer dispositivo compatível com o UPnP na sua rede vai encontrar seu servidor. Smart TVs, DVD/Blu-ray players, video games (como o PS3) e outros. No caso de outros smartphones como iPhone ou Blackberry, pode ser necessário instalar algum aplicativo, mas duvido que seja difícil. Segue uma lista com vários softwares servidores e clientes [5]. Agora não tem desculpa. Referências [1] http://sourceforge.net/projects/minidlna/ [2] http://www.videolan.org/vlc/ [3] https://www.videolan.org/vlc/download-android.html [5] http://en.wikipedia.org/wiki/List_of_UPnP_AV_media_servers_and_clients#Android
  3. Há algum tempo tinha vontade de escrever algo sobre os padrões da linguagem C e recentemente, motivado por uma discussão na lista exploits-brasil [1] e aproveitando um domingo nublado no Rio de Janeiro, decidi começar. Este artigo vai ajudá-lo a entender (e quando utilizar) os padrões de programação da linguagem C. Não ache que a utilidade é teórica, pois não é. Seguir um padrão pode ajudar na segurança de código, otimização e até aumentar a produtividade, como veremos mais a diante (sabia que C tem o tipo boolean, por exemplo?). 1. Introdução A maioria das linguagens de programação seguem padrões abertos, que os criadores de compiladores (ou interpretadores) precisam seguir para suportar a linguagem completamente. A linguagem C foi criada por Denis Ritchie (falecido em 2011) entre 1969 e 1973 com o objetivo de substituir o Assembly (o que explica a existência comandos como goto na linguagem). Desde então, várias versões de compiladores foram criadas, por várias pessoas e empresas, sem seguir uma especificação oficial. Por isso, em 1983, o American National Standards Institute, mais conhecido pela siga ANSI, formou um comitê para estabelecer um padrão, que só foi concluído em 1989. 2. Linha do tempo da linguagem C 1969-1973: Denis Ritchie cria a linguagem C 1983: O ANSI cria um comitê para especificar a linguagem 1989: A especificação ANSI X3.159-1989 é concluída. O nome informal dela é C89 e também é conhecida por ANSI C. 1990: O ISO (International Organization for Standardization) assume a especificação de linguagem e cria um grupo de trabalho (working group) chamado WG14 [2] para cuidar da especificação do C, que é renomeada para ISO/IEC 9899:1990, informalmente conhecida como C90. Não houve mudanças na linguagem. Esta especificação se mantém por vários anos e define a linguagem C que “todos conhecem”. 1999: O WG14 publica a especificação ISO/IEC 9899:1999, informalmente chamada de C99. Esta já traz várias novidades que serão tratadas mais à frente neste artigo. 2011: O WG14 publica a ISO/IEC 9899:2011, conhecida como C11. Este é o padrão atual da linguagem. Vários recursos cobertos nas especificações mais recentes já eram implementados pelos desenvolvedores de compiladores por sua conta. No caso do GCC, por exemplo, o Projeto GNU criou os padrões GNU89, GNU99 e GNU11 com suas próprias extensões. O Visual C++ da Microsoft também tem várias extensões da linguagem (lembre-se que em geral qualquer compilador C++ compila C, pois a linguagem é a mesma, mas a recíproca não é verdadeira). Vou mostrar alguns recursos das especificações atuais, utilizando o gcc 4.6 para os exemplos. A opção -std do gcc define qual especificação usar. Já a -pedantic faz o gcc alertar sobre qualquer construção que seja proibida pela especificação, de forma bem rigorosa. 3. Recursos da C99 Tipo booleano: Incluindo a stdbool.h, você pode declarar variáveis do tipo bool e usar os estados verdadeiro e falso, assim: #include <stdbool.h> void main() { bool var; if (var) var = false; } É fato que isso já era feito usando-se 1 e 0 e na verdade os estados true e false não passam de outros nomes (#define) para estes números, respectivamente. No entanto, a legibilidade do código melhora com o tipo bool. Comentários com // A C89 só permite comentários entre /* e */, o que dá um trabalho extra para comentários de uma linha. Usando a C99, você pode iniciar a linha com //, como no C++, PHP etc. e esta linha será um comentário perfeitamente válido. Funções em linha Quando você chama uma função no código, o compilador normalmente gera uma instrução CALL (do Assembly) para desviar o fluxo de código para o endereço da função. Isso envolve passagem de parâmetros pela pilha de memória (stack), retorno etc. Ou seja, isto gera um ônus de processamento e consumo de memória considerável, principalmente associado ao número e tamanho dos argumentos. Um código que não use funções é mais otimizado, no entanto, é mais difícil de manter. Para resolver isso, você pode dizer que uma função é em linha (inline). Dessa forma, toda vez que a função for chamada, o código dela será copiado para o trecho onde a chamada aconteceu. É como se você copiasse e colasse a função em vários trechos, mas sem precisar fazer isso. Imagine que temos a função: inline int soma(int num1, num2) { return num1 + num2; } Como ela foi declarada como inline, ao chamá-la por exemplo como: var = soma(3, 5); O compilador vai substiuir por: var = 3 + 5; Vetores de tamanho variável Na C99 você pode declarar um vetor assim: void funcao(int numero) { char vet[numero]; } O tamanho do array de char vet depende do parâmetro recebido. Isso era resolvido antes com funções para alocação dinâmica. Há adições também do tipo complex, para números complexos (e imaginários) e muito mais novidades. No site do WG14 [2] há toda a especificação para download. Para utilizar a C99 com o gcc, use: $ gcc -std=c99 fonte.c 4. Recursos da C11 Novas funções seguras (com bound checking) As funções tidas como inseguras como strcpy, strcat etc ganharam novas versões sufixadas com “_s”, de safe. Basicamente elas recebem um argumento a mais que é o número de caracteres para trabalhar. São elas: Multithreading (thread.h) Apesar de os SOs modernos fornecerem funções de API para multithreading, ao ser inserida oficialmente no C possivelmente vai ajudar na portabilidade de aplicativos que utilizem este recurso. É algo muito bom para a linguagem. Estruturas e uniões anônimas Útil quando estão aninhados, este foi um recurso que senti falta quando programei a primeira versao do pev, que usa muitas estruturas. Se queremos que um campo de uma estrutura seja outra estrutura, antes da C11, é preciso fazer: struct ponto { int x; int y; } struct reta { struct ponto p; } Aí para acessar: struct reta r; r.p.x = 10; Com a C11, a estrutura reta pode ser: struct reta { struct ponto; } E o acesso: r.x = 10; Dizemos então que a estrutura reta possui uma estrutura anônima (sem nome) como campo. 5. Qual especificação usar? Na hora de usar uma especificação e seguir suas regras, você deve pesar como é o seu projeto, em quais sistemas ele vai rodar, como será distribuído etc. Por exemplo, se é um projeto interno e o compilador que você utiliza suporta a C99, não vejo por que não utilizá-la. Agora, se você vai distribuir seu código, aí já tem que pensar nas versões dos compiladores de quem for baixar. Será que o gcc do usuário do seu software é a última versão? Será que suporta a C11? No hdump, um dumper hexa/ASCII que fiz para usar no Windows e no Solaris, eu optei por trabalhar com C89 por dois motivos: o código é pequeno e eu precisava levar o fonte para compilar em versões antigas do Solaris, com compiladores que não suportavam a C99, por exemplo. Já para o pev um analisador de binários de Windows, eu comecei em ANSI mas já estou atualizando para C99, para deixar o código mais legível e utilizar os novos recursos. Eu distribuo o fonte e binários já compilados, inclusive para Windows, então não tem problema para os usuários. O importante é ler as especificações e perceber se tem algo que te ajuda/atrapalha no projeto, pensando no escopo, usuários e sistemas alvo. Só a C11 que acho que ainda não dá para distribuir um código em produção, visto que nenhum compilador à suporta completamente. Recomendo usá-la internamente e esperar um pouco antes de liberar código com ela, pois está muito recente. 6. Conclusão Existem outros recursos interessantes na C11 [3] mas ainda não conheço um compilador que a suporte 100%. Na verdade o gcc também não suporta a C99 por inteiro ainda. Existe até uma página onde você pode acompanhar as mudanças no suporte à C99 [4]. Na versão 4.6, o gcc já estreiou o suporte inicial à C11, sob a opção -std=c1x. A versão 4.7 já vai vir com suporte a mais recursos desta especificação, sob a opção -std=c11. Inslusive cabe um comentário: é muito bom poder contar com um conjunto de compiladores constantemente atualizado e bem feitos como são os do GCC. Essa maravilhosa suíte inclui compiladores para C, C++, Objective-C, Fortran, Java, Ada, e Go. Todos livres. É um espetáculo mesmo. O último rascunho ISO/IEC 9899:201x, divulgado em abril do ano passado está disponível em PDF [5] mas a especificação mesmo, publicada em dezembro, está à venda por cerca de R$ 450,00 na ISO Store [6]. Referências [1] https://groups.google.com/forum/?fromgroups#!forum/exploits-brasil [2] http://www.open-std.org/JTC1/SC22/WG14/ [3] http://en.wikipedia.org/wiki/C11_%28C_standard_revision%29 [4] http://gcc.gnu.org/gcc-4.6/c99status.html [5] http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf [6] http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=57853
  4. 1. Introdução Dumps de memória, discos ou arquivos binários em geral podem conter outros arquivos dentro. Por exemplo, um arquivo TAR (agrupado, sem compactação) praticamente concatena vários arquivos e um só. O arquivo resultante contém todos os bytes dos arquivos de entrada e mais algumas informações que o formato TAR exige. O mesmo acontece com dumps feitos de memória gerados com software de cópia bit a bit, como o dd, parte integrante do coreutils [1], do projeto GNU. Estes podem, e geralmente é que acontece, conter vários arquivos de diferentes tipos. Neste artigo mostrarei uma forma manual, usando ferramentas básicas, para extrair dados de dentro destes dumps. Ao ministrar um dos cursos de segurança na 4Linux, gerei este desafio: O objetivo é extrair dados válidos de um dump de 1 MB fornecido. Dentro dele, há dois arquivos. Não sabemos que tipo de arquivo há dentro do dump, então temos que procurar. NOTA: Este texto é didático e você pode seguir utilizando uma máquina com Linux para fazer a análise do dump. Basta baixar o binário dump.bin. 2. Como procurar tipos de arquivo Antes de tudo, é preciso saber o que se vai procurar. Você pode querer procurar milhares de tipos diferentes, mas terá que conhecer o formato de cada um deles para identificar possíveis bytes dentro do dump. Neste artigo vamos buscar por arquivos BMP e GIF mas este texto pode servir de guia para buscar qualquer tipo. Para isso precisamos estudar o a especificação de cada formato. Este tipo de documento quem fez é o desenvolvedor do formato e é particularmente interessante para desenvovledores de software que trabalharão com imagens como editores, visualizadores, browsers etc. A Wikipedia é uma ótima fonte para isso, mas o site dos consórcios que desenvolvem e mantêm os padrões também podem ser utilizados. Basta googlar por “<extensão> file format” e você achará muitos links. 3. Analisando a especificação do bitmap (BMP) Para o nosso interesse, que não é desenvolver um aplicativo que trabalhe com o formato, mas sim entender o básico do formato, a parte mais interessante está na estrutura de um arquivo bitmap [2], onde cada grupo de bytes de um arquivo deste tipo ganha um significado. O que queremos é saber os bytes que definem o início de um arquivo bitmap. Em outras palavras, queremos saber que bytes estão no cabeçalho (header) de um arquivo bitmap. Veja que na especificação consta: “the header field used to identify the BMP & DIB file is 0x42 0x4D in hexadecimal, same as BM in ASCII” Achamos. Isso nos leva a crer que um bitmap tradicional começa com os bytes 0x42 e 0x4d, o mesmo que BM em ASCII. Sabendo disso podemos checar as strings dentro de nosso dump e fazer um filtro pela string “BM”, certo? 3.1 Buscando a string que identifica um bitmap Para isso, nada mais indicado que o strings, do conjunto binutils [3], também do projeto GNU (thanks FSF!). $ strings -t x dump.bin | grep BM 6ae6d BMP& 8f699 g,UBM_ be081 ?O~BMY ed589 YBMG f63c2 xBM5 A opção “-t x” do strings faz com que ele imprima os endereços (posições) em hexa das strings que encontrou. NOTA: Já se perguntou como funciona o strings? Uma ideia é ler cada byte e testar se este é imprimível, ou seja, tem representação gráfica na tabela ASCII. Caso seja imprimível, só mandar pra tela. Um código de exemplo seria: #include <stdio.h> #include <string.h> int main(int argc, char *argv[1]) { unsigned char byte; FILE *fp = fopen(argv[1], “rb”); while (fread(&byte, sizeof(unsigned char), 1, fp)) putchar(isprint(byte) ? byte : ‘n’); fclose(fp); } Código apenas para fins didáticos. Precisaria de melhorias se fosse usado pra valer. De acordo com a saída do strings, parece que temos 5 bitmaps dentro deste arquivo. Será? Na verdade não podemos garantir. Os bytes referentes aos caracteres ‘B’ e ‘M’ podem ser parte de outro arquivo, de uma música, de um texto ou simplesmente parte de uma instrução de algum programa. Não pode-se garantir que é um bitmap. Temos que analisar! 3.2 Analisando a primeira ocorrência da string BM Sabemos a posição da primeira ocorrência porque o comando strings nos deu. Agora vamos usar um visualizador hexa/ascii para ver o que há próximo destes bytes. No conjunto coreutils há o od, mas eu estou acostumado ao hexdump (hd), do BSD: $ hd -s 0x6ae6d -n 32 dump.bin 0006ae6d 42 4d 50 26 bf 29 c1 f3 f3 81 f0 99 aa f0 aa fd |BMP&.)……….| 0006ae7d d3 d1 e0 3e 92 54 84 b1 18 74 4e b5 00 c1 ce 06 |…>.T…tN…..| A opção “-s 0x6ae6d” faz com que o hd pule (skip) essa quantidade de bytes a partir do início do arquivo, caindo diretamente em nossa string. Já a opção “-n 32” faz com que o hd mostre apenas bytes à frente. Só estamos dando uma olhada, não precisamos ver o arquivo inteiro neste momento. A documentação também diz que os próximos 4 bytes de um bitmap representam o tamanho do arquivo em bytes. Então o tamanho da nossa *possível* imagem seriam os 4 bytes “50 26 bf 29”. Organizando-dos em little-endian (de trás para frente) pois estes representam um número inteiro e não texto, temos 0x29bf2650. Quanto dá isso em decimal? O bash responde: $ echo $((0x29bf2650)) 700393040 O bc também responderia (mas as letras precisam estar em maiúsculo): $ echo “ibase=16;29BF2650” | bc 700393040 Quanto dá isso em megabytes? $ echo $((0x29bf2650 / 1024 / 1024)) 667 Então nosso provável bitmap tem mais de 700 milhões de bytes, aproximadamente 667 MB. Impossível para um dump de 1 MB, certo? Descartado… esses bytes não fazem parte de um bitmap e foi somente coincidência a string “BM”, ainda mais seguida de um “P” (eu fiz isso sem querer hehe). Deixarei para o leitor analisar as outras ocorrências da string “BM”. Vá se divertir! 4. Buscando a string que identifica um GIF Agora vamos buscar GIF. Assim como fizemos com o bitmap, vamos olhar a especificação do GIF [4]. De cara, vemos o header da última versão do formato, que deve ser composto pelos bytes 0x47 0x49 0x46 0x38 0x39 0x61, que representam em ASCII a string “GIF89a”. Se quiser confirmar, pode usar o comando ascii do Linux: $ sudo apt-get install ascii $ ascii Então vamos ao grep: $ strings -t x dump.bin | grep GIF89a 80000 GIF89ae Opa, casamos com a string inteira. Seria muita coincidência esses 6 caracteres casarem se isso não for o início de um GIF? Não sei. Vamos analisar: $ hd -s 0x80000 -n 64 dump.bin 00080000 47 49 46 38 39 61 65 01 1a 02 f7 00 00 bb 9c 91 |GIF89ae………| 00080010 6e 5b 54 d9 ad 9c 6b 69 69 ca a4 94 e3 8c 7d 3f |n[T…kii…..}?| 00080020 1e 0e 33 0d 09 f6 cb ba e1 b4 a3 e3 bb aa eb ca |..3………….| 00080030 8a c8 44 39 0b 05 04 6b 26 21 44 27 19 fa f2 e4 |..D9…k&!D’….| A documentação diz que depois do header de 6 bytes, temos 2 bytes identificando a largura (0x165) e mais 2 para a altura (0x21a), o que definiria um GIF de 357×538 pixels. Aceitável… O próximo byte, segundo o exemplo na documentação, quando é 0xf7, diz que o GIF tem 256 cores, dentre outras informações. Este byte bate com o nosso 0xf7. Parece mesmo ser um GIF… Vamos testar se é um GIF mesmo? Não precisamos ir até o final checando todos os campos. A maioria dos formatos de arquivo definem um ou mais bytes para marcar o fim do arquivo. No caso do GIF, veja na documentação que os últimos bytes são 0x00 (end) e 0x3b (GIF terminator). Lembre-se que isso é *específico* do formato GIF e não é regra para qualquer tipo de arquivo. Cada um tem seu formato. NOTA: Os formatos que não possuem bytes que marcam o fim geralmente possuem um campo que diz o tamanho completo do arquivo, como vimos no bitmap. 4.1 Estratégia para extrair o possível GIF do dump Podemos usar a estratégia de extrair os bytes desde o ‘G’ do “GIF89a” até a primeira sequência 0x00 0x3b. Se for um GIF real, funcionaria, concorda? Vamos buscar então: $ hd -s 0x80000 dump.bin | grep –color “00 3b” 00082b60 3d 97 87 80 00 00 3b 55 d9 60 ab 23 eb 24 ac f1 |=…..;U.`.#.$..| 000ef4e0 2b 61 8d 77 00 3b 7c 58 62 23 4a dc 4c 15 42 da |+a.w.;|Xb#J.L.B.| A primeira sequência que “00 3b” que o grep encontrou após o offset 0x80000 (início do GIF) está em 0x82b65, conseguiu visualizar? É só ir somando 1 em hexa, para cada byte. Neste offset está o 0x00 e em 0x82b66 está o 0x3b. Seguindo nossa teoria, o próximo byte, 0x55 em 0x82b67 não faz parte do GIF. Se diminuirmos este valor de 0x80000, teremos o tamanho total do suposto GIF. Vamos ver: $ echo $((0x82b67-0x80000)) 11111 A resposta é em decimal. Só lembrando algumas informações que leventamos: Tipo: GIFResolução: 357×538 pixelsTamanho: 11111 bytes (11 KB) 4.2 Extração com o dd Se estivermos certo, veremos um GIF quando extrairmos esses bytes. Para isso, podemos usar o dd: $ dd if=dump.bin of=possivel.gif bs=1 count=11111 skip=$((0x80000)) 11111+0 registros de entrada 11111+0 registros de saída 11111 bytes (11 kB) copiados, 0,036675 s, 303 kB/s A opção bs (block size) informa ao dd para assumir blocos de 1 byte. A opção count diz quantos blocos ele vai ler, então vão dar exatamente 11111 bytes. E por fim a opção skip, que só aceita números em decimal (por isso a conversão com o bash) informa quantos bytes o dd vai pular para começar a ler, assim como a opção “-s” do hd. Temos que começar a extração do byte 0x80000, que é onde começa o GIF, lembra? Vamos conferir: $ file possivel.gif possivel.gif: GIF image data, version 89a, 357 x 538 $ wc -c possivel.gif 11111 possivel.gif Agora é só visualizar… 5. Ferramentas para automatizar o trabalho Agora a notícia chata depois que você leu tudo isso: existem ferramentas que fazem tudo isso de maneira automatizada. Uma delas é o foremost [5], que conhece vários tipos de arquivo e é capaz de extrair com precisão a partir de dumps como este. No entanto, considero que mais importante que conhecer ferramentas é saber como elas funcionam e/ou como fazer as coisas sozinho. Sem elas. Say no to script kiddies! 6. Referências A ilustração utilizada neste artigo foi gentilmente cedida por Anderson Barros [6] e seu uso foi autorizado. [1] http://www.gnu.org/software/coreutils/ [2] http://en.wikipedia.org/wiki/BMP_file_format#Bitmap_file_header [3] http://www.gnu.org/software/binutils/ [4] http://en.wikipedia.org/wiki/Graphics_Interchange_Format#Example_GIF_file [5] http://foremost.sourceforge.net/ [6] http://anderson-barros.blogspot.com
  5. 1. Introdução As mídias USB, e em especial os pen drives, são responsáveis por boa parte das infecções por vírus e malwares nas estações de trabalho e até servidores utilizados por usuários incautos. Um simples “espetar” de um pen drive USB pode passar por cima de todas as medidas segurança implementadas no ambiente de rede. Pensando nisso, resolvi buscar uma forma de imunizar uma mídia USB, de forma passiva, de modo que a proteção fique na mídia e não na máquina. Não existe milgare, mas é possível diminuir bastante o risco de infecção com a técnica que apresentarei. 2. Como a infecção ocorre Quando uma mídia USB é inserida numa máquina com Windows, o recurso de autoreprodução é iniciado. Se na raiz da mídia existir um arquivo autorun.inf, este é lido pelo sistema e suas instruções são processadas, tudo de forma automática. A técnica mais eficiente para infectar uma mídia USB é, então, copiar para a mídia o malware em si (geralmente um binário PE) e criar também um autorun.inf na raiz da mídia que execute o malware toda vez que a mídia é inserida numa máquina com Windows. Este processo é explicado no texto “Como remover vírus de pen drive” [1], que escrevi em janeiro de 2010. 3. A ideia da proteção Há várias tentativas de evitar essas infecções, desde programas simples que tentam remover indícios de vírus nas mídias USB até softwares mais complexos, apelidados de “firewalls” USB, que permitem controlar todo o tráfego. O problema é que a máquina é protegida, mas não a mídia USB. Se você usa o pen drive, por exemplo, em outra máquina, ele pode ser infectado e servir de meio de disseminação, pois os softwares que protegem a USB estão na sua máquina e não no pen drive. Como todos os malwares que analisei criam um autorun.inf para se autoreproduzir, a ideia é criar um autorun.inf sadio no raíz da mídia USB, que seja “inalterável”, “indeletável”. Dessa forma, o malware não conseguirá sobrescrever o autorun.inf saudável pelo seu malicioso e o executável do malware pode até ser copiado para a mídia, mas não será inicializado quando a mídia for inserida num Windows qualquer, ou seja, ficará simplesmente armazenado na mídia, mas inofensivo (a não ser que algum usuário encontre manualmente o executável e o execute). Mas será possível? Vamos aos testes. Verificando a possibilidade Depois de algumas buscas sobre criação de arquivos “indeletáveis”, encontrei o Panda USB Vaccine [2], que promete fazer exatamente o proposto. Ele realmente funciona e cria um autorun.inf “indeletável” no raíz da mídia USB. O inconveniente, para mim, é que ele é um software de código fechado e não há documentação sobre como ele faz isso, além de rodar somente em Windows. Eu estava desenvolvendo o USBForce [3], um software livre para proteção contra pragas em mídias USB quando encontrei o Vaccine. Naturalmente eu gostaria de implementar essa funcionalidade de criação de um autorun.inf “indeletável” no meu software. Além disso, não conseguiria dormir sem saber como (tecnicamente) este arquivo é criado e se era mesmo indeletável. 4. Revertendo a técnica do Vaccine: Utilizei algumas ferramentas para saber como o Vaccine funciona, começando pelo RDG Packer Detector [4], que além de outras funcionalidades, consegue saber qual o compilador utilizado para gerar o binário, informação muito útil antes do debugging: O RDG informa que não há packer e que o software foi escrito no Borland C++. Não se pode tomar tudo como verdade, mas é um bom ponto de partida. Além disso, o RDG detecta uma chamada à função IsDebuggerPresent [5]. Alguns softwares se encerram ao perceberem que estão sendo debugados e esta função é uma das formas de ter essa percepção. Geralmente o compilador coloca algumas informações interessantes na seção .data do executável gerado. Não custa olhar rapidamente o que tem lá. Para isso eu usei o pev [6] com a opção “-s”, para exibir informações sobre as seções do executável: > pev -s USBVaccine.exe | more Sections: Name: .text Virtual size: 0xd9000 Virtual address: 0x1000 Data size: 0xd8600 Data offset: 0x600 Characteristics: 0x60000020 (01100000000000000000000000100000) contains executable code is executable is readable Name: .data Virtual size: 0x1d000 Virtual address: 0xda000 Data size: 0x12c00 Data offset: 0xd8c00 Characteristics: 0xc0000040 (11000000000000000000000001000000) contains initialized data is readable is writable Sabendo o offset (posição) onde a seção .data começa, usei o hdump [7] para espiar os bytes: > hdump USBVaccine.exe | findstr “d8c[0-9]0″ 000d8c00 43 6f 64 65 47 65 61 72 20 43 2b 2b 20 2d 20 43|CodeGear C++ –C| 000d8c10 6f 70 79 72 69 67 68 74 20 32 30 30 38 20 45 6d|opyright 2008Em| 000d8c20 62 61 72 63 61 64 65 72 6f 20 54 65 63 68 6e 6f|barcaderoTechno| 000d8c30 6c 6f 67 69 65 73 00 00 00 10 40 00 b2 12 40 00|logies….@…@.| 000d8c40 b2 12 40 00 22 15 40 00 01 00 00 00 00 00 00 00|..@.”.@………| 000d8c50 fc 64 40 00 00 00 00 00 00 00 00 00 00 00 00 00|.d@………….| 000d8c60 30 99 4e 00 4c ad 4e 00 50 ad 4e 00 68 d0 4b 00|0.N.L.N.P.N.h.K.| 000d8c70 c0 d1 4b 00 50 d2 4b 00 e4 d3 4b 00 b0 93 4e 00|..K.P.K…K…N.| 000d8c80 00 00 f8 ac 4e 00 74 ad 4e 00 ec aa 4e 00 00 00|….N.t.N…N…| 000d8c90 00 00 00 00 00 00 00 00 00 00 00 90 00 00 00 00|…………….| De fato, parece que o binário foi escrito em Borland C++, mas com uma versão mais atual (a Embarcadero comprou a divisão CodeGear da Borland). Vamos à execução do software: O que procuramos é o código do evento “click” do botão “Vaccinate USB”, certo? Existem ferramentas específicas para se trabalhar com binários gerados em versões específicas de compiladores. No caso do BC++, há uma ferramenta particularmente interessante chamada E2A (Event To Address), que consegue dizer o endereço de cada evento do software, o que facilita muito a vida: O endereço da função que o evento “click” do botão chama é 0x404C58. Agora é debugar. Usei o OllyDbg [8] para isso. A ideia é colocar um breakpoint neste endereço, rodar o Vaccine no debugger, clicar no botão para vacinar a mídia e seguir passo a passo o que o software faz, a partir do breakpoint. 5. Entendendo a proteção: Conforme é mostrado no vídeo, o Vaccine cria um arquivo AUTORUN.INF normal na raíz da mídia, mas depois busca pela entrada deste arquivo na FAT para localizar o byte que controla os atributos. Segundo a documentação [9], é o byte depois da extensão do arquivo. Para facilitar a compreensão, usei o Hex Workshop [10], que tem o recurso de editar discos e volumes lógicos. Além disso, ele permite a criação de estruturas personalizadas para interpretar os bytes visualizados. Criei uma estrutura simples (fat32-directory-table.hsl) para reproduzir a Directory Table da FAT-32. O byte em questão é um bitfield, onde cada bit corresponde a um atributo:Bit Máscara Atributo 0 0x01 Somente leitura 1 0x02 Oculto 2 0x04 Sistema 3 0x08 Nome de volume 4 0x10 Subdiretório 5 0x20 Arquivo 6 0x40 Não usado 1 7 0x80 Não usado 2 Lembrando que no vídeo mostrei que o Vaccine grava esse byte como 0x20 (Arquivo) e depois o busca em disco e altera para 0x42 (Não usado 1 + Oculto). É aí que mora a mágica. Não sei o motivo, mas quando o atributo 0x40 está setado, não é possível ler, executar, modificar ou excluir o arquivo. Fiz testes com os outros atributos mas o comportamento esperado só se dá com este bit 6 setado mesmo. Tentativas de implementação Eu tentei, ingenuamente, implementar o recurso no USBForce, que foi escrito em VBScript, mas a propriedade “Attributes” é um enum de valores pré-definidos e não aceita qualquer valor, portanto não aceitou o 0x42: Set objFSO = CreateObject(“Scripting.FileSystemObject”) Set objFile = objFSO.GetFile(“F:AUTORUN.INF”) objFile.Attributes = &H20 ‘Arquivo WScript.Echo “0x” + Hex(objFile.Attributes) objFile.Attributes = &H40 ‘Não usado 1 WScript.Echo “0x” + Hex(objFile.Attributes) O código acima prevê que já haja um arquivo autorun.inf no volume F. Ele imprime 0x20 e depois 0x0, ou seja, a tentativa de ativar o atributo não usado falha. Tentei também usando a API do Windows diretamente, em 😄 #include <windows.h> int main(void) { HANDLE out = CreateFile(“f:\autorun.inf”, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0x40, NULL); CloseHandle(out); return 0; } O resultado foi um arquivo criado com 0x20 (Arquivo) e não com o 0x40 que pedi. Por enquanto vou adotar como solução possível a busca diretamente na FAT pela entrada do arquivo e modificação bruta do byte de atributos, como o Vaccine faz. 6. Prova de Conceito Como incentivador do software livre, não poderia deixar de implementar esta técnica em código aberto e para funcionar no GNU/Linux. Por isso escrevi um programa para realizar o trabalho, que batizei de OpenVaccine [11]. Por enquanto ele é só uma prova de conceito e seu uso não é encorajado em ambientes de produção. Referências [1] https://www.mentebinaria.com.br/artigos/segurança/como-remover-vírus-de-pen-drive-r5/ [2] http://www.pandasecurity.com/homeusers/downloads/usbvaccine [3] https://github.com/merces/usbforce [4] http://www.rdgsoft.net [5] http://msdn.microsoft.com/en-us/library/ms680345(VS.85).aspx [6] https://github.com/merces/pev [7] https://github.com/merces/hdump [8] http://www.ollydbg.de [9] http://www.nationmaster.com/encyclopedia/VFAT#Directory_Table [10] http://www.hexworkshop.com [11] http://openvaccine.sf.net
  6. Aproveitando que farei uma apresentação sobre pacotes deb na comemoração do 18º aniversário do Debian GNU/Linux, decidi escrever este artigo, para mostrar o que é um pacote deb e como ele pode ser gerado de forma quase artesanal. O objetivo deste artigo é mostrar o que é um pacote deb e como ele se comporta. Mostrarei como olhar o conteúdo de um pacote deb a fim de copiar sua estrutura para gerar um novo, coisa que fiz quando comecei a me interessar pelo assunto. Este artigo faz parte de um objetivo maior, que é atrair colaboradores para a comunidade Debian. Um pacote “caseiro” não será aceito nos repositórios oficiais, mas iniciará o leitor no mundo do empacotamento e, quem sabe, despertará o Debian Maintainer que há em você. 1. Introdução Imagine um mundo sem pacotes deb. Todos os softwares livres seriam distribuídos em código-fonte. Você seria obrigado a baixar, compilar e instalar. Fácil? Nem sempre. Os programas possuem dependências de bibliotecas que o desenvolvedor usou. Na máquina dele essas bibliotecas estão instaladas, na sua provavelmente não. Então seria preciso instalá-las antes de compilar o programa que você quer. Claro que os jargões utilizados aqui só fazem sentido para usuários avançados. O Linux não teria conquistado a popularidade que tem hoje se os usuários leigos tivessem que compilar o Firefox para navegar na internet. Os pacotes supriram esta necessidade e hoje um software inteiro com suas dezenas de dependências pode ser instalado graficamente ou com uma curta e simples linha de comando. Cabe aqui um viva ao software livre. o/ Este artigo não é para leigos, mas para quem gostaria de entender como o pacote deb funciona. 2. Pacote “caseiro” x pacote oficial Para fazer um pacote e submetê-lo ao repositório oficial, você precisa ler, compreender e seguir rígidas regras de qualidade, que podem ser encontradas nos documentos oficiais [1]. No entanto, creio que o modo mais fácil é começar por um pacote “caseiro”, que não segue a política, para entender o funcionamento e então partir para o estudo dos documentos. Foi assim que deu certo comigo e hoje mantenho o pacote do pev [2] no repositório oficial (testing). A comunidade Debian oferece uma série de ferramentas para criação de pacotes, sempre com foco nos pacotes oficiais. Neste artigo evitarei usar tais ferramentas, mas vou comentar, para que o leitor já se ambiente. 3. Do que é feito o pacote deb Se você der uma olhada em no diretório /var/cache/apt/archives provavelmente vai encontrar vários pacotes deb. Eles estão lá porque foram baixados por você seja via Synaptic, apt-get, aptitude ou outro gerenciador de pacotes que use o dpkg. Antes de usar o dpkg (a ferramenta oficial) para analisar um pacote deb, vamos ver do que um pacote deb é feito. Escolhi como exemplo o pacote do wget. $ ls -lh wget* -rw-r–r– 1 fernando fernando 717K Aug 17 00:26 wget_1.12-5_amd64.deb Vamos copiar o pacote para o /tmp, para manter o cache intacto: $ cp wget_1.12-5_amd64.deb /tmp No diretório /tmp, podemos usar o comando file para ver o tipo de arquivo do pacote deb: $ cd /tmp $ file wget_1.12-5_amd64.deb wget_1.12-5_amd64.deb: Debian binary package (format 2.0) A libmagic (usada pelo file) reconhece o pacote corretamente. Mas será que os desenvolvedores criaram realmente um tipo de arquivo completamente novo para armazenar o conteúdo de um programa? Sabemos que dentro de um pacote deb há os arquivos executáveis do programa, documentação, ícones etc. Não seria viávei utilizar um agrupador de arquivos com compressão ou coisa do tipo? Eric Raymond, um dos hackers mais respeitados do mundo detém a seguinte crença, escrita em seu documento “How to become a hacker” [3]: “No problem should ever have to be solved twice” (Nenhum problema deve ser resolvido duas vezes). Ou seja, não é preciso “reinventar a roda”, como dizemos popularmente. Com base nesta inteligente frase, os desenvolvedores do dpkg e do formato deb usaram sim o que já exitia para atingir seus objetivos, o que foi brilhante. Na página de manual do formato deb(5), podemos ler: “The file is an ar archive with a magic value of !<arch>.” Para conferir, comande: $ man deb Então estamos falando de um arquivo ar [4]. Conforme você pode ver na referência, ar é um utilitário do conjunto binutils, do projeto GNU, para criar, modificar e extrair arquivos. É um agrupador, assim como o tar. Vamos conferir o magic value como disse o man? $ hd -n 64 wget* 00000000 21 3c 61 72 63 68 3e 0a 64 65 62 69 61 6e 2d 62 |!<arch>.debian-b| 00000010 69 6e 61 72 79 20 20 20 31 33 31 31 34 35 31 34 |inary 13114514| 00000020 35 31 20 20 30 20 20 20 20 20 30 20 20 20 20 20 |51 0 0 | 00000030 31 30 30 36 34 34 20 20 34 20 20 20 20 20 20 20 |100644 4 | Certinho. O tal “!<arch>” esta no início do arquivo, o que sugere ser o seu magic number. Vamos listar o conteúdo deste arquivo com a ferramenta ar então: $ ar tv wget* rw-r–r– 0/0 4 Jul 23 17:04 2011 debian-binary rw-r–r– 0/0 2432 Jul 23 17:04 2011 control.tar.gz rw-r–r– 0/0 731281 Jul 23 17:04 2011 data.tar.gz Então temos um arquivo ar com três arquivos. E se criarmos um ar com um arquivo qualquer, o que será que o file retorna? $ echo “meu texto” > texto.txt $ ar r teste.deb texto.txt ar: creating teste.deb O comando acima criou um arquivo ar chamado teste.deb (lembre-se que o Linux despreza extensões) e adicionou o arquivo texto.txt nele. $ file teste.deb teste.deb: current ar archive O file retorna que é um arquivo ar, corretamente. Mas por que o file reconhece como pacote do wget corretamente como binário do debian? A resposta está no código-fonte do file [5], que identifica um pacote binário se depois do magic number do ar, o arquivo contiver a string “debian-binary”. De fato, o formato ar define o magic e o nome do primeiro arquivo agrupado em seguida. Então bastaria que criássemos um arquivo ar com um arquivo debian-binary qualquer para o “enganar” o file. $ echo 2011 > debian-binary $ ar r fake.deb debian-binary ar: creating fake.deb $ file fake.deb fake.deb: Debian binary package (format 2011) Agora sim. O file não faz nenhuma verificação adicional (e nem deveria). Mas a intenção aqui não é hackear o file (até porque estamos falando de um formato livre, com extensa documentação), e sim criar um deb “na mão”. Perceba que o formato apareceu como “2011”. Claro que é um pacote inválido e se você tentar instalar o dpkg vai gerar um erro. Nem perca seu tempo. 4. Extraindo um pacote deb Voltando ao que interessa, vamos extrair o conteúdo do pacote deb do wget para conferir o que há de interessante: $ mkdir wget $ cd wget $ ar xv ../wget* x – debian-binary x – control.tar.gz x – data.tar.gz $ cat debian-binary 2.0 Exatamente o que o file informou. Versão 2.0 do formato deb. Beleza. $ tar tvzf control.tar.gz drwxr-xr-x root/root 0 2011-07-23 17:04 ./ -rw-r–r– root/root 3832 2011-07-23 17:04 ./md5sums -rw-r–r– root/root 12 2011-07-23 17:04 ./conffiles -rw-r–r– root/root 1327 2011-07-23 17:04 ./control Acima vemos alguns arquivos de controle do pacote. O interesse maior é no arquivo “control”, necessário para um pacote funcionar. O md5sums também é legal de se ter. $ tar tvzf data.tar.gz <saída suprimida=””> -rw-r–r– root/root 651 2009-09-21 23:52 ./usr/share/doc/wget/ChangeLog.README drwxr-xr-x root/root 0 2011-07-23 17:04 ./usr/bin/ -rwxr-xr-x root/root 353824 2011-07-23 17:04 ./usr/bin/wget Já o data.tar.gz contém os dados do pacote em si, incluindo binários executáveis e documentação, todos numa estrutura bem definida. Aliás, é esta estrutura que o pacote cria ao ser instalado. Estou mais interessado no control.tar.gz. Vamos extraí-lo: $ tar xvzf control.tar.gz ./ ./md5sums ./conffiles ./control $ head -5 md5sums 1b2acae8540b64a3170dc4ce0200809e usr/bin/wget d62b0aafbbacf1d54031ded4d1a5f232 usr/share/doc/wget/AUTHORS 2f58d6d92cabcf358718a564d3e132d4 usr/share/doc/wget/ChangeLog.README 2b95a82f1c7499025d67ff86af2d7ecd usr/share/doc/wget/MAILING-LIST 9e83cee67a496f5eb62aecf283e14367 usr/share/doc/wget/NEWS.gz Certo, vemos no arquivo md5sums, o hash MD5 de cada arquivo incluso no data.tar.gz (no entanto só imprimi 5 linhas com o head). Não seria difícil gerar isso para o nosso pacote “artesenal”. $ cat control Package: wget Version: 1.12-5 Architecture: amd64 Maintainer: Noël Köthe Installed-Size: 2344 Depends: libc6 (>= 2.3), libidn11 (>= 1.13), libssl1.0.0 (>= 1.0.0), dpkg (>= 1.15.4) | install-info Conflicts: wget-ssl Section: web Priority: important Multi-Arch: foreign Homepage: http://www.gnu.org/software/wget/ Description: retrieves files from the web Wget is a network utility to retrieve files from the web using HTTP(S) and FTP, the two most widely used internet protocols. It works non-interactively, so it will work in the background, after having logged off. The program supports recursive retrieval of web-authoring pages as well as ftp sites — you can use wget to make mirrors of archives and home pages or to travel the web like a WWW robot. . Wget works particularly well with slow or unstable connections by continuing to retrieve a document until the document is fully downloaded. Re-getting files from where it left off works on servers (both HTTP and FTP) that support it. Both HTTP and FTP retrievals can be time stamped, so wget can see if the remote file has changed since the last retrieval and automatically retrieve the new version if it has. . Wget supports proxy servers; this can lighten the network load, speed up retrieval, and provide access behind firewalls. Este é o arquivo que descreve o pacote. A referência deste arquivo é tratada na Debian Policy [6], mas os campos mais comumente usados para pacotes simples são: Package – Nome do pacote Version – Versão do programa-versão do pacote Architecture– Arquitetura para qual o programa foi compilado. Pode ser i386, amd64, dentre outras. Pode ser “all” para scripts, por exemplo. Maintainer – Aqui vai o seu nome e e-mail. O criador do pacote (não do programa a ser empacotado). Installed-Size – O espaço estimado, em bytes, requerido para instalar o pacote. Depends – Os pacotes dos quais seu pacote depende. Se for um programa em Python, vai depender do interpretador python, por exemplo. Homepage – O site do programa empacotado Description-A descrição do pacote. Uma linha para a descrição curta e demais para descrição longa, onde todas devem começar com um espaço e as linhas em branco devem possuir um ponto (.), que não vai aparecer no final das contas. Agora que já explicamos a estrutura de um pacote deb básico, vamos ver como criar um. 5. Criando um pacote “artesanal” Precisamos ter o que empcotar. Então vamos criar um software de teste. $ mkdir /tmp/nada-1.0 $ cd /tmp/nada-1.0 $ echo -e “#include <stdio.h>nnint main()n{ntputs(“nada…”);ntreturn 0;n}” > nada.c Os comandos acima devem criar um arquivo nada.c, no diretório /tmp/nada-1.0, com o seguinte conteúdo: #include <stdio.h> int main() { puts(“nada…”); return 0; } Agora precisamos compilar o programa: $ gcc -o nada nada.c O binário “nada” precisa de uma estrutura. Então vamos colocá-lo num usr/bin: $ mkdir -p usr/bin $ mv nada usr/bin/ NOTA: Cuidado para não tentar mover o “nada” para o /usr/bin do sistema. Agora temos a seguinte estrutura: $ find . . ./usr ./usr/bin ./usr/bin/nada ./nada.c Precisamos do arquivo de controle. Que tal este? Package: nada Version: 1.0 Architecture: amd64 Maintainer: Você <seu@email.com.br> Homepage: http://www.tacomnada.com Installed-Size: 6560 Depends: libc6 (>= 2.2.5) Description: program that does nothing Na realidade o nada conseguie imprimir a string “nada…” na tela. Claro que pode servir para algo, mas nao saberia dizer para que. Talvez para aprender a empacotar no Debian, ou para nao fazer nada. . Depois de falar tanto, nao vou dizer mais nada. Basta salvar como control (sem extensão mesmo). Para o campo Installed-Size, eu contei os bytes do binário “nada”: $ wc -c nada 6560 nada Já no campo Depends, é interessante avaliar a saída do ldd: $ ldd nada linux-vdso.so.1 => (0x00007fffea5ff000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fcebbf5d000) /lib64/ld-linux-x86-64.so.2 (0x00007fcebc2fe000) A primeira entrada é a uma biblioteca virtual para interface com o kernel. Ela sempre existirá e também pode aparecer como linux-gate.so.1. Em seguida temos duas bibliotecas reais. Supondo que não saibamos em qual pacote elas se encontram, podemos usar o apt-file: $ apt-file search libc.so.6 libc6: /lib/x86_64-linux-gnu/libc.so.6 libc6-i386: /lib32/libc.so.6 O pacote de nosso interece é o libc6. Agora, a versão da biblioteca requerida vai depender das funções que o programador utilizou. Isto consultado ser pego no site do projeto ou diretamente com o desenvolvedor. Usei 2.2.5. Vamos buscar a última lib que o ldd detectou: $ apt-file search ld-linux-x86-64.so.2 libc6: /lib/ld-linux-x86-64.so.2 libc6: /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 Também está no pacote libc6. Beleza, então só temos esta dependência. Agora precisamos gerar os arquivos control.tar.gz (com o control dentro), data.tar.gz (com toda a árvode usr dentro) e debian-binary (com a string 2.0 dentro): $ tar cvzf data.tar.gz usr/ usr/ usr/bin/ usr/bin/nada $ tar cvzf control.tar.gz control control $ echo 2.0 > debian-binary $ ar r nada-1.0_amd64.deb debian-binary control.tar.gz data.tar.gz ar: creating nada-1.0_amd64.deb fernando@localhost:/tmp/wget/nada-1.0$ file nada-1.0_amd64.deb nada-1.0_amd64.deb: Debian binary package (format 2.0) Perceba que o primeiro arquivo a ser adicionado no ar é o debian-binary. E agora vamos testar. $ sudo dpkg -i nada-1.0_amd64.deb Selecting previously deselected package nada. (Reading database … 146331 files and directories currently installed.) Unpacking nada (from nada-1.0_amd64.deb) … Setting up nada (1.0) … $ nada nada… $ sudo dpkg -P nada (Reading database … 146332 files and directories currently installed.) Removing nada … 6. Conclusão O pacote funciona. Inclusive eu fiz um script (makedeb_v2.sh) para empacotar [quase que] desta forma o Evan’s Debugger [7]. Vale a pena dar uma olhada, pois tem outros arquivos e comandos que não mencionei aqui. No entanto, ressalto que a preferência deve ser para pacotes com alta qualidade. Provavelmente escreverei um outro artigo sobre a forma correta de se criar pacotes, com base na política do Debian e com as ferramentas providas pela comunidade. Não é fácil fazer um pacote de qualidade, mas o primeiro passo, na minha opinião, é entender com o que estamos lidando e espero que este artigo tenha atingido este objetivo. [1] http://www.debian.org/doc/#manuals [2] http://packages.debian.org/wheezy/pev [3] http://catb.org/~esr/faqs/hacker-howto.html#believe2 [4] http://www.gnu.org/s/binutils/ [5] ftp://ftp.astron.com/pub/file/ [6] http://www.debian.org/doc/debian-policy/ch-controlfields.html [7] http://codef00.com/projects#debugger
  7. Mês passado eu postei no blog uma análise básica de um malware brasileiro que recebi por e-mail. Algumas pessoas se ofereceram para continuar a análise mas não o fizeram, por motivos diversos. Eu tomei vergonha e achei uma máquina virtual com o XP e alguns programas velhos que ajudariam na análise. Este artigo é o meu relato sobre esse malware. No post do blog cheguei a identificar que o malware tinha UPX [1], então neste artigo vamos partir da remoção do packer: $ wc -c IMG2005M.exe 58880 IMG2005M.exe $ upx -dqq IMG2005M.exe 102400 <- 58880 57.50% win32/pe IMG2005M.exe $ wc -c IMG2005M.exe 102400 IMG2005M.exe Ainda no Linux, fui atrás do entry point do binário com o pev [2]: $ pev -o IMG2005M.exe | grep -iA3 entry Entry point: 0x1758 Address of CODE section: 0x1000 Address of DATA section: 0x10000 Imagebase: 0x400000 Soma-se o EP à ImageBase e tem-se o offset do EP: 0x401758. Daqui pra frente resta muito pouco a se fazer com ferramentas nativas do Linux já que o binário é de Windows e o pev ainda não disassembla. Em seguida eu abri uma VM com Windows XP para ver a cara do inimigo: Brincalhão esse coder hein? Detalhe para o ícone de fotos no EXE – usuário leigo cai fácil. Depois usei o RDG Packer Detector [3] para tentar descobrir o compilador utilizado no arquivo, se há alguma criptografia conhecida envolvida, hashes etc: O RDG detectou que é um binário compilado em Visual Basic 6.0, em código nativo. Pelo visto era só o UPX mesmo pois o RDG não detectou mais nada. No entanto, são suposições… Usando o OllyDbg [4], abri o malandro, e sempre a primeira coisa que vem em minha mente binária é buscar pelas strings hardcoded no executável. As strings dentro de um executável podem dizer *muito* sobre ele. Vale sempre a pena “passar o olho” nelas. No Olly é bem simples buscá-las: basta clicar com o botão direito e ir em “Search for -> All referenced strings”. Várias strings foram encontradas (referenced-strings.txt). Destaco algumas: UNICODE “*AC:Documents and SettingsMasterDesktopProfessor PardalSummer_TRSSummer_TRS.vbp” ASCII “GetLocaleInfoA” ASCII “GetUserDefaultLCID” UNICODE “6655475C45405C58521B606F53535577515F505640625441” UNICODE “625F5B06076B7A465047524C58585F6B4A46415059” UNICODE “695B5E545F470707584D1D4C5C46” A primeira string, bem, acho que dispensa comentários né? Em seguida duas funções da API do Windows para identificação do idioma. Seria particularmente interessante um malware brasileiro ter versões do Windows em português como alvo? As outras três eu escolhi aleatoriamente. Acontece que criadores de malware sabem que precisam proteger suas strings, do contrário, muita coisa ficaria exposta a qualquer curioso que souber utilizar o comando strings do *n?x ou funções como essa do OllyDbg. Como há várias strings similares a essas três no binário, aposto que são strings encriptadas pelo programador do malware. Você não? =P De volta à tela do disassembly (ALT+C), parado bem no EP: 00401758 PUSH 00401850 0040175D CALL <JMP.&MSVBVM60.#100> ;Jump to MSVBVM60.ThunRTMain O jeito como o compilador constrói o executável é importante. No caso do VB é desse jeito acima: coloca-se um endereço na pilha e chama uma função da biblioteca. Esse endereço PUSHado é o offset de uma estrutura que define todo o programa em VB. Olhando a estrutura em 0x401850 no dump, dá pra identificar o offset do ProjectStruct (vem após esse 0x409): 00401850 56 42 35 21|F0 1F 56 42|36 45 53 2E|44 4C 4C 00| VB5!ðVB6ES.DLL. 00401860 00 00 00 00|2A 00 00 00|00 00 00 00|00 00 00 00| ….*……….. 00401870 00 00 0A 00|0A 0C 00 00|09 04 00 00|D0 AE 40 00| ………..Ю@. 00401880 D8 1B 40 00|00 F8 30 00|00 FF FF FF|08 00 00 00| Ø@..ø0..ÿÿÿ… Então fui para o endereço 0x40AED0 no disassembler (CTRL+G) e coloquei um breakpoint (F2) logo no início da função. Aí foi só seguir com o F8 até chegar numa call que parecia interessante pois era uma chamada de função interna em 0x40AF1C. 0040AF16 MOV EAX, DWORD PTR SS:[LOCAL.5] 0040AF19 PUSH EAX 0040AF1A MOV ECX, DWORD PTR DS:[EAX] 0040AF1C CALL DWORD PTR DS:[ECX+1C] (0x40B450) 0040AF1F TEST EAX, EAX 0040AF21 FCLEX Ao entrar nessa call, caí em 0x40B450. Nessa função dá pra ver uma daquelas strings doidas sendo operada em 0x40B4B7. Foi ali mesmo que coloquei um breakpoint e mandei rodar (F9). 0040B4B0 MOV DWORD PTR SS:[LOCAL.1], 3 0040B4B7 MOV EDX, 00402664 ; UNICODE “6655475C45405C58521B606F53535577515F505640625441” 0040B4BC LEA ECX, [LOCAL.11] 0040B4BF CALL DWORD PTR DS:[<&MSVBVM60.__vbaStrCopy>] 0040B4C5 LEA EAX, [LOCAL.11] 0040B4C8 PUSH EAX 0040B4C9 LEA ECX, [LOCAL.17] 0040B4CC PUSH ECX 0040B4CD CALL 0040AF60 0040B4D2 PUSH 0 0040B4D4 LEA EDX, [LOCAL.17] 0040B4D7 PUSH EDX 0040B4D8 LEA EAX, [LOCAL.12] 0040B4DB PUSH EAX 0040B4DC CALL DWORD PTR DS:[<&MSVBVM60.__vbaStrVarVal>] 0040B4E2 PUSH EAX 0040B4E3 LEA ECX, [LOCAL.21] 0040B4E6 PUSH ECX Fui seguindo com F8 e quando passei da chamada para __vbaStrVarVal em 0x40B4DC não é que a string se transformou?! O que era “6655475C45405C58521B606F53535577515F505640625441” virou “Scripting.SWbemObjectSet”, mas como? Os olhos aqui têm que estar afiados. Se liga: $ echo -n 6655475C45405C58521B606F53535577515F505640625441 | wc -c 48 $ echo -n Scripting.SWbemObjectSet | wc -c 24 A string encriptada tem o dobro do tamanho. Além disso, parecem ser bytes hexa já que todos os dígitos vão de 0 a F. Mas se você achou que o Professor Pardal simplesmente usou os bytes hexa de cada caracter da string original para encriptá-la, se enganou. É um pouquinho mais trabalhado. hehehe Repara que antes da string ser desencriptada, o fluxo de execução só passou por uma função local, uma call em 0x40B4CD para 0x40AF60. As outras são funções da MSVBVM60.DLL. Portanto pude supor que é nesta função que está a rotina de desencriptação, coloquei um breakpoint lá e comecei tudo de novo… Essa função que desencripta (0x40AF60) é relativamente grande. Vou resumir o que ela faz: O primeiro loop (de 0x40B08F a 0x40B123) cria a seguinte string fixa gigante que será usada mais à frente: “5655545655381688355541151683736525351505152535455336435363716816838424040414195431239696123125941259” Por que usar um loop para criar uma string fixa ao invés de declará-la hardcoded? Essa string é a chave para descriptografar todas as outras. O Professor Pardal não a deixaria tão solta. Se tiver curiosidade de descobrir como ela é gerada vai ser um ótimo treino, principalmente pra quem está começando. Qualquer coisa tô pelo Twitter. O segundo loop pega dois caracteres da string encriptada e os interpreta de uma vez como um byte hexadecimal (um número). Depois disso, XOReia (tem gente que fala mIgUxOO) este número com o equivalente númerico do caractere da string chave na mesma posição. Complicou? Vamos a um exemplo… String encriptada: 665547 1. Pegam-se dois caracteres da string encriptada e os interpreta como um número hexa: “66” -> 0x66 2. Pega-se o equivalente numérico de um caractere da string chave: “5” -> 0x35 3. Faz-se o xor entre esses números 0x66 ^ 0x35 = 0x53 4. Converte o resultado para o equivalente em ASCII. $ printf “x53n” 5. Volta para o passo 1 até que todos os caracteres da string encriptada tenham sido pegos, sempre dois a dois. O próximo cálculo será 0x55 ^ 0x36 e o outro, 0x47 ^ 0x35, certo? Pra você não se perder no disassembly, o xor fica em 0x40B309 – é uma chamada à função __vbaVarXor da MSVBVM60.DLL. Nem bitwise programa em VB faz, rapaiz! Escrevi um programa em C para fazer este trabalho sujo de desencriptar as strings (pardal_decrypt.c) : $ gcc -o pardal_decrypt pardal_decrypt.c $ ./pardal_decrypt 6655475C45405C58521B606F53535577515F505640625441 Scripting.SWbemObjectSet Em 0x40B4E7, depois de desencriptar a primeira string, o malware chama a função rtcCreateObject2 da biblioteca. Conseguimos saber o nome dela graças à análise do OllyDbg. Dá pra imaginar o que essa função faz pelo nome e pela string que foi descriptografada antes dela. Não tenho intenção aqui de reescrever o código do malawre, então não vou me focar em como ele faz e sim o que ele faz. Logo, meus chutes de código não refletem necessariamente a realidade. Daqui pra frente seria bom acompanhar olhando o disassembly do malware (cuidado – não vá rodar o cara!) Na sequência, mais strings são desencriptadas: “Scripting.SWbemObjectSet” e “Scripting.SWbemObject”. Objetos destas classes também são criados. Em seguida mais duas strings: “Win32_OperatingSystem” e “winmgmts:{impersonationLevel=impersonate}”. Não é difícil deduzir que será feita uma query WMI. Em 0x40B672 há uma chamada para a rtcGetObject. É claro que ter alguma experiência com programação ajuda aqui. No caso de executáveis feitos em VB, ter programado em VBScript me ajuda. Entrando na call em 0x40B70D, cheguei na função 0x40E210, que pega o idioma da instalação do Windows. Ainda nesta função, em 0x40E38F, o malware verifica se a string “uguês (Brasil)” faz parte da string de retorno do idioma. É uma chamada à função InStr() do VB. De volta ao fluxo anterior, em 0x40B726 rola um “for each” que vai iterar através da coleção de objetos que a query “SELECT * FROM Win32_OperatingSystem”, montada algumas linhas antes, retorna. Nesse loop duas coisas são feitas: o atributo Caption do objeto retornado é guardado e o idioma é checado. Em 0x40B87F há uma call interna para 0x4019ED. Esta é um call bem legal de acompanhar de perto. Ela desencripta a string “TMP” e em 0x40BB58 chama a rtcEnvironBstr, que vai retornar o valor de uma variável de ambiente. No caso, o valor da variável de ambiente TMP que é o caminho completo do diretório temporário do usuário logado: C:> echo %TMP% C:DOCUME~1xpclientCONFIG~1Temp O malware desperta a curiosidade: o que ele quer no temp? Baixar algo? Gravar algo? Em 0x40BF57 o corno chama a rtcFileCopy e se copia para o diretório temporário do usuário. Em seguida testa se está no Windows Vista ou 7 para usar o runas, porque ele vai querer executar a sua cópia com privilégios administrativos. Em 0x40C101 ele manda executar sua cópia com uma chamada a rtcShell e morre. O trabalho sujo mesmo será feito pela cópia agora. Abri a cópia no debugger e vi que em 0x40C12A o malware verifica se seu caminho atual de execução é o diretório temporário. O problema é que pelo menos no Windows XP, essa checagem foi feita de maneira errada (pois é, bug no malware). O caminho atual pego vem no formato 8.3 do DOS. Veja: 0040C12A MOV EAX, DWORD PTR SS:[LOCAL.10] ; UNICODE “C:\DOCUME~1\xpclient\CONFIG~1\Temp\IMG2005M.exe” Quando essa string é comparada com o conteúdo da variável ambiente TMP, o resultado é falso e o malware tenta se copiar novamente para o temp, mas já existe um executável dele lá (que é ele mesmo) e não consegue se copiar. Aí entra em loop. =P Zerei, em tempo de execução, o valor de EAX, que é o registrador que armazena o retorno da __vbaStrCmp, para que o salto em 0x40C132 aconteça. Seguindo a análise, entrei numa call para 0x40C640 que busca o valor “EnableLUA” na chave “HKLMSOFTWAREMicrosoftWindowsCurrentVersionPoliciesSystem”. Caso não exista ou não seja zero, o malware seta para zero, desabilitando esta proteção do Windows. Mais a frente, em 0x40C939 ele exibe essa mensagem: Isso não é um erro causado pelo malware – é uma mensagem inofensiva, exibida de propósito. O texto é fixo e essas strings fazem parte das strings criptografadas. Elas estão em 0x40C8CF e 0x40C8EA. Agora o processo fica residente aguardando acesso à internet. Para isso ele tenta acessar a página do Google. Quando conseguir, vai para a última função do mal em 0x40C9D0. À esta altura do campeonato, basta olhar a função que você vai perceber todo este fluxo. Logo no início dela, um arquivo mkajs21mx.tmp é criado no diretório temp do usuário. Em seguida o malware faz uma conexão com um serivodr MS-SQL remoto, na URLia8eaatjyur0gqzaslrlqw2n8k.zlg.br na porta 9321. Nome de usuário, senha e nome do banco também são desencriptados pela mesmíssima rotina 0x40AF60. Após conectar, o malare faz a seguinte query SQL: SELECT TOP 1 * FROM Professor_Carregador ORDER BY Professor_Carregador_ID DESC O retorno dessa consulta tem ~70k e começa com 0x4d e 0x5a. O que é o que é? =D O malware salva o binário pego via SQL em %WINDIR%System32MakeObject.dll e depois registra essa DLL com o regsvr32.exe como um complemento do IE: Aí vem outra query: INSERT INTO Professor_Informa(Professor_Informa_WithEvents,Professor_Informa_Control) VALUES(‘0’, ‘0’) Fiz um script em VBScript pra pegar alguns registros deste banco (pardal_sql.vbs) e descobri que há mais de 6000 registros já. Ou seja, um número próximo desse de computadores brasileiros foram infectados. Impressionante. Já em relação aos registros, não tem nada demais. Por fim, só pra não dizer que não perturbou, o malware tenta apagar o arquivo WAV que é o somzinho de inicialização do Windows, mas pelo menos no XP SP3 PT-BR ele errou o nome e tentou apagar o Início do Windows XP.wav quando o nome real é Inicialização do Windows XP.wav. Segundo bug. E o que faz a MakeObject.dll? Eu dei uma pesquisada e achei alguns usuários reportando sintomas de envio automático de e-mail via Hotmail. Por coincidência ou não, todos tinham essa DLL em logs do HiJackThis. Mas no momento estou cansado de ver funções do VB na minha frente. Então quem quiser analisar, eu envio a DLL. Os usuários de Windows têm então mais duas coisas para se preocupar: a falta do somzinho de incialização e um MakeObjects como complemento no IE. Sem falar na DLL em si, que não sabemos o que faz ainda. Boa sorte! hehe Referências: [1] upx.sourceforge.net [2] https://github.com/merces/pev [3] rdgsoft.net [4] www.ollydbg.de
  8. Keygen, que abrevia “Key Generator” é um software capaz de gerar chaves válidas de registro para um software protegido. A prática desta técnica pode (e provavelmente vai) infringir algumas leis quando usada em softwares comerciais. No entanto, existem alguns desafios na internet chamados de “keygenmes”, que são programas feitos justamente para serem quebrados. O desafio está em criar keygens para eles. Este é um estudo muito interessante que treina bastante a lógica, matemática, assembly e até mesmo massageia o ego, se você vencer. Obviamente você não tem acesso ao código-fonte do desafio, então um disassembler, software capaz de interpretar os bytes do binário como mnemônicos assembly, precisará ser usado. Para facilitar o keygenning, também é interessante usar um debugger. Neste artigo eu vou usar um poderoso debugger e disassembler multiplataforma chamado EDB, projeto este que apoio. Ele foi escrito em C++ e Qt (então você precisa ter a Qt instalada para rodar). No github do projeto há instruções para instalação no Debian/Ubuntu, Fedora, Gentoo e para compilação (em qualquer ambiente). Neste artigo, usaremos dois arquivos: keygenme.c -> código-fonte do keygenme (desafio). keygen.c -> código-fonte de um keygen para o keygenme (solução). NOTA: O texto prevê que não conhecemos o código-fonte do desafio. Logo, se você olhar o fonte do keygenme (keygenme.c) ou do keygen proposto (keygen.c) antes de seguir a leitura, tudo perde a graça! Fica tranqüilo que no final dá tudo certo. Aliás, se ainda não deu certo, é porque não chegou ao final. Depois de baixar os caras, pode compilar só o keygenme.c, com a sintaxe básica do gcc: $ gcc -o keygenme keygenme.c O keygenme espera que você passe como argumentos o seu nome e sua chave (esta última você não sabe). Vamos ver se damos sorte: $ ./keygenme Fernando FCKGW-90908-30BCD Chave inválida Claro que eu chutei uma chave qualquer para simular uma tentativa de registro mas não acertei (eu tinha alguma chance de acertar?). Até agora não sabemos nada sobre a chave. Ela pode ser alfanumérica, só numérica, ter ou não hífens, enfim, as possibilidades são infinitas. Desafio é desafio! Vamos abrir o binário no EDB. Aliás, uma das facilidades dele é já colocar automaticamente um breakpoint no entrypoint do binário, ou seja, ao abrir um binário, basta mandar o EDB rodá-lo (tecla F9) e ele parará justamente no início da main(). Após fazer isso, você provavelmente verá algo como: 00000000004006cc: push rbp 00000000004006cd: mov rbp, rsp 00000000004006d0: push rbx 00000000004006d1: sub rsp, 56 00000000004006d5: mov dword ptr [rbp-52], edi 00000000004006d8: mov qword ptr [rbp-64], rsi 00000000004006dc: mov rax, qword ptr [rbp-64] NOTA: Ao compilar o keygenme.c na sua máquina, os endereços vão mudar, mas é só ter isso em mente e seguir tranqüilo, fazendo as adaptações. Aqui você tem que notar 4 coisas: A seta vermelha (que aparece no EDB) indica qual é a próxima instrução a ser executada pelo processador. A sintaxe do assembly é Intel. Eu estou num sistema de 64-bits, por isso os registradores aparecem como rbp, rbx, rsp, rax, etc. Se fosse um sistema de 32-bits, seria ebp, ebx, esp, eax e por aí vai (só trocar o “r” pelo “e”). De um modo geral, toda função (e a main não é uma exceção), começa com um “push rbp” e termina com um “ret”. Não temos tempo para destrinchar linha a linha, mas vamos tratar das linhas mais importates. Aliás, o debugger te ajuda a não precisar de muito conhecimento em assembly para entender o que as linhas fazem. Teclando F8 (Step out), passamos para a próxima instrução. Podemos ir teclando F8 calmamente até chegar nas seguintes linhas: 00000000004006e7: test rax, rax 00000000004006ea: jz 0x00000000004006fc As instruções acima trabalham em conjunto. Em 0x4006e7, o registrador rax é verificado pela instrução test. Se seu valor for zero, a próxima instrução será um salto para 0x4006fc. Se seguirmos dando F8, veremos que este salto vai nos jogar para a linha a seguir: 00000000004006fc: call 0x00000000004006b4 Nela tem a instrução call, que é uma chamada de função. Mais um F8 e pimba, o programa encerra. Ok, e para que eu faço isso? Bem, a questão é perguntar-se: por que o programa encerrou? Porque ele chamou a função 0x4006b4. E por que ele a chamou? Porque o salto em 0x4006ea aconteceu. E por que o salto aconteceu? Porque o teste em 0x4006e7 deu verdadeiro. E porque deu verdadeiro? Porque rax estava zerado. Logo, para que o programa não encerre logo no início de sua execução, é preciso ter algo em rax. Para não alongar muito o texto, eu vou dar a cola: rax precisa apontar para um argumento (char *), do contrário, não dá pra começar a brincadeira né? A execução que acompanhamos foi como se tivéssemos feito: $ ./keygenme Chave inválida E pronto. Não passamos argumentos, então não há o que testar. O programa encerra depois de imprimir a mensagem de erro. Vamos corrigir isso. No EDB, é preciso colocar os argumentos em Options -> Applications Arguments. Coloquei dois argumentos para chamar o binário como: $ ./keygenme Fernando 30303030 Agora sim a gente passa naquele teste teste em 0x4006ea (e também no teste em 0x4006fa, que testa o segundo argumento). Mas é preciso reabrir o arquivo no EDB depois de configurar argumentos. Começando novamente com F8, ao passar por 0x4006ea sem pular, caímos em 0x4006ec. Esta instrução mov copia (pois é, ela não move!) o endereço de memória do primeiro argumento para o registrador rax. 00000000004006ec: mov rax, qword ptr [rbp-64] Ainda bem que agora ele não é nulo. Do contrário, teríamos um belo Segmentation Fault. Por isso do teste antes. O mesmo acontece em 0x400701, só que para o segundo argumento. Agora muita atenção no trecho abaixo: 0000000000400709: mov rax, qword ptr [rax] 000000000040070c: mov rdi, rax 000000000040070f: call 0x0000000000400588 Novamente o primeiro argumento (Fernando) é endereçado em rax. E ao passar da call em 0x40070f, o número 8 é posto em rax. Sabendo que o rax é o registrador geralmente usado tanto para passagem de parâmetro quanto para retorno de função, podemos entender que o endereço da string “Fernando” foi passado para a função 0x400588 e esta retornou 8. Consegue ver alguma relação? O que o número 8 tem a ver com a string “Fernando”? Se quiser confirmar sua suspeita, pode mudar este argumento no EDB, reabrir o keygenme e avaliar o novo número de retorno. Mais abaixo, segue uma tremenda sacanagem: 0000000000400717: cmp dword ptr [rbp-40], 3 000000000040071b: jle 0x0000000000400723 000000000040071d: cmp dword ptr [rbp-40], 20 0000000000400721: jle 0x0000000000400728 0000000000400723: call 0x00000000004006b4 Em 0x400717 o valor 8 (no caso do meu exemplo) é comparado com 3. Na seqüência vemos um salto jle (Jump if Lower or Equals) para 0x400723. E neste endereço, tem uma call pra 0x4006b4. Lembra desta call? Não foi ela quem encerrou o programa da outra vez? Não podemos cair nela. Sorte que 8 é maior que 3. Certo, não saltamos. Agora abaixo: 000000000040071d: cmp dword ptr [rbp-40], 20 0000000000400721: jle 0x0000000000400728 0000000000400723: call 0x00000000004006b4 0000000000400728: mov eax, dword ptr [rbp-40] Outra comparação. Desta vez para ver se o 8 é menor ou igual a 20. Se não for, ele não salta e cai na call maldita novamente, para encerrar o programa. Que conclusões podemos chegar? O programa testa se os dois argumentos existem. Basta que um não exista para que o programa seja encerrado. O 8 visto aqui é o tamanho da string nome (primeiro parâmetro, que no meu exemplo foi “Fernando”). Caso o tamanho da string não esteja entre 4 e 20 caracteres, o programa encerra. Seguindo com F8, chegamos neste bloco: 000000000040075d: movsxd rax, rbx 0000000000400760: add rax, qword ptr [rbp-32] 0000000000400764: movzx eax, byte ptr [rax] 0000000000400767: movsx eax, al 000000000040076a: mov edi, eax 000000000040076c: call 0x00000000004005a8 0000000000400771: test eax, eax 0000000000400773: jnz 0x000000000040077a 0000000000400775: call 0x00000000004006b4 000000000040077a: add ebx, 1 000000000040077d: cmp ebx, dword ptr [rbp-40] 0000000000400780: jl 0x000000000040075d Se você não conhece assembly, pode ser que não esteja claro, mas o debugger com certeza vai te entregar que isso é um loop determinado (um for). Vou deixar essa análise de lado, mas quem não conhece pode olhar o fonte em C depois e tentar identificá-lo aqui. Mais abaixo, outro loop: 0000000000400789: movsxd rax, rbx 000000000040078c: add rax, qword ptr [rbp-32] 0000000000400790: movzx eax, byte ptr [rax] 0000000000400793: movsx eax, al 0000000000400796: add eax, 10 0000000000400799: add dword ptr [rbp-36], eax 000000000040079c: add ebx, 1 000000000040079f: cmp ebx, dword ptr [rbp-40] 00000000004007a2: jl 0x0000000000400789 Esse já é mais simples. Pelo debugger você vai perceber que ele pega o valor ASCII de cada caracter do primeiro parâmetro e soma com 10. E vai somando esses resultados também (em memória, no endereço [rbp-36]). Quando este loop acabar, o endereço [rbp-36] conterá a soma em ASCII de todos os caracteres da string do nome somados, mais o resultado de 10 vezes o número de caracteres da string. Ou seja, se o nome fosse “ABCDE”, teríamos: A -> 65 B -> 66 C -> 67 D -> 68 E -> 69 65+10 + 66+10 + 67+10 + 68+10 + 69+10 = 385 Dá no mesmo que: 65 + 66 + 67 + 68 + 69 + 10 * 5 = 385 E esta é a lógica do programa. Ele pega o nome de usuário inserido, aumenta os valores de cada caracter em 10 unidades e depois os soma. O resultado é a chave para o nome de usuário inserido. Além disso, há as restrições de tamanho de nome e mais algumas que precisam ser implementadas no keygen. Agora é só fazer o keygen (lembrando que propus um no começo do artigo). Se quiser brincar, pode escrever um programa na sua linguagem preferida que receba um nome de usuário de acordo com as regras impostas pelo keygenme e gere uma chave válida para este usuário.
  9. 1. Introdução Um analisador de executáveis é um software capaz de prover informações sobre um executável que podem ser muito úteis para pesquisadores de Segurança da Informação na análise de malware, forense computacional ou engenharia reversa. Este artigo objetiva demonstrar como um analisador de executáveis é construído, abordando técnicas para uma análise precisa e eficiente. É utilizado como base um software de código aberto chamado “pev”, de desenvolvimento próprio, que analisa binários PE32 (usados no MS-Windows) e pode ser compilador tanto em sistemas UNIXlike quanto no próprio Windows, pois foi escrito em ANSI C. O processo de construção exige conhecimento do ambiente e da linguagem de programação escolhida. O estudo necessário para tal é de grande valor na carreira do pesquisador de segurança. 2. O executável Antes de iniciar, precisamos compreender o que é um arquivo executável. Sabemos que todo e qualquer arquivo no disco rígido não passa de uma sequência de bits armazenados por um processo elteromagnético nos pratos do disco. A diferença entre um arquivo MP3 e um PNG, por exemplo, é a forma como esses bits serão interpretados. No caso do executável, os bits presentes no arquivo representam instruções de máquina (Assembly) para o microprocessador da arquitetura em questão (Intel, SPARC etc). Veja: Binário Decimal Hexadecimal Assembly x86 ASCII 01010101 85 55 push ebp U O mesmo byte (conjunto de 8 bits) pode ser interpretado de diversas formas. De fato, é por este motivo que um software editor hexadecimal abre qualquer tipo de arquivo, inclusive áreas do disco diretamente, lendo byte a byte sem qualquer interpretação. O arquivo executável é um formato complexo (bem diferente de um arquivo em texto puro – clear text, por exemplo). Além dos bytes referentes ao código do programa em si, é preciso adicionar milhares de bytes que constituem informações para guiar o kernel do SO à execução do binário. É preciso informar, por exemplo, para qual arquitetura o executável foi compilado, quanto de memória será alocada para rodar o programa, que partes do programa em memória serão exclusivas, somente para leitura e mais uma série de diretivas. 3. O formato Para suprir todas essas necessidades de informações é que existem os formatos. Estes definem padrões que um arquivo deve seguir para ser corretamente interpretado pelo seu programa associado ou pelo próprio SO, no caso de um executável. Atualmente lidamos basicamente com dois formatos de executáveis: o PE e o ELF. O primeiro é utilizado pela família Windows e o segundo, pelos sistemas UNIX-like. O que um analisador precisa informar? Já dissemos que num executável não há somente o código que o programador escreveu na linguagem de programação convertido para código de máquina. Por isso, em tese, um analisador deveria nos dar toda esta informação “escondida” no executável. Os desenvolvedores dos formatos de executáveis geralmente liberam esta documentação porque todos os compiladores precisam gerar executáveis compatíveis e, por isso, têm de conhecer a especificação. Então o primeiro passo para se construir um analisador é obter a documentação do formato: Formato PE: https://www.mentebinaria.com.br/files/file/18-microsoft-portable-executable-and-common-object-file-format-specification/ Formato ELF: https://refspecs.linuxfoundation.org/ Na documentação do formato, constam todos os campos pré-definidos que se espera encontrar num executável. Mas é claro que nem tudo é necessário para se construir um bom analisador. Alguns campos possuem grande utilidade prática, enquanto outros raramente são necessários. Cabe a nós filtrar o que é importante para o objetivo. 4. O analisador Um código que consiga interpretar os campos que o formato define num executável precisa: Verificar se o binário é de tal formato. Ler os bytes do binário de acordo com a especificação. Imprimir os nomes do campo e seus respectivos valores na tela. Simples? Nem tanto. Geralmente um analisador é um software pequeno que roda rápido (porque já sabe o que vai ler), mas o código-fonte é grande e pode vir a ser complexo. Para um exemplo prático, imagine que o formato PE defina o seguinte: ➔ Para ser um arquivo PE válido, os primeiros dois bytes do arquivo binário devem ser 0x4D e 0x5A. Neste caso, o analisador precisa fazer tal verificação: int verify(char *filename) { FILE *fp = fopen(filename, “rb”); char bytes[2]; fread(bytes, 2, 1, fp); if (bytes[0] == ‘M’ && bytes[1] == ‘Z’) return 1; return 0; } E fim de papo! Sim, um malware não pode alterar estes bytes, do contrário o Windows não o executará, portanto, não tenha medo em testar e encerrar o program caso não haja as letras MZ no início do arquivo, que são a representação em ASCII dos bytes 4D e 5A, em hexa. Experimente alterar um desses bytes de um executável PE e tente rodá-lo para ver o que acontece. Humm… será que um executável PE com o primeiro byte alterado passaria via e-mail pelo firewall de sua empresa? Agora digamos que a especificação do formato PE também diga: ➔ 16 bytes à frente desta assinatura MZ encontra-se o checksum do arquivo, que tem um comprimento também de 2 bytes. Bastaria “andar” pelo arquivo para ler: unsigned short checksum; fseek(fp, 16, SEEK_CUR); fread(&checksum, 2, 1, fp); printf(“%dn”, checksum); PS.: Em C, o tipo short, que abrevia short int, tem 2 bytes na arquitetura Intel x86. Seguindo essa lógica, podemos imprimir todos os campos de um binário, bastando apenas seguir a especificação do formato. No entanto, há recursos de linguagem que podem facilitar a vida. Veja um trecho interessante da biblioteca windows.h abaixo: typedef struct _IMAGE_FILE_HEADER { WORD Machine; WORD NumberOfSections; DWORD TimeDateStamp; DWORD PointerToSymbolTable; DWORD NumberOfSymbols; WORD SizeOfOptionalHeader; WORD Characteristics; } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; No caso do C, se definirmos um tipo WORD e DWORD com o typedef, obtemos um struct prontinho para ser usado e com os nomes dos campos. O mesmo existe para o formato ELF. 5. Fazendo mais Imprimir informações brutas faz parte, mas a graça de um analisador está em sua capacidade de fazer mais que isso. Por exemplo, tratar um timestamp ou realçar o Entry Point (EP) de um binário são técnicas simples e que vão ajudar muito quem utilizará o software. 6. Binários com proteção Um bom analisador deve esperar um binário que contenha um packer, crypter ou qualquer outro tipo de proteção. Neste caso, é necessário estudar e entender a rotina da proteção, fazer engenharia reversa, e inserir rotinas no analyzer para detectar ou mesmo remover as proteções dos executáveis. Isso vai dar um trabalho extra (e constante, porque novas proteções não param de surgir, além de atualizações das proteções existentes) mas sua implementação depende do objetivo desejado. A maioria dos analisadores somente reconhece que há uma proteção (alguns dizem qual é ela, batendo a assinatura contra um banco de dados), mas poucos a removem sem plugins adicionais. 7. pev Software livre (GPLv3) inicialmente desenvolvido para exibir o valor do campo “Product Version” de um executável PE. fernando@brussels:~$ pev -c ~/winapp/wrar393br.exe COFF header: Machine: 0x14c Number of sections: 5 Date/time stamp: 1268634487 (03/15/2010 at 06:28:07 AM) Symbol Table offset: 0 Number of symbols: 0 Size of optional header: 0xe0 Characteristics: 0x103 Página do projeto: https://github.com/merces/pev 8. Conclusão Conhecer bem os executáveis é obrigação de quem trabalha ou pretende trabalhar com análise de malware ou forense computacional e nada melhor que um estudo dirigido, que force resultados para atingir este objetivo. Desenvolver uma aplicação que interprete um executável “de cabo-a-rabo” é um ótimo começo.
  10. Com as vantagens óbvias das câmeras digitais, incluindo as dos smartphones, mais e mais pessoas tiram fotos todos os dias, em todos os lugares, fazendo as mais variadas “tripulias”. O que pouca gente sabe é que esses arquivos de fotos podem conter dados um tanto quanto comprometedores, como data, condições em que a foto foi tirada e até mesmo onde você estava! Se você olhar as propriedades de uma foto em .jpg tirada com uma câmera digital, poderá se surpreender com as informações que estão nela. No GNU/Linux: No Windows XP: Percebeu a marca e modelo da câmera, além da data quando a foto foi tirada? Por alguma razão o Windows não mostrou, mas o Linux sim, então este tipo de informação está em algum lugar no arquivo. Vamos ver… Eu não lembro de ter visto câmera digital que não salvasse os arquivos no formato JPEG. Por um lado é bom: padronização e compressão, já que o formato tem uma boa compressão e perde pouca qualidade, se bem ajustado. O formato JPEG, assim como qualquer outro formato de arquivo que se preze, define campos que serão interpretados pelos aplicativos que vão trabalhar com ele. Ou seja, poderia dizer que do primeiro byte de um arquivo .jpg até o enésimo byte, está a informação X. Deste em diante até um outro byte, está a informação Y e assim por diante. Claro que a imagem em si (leia-se o que é renderizável) ocupa um campo geralmente grande e de tamanho variável. Sem problemas se estudarmos o formato JPEG um pouquinho… No cabeçalho do formato JPEG, cada campo (chamado de “marker”) é iniciado por um byte 0xff e o próximo byte define que tipo de campo é. De acordo com a especificação [1], o campo “0xff 0xd8” é o SOI (Start Of Image) e marca o início da imagem. Então se olharmos o conteúdo com um visualizador hexa, podemos observar: $ hd -n 64 tracking.jpg 00000000 ff d8 ff e1 48 88 45 78 69 66 00 00 4d 4d 00 2a |….H.Exif..MM.*| 00000010 00 00 00 08 00 0a 01 0f 00 02 00 00 00 08 00 00 |…………….| 00000020 00 86 01 10 00 02 00 00 00 0a 00 00 00 8e 01 12 |…………….| 00000030 00 03 00 00 00 01 00 01 00 00 01 1a 00 05 00 00 |…………….| A documentação prevê campos para abrigar as dimensões da imagem, compressão utililizada, cores etc. Inclusive há um campo para comentários definido por “0xff 0xfe”, de tamanho variável. Vamos procurá-lo? Pode ser que nossa informação esteja lá: Nada encontrado. O arquivo tem várias informações mas nem consta o campo de comentários previsto na especificação JPEG. Neste caso, vamos dar uma olhada com mais carinho nos bytes: $ hd -n 512 tracking.jpg 00000000 ff d8 ff e1 48 88 45 78 69 66 00 00 4d 4d 00 2a |….H.Exif..MM.*| 00000010 00 00 00 08 00 0a 01 0f 00 02 00 00 00 08 00 00 |…………….| 00000020 00 86 01 10 00 02 00 00 00 0a 00 00 00 8e 01 12 |…………….| 00000030 00 03 00 00 00 01 00 01 00 00 01 1a 00 05 00 00 |…………….| 00000040 00 01 00 00 00 98 01 1b 00 05 00 00 00 01 00 00 |…………….| 00000050 00 a0 01 28 00 03 00 00 00 01 00 02 00 00 01 32 |…(………..2| 00000060 00 02 00 00 00 32 00 00 00 a8 02 13 00 03 00 00 |…..2……….| 00000070 00 01 00 01 00 00 87 69 00 04 00 00 00 01 00 00 |…….i……..| 00000080 00 da 88 25 00 04 00 00 00 01 00 00 02 56 00 00 |…%………V..| 00000090 02 c4 53 41 4d 53 55 4e 47 00 47 54 2d 49 35 35 |..SAMSUNG.GT-I55| 000000a0 30 30 42 00 00 00 00 48 00 00 00 01 00 00 00 48 |00B….H…….H| 000000b0 00 00 00 01 32 30 31 30 2d 31 31 2d 31 38 20 30 |….2010-11-18 0| 000000c0 30 3a 34 36 3a 32 33 20 00 00 00 00 00 00 00 00 |0:46:23 ……..| 000000d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |…………….| 000000e0 00 00 00 00 00 00 00 11 82 9a 00 05 00 00 00 01 |…………….| 000000f0 00 00 01 ac 82 9d 00 05 00 00 00 01 00 00 01 b4 |…………….| 00000100 88 22 00 03 00 00 00 01 00 03 00 00 88 27 00 03 |.”………..’..| 00000110 00 00 00 01 00 c8 00 00 90 00 00 02 00 00 00 0f |…………….| 00000120 00 00 01 bc 90 03 00 02 00 00 00 32 00 00 01 cc |………..2….| 00000130 90 04 00 02 00 00 00 32 00 00 01 fe 91 01 00 07 |…….2……..| 00000140 00 00 00 04 01 02 03 00 92 07 00 03 00 00 00 01 |…………….| 00000150 00 02 00 00 92 0a 00 05 00 00 00 01 00 00 02 30 |……………0| 00000160 a0 00 00 07 00 00 00 04 30 31 30 30 a0 01 00 03 |……..0100….| 00000170 00 00 00 01 00 01 00 00 a0 02 00 04 00 00 00 01 |…………….| 00000180 00 00 06 40 a0 03 00 04 00 00 00 01 00 00 04 b0 |…@…………| 00000190 a0 05 00 04 00 00 00 01 00 00 02 38 a4 03 00 03 |………..8….| 000001a0 00 00 00 01 00 00 00 00 a4 06 00 03 00 00 00 01 |…………….| 000001b0 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 08 |…………….| 000001c0 00 00 00 87 00 00 00 32 56 65 72 73 69 6f 6e 20 |…….2Version | 000001d0 32 2e 32 00 00 00 00 00 32 30 31 30 2d 31 31 2d |2.2…..2010-11-| 000001e0 31 38 20 30 30 3a 34 36 3a 32 33 20 00 00 00 00 |18 00:46:23 ….| Opa, começamos a achar as informações que queremos, mas em qual campo estamos? Veja que começou o “0xff 0xe1” e até o fim deste dump de 512 bytes não começou outro campo, ou seja, ainda estamos nele. A documentação do formato JPEG diz que os campos “0xff 0xeN”, onde “N” pode variar, representa um campo chamado APPN (no nosso caso, N=1, entao nosso campo é APP1). Este campo é usado para gravar metadados no formato EXIF, usado por vários tipos de arquivo para abrigar informações adicionais. O objetivo agora é destrinchar o EXIF e ver como ele está estruturado. O primeiro passo é olhar a documentação [2] em busca da especificação de seus campos. O EXIF é usado “dentro” de outros formatos. Não vou comentar sobre a estrutura do EXIF aqui, pois demandaria um longo texto. Por hora, basta otimizarmos procurando um software que seja capaz de ler os metadados EXIF em um JPG, certo? O software que mais me atendeu foi o jhead [3]. Além de ser livre, é cheio de recursos interessantes, inclusive pode apagar todas as informações EXIF de um arquivo .jpg, a fim de evitar rastros. $ jhead tracking.jpg File name : tracking.jpg File size : 314085 bytes File date : 2011:03:30 02:13:53 Camera make : SAMSUNG Camera model : GT-I5500B Date/Time : 2010-11-18 00:46:23 Resolution : 1600 x 1200 Focal length : 2.7mm Exposure time: 0.125 s (1/8) Aperture : f/2.7 ISO equiv. : 200 Whitebalance : Auto Metering Mode: center weight Exposure : aperture priority (semi-auto) GPS Latitude : ? 12d 23m 28.937s GPS Longitude: ? 13d 20m 33.702s Epa! Latitude e longitude? Por que isso não apareceu no Windows, nem no Linux, no ambiente gráfico? Sério que dá pra saber onde eu estou? Sério… Qualquer pessoa que tiver sua foto (Facebook?) pode pegar essas informações e converter para coordenadas de GPS, existem até conversores na internet. Depois é só usar alguma API de “Reverse Geocoding”, que é a técnica de obter um mapa de um lugar a partir de suas coordenadas GPS. Moral da história: muito cuidado onde coloca suas fotos. Se quiser um pouquinho menos de exposição, você pode remover todas as informações EXIF com a opção -purejpg do jhead: $ jhead -purejpg tracking.jpg Modified: tracking.jpg $ jhead tracking.jpg File name : tracking.jpg File size : 295533 bytes File date : 2011:03:30 02:13:53 Resolution : 1600 x 1200 Veja que agora só ficam informações básicas como resolução e data de modificação do arquivo (o mesmo usado pelo SO). É, a vida digital tem um preço… [1] http://en.wikipedia.org/wiki/JPEG#Syntax_and_structure [2] http://www.exif.org/Exif2-2.PDF [3] http://www.sentex.net/~mwandel/jhead/
  11. Introdução Em muitas faculdades brasileiras a linguagem C é ensinada aos alunos de cursos de tecnologia. Mesmo assustando os novatos, vários alunos resistem e vencem a matéria. O problema é entender por qual motivo o C foi escolhido para iniciar o curso de programação. Seria uma linguagem didática para se aprender a programar? Um teste para ver quem tem ou não o “jeito pra coisa”? Alguns diriam que o correto seria começar com Pascal, mas há quem defenda linguagens mais modernas como Python, Perl, Ruby ou PHP. E aí, para que serve o C no primeiro período? Neste artigo farei uma análise sobre o que se aprende da linguagem, o motivo pelo qual ela surge no início do curso, seu valor de mercado e o que é possível fazer com esse start que a faculdade nos dá. A linguagem C A importância histórica da linguagem C é inegável e dispensa maiores comentários. Sabemos que até hoje a maioria dos softwares mais poderosos são feitos em C e/ou C++ (um super conjunto de C, orientado à objetos). O kernel Linux e outros núcleos de SOs são feitos basicamente em C. Muitos drivers de dispositivos como placas de rede, som, vídeo etc são feitos em C. Se contarmos o C++ nesta conta, chegamos perto de 100% dos kernels e drivers. Os interpretadores e compiladores das principais linguagens de programação também não fogem à regra e são feitos em C. Existe uma frase que afirma: metade do universo é feito em C. E é bem verdade. Pelo visto, a linguagem serve para alguma coisa… Ensino da linguagem C Você acabou de entrar na faculdade, está tendo aulas desta linguagem e não está entendendo nada? Não se preocupe, você não está sozinho. Algumas instituições de ensino acham que C é uma liguagem didática, quando não é. Para se aprender a programar, usa-se pseudo-linguagem, PORTUGOL e ferramentas do gênero. Nem mesmo o Pascal, considerado mais fácil de se aprender que o C, é atraente ou interessante à primeira vista. O grande monstro que aterroriza o aluno é a pergunta: “Por que eu vou fazer isso? Para que?”. Pois é, para que escrever um programa em C que calcule a média de três alunos e imprima na tela? Qual a lição tirada disso? A resposta é simples: nenhuma. A maneira como a linguagem é lecionada tenta empurrar o C guela abaixo em estudantes que viram, por exemplo, Visual Basic e Delphi no segundo grau. Isto é, se é que estudaram tais linguagens ou lembram-se delas. Não poderia dar certo mesmo. Antes de criar um programa, o aluno tem que saber o que está fazendo. O que é um programa, para que serve isso, o que é um arquivo executável, um binário, bits, bytes, o processador, dentre outros conceitos importantíssimos antes de se escrever o primeiro “Hello World”. O resultado do ensino forçado é o alto íncide de reprovação, abandono, mudança de curso e desistência. É comum encontrar alunos que estão no fim do curso de programação mas ainda não passaram nas matérias mais básicas de C. É o terror da faculdade. Definitvamente, a linguagem C vira uma vilã e a frase mais ouvida nos corredores sobre o assunto é que “C é chato”. Por que a linguagem C é chata? Porque ela não te mima. Numa escala onde o nível mais alto é o mais próximo da linguagem usada pelo ser humano e o mais baixo, da linguagem usada pelos microprocessadores, a linguagem C é considerada de nível médio. Assembly, por exemplo, é de baixo nível, enquanto Object Pascal (usada no Delphi), de alto nível. Isso significa que para programar em C é preciso conhecer conceitos mais próximos do hardware, que as linguagens de alto nível abstraem para o programador, tornando o trabalho mais fácil. Por isso temos a impressão de que C é chato, difícil, sem sentido. Realmente, sem os conceitos básicos de computação bem sólidos, um código em C pode tornar-se incompreensível. Vejamos um exemplo. Um código em PHP (alto nível) para se declarar uma variável e armazenar uma frase nela: <?php $str = “Essa é minha string”; ?> Um código equivalente em C, seria: void main(void) { char str[] = “Essa é minha string”; } No código em C, uma função teve de ser escrita (a main, que é a função principal de um programa), inclusive com seu tipo de retorno e parâmetros, onde usei void para não retornar nem receber nada. Além disso, foi criado um vetor de caracteres (char) para armazenar a frase. Em C, entende-se como string um vetor de caracteres (ou ponteiro para um conjunto deles) onde o último caracter é o NULL, código 0x00 na tabela ASCII. Tá vendo por que se precisa dos conceitos de computação até para começar uma frase em C? Agora perceba a jogada: #include <string.h> void main(void) { char str[21]; strcpy(str, “Veja, sou uma string”); } A função strcpy(), fornecida pelo header string.h, copia caracteres para uma variável do tipo vetor (ponteiro, na verdade, mas isto é outro assunto) de caracteres e adiciona um caractere nulo (NULL), zerado, na última posição. Perceba que iniciamos o vetor de char com 21 posições, para abrigar os 20 caracteres da frase proposta mais o NULL, que é um caractere só. As coisas começam a fazer sentido, apesar de “feias”, não? E assim é o C. Exigente, porém elegante. Se tem os conceitos de computação, sem dúvida não terá grandes dificuldades com a linguagem. Usando o C na vida e no mercado de trabalho Certo, você se convenceu de que C é legal de aprender, poderoso e aprendeu. E agora, faz o quê? Tem um colega seu ganhando dinheiro fazendo sites em Ruby on Rails. Outro faturando uma grana fazendo sistemas em Delphi para clientes, com imagens, botões brilhantes e multimídia. O que você, recém-estudado programador em C vai fazer com aquela tela preta pedindo dados com scanf()? Nada. Não é assim que se trabalha com C, ou pelo menos, não mais. Já foi o tempo em que os sistemas eram feitos dessa maneira. Além disso, mesmo nesse tempo a linguagem C foi rapidamente substituída neste meio pela linguagem CLIPPER no mundos dos PCs e pelo COBOL, nos mainframes. O forte do C hoje são aplicações desktop, inclusive as baseadas em rede e daemons (serviços). C também é útil para escrever compiladores e interpretadores para outras linguagens, por exemplo. Sabia que o PHP é escrito em C? Pois é, assim como Python, Ruby, BASH e muitos outros interpretadores. Então tem alguém ganhando dinheiro com C por aí, concorda? Vale a pena citar também o desenvolvimento embarcados, para microcontroladores e vários microprocessadores, incluindo ARM (usado em vários aparelhos Android). Em novembro do ano passado houve uma edição de um evento chamado Universidade Livre em que Olivier Hallot, diretor da ALTA (antiga BrOffice.org) falou durante alguns minutos numa faculdade carioca da dificuldade de encontrar programadores para contratar e fez um apelo para que os alunos levem a sério que o mercado está muito carente de bons programadores, principalmente em C/C++. Também em setembro do ano passado uma empresa publicou uma vaga no Rio de Janeiro buscando um profissional com os seguintes conhecimentos: Sistema Operacional Linux; Banco de dados MySQL; Criação e manutenção de tabelas, relacionamentos, scripts, etc.; Linguagem C, e das APIs: (V4L2), GTK, além de OpenGL; Adobe Flex. O salário inicial era de R$ 5.000,00. A vaga deve estar aberta até hoje… Em dezembro de 2011, uma grande operadora telefônica abriu nada menos que 20 vagas para desenvolvedores em C no Rio de Janeiro. Empresas que atendem infraestrutura, telecomunicações, embarcados, móveis, desenvolvimento do Linux e kernels derivados também precisam muito de programadores deste tipo. Enfim, vagas não faltam! Então por que aprendo Java na faculdade? A faculdade tenta ser a mais moderna possível, mas esquece de verdadeiramente orientar na profissão. Java é uma linguagem potente, flexível e poderosa mas tem um fim completamente diferente da linguagem C. Com Java se programa para web, dispositivos móveis, aplicações locais (pouco usada), sistemas de informação, embarcados etc. A flexibilidade é enorme, mas o foco é outro. Não se faz uma suíte de aplicativos em Java, simplesmente porque existe o C pra isso. Um sniffer de rede ou um software ping, por exemplo, são feitos em C, porque C é pra isso. Já uma interface de um aparelho GPS, é feita em Java. Questão de adeqüação. O mercado de Java é tão grande quanto o de C no mundo, mas é maior no Brasil. No entanto, o que não pode é a faculdade tratar a linguagem C como uma introdução à programação, para que o aluno depois aprenda Java. Uma coisa não tem nada a ver com a outra. São dois nichos completamente diferentes e em ambos os casos, é possível conseguir um bom emprego e alavancar na profissão, tanto aqui quanto fora. Minha faculdade usa Python para ensinar a programar. É legal? Não creio. Python é super divertido e viciante mas não exige os conceitos de computação que todo programador deve ter. A resposta é a mesma para todas as linguagens de alto nível. Como escrevi anteriormente, se começa a programar com uma pseudo-linguagem, para desenvolver a lógica. Antes do estudo de programação médio/alto nível, é preciso estudar computação, do ponto de vista da arquitetura em si (que vai incluir Assembly, SO etc) e aí sim, subir de nível. Se bem gerenciado, é possível manter estas disciplinas em paralelo, mas o programa deve ser cuidadoso (o que as instituições não andam respeitando – Eu já vi projeto de bancos de dados no segundo período. O aluno, teoricamente, nunca usou uma mysql.h ou outras bibliotecas para acesso a SGBD’s em outras linguagens). Quem aprende direto no alto nível e se dá bem, ótimo – e está de parabéns. Mas o objetivo do artigo é trazer a linguagem C à tona e não competir com outras linguagens. Venho comprovando a tese de que aprender “de baixo para cima” dá certo. Já consegui fazer um amigo escrever um programa em Assembly do zero para calcula a média de alunos. Aí sim ele viu o que é obter dados do teclado, calcular e exibir. Teve de entender por completo a tabela ASCII, uso de registradores gerais, syscalls e interrupções de software. Quando foi para o C, não teve o menor problema. E o que dá pra fazer com o C aprendido na faculdade? Só com ele, não muita coisa, mas com um pouquinho de pesquisa e afinco, gera-se resultados. Um exemplo é o grupo Coding 40°, onde eu e mais três alunos do curso de Ciência da Computação nos unimos para estudar e acabamos desenvolvendo um pequeno software, capaz de imprimir informações sobre executáveis PE (.exe, .dll etc) na tela. Nada complicado, agora que já está pronto. rs Sabe quando você está no Windows e vai nas propriedades de um .exe ou .dll e há uma aba “Versão” como na imagem abaixo? A proposta inicial era criar um software capaz de conseguir essa informação, recebendo como entrada o caminho do arquivo executável. O software deveria funcionar no Linux, já que nesse SO não é possível ver esta aba “Versão” nas propriedades dos executáveis de Windows, obviamente. Foi aí que fizemos o pev. Hoje ele já exibe várias outras informações sobre o executável além da versão. Conclusão Estudar C, C++, Assembly e outras linguagens tidas como “terríveis” é, sem dúvida, uma boa pedida. Há inúmeros projetos no mundo todo precisando de bons programadores nessas linguagens. Não encare o “C de faculdade” como um vilão ou uma introdução à programação porque não é. A linguagem C é uma linguagem poderosa e comercial. Nada de dizer que C é coisa de maluco. Ainda não sabe o que fazer com C? Está em dúvida sobre seus aspectos modernos? Nós temos um curso de programação moderna utilizando a linguagem C para você
  12. É notória a quantidade de pen drives e cartões de memória infectados com vírus. Freqüentemente nos deparamos com vírus novos, que os antivírus não conhecem (a vacina é criada depois de certo tempo). Essas pragas disseminadas por pen drives são tão perigosas quanto as disseminadas pela internet e às vezes até mais destruidoras, ou seja, o cuidado deve ser redobrado. Veja neste artigo como se defender de um pen drive ou cartão de memória infectado e saiba como remover o vírus destes dispositivos. O primeiro ponto a entender é como um vírus infecta um pen drive. Sabemos que vírus são programas (executáveis) e a infecção de um pen drive acontece quando o inserimos em um PC infectado (ou quando o PC infectado já possui um pen drive inserido na porta USB). Se o pen drive infectado é inserido em PC saudável e sem proteção adequada, este PC é infectado e passa a infectar todos os pen drives inseridos nele posteriormente, ou seja, a disseminação da praga fica fora de controle. Infelizmente, tudo funciona muito bem graças a uma ajudinha da arquitetura e controle de permissões do Windows. Acontece que normalmente o usuário logado num sistema Windows pertence ao grupo “Administradores”, isto é, possui todas as permissões de escrita e leitura em praticamente todo o disco rígido e áreas de memória. Para um vírus, isto é um prato cheio. Da mesma maneira que um programa de instalação qualquer pode ser executado com sucesso pelo usuário logado (copiando arquivos para diretórios de sistema e criando processos privilegiados), um vírus também pode, pois rodará com as permissões do usuário, que são as de administrador do sistema (as máximas). Quando o usuário incauto executa um vírus, este normalmente carrega-se em memória, copia-se para vários locais (backup), tenta infectar outras máquinas na rede e faz com que seja inicializado a cada reinicialização do sistema. É mais ou menos a definição de um worm. Tratamos de algumas técnicas manuais utilizadas para remover estes vírus no artigo Remoção manual de malware no Windows. Assim que um pen drive sadio é inserido na porta USB, a praga copia-se para ele. Até aí, nenhuma novidade. Mas como, ao inserir este pen drive em outro computador, ocorre a infecção? Acontece que o Windows possui uma facilidade chamada autorun (ou auto-inicialização) para drives montados e com letra atribuída. Como os pen drives são reconhecidos como uma unidade de disco (recebem inclusive uma letra disponível para serem acessados, por exemplo, F:), esta facilidade pode ser usada nestes dispositivos. Funcionamento da auto-inicialização: O autorun serve para executar algum aplicativo assim que o drive é montado. O exemplo clássico são os CD-ROMs que, ao serem inseridos no drive de CD, abrem uma aplicação com menus e outros recursos. A questão é que o autorun pode ser utilizado em qualquer drive, seja uma partição do disco, CD-ROM, DVD, pen-drive, câmera digital, etc. Para usar esta facilidade, basta adicionar um arquivo chamado autorun.inf no diretório raiz do drive em questão. Neste arquivo são escritas rotinas que o Windows deverá seguir quando assim que o drive for montado. Veja um exemplo: [autorun] open=menu.exe icon=menu.ico Se você salvar o conteúdo do exemplo acima num arquivo de texto, nomeá-lo autorun.inf e movê-lo para o diretório raiz de um drive, ao montar ou acessar este drive, as instruções contidas neste arquivo serão executadas. Creio que as instruções sejam auto-explicativas. Basicamente o ícone do drive exibirá agora o ícone menu.ico, contido no diretório raiz do drive (do CD-ROM, por exemplo) e a aplicação menu.exe será carregada. À esta altura você deve imaginar como o vírus de pen-drive é executado assim que ele é inserido. Isso mesmo, usando essa técnica. Todos os pen-drives infectados que analisei continham um arquivo autorun.inf, com instruções para execução do vírus. Evitando a contaminação: Ficar imune é impossível. Nunca poderemos prever as técnicas de contaminação que serão inventadas. Aliás, cá entre nós, são muito “boas”. No entanto, algumas medidas podem ser tomadas para diminuir os riscos de contaminação. São elas: 1. Desabilitar a auto-reprodução em unidades removíveis. No registro, navegue até a chave HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindowsCurrentVersionpoliciesExplorer e sete o valor NoDriveTypeAutoRun para 4. Isso desabilitará a auto-reprodução em unidades removíveis. Se quiser desabilitar em qualquer tipo de unidade (recomendo), utilize o valor ff. 2. Sempre abrir a unidade removível (pen-drive, cartão de memória, etc) pelo Windows Explorer, ao invés de dar duplo-clique no ícone. Basta clicar com o botão direito na unidade e escolher “Explorar” ou abrir o Windows Explorer e clicar sobre a unidade. 3. Desabilitar a execução do arquivo autorun.inf, geralmente responsável pela infecção.Copie e cole as linhas abaixo para o Bloco de Notas e salve como inf.reg: REGEDIT4 [HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionIniFileMappingAutorun.inf] @=”@SYS:DoesNotExistSEUNOME” Substitua SEUNOME pelo seu primeiro nome (sem acento ou espaços). Depois basta executar este inf.reg que você gerou. Para facilitar as coisas, escrevi um software que faz este trabalho. Chamei-o de USBForce. Funciona assim: Na primeira vez que é executado na máquina, o USBForce: Desabilita a auto-reprodução em todas as unidades. Desabilita o reconhecimento de arquivos autorun.inf. Habilita sua proteção. A partir deste momento, ao inserir um pen-drive, cartão de memória ou qualquer dispositivo removível, o Windows o reconhecerá mas nenhuma tela será aberta. Você deve então clicar no ícone do USBForce em sua área de trabalho. Ele abrirá o dispotivo pra você e tentará detectar se existe algum vestígio de infecção de vírus no dispositivo. Em caso afirmativo, ele tentará localizar o vírus e deletar (você será avisado antes). Naturalmente é interessante colocá-lo em alguma pasta e criar um atalho pra ele na área de trabalho, para facilitar o acesso.
  13. Recentemente fui requisitado para verificar a possibilidade de um determinado sistema cliente-servidor funcionar em thin clients (terminais leves, que usam o processamento e o SO de um servidor de terminais). O sistema baseia-se em um aplicativo servidor e um pequeno aplicativo cliente, que deve ser instalado em todas as estações. Mas ele não foi feito para funcionar com terminal services: este pequeno aplicativo instalado nas estações mantém suas configurações numa chave de registro em HKEY_LOCAL_MACHINE. Logo, cada estação precisa de um SO para que o aplicativo crie esta chave e armazene suas configurações individuais. Imediatamente pensei que se este aplicativo armazenasse suas configurações em HKEY_CURRENT_USER, seu funcionamento em thin client seria viável, uma vez que esta chave do registro existe para cada usuário que se loga no sistema (diferente da chave HKEY_LOCAL_MACHINE, que é única no SO). Neste artigo vou mostrar como alterar o comportamento de um executável que armazena suas configurações em HKLM, fazendo-o armazenar em HKCU, para atingir o objetivo desejado. O primeiro passo é analisar o executável e verificar que valores ele cria no registro e qual o momento em que isto acontece. Para isso podemos usar o RegMon (Registry Monitor). Com o RegMon aberto, criei um filtro para incluir somente o cliente.exe na monitoração do RegMon e dar um highlight quando a operação for CreateKey (criar uma chave no registro). Ao executar cliente.exe, vamos ver o que o RegMon informa. Na primeira linha com highlight em vermelho, vemos que o software procura pela chave HKCUSoftwarePCSCliente. Como não existia tal chave, ele a criou (visível nas linhas que seguem). Depois o software procura pela chave HKLMSoftwarePCSCliente e também não encontra (veja a coluna RESULT) e também a cria. Rolando mais abaixo, vemos que ele procura por sub-chaves também, mas ainda não as cria: Cliquei no botão Aplicar do software, que salva as configurações e aí sim, o RegMon acusou várias criações de sub-chaves (CreateKey) e inclusive a criação de valores (SetValue). Veja que todos são em HKLM: Nossa intenção é fazer com que o software crie estes valores em HKCU, para permitir o uso de thin clients. Já sabemos quando e onde o programa cria as chaves, agora precisamos saber qual é a API do Windows responsável por criar as chaves no registro. Para isso, basta consultar o Win32 SDK Online Help (win32.hlp). Na busca, procurei por “reg” e imediatamente achei a RegCreateKey e a RegCreateKeyEx. Mas a RegCreateKey é para compatibilidade com aplicativos do Windows 3.11, portanto a API correta é a RegCreateKeyEx. Vamos olhar o trecho inicial da documentação sobre ela: LONG RegCreateKeyEx( HKEY hKey, // handle of an open key LPCTSTR lpszSubKey, // address of subkey name DWORD dwReserved, // reserved LPTSTR lpszClass, // address of class string DWORD fdwOptions, // special options flag REGSAM samDesired, // desired security access LPSECURITY_ATTRIBUTES lpSecurityAttributes, // address of key security structure PHKEY phkResult, // address of buffer for opened handle LPDWORD lpdwDisposition // address of disposition value buffer ); Parameters: hKey Identifies a currently open key or any of the following predefined reserved handle values: HKEY_CLASSES_ROOT HKEY_CURRENT_USER HKEY_LOCAL_MACHINE HKEY_USERS The key opened or created by the RegCreateKeyEx function is a subkey of the key identified by the hKey parameter. Basta ler este trecho da documentação para perceber que o parâmetro hKey, que é passado para a função RegCreateKey é o responsável por definir se a criação da sub-chave será em HKCR, HKCU, HKLM ou HKU. Então temos que encontrar o ponto onde o programa chama a função RegCreateKey, passando como parâmetro hKey o valor que representa HKCL e substituir pelo valor que representa HKCU. Mas que valores são esses? Bem, eu não achei na documentação mas podemos encontrar isso já debugando o programa. Mãos à obra! Ao abrir o cliente.exe no OllyDbg (um debugger de executáveis 32-bits), antes de rodar o software, precisamos definir breakpoints (pontos de parada, numa tradução livre) para que o debugger interrompa a execução do software quando a API que queremos for chamada, no caso, a RegCreateKeyEx. Para isso, na Command Bar, basta digitar “BP RegCreateKeyExA” (sem aspas) e pressionar [ENTER]. Este “A” é porque a função trabalha com caractes em ASCII. Para caracteres UNICODE, usa-se “W”. Agora é só rodar o programa no OllyDbg (F9) e esperar parar. A primeira parada (breakpoint) na função RegCreateKeyExA não é a que esperamos, pois antes de criar a nossa chave, o software precisa criar outras chaves (de sistema) que são essenciais ao seu funcionamento. Portanto, vamos apertando F9 (Run) até que a parada seja na hora da criação de nossa chave. Aqui precisei rodar (F9) mais cinco vezes. Quando for a correta, a stack (pilha de memória) que o OllyDbg mostra, ficará como na imagem abaixo: Perceba que foi passado o valor hexa 80000002 como parâmetro hKey e isso resultou HKEY_LOCAL_MACHINE (o OllyDbg nos mostra). Os possíveis valores são: Chave Valor (em hexa) HKEY_CLASSES_ROOT 80000000 HKEY_CURRENT_USER 80000001 HKEY_LOCAL_MACHINE 80000002 HKEY_USERS 80000003 HKEY_PERFORMANCE_DATA 80000004 HKEY_CURRENT_CONFIG 80000005 HKEY_DYN_DATA 80000006 DICA: Se clicarmos com o botão direito no valor 80000002, na stack, e escolhermos a opção “Modify”, poderemos mudar este valor em tempo de execução. Mas é claro que a alteração não será permanente pois estaríamos alterando na memória. Para alterar permanentemente, precisamos saber em que parte do programa está este valor 80000002 (HKLM) e mudar para 80000001 (HKCU), mas em respeito ao desenvolvedor do software, não mostrarei publicamente como fazer isso.
  14. Muitos jogos antigos apresentam problemas ao serem executados ou simplesmente instalados no Windows Vista. Isto acontece por conseqüência de diversos fatores, mas o principal é que quando o jogo (ou software) foi desenvolvido, o Windows Vista ainda não estava no mercado, o que impediu testes de serem realizados, entre outros aspectos. Este artigo mostra um exemplo de como utilizar a ER para estudar o executável do game e saber o que o impede de rodar no Vista. Você verá como uma simples alteração em 2 bytes de um arquivo PE pode salvar seu fim de semana. Ao tentar instalar o jogo Mortal Kombat 4 (PC) no Windows Vista Home Basic, obtivemos um erro fatal que dizia: “Start Menu Error”. A única opção era clicar no botão OK, que encerraria a execução do programa de instalação. Numa tentativa de contornar tal situção, copiamos o conteúdo do CD-ROM para um diretório no disco rígido e tentamos executar o game pelo seu executável direto, o MK4.EXE. Isso resultou no seguinte erro: É notável que o executável checa se a instalação do jogo foi feita e, como não foi feita, recebemos a mensagem acima. Ao clicar em OK, o processo é encerrado. Mas o que será que o jogo checa para saber se está instalado ou não? Para responder a essa pergunta precisaremos de um debugger de executáveis. Um aplicativo que mostra, em assembly, as rotinas executadas por arquivo PE. Usaremos o OllyDbg para tal função. Ao abrir o executável MK4.EXE no OllyDbg, vamos procurar pela string de texto contida na mensagem de erro da primeira imagem. Para isto, clique com o botão direito do mouse no primeiro quadrante e escolha “Search for > All referenced text strings”, como sugere a imagem abaixo: A próxima tela mostra uma lista contendo todas as strings de texto encontradas e entendidas no arquivo MK4.EXE. Nela, clicando novamente com o botão direito do mouse e escolhendo “Search text”, abrirá uma janela como a mostrada abaixo e então digitamos o texto “CD” (sem aspas) e marcamos a opção para diferenciar o caso, para filtrar a pesquisa. Isso foi feito para encontrarmos a string de texto que nos foi exibida no erro incial, lembra-se? O texto era “Mortal Kombat 4 is not installed. Run Setup from the CD”. Por isso buscamos a palavra “CD”, para achar essa string dentro do executável do jogo, o que nos leva para próximo da rotina onde esta mensagem é chamada. Vamos ver o resultado na imagem abaixo: O Olly nos mostra que no endereço 004AD2B1, o comando é PUSH 004F474C, que vai empurrar para a memória (stack) o nosso texto. Para localizarmos exatamente onde está este comando no programa, basta darmos um ENTER nesta linha e a tela abaixo é exibida. Entramos no bloco onde o texto do erro é exibido na tela. Vamos subir um pouco para ver o que vem antes. Na linha 004AD299 temos um CALL (como um GOTO) e, depois que a CALL temina e o programa volta para sua execução normal, temos um TEST EAX, EAX, que é um comando que verifica se o conteúdo de EAX é zero. Então podemos prever que a CALL altera o valor de EAX. Mais abaixo, temos um JNZ 004AD2D4. Esse JNZ significa Jump if Not Zero (Pule se não for zero), o que quer dizer que a execução do programa saltará para a linha 004AD2D4 se o conteúdo de EAX não for zero. Bom, se a execução não saltar e seguir abaixo, cairemos na mensagem de erro. Se saltar, a pularemos. Então seria interessante alterar essa parte do programa para que sempre salte para a linha 004AD2D4. Isso significa que independente do resultado do teste anterior (TEST EAX, EAX) o salto ocorrerá, sempre. Assim nunca cairemos na mensagem novamente e programa continuará sua execução normal. O comando que faz o papel de salto incondicional em assembly é o JMP (Jump). Então, vamos alterar o JNZ da linha 004AD2A0 para JMP (e manter o resto da linha). Para isso, basta selecionar a linha e apertar a barra de espaços, depois clicar em Assemble. Feito isso, o Olly marca nossa alteração em vermelho. Clicando com o botão direito do mouse sobre a alteração (ou próximo) e escolhendo “Copy > All modifications”, uma tela com as modificações abre e então basta clicar novamente com o botão direito e escolher “Save file”. Dei o nome de MK4-mod.EXE para facilitar o reconhecimento. Agora vamos ao teste. Ao executar este novo executável modificando, vemos o jogo rodando: É importante esclarecer que os passos descritos aqui não são genéricos e não servem para todos os softwares que não funcionam, em primeira instância, no Vista. O funcionamento depende de vários fatores e principalmente do nível de integração do software com o SO para o qual ele foi desenvoldido. O objetivo deste artigo foi demonstrar como a ER pode nos ajudar a resolver pequenos (e grandes, por quê não?) problemas do dia-a-dia na informática. Este é um dentre dezenas de exemplos de uso da ER para soluções que seriam um pouco difíceis sem ela.
  15. A maioria dos vírus e pragas virtuais compartilham de certos métodos de auto-inicialização com o SO. Isto inclui os spywares e seus similares. Os sistemas Windows possuem métodos para inicializar programas junto ao seu carregamento limitados. Na maioria das vezes os vírus iniciam justamente por eles e daí a importância de conhecê-los e saber gerenciá-los. Desta forma, o técnico pode remover manualmente muitas pragas, o que economizará tempo com scans de softwares antivírus e anti-spys, além de ser extremamente útil quando o vírus ataca estes softwares de proteção, impedindo sua inicialização. Primeiro vamos ver de que jeito um aplicativo pode ser iniciado junto ao Windows. A maior parte dos vírus age deste jeito. Em sistemas baseados em Windows NT, o que inclui os Windows 2000, XP e 2003, os métodos de inicialização de programas são: Através da pasta Inicializar do Menu Iniciar. No registro do sitema. Por serviço de sistema (o que inclui certa parte do registro). Na primeira maneira, basta colocar um atalho para o programa que se deseja executar na pasta Inicializar do Menu Iniciar. Por exemplo, na imagem abaixo, a cada inicialização do Windows, inicializaremos a Calculadora junto. Obviamente um vírus pode se aproveitar deste recurso e colocar um atalho para si neta pasta mas não é comum isso acontecer pois o vírus ficaria facilmente visível e uma das intenções de vírus complexos é passar despercebido ao usuário/técnico. De qualquer forma, não custa conferir. Agora vamos ao método mais usado, o registro do sistema. Aqui precisaremos explicar resumidamente como o registro destas versões do Windows funciona. O registro é um banco de dados que armazena informações essenciais sobre diversos softwares instalados no Windows, além de informações pertinentes ao próprio sistema. Por conta disto, é comum apelidar o registro de “alma do sistema”. Esse banco de dados possui, além de outros dados, chaves, sub-chaves e valores numa organização hierárquica (similar ao Windows Explorer). Para o artigo, precisaremos conhecer essas três chaves: HKEY_LOCAL_MACHINE – Esta é a chave mais importante do registro. Nela estão contidas informações sobre o PC (hardware instalado, softwares com sua opções e configurações e outros itens). Inclusive veremos que um programa pode ser inicializado por uma sub-chave desta chave. HKEY_USER – Nesta chave são definidas configurações personalizadas para cada usuário do sistema, já que as versões do Windows mais novas permitem logon simultâneo ou não de usuários diferentes. De maneira similar à chave anterior, um vírus pode inicializar-se junto ao SO somente para um usuário específico, usando uma sub-chave desta chave. HKEY_CURRENT_USER – Como o nome sugere, mantém informações sobre o usuário que está atualmente logado no sistema. Todo o registro é dinâmico mas esta chave merece uma definição de dinamismo especial pois muda os valores de suas sub-chaves completamente quando logamos com outro usuário no Windows. Não é difícil de deduzir que ela é um atalho para uma sub-chave de HKEY_USER, já que esta última mantém uma sub-chave para cada usuário cadastrado no sistema. Por exemplo, se logarmos com o usuário “Fernando”, esta chave será uma cópia da sub-chave HKEY_USER. SID (Security Identifier) é uma identificação única que cada usuário tem e o SO conhece os usuários através deste SID. Abaixo, o utilitário “regedit” (Registry Editor), usado para visualizar o conteúdo do registro do sitema. Perceba a igualdade entre as áreas destacadas em vermelho. É justamente o que falamos na explicação da chave HKEY_CURRENT_USER. Note o SID do meu suário também. Depois desta breve introdução ao registro do sistema, podemos partir para as sub-chaves que realmente importam na questão da remoção manual de vírus. São elas: HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindowsCurrentVersionRun HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindowsCurrentVersionRunOnce HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindowsCurrentVersionRunOnceEx HKEY_USERS\SOFTWAREMicrosoftWindowsCurrentVersionRun HKEY_USERS\SOFTWAREMicrosoftWindowsCurrentVersionRunOnce As duas últimas chaves acima dependem do SID do usuário mas se a suspeita de vírus for no usuário que está logado, você pode acessá-las pelo atalho como comentamos acima. Tudo o que estiver nestas chaves será inicializado junto ao sistema. Faça o teste: verifique o que tem nas sub-chaves de seu PC e veja os caminhos para os arquivos que incializam. No exemplo HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindowsCurrentVersionRun da máquina que usei, está assim. Como podemos ver, só há três valores, que são os caminhos absolutos dos executáveis que inicializam junto à máquina usada. Esta é uma máquina virtual. Numa máquina real, os valores normais são outros. Para remover programas da inicialização, basta remover os valores desejados. É importante salientar que muitos arquivos presentes nestas sub-chaves são essenciais ao sistema e não devem ser removidos. Cabe ao técnico saber identificar caminhos e nomes de executáveis suspeitos. Se você tiver dúvida quando à procedência de algum arquivo, pode digitar seu nome no site Process Library, que mantém uma lista atualizadas de processos (programas em execução) para nos ajudar a identificar se são do sistema ou não. Há ainda o método de serviços que podem ser utilizado por alguns vírus mais complexos. A tela de serviços você tem acesso indo no menu Iniciar > Executar, digitando services.msc e clicando no botão OK. Eis a da máquina que usamos para o artigo. Esses serviços são na verdade processos (programas) inicializados que recebem este nome pela capacidade de poderem ser muito melhor gerenciados pelo sistema operacional que um processo comum. Perceba a coluna “Status” na imagem. Um serviço pode ser iniciado, reiniciado, pausado ou parado e seu método de inicialização pode ser manual (quando clicado), automático (a cada inicialização do sistema) ou desativado. Um vírus obviamente se aproveitaria do método automático e poderíamos pará-lo e depois desativá-lo numa remoação manual. Assim como os processos comuns, a maioria dos serviços é essencial ao sistema, portanto, é bom que se faça uma pesquisa sobre ele (no Google e sites similares) antes de parar ou desativar um serviço suspeito ou não. Ao entrarmos nas propriedades de um serviço, vemos o caminho do executável ao qual ele se refere além de uma caixa drop-down para alterar o tipo de inicialização, como mostra a imagem abaixo. Com este básico conhecimento, muitos vírus podem ser removidos mas é claro que não basta. Recomendamos sempre um scan com um bom antivírus atualizado e com um anti-spy, mesmo após a remoção manual. Existem alguns programas que podem ajudar na identificação de vírus e na remoção manual. Abaixo segue uma lista com descrição: Process Explorer – monitora os processos em execuçao em tempo real, o que permite identificarmos se algum processo suspeito está sendo executado. O Process Explorer também mostra o que o processo está fazendo ou tentou fazer (quando bloqueado pelo SO). HiJackThis – gera uma lista e um arquivo de log com todos os processos que inicializam junto ao sistema, podendo ser utlizado inclusive para remover o que sejam julgados suspeitos (muito cuidado com seu julgamento). NOTA: O projeto original foi descontinuado, mas o usuário Polshyn Stanislav da Ucrânia o continuou. Gmer - uma aplicação que detecta e remove rootkits (dentro de um limite, claro). A ideia é procurar por uma lista de coisas escondidas, tais como processos, threads, serviços, arquivos etc. MSCONFIG – utilitário presente no Windows XP mas que pode ser copiado a partir do arquivo msconfig.exe para outros sitemas Windows. Ele mostra de forma interativa o que está sendo inicializado junto ao sistema além de permitir a consulta de outras informações. Você pode chamá-lo a partir do menu executar.
  16. O Wireshark é um dos sniffers eanalisadores de tráfego de rede mais conhecidos atualmente. Ficou muito famoso com seu antigo nome, Ethereal. Além de ter uma ótima interface gráfica, também possui uma interface em linha de comando muito poderosa. Neste artigo vamos ver como são frágeis os sistemas de login em texto puro. Sniffaremos uma tentativa de login num servidor FTP e num site HTTP, que inclusive, é o nosso. Um sniffer coloca a interface de rede da máquina onde está instalado em modo promíscuo, neste modo a interface capturará qualquer pacote que receba, mesmo os não destinados a ela. Normalmente as interfaces de rede descartam os pacotes que não são destinados à elas. É o famoso broadcasting de pacotes. Há também sniffers que não colocam a interface em modo promíscuo (para não serem detectados) mas neste caso, a máquina com o sniffer instalado tem que ficar entre os comunicantes e não só na mesma estrutura de rede. Outro detalhe é que um hub sempre manda o pacote que recebe por uma porta para todas as outras portas, o que facilita o sniffing. Já os switchs, não fazem isso (na verdade fazem só na primeira vez em que são ligados na rede e estão escrevendo sua tabela ARP. Depois mantêm tal tabela até ser desligado da energia). Mas não é impossível sniffar numa rede só com switchs, só é mais difícil. O primeiro passo é saber onde instalar o Wireshark. Este passo é muito importante pois definirá o sucesso na captura de pacotes. Atente para as disposições da máquina onde estará o Wireshark. Se for uma rede Ethernet concentrada por um hub, a máquina que rodará o Wireshark poderá ficar em qualquer lugar, basta ser conectada ao hub: Já que o hub envia todo o tráfego em broadcast, qualquer máquina recebe todo o tráfego da rede, então basta que a interface de uma delas esteja em modo promíscuo (Wireshark instalado e rodando) para que o sniffing tenha sucesso. Se o concentrador central de sua rede for um switch, você poderá colocar um hub entre o servidor e o switch e conectar uma máquina com o Wireshark neste hub. Assim, toda comunicação entre servidor e rede passará pela máquina com o Wireshark. Veja a imagem a seguir: Há outras maneiras de se fazer o sniffing com switchs, principalmente se o switch for configurável (que possibilita port spanning). Mas deixaremos tal discussão para outra hora. Pode-se discutir sobre assunto em nosso fórum também. Instalação do Wireshark Depois de escolhida a máquina onde será instalado o Wireshark, vamos para sua instalação, que pode ser feita de várias maneiras (baixando e compilando seu código-fonte, baixando sua instalação, instalando via repositório para sistemas baseados em Linux, etc). Como sempre, utilizei o Ubuntu para escrever este tutorial. No Ubuntu a instalação é fácil, basta comandar: $ sudo apt-get install wireshark Abra o Wireshark e clique no primeiro botão, entitulado "List available capture interfaces...". A janela que abrir mostrará uma lista com as interfaces de rede da máquina. Aqui a minha disposição na rede é como mostra a figura abaixo: Perceba que tenho o Wireshark instalado na máquina que dá acesso ao servidor. As outras duas máquinas estão conectadas ao servidor principal através dela. Captura dos pacotes Uma das máquinas tem o IP 192.168.0.23, e é através dela que vou acessar a internet e abrir um site FTP e um HTTP para que a máquina com o Wireshark capture os pacotes de tais conexões. Voltando ao Wireshark, escolhi a minha conexão eth1 (192.168.0.5) que é a placa de rede da máquina onde o Wireshark está instalado, que está ligada ao switch do esquema acima. Ao clicar em "Start", inicia-se a captura dos pacotes. Então, a partir da máquina 192.168.0.23, acessei o site FTP da Asus (ftp.asus.com) para uma tentativa de login. Fui pelo próprio comando FTP do Windows 98: c:\> ftp ftp.asus.com E inseri o nome de usuário fernando e password 3966lop98. Obviamente recebi um erro de falha no login. Neste momento podemos clicar no botão Stop (quarto botão da barra do Wireshark). Vamos analisar o que o Wireshark capturou: Perceba que no pacote de número 14, há menção ao meu nome de usuário no comando USER do FTP. É importante conhecer o protocolo que será analisado para obter bons resultados com o sniffing. No pacote 16 o servidor da Asus me informa que preciso informar um password. E nos pacotes 18 e 19, abro uma conexão TCP e envio meu password com um comando PASS, respectivamente. Veja que o argumento do comando PASS é 3966lop98, que é justamente o password que informei através da máquina 192.168.0.23! O protolo FTP transmite os comandos e argumentos em texto puro. É extremamente inseguro e em seu lugar é recomendável utilizar o SFTP (Secure File Transfer Protocol). Claro que foi uma transmissão pequena e premeditada. Se fôssemos analisar diante de vários milhares de pacotes, seria difícil achar manualmente o pacote que se relaciona com o login. Para isso existem os filtros personalizados e montados com expressões regulares pelo próprio Wireshark. Usaremos um filtro no próximo sniffing, que será HTTP. Sniffing em login do Mente Binária Agora vamos acessar um site a partir da máquina 192.168.0.23 para analisarmos os pacotes. Seguindo a mesma lógica do item anterior, vamos capturar os pacotes até que o login seja efetuado. Loguei com um usuário. Vamos descobrir quem é (e sua senha) através do Wireshark. A análise de pacotes retornou 742 pacotes até eu clicar em Stop. Não vamos olhar um a um e sim usar um filtro. No campo "Filter", digite: "http matches POST" (sem aspas, respeitando a caixa). E clique em "Apply". Vamos ver o resultado: Veja que filtramos só para pacotes HTTP que contenham o comando POST. Este é um comando de formulário HTTP (comumente usado em logins). É o comando dado pelo botão "Enviar" do nosso site. No pacote de número 150, vemos o nome de usuário e a senha sendo enviados para o servidor de nosso site! Conclusão Espero que o propósito deste artigo tenha sido atingido: mostrar que alguns protocolos são facilmente vulneráveis. Com um rápido sniffing, podemos reunir informações que seriam suficientes para um invasor se apossar de um conta da vítima. Teste o Wireshark em sua rede, veja como ele se comporta capturando pacotes HTTPS, SFTP, SSH, etc, que são protocolos seguros. Veja que as informações estarão encriptadas.
  17. Apesar de ser um assunto que já foi tratado em muitos sites e fóruns, continuo a observar que alguns usuários do Windows devem ter mais atenção com o tipo de arquivo que aplica o famoso duplo-clique. Identificar se o arquivo é inofensivo ou se é uma ameaça pode ser mais fácil do que se imagina. Este artigo tem como intenção desmistificar as lendas sobre as extensões de arquivos maliciosos e alertar sobre as extensões perigosas. O sistema operacional Windows reconhece (deduz o conteúdo) dos arquvios por sua extensão. Mas o que é extensão de arquivo? Extensão de arquivo para o MS-Windows, comumente são os três caracteres após o ponto, no nome completo do arquivo. Por exemplo, documento001.txt, é um arquivo com extensão .TXT, logo, o Windows deduzirá que é um documento de texto e associará este documento à um certo programa, que poderá abrí-lo (recebendo seu caminho como parâmetro) mediante um duplo-clique neste documento. Confundiu? Bom, em termos práticos, cada extensão de arquivo que mereça, possui um determinado programa responsável por interpretar um duplo-clique num arquivo que possua tal extensão. Essas informações (quais extensões são abertas por qual programa) ficam no registro do Windows. Ainda com o exemplo do documento001.txt, vamos analisar sua associação: ao dar um duplo-clique nele, vemos que ele é aberto pelo Bloco de Notas (notepad.exe). A chave do registro responsável por armazenar as informações desta associação é, no Windows XP: HKEY_CLASSES_ROOT\txtfile\shell\open\command. Veja a imagem: Perceba que na coluna "Dados" há o caminho completo do notepad.exe (usando uma variável de sistema sim, mas não deixa de ser o caminho absoluto), seguido de %1. Já sabemos que é o Bloco de Notas (notepad.exe) que abrirá arquivos de texto, agora vamos entender o parâmetro. Eu escrevi mais acima que o caminho do arquivo a ser aberto era passado por parâmetro. É justamente isso que o "%1" faz. Essa variável armazena o caminho absoluto do arquivo que está sendo acessado, no instante do acesso. Portanto, se você clicou no arquivo documento001.txt e ele está em C:\docs\, esta variável conterá o valor C:\docs\documento001.txt. Isso informa ao Bloco de Notas onde está o arquivo. Agora que já sabemos o que é e como funciona a extensão, vamos aos riscos. Um arquivo executável precisa ter uma extensão de executável para ser executado. As extensões mais comuns de arquivos executáveis são: EXE, COM, BAT, VBS, MSI, SCR, arquivos do Office (porque podem conter macros, que são executáveis). Um vírus, obrigatoriamente, tem que ter uma dessas extensões. Ou seja, ele tem que ser um executável. O problema é que há certos disfarces utilizados pelos disseminadores de vírus. Um deles é colocar um nome de arquivo do tipo: "arquivo.jpg .exe". Assim mesmo, com vários espaços entre o .jpg e o .exe. A extensão deste arquivo de exemplo é .EXE e ele será executado como tal! Não é .jpg! O texto ".jpg" neste caso faz parte do nome do arquivo e não da extensão. A "técnica" de colocar espaços é para que os programas de email e webmail identifiquem um nome muito grande e exibam reticências após o .jpg, dando a impressão que é um arquivo de imagem. Detalhe que o ícone de um executável pode ser facilmente alterado para o ícone de uma imagem, o que aumenta as chances da vítima de ser enganada. Os vírus são programas. Logo, repito, são executáveis. Um arquivo de áudio puro, por exemplo, não pode ser vírus! A extensão .mp3 estaria associada ao Windows Media Player, ou Winamp, ou qualquer outro. Esses software não executam rotinas de executáveis. Só entendem fluxo de mídia, portanto, não executam as rotinas virais diretamente. Mas eles podem conter falhas que sejam exploradas através de payloads maliciosos de arquivos de mídia ou playlists (como os .m3u). Tenha certeza da extensão do arquivo e de sua procedência, e estará praticamente livre de ser infectado por um vírus anexo à um e-mail ou disponível para download. Mas lembre-se que os arquivos podem também estar zipados (.ZIP) ou compactados com outro compactador (RAR, LZIP, GZIP, LHA, JAR, etc). Dentro deles é que você deve examinar a extensão do arquivo.
×
×
  • Create New...