Jump to content

Fernando Mercês

Administradores
  • Content Count

    711
  • Joined

  • Last visited

Community Reputation

0 Neutral

Personal Information

Recent Profile Visitors

7,120 profile views
  1. Fernando Mercês

    Cursos

    É, acho que seria muito legal ter um curso de Assembly no canal, mas não sei se sou a melhor pessoa pra isso viu. Tô aberto a propostas. kkkkk Por hora, conheço esse em Português ó: Abraço!
  2. 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! 🙏
  3. @fredericopissarra, pode dar uma luz aqui? 🙂
  4. Eita, não sabia disso. Você tem razão: a função é preparada para limpar buffers de saída, não de entrada. Tenho a sensação de já ter usado com buffer de entrada, então deve ter bastante código bugado meu por aí! kkkk Existe a fpurge também, mas não sei se é padrão. Nunca usei. Obrigado por alertar, @Marioh! 👍 Abraço!
  5. Yep, como o @Marioh falou, é preciso consumir o buffer (no caso, o stdin, que é o buffer que você tá usando). Acontece que ao digitar 1 e pressionar [enter], você insere dois bytes no buffer: o '1' e '\n'. Aí você tá consumindo só o primeiro com o seu scanf(), deixando o '\n' lá. Como a fgets() lê até encontrar um \n, ela o encontra logo de cara não copia mais nada pra char nome[]. Ou seja, o comportamento é o esperado. Tem outras soluções além da do Marioh. No final vão meio que dar no mesmo: 1. Usando a fflush(): scanf("%c", &value_menu); fflush(stdin); 2. Lendo o \n (que é o segundo byte) pra outra variável (meio feio): char tmp; scanf("%c%c", &value_menu, &tmp); No caso acima você não usaria o char tmp nunca mais, mas se olhar a documentação da scanf, vai ver que tem isso ó: CONVERSIONS Following the % character introducing a conversion, there may be a number of flag characters, as follows: * Suppresses assignment. The conversion that follows occurs as usual, but no pointer is used; the result of the conversion is simply discarded. 3. Ou seja, podes usar o *: scanf("%c%*c", &value_menu); Assim não vai precisar da variável auxiliar. Independente do que escolher (e provavelmente há soluções além dessas), o importante é você entender o que se passa. 🙂 Abraço!
  6. @dudsdev não creio que seja o menor PE possível do ponto de vista do tamanho do arquivo (porque ainda teria alguns cabeçalhos que daria pra remover e tal) mas de funcionalidade o código compila sim. Chutaria uns 2KB de tamanho... Abraço.
  7. Oi @kassane. Eu não tenho usado o Telegram mas se puder compartilhar lá, agradecemos! 😉
  8. O que é Análise de Malware? É o processo de entender como um malware funciona, o que faz e qual seu impacto no ambiente alvo. Pode envolver análise de toda a campanha ou infraestrutura do malware e até mesmo chegar em atribuição. Como funciona? Com conhecimentos de engenharia reversa, análise estática e dinâmica, além de algum conhecimento específico da comunidade de investigação, é possível mapear as rotinas maliciosas de um arquivo, entender seu funcionamento e até mesmo gerar assinaturas para deteção, prevenção e mitigação de ataques de malware. O treinamento O AMO (Análise de Malware Online) é um treinamento básico gratuito publicado no nosso canal no YouTube Papo Binário, graças ao suporte dos nossos apoiadores. A ideia é formar um analista de malware júnior, que possa atuar com análise em empresas ocupando cargos na área de análise de malware, resposta à incidentes, SOC, analista forense, entre outros. As aulas publicadas estão abaixo. Pré-requisitos Conhecimento em criação de máquinas virtuais. Ter assistido o Curso de Engenharia Reversa Online (CERO). Ter assistido o Programação Moderna em C. Instrutor @Fernando Mercês Aulas publicadas Aula 0 +1 - Preparando o ambiente e analise inicial do primeiro malware! Aula 02 - Conhecendo o DIE e o IDA
  9. Saudações, gnoo. É, verifiquei aqui no sistema e não tem essa opção, não sei o motivo, já que é possível editar os posts no fórum e as avaliações feitas, por exemplo. O jeito é comentar de novo com a ratificação. Não é a solução mais adequada, entendo, mas não vejo outra alternativa rápida. Abraço.
  10. É nóis! Obrigado pelas perguntas também! 😉 Sim, os endereços nela são resolvidos em loading time e o loader a preenche. Eu não lembro se falo especificamente desta seção, mas já viu esta aula do CERO? De qualquer forma, tem o livro "Learning Linux Binary Analysis" do elfmaster que tem mais informações. Eu sei pouco sobre este processo, só entendo o básico mesmo. Sinceramente, eu encontrei esse arquivo por minha conta quando tava tentando dumpar a memória de um processo no contexto de forense. Nunca vi documentação sobre ele, mas acho que o comando pmap o utiliza, então pode ser outra forma de buscar também, além do fonte do kernel em si claro. Abraço!
  11. Olá, bom dia! Estamos buscando um Desenvolvedor com conhecimento de C++ que tenha experiência com framework Qt, Git e MySQL para atuar em uma empresa de tecnologia especializada em Engenharia Submarina localizada na Barra da Tijuca. Inglês de intermediário a avançado é importante. Você tem interesse? Pode encaminhar seu CV com atual salário? Caso não, posso contar com a sua indicação de bons profissionais? Aguardo seu retorno. Obrigada, isabelle.teixeira @ beexecutive.com . br
  12. Olha, eu não sou um grande especialista e toda vez que olho no Linux parece que tá tudo diferente mas acho que só tá te faltando o conceito de mapeamento (mapping). No caso do ELF os segmentos (no arquivo) são mapeados para a memória, ou seja, têm seu conteúdo copiado e ganham endereços lá. Só que cada segmento pode possuir várias seções. Por exemplo, vou compilar um programa que imprime seu próprio PID e também tem uma string (pra forçar a criação da seção .data no binário): #include <stdio.h> #include <sys/types.h> #include <unistd.h> int main(void) { char s[] = "Meu PID:"; printf("%s %d\n", s, getpid()); while (1); return 0; } Podemos compilar e analisar como ficaram os segmentos e as seções no binário (no arquivo, antes de ser carregado, ou seja, antes de ter suas seções mapeadas para a memória): $ gcc -no-pie -o hello hello.c $ readelf -l hello Elf file type is EXEC (Executable file) Entry point 0x401050 There are 11 program headers, starting at offset 64 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040 0x0000000000000268 0x0000000000000268 R 0x8 INTERP 0x00000000000002a8 0x00000000004002a8 0x00000000004002a8 0x000000000000001c 0x000000000000001c R 0x1 [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000 0x0000000000000470 0x0000000000000470 R 0x1000 LOAD 0x0000000000001000 0x0000000000401000 0x0000000000401000 0x00000000000001dd 0x00000000000001dd R E 0x1000 LOAD 0x0000000000002000 0x0000000000402000 0x0000000000402000 0x0000000000000148 0x0000000000000148 R 0x1000 LOAD 0x0000000000002e10 0x0000000000403e10 0x0000000000403e10 0x0000000000000228 0x0000000000000230 RW 0x1000 DYNAMIC 0x0000000000002e20 0x0000000000403e20 0x0000000000403e20 0x00000000000001d0 0x00000000000001d0 RW 0x8 NOTE 0x00000000000002c4 0x00000000004002c4 0x00000000004002c4 0x0000000000000044 0x0000000000000044 R 0x4 GNU_EH_FRAME 0x000000000000200c 0x000000000040200c 0x000000000040200c 0x000000000000003c 0x000000000000003c R 0x4 GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 RW 0x10 GNU_RELRO 0x0000000000002e10 0x0000000000403e10 0x0000000000403e10 0x00000000000001f0 0x00000000000001f0 R 0x1 Section to Segment mapping: Segment Sections... 00 01 .interp 02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt 03 .init .plt .text .fini 04 .rodata .eh_frame_hdr .eh_frame 05 .init_array .fini_array .dynamic .got .got.plt .data .bss 06 .dynamic 07 .note.ABI-tag .note.gnu.build-id 08 .eh_frame_hdr 09 10 .init_array .fini_array .dynamic .got Perceba que os segmentos de 02 a 05 são do tipo LOAD. Isto significa que eles serão mapeados/carregados/copiados para a memória. Na sequência você qual seção pertence a cada segmento. Daria pra dizer o seguinte então: A .text tá no segmento 03, vai ser mapeada em 401000, que tem permissão de leitura e execução (R E). A .rodata tá no segmento 04, vai ser mapeada em 402000, que tem permissão de leitura somente (R). A .data e .bss estão no segmento 05, vão ser mapeadas em 403e10, que tem permissão de leitura e escrita (R W). Só que seções diferentes vão para regiões diferentes da memória do processo, como a imagem que você postou ilustra, então esse mapeamento não é um pra um. Pra checar, executa o programa e pega o PID: $ ./hello Meu PID: 2186 Noutra aba: $ cat /proc/2186/maps 00400000-00401000 r--p 00000000 08:01 131608 /tmp/hello 00401000-00402000 r-xp 00001000 08:01 131608 /tmp/hello 00402000-00403000 r--p 00002000 08:01 131608 /tmp/hello 00403000-00404000 r--p 00002000 08:01 131608 /tmp/hello 00404000-00405000 rw-p 00003000 08:01 131608 /tmp/hello 01620000-01641000 rw-p 00000000 00:00 0 [heap] 7f9efaa89000-7f9efaaab000 r--p 00000000 08:01 664113 /usr/lib/x86_64-linux-gnu/libc-2.28.so 7f9efaaab000-7f9efabf3000 r-xp 00022000 08:01 664113 /usr/lib/x86_64-linux-gnu/libc-2.28.so 7f9efabf3000-7f9efac3f000 r--p 0016a000 08:01 664113 /usr/lib/x86_64-linux-gnu/libc-2.28.so 7f9efac3f000-7f9efac40000 ---p 001b6000 08:01 664113 /usr/lib/x86_64-linux-gnu/libc-2.28.so 7f9efac40000-7f9efac44000 r--p 001b6000 08:01 664113 /usr/lib/x86_64-linux-gnu/libc-2.28.so 7f9efac44000-7f9efac46000 rw-p 001ba000 08:01 664113 /usr/lib/x86_64-linux-gnu/libc-2.28.so 7f9efac46000-7f9efac4c000 rw-p 00000000 00:00 0 7f9efac71000-7f9efac72000 r--p 00000000 08:01 664105 /usr/lib/x86_64-linux-gnu/ld-2.28.so 7f9efac72000-7f9efac90000 r-xp 00001000 08:01 664105 /usr/lib/x86_64-linux-gnu/ld-2.28.so 7f9efac90000-7f9efac98000 r--p 0001f000 08:01 664105 /usr/lib/x86_64-linux-gnu/ld-2.28.so 7f9efac98000-7f9efac99000 r--p 00026000 08:01 664105 /usr/lib/x86_64-linux-gnu/ld-2.28.so 7f9efac99000-7f9efac9a000 rw-p 00027000 08:01 664105 /usr/lib/x86_64-linux-gnu/ld-2.28.so 7f9efac9a000-7f9efac9b000 rw-p 00000000 00:00 0 7ffcd5fbf000-7ffcd5fe0000 rw-p 00000000 00:00 0 [stack] 7ffcd5feb000-7ffcd5fee000 r--p 00000000 00:00 0 [vvar] 7ffcd5fee000-7ffcd5ff0000 r-xp 00000000 00:00 0 [vdso] Veja que temos 5 segmentos no hello, além de heap e stack. Mas eram 4 segmentos que seriam mapeados né? É, mas algumas seções deles foram mapeadas com permissões diferentes, exigindo outros segmentos. Para ver quais, pode usar o gdb, attachando no processo: $ gdb -q -p 2186 Attaching to process 2186 Reading symbols from /tmp/hello...(no debugging symbols found)...done. Reading symbols from /lib/x86_64-linux-gnu/libc.so.6...(no debugging symbols found)...done. Reading symbols from /lib64/ld-linux-x86-64.so.2...(no debugging symbols found)...done. 0x000000000040116b in main () (gdb) info file Symbols from "/tmp/hello". Native process: Using the running image of attached process 2186. While running this, GDB does not access memory from... Local exec file: `/tmp/hello', file type elf64-x86-64. Entry point: 0x401050 0x00000000004002a8 - 0x00000000004002c4 is .interp 0x00000000004002c4 - 0x00000000004002e4 is .note.ABI-tag 0x00000000004002e4 - 0x0000000000400308 is .note.gnu.build-id 0x0000000000400308 - 0x0000000000400324 is .gnu.hash 0x0000000000400328 - 0x00000000004003a0 is .dynsym 0x00000000004003a0 - 0x00000000004003e6 is .dynstr 0x00000000004003e6 - 0x00000000004003f0 is .gnu.version 0x00000000004003f0 - 0x0000000000400410 is .gnu.version_r 0x0000000000400410 - 0x0000000000400440 is .rela.dyn 0x0000000000400440 - 0x0000000000400470 is .rela.plt 0x0000000000401000 - 0x0000000000401017 is .init 0x0000000000401020 - 0x0000000000401050 is .plt 0x0000000000401050 - 0x00000000004011d1 is .text 0x00000000004011d4 - 0x00000000004011dd is .fini 0x0000000000402000 - 0x000000000040200b is .rodata 0x000000000040200c - 0x0000000000402048 is .eh_frame_hdr 0x0000000000402048 - 0x0000000000402148 is .eh_frame 0x0000000000403e10 - 0x0000000000403e18 is .init_array 0x0000000000403e18 - 0x0000000000403e20 is .fini_array 0x0000000000403e20 - 0x0000000000403ff0 is .dynamic 0x0000000000403ff0 - 0x0000000000404000 is .got 0x0000000000404000 - 0x0000000000404028 is .got.plt 0x0000000000404028 - 0x0000000000404038 is .data 0x0000000000404038 - 0x0000000000404040 is .bss Taí, todas as seç˜ões daqueles 4 segmentos do tipo LOAD (PT_LOAD o nome certo hehe) e seus devidos endereços de início e sim. Você pode fazer um paralelo com o conteúdo do /proc/<pid>/maps e ver que faz sentido, por exemplo, a .data estar em 404028 já que a .got.plt também precisa ficar numa região com leitura e escrita e esta é listada antes da data no segmento 05. Já a .got, originalmente (no arquivo), num segmento com rw, foi para uma região somente com leitura. Resumindo, na prática o número de regiões de memória (segmentos na imagem que você postou) pode ser outro. Pode ser confuso mesmo, porque tem segmento e seção no arquivo, depois segmento em memória e seção na memória, ou seja, 4 coisas, mas o mapeamento não é um pra um. Espero que ajude. Brincando mais com gdb, readelf, etc aí e fazendo mais testes tenho certeza que vai ficar mais claro. 👍 Abraço!
  13. Opa, tem várias coisas incorretas aí. O certo seria imprimir a. O tamanho do array é 5, mas você tá passando 10. Essa linha diz "enquanto 10 for menor que 10", então nem nada neste bloco será executado... A sua função sort também tá meio confusa. Não entendi bem como você quis fazer. O bubble sort consiste simplesmente em percorrer o array n-1 vezes, onde n é o tamanho do array (ou seja, seu número de elementos) testando se o elemento é menor que o anterior. Se sim, trocam-se. Recomendo estudar mais o bubble sort. Tem muito material na web sobre, além de vídeos no YouTube, etc. Só não pegue nada pronto, do contrário você não aprende e daí seu tempo de estudo é jogado no lixo e a grana e o tempo que você tá investindo neste curso/faculdade também. 😉 Abraço!
  14. Fernando Mercês

    MBConf@Home

    until
    Nossa primeira conferência, totalmente online e gratuita! Todas as informações sobre a MBConf estão disponíveis em http://menteb.in/conf . Se você já viu tudo lá e quer se registrar, basta confirmar sua presença aqui. Pouco antes do horário de início da conferência você receberá um link para ingressar nela. Agenda 10h00 - Abertura - Fernando Mercês 10h15 - Análise do malware Guildma - Renato Marinho 11h05 - Sistemas embarcados - Sergio Prado 11h55 - Debugging tricks - Thiago Queiroz 12h45 - Bluetooth shell - Noilson Caio 13h35 - Debugging com WinDbg - Wanderley Caloni 14h25 - Engenharia Reversa em Android - Maycon Vitali 15h10 - Encerramento - Anchises Moraes Lembre-se de compartilhar a conferência nas suas redes sociais. 😎
  15. Opa! Na verdade a função compara o argumento recebido (char param_1) com os elementos de um array (eu prefiro chamar assim, pra não confundir com os tipos vector em algumas lingaugens). Só pra ficar mais preciso, pois não é "um valor de um vetor". Mas eu entendi o que você quis dizer. 😉 O array tem que estar pronto quando essa função é chamada. Logo, basta colocar um breakpoint logo no início dessa função e olhar o conteúdo do array. Não lembro se o Ghidra já tem debugger, mas você pode usar o gdb. Na real, podemos assumir que não existem muitos "tipos" em baixo nível. É tudo número mesmo e o tamanho é que importa. Se o Ghidra usou (int) e o tamanho do elemento é 4, é isso, estamos diante de um array de elementos do tipo int32 (4 bytes). É uma boa dedução, mas dá pra ver isso rápido porque o Ghidra já nomeou o vetor como DAT_. Este prefixo é para variáveis na seção .data (ou numa seção com as mesmas propriedades, já que o nome da seção em si não vale de muita coisa). A distância do endereço dele (301020) para a função (1007fa), que tá numa seção de código - normalmente a .text), também sugere que ele está em outra seção e se você for no "memory map" do Ghidra (entre aspas porque não lembro como o Ghidra chama), vai ver que 301020 está no range da seção .data (entre seu início e fim). Se você desconfiar das suposições do Ghidra, pode sempre checar o Assembly. De novo, o que vai definir o tipo é como o conteúdo é lido. Se são 4 bytes, é int32. Se as instruções de comparação são as que consideram sinal, então é signed. Do contrário, unsigned. E por aí vai. Ótimas perguntas! Abraço!
×
×
  • Create New...