Ir para conteúdo
  • Cadastre-se

Pesquisar na Comunidade

Mostrando resultados para as tags ''engenharia reversa''.



Mais opções de pesquisa

  • Pesquisar por Tags

    Digite tags separadas por vírgulas
  • Pesquisar por Autor

Tipo de Conteúdo


Fóruns

  • Conteúdo Mente Binária
    • Núcleo
    • Projeto Bumerangue
    • Fale com a gente!
  • Comunidade
    • Engenharia Reversa
    • Programação
    • Redes
    • Análise de malware
    • Teste de intrusão
    • Sistemas operacionais
    • Certificações
    • Oportunidades
    • Outros

Categorias

  • Crackmes
  • Documentação
  • Debuggers
  • Ferramentas para PE
  • Utilitários
  • Unpackers

Encontrar resultados em...

Encontrar resultados que...


Data de Criação

  • Início

    FIM


Data de Atualização

  • Início

    FIM


Filtrar pelo número de...

Data de Registro

  • Início

    FIM


Grupo


Website


Github


LinkedIn

Encontrado 14 registros

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

    Keygen Feito em python

    Este e um keygen simples feito em python, ele e voltado a resolver um algoritmo de um crack-me que encontrei na net, e como agente não ver por ai como é feito um keygen fiz o meu próprio em um script bem simples mas que funciona. lembrando que isso deve ser usado para fiz educacionais. ############################## # Autor: Aof # # date: 09/06/2018 # # name: KEyGen User and Pass # ############################## string = input('user: ') #entrada do usuario const = 51651293 #serial inicial. obs uma constante print('Pass for user: '+string) #loop que faz o Bitwise for i in string: x=ord(i) #pega o valor ascii do 'i' e move para o x #print(x) const += x #soma o const com o valor de x print(const) #mostra o serial final Explicação: O keygen primeiro pega o tamanho do texto digitado, depois ele faz um loop, nesse loop ele pega o valor da letra na tabela ASCII e com esse valor o algoritmo somado com a constante 51651293. Com isso sabemos que conforme a loop for rodando a constante vai sendo incrementada e na final da o resultado esperado, nosso serial.
  3. Complementando os artigos criados sobre máquina virtual para ambiente Windows e Linux, este tutorial tem como finalidade auxiliar na criação de uma máquina virtual para análise de binários, possivelmente maliciosos, em ambiente macOS. Configurações da Máquina Virtual 2 processadores (cores). 2GB de RAM. Placa de rede em modo NAT (em casos aonde você realmente precisa de comunicação com um C&C). Placa de rede em modo Host-Only. Compartilhamento de Pastas desativado ou Host-Only (com host local). Aqui vem um ponto interessante: como tenho receio de malwares que detectam o ambiente de virtualização (ex: VMware Fusion) e tentam escapar do guest pro host, rodo sempre o SO guest num SO host diferente. No caso, rodo a máquina virtual com macOS mas o SO host é Linux. Sistema Operacional virtual 10.10.1 (Yosemite) publicado em 2014 10.13.4 (High Sierra) Versão Atual Obs.: As duas versões do macOS mencionados acima são para demonstrar a tela de configuração do Gatekeeper de cada versão. A importância das versões está nos diferentes tipos de malware que podem se propagar em versões específicas. No entanto, basta escolher uma. O Gatekeeper é um componente de proteção para o macOS existente desde a edição Mountain Lion. A responsabilidade deste constituinte é encontrar a identificação do desenvolvedor (Developer ID, também conhecido como “assinatura de autenticidade”), que é fornecido pela própria Apple. Quando em conformidade, o Gatekeeper mantém-se adormecido até o momento do cujo arquivo executável ou instalador ser flagrado com a assinatura de autenticidade ausente ou por ser reconhecido pela semelhança de algum tipo de malware. Uma vez que você utiliza softwares baixados através da App Store e ou assinados pela Apple você já possui uma certa segurança. Tendo consciência que boa parte dos últimos malwares para macOS dependiam que este recurso estivesse desativado, consequentemente, permitindo o download e instalação de qualquer software não identificado. Desativando o Gatekeeper Imagem 1: versão Yosemite No macOS Sierra e posterior a opção “Anywhere” não aparece mais, agora o sistema operacional perguntará para o usuário se ele deseja permitir que o software realmente seja instalado / executado no sistema. Porém há maneira de desabilitar o Gatekeeper e voltar com a opção como mostra na Imagem 1, usando o spctl (SecAssessment system policy security), via Terminal: $ sudo spctl --master-disable Imagem 2: versão high-sierra Outro sistema de segurança é o SIP (System Integrity Protection). Eu ainda não vi nenhuma necessidade de desativar para rodar malware porem caso precisem: SIP (proteção de integridade do sistema) Clique no menu Selecione Reiniciar ... Mantenha pressionado o ⌘comando-R para inicializar no sistema de recuperação. Clique no menu Utilitários e selecione Terminal. Digite csrutil disable e pressione Enter. Feche o aplicativo Terminal. Clique no menu e selecione Reiniciar .... Ferramentas No macOS além da própria Apple Store (que com certeza neste caso não terá as principais ferramentas que precisamos), também temos algumas boas fontes de ferramentas. O MacPorts, sistema de pacotes muito utilizado, e também o Homebrew suprem muito bem nossas necessidades quanto aos pacotes. Abaixo deixei um lista de ferramentas tanto para análise estática quanto dinâmica, claro que em alguns casos a mesma ferramenta pode ser utilizada em ambos os tipos de análise. Analise Estática xxd -> Cria um dump a partir de um binário, parecido com o hexdump. strip -> Remove e ou modifica a tabela de símbolos de um binario. hexEdit -> Editor hexadecimal. lipo -> Cria ou modifica arquivos multi-arquitetura, na imagem 6 tempos um exemplo da sua funcionalidade. otool -> Exibe informações sobre binários Mach-O (tipo um objdump). jtool -> Versão melhoradas do otool. nm -> Exibe a tabela de símbolos. codesign -> Usado para criar, verificar e exibir assinaturas de código. machOView -> Interface visual para edição de binários mach-o. class-dump -> Usado para examinar informações em tempo de execução do Objective-C armazenadas em arquivos Mach-O. dtrace -> Ferramenta usada para analisar comportamento do Sistema Operacional e dos programas em execução. fs_usage -> Exibe informações sobre chamadas de sistemas, executa rastreamento do kernel e processos, tudo em real-time. xattr -> Usado para exibir, modificar e ou remover atributos(metadados) de arquivos, diretórios e links simbólicos. Analise Dinâmica xcode -> IDE de desenvolvimento de software oficial da apple, possui recursos internos para testes de perfomance de sistema. hopper -> Ferramenta usada para disassemble e decompile de arquivos mach-o 32/64bits. lldb -> Debugger utilizado para depurar programas C, C ++, Objective-C, Objective-C ++ e Swift. fseventer -> Ferramenta gráfica usada para verificar atividades em disco e execução de processos de forma visual. open snoop -> Usado para rastrear acessos de arquivos, aplicativos, processos e também monitoramento do filesystem, você pode utilizar ele em conjunto com o Dtrace. activity Monitor -> Exibe processos que estão sendo executados no macOS. procexp -> Ferramenta exibe informações acessíveis pelo proc_info para exibição de processos, parecido com o top e htop. lsock -> Baseado no PF_SYSTEM o lsock e usado para visualização em real time das conexões (Sockets) no sistema, similar ao netstat. Por se tratar de máquina virtual para pesquisas em macOS, não poderia deixar de mencionar as ferramentas do pessoal da Objective-See. Vale a pena testar e acompanhar os artigos deles. Imagem 3: ferramentas objective-see Destaco três ferramentas especificas para quem estiver analisando binários do tipo Mach-O: Imagem 4: ferramenta otool exibindo o magic number do binário. Imagem 5: ferramenta jtool exibindo o endereço da função main() do binário Mach-O Imagem 6: ferramenta lipo extraindo o suporte a uma arquitetura especifica em binários do tipo fat. Considerações finais Criar um snapshot da instalação default; Ficar atento: Anti-Disasssembly Anti-Debugging Sistemas de Ofuscação Anti-VM Ficar atento às falhas publicadas
  4. Fernando Mercês

    Análise do malware IMG2005M.exe

    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
  5. Resolvi montar este tutorial depois do grande trabalho que tive pra atualizar a máquina virtual que utilizo para analisar malware. Perdi horas procurando e instalando programas. Minha vontade era de distribuir um OVA com a máquina já pronta e talvez faça isso no futuro mas por hora vou discutir aqui os programas que utilizo para uma análise básica de malware ou engenharia reversa de algum software. Máquina virtual 512 MB de memória Placa de rede em modo NAT Sistema operacional Utilizo o Windows 7 64-bits em Português do Brasil, para facilitar na análise de malware brasileiro que teste o idioma do SO. Claro que em alguns casos vale a pena você ter máquinas virtuais de outras versões do Windows e em diferentes idiomas para ver se o comportamento do seu alvo muda. “Alvo” aqui significa o arquivo que você vai analisar, seja porque ele é suspeito seja porque você quer conhecer seu funcionamento. Algums dicas durante de instalação: Não utilizar seu nome verdadeiro na criação da conta de usuário do Windows durante a instalação. Não há necessidade. Não instalar as extensões do software virtualizador como VMware Tools ou VirtualBox Guest Additions. Não instalar nenhum antivírus, firewall ou qualquer software de proteção. Desabilitar todos os itens na “Central de Segurança” do Windows, incluindo as atualizações automáticas. Arquivos diversos Costumo espalhar uns arquivos .doc, .xls etc pela máquina, a fim de parecer que é uma máquina real. Algumas ameaças (principalmente ransomware) tentam encriptar ou mesmo destruir arquivos das vítimas. Software geral 7-Zip: Descomprime qualquer formato! E é livre. Firefox: Browser livre. Flash: Caso precise analisar SWF ou visualizar algum site que exija, mas cuidado pra não baixar tralha da McAfee junto. Java: Somente a JRE (Java Runtime Environment), para analisar classes Java. .NET Framework: Para permitir que ferramentas e alvos escritos nesta linguagem rodem. Notepad++: o único editor de arquivos que você precisa no Windows! Software específico API Monitor: Pode monitorar qualquer chamada a uma função específica da API do Windows ou de uma DLL específica. Muito bom. CFF Explorer: Analisador de PE. DeDe: Descompilador para Delphi. Extremamente útil para os malwares brasileiros! DIE (Detect It Easy): Detector de packer novo e promissor com suporte a regras personalizadas. Dis#: Descompilador para .NET. E2A (Event to Address): Somente para alvos escritos em Borland C++, dá os endereços dos eventos, como o clique de um botão por exemplo. Impressionante. FileGrab: Monitora o filesystem e copia para um diretório qualquer arquivo criado nele. Útil pra monitorar comportamento de malware que se deleta. Hex Workshop: Editor hexa animal, mas pago (não se preocupe, há um livre no final da lista). IDA Free: Disassembler e debugger. Tem uma função graph muito útil, além de suporte a vários tipos de binários e muitos recursos. ImpRec (Import Reconstructor): Útil para unpacking manual, para reconstruir a IAT (Import Address Table) de binários. JD-GUI: Descompilador Java (gera código Java a partir dos .class). .NET Reflector: Outro descompilador pra .NET. Assim como o Dis#, gera MSIL (Microsoft Intermeddiate Language) como saída, não Assembly. OllyDbg: Debugger e disassembler. O principal programa para análise. PeStudio: Analisador de arquivos PE. pev: Toolkit para análise de binários. ProcessExplorer: Monitora o que os processos estão fazendo no sitema (chaves de registro que estão abrindo, outros arquivos etc). RawCap: Sniffer de pacotes que usa raw sockets. É muito rápido e simples de usar. Gera o PCAP pra ser analisado no Wireshark futuramente, o que pode ser feito fora da máquina virtual. RDG Packer Detector: Melhor detector de packer que conheço. RegShot: Usado para tirar uma “foto” do sistema antes e outra depois da execução de um aplicativo, a fim de compará-las para investigar o que foi alterado. ResHack (Resource Hacker): Editor de recursos (ícones, telas e afins dentro do binário). wxHexEditor: Editor hexadecimal livre, cheio de recursos. Tem a capacidade de editar discos inclusive. Software opcional, caso precise programar Orwell Dev-C++: Versão em constante atualização do Dev-C++, para quem precisar codar em C/C++ para ver o comportamento de uma função da API do Windows por exemplo. Visual Studio Express: Para desenvolver em .NET, a Microsoft oferece uma versão gratuita (Express) do Visual Studio. IMPORTANTE: Após instalar todos os softwares, é de extrema utilidade criar um snapshot da máquina virtual, pra você voltar facilmente a este estado limpo após analisar um arquivo suspeito, por exemplo. Se você é iniciante, recomendo baixar e testar um programa de cada vez, até entender o motivo pelo qual ele pode ser útil. Alguns programas da lista acima são redundantes entre si pois possuem o mesmo objetivo, mas às vez um é mais preciso que outro dependendo do alvo, por isso é interessante mantê-los. Recomendo colocar as ferramentas que não possuem programa de instalação, como o E2A, num diretório tipo C:tools e criar atalhos para os executáveis principais na área de trabalho. O artigo é curto mas o trabalho que o leitor terá é grande.
  6. 3mm4

    NoREpls

    Desafio feito pelo dtm. Categoria: Easy! O desafio é conseguir usar a versão "completa" do software removendo tudo que te impede. Chall: http://www.mediafire.com/file/h789ga3v3gxv9vr/NoREpls.exe VirusTotal: https://www.virustotal.com/en/file/6adb6cd98c6da46ffe39ff8b5fe10634358cdc48c432369edd06f939be596bd2/analysis/1500375245/
  7. Caro leitor, você gosta de desafios? Neste artigo vou contar como resolvi um desafio de engenharia reversa do Shellterlabs, mas sem usar um disassembly! Para quem não é acostumado com o termo, de acordo com o grupo CTF-BR!, um CTF (Capture The Flag) nada mais é do que uma competição que envolve diversas áreas mas principalmente as áreas ligadas à segurança da informação. No Papo Binário também há um vídeo sobre o assunto. O desafio em questão é o Shellter Hacking Express Acidentalmente. Em sua descrição, há a seguinte frase: Acidentalmente codificamos a chave. Isso não diz muita coisa mas ao baixar o binário, percebemos que há dois arquivos: tar tf ~/Downloads/e74a74b5-86cf-4cb3-a5bb-18a36ef067cf.tgz RevEng400/ RevEng400/encoder RevEng400/key.enc Usando o comando file, verifiquei de que tipo são os arquivos extraídos: cd RevEng400/ $ file * encoder: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.2.5, stripped key.enc: data Ao ver que o encoder é um binário ELF, fui direto analisar seu código num disassembler usando objdump e gdb, mas percebi que o binário não continha os símbolos, o que torna sua análise um pouco mais difícil. Sendo iniciante em engenharia reversa e depois de horas analisando a função de cifragem, confesso que fiquei sem saber para onde ir (já viu algum apressadinho tentando aprender a tocar guitarra? Pois é, já quer ir lá tocar aquele solo, e na velocidade Steve Vai, aí não dá né? rs) e desisti, mas não por muito tempo (ei crianças, nunca desistam dos seus sonhos viu! rs), e procurei o nosso querido prof. @Fernando Mercês lá no servidor do Discord, que me deu umas dicas. Segue trecho da conversa: > @fernandom @gzn sei que vc ta treinando ER, mas nem precisa disassemblar esse binario pra esse desafio nego > se vc olhar bem, vai ver que a saída do encoder tem o tamanho da string de entrada + o byte 0x03 no final > olhando a chave encodada (key.enc), é razoável admitir que ela tenha 16 caracteres então > você só precisa encontrar qual deles é o 0xef .. um loop com bash mata > supondo que seja o 'A'... então 'A' -> 0xef, aí você vai precisar da letra que gera o 0xf9 e assim sucessivamente, até chegar em 16 Já ouviu a expressão "pensar fora da caixa"? Pois é! Por que eu fui direto disassemblar? Esse é um dos problemas quando nós estamos começando: às vezes a gente acha que o método mais difícil deve ser o único ou o melhor para se resolver problemas, mas nem sempre é assim. Daí pensei: se o Mercês falou que não é muito difícil, vamos ao menos tentar não é? Bem, a primeira coisa que fiz foi ver uma maneira de imprimir o conteúdo do binário em hexadecimal. Para isso criei um pequeno script que usa o hexdump para me dar uma saída somente com os bytes em hexadecimal do parâmetro que receber. Chamei o script de hexdump.sh e depois dei permissão de execução nele (chmod +x). Seu conteúdo é o seguinte: #!/bin/sh hexdump -v -e '/1 "%02X "' $1 Então comecei os testes: for letra in 0 A a; do echo -n "$letra "; ./encoder $letra | ./hexdump.sh; echo; done 0 7F 01 A F7 02 a F7 03 Parece que nem sempre o final é 0x03... Bem, fui verificar o conteúdo do arquivo key.enc e encontrei isso: ./hexdump.sh < key.enc EF F9 42 09 A3 1A 43 F7 8C 8B BB 22 2A C2 A3 14 03 Pela lógica, já que essa é a chave codificada, se eu passar a chave correta original em texto como parâmetro para o binário encode ele terá que gerar a sequência acima. Seguindo a dica do Mercês, usei o próprio bash para tentar quebrar o desafio, primeiro mostrando o conteúdo em hexadecimal da chave codificada, depois iterando pelos caracteres possíveis e filtrando pelo primeiro byte dela: hexdump.sh < key.enc; echo for ((i=32;i<=126;i++)); do > l=$(printf "\x$(printf "%x" $i)") > echo -n "$l " > ./encoder "$l" | ./hexdump.sh > echo done | grep 'EF' Este código basicamente faz: Mostra os bytes em hexadecimal da chave a cada vez que executarmos esse comando (só pra saber qual byte é o próximo). Itera por todos caracteres imprimíveis da tabela ASCII (faixa de 32 à 126 em decimal). Imprime o caractere na tela sem a quebra de linha. Passa essa letra para como argumento para o binário encode e imprime a saída dele em hexadecimal. Por fim, usa o grep para encontrar uma combinação que tenha o próximo byte da chave. Partindo para um exemplo prático, fui tentar encontrar a primeira letra dessa chave, sabendo que sua versão codificada deve resultar no byte 0xEF: ./hexdump.sh < key.enc; echo EF F9 42 09 A3 1A 43 F7 8C 8B BB 22 2A C2 A3 14 03 for ((i=32;i<=126;i++)); do > caractere=$(printf "\x$(printf "%x" $i)") > echo -n "$caractere ";./encoder "$l" | ./hexdump > echo done | grep 'EF' " EF 01 B EF 02 b EF 03 Conforme pode ver acima, encontrei três caracteres diferentes que, quando encodados pelo encoder, geram o byte 0xEF: ", B, e b. Escolhi seguir com o B, prefixando-o na chave para dar sequência ao script e ver se encontramos o caractere que resulta no próximo byte da chave codificada (0xF9): ./hexdump.sh < key.enc; echo EF F9 42 09 A3 1A 43 F7 8C 8B BB 22 2A C2 A3 14 03 for ((i=32;i<=126;i++)); do > caractere=$(printf "\x$(printf "%x" $i)") > echo -n "B${caractere} " > ./encoder "B$l" | ./hexdump > echo done | grep 'EF F9' % EF F9 01 E EF F9 02 e EF F9 03 Mais uma vez encontrei três opções. Foi só continuar este processo até encontrar a chave que gera os exatos 16 bytes do arquivo key.enc. Aproveitei e automatizei um brute forcer com Python: #!/usr/bin/env python3 # -*- coding: utf-8 -*- import subprocess def encode(arg): result = subprocess.run(['./encoder', arg], stdout=subprocess.PIPE) return result.stdout def loadKey(): key_enc = [] with open('./key.enc', 'rb') as file: while True: byte = file.read(1) if byte: # a ordem dos bytes aqui não importa (porque trata-se apenas de 1 byte), mas é necessário especificar key_enc.append(int.from_bytes(byte, byteorder='little')) else: break return key_enc def permutations(key='', key_enc=loadKey(), key_i=0): if key_i == len(key_enc) - 1: print(key) return for char in (chr(i) for i in range(32, 127)): result = encode(key + char) if result[key_i] == key_enc[key_i] and key_i < len(key_enc): permutations(key=key + char, key_enc=key_enc, key_i=key_i + 1) def main(): permutations() if __name__ == "__main__": main() Saída codificada em base64 (pra não estragar a brincadeira de quem vai tentar resolver o desafio por conta própria): QmV3aXRjaGluZyBTZXh0LwpCZXdpdGNoaW5nIFNleHRPCkJld2l0Y2hpbmcgU2V4dG8K Segue vídeo do canal com a explicação do algortimo de encoding desse desafio:
  8. Fernando Mercês

    Como são feitos os keygens

    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. Fernando Mercês

    Estude binários de Windows com o novo pev

    Desde 2013 que estamos trabalhando duro numa nova versão do pev, nosso toolkit para análise de binários PE (Portable Executable), o formato utilizado pelos executáveis (EXE, DLL, OCX, etc) do Windows. O pev é um projeto em que me sinto muito feliz de fazer parte e o principal motivo é que existe algo de muito forte e especial nele: colaboração. Quase 30 pessoas contribuíram com o projeto de alguma forma (código, testes, empacotamento, etc) e hoje ele está presente nos repositórios das principais distribuições Linux, inclusive as focadas em segurança de alguma forma. Outro ponto importante é que ele começou como um projeto de faculdade lá em 2010, junto com outros alunos. Como a faculdade nem laboratório de pesquisa tinha, fizemos o nosso próprio grupo, batizado de Coding 40°, em homenagem ao calor da nossa cidade. O projeto atraiu muitos colaboradores, incluindo um que já commitou mais que eu no repositório e redefiniu, de forma muito profissional, toda a infraestrutura por trás do pev e da libpe. Foi o Jardel Weyrich. Sério, dá pra detectar que um projeto teve sucesso quando aparece alguém que já contribuiu com mais código para ele do que você! Por último, mas não menos importante: o pev é livre. E na boa, até agradeço as centenas de programadores que fizeram analisadores de PE proprietários, porque me motivaram a iniciar o pev. Era preciso um analisador de binários PE de código aberto, que permitisse estudantes e curiosos entender como um PE é parseado, que campos são importantes, por que a análise estática é tão poderosa, etc. Hoje, mesmo com todos os bugs que ainda hão de surgir (fora os que já surgiram e ainda não conseguimos corrigir!). Quero agradecer, de forma permanente, a todos que se envolvem ou se envolveram de alguma forma com o pev: usando, avisando-nos sobre bugs, contribuindo com código, ideias, testes, tudo importa! Obrigado, de coração. Emoções à parte, vamos partir para a análise de binários e falar das novidades do pev 0.80, lançado hoje! Acontece que um binário PE pode esconder muitas informações importantes antes mesmo de ser executado. Por exemplo, com a ferramenta readpe, do pev, você vai descobrir que um executável PE é dividido em cabeçalhos e seções e eles armazenam várias informações interessantes, por exemplo: $ readpe arquivo.exe DOS Header Magic number: 0x5a4d (MZ) Bytes in last page: 144 Pages in file: 3 Relocations: 0 Size of header in paragraphs: 4 Minimum extra paragraphs: 0 Maximum extra paragraphs: 65535 Initial (relative) SS value: 0 Initial SP value: 0xb8 Initial IP value: 0 Initial (relative) CS value: 0 Address of relocation table: 0x40 Overlay number: 0 OEM identifier: 0 OEM information: 0 PE header offset: 0x80 COFF/File header Machine: 0x14c IMAGE_FILE_MACHINE_I386 Number of sections: 3 Date/time stamp: 1425562907 (Thu, 05 Mar 2015 13:41:47 UTC) Symbol Table offset: 0 Number of symbols: 0 Size of optional header: 0xe0 Characteristics: 0x102 Characteristics names IMAGE_FILE_EXECUTABLE_IMAGE IMAGE_FILE_32BIT_MACHINE Optional/Image header Magic number: 0x10b (PE32) Linker major version: 11 Linker minor version: 0 Size of .text section: 0x2a800 Size of .data section: 0x3400 Size of .bss section: 0 Entrypoint: 0x2c78e Address of .text section: 0x2000 Address of .data section: 0x2e000 ImageBase: 0x400000 Alignment of sections: 0x2000 Alignment factor: 0x200 Major version of required OS: 4 Minor version of required OS: 0 Major version of image: 0 Minor version of image: 0 Major version of subsystem: 4 Minor version of subsystem: 0 Size of image: 0x34000 Size of headers: 0x200 Checksum: 0 Subsystem required: 0x2 (IMAGE_SUBSYSTEM_WINDOWS_GUI) DLL characteristics: 0x8540 DLL characteristics names IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE IMAGE_DLLCHARACTERISTICS_NX_COMPAT IMAGE_DLLCHARACTERISTICS_NO_SEH IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE Size of stack to reserve: 0x100000 Size of stack to commit: 0x1000 Size of heap space to reserve: 0x100000 Size of heap space to commit: 0x1000 Data directories Directory IMAGE_DIRECTORY_ENTRY_IMPORT: 0x2c738 (83 bytes) Directory IMAGE_DIRECTORY_ENTRY_RESOURCE: 0x2e000 (12728 bytes) Directory IMAGE_DIRECTORY_ENTRY_BASERELOC: 0x32000 (12 bytes) Directory IMAGE_DIRECTORY_ENTRY_IAT: 0x2000 (8 bytes) Directory IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR: 0x2008 (72 bytes) Imported functions Library Name: mscoree.dll Functions Function Name: _CorExeMain export directory not found Sections Section Name: .text Virtual Address: 0x2000 Physical Address: 0x2a794 Size: 0x2a800 (174080 bytes) Pointer To Data: 0x200 Relocations: 0 Characteristics: 0x60000020 Characteristic Names IMAGE_SCN_CNT_CODE IMAGE_SCN_MEM_EXECUTE IMAGE_SCN_MEM_READ Section Name: .rsrc Virtual Address: 0x2e000 Physical Address: 0x31b8 Size: 0x3200 (12800 bytes) Pointer To Data: 0x2aa00 Relocations: 0 Characteristics: 0x40000040 Characteristic Names IMAGE_SCN_CNT_INITIALIZED_DATA IMAGE_SCN_MEM_READ Section Name: .reloc Virtual Address: 0x32000 Physical Address: 0xc Size: 0x200 (512 bytes) Pointer To Data: 0x2dc00 Relocations: 0 Characteristics: 0x42000040 Characteristic Names IMAGE_SCN_CNT_INITIALIZED_DATA IMAGE_SCN_MEM_DISCARDABLE IMAGE_SCN_MEM_READ Só olhando para este cabeçalho, já dá pra dizer muita coisa sobre esse binário. Por exemplo, é possível inferir que ele foi compilado em 5 de março de 2015, é um executável de janelas (GUI – Graphical User Interface) e na Import Table dele tem apenas uma entrada, uma função chamada _CorExeMain importada da biblioteca mscoree.dll. Isso já dá a dica que esse é binário foi compilado em .NET. Como eu sei disso? É que eu já vi vários binários em .NET e todos eles importam somente essa função. Não se preocupe, com o tempo você também pega os macetes! Claro que, para estudar os binários de forma eficiente é legal ler também outros tutoriais sobre o assunto. O que mais gosto em Português é O formato PE, da Vovó Vicky, isso mesmo, uma simpática senhora, médica, do Sul do país que adora arquivos PE. ❤️ Voltando ao nosso binário, vamos dar uma olhada nas strings presentes nele com o pestr: $ pestr -so arquivo.exe 0x2d81e .rsrc FileDescription 0x2d840 .rsrc Gynx 0x2d852 .rsrc FileVersion 0x2d86c .rsrc 1.0.0.0 0x2d882 .rsrc InternalName 0x2d89c .rsrc Gynx.exe 0x2d8b6 .rsrc LegalCopyright 0x2d8d4 .rsrc Copyright 0x2d8ea .rsrc 2015 0x2d8fe .rsrc OriginalFilename A lista de strings completa é bem maior, mas eu cortei só para mostrar algumas. Primeira pergunta que pode surgir aqui é: por que fizemos um programa pestr se já existe o programa strings? Bem, você pode notar pela saída que dizemos muito mais que a string (texto) em si. Sabemos em que seção ela está e o offset (posição) no arquivo. Além disso, o pestr imprime strings ASCII e Unicode. No meio das strings tem outra informação de data, que reforça o ano da data de compilação. Tem também o nome original do arquivo (quando o programador compilou). Se você pesquisar no Google pelo nome do arquivo, já vai encontrar muita coisa! Continuando nossa análise, vamos ver com o pescan se tem algo estranho neste arquivo: $ pescan -v ~/dotnet file entropy: 5.252392 (normal) fpu anti-disassembly: no imagebase: normal - 0x400000 entrypoint: normal - va: 0x2c78e - raw: 0x2a98e DOS stub: normal TLS directory: not found timestamp: normal - Thu, 05 Mar 2015 13:41:47 UTC section count: 3 sections section .text: normal section .rsrc: normal section .reloc: small length A baixa entropia sugere que o binário não está comprimido (packed) e tudo parece estar normal. Isso significa que o binário não está protegido, não que seu código não seja malicioso. Agora, já entrando nos novos recursos, que tal saber os hashes deste arquivo? Buscando por eles no Google também é possível encontrar algumas informações. Ao invés de calcular um por um, utilizando vários programas, o pev conta com o pehash, que faz tudo pra você: $ pehash -a arquivo.exe file filepath: /home/user/arquivo.exe md5: 1f4c40ff46297bdc5a595cd574e0db64 sha1: 2bc6fa6558988d628dd4b95d0741405685c1c232 sha256: b823a04f49463e410c9f823baade182eb283ba073b40c6d8cc443a570a9a1df6 ssdeep: 3072:yLYmWbfXGKJy7DFR9KwHS+MASjB5jL0S9Q0VwuUcu:AYmWbfX3y7DFR9KwHS+MAS/js0VhU imphash: f34d5f2d4577ed6d9ceec516c1f5a744 headers header header_name: IMAGE_DOS_HEADER md5: 919f1c12cf0d1cd93d7f1077f13ac374 sha1: 3d43712e5606b4640b85f5f0e25e9db8ed552074 sha256: c46a3fc444808f3b86a7e757e5202d16f8ea9bf1c6aff2cabc593e7d0f2c9ad2 ssdeep: 3:WlWUqt/vllnln:idqP header header_name: IMAGE_COFF_HEADER md5: 15174f39bbe557d104f169053af5c7a2 sha1: 21f9311fa5e1a7c8724c657db39ef2fbb1b896ce sha256: 7430166bd1b2d5c400f2f5fb60c90393b50388b97e877aa7cf0c3057a413a85f ssdeep: 3:HHJl/fkn:HHJlkn header header_name: IMAGE_OPTIONAL_HEADER md5: 3b079fa2a88b6901db907bc47dee2d67 sha1: 21c1c517743170c94d1ade608ff7d2746fd7e3ea sha256: 0fec7657e9361e8262af5872bf3784a7647b6978f3d0e35a419d2f410c8654a0 ssdeep: 3:6FZ//llAlFlllXNB//llrllel/dglPt1l9tllH:pfGwlN sections section section_name: .text md5: 973d11759194c14071aa6963de8f55c7 sha1: 1934e0085c8776e3243bf658e95b8943d4f91bc9 sha256: e68349bfcb04b20c11973ce27770570ebb22c8c7750133d073f15f7ec9eeda38 ssdeep: 3072:XLYmWbfXGKJy7DFR9KwHS+MASjB5jL0S9Q0VwuUc:bYmWbfX3y7DFR9KwHS+MAS/js0VhU section section_name: .rsrc md5: 9d498bee08b1cf075d8aca022f1e16e9 sha1: f938a8c23185b023bb5dd12082013c2a628bc0d3 sha256: eb7454309df90fea2d01c887b0040e8237353919efc06a7081a7abb15091283a ssdeep: 24:3A5t/ug9GXBEiViKZWIDDOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO:weg90VWIDZ6Kp+l4BhDqFWSfbNtm section section_name: .reloc md5: 37ab911460a0b274ffa9f4e388ec933b sha1: 7c9eac3b843c9a7ab8ef08aac5c14e4c51b2c703 sha256: fb50df3b064d126999295b35f1a151f8fbe7e3593e306a9255a534999843f5b2 ssdeep: 3:LlQl:S Achou que teria os hashes do arquivo somente? Pois é, o pev surpreende mesmo! O pehash calcula MD5, SHA1, SHA-256 e ssdeep (uma técnica de fuzzy hash que vale a pena pesquisar sobre!) para cada cabeçalho e seção. Além disso, a partir das funções importadas do binário o pehash calcula o imphash, que já expliquei no artigo Entendendo o imphash. Tanto o imphash quanto o ssdeep podem ser utilizados, de maneiras diferentes, para saber se um arquivo é parecido com outro, se são variantes do mesmo código-fonte, mas neste caso o imphash não ajuda por ser um binário .NET (todos eles importam aquela mesma função única que comentei acima). Também é possível, com o peres (criação do Marcelo Fleury), extrair os recursos e checar, por exemplo, o manifesto em XML dos arquivo: $ peres -x arquivo.exe Save On: resources/icons/2.ico Save On: resources/icons/3.ico Save On: resources/icons/4.ico Save On: resources/icons/5.ico Save On: resources/icons/6.ico Save On: resources/icons/7.ico Save On: resources/icons/8.ico Save On: resources/groupicons/32512.ico Save On: resources/versions/1.rc Save On: resources/manifests/1.xml $ cat resources/manifests/1.xml <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> <security> <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3"> <requestedExecutionLevel level="asInvoker" uiAccess="false"/> </requestedPrivileges> </security> </trustInfo> </assembly> O ícone utilizado também é extraído (groupicons) mas ele é quebrado em várias imagens (icons) e precisam ser reconstruídos se você precisar do ícone original do arquivo. De qualquer forma, você pode calcular o hash deles e comparar com os dos ícones de outros arquivos, se for essa a intenção. Uma outra novidade é o programa cpload, para analisar arquivos .cpl (Control Panel Applets). Ele é presente somente na versão do pev para Windows, pois serve para analisar binários dinamicamente. Este formato é um pouco diferente do EXE convencional (na verdade um CPL é uma “DLL que executa com dois cliques”). Na época o formato desses arquivos era muito pouco conhecido e até escrevi um artigo para a Wikipédia em Português sobre. Com o cpload, é possível enviar para a função presente no arquivo .cpl todas as mensagens que ela deve reconhecer. Assim o analista pode ver qual delas dispara o código. No exemplo abaixo, a mensagem CPL_DBLCLK (clique duplo) foi enviada para um .cpl presente no Windows chamado joy.cpl. Veja que o programa executa e mostra uma janela: Adicionalmente, se você fizer isso no contexto de um debugger, passando os argumentos como na imagem abaixo: O cpload força um software breakpoint antes do carregamento do arquivo .cpl pela função LoadLibrary() e antes da entrada na CPLApplet(), que é a função principal de um arquivo .cpl, o que permite ao analista enxergar o trecho exato de código dele, sem precisar debugar processos do sistema como o rundll32.exe. E ainda sobre o pev, os plugins de formato de saída estão dando um show (ideia do Jan Seidl e portada para plugins pelo Jardel). Olha só como fica a saída se você pedir ao pedis, nosso disassembler, para disassemblar o entrypoint de um binário PE em JSON: $ pedis --format json --entrypoint PUTTY.EXE { "54eb0": "6a 60 push 0x60", "54eb2": "68 70 7b 47 00 push 0x477b70", "54eb7": "e8 08 21 00 00 call 0x456fc4", "54ebc": "bf 94 00 00 00 mov edi, 0x94", "54ec1": "8b c7 mov eax, edi", "54ec3": "e8 b8 fa ff ff call 0x454980", "54ec8": "89 65 e8 mov [ebp-0x18], esp", "54ecb": "8b f4 mov esi, esp", "54ecd": "89 3e mov [esi], edi", "54ecf": "56 push esi", "54ed0": "ff 15 e0 d2 45 00 call dword [0x45d2e0]", "54ed6": "8b 4e 10 mov ecx, [esi+0x10]", "54ed9": "89 0d 48 e1 47 00 mov [0x47e148], ecx", "54edf": "8b 46 04 mov eax, [esi+0x4]", "54ee2": "a3 54 e1 47 00 mov [0x47e154], eax", "54ee7": "8b 56 08 mov edx, [esi+0x8]", "54eea": "89 15 58 e1 47 00 mov [0x47e158], edx", "54ef0": "8b 76 0c mov esi, [esi+0xc]", "54ef3": "81 e6 ff 7f 00 00 and esi, 0x7fff", "54ef9": "89 35 4c e1 47 00 mov [0x47e14c], esi", "54eff": "83 f9 02 cmp ecx, 0x2", "54f02": "74 0c jz 0x454f10", "54f04": "81 ce 00 80 00 00 or esi, 0x8000", "54f0a": "89 35 4c e1 47 00 mov [0x47e14c], esi", "54f10": "c1 e0 08 shl eax, 0x8", "54f13": "03 c2 add eax, edx", "54f15": "a3 50 e1 47 00 mov [0x47e150], eax", "54f1a": "33 f6 xor esi, esi", "54f1c": "56 push esi", "54f1d": "8b 3d d8 d2 45 00 mov edi, [0x45d2d8]", "54f23": "ff d7 call edi0x454f25", "54f25": "66 81 38 4d 5a cmp word [eax], 0x5a4d", "54f2a": "75 1f jnz 0x454f4b", "54f2c": "8b 48 3c mov ecx, [eax+0x3c]", "54f2f": "03 c8 add ecx, eax", "54f31": "81 39 50 45 00 00 cmp dword [ecx], 0x4550", "54f37": "75 12 jnz 0x454f4b", "54f39": "0f b7 41 18 movzx eax, word [ecx+0x18]", "54f3d": "3d 0b 01 00 00 cmp eax, 0x10b", "54f42": "74 1f jz 0x454f63", "54f44": "3d 0b 02 00 00 cmp eax, 0x20b", "54f49": "74 05 jz 0x454f50", "54f4b": "89 75 e4 mov [ebp-0x1c], esi", "54f4e": "eb 27 jmp 0x454f77", "54f50": "83 b9 84 00 00 00 0e cmp dword [ecx+0x84], 0xe", "54f57": "76 f2 jbe 0x45504b", "54f59": "33 c0 xor eax, eax", "54f5b": "39 b1 f8 00 00 00 cmp [ecx+0xf8], esi", "54f61": "eb 0e jmp 0x454f71", "54f63": "83 79 74 0e cmp dword [ecx+0x74], 0xe", "54f67": "76 e2 jbe 0x45504b", "54f69": "33 c0 xor eax, eax", "54f6b": "39 b1 e8 00 00 00 cmp [ecx+0xe8], esi", "54f71": "0f 95 c0 setnz al", "54f74": "89 45 e4 mov [ebp-0x1c], eax", "54f77": "56 push esi", "54f78": "e8 b4 28 00 00 call 0x457831", "54f7d": "59 pop ecx", "54f7e": "85 c0 test eax, eax", "54f80": "75 21 jnz 0x454fa3", "54f82": "83 3d 94 e1 47 00 01 cmp dword [0x47e194], 0x1", "54f89": "75 05 jnz 0x454f90", "54f8b": "e8 56 44 00 00 call 0x4593e6", "54f90": "6a 1c push 0x1c", "54f92": "e8 d8 42 00 00 call 0x45926f", "54f97": "68 ff 00 00 00 push 0xff", "54f9c": "e8 be fb ff ff call 0x454b5f", "54fa1": "59 pop ecx", "54fa2": "59 pop ecx", "54fa3": "e8 65 47 00 00 call 0x45970d", "54fa8": "89 75 fc mov [ebp-0x4], esi", "54fab": "e8 e9 25 00 00 call 0x457599", "54fb0": "85 c0 test eax, eax", "54fb2": "7d 08 jge 0x454fbc", "54fb4": "6a 1b push 0x1b", "54fb6": "e8 d0 fe ff ff call 0x454e8b", "54fbb": "59 pop ecx", "54fbc": "ff 15 a8 d1 45 00 call dword [0x45d1a8]", "54fc2": "a3 00 e9 47 00 mov [0x47e900], eax", "54fc7": "e8 af 4d 00 00 call 0x459d7b", "54fcc": "a3 8c e1 47 00 mov [0x47e18c], eax", "54fd1": "e8 03 4d 00 00 call 0x459cd9", "54fd6": "85 c0 test eax, eax", "54fd8": "7d 08 jge 0x454fe2", "54fda": "6a 08 push 0x8", "54fdc": "e8 aa fe ff ff call 0x454e8b", "54fe1": "59 pop ecx", "54fe2": "e8 bf 4a 00 00 call 0x459aa6", "54fe7": "85 c0 test eax, eax", "54fe9": "7d 08 jge 0x454ff3", "54feb": "6a 09 push 0x9", "54fed": "e8 99 fe ff ff call 0x454e8b", "54ff2": "59 pop ecx", "54ff3": "6a 01 push 0x1", "54ff5": "e8 95 fb ff ff call 0x454b8f", "54ffa": "59 pop ecx", "54ffb": "89 45 d8 mov [ebp-0x28], eax", "54ffe": "3b c6 cmp eax, esi", "55000": "74 07 jz 0x455009", "55002": "50 push eax", "55003": "e8 83 fe ff ff call 0x454e8b", "55008": "59 pop ecx", "55009": "89 75 bc mov [ebp-0x44], esi", "5500c": "8d 45 90 lea eax, [ebp-0x70]", "5500f": "50 push eax", "55010": "ff 15 ac d1 45 00 call dword [0x45d1ac]", "55016": "e8 2e 4a 00 00 call 0x459a49", "5501b": "89 45 e0 mov [ebp-0x20], eax", "5501e": "f6 45 bc 01 test byte [ebp-0x44], 0x1", "55022": "74 06 jz 0x45502a", "55024": "0f b7 45 c0 movzx eax, word [ebp-0x40]", "55028": "eb 03 jmp 0x45502d", "5502a": "6a 0a push 0xa", "5502c": "58 pop eax", "5502d": "50 push eax", "5502e": "ff 75 e0 push dword [ebp-0x20]", "55031": "56 push esi", "55032": "56 push esi", "55033": "ff d7 call edi0x455035", "55035": "50 push eax", "55036": "e8 93 3b ff ff call 0x448bce", "5503b": "8b f8 mov edi, eax", "5503d": "89 7d d4 mov [ebp-0x2c], edi", "55040": "39 75 e4 cmp [ebp-0x1c], esi", "55043": "75 06 jnz 0x45504b", "55045": "57 push edi", "55046": "e8 6f fc ff ff call 0x454cba", "5504b": "e8 8c fc ff ff call 0x454cdc", "55050": "eb 2b jmp 0x45507d", "55052": "8b 45 ec mov eax, [ebp-0x14]", "55055": "8b 08 mov ecx, [eax]", "55057": "8b 09 mov ecx, [ecx]", "55059": "89 4d dc mov [ebp-0x24], ecx", "5505c": "50 push eax", "5505d": "51 push ecx", "5505e": "e8 75 48 00 00 call 0x4598d8", "55063": "59 pop ecx", "55064": "59 pop ecx", "55065": "c3 ret" Os formatos de saída suportados por todos os programas do toolkit incluem JSON, XML, CSV e o padrão, texto. Assim o pev se faz útil para quem quer automatizar o processo de análise estática de binários PE, para salvar em um banco de dados para consulta posterior, por exemplo. A documentação para a versão 0.80 está pronta e recomendo fortemente que você a leia se quiser aproveitar o máximo do toolkit. O changelog completo também está disponível. Despeço-me com a certeza de que fizemos um bom trabalho e de que o software livre é viável, funciona e é, principalmente, algo prazeroso de se manter, que pode ajudar pessoas e empresas a atingirem seus objetivos, sem custo. Confere lá: https://github.com/merces/pev
  10. Fernando Mercês

    Entendendo o imphash

    No início de 2014, a Mandiant publicou que estava calculando o hash MD5 das funções importadas por binários PE para buscar variantes de malware [1]. Eles também fizeram um patch na biblioteca pefile [2] para suportar o novo cálculo. A ideia colou e até o Virus Total passou a utilizar [3]. Eu mesmo utilizei um tempo sem entender direito até que um dia decidi estudá-lo para implementar no pev [4] (que ainda não fiz) e hoje decidi escrever sobre. Todo binário PE que se preze usa funções de bibliotecas. Assim sendo, desde os primórdios da especificação PE, há o que é conhecido por IT – Import Table, que é uma tabela contendo uma lista de cada módulo (DLL) que o binário utiliza (importa) com suas respectivas funções. Por exemplo, vamos analisar o programa no estilo “Hello, world” abaixo: #include <stdio.h> int main(void) { puts("Quem avisa chato eh!"); return 0; } Ao compilar usando o Dev-Cpp [5] no Windows 7, podemos checar quais funções são importadas por ele: C:\> readpe --imports hello.exe Imported functions Library Name: KERNEL32.dll Functions Function Name: DeleteCriticalSection Function Name: EnterCriticalSection Function Name: GetCurrentProcess Function Name: GetCurrentProcessId Function Name: GetCurrentThreadId Function Name: GetLastError Function Name: GetStartupInfoA Function Name: GetSystemTimeAsFileTime Function Name: GetTickCount Function Name: InitializeCriticalSection Function Name: LeaveCriticalSection Function Name: QueryPerformanceCounter Function Name: RtlAddFunctionTable Function Name: RtlCaptureContext Function Name: RtlLookupFunctionEntry Function Name: RtlVirtualUnwind Function Name: SetUnhandledExceptionFilter Function Name: Sleep Function Name: TerminateProcess Function Name: TlsGetValue Function Name: UnhandledExceptionFilter Function Name: VirtualProtect Function Name: VirtualQuery Library Name: msvcrt.dll Functions Function Name: __C_specific_handler Function Name: __dllonexit Function Name: __getmainargs Function Name: __initenv Function Name: __iob_func Function Name: __lconv_init Function Name: __set_app_type Function Name: __setusermatherr Function Name: _acmdln Function Name: _amsg_exit Function Name: _cexit Function Name: _fmode Function Name: _initterm Function Name: _lock Function Name: _onexit Function Name: _unlock Function Name: abort Function Name: calloc Function Name: exit Function Name: fprintf Function Name: free Function Name: fwrite Function Name: malloc Function Name: memcpy Function Name: puts Function Name: signal Function Name: strlen Function Name: strncmp Function Name: vfprintf Pois é, apesar de eu ter chamado apenas a função puts(), o binário precisa de muito mais para funcionar. Agora um segundo exemplo, com um recurso a mais, mas sem chamar nenhuma outra função da mesma biblioteca: #include <stdio.h> char *superfuncaonova(int n) { if (n >= 666) return "arre, porra!"; else return "eu quero eh rock, diabo!"; } int main(void) { puts("Quem avisa chato eh!"); puts(superfuncaonova(1)); return 0; } Após compilar e listar os imports deste hello2.exe, você vai confirmar que é exatamente a mesma lista do primeiro hello.exe. Isto porque, apesar de ter uma função interna nova, não utiliza outra função de biblioteca nova, o que não altera a IAT. Isso é comum em variantes de uma mesma família de malware já que as funcionalidades estão prontas e o que difere de uma variante para outra normalmente são informações como servidor de comando e controle, algumas strings, etc. Sendo assim, é inteligente utilizar essa lista para buscar famílias. Parabéns para quem pensou nisso. E como o imphash foi implementado? Comparar cada item da lista não seria prático então alguém teve a ideia de tirar o hash MD5 da lista, obedecendo o seguinte padrão: Varrendo a import table (IT), na ordem em que aparecem os imports: Prefixar cada função com o nome do módulo sem extensão (mas mantendo o ponto) da qual ela é importada. Por exemplo, se o binário importa DeleteCriticalSection() da KERNEL32.DLL, esse import reescrito ficaria KERNEL32.EnterCriticalSection. Caso não haja nome da função, tentar resolver e, caso não consiga, utilizar seu número ordinal. Converter tudo para minúsculo. Criar uma string com os imports no padrão acima, separados por vírgula. Calcular o hash MD5 da string acima. Eu salvei a saída do readpe para um arquivo de texto e, usando expressões regulares, o editei até que ficasse no padrão definido acima: $ cat hello.imports kernel32.deletecriticalsection,kernel32.entercriticalsection,kernel32.getcurrentprocess,kernel32.getcurrentprocessid,kernel32.getcurrentthreadid,kernel32.getlasterror,kernel32.getstartupinfoa,kernel32.getsystemtimeasfiletime,kernel32.gettickcount,kernel32.initializecriticalsection,kernel32.leavecriticalsection,kernel32.queryperformancecounter,kernel32.rtladdfunctiontable,kernel32.rtlcapturecontext,kernel32.rtllookupfunctionentry,kernel32.rtlvirtualunwind,kernel32.setunhandledexceptionfilter,kernel32.sleep,kernel32.terminateprocess,kernel32.tlsgetvalue,kernel32.unhandledexceptionfilter,kernel32.virtualprotect,kernel32.virtualquery,msvcrt.__c_specific_handler,msvcrt.__dllonexit,msvcrt.__getmainargs,msvcrt.__initenv,msvcrt.__iob_func,msvcrt.__lconv_init,msvcrt.__set_app_type,msvcrt.__setusermatherr,msvcrt._acmdln,msvcrt._amsg_exit,msvcrt._cexit,msvcrt._fmode,msvcrt._initterm,msvcrt._lock,msvcrt._onexit,msvcrt._unlock,msvcrt.abort,msvcrt.calloc,msvcrt.exit,msvcrt.fprintf,msvcrt.free,msvcrt.fwrite,msvcrt.malloc,msvcrt.memcpy,msvcrt.puts, msvcrt.signal,msvcrt.strlen,msvcrt.strncmp,msvcrt.vfprintf NOTA: O arquivo tem somente uma linha, sendo que não há um caractere de nova linha (\n ) no final dela, veja: $ hdump hello.imports | tail 000003c0 74 2e 65 78 69 74 2c 6d 73 76 63 72 74 2e 66 70 |t.exit,msvcrt.fp| 000003d0 72 69 6e 74 66 2c 6d 73 76 63 72 74 2e 66 72 65 |rintf,msvcrt.fre| 000003e0 65 2c 6d 73 76 63 72 74 2e 66 77 72 69 74 65 2c |e,msvcrt.fwrite,| 000003f0 6d 73 76 63 72 74 2e 6d 61 6c 6c 6f 63 2c 6d 73 |msvcrt.malloc,ms| 00000400 76 63 72 74 2e 6d 65 6d 63 70 79 2c 6d 73 76 63 |vcrt.memcpy,msvc| 00000410 72 74 2e 70 75 74 73 2c 6d 73 76 63 72 74 2e 73 |rt.puts,msvcrt.s| 00000420 69 67 6e 61 6c 2c 6d 73 76 63 72 74 2e 73 74 72 |ignal,msvcrt.str| 00000430 6c 65 6e 2c 6d 73 76 63 72 74 2e 73 74 72 6e 63 |len,msvcrt.strnc| 00000440 6d 70 2c 6d 73 76 63 72 74 2e 76 66 70 72 69 6e |mp,msvcrt.vfprin| 00000450 74 66 |tf| O imphash deste arquivo então deve ser o hash MD5 do conteúdo de hello.imports. Vamos ver? $ python imphash.py hello.exe # usando a pefile 3856e6eb1020e4f12c9b8f75c966a09c $ md5 hello.imports MD5 (hello.imports) = 3856e6eb1020e4f12c9b8f75c966a09c Legal! Agora que sabemos como calcular o imphash de binários PE, podemos implementar em qualquer software, certo? Mais ou menos. Você percebeu que ignoramos o passo 2? Pois é, no próximo artigo sobre o assunto vou explicar o motivo pelo qual o imphash está quebrado. Até lá. o/ Referências [1] https://www.fireeye.com/blog/threat-research/2014/01/tracking-malware-import-hashing.html [2] https://github.com/erocarrera/pefile [3] http://blog.virustotal.com/2014/02/virustotal-imphash.html [4] https://github.com/merces/pev [5] https://sourceforge.net/projects/orwelldevcpp/
  11. No últmo artigo falei sobre como montar uma máquina virtual básica para ER em Windows. Agora chegou a vez do Linux, já que o mundo não é feito só de PE. A ideia aqui é montar um ambiente legal para reverter binários ELF. Dá uma olhada neste vídeo (em tela cheia de preferência) pra ter uma ideia do poder dessas ferramentas. Legal né? Então mãos à obra! Máquina virtual 512 MB de memória Placa de rede em modo NAT Placa de rede em modo host-only Sistema operacional Utilizo a ISO netinstall do Debian 7 64-bits mas como sempre você é livre para utilizar a distribuição que quiser, só que este artigo é escrito com base nesta. Algumas dicas de instalação: Não utilizar seu nome verdadeiro na criação da conta de usuário. Não instalar as extensões do software virtualizador como VMware Tools ou VirtualBox Guest Additions. Não configurar nenhuma regra de firewall ou qualquer software de proteção. /etc/apt/sources.list Eu deixo do seguinte jeito: deb http://ftp.br.debian.org/debian/ jessie main contrib non-free deb http://security.debian.org/ jessie/updates main deb http://ftp.br.debian.org/debian/ jessie-updates main deb http://ftp.br.debian.org/debian/ jessie-backports main O importante aqui é ter o “contrib non-free” após o main, infelizmente, pois alguns programas como o rar não são livres. Depois de configurado este arquivo, é bom atualizar os pacotes existentes: # apt-get update # apt-get upgrade -y Pacotes básicos # apt-get install zip unzip rar unrar p7zip file sudo git vim rcconf openssh-server Git para clonar repositórios de outros software que precisamos, servidor ssh para acessar a máquina de forma fácil, descompactadores etc. Software específico wxHexEditor: Este é o melhor editor hexa que tem em minha opinião. Edita dispositivos e até processos em memória. Para compilá-lo, primeiro você vai ter que instalar suas dependências: # apt-get install autoconf libtool libwxgtk2.8-dev gettext Depois é só baixar o tarball, descomprimir, entrar no diretório e comandar os famosos: $ ./configure $ make # make install Bokken: IDE muito boa para o pyew e radare2 (especialmente este útlimo). Com o Bokken é possível gerar inclusive um diagrama de fluxo das funções utilizadas pelo ELF. Na prática, ele server como disassembler, não como debugger. Ele é escrito em Python. Vamos à instalação das dependências então: # apt-get install python-radare2 pyew python-gtk2 python-gtksourceview2 graphviz python-rsvg python-utidylib Depois baixe o tarball (não use o pacote .deb disponível, ele está desatualizado com relação às dependências), descompacte e mova o diretório bokken-1.6 para um de sua prefência (/opt em nosso caso). Aí basta editar a varíavel BOKKEN_DIR no script /opt/bokken-1.6/bokken para refletir o novo diretório do programa (BOKKEN_DIR=/opt/bokken-1.6) e criar um link simbólico para este script em /usr/local/bin, para que este possa ser chamado a partir de qualquer diretório: # ln -s /opt/bokken-1.6/bokken /usr/local/bin/bokken Evan’s Debugger: Este projeto é simplesmente fenomenal. A ideia do Evan foi criar um debugger livre com base no OllyDbg. Eu apresentei este projeto na conferência H2HC de 2010 (fiz algumas contribuições) e e de lá pra cá o edb, como é chamado seu binário principal, só melhora. Dependências: # apt-get install libqt4-dev libboost-dev xterm yasm Após baixar a última versão do debugger e descomprimir, para compilar e instalar: $ qmake $ make # make install Agora em ~/.config/codef00.com/edb.conf você precisa configurar os diretórios utilizados pelo edb: [Directories] directory.symbol.path=/home/<usuario>/.config/codef00.com/symbols directory.plugin.path=/lib64/edb directory.session.path=/tmp E criar o diretório de symbols: $ mkdir -p ~/.config/codef00.com/symbols bashacks: Valeria a pena só pelas funções asciitable e utf8table. Brincadeiras à parte, tem várias funções úteis para ER, como a hexcalc: $ hexcalc 4f + 2e 0x7d Para instalar, é clonar o repositório com o git, mover para um diretório /opt por exemplo e adicionar no ~/.bashrc: $ git clone https://github.com/merces/bashacks.git # mv bashacks /opt $ echo 'source /opt/bashacks/bashacks.sh' >> ~/.bashrc Pronto. Ao relogar todas as funções estarão disponíveis. Se quiser saber um pouco mais sobre basta assistir ao nosso vídeo sobre esta ferramenta. Utilizo ainda os seguintes pacotes (apt-get install neles): ht: instala o programa hte, um poderoso editor e disassembler de binários que utiliza a ncurses. Já me salvou onde não tinha X. strace: rastreia chamadas de sistema feitas por binários ltrace: rastreia chamadas de bibliotecas feitas por binários tcpdump: captura de tráfego de rede foremost: ajuda nos casos em que há binários dentro de binários unhide: mostra processos e conexões ocultas E há ainda os nativos do Linux que também são úteis: readelf: mostra informações sobre binários ELF como symbols, cabeçalhos, seções etc objdump: tipo o anterior, mas suporta outros formatos. Disassembla também. Um comando legal com ele é: $ objdump -M intel -d <arquivo> Observações Após instalar todos os softwares, é de extrema utilidade criar um snapshot da máquina virtual, pra você voltar facilmente a este estado limpo após analisar um arquivo suspeito, por exemplo. Não precisa instalar um ambiente gráfico. Pode conectar na máquina com ssh -X e rodar os programas gráficos. Existe uma distribuição Linux chamada REMnux com foco em engenharia reversa. Eu sugeri ao autor que incluisse as ferramentas que menciono aqui.
  12. 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.
  13. Fernando Mercês

    Alterando o comportamento de um executável

    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. Fernando Mercês

    Forçando um game a rodar no Vista

    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.
×