teuzero Posted January 27, 2024 at 10:59 PM Share Posted January 27, 2024 at 10:59 PM Montei um crypter x64 pequeno basico dos basicos usando nasm, eu gosto de estudar assembly, C/C++. É um "Process hollowing" na gambiarra, mais é só para estudo mesmo. Está no github: https://github.com/TeuZero/BasicCrypterX64-NASM Eu tenho um blog onde posto algumas coisas, https://c0d3r3d.blogspot.com/ Gosto muito de aprender com outras pessoas. Quem quiser dar uma olhada no código, comentar, sugestões, criticas, fiquem avontade, estou aqui para aprender. Muito Obrigado. 2 Quote Link to comment Share on other sites More sharing options...
fredericopissarra Posted January 31, 2024 at 04:53 PM Share Posted January 31, 2024 at 04:53 PM Entendi que a intenção é ofuscar o código ao máximo, mas, mesmo assim, acho que têm algumas coisas que podem ser melhoradas: Em primeiro lugar, se for criar sessões que não sejam "de sistema" como ".bss" (e não ".BSS"), ".data", ".rdata" e ".text", não use o "." inicial. Sessões que não sejam "de sistema" não deveriam ter esse "." inicial. Constantes, ou seja, valores em memória que não serão modificados no decorrer do código, deveriam estar na sessão ".rdata" e não em ".data". Se for usar estruturas (struc) e blocos de dados inicializados com essas estruturas, os elementos não especificados são, automaticamente, inicializados com zeros. Por exemplo, o label ctx poderia ser definido como: ; Coloque em .bss para não colocar dados da imagem binária do executável final. ; O loader automaticamente zera toda a sessão .bss. section .bss ctx: istruc CONTEXT ; Aqui, TODOS os membros serão zerados. iend A mesma coisa (zerar automaticamente) acontece com "variáveis" comuns... Por exemplo: section .bss ; De novo, colocando em .bss para evitar fazer uma grande imagem binária... ; addressAlloc times 8 dq 0 ; TamArqProgram times 8 dq 0 ; TamArqTarget times 8 dq 0 ; Serão zerados, automaticamente. addressAlloc: resq 8 TamArqProgram: resq 8 TamArqTarget: resq 8 Para zerar um registrador R?? qualquer não é necessário usar R?? na instrução, mas apenas a versão E??. Como em: xor edx, edx ; Zera todo o RDX (sem o prefixo REX). A mesma coisa acontece se tivermos que inicializar um registrador com valores menores que 2³²-1: mov eax,1 ; Opcode: B8 01 00 00 00 mov rax,1 ; Opcode: 48 B8 01 00 00 00 00 00 00 00 O NASM tende a otimizar isso, nesse caso específico, mas é bom sempre usar E?? ao invés de R??, se for o caso. Um endereço efetivo (no formato [offset]), numa instrução, é ABSOLUTO e exige um fixup para relocação. No entanto, no modo x86-64, temos endereçamentos relativos ao registrador RIP, que elimina a relocação. Adicione a diretiva default rel no seu código. Evite recarregar o mesmo dado mais de uma vez, como em: mov rcx,[alloc] mov rax,[alloc] Mesmo que: mov rcx,[alloc] mov rax,rcx Insira uma dependência no stream das instruções (a inicialização de RAX depende de RCX)... Isso ainda é mais rápido (e menor) que DOIS acessos à memória. Pra quê tanta manipulação da pilha se funções da Win32 API para x86-64 tendem a NÃO usar a pilha para receber argumentos? Tenha em mente, também, que RBX, RBP e de R12 até R15 são preservados pela função chamada (no caso da Win32 API) e suas funções deveriam preservá-las também... Lembre-se que todo endereço efetivo no modo x86-64 é de 64 bits. Essas instruções estão erradas: xor rbx,rbx ; Pra quê? mov ebx,[eax] ; RBX inteiro é inicializado e EAX está errado aqui. Chamadas indiretas, como em call r12, sofrem do problema de branch misprediction de forma muito mais fácil do que uma chamada direta. O processador mantém a estatística das chamadas... se a chamada anterior for "para frente" e R12 aponta "para trás", alguns ciclos de penalidade serão usados para recarregar o cache L1I... []s Fred 1 Quote Link to comment Share on other sites More sharing options...
teuzero Posted February 6, 2024 at 12:55 PM Author Share Posted February 6, 2024 at 12:55 PM Muito Obrigado, só tenho que agradeçer! default rel não entendi. " mov ebx,[eax] ; RBX inteiro é inicializado e EAX está errado aqui." eax seria, algo como dword [rax+offset]? prefixo R é melhor ne ai não fica dados no registrador. Quote Link to comment Share on other sites More sharing options...
fredericopissarra Posted February 8, 2024 at 12:46 PM Share Posted February 8, 2024 at 12:46 PM Em 06/02/2024 em 09:55, teuzero disse: default rel não entendi. " mov ebx,[eax] ; RBX inteiro é inicializado e EAX está errado aqui." eax seria, algo como dword [rax+offset]? prefixo R é melhor ne ai não fica dados no registrador. Coisas como: section .bss var: resd 1 section .text ... mov eax,[var] Aqui var é um offset no endereço efetivo [var]. Como o NASM está usando um endereçamento não relativo a RIP o linker precisará colocar um fixup, uma entrada de relocação. Um jeito de evitar isso no modo x86-64 é usando mov eax,[rel val], dizendo ao NASM que o offset é relativo a RIP. Mas é melhor dizer a ele que TODOS os endereços desse tipo são relativos a RIP via diretiva default rel. A vantagem do modo x86-64 e os endereços relativos a RIP é que bem menos entradas na tabela de relocação são necessárias no executável final... Note que endereços relativos a RIP têm apenas o offset, nunca outro registrador... Algo como [rax+4] ou [rbx+4*rax+4] não são relativos a RIP. Com relação ao mov ebx,[eax], note que no modo x86-64 os endereços têm sempre 64 bits de tamanho. Ao usar [eax] você está, explicitamente, extirpando os 32 bits superiores de um endereço. Endereços devem usar registradores R??, assim, o correto ali seria mov ebx,[rax]. Note que, aqui, RBX será inicializado e os 32 bits superiores serão, automaticamente, zerados, tornando o xor rbx,rbx anterior desnecessário. Além disso, ao usar EAX num endereço efetivo o NASM é obrigado e acrescentar um prefixo 0x67 na sua instrução. Evite usar endereços de 32 bits num código de 64. Mesmo que não seja para endereços... Por exemplo, suponha que eu queira multiplicar EAX (de 32 bits) por 5. Isso pode ser facilmente feito com lea eax,[rax+4*rax]. Note que o destino é EAX, mas o endereço efetivo é RAX+4*RAX. O endereço efetivo é calculado em 64 bits (como deve ser), mas a atribuição final estirpará os 32 bits superiores (e os zerará). PS: Yep... multiplicar EAX por 10 é tão simples quanto: lea eax,[rax+4*rax] ; ou '5*rax', o NASM entende que isso é 'rax+4*rax' add eax,eax Desde que estejamos trabalhando com valores sem sinal. 1 Quote Link to comment Share on other sites More sharing options...
teuzero Posted February 9, 2024 at 08:14 AM Author Share Posted February 9, 2024 at 08:14 AM Muito obrigado pela correção e pela dica.😃 Quote Link to comment Share on other sites More sharing options...
teuzero Posted February 9, 2024 at 08:18 AM Author Share Posted February 9, 2024 at 08:18 AM A final começei lendo seu livro de assembly , mais não terminei! está escrito tudo certinho.. ta de parabéns! vlw! Quote Link to comment Share on other sites More sharing options...
teuzero Posted February 9, 2024 at 12:22 PM Author Share Posted February 9, 2024 at 12:22 PM Em 08/02/2024 em 09:46, fredericopissarra disse: Coisas como: section .bss var: resd 1 section .text ... mov eax,[var] Aqui var é um offset no endereço efetivo [var]. Como o NASM está usando um endereçamento não relativo a RIP o linker precisará colocar um fixup, uma entrada de relocação. Um jeito de evitar isso no modo x86-64 é usando mov eax,[rel val], dizendo ao NASM que o offset é relativo a RIP. Mas é melhor dizer a ele que TODOS os endereços desse tipo são relativos a RIP via diretiva default rel. A vantagem do modo x86-64 e os endereços relativos a RIP é que bem menos entradas na tabela de relocação são necessárias no executável final... Note que endereços relativos a RIP têm apenas o offset, nunca outro registrador... Algo como [rax+4] ou [rbx+4*rax+4] não são relativos a RIP. Com relação ao mov ebx,[eax], note que no modo x86-64 os endereços têm sempre 64 bits de tamanho. Ao usar [eax] você está, explicitamente, extirpando os 32 bits superiores de um endereço. Endereços devem usar registradores R??, assim, o correto ali seria mov ebx,[rax]. Note que, aqui, RBX será inicializado e os 32 bits superiores serão, automaticamente, zerados, tornando o xor rbx,rbx anterior desnecessário. Além disso, ao usar EAX num endereço efetivo o NASM é obrigado e acrescentar um prefixo 0x67 na sua instrução. Evite usar endereços de 32 bits num código de 64. Mesmo que não seja para endereços... Por exemplo, suponha que eu queira multiplicar EAX (de 32 bits) por 5. Isso pode ser facilmente feito com lea eax,[rax+4*rax]. Note que o destino é EAX, mas o endereço efetivo é RAX+4*RAX. O endereço efetivo é calculado em 64 bits (como deve ser), mas a atribuição final estirpará os 32 bits superiores (e os zerará). PS: Yep... multiplicar EAX por 10 é tão simples quanto: lea eax,[rax+4*rax] ; ou '5*rax', o NASM entende que isso é 'rax+4*rax' add eax,eax Desde que estejamos trabalhando com valores sem sinal. é eu vi que a realocação tem muitas coisas desnecessária, eu vi que virtualprotectex não esta vindo o tamanho da seção certa nem as permisssões... de certo é só fazer o va dos endereços e copiar os códigos para o endereço e dar permissão. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.