Jump to content

Felipe.Silva

Membros
  • Content Count

    58
  • Joined

  • Last visited

  • Country

    Brazil

Everything posted by Felipe.Silva

  1. Tenho uma ferramenta em Bash no meu GitHub chamada 'new' que pode ser útil para gerar executáveis assim, sem precisar de modificar "na mão". No repositório tu pode olhar a pasta "templates" que um deles é o bin-elf64. A sequência de comandos ficaria assim: $ nasm tst.asm -o tst.bin $ new bin-elf64 file=tst.bin out=tst $ chmod +x tst $ ./tst Um "Hello World!" fica com 171 bytes. Mas um detalhe: Menor não necessariamente significa melhor. Isso aí é só de brincadeira, o executável não vai ficar mais eficiente porque você "capou" ele. Mas como passa-tempo é válido.
  2. A questão é que usando um registrador de 64 bits, a instrução divide o valor contido em RDX:RAX pelo valor passado como operando. Considere que RDX são os 8 bytes mais significativos do valor, e RAX seriam os 8 bytes menos significativos. Simplesmente zerar RDX traria um resultado incorreto se o número em RAX fosse negativo, o certo seria fazer um sign-extend e a instrução CQO faz isso para você: mov rax, 2345 cqo ; RDX:RAX = sign_extend(RAX) idiv rbx
  3. Colega, pelo que pesquisei aqui isto é uma instrução intrísseca do compilador do Visual Studio (Microsoft). Tem muita coisa em C que não é padronização da linguagem mas sim de alguma implementação específica, esta é uma delas. https://docs.microsoft.com/pt-br/cpp/intrinsics/sidt?view=vs-2019 Se você usa o GCC, e suponho que sim, você pode usar Inline Assembly para isso. (outros compiladores também suportam Inline Assembly, mas com sintaxe diferente) Vou deixar um exemplo: #include <stdio.h> #include <inttypes.h> typedef struct idtr_s { void *base; uint16_t limit; } idtr_t; inline void sidt(idtr_t *idtr) { __asm__ ( "sidt %0" : "=m" (idtr) ); } int main(void) { idtr_t idt; sidt(&idt); printf("%p, %" PRIu16 "\n", idt.base, idt.limit); return 0; } Detalhe que o uso de uma inline function demanda que você ligue a otimização de código na hora de compilar. Tu pode por exemplo fazer assim: $ gcc -O2 tst.c -o tst Ah, uma pequena nota, sem querer atropelhar o autor. Mas o uso de DWORD para 'base' não está tecnicamente correto. Ele está assumindo que o tamanho do dado será necessariamente 4 bytes, mas isso só é verdade se o programa não estiver rodando em modo de 64-bit. Por isso usei um ponteiro void no lugar. EDIT: A instrução SIDT não carrega a IDT, carrega o IDTR que é um registrador que armazena estas informações sobre a IDT. "base" no caso é o endereço inicial de onde a IDT está.
  4. $ gcc jonth.c -o test.c -E Eu usei -E do GCC para ver o conteúdo expandido. Depois procurei pelo texto "main(" no arquivo de saída e encontrei. Aproveitando das diretivas de linha que o GCC insere, eu vi que o conteúdo estava expandindo no arquivo /usr/include/X11/Xlib.c na linha 3889. Nesta linha contém a seguinte declaração: extern XIM XIMOfIC( XIC /* ic */ ); O `#define` da linha 5 seta o macro de nome XIMOfIC. Perto do final desse macro nós temos um macro "O" ali. Veja em negrito: Acontece que aonde tem esse "O" é expandido o conteúdo do macro B. Pelo visto um macro que expande para o nome de outro macro dentro de um header file, é expandido mais uma vez. Parece que há uma repetição do pré-processamento. Para testar isso eu fiz o seguinte: // Arquivo tst.c #define B X Y #define X 5 #define Y , c = 3 #include <stdio.h> #include "h.h" int main(void) { printf("%d, %d\n", b, c); return 0; } // Arquivo h.h int b = B; E funcionou. No arquivo "h.h", B expandiu para o conteúdo de X e Y em sequência. Formando: int b = 5, c = 3; E se a gente voltar para o primeiro `#define`, a gente nota que a declaração do macro "O" é feita como uma sequência de letras, onde a primeira é B. Seguindo essa "corrente" os macros vão expandindo um atrás do outro. Isso é brilhante hahaha.
  5. Não, eles não fazem a mesma coisa. O código em Assembly é para 32-bit e faz uma chamada para a syscall write do kernel Linux. Já o código em C é compilado para 64-bit e faz uma chamada para a função printf() da libc. Os código são diferentes porque são diferentes. Além disso C é uma linguagem de alto nível, então é de se esperar que haja mais código no executável do que o programador consegue enxergar. C ainda é minimalista, mas experimenta ler o código de outras linguagens de alto nível para ver a diferença absurda. Ah, e só porque o resultado é o mesmo não quer dizer que esteja fazendo a mesma coisa. Vou ilustrar com números: 4 + 5 = 9 18 / 2 = 9 Só porque as duas operações resultam em 9, não quer dizer que façam a mesma coisa. Linguagem de programação é muito mais do que você digita, e mais ainda do que você pode ver no terminal.
  6. Concordo plenamente. De qualquer forma, foi uma excelente descoberta de sua parte.
  7. Sim, eu quero vê-las e agradeço as críticas. Sobre o binário, é que eu vejo muitas pessoas que não são capazes de separar o conceito do sistema numérico binário do próprio código de máquina em si. Por exemplo, já vi gente vendo um número em binário de uma letra ASCII e falando que era "o código de máquina da letra A". Outros já vi afirmando que binário é uma criptografia. Por isso eu tentei deixar claro que código de máquina é uma coisa, e o sistema numérico binário é outra. Embora o código de máquina seja fisicamente em sistema binário. E o mesmo vale para hexadecimal, já vi pessoas achando que código de máquina é hexadecimal porque abriu um binário em um editor hexa e viu os bytes em hexadecimal... Sobre o resto, vou revisar. Muito obrigado.
  8. Galera, estou escrevendo uma série de artigos no Medium explicando o básico do código de máquina da arquitetura x86. (desconsiderando o modo de 64-bits e outras tecnologias que eu não estudei) Essa informação pode ser útil para entender melhor o Assembly da arquitetura e também se alguém, por algum motivo aleatório, quiser desenvolver um assembler, disassembler, emulador ou qualquer outra ferramenta que exija esse conhecimento. No final de cada artigo tem um link para a próxima parte, mas mesmo assim irei deixar todas as partes indexadas aqui. Quando eu escrever novas partes irei comentar aqui no post. Código de máquina x86 — Parte 0 | Introdução Código de máquina x86 — Parte 1 | Conhecendo Código de máquina x86 — Parte 2 | Opcode Código de máquina x86 — Parte 3 | Prefixos
  9. Fui o único que votou no projeto Bumerangue. ☹️
  10. Sim, falhas do tipo continuam sendo encontradas. E sempre serão, não acredito que um dia haverá um sistema 100% seguro.
  11. Não sou lá um especialista no assunto, mas enquanto não aparece uma pessoa mais qualificada você vai ter que se contentar com o que eu sei. rsrsrs :P Antes de mais nada esse método é para atacar redes WPA2-PSK, que é um dos métodos de autenticação que uma rede WPA2 suporta. Geralmente esse método de autenticação mais simples é usado em residências. Empresas usam o outro método porque é mais seguro. (não sei como o outro método funciona, nem pergunte.) O "método tradicional" para quebrar uma senha de uma rede WPA2 segue três passos simples: 1) Monitorar o tráfego no canal em que se encontra a rede alvo. 2) Esperar pacientemente que alguém se conecte a rede. Quando o handshake é capturado é o "sinal" que alguém tentou se conectar a rede. 3) Quebrar a senha por força bruta. (se você pegou o handshake de uma conexão falha vai perder tempo :P ) Obs.: O passo dois pode ser acelerado forçando a desautenticação de algum dispositivo forjando pacotes. Mas isso não é importante para o assunto. Como eu disse eu não entendo muita coisa, mas o ataque de força bruta acontece no pacote de comunicação do 4-Way Handshake. Basicamente ele tenta "simular" a criação desse pacote usando uma chave X como se fosse a senha de autenticação. Se o resumo do pacote bater com o resumo do handshake capturado, então quer quiser que a senha é a correta. O novo método apresentado no artigo elimina a necessidade de capturar o handshake. (essa é a parte mais problemática) Isso porque ele descobriu que esse tal de PMKID é gerado usando o algoritmo HMAC-SHA1. Onde é usado como chave o PMK* e como dado a concatenação da string "PMK Name", o MAC do AP(o roteador neste caso) e o MAC do dispositivo que está se comunicando com o roteador. Ou seja: "PMK Name" + MAC_DO_ROTEADOR + MAC_DO_DISPOSITIVO Tudo isso concatenado é "hasheado" usando algoritmo HMAC-SHA1 e usando como chave o PMK. Onde o "PMK" nada mais é que a senha configurada para a rede WiFi. O MAC do roteador e do dispositivo pode ser capturado facilmente monitorando a rede... Percebeu onde se encontra a vulnerabilidade do sistema? Dessa forma dá para descobrir facilmente o dado, só o que não temos é a chave utilizada. (a senha da rede WiFi) Ou seja, isso abre uma porta para fazer força bruta... É só usar o algoritmo HMAC-SHA1 no dado com senhas diferentes... Se conseguir o mesmo PMKID significa que usou a mesma chave... Logo, você conseguiu senha da rede. ^-^
  12. Hehehe, comigo foi o contrário. Eu já sabia Assembly quando comecei a aprender C, aí eu compilava código em C e lia em Assembly para entender melhor o funcionamento do executável gerado. O próprio GCC oferece opção para apenas compilar o código, sem montar nem linkar ele. Dessa forma é possível ler o Assembly. Só usar: $ gcc programa.c -o programa.asm -S # Com sintaxe da Intel $ gcc programa.c -o programa.asm -S -masm=intel Fica a dica para quem quiser dar uma olhada. É interessante também usar o objdump para ver o programa depois de linkado. Opção -d do objdump faz ele disassemblar a seção de código.
  13. @Leandro Fróes. Posso ter interpretado errado o texto dele, mas eu acho que não era ASLR. Ele disse que quando clicou "RUN" ele foi para o endereço 401000. Eu não sei o motivo e nem sei do que se trata, mas quando eu abro um executável no EDB ele também executa um código antes do entry point. Fica na região de memória do ld-2.26.so então eu imagino que seja alguma inicialização do S.O. Deve ser o mesmo caso no Windows. Mas como eu já disse eu não sei direito do que se trata. Inclusive eu queria perguntar aqui sobre isso e me esqueci.
  14. Olha eu vou te dar a "dica", mas você não vai gostar. 1) Você tem experiência com desenvolvimento de drivers? Se não, começa estudando isso. 2) Depois que ter pelo menos alguns meses de experiência com desenvolvimento de drivers, começa a estudar engenharia reversa. O curso CERO do Papo Binário é um bom começo... Eu imagino que ele já esteja completo quando você terminar de estudar sobre drivers. 3) Após o CERO aprenda Assembly. Mas cuidado para não aprender Assembly da arquitetura errada, existem várias linguagens Assembly... 4) Sabe redes? Se não, estude. Se sim, estude mais ainda. 5) Depois de estudar engenharia reversa e Assembly por pelo menos 1 ano e ter bastante conhecimento sobre redes, você já deve estar apto a tentar fazer a engenharia reversa no driver. Eu diria que ao todo, somando estudos e "mão na massa", você vai levar em torno de 1 ano e meio a 2 anos para encontrar a falha e corrigir ela. Isso se você conseguir, não é algo 100% garantido. Você pode fazer tudo isso ou reportar a falha e esperar uma atualização de correção. Você decide, não quero mandar na sua vida. 😙
  15. Galera, tenho um canal no YouTube faz um tempo(uns 3 meses) e hoje resolvi voltar a publicar vídeos. (parei por 2 meses) Neste vídeo eu resolvi falar sobre automodificação de código na linguagem C em ambientes Linux. Por favor, podem criticar a vontade. Não precisam se conter. Link para o vídeo: https://www.youtube.com/watch?v=mFZ5XcuD7vc
  16. Aproveitando o assunto. O que vocês acham de um curso como o da Desec Security? Estou acompanhando a empresa faz um tempo e eles estão cada vez mais se profissionalizando. E inclusive mais recentemente criaram um certificado próprio(DCPT). Acham que é uma boa para quem quer aprender Pentest?
  17. Seu professor não é chato, fazer um código bem organizado é tão importante quanto fazer um código que funcione. Se for só uma coisa ou outra, não adianta de nada. Tem que ser os dois. Aqui tem umas dicas interessantes. É para C++ porém boa parte é aproveitável para qualquer linguagem. > https://github.com/kelvins/Boas-Praticas-Cplusplus Vale lembrar que nenhuma dica que você achar por ai é uma "verdade absoluta". Cada um tem sua opinião e ponto de vista. Uma dica que eu dou é você sempre seguir o estilo de código recomendado da linguagem de programação. No caso da linguagem C tem o GNU Coding Standards que eu recomendo que você siga. (mas eu mesmo não sigo tudo, só para constar) Outra dica é que você use espaços para indentar o código ao invés de TAB. Eu uso quatro espaços. A maioria, senão todos os editores de código tem opção para converter o TAB em N espaços. Assim você continua com a praticidade de pressionar a tecla TAB porém usando espaços para indentar. O motivo disso é por causa de alinhamento. O tamanho de um TAB pode variar de acordo com editor e fonte utilizados. Um código que parece bem alinhado para você pode estar uma bagunça para outra pessoa. Já com espaços isso não é um problema.
  18. Isso é muito bacana... Eu não conhecia essa função. É de grande utilidade com certeza.
  19. Dependendo do motor do jogo algumas coisas vão mudar. Já brinquei com algumas game engines e elas tinham algo em comum: Uma função para criar uma nova instância e um elemento que serve de "modelo". Vou usar como exemplo uma biblioteca para desenvolvimento de jogos que eu mesmo fiz em JavaScript. Na minha gamelib existem os construtores Model() e Entity(). Model() cria uma espécie de modelo que não existe dentro do ambiente do jogo. É a partir do modelo que definimos coisas como sprites, códigos de execução em eventos etc. Enfim, todas as características que um objeto dentro do jogo precisa ter. Já Entity() cria uma entidade dentro do jogo a partir de um modelo criado por Model(). Essa entidade é "uma cópia" exata do modelo, e a entidade sim existe dentro do jogo... Então na hora que você cria uma entidade a partir do modelo, desde então ela existe no cenário do jogo. Esse conceito é quase o mesmo para qualquer game engine ou gamelib. Então digamos que você queira modificar um jogo para dropar um item qualquer, você precisa de duas coisas: 1) Descobrir qual é o "modelo" daquele objeto. (o nome pode variar de acordo com game engine/biblioteca) 2) Descobrir qual é a função que cria uma nova entidade e como usá-la. Se o jogo foi desenvolvido usando alguma game engine/biblioteca "pública", isto é, que não é exclusiva daquela empresa... Então você pode baixar ela e ler a documentação para descobrir como usar as funções para criar entidades etc. E você pode também fazer testes a partir de um código que você tem controle... Só ir estudando a estrutura do código até encontrar o que procura.
  20. Não é questão de ter faltado detalhes, mas sim os que você deu estavam errados. Mas eu entendo o seu ponto.
  21. Desculpe, mas eu devo fazer três correções. 1) Na verdade o opcode nessa instrução é somente o byte E8. Os outros quatro bytes são um valor imediato. Como uma analogia, pense que o opcode é "o nome da função" e os bytes seguintes são "argumentos" para essa função. Algo como: e8(0x78563412); No artigo da Wikipédia sobre código de máquina eu detalhei como uma instrução em código de máquina da arquitetura x86 é formada. Nele eu especifiquei sobre o formato da instrução e expliquei sobre os prefixos. Em resumo uma instrução de código de máquina pode haver prefixo, opcode, o byte ModR/M, o byte SIB, endereço de deslocamento e um valor imediato. É muito mais do que simplesmente opcode. 2) Na verdade os dados ficam em little-endian, logo a sequência de bytes 12 34 56 78 não formam o valor 0x12345678... Mas sim 0x78563412. 3) Tanto jmps quanto calls "curtos" (near) em código de máquina trabalham com endereços relativos. Então o call E8 12 34 56 78 na verdade não irá chamar uma instrução localizada no endereço 0x78563412... Mas sim executar a instrução 0x78563412 bytes a frente. Um exemplo básico: org 0x100 call teste ret teste: nop ret Trabalhando na sua suposição o call na linha 3 iria ser: E8 06 01 Ou seja, um call para o endereço 0x106. Mas na verdade é E8 01 00 Que seria um call para a instrução 1 byte a frente. (ret tem o tamanho de 1 byte, C3) Apenas calls e jmps "longos" (far) que usam o endereço exato. Os curtos usam endereço relativo. Então por exemplo a instrução call 0x7000:teste iria gerar: 9A 06 01 00 70 Dessa vez sim especificando o endereço exato. (0x106 no segmento 0x7000)
  22. @Leandro Fróes. Posso ter interpretado errado, mas eu trabalhei minha resposta com a teoria que o Luciano acreditava que os dados inseridos em um registrador em algum momento iriam parar na memória RAM. Ele não fez perguntas tipo: "como gravar X em memória?" Ele perguntou algo como: "em que momento isso vai ser gravado na memória?" Ele também perguntou sobre como funcionaria esse fluxo (de dados). Então eu acredito que na cabeça dele, ele pensava que os dados eram salvos "temporariamente" nos registradores e em algum momento o processador ia mover aquilo para a memória RAM. Eu não afirmei o contrário. Creio que houve uma falha na nossa comunicação.
  23. Resposta curta: Nunca Acho que você não entendeu o que é um registrador. Um registrador é uma pequena área de memória que fica dentro do processador, ela pode ser acessada de maneira muito mais rápida do que a memória RAM. (por causa do hardware) Quando você define o valor de um registrador está mudando o valor daquele registrador e apenas isso, o processador não vai "enviar" os dados nos registradores para a memória RAM. O registrador não é tipo um cache para acessar a RAM. Você deve ter se confundido porque deve ter lido algo sobre os registradores serem usados para armazenar dados temporariamente. Essa explicação se dá porque é muito mais rápido se você carregar os dados da memória e jogar em um registrador, trabalhar com esses dados no registrador e depois você jogar os dados de volta na RAM. O processador não vai fazer isso magicamente, é só um "truque" que o programador faz para otimizar tarefas de loop, por exemplo. Se quiser aprender sobre as instruções da linguagem Assembly, eu recomendo usar como referência o site c9x.me. Ele não tem um sistema de pesquisa mas você pode usar o Google para encontrar referências das instruções. Exemplo de pesquisa: site:c9x.me mov Com a pesquisa acima podemos encontrar a referência da instrução mov. No site tem uma explicação sobre a instrução e até mesmo um pseudo-código para explicar o que exatamente a instrução faz.
  24. Eu pesquisei no Google: msdn registry E encontrei isso: Registry Functions (Windows) É só procurar na MSDN que você encontra toda a documentação da API do Windows. Lá não apenas explica como usar como também exibe um exemplo de uso.
×
×
  • Create New...