Fala galera, voltei 😀 Sem piedade, como sempre.

Para quem já conseguiu matar o desafio 6… vamos para o 7:


Tentar o Desafio 7


Então.. vamos continuar a batalha com o desafio 6?

[[ Descobrindo o problema ]]

Pulamos aquele jump enjoado sobre o qual eu falei, mas o programa não mostrou o nosso artigo: porque? De volta ao debugging, seu preguiçoso! Pra isso, faça tudo o que você fez antes… PORÉM!!! No último passo, onde você pressiona F9 pro programa te entregar o artigo de mão beijada, você vai continuar debugando passo a passo pra ver o que acontece.

Logo abaixo, você vê o seguinte trecho de código:

00401197  |> 6A 0A          PUSH 0A
00401199  |. 52             PUSH EDX
0040119A  |. 8D4424 0C      LEA EAX,DWORD PTR SS:[ESP+C]
0040119E  |. 6A 0B          PUSH 0B
004011A0  |. 50             PUSH EAX
004011A1  |. FF15 98204000  CALL DWORD PTR DS:[<&MSVCR80.strncpy_s>] ;  MSVCR80.strncpy_s

O valor de EDX, quando você olha nos registradores, é um endereço que aponta para a senha que você digitou… hummm… Você percebe que ele está passado parâmetros para a função strncpy_s do VC++, que copia uma string para dentro da outra, considerando o tamanho. EAX recebe o endereço do buffer que irá receber a cópia da string, o qual está dentro da pilha.. muito bem.

Após isso, o seguinte trecho de código:

004011A7  |. 83C4 10        ADD ESP,10
004011AA  |. 8D4C24 04      LEA ECX,DWORD PTR SS:[ESP+4]
004011AE  |. E8 4DFFFFFF    CALL Esconded.00401100

O registrador da pilha está sendo movido.. o CALL deve chamar uma função que tem algumas variáveis dentro.. Olhando o valor de ECX após a intrução LEA, verificamos que ele contém um ponteiro para a senha digitada. Somos invasivos. Vamos debugando passo a passo, com a tecla F8, até chegar ao CALL e pressionar F7 pra debugar dentro da função.

Então passamos para uma pequena função:

00401100  /$ 53             PUSH EBX
00401101  |. 56             PUSH ESI
00401102  |. BE 5C564000    MOV ESI,Esconded.0040565C                ;  ASCII "C0d3Bunk3r"
00401107  |. 33D2           XOR EDX,EDX
00401109  |. 57             PUSH EDI
0040110A  |. B8 01000000    MOV EAX,1
0040110F  |. 2BF1           SUB ESI,ECX
00401111  |> 83FA 0A        CMP EDX,0A
00401114  |. 73 1B          JNB SHORT Esconded.00401131
00401116  |. 0FBE3C0E       MOVSX EDI,BYTE PTR DS:[ESI+ECX]
0040111A  |. 0FBE19         MOVSX EBX,BYTE PTR DS:[ECX]
0040111D  |. 83C7 02        ADD EDI,2
00401120  |. 3BDF           CMP EBX,EDI
00401122  |. 74 02          JE SHORT Esconded.00401126
00401124  |. 33C0           XOR EAX,EAX
00401126  |> 83C2 01        ADD EDX,1
00401129  |. 83C1 01        ADD ECX,1
0040112C  |. 66:85C0        TEST AX,AX
0040112F  |.^75 E0          JNZ SHORT Esconded.00401111
00401131  |> 5F             POP EDI
00401132  |. 5E             POP ESI
00401133  |. 5B             POP EBX
00401134  \. C3             RETN

Muito interessante… uma string secreta. Clique com o botão direito em qualquer parte desse código e depois em Analysis | Analyze code. Com isso, ele tenta deduzir alguns detalhes sobre a função. E ele descobre. Olhando no Olly Debugger, você percebe que há um colchete grande em torno da função e mais um colchete pequeno dentro dela. Esse colchete menor significa um loop. Hummm…

Mas não sabemos qual o papel dessa função.. então vamos fazer o seguinte.. vamos sair dela, com a tecla Ctrl+F9 (Execute till return) e depois vamos ver quais valores a função de fora está usando como saída dela e vamos ver o que ela espera.

