Jump to content

paulosgf

Supporter - Byte
  • Posts

    22
  • Joined

  • Last visited

  • Days Won

    7

paulosgf last won the day on March 1

paulosgf had the most liked content!

About paulosgf

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

paulosgf's Achievements

19

Reputation

  1. Introdução Olá pessoal! Este é mais um artigo da série onde falo sobre técnicas de anti-engenharia reversa. Neles compartilho um pouco dos tutoriais práticos que desenvolvi ao longo das minhas pesquisas e estudos. No primeiro artigo, falei sobre a categoria de anti-debugging TLS Callback. Agora vou falar um pouco sobre as técnicas da categoria que utilizam flags de depuração do próprio processo para detectar o debugger. Antes de mais nada, vale entender o que são essas flags. O que são flags de depuração? Estas flags são valores presentes dentro do espaço de endereço do processo, permanecendo na memória até o encerramento deste programa. A presença e/ou determinado valor destes bits podem indicar que o processo está em um ambiente de depurador. Como estas técnicas são válidas apenas para o ambiente Windows, seu significado e valores são definidos na documentação oficial do SO (WinAPI) [1], entretanto algumas flags podem não ser documentadas e, neste caso, somente se encontra informação em livros como o Windows Internals [2]. A primeira que vamos analisar é a NtGlobalFlag, desenvolvida na época do antigo Windows NT. Esta flag é definida no cabeçalho do Process Environment Block (PEB) de todos processos no sistema e é usada especificamente para indicar se o processo está executando ou não em depurador. Antes de aprofundar nas particularidades desta flag, precisamos entender o que é a PEB, que foi citada anteriormente. De forma resumida, é uma estrutura que existe em cada processo do Windows e é apontada por outra estrutura chamada TEB (Thread Environment Block), que por sua vez contém informações em relação às threads do processo. O código da PEB é composto por uma struct e sua versão para 32 bits no header windows.h ou winternl.h é a seguinte: typedef struct _PEB { BYTE Reserved1[2]; BYTE BeingDebugged; BYTE Reserved2[1]; PVOID Reserved3[2]; PPEB_LDR_DATA Ldr; PRTL_USER_PROCESS_PARAMETERS ProcessParameters; PVOID Reserved4[3]; PVOID AtlThunkSListPtr; PVOID Reserved5; ULONG Reserved6; PVOID Reserved7; ULONG Reserved8; ULONG AtlThunkSListPtr32; PVOID Reserved9[45]; BYTE Reserved10[96]; PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine; BYTE Reserved11[128]; PVOID Reserved12[1]; ULONG SessionId; } PEB, *PPEB; *O 3º byte desta estrutura é o campo BeingDebugged e este é definido pelo Windows para todo binário que está sendo executado, sendo 0 (padrão) para indicar que o processo não está sendo depurado e 1 para depurado. Este byte é avaliado na técnica abordada no Curso de Engenharia Reversa Online (CERO) através da função IsDebuggerPresent() e por conta disto esta técnica não será abordada neste artigo. A Aula 24 - Anti-debug, do CERO, aborda a PEB de forma prática. Uma referência completa sobre a PEB também pode ser acessada em [3]. NtGlobalFlag Esta flag também é um campo da PEB e se encontra no offset 0x68 em ambientes 32 bits, ou offset 0xBC em 64 bits, contados a partir do seu início. Esta flag é usada para rastreamento e depuração de programas. Seu valor é 0 por padrão, indicando que não há depurador no ambiente. Caso se inicie o programa primeiro e depois de já iniciado, se analise seu processo no depurador, os campos da NtGlobalFlag não são alterados, deixando esta abordagem sem efeito. No entanto, caso se execute o programa diretamente em um depurador, as seguintes flags serão definidas: FLG_HEAP_ENABLE_TAIL_CHECK (0x10) FLG_HEAP_ENABLE_FREE_CHECK (0x20) FLG_HEAP_VALIDATE_PARAMETERS (0x40) A presença de um depurador no ambiente é confirmada ao se verificar o valor 0x70 resultante da operação OR entre estas flags. Disassemble de processo 32 Bits: mov eax, fs:[30h] // acessa a PEB no registrador de segmento mov al, [eax+68h] // a partir da PEB acessa a flag NtGlobalFlag and al, 70h // soma as flags FLG_HEAP_* cmp al, 70h // verifica se o valor da soma é 0x70 call being_debugged // caso positivo, avisa sobre depuração Disassemble de processo 64 Bits: mov rax, gs:[60h] // acessa a PEB no registrador de segmento mov al, [rax+BCh] // a partir da PEB acessa a flag NtGlobalFlag and al, 70h cmp al, 70h call being_debugged Disassemble de processo WOW64 (64 Bits que aceita também 32 bits): mov eax, fs:[30h] // acessa a PEB no registrador de segmento mov al, [eax+10BCh] // a partir da PEB acessa a flag NtGlobalFlag and al, 70h cmp al, 70h call being_debugged Código de exemplo: #include <windows.h> #include <iostream> using namespace std; // define o valor das flags #define FLG_HEAP_ENABLE_TAIL_CHECK 0x10 #define FLG_HEAP_ENABLE_FREE_CHECK 0x20 #define FLG_HEAP_VALIDATE_PARAMETERS 0x40 // executa a operação OR entre o valor de todas as flags #define NT_GLOBAL_FLAG_DEBUGGED (FLG_HEAP_ENABLE_TAIL_CHECK | FLG_HEAP_ENABLE_FREE_CHECK | FLG_HEAP_VALIDATE_PARAMETERS) // se não for 64 bits #ifndef _WIN64 PVOID pPeb = (PVOID)__readfsdword(0x30); // PEB DWORD dwNtGlobalFlag = *(PDWORD)((PBYTE) pPeb + 0x68); // flag #else // o mesmo procedimento para 64 bits PVOID pPeb = (PVOID)__readgsqword(0x60); DWORD dwNtGlobalFlag = *(PDWORD)((PBYTE)pPeb + 0xBC); #endif // _WIN64 // função que acusa depurador e sai void being_debugged() { std::cout << "Stop debugging program!" << std::endl; exit(-1); } int main() { // se em ambiente de depurador chama função de saída if (dwNtGlobalFlag & NT_GLOBAL_FLAG_DEBUGGED) being_debugged(); eles // senão segue normalmente std::cout << "Hello World!\n"; return 0; } Desabilitando a Técnica Para desviar desta verificação, basta definir o campo NtGlobalFlag da estrutura PEB com 0 antes que este valor seja verificado pela proteção anti-depuração. Abaixo são alguns exemplos de como zerar esta flag: No x64dgb com o plugin ScyllaHide: Plugins → ScyllaHide → Options -> NtGlobalFlag [x] Manualmente no x64dbg: 1. Ctrl+G → peb()+0x68 (32 bits) ou Ctrl+G → peb()+0xBC (64 bits) 2. Trocar o valor do byte de 0x70 para zero No OllyDbg com o plugin Command Line: 1. Plugins → Command Line → Command Line 2. dump fs:[30]+0x68 (32 bits) ou dump gs:[60]+0xBC (64 bits) 3. No painel dump de memória selecionar o 1º byte → Binary → Fill with 00's *Também é possível usar o plugin Olly-Advanced Bom, iniciei aqui uma nova categoria de técnicas Anti-Debug, a das flags de depuração, iniciando com a flag NtGlobalFlag. No próximo artigo vou falar sobre uma técnica que usa 2 flags em conjunto, a Heap Flags e a ForceFlags. Espero que tenham gostado. Forte abraço & até o próximo! Referências: [1] https://docs.microsoft.com/en-us/windows/win32/apiindex/windows-api-list [2] https://docs.microsoft.com/en-us/sysinternals/resources/windows-internals [3] https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/pebteb/peb/index.htm
  2. Galera, segue minha resolução do desafio nível "Fácil" Primeiramente, verificamos no DiE que se trata de um crackme em Delphi que esconde a WinAPI debaixo de várias camadas de sua biblioteca própria, a Visual Component Library (VCL) usada em formulários e que só então chama as funções Windows em nível mais baixo. Então temos de usar um decodificador dos componentes do formulário Delphi, como o Interactive Delphi Reconstructor (IDR). [1] Abrir o programa no IDR -> File -> Load File -> Autodetect Version Localizar a main() do Delphi, classe TMain: IDR -> Forms (F5) -> Text -> TMAIN {main} -> (duplo clique) Localizar o objeto TButton e seu evento OnClick relacionado conforme imagem: Há também o Delphi Decompiler (DeDe), que pode ser mais prático para alguns do que o IDR. [2] Configurar o IDA Free com a assinatura FLIRT da VCL, pois facilita muito a identificação dos componentes. Certifique-se de que a assinatura FLIRT mssdk32 seja adicionada também. Vide em [3]. O Delphi usa a convenção de chamada fastcall, porém é diferente da chamada fastcall do Windows. Os parâmetros são passados nesta ordem: EAX, EDX, ECX, os outros na pilha. Para classes, o ponteiro this é passado em EAX. Quando um construtor é chamado, a estrutura da classe é passada em EAX e o ponteiro this é também retornado em EAX. Configurar o IDA: Options -> Compiler -> Compiler = Delphi Calling convention = Fastcall O Delphi usa strings no estilo Pascal. O comprimento da string é especificado no início da string. As strings armazenadas no arquivo binário estão no formato estilo C, primeiro precisam ser convertidas chamando o método LStrAsg(). Novamente configurar o IDA em: Options -> String Literals -> Manage Defaults -> Default String Literal Type = Delphi Agora buscar o evento Button4Click que aparece como TMain_Button4Click, visto que é derivado da TMain. Ele começa no offset 0x0043B374. Recebe a entrada no campo TMain.Edit4:TEdit através da função @TControl@GetText. Em seguida calcula o tamanho da entrada com a função @@LStrLen. O Algoritmo é meio confuso, conforme se vê na seção codificação, então simulei ele em um script python para facilitar o entendimento... #!/usr/bin/env python3 txt = input("no que estou pensando... ") # Entrada laco = 16 # 16 passadas acumul = 0 # Variável temporaria que acumula codificação de cada char da entrada cod = 0 # Saída codificada while (laco > 0): print("LACO: " + str(laco)) # Informa nova passada tam = len(txt) # Tamanho da entrada contador = 1 # Conta cada char da entrada for n in range(0,tam): # Laço percorrendo toda a entrada ch = ord(txt[n]) print(chr(ch) + ": " + hex(ch)) # Char e seu valor ASCII resto = contador + acumul ch = ch + resto acumul = ch print("acumulador: " + hex(acumul)) # Char codificado nesta volta cod += cod cod = cod ^ acumul ch = acumul resto = ch % contador resto += 1 print("resto: " + hex(resto)) # Resto da divisão entre char e contador ch = int(ch / contador) print("quociente: " + hex(ch)) # Quociente da divisão cod += int(resto) # Valor codificado da entrada nesta volta print("codificacao: " + "0x{}".format(hex(cod)[len(hex(cod))-8:len(hex(cod))])) contador += 1 print("contador: " + hex(contador)) # Incrementa contador de char tam -= 1 print("tamanho: " + hex(tam)) # Decrementa tamanho da entrada print("\n") laco -= 1 # Decrementa uma volta no laço ... porém como é um algoritmo de Hash, não é reversível (one-way-hash). Sem as variáveis resto da divisão e, principalmente o tamanho da entrada original, não há como recuperá-la. Talvez se analisando o incremento a cada passada do hash dentro do espaço de endereçamento do algoritmo seja possível deduzir o tamanho, mas ai foge do escopo "Fácil", proposto pelo desafio. A codificação é feita em cada char num laço repetido 16 vezes. Em seguida vem a 1ª validação comparando com o valor 0x3810. No x64dbg, iniciar com um breakpoint no endereço de inicio da rotina de codificação, em 0x0043B3C3. Dar F9. Após o algoritmo de codificação, há 4 testes nos endereços 0x0043B3FA, 0x0043B402, 0x0043B428, 0x0043B452. Definir um breakpoint em cada um e remover o do inicio do algoritmo de codificação. Dar novamente F9, parando no 1º teste. Executar a instrução de comparação com F8 e, em seguida na instrução de salto, habilidar a Zero Flag (ZF=1) com duplo-clique. Repetir os mesmos passos para os demais testes até chegar ao final, onde qualquer senha é aceita. []'s [1] https://github.com/crypto2011/IDR [2] https://www.softpedia.com/get/Programming/Debuggers-Decompilers-Dissasemblers/DeDe.shtml#download [3] https://www.sawatzki.de/download.htm
  3. Introdução Reunir informações sobre técnica de "Anti- Engenharia Reversa" é a proposta dessa série de artigos que serão publicados aqui no Mente Binária. É um tema em que tenho pesquisado e criado pequenos tutoriais práticos para mim mesmo durante o último ano, e resolvi compartilhá-los. Em cada um deles darei uma passada nas principais técnicas encontradas em todas as classes de defesa dos criadores de malware, além de mostrar como desabilitá-las. Tudo com exemplos de código para se entender na prática mesmo. *Para a implementação estarei utilizando o Visual Studio Community, pois este tem o suporte ao ambiente C Runtime (CRT) necessário. A grande maioria das técnicas que serão apresentadas é para o ambiente Windows. Alguns poucos casos, que serão informados, são para Linux. Como pré-requisito, é necessário algum conhecimento de linguagem C\C++, um pouco de Assembly e principalmente Engenharia Reversa. Todos estes tópicos são abordados nos cursos gratuitos no canal do Mente Binária no Youtube. *No caso, não apresentarei a técnica mais simples/comum que utiliza a função IsDebuggerPresent(), pois esta técnica é explicada na última aula do curso do CERO aqui do Mente Binária. Classes de Anti-Engenharia Reversa Essas classes são uma forma de categorizar os métodos de "Anti-Engenharia Reversa", agrupando métodos de evasão semelhantes num mesmo grupo ou “classe”. Como não existe uma classificação oficial, estas classes estão baseadas na divisão apresentada nesta referência, com algumas adaptações. TLS Callback Windows: A Thread Local Storage (TLS) é um recurso do Windows para definir objetos (variáveis) na memória que possam ter valores diferentes para cada thread criada por um processo. Por exemplo, ao imprimir um documento, uma thread se encarrega de mostrar o documento na tela, enquanto outra acessa esta mesma informação simultaneamente para lidar com a impressão. A thread de impressão pode ter uma variável que armazena a quantidade de páginas impressas, porém, esta variável não se faz necessária na thread que apresenta o documento na tela. Esta informação é armazenada (daí o nome "Thread Local Storage") numa região definida no cabeçalho dos binários PE (Portable Executable), e o acesso só é permitido para sua respectiva thread. As funções TLS Callback executam antes do binário alcançar seu "ponto de início", chamando e inicializando estes objetos TLS através de métodos construtores e os removendo da memória por métodos destrutores após seu uso. Com isso em mente, as TLS Callbacks também podem ser utilizadas pelos desenvolvedores de malware para permitir que se execute código antes que o binário chegue à região tradicional de início do programa, conhecido como Entrypoint, que irá levar em seguida à função main(). Isto cria diversas oportunidades como por exemplo executar código malicioso antes que o depurador possa detectá-lo (levar o malware a encerrar o processo antes mesmo de executar as rotinas maliciosas ao perceber que está sendo depurado, por exemplo). Alguns exemplos de malwares que empregam TLS Callbacks no Windows são: Nadnadzzz botnet de 2009; Grum botnet de 2008, através do Grum rootkit; Ursnif (mais recente); Implementação A biblioteca C Runtime (CRT) do Visual Studio provê suporte para fácil criação de TLS Callbacks (como comentado aqui) graças ao código em “C:\Program Files (x86)\Microsoft Visual Studio xx.0\VC\crt\src\tlssup.c”, que cria um diretório de dados TLS baseado na seguinte estrutura: typedef struct _IMAGE_TLS_DIRECTORY32 { DWORD StartAddressOfRawData; /* Início da seção TLS AddressOfRawData*/ DWORD EndAddressOfRawData; /* Endereço final na seção TLS */ DWORD AddressOfIndex; /* Índice da seção TLS */ DWORD AddressOfCallBacks; /* Ponteiro para o array de funções callback */ DWORD SizeOfZeroFill; DWORD Characteristics; } __tls_used; Para criarmos a nossa própria callback precisamos primeiro defini-la no seguinte formato: VOID WINAPI tls_callback1( PVOID DllHandle, DWORD Reason, PVOID Reserved) { codigo_funcao; } As constantes para DWORD Reason podem ser: DLL_PROCESS_DETACH = 0 DLL_PROCESS_ATTACH = 1 DLL_THREAD_ATTACH = 2 DLL_THREAD_DETACH = 3 Depois a callback precisa ser alocada da seguinte forma: PIMAGE_TLS_CALLBACK ponteiro_tls_callback = tls_callback1; Após esta contextualização do mecanismo de funcionamento, segue um exemplo de código onde a função de callback detecta o ambiente de depuração através da função IsDebuggerPresent() e sai informando que o mesmo foi descoberto. Caso contrário, informa que o programa está executando normalmente: #include <iostream> #include <windows.h> using namespace std; // Declara uma variável global requerida para a chamada TLS Callback static int v1 = 0; // Declara a callback VOID WINAPI tls_callback1( PVOID DllHandle, DWORD Reason, PVOID Reserved) { if (Reason == DLL_PROCESS_ATTACH) { v1 = 1; // dentro da Callback altera o valor da variável if (IsDebuggerPresent()) { cout << "Stop debugging program!" << endl; TerminateProcess(GetCurrentProcess(), 0x1); exit(1); } } } // Cria objeto conforme a arquitetura através de #pragmas, que são instruções específicas do compilador #ifdef _M_AMD64 // para arquitetura x86_64 #pragma comment (linker, "/INCLUDE:__tls_used") // instrui linker usar o diretório TLS #pragma comment (linker, "/INCLUDE:p_tls_callback1") // instrui linker usar ponteiro do mesmo tipo da callback tls_callback1 declarada antes #pragma const_seg(push) // carrega o ponteiro na stack do compilador para uso da callback no segmento de dados const (.rdata) #pragma const_seg(".CRT$XLA") // cria nova seção TLS EXTERN_C const PIMAGE_TLS_CALLBACK p_tls_callback1 = tls_callback1; // atribui tls_callback1 ao ponteiro p_tls_callback1 #pragma const_seg(pop) // remove o ponteiro da stack após o uso #endif // fim deste bloco #ifdef _M_IX86 // para a arquitetura x86. as instruções tem as mesmas finalidades do bloco anterior #pragma comment (linker, "/INCLUDE:__tls_used") #pragma comment (linker, "/INCLUDE:_p_tls_callback1") #pragma data_seg(push) #pragma data_seg(".CRT$XLA") EXTERN_C PIMAGE_TLS_CALLBACK p_tls_callback1 = tls_callback1; #pragma data_seg(pop) #endif // main() só será executada depois, quando um depurador não for detectado int main(int argc, char* argv[]) { cout << "Normal execution!" << endl; printf("test value from tls callback is: tls = %d\n", v1); return 0; } Abaixo estão algumas dicas de como lidar com TLS Callbacks em diversas ferramentas: x64dbg: Options → Preferencias → Eventos → TLS Callbacks OllyDbg: Options -> Debug Options -> Events -> Make first pause at -> System breakpoint (para em TLS Callbacks) OllyDbg: Plugin Olly Advanced → Break on TLS Callback IDA Pro: Ctrl+E → Choose an entry point IDA Pro (Debugger): Debugger -> Debugger options -> Events -> Stop on (marcar todas as opções) Linux: O Linux também suporta o Thread Local Storage (TLS), conforme descrito nestes 2 excelentes artigos: https://maskray.me/blog/2021-02-14-all-about-thread-local-storage https://chao-tic.github.io/blog/2018/12/25/tls No entanto, o recurso de TLS no Linux aparentemente só permite a inicialização de variáveis ou objetos, e não chamada de função (como é o caso no Windows). Isto somente seria possível de dentro da main(), como nesse pequeno exemplo que usa o recurso de suporte a threads da Glibc: #include <stdio.h> // TLS que define e inicializa a variável __thread int main_tls_var = 2; int main() { printf("%d\n", main_tls_var); return 0; } $ ./tls 2 No entanto, ainda é possível executar funções antes ou depois da main() no Linux através das funções construtoras e destrutoras devido ao suporte do GCC. Observem que uma função construtora em C++ instancia uma variável, definindo seu valor, enquanto que a destrutora a remove da memória após a conclusão da execução (normalmente em retorno de funções): #include<stdio.h> /* atributo construtor em myStartupFun() para executar antes de main() */ void myStartupFun (void) __attribute__ ((constructor)); /* atributo destrutor em myCleanupFun() para executar depois de main() */ void myCleanupFun (void) __attribute__ ((destructor)); /* implementacao de myStartupFun */ void myStartupFun (void) { printf ("startup code before main()\n"); } /* implementacao de myCleanupFun */ void myCleanupFun (void) { printf ("cleanup code after main()\n"); } int main (void) { printf ("hello\n"); return 0; } *Por padrão a __libc_start_main(*main(), argc, **argv, __libc_csu_init(), __libc_csu_fini()) é a função da glibc que chama a main(), onde __libc_csu_init é o construtor e __libc_csu_fini é o destrutor. No Linux também é possível evadir o depurador através da função ptrace, mas isso é um assunto que abordarei mais para frente, inclusive com exemplo prático. Por hora convido vocês a continuarem sintonizados, pois no próximo artigo começarei a tratar de técnicas da classe que utiliza flags de depuração para detectar o debugger. Forte abraço & até lá! Referências: https://www.codeproject.com/Articles/1090943/Anti-Debug-Protection-Techniques-Implementation-an https://anti-debug.checkpoint.com/ https://rayanfam.com/topics/defeating-malware-anti-vm-techniques-cpuid-based-instructions/ https://evasions.checkpoint.com/ https://anti-reversing.com/Downloads/Anti-Reversing/The_Ultimate_Anti-Reversing_Reference.pdf
  4. Obrigado @anderson_leite! Essa dica final me deu uma direção a tomar daqui em diante. Vi que tu estavas sem tempo, porque tá elaborando um treinamento justamente sobre o radare2. Vou acompanhar para melhorar minhas habilidades nisso. ? Abraços!
  5. Bom, isso foi uma análise feita a partir da força bruta, então teve um certo nível de ajuda para chegar na solução. Por acaso tu terias alguma estratégia de raciocínio que leve a solução sem força bruta? Abraços!
  6. A função de decodificação funciona da seguinte maneira:
  7. A partir dai tentei entender como o algoritmo funciona e achei o seguinte:
  8. Obrigado pelo apoio amigo! Pois então... eu já havia dado uma olhada prévia com o radare2, mas acho que a analise dinâmica sempre dá mais retorno. De qualquer forma não consegui evoluir mais e parti para a força bruta, criei esse script aqui:
  9. Este valor 5 do último dígito eu deduzi a partir dos valores encontrados subsequentemente ao endereço carregado em EAX: Entretanto, os valores dos outros dígitos não consegui achar... Tu tens alguma dica?
  10. Quando inciamos com um número de 3 dígitos e executamos o binário até retornar desta última chamada, ele volta para a main() e segue para o algorítimo principal.
  11. Agora a solução do crackme em si, começando pelas suas verificações.
  12. Oi anderson_leite! Cara, crackme difícil esse, heim!? O fato de não ter símbolos já dificulta bastante. Eu consegui superar essa fase, mas não estou entendendo a lógica dele. Mas vamos ver até onde cheguei: O binário não tem símbolos. paulo@nirvana:~/crack/crackmes$ nm ./callme nm: ./callme: nenhum símbolo Iniciei sem argumentos para ver o que ele retorna e depois tentei uma string aleatória como entrada. paulo@nirvana:~/crack/crackmes$ ./callme Usage: ./callme key paulo@nirvana:~/crack/crackmes$ ./callme foo Try harder paulo@nirvana:~/crack/crackmes$ Agora no debugger. Uso a extensão GEF no GDB, que facilita bastante o trabalho, retornando o estado dos registradores, Stack, linhas de código executadas e à executar em a cada instrução. Recomento! paulo@nirvana:~/crack/crackmes$ gdb --args ./callme foo GEF for linux ready, type `gef' to start, `gef config' to configure 96 commands loaded for GDB 9.2 using Python engine 3.8 [+] Configuration from '/home/paulo/.gef.rc' restored [+] 32 extra commands added from '~/crack/GDB/gef-scripts/scripts' Voltron loaded. Reading symbols from ./callme... (No debugging symbols found in ./callme) gef➤ break main Function "main" not defined. gef➤ info file Symbols from "/home/paulo/crack/crackmes/callme". Local exec file: `/home/paulo/crack/crackmes/callme', file type elf64-x86-64. Entry point: 0x10a0 0x00000000000002a8 - 0x00000000000002c4 is .interp 0x00000000000002c4 - 0x00000000000002e4 is .note.ABI-tag 0x00000000000002e8 - 0x0000000000000310 is .gnu.hash 0x0000000000000310 - 0x0000000000000448 is .dynsym 0x0000000000000448 - 0x00000000000004f6 is .dynstr 0x00000000000004f6 - 0x0000000000000510 is .gnu.version 0x0000000000000510 - 0x0000000000000530 is .gnu.version_r 0x0000000000000530 - 0x0000000000000698 is .rela.dyn 0x0000000000000698 - 0x0000000000000728 is .rela.plt 0x0000000000001000 - 0x0000000000001017 is .init 0x0000000000001020 - 0x0000000000001090 is .plt 0x0000000000001090 - 0x0000000000001098 is .plt.got 0x00000000000010a0 - 0x0000000000001441 is .text 0x0000000000001444 - 0x000000000000144d is .fini 0x0000000000002000 - 0x0000000000002080 is .rodata 0x0000000000002080 - 0x00000000000020cc is .eh_frame_hdr 0x00000000000020d0 - 0x0000000000002218 is .eh_frame 0x0000000000003de8 - 0x0000000000003df0 is .init_array 0x0000000000003df0 - 0x0000000000003df8 is .fini_array 0x0000000000003df8 - 0x0000000000003fd8 is .dynamic 0x0000000000003fd8 - 0x0000000000004000 is .got 0x0000000000004000 - 0x0000000000004048 is .got.plt 0x0000000000004060 - 0x0000000000004118 is .data 0x0000000000004120 - 0x0000000000004130 is .bss gef➤ break *0x10a0 Ponto de parada 1 at 0x10a0 gef➤ run Starting program: /home/paulo/crack/crackmes/callme foo Warning: Cannot insert breakpoint 1. Não é possível acessar a memória no endereço 0x10a0 gef➤ start [*] gdb is already running gef➤ Como o programa não resolve símbolos, não acha a função main() e nem mesmo o entrypoint. Nestes casos é preciso um recurso novo do GDB 7.x.
  13. Salve galera! Primeiramente quero agradecer a mais este exemplar para estudo de ER. Me ensinou conceitos em que eu só tinha vagamente ouvido falar. Realmente abriu a mente! Como foi comentado pelos colegas, existe de fato pelo menos mais uma abordagem de solução. Trata-se do problema de fatoração em números primos de determinado número. A criptografia de chave pública RSA usa este conceito, e sua segurança se baseia no fato de que é computacionalmente trabalhoso fatorar números primos gigantescos, exigindo geralmente milhares de anos de processamento por força bruta para se quebrar as chaves com tecnologia atual. A não ser que se empregue computação quântica! ? Como os valores em questão são de tamanho aceitável, é possível empregar um algoritmo próprio para a fatoração. Segue então um exemplo adaptado por mim deste código, cujo crédito é de rathbhupendra e a referência vem de https://www.geeksforgeeks.org/print-all-prime-factors-of-a-given-number/ #include <stdio.h> #include <math.h> // Imprimir todos fatores primos de dado número void primeFactors(int n) { // Passo 1: trata de números compostos (primo x primo) // Enquanto for par, divide número por 2 e imprime 2 while (n%2 == 0) { printf("%d", 2); n = n/2; } // Passo 2: ainda tratando de números compostos // Quando o número se tornar ímpar, inicia um laço iterando de // 3 à raiz quadrada do dado número, incrementando de 2 em 2, // imprime este índice e divide o número pelo mesmo for (int i = 3; i <= sqrt(n); i = i+2) { while (n%i == 0) { printf("%d", i); n = n/i; } } // Passo 3: trata dos números primos // Se o dado número não terminou em 1, então é um primo // maior que 2. Imprimir este valor, concluindo a fatoração if (n > 2) printf ("%d", n); printf("\n"); } int main() { int n; printf("%s", "Numero a ser fatorado: "); scanf("%d", &n); primeFactors(n); return 0; } // This is code is contributed by rathbhupendra Segue resultado de sua execução: paulo@nirvana:~/crack/crackmes$ ./fatoresPrimos Numero a ser fatorado: 720300 223557777 paulo@nirvana:~/crack/crackmes$ ./crackme-linux02 223557777 Isso aí!! A chave é válida! paulo@nirvana:~/crack/crackmes$ Abraços!
  14. Parabéns Fernando & equipe! Não conheço nenhum curso em português sobre o tema, com esse nível de detalhamento e ainda com o compartilhamento da experiência que quem realmente entende. Trata-se de um assunto realmente complexo e que deve ser tratado com seriedade, senão não há avanço no aprendizado. Pela primeira vez estou começando a entender. Como sugestão acho que seria interessante futuramente falar também da arquitetura de 64 bits, que é a arquitetura atual e veio para dominar. Também seria muito enriquecedor tratar de tópicos como ofuscação \ criptografia de código conforme se encontra em muitos malwares, e que também não se acha fácil material que trate do assunto. Fica a dica! Grande abraço a todos ai, e muito obrigado pela dedicação e compartilhamento do conhecimento!
×
×
  • Create New...