Jump to content
  • Disassemblando x86 em Go

       (3 reviews)

    Comecei a estudar a linguagem Go há alguns dias e fiquei muito impressionado com seus recursos. A facilidade para programação paralela, o fato de ter ponteiros, funções que retornam mais de um valor, código enxuto (se você declarar uma variável e não usar, o programa nem compila!) e outros realmente me encantaram.

    Recentemente precisei disassemblar um trecho de código de um binário PE para um projeto que está escrito em Go. Vi que existem algumas bibliotecas prontas para serem usadas, como gapstone (bindings da Capstone) e go-zydis (bindings da Zydis) mas não encontrei uma nativa.

    No entanto, vi que existe uma ferramenta nativa no toolset da linguagem similar ao objdump do GNU binutils:

    $ go doc cmd/objdump
    Objdump disassembles executable files.
    
    Usage:
    
        go tool objdump [-s symregexp] binary
    
    Objdump prints a disassembly of all text symbols (code) in the binary. If
    the -s option is present, objdump only disassembles symbols with names
    matching the regular expression.

    Compilei um "hello, world" em Go só pra ver:

    ~/hello $ cat main.go
    package main
    
    import "fmt"
    
    func main() {
    	fmt.Println("menteb.in")
    }
    
    ~/hello $ go build

    E de fato o objdump da Go funciona:

    ~/hello $ go tool objdump hello | head
    TEXT go.buildid(SB)
      :-134217728		0x1001000		ff20			JMP 0(AX)
      :-134217728		0x1001002		476f			OUTSD DS:0(SI), DX
      :-134217728		0x1001004		206275			ANDB AH, 0x75(DX)
      :-134217728		0x1001007		696c642049443a20	IMULL $0x203a4449, 0x20(SP), BP
      :-1			0x100100f		226d35			ANDB 0x35(BP), CH
      :-1			0x1001012		4c6f			OUTSD DS:0(SI), DX
      :-1			0x1001014		6a52			PUSHL $0x52
      :-1			0x1001016		436e			OUTSB DS:0(SI), DX
      :-1			0x1001018		4a31794f		XORQ DI, 0x4f(CX)

    Mas ao tentar com o um PE compilado pra 64-bits, descobri que só funciona com binários feito em Go. ?

    $ go tool objdump putty.exe
    objdump: disassemble putty.exe: no runtime.pclntab symbol found

    De qualquer forma, resolvi olhar o código-fonte deste objdump interno da linguagem pra ver qual é dessa mandinga.  Na linha 43 do main.go do objdump tem um import pra uma biblioteca chamada objfile. Pensei: Wow, deve ser uma biblioteca de disassembly, talvez eu possa alterar ! E na hora já criei um projeto tentando usá-la mas fui surpreendido com um errão! kkkk

    ~hello $ cat main.go
    package main
    
    import "fmt"
    import "cmd/internal/objfile"
    
    func main() {
    	fmt.Println("menteb.in")
    }
    
    ~hello $ go build
    main.go:4:8: use of internal package cmd/internal/objfile not allowed

    Não pesquisei muito sobre essa história sobre eu não poder usar um pacote interno (por quê o objdump pode e eu não posso?!), mas fui olhar esta objfile e terminei encontrando seu fonte. Para minha alegria, neste arquivos disasm.go vi os seguintes imports:

    "golang.org/x/arch/arm/armasm"
    "golang.org/x/arch/arm64/arm64asm"
    "golang.org/x/arch/ppc64/ppc64asm"
    "golang.org/x/arch/x86/x86asm"

    Agora sim, carái! É tudo público e posso usar. Desculpe o desabafo.. hehe o artigo na verdade começa aqui mas quis contar como cheguei porque né. ?

    Cada uma dessas bibliotecas possui uma função Decode() justamente pra decodificar uma instrução (tipo Inst). Testei com um NOP em 64-bits, só pra ver:

    package main
    
    import (
    	"fmt"
    	"log"
    
    	"golang.org/x/arch/x86/x86asm"
    )
    
    func main() {
    	dados := []byte{0x90}
    
    	ins, err := x86asm.Decode(dados, 64)
    
    	if err != nil {
    		log.Fatalln(err)
    	}
    
    	fmt.Println(ins)
    }

    A saída foi exatamente a esperada:

    $ ./hello
    NOP

    Show. Agora é abrir um PE, ler de onde quero e daí disassemblar usado essa x86asm.Decode() num loop, mas vou deixar esse exercício aí pra quem quiser treinar Go. Ou se acharem útil posso postar um aqui mais tarde. Aqui já funcionou mas precisa de uma polida. ?

    Perceba também que há bibliotecas para ARM e PowerPC. Achei bem maneiro. Talvez em breve o time da Go adicione suporte a mais arquiteturas. Amém! ? 

    Edited by Fernando Mercês


    User Feedback

    Join the conversation

    You can post now and register later. If you have an account, sign in now to post with your account.
    Note: Your post will require moderator approval before it will be visible.

    Guest

    • This will not be shown to other users.
    • Add a review...

      ×   Pasted as rich text.   Paste as plain text instead

        Only 75 emoji are allowed.

      ×   Your link has been automatically embedded.   Display as a link instead

      ×   Your previous content has been restored.   Clear editor

      ×   You cannot paste images directly. Upload or insert images from URL.



  • Similar Content

    • By Julliana Bauer
      A presença de Security Champions nas equipes de desenvolvimento pode trazer uma visão mais estruturada acerca da segurança de aplicações. Além disso, ele pode ser um grande influenciador da cultura de segurança dentro da empresa.
      Mas você sabe qual é o papel deste profissional?
      Conversamos com o Rodrigo Maués, Tech Lead na Conviso Application Security, para entender melhor quem é e qual o papel do Security Champion dentro de um time de segurança.

      O que é o Security Champion
      De acordo com Maués, dentro de empresas que produzem softwares, é comum existir um atrito ocasional entre duas áreas.
      Para a área de desenvolvimento, as equipes de segurança são pontos de gargalo dentro de um processo já bem pressionado e com prazos apertados. Do outro lado, temos as equipes de segurança que, por vezes, entram em conflito ao buscar introduzir mais segurança nos produtos entregues pelos desenvolvedores.
       “Este conflito nem sempre é facilmente solucionado, pois vai contra algo que, do ponto de vista comercial, é bem mais forte que a validação de um código: as demandas de um mercado cada vez mais inovador e ágil”, contextualiza o Tech Lead. 
      É aí que entra o papel do Security Champion. No mundo de Segurança de Aplicações, entendemos os  Security Champions como sendo os membros das equipes de desenvolvimento que receberam treinamento específico para atuar como ponto focal de segurança de aplicações dentro do time. 
      Quando um membro do time de desenvolvimento se torna um Security Champion, ele estabelece uma melhor comunicação com seus pares e muda a cultura do desenvolvimento de dentro para fora, já que acessa a mesma linguagem dos membros envolvidos na produção do software.
      “Desta forma, o time de Desenvolvimento consegue compreender muito melhor a informação passada, uma vez que recebe o conhecimento de um dos seus”, esclarece Maués. 
      Ou seja: o Security Champion trabalha como uma ponte entre as duas áreas, de forma conciliadora, para garantir que a cultura da segurança seja mantida sem desgastar as equipes.

      Quais as responsabilidades de um Security Champion?
      Entre as principais responsabilidades dos Security Champions está a mudança cultural dos desenvolvedores, que devem passar a ter um olhar mais cuidadoso no trabalho de codificação, aplicando as melhores práticas de desenvolvimento seguro e buscando ter um olhar cada vez mais focado em segurança desde o início da criação de seus produtos.
      Um Security Champion, de forma geral, é um transformador cultural para as equipes de desenvolvimento, e atua também como uma ponte de ligação entre as áreas de desenvolvimento e de segurança. “É ele quem consegue manter um entendimento dos dois mundos, amenizando os conflitos e disputas”, esclarece Maués. 
      Algumas atividades comuns do dia a dia de um Security Champion são:
      Ajudar na realização de revisões de segurança;  Ajudar com a observação de melhores práticas de segurança; Desenvolver Modelagem de Ameaças para aplicativos novos e em evolução; Participar de movimentos de P&D – Pesquisa e Desenvolvimento; Orientar na identificação de requisitos de segurança; Avaliar e estudar bugs em código; Servir de elo de contato entre as demais equipes da área de segurança. No entanto, é muito importante ressaltar que o Security Champion não realiza essas tarefas sozinho. Tudo é feito em colaboração com o time! Afinal, o papel do Security Champion não é o de centralizar o conhecimento - e sim, de disseminá-lo nas equipes. 

      Como se tornar um Security Champion? Existe um perfil específico?
      É comum que Security Champions sejam desenvolvedores treinados e devidamente capacitados para suportar as iniciativas de segurança. 
      No entanto, isso não é regra - é possível que profissionais egressos de outras áreas, mas com algum conhecimento em desenvolvimento, recebam treinamento para atuar como Security Champions caso cumpram outros requisitos.
      De todo modo, é preciso ressaltar que Security Champions não são profissionais de segurança de aplicações que são focados exclusivamente em segurança. Essa confusão é muito comum, mas é uma concepção errada. 
      A escolha dos Security Champions dentro de cada time gera uma noção de pertencimento e ajuda no trabalho com os desenvolvedores. É imprescindível um trabalho cauteloso, que começa por um mapeamento de times. 
      Para isso, é preciso identificar e capacitar membros dos times de desenvolvimento que tenham esse perfil, para que atuem como facilitadores de segurança. E este papel exige características comportamentais, como iniciativa e autogestão.  Mas caso você sinta afinidade com essa carreira, esse já é um ótimo indício!

      Vagas na Conviso
      A Conviso, mantenedora aqui do Mente Binária, está com muitas vagas abertas. São vagas para áreas variadas dentro da empresa - de Desenvolvedor a Analista de Segurança da Informação.
      Caso você sinta afinidade com a especialidade da Conviso - Segurança de Aplicações - não deixe de se inscrever, ou mesmo de se cadastrar em nosso banco de talentos. É só clicar no botão abaixo:

    • By Fernando Mercês
      A Hex-Rays surpreendeu todo mundo agora. Liberou a versão mais recente do IDA, a 7.6, no modelo freeware. Só isso já seria muito bom, mas eles foram além: O IDA 7.6 freeware inclui um descompilador online (cloud-based) gratuito! Pois é, pessoas... Parece que a concorrência exercida pelo Ghidra realmente está sendo saudável para a comunidade. Ao disparar o plugin de descompilação (F5), o programa deixa claro que este recurso na versão gratuita suporta somente binários de 64-bits, dentre outros avisos:

       
      Ao continuar, a descompilação é concluída e códiogo em pseudo-C é exibido ao lado. Veja o exemplo descompilando o notead.exe nativo do Windows:

      Os recursos de interação como renomear variáveis e funções no descompilador estão habilitados, uma notícia muito boa para a comunidade!
      Além do descompilador, a versão 7.6 do IDA freeware inclui:
      Modo escuro
      Tá na moda né? E o IDA não ficou de fora. Se você for em Options -> Colors e mudar o Theme para dark, vai ter um visual assim:

       
      Organização de dados em pastas
      Sendo um disassembler interativo, a versão 7.6 oferece a opção de organizar os dados em pastas. Isso vale para vários locais. Um deles é a janela de funções. É possível agora criar pastas e organizar as funções dentro delas. Para isso, basta clicar com o botão direito do mouse na janela de funções e escolher Show folders. Depois é só selecionar as funções desejadas e escolher Create folder with items.

      E mais:
      Suporte a binários compilados em Go. Suporte ao novo Apple M1. Rebasing mais rápido. Sincronização entre o descompilador online e o disassembly. Lembando que o disassembler em si suporta binários de 32-bits também (PE, ELF e Mach-O). Só o descompilador que não.
       

    • By Bruna Chieco
      O professor, analista de sistemas e economista José Augusto N. G. Manzano lançou o livro "Algoritmos Funcionais - Introdução minimalista à lógica de programação funcional pura aplicada à teoria dos conjuntos". 
      O livro busca fundamentar, dentro de seu escopo, diversas ações matemáticas e de programação importantes dentro do universo funcional. "Este livro foi escrito exclusivamente para uso em sala de aula. É um material voltado à apresentação e estudo da 'Lógica de Programação Funcional', que vem tomando grandes posições dentro do universo comercial no desenvolvimento de software", escreveu o autor em publicação no seu LinkedIn.
      Paradigma funcional – O paradigma funcional tem um jeito de programar específico, diferenciado do tradicional, com algumas vantagens para certas aplicações. Uma das áreas em que o paradigma funcional se destaca é a ciência de dados, que tem sido uma grande aposta das empresas, especialmente startups, na busca de profissionais.
      Dentro do paradigma funcional, há várias linguagens de programação. O Nubank, por exemplo, utiliza bastante uma linguagem chamada Clojure, e tem uma alta demanda por profissionais que programem nessa linguagem (saiba mais). 
      Este livro, portanto, é uma introdução para quem quer adentrar nesse mundo das linguagens funcionais. Qualquer futuro programador em uma dessas linguagens funcionais vai se beneficiar deste conteúdo. 

      Clique e veja como comprar
      O professor Manzano se destaca também por produzir muitos materiais da área, se mantendo sempre atualizado sobre o que o mercado está precisando no momento. Já divulgamos outros trabalhos de sua autoria aqui no Mente Binária, entre eles o livro 'Fundamentos em Programação Assembly':
       
      Inclusive, o canal do professor Manzano no YouTube possui mini aulas sobre programação de computadores e outros assuntos relacionados à área da computação e tecnologia da informação. Vale conferir!
    • By Bruna Chieco
      Pesquisadores da Sayfer fizeram engenharia reversa no WhatsApp Web e acabaram encontrando "sem querer" um recurso desabilitado por padrão, ou seja, que ainda não está liberado. A função pre-released descoberta permite que a velocidade de um áudio seja acelerada. 
      Os pesquisadores, na verdade, estavam fazendo uma pesquisa sobre outro projeto quando descobriram acidentalmente que o WhatsApp tem um sinalizador para o recurso oculto que permite aos usuários alterar a taxa de reprodução de mensagens de voz.
      Uma das ferramentas de pesquisa utilizadas pelos pesquisadores permitiu essa alteração, sendo cada mensagem de voz é essencialmente uma marca de áudio com um blob de uma fonte de áudio. Para alterar a velocidade, contudo, foi necessário fazer engenharia reversa no código-fonte minimizado do WhatsApp para a web.
      Eles descobriram que o WhatsApp tem três velocidades pré-determinadas para os áudios, porém, desabilitadas. Em publicação, os pesquisadores explicam o passo a passo do que fizeram para conseguir alterar a taxa de reprodução dos áudios.
      E se você quiser saber mais sobre engenharia reversa, o Mente Binária tem um curso com 24 aulas que pode ser acessado por meio do nossos canal no YouTube:
       
    • By Felipe.Silva
      Injeção de código é uma técnica que consiste em adicionar instruções extras em um executável. Essas instruções podem ser adicionadas em vários lugares do programa, inclusive executar antes do entry point original.
      O uso mais comum para injeção de código é para fins maliciosos, onde um shellcode poderia ser inserido no executável e dar acesso remoto para um atacante. Mas um exemplo de uso "justo" para essa técnica é para fins de patching no executável quando você quer que algo seja alterado em tempo de execução no binário.
      Se você já tentou injetar código em um executável manualmente deve saber que não é uma tarefa tão divertida. Pensando neste tipo de impasse, imaginei que seria interessante ter uma ferramenta para automatizar esse tipo de manipulação de um executável. Por esse motivo criei o pei, uma ferramenta para automatizar injeção de código e outros tipos de manipulações em executáveis PE de 32-bit e 64-bit.
      O pei foi programado pensando na automação e por isso é fácil usar a ferramenta a partir de um script. Com ela você pode obter e modificar valores no executável, e é claro, injetar código.
      Antes de qualquer coisa você pode instalar o pei em seu Linux rodando os comandos abaixo:
      git clone https://github.com/Silva97/pei cd pei make sudo make install Nota: Caso use Windows e não esteja usando WSL ou um MSYS2 da vida, você pode compilar o projeto instalando o make e o MinGW (recomendo usar o Chocolatey). No entanto, o “sudo make install” não vai funcionar no Windows, você vai ter que adicionar o executável ao PATH manualmente.
      Se você não estiver a fim de compilar o executável, fiz o favor de adicionar o binário da ferramenta compilado para Windows nos releases dela. Você pode baixá-lo no link https://github.com/Silva97/pei/releases/latest.
      O uso básico da ferramenta segue o seguinte formato:
      pei [opções] <operação> <executável> [argumento] Se você quiser ver mais detalhes de como usar a ferramenta você pode rodar “pei -h”.
      Operações
      As operações são o que determinam o que a ferramenta irá fazer com o executável, indo desde exibir informações sobre ele até modificar campos dos cabeçalhos.
      show
      A operação show serve para exibir informações sobre o executável e campos dos cabeçalhos. Se você não passar argumentos para a operação por padrão ela irá exibir informações básicas do executável:

      Você também pode especificar uma combinação de letras para escolher quais campos serão exibidos, dentre elas: c (COFF header), o (optional header), d (data directories) e s (section). Exemplo:
      $ pei show test.exe co Esse comando, por exemplo, exibe o COFF header e optional header do executável.
      get
      A operação get pega o valor de um campo individual de um dos cabeçalhos (coff, optional ou section) do executável.
      Seguindo uma notação com pontos, semelhante à acessar campos de estruturas em C, você pode especificar o cabeçalho e o nome do campo para ser obtido. Por exemplo, para obter o entry point do executável o comando ficaria:
      $ pei get executavel.exe optional.entry_point '%x' 14f0 Dica: Veja o nome dos campos dos cabeçalhos usando a operação show.
      O argumento após o nome do campo é uma string de formatação idêntica a da função printf() da libc, que aceita quaisquer flags de formatação disponíveis para a função.
      Para acessar os campos de uma seção é necessário especificar o número da seção também, como demonstrado no print abaixo:

      edit
      A operação edit serve para modificar o valor de campos, onde o nome do campo é especificado de maneira idêntica à operação get.
      Você pode utilizar os operadores `=`, `|=` e `&=` que fazem exatamente a mesma coisa que na linguagem C. Exemplos:
      $ pei edit executavel.exe section.0.name = .code $ pei edit executavel.exe optional.entry_point = 0xabcd1234 Esta operação aceita números em decimal, hexadecimal ou octal na hora de definir o valor de campos numéricos.
      zeros
      Esta operação simplesmente exibe uma lista das maiores sequências de bytes nulo em cada seção do executável. É este espaço que é utilizado para injetar o código, tornando a operação útil para você poder escolher em qual seção injetar o código.
      $ pei zeros executavel.exe Section #0 '.text': 0x00000000000022fb of 13 bytes Section #1 '.data': 0x000000000000242c of 1012 bytes Section #2 '.rdata': 0x0000000000002a5b of 37 bytes Section #6 '.idata': 0x0000000000003a26 of 22 bytes Section #7 '.CRT': 0x000000000000420b of 21 bytes Section #9 '/4': 0x0000000000004649 of 23 bytes Section #10 '/19': 0x0000000000004cbe of 10 bytes Section #12 '/45': 0x000000000003e2fc of 5 bytes Section #13 '/57': 0x0000000000041019 of 8 bytes Section #15 '/81': 0x0000000000043c33 of 44 bytes Section #16 '/92': 0x0000000000045509 of 23 bytes inject - A cereja do bolo 🙂
      Esta é a operação que injeta o código. Você pode usar a opção -f para especificar o arquivo contendo o código a ser injetado, que seria um raw binary. Onde esse arquivo deve conter apenas as instruções de código de máquina a ser injetado, como um shellcode por exemplo.
      Opcionalmente, você pode usar a opção -s para especificar o número da seção que você quer injetar o código. Se a opção não for especificada, por padrão o pei vai injetar onde houver mais espaço disponível.
      $ pei -f my_code.bin inject executavel.exe Writed code of 12 bytes on offset 0x0000000000043924 of section #15 '/81' Após o código injetado, o pei insere um jump absoluto para o entry point original do executável, fazendo com que após o código injetado ser executado o fluxo de execução do programa continue normalmente.
      Outra modificação que o pei faz é desabilitar o dynamic base do executável, para evitar que o jump aponte para o endereço errado.
      Dica: Injetando Código Muito Grande
      Se você precisar injetar um código maior do que o espaço disponível apontado pela operação zeros, você pode dividir o código a ser injetado em várias partes e injetar cada parte por vez, seguindo a ordem da última até a primeira parte. Isso funciona porque o pei irá adicionar um jump no final do código para o endereço que está sendo indicado como entry point. Se você já injetou código antes, esse endereço é o endereço do código anteriormente injetado. 🙂
      Dessa forma você pode fazer um chain de saltos da primeira parte até a última e então saltar para o entry point original. Exemplo:
      $ pei inject -f parte-3.bin executavel.exe Writed code of 87 bytes on offset 0x0000000000043833 of section #15 '/81' $ pei inject -f parte-2.bin executavel.exe Writed code of 80 bytes on offset 0x0000000000044924 of section #11 '/23' $ pei inject -f parte-1.bin executavel.exe Writed code of 32 bytes on offset 0x0000000000401a15 of section #1 '.text' Isso irá criar a seguinte ordem de execução: parte-1.bin -> parte-2.bin -> parte-3.bin -> entry point. Onde as setas “->” representam os saltos.
      diff
      Esta operação exibe diferenças entre dois executáveis. Ela compara cada campo dos cabeçalhos do executável e o conteúdo das seções, e depois exibe estas diferenças no terminal. Você pode usar a opção -c (ou --color) para que a saída da operação seja colorida:

      Em vermelho são os valores no executável original que foram modificados e em verde são os valores novos no executável modificado.
      patch
      Essa operação lê um texto de entrada no mesmo formato que a saída da operação diff e replica as mesmas modificações no executável. Exemplo:
      $ pei patch executavel2.exe diff-output.txt Caso você não especifique um patch file, o pei irá ler de stdin. Assim, é possível que você faça um pipe entre uma operação de diff e patch:
      $ pei diff original.exe mod.exe | pei patch outro.exe A diferença de fazer isto e simplesmente fazer uma cópia do executável modificado, é que a ideia é replicar somente as diferenças entre os executáveis. Quaisquer outros campos não são tocados pela operação patch, o que te permite salvar alterações e replicá-las por meio de um script. 🙂
      Se você quiser, também é possível escrever um patch file manualmente, bastando imitar a saída de diff. Uma dica é que os valores dos campos originais (em vermelho) não são necessários para a operação patch, então você pode inserir somente os valores novos. Seguindo o print da saída do diff que utilizamos agora pouco como exemplo:
      optional.entry_point xxx 0x4ca33 section.0.name xxx .code section.15.characteristics xxx 0x62100040 // @O texto não faz diferença, só importa o @ no início da linha // Daqui para baixo qualquer linha que não inicie com + será ignorada. +0x43830 00 30 9f 61 62 63 0a b8 f0 14 40 00 ff e0 00 00 // O formato é: +0xoffset bytes em hexadecimal Nota: Onde há o “xxx” seriam os campos dos valores originais (em vermelho) que não são lidos pelo pei porém são necessários para imitar a saída da operação diff. Você pode colocar qualquer valor aí que não dará erro já que são ignorados.
      Você pode usar a operação patch em scripts para automatizar várias modificações em um executável ao mesmo tempo, inclusive em bytes de um determinado offset.
×
×
  • Create New...