004011B3  |. 66:85C0        TEST AX,AX
004011B6  |.^74 C9          JE SHORT Esconded.00401181
004011B8  |. 8B47 08        MOV EAX,DWORD PTR DS:[EDI+8]
...
00401181  |> 68 68564000    PUSH Esconded.00405668                   ; /format = "\n*** Senha Invalida!! Voce so pode estar sacaneando!\n\n"
00401186  |. FF15 AC204000  CALL DWORD PTR DS:[<&MSVCR80.printf>]    ; \printf

Esse é o próximo código que virá. A instrução TEST AX,AX vai verificar se o registrador de 16 bits AX contém o valor 0. Se ele tiver, ele vai pular de volta para a mensagem de senha inválida.. Sabe o que isso quer dizer? Que aqula nossa funçãozinha do mal faz a validação da senha 😀 Felicidades, congratulações, estamos chegando perto.

Nesse momento, tentaremos resolver do modo preguiçoso. Pularemos esse segundo jump. Isso funciona muitas vezes. Vamos direto para a instrução MOV, no endereço 004011B8 🙂 Fechou? 😀 Agora mande o F9 pra pegar o seu artigo do coração.

O C:\out.zip está lá!!! Maravilha. Você tenta descompactá-lo e… BOOM! O seu compactador te dá uma mensagem miserável de que o arquivo está corrompido. Você abre o arquivo em um editor hexadecimal e vê um monte de lixo lá dentro. Não existe a assinatura do arquivo ZIP, nem de tipo de arquivo nenhum. Ele não faz nenhum sentido. O que isso quer dizer?? hein, hein, hein??? Que, com grande probabilidade, ele usa a sua senha pra decriptar o artigo. Agora pegou :S

[[ Analisando profundamente ]]

Bom, ficamos irritados. Estamos sendo feitos de idiotas. Vamos fazer o seguinte.. dessa vez vamos analisar o código TODO desde o ponto onde a senha é checada para sabermos… A SENHA DO SISTEMA! Sim, vamos atrás da senha dessa vez. Queremos a nossa dignidade recuperada.

Vamos reiniciar o programa. Localize a procedure onde a mensagem de erro de senha é exibida. Faça o OllyDebugger analisar o código pra você. Apague aquele maldito arquivo C:\out.zip do seu HD. Vamos recomeçar.

00401140  /$ 83EC 10        SUB ESP,10
00401143  |. A1 00604000    MOV EAX,DWORD PTR DS:[406000]
00401148  |. 33C4           XOR EAX,ESP
0040114A  |. 894424 0C      MOV DWORD PTR SS:[ESP+C],EAX
0040114E  |. 837C24 14 03   CMP DWORD PTR SS:[ESP+14],3
00401153  |. 57             PUSH EDI
00401154  |. 8B7C24 1C      MOV EDI,DWORD PTR SS:[ESP+1C]
00401158  |. 74 0D          JE SHORT Esconded.00401167
0040115A  |. E8 A1FEFFFF    CALL Esconded.00401000
0040115F  |. 6A 01          PUSH 1                                   ; /status = 1
00401161  |. FF15 9C204000  CALL DWORD PTR DS:[<&MSVCR80.exit>]      ; \exit
00401167  |> 8B57 04        MOV EDX,DWORD PTR DS:[EDI+4]
0040116A  |. 8BC2           MOV EAX,EDX
0040116C  |. 56             PUSH ESI
0040116D  |. 8D70 01        LEA ESI,DWORD PTR DS:[EAX+1]
00401170  |> 8A08           MOV CL,BYTE PTR DS:[EAX]
00401172  |. 83C0 01        ADD EAX,1
00401175  |. 84C9           TEST CL,CL
00401177  |.^75 F7         \JNZ SHORT Esconded.00401170
00401179  |. 2BC6           SUB EAX,ESI
0040117B  |. 83F8 0A        CMP EAX,0A
0040117E  |. 5E             POP ESI
0040117F  |. 74 16          JE SHORT Esconded.00401197
00401181  |> 68 68564000    PUSH Esconded.00405668                   ; /format = "\n*** Senha Invalida!! Voce so pode estar sacaneando!\n\n"
00401186  |. FF15 AC204000  CALL DWORD PTR DS:[<&MSVCR80.printf>]    ; \printf
0040118C  |. 83C4 04        ADD ESP,4
0040118F  |. 6A 02          PUSH 2                                   ; /status = 2
00401191  |> FF15 9C204000  CALL DWORD PTR DS:[<&MSVCR80.exit>]      ; \exit

Coloque um breakpoint no início dessa procedure e execute o programa até lá, com a tecla F9. Executando o programa PASSO A PASSO, você verifica que ele pula o primeiro exit e vai direto para a instrução MOV no endereço 00401167. Vamos continuar caminhando. Após executar essa instrução, você percebe que o registrador EDX aponta para a senha. Isso a gente já sabia :@ A próxima instrução faz com que EAX também aponte para a senha.

Depois você encontra um pequeno loopzinho, após a instrução LEA:

0040116D  |. 8D70 01        LEA ESI,DWORD PTR DS:[EAX+1]
00401170  |> 8A08           MOV CL,BYTE PTR DS:[EAX]
00401172  |. 83C0 01       ADD EAX,1
00401175  |. 84C9           TEST CL,CL
00401177  |.^75 F7         JNZ SHORT Esconded.00401170

Esse loop, depois de rápida análise, você percebe que ele faz pra apontar EAX para o seu byte zero (lembre-se de que as strings no C terminam em zero). A instrução LEA capturou a posição de EAX antes do loop. Agora as próximas instruções são interessantes:

00401179  |. 2BC6           SUB EAX,ESI
0040117B  |. 83F8 0A        CMP EAX,0A

Ele está subtraindo o valor de EAX (endereço do último caractere, ou o \x00) pelo de ESI (endereço do primeiro byte). E depois compara esse valor com 0A (10 decimal). O que é isso??? Ele está verificando o tamanho da string!! Beleza 🙂 Se o tamnanho for menor que 10, ele pula o primeiro exit. Já sabemos que a nossa senha tem 10 caracteres. Vamos reiniciar o nosso debugging assim! Antes, porém, vamos trocar os parâmetros para “abcdefghij” “c:\saida.zip“.

Agora sim, 10 caracteres, estamos caminhando. Executamos de novo o programa até o jump no endereço 0040117F e não precisamos de nenhuma atitude especial para que ele pule a mensagem de senha incorreta 🙂 Vamos para a validação! Lembra desse trecho de código abaixo?

004011A7  |. 83C4 10        ADD ESP,10
004011AA  |. 8D4C24 04      LEA ECX,DWORD PTR SS:[ESP+4]
004011AE  |. E8 4DFFFFFF    CALL Esconded.00401100

É aquela função interessante que vimos antes. Sabemos que ECX contém um ponteiro para a nossa senha fake digitada. Vamos recapitular aquele código:

00401100  /$ 53             PUSH EBX
00401101  |. 56             PUSH ESI
00401102  |. BE 5C564000    MOV ESI,Esconded.0040565C                ;  ASCII "C0d3Bunk3r"
00401107  |. 33D2           XOR EDX,EDX
00401109  |. 57             PUSH EDI
0040110A  |. B8 01000000    MOV EAX,1
0040110F  |. 2BF1           SUB ESI,ECX
00401111  |> 83FA 0A        CMP EDX,0A
00401114  |. 73 1B          JNB SHORT Esconded.00401131
00401116  |. 0FBE3C0E       MOVSX EDI,BYTE PTR DS:[ESI+ECX]
0040111A  |. 0FBE19         MOVSX EBX,BYTE PTR DS:[ECX]
0040111D  |. 83C7 02        ADD EDI,2
00401120  |. 3BDF           CMP EBX,EDI
00401122  |. 74 02          JE SHORT Esconded.00401126
00401124  |. 33C0           XOR EAX,EAX
00401126  |> 83C2 01        ADD EDX,1
00401129  |. 83C1 01        ADD ECX,1
0040112C  |. 66:85C0        TEST AX,AX
0040112F  |.^75 E0          JNZ SHORT Esconded.00401111
00401131  |> 5F             POP EDI
00401132  |. 5E             POP ESI
00401133  |. 5B             POP EBX
00401134  \. C3             RETN

Lembra que após a execução desse trecho de código, o programa verifica se AX é diferente de zero? O que temos que fazer aqui é digitar uma senha tal que o algoritmo acima retorne algo diferente de zero. E como vamos fazer isso???

[[ Descompilando mentalmente ]]

O código acima não é difícil de se entender. Ele não chama nenhuma função remota (CALL) e não tem nenhuma daquelas funções sobrenaturais para cálculos de números de ponto flutuante. Nada que não possamos resolver 🙂 Vamos primeiro transformar isso em um código mais simples de se entender, utilizando GOTOs.

funcao valida_senha() {

	char* ECX = "abcdefghij"; // Nossa senha fake!!

	char* ESI = "C0d3Bunk3r"; // O que que é isso??
	int EDX = 0;


	char * EBX = NULL;
	char EDI;

	short AX = 1;

	// Estranho trecho de código. ESI recebe um endereço
	// relativo ao endereço da nossa senha. Odeio compiladores
	// às vezes
	ESI = ESI^ - ECX;

	// Um loop... que termina se AX for igual a 0
	do {

		// Vamos trabalhar com goto, por enquanto.
		if (EDX >= 10) {
			GOTO LABEL_2;
		}

		// Que viagem! EDI contém o primeiro caractere daquela string maluca,
		// no primeiro passo do loop
		EDI = (ESI + ECX)^;
		EBX = ECX;

		// Essa instrução soma o código do caractere em EDI mais 2.... hummmmmm
		EDI = EDI + 2;

		// Agora aparece algo muito interessante.. aham!
		if (EDI != EBX) {
			AX = 0; // hummmmmmmmmmmmmmmmmm
		}

		// Incrementa os passos do Loop
		EDX = EDX + 1; // Somando o inteiro
		ECX = ECX + 1; // Movendo o ponteiro para o próximo passo da nossa senha


	} while (AX != 0);

:LABEL_2


}

Analisou o código meio estruturado aqui?? Sabemos em que ponto o algoritmo zera o valor de AX. Ele só faz isso se EDI for diferente de EBX. EBX contém a nossa senha… EDI contém aquela string maluca (C0d3Bunk3r) com cada caractere somado a dois… E quando ele sai do loop??? Quando EDX, que ele vai acumulando chegar a 10. Matamos a charada!

Ele faz um loop, percorre cada caractere da nossa senha e compara com cada caractere da string doida, somando o valor a 2. Algoritmo simples. Amador. Looser! N3wB13! Então… qual é a senha? É só pegar o código de cada caractere da string maluca.. e somar 2 🙂 Com um editor hexadecimal, você faz isso na mão, brincando, de olho fechado, de cabeça pra baixo, enquanto lê um livro e faz coisas inadequadas para menores com sua cônjuge.

Editor Hexadecimal

Então você sabe a senha.. E2f5Dwpm5t

Vamos tentar direto no CMD:

Z:\>Escondedor-V6.exe E2f5Dwpm5t C:\meu_artigo.zip
Meus parabens amigo. Voce merece o meu respeito!
Z:\>

Aeeeeeeeeee… 6 x 0 pra nós.

[[ O Interminável Desafio ]]

Gostou? Aumentou o nível não é? Você saiu um pouco das suas receitas de bolo e teve que olhar um código ASM e entedê-lo com um bom nível de detalhes antes de conseguir burlar a segurança proposta. Se você conseguiu reproduzir o que foi feito aqui e enteder 100%, então você tem um conhecimento muito bom. O mundo está em perigo 🙂

Por isso! Eu quero te fazer um desafio maior. O Desafio 7. Esse vai te deixar sem dormir. Definitivamente. O Desafio 7 explora um pouco mais de suas áreas de conhecimento. Se você passar do desafio 7, vamos pra um outro nível, de novo.

Não pretendo postar a solução do 7 tão cedo, a menos que pelo menos uma pessoa consiga quebrá-lo.


Tentar o Desafio 7


Abraços a todos! Postem seus comentários.