Ir para conteúdo

cade.exe


Fernando Mercês

Posts Recomendados

Não é exatamente um desafio, mas este pequeno programa foi usado para exemplificar o assunto de strings ofuscadas na aula 23 do CERO e queria postá-lo aqui.

O lance é entender por que a string não aparece na análise estática. Bem básico, mas é a proposta do CERO mesmo. 🙂

cade.exe

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

  • 2 anos depois...
Em 30/09/2018 em 05:35, Fernando Mercês disse:

Não é exatamente um desafio, mas este pequeno programa foi usado para exemplificar o assunto de strings ofuscadas na aula 23 do CERO e queria postá-lo aqui.

O lance é entender por que a string não aparece na análise estática. Bem básico, mas é a proposta do CERO mesmo. ?

cade.exe

Olá criador do desafio,

 

Vamos a nossa analise:


1º Parte

	Quando encontramos o EntryPoint a parte que mais chama a atenção é a seguinte:
    
        mov     dword ptr [esp+1Ch], 6A645B63h       ; Char 1   
	mov     dword ptr [esp+20h], 645F585Bh       ; Char 2
	mov     dword ptr [esp+24h], 575F6857h       ; Char 3
	mov     dword ptr [esp+28h], 63655924h       ; Char 4
	mov     dword ptr [esp+2Ch], 685824h         ; Char 5

Nesse caso temos alguns valores sendo movidos para algumas posições da memória indicadas por 
ESP +offset, e colados lá, isso é caracteristica de um array, ou da instrução strcpy.


2º Vamos organizar isso e tentar encaixar na instrução do strcpy, ficará da seguinte forma:
 

int main(int argc, char** argv) {
	char buffer[20];
	/*
	mov     dword ptr [esp+1Ch], 6A645B63h       ; Char 1   
	mov     dword ptr [esp+20h], 645F585Bh       ; Char 2
	mov     dword ptr [esp+24h], 575F6857h       ; Char 3
	mov     dword ptr [esp+28h], 63655924h       ; Char 4
	mov     dword ptr [esp+2Ch], 685824h         ; Char 5
	
	
	ESP[0] =  6A645B63h;
	ESP[1] = 645F585Bh;
	ESP[2] = 575F6857h;
	ESP[3] = 63655924h;
	ESP[4] = 685824h;
	
	Simplificando:
	strcpy(ESP, "c[dj[X_dWh_W$Yec$Xh")
	*/
	strcpy(buffer, "c[dj[X_dWh_W$Yec$Xh");
}

Interessante temos um buffer sendo alimentado por uma string aparentemente ofuscada e convertida do hexadecimal para o ASCII.


3º Vamos entender mais um pouco de como é feito essa chamada

lea     eax, [esp+1Ch]  ; copia o endereço de uma posição para EAX
mov     [esp], eax         ; move o conteúdo do endereço pego para ESP
call    DecriptStr          ; chama uma call para um função
mov     [esp], eax         ; retorno da call

! Todas calls costumam retornam em EAX

nosso peseudocódigo ficará da seguinte forma:

DecriptStr([ESP]);

Legal, temos um ponteiro de um char sendo movido para uma função ? sim.
Veja a reescrita disso com as informações que já temos:

int main(int argc, char** argv) {

  // Nova adição, para receber o retorno de EAX
   const char* escrever;

	char buffer[20];
	/*
	mov     dword ptr [esp+1Ch], 6A645B63h       ; Char 1   
	mov     dword ptr [esp+20h], 645F585Bh       ; Char 2
	mov     dword ptr [esp+24h], 575F6857h       ; Char 3
	mov     dword ptr [esp+28h], 63655924h       ; Char 4
	mov     dword ptr [esp+2Ch], 685824h         ; Char 5
	
	
	ESP[0] =  6A645B63h;
	ESP[1] = 645F585Bh;
	ESP[2] = 575F6857h;
	ESP[3] = 63655924h;
	ESP[4] = 685824h;
	
	Simplificando:
	strcpy(ESP, "c[dj[X_dWh_W$Yec$Xh")
	*/
	strcpy(buffer, "c[dj[X_dWh_W$Yec$Xh");
     // nova adição
	escrever = DecriptStr(buffer); // Simplificamos os detalhes, então a call é efetuada o retorno volta em EAX e volta a "referência" para um endereço.
}

4º Nosso código está ficando bacana, vamos prestar mais atenção nos próximos detalhes abaixo disso:
 

call    _puts          ; Chama a função puts para escrever nosso retorno
mov     eax, 0      ; Lembra da dica de retorno da função ? sim, nosso main também é uma subcall e ele vai retornar 0 


Pseudocode:
puts(escrever);

return 0;

 

5º Vamos reescrever isso agora no código que já entendemos.

 

int main(int argc, char** argv) {
	const char* escrever;
	char buffer[20];
	
	/*
	mov     dword ptr [esp+1Ch], 6A645B63h       ; Char 1   
	mov     dword ptr [esp+20h], 645F585Bh       ; Char 2
	mov     dword ptr [esp+24h], 575F6857h       ; Char 3
	mov     dword ptr [esp+28h], 63655924h       ; Char 4
	mov     dword ptr [esp+2Ch], 685824h         ; Char 5
	
	
	ESP[0] =  6A645B63h;
	ESP[1] = 645F585Bh;
	ESP[2] = 575F6857h;
	ESP[3] = 63655924h;
	ESP[4] = 685824h;
	
	Simplificando:
	strcpy(ESP, "c[dj[X_dWh_W$Yec$Xh")
	*/
	strcpy(buffer, "c[dj[X_dWh_W$Yec$Xh");
	
	escrever = DecriptStr(buffer);
	puts(escrever);
	
	return 0;
}


Dando continuidade com nossa reconstrução e agilizando o processo, analisei a função de DecriptStr e ficou da seguinte forma:
 

char* DecriptStr(char *stringOfuscada){
	char *armazenaLocal;
	armazenaLocal = stringOfuscada;
	while(*stringOfuscada)
		*stringOfuscada++ += 10;
	return armazenaLocal;
}

Esse é o algoritmo usado para ocultar as strings do Debugger, basicamente, é relativamente simples esconder alguma string do debbuger, por esse fato devemos também ficar atento no que acontece no algoritmo do mesmo verificar se temos argumentos suspeitos sendo passados.

Conclusão:
Esse tipo de algoritmo é muito comum em malwares como o bom e velho XOR, porem são muito utilizados em diversos software que você encontrar durante sua analise, fique atento a isso !

E para o fim temos o resultado:

Obrigado pela sua Leitura, e espero que isso tenha sido útil para alguém !

Saudações a todos. 

Sem Título.png

CADE_Algoritimo.zip

  • Agradecer 1
  • Curtir 1
Link para o comentário
Compartilhar em outros sites

  • 2 anos depois...
Em 16/01/2021 em 15:08, Hakuoo disse:

Olá criador do desafio,

 

Vamos a nossa analise:


1º Parte

	Quando encontramos o EntryPoint a parte que mais chama a atenção é a seguinte:
    
        mov     dword ptr [esp+1Ch], 6A645B63h       ; Char 1   
	mov     dword ptr [esp+20h], 645F585Bh       ; Char 2
	mov     dword ptr [esp+24h], 575F6857h       ; Char 3
	mov     dword ptr [esp+28h], 63655924h       ; Char 4
	mov     dword ptr [esp+2Ch], 685824h         ; Char 5

Nesse caso temos alguns valores sendo movidos para algumas posições da memória indicadas por 
ESP +offset, e colados lá, isso é caracteristica de um array, ou da instrução strcpy.


2º Vamos organizar isso e tentar encaixar na instrução do strcpy, ficará da seguinte forma:
 

int main(int argc, char** argv) {
	char buffer[20];
	/*
	mov     dword ptr [esp+1Ch], 6A645B63h       ; Char 1   
	mov     dword ptr [esp+20h], 645F585Bh       ; Char 2
	mov     dword ptr [esp+24h], 575F6857h       ; Char 3
	mov     dword ptr [esp+28h], 63655924h       ; Char 4
	mov     dword ptr [esp+2Ch], 685824h         ; Char 5
	
	
	ESP[0] =  6A645B63h;
	ESP[1] = 645F585Bh;
	ESP[2] = 575F6857h;
	ESP[3] = 63655924h;
	ESP[4] = 685824h;
	
	Simplificando:
	strcpy(ESP, "c[dj[X_dWh_W$Yec$Xh")
	*/
	strcpy(buffer, "c[dj[X_dWh_W$Yec$Xh");
}

Interessante temos um buffer sendo alimentado por uma string aparentemente ofuscada e convertida do hexadecimal para o ASCII.


3º Vamos entender mais um pouco de como é feito essa chamada

lea     eax, [esp+1Ch]  ; copia o endereço de uma posição para EAX
mov     [esp], eax         ; move o conteúdo do endereço pego para ESP
call    DecriptStr          ; chama uma call para um função
mov     [esp], eax         ; retorno da call

! Todas calls costumam retornam em EAX

nosso peseudocódigo ficará da seguinte forma:

DecriptStr([ESP]);

Legal, temos um ponteiro de um char sendo movido para uma função ? sim.
Veja a reescrita disso com as informações que já temos:

int main(int argc, char** argv) {

  // Nova adição, para receber o retorno de EAX
   const char* escrever;

	char buffer[20];
	/*
	mov     dword ptr [esp+1Ch], 6A645B63h       ; Char 1   
	mov     dword ptr [esp+20h], 645F585Bh       ; Char 2
	mov     dword ptr [esp+24h], 575F6857h       ; Char 3
	mov     dword ptr [esp+28h], 63655924h       ; Char 4
	mov     dword ptr [esp+2Ch], 685824h         ; Char 5
	
	
	ESP[0] =  6A645B63h;
	ESP[1] = 645F585Bh;
	ESP[2] = 575F6857h;
	ESP[3] = 63655924h;
	ESP[4] = 685824h;
	
	Simplificando:
	strcpy(ESP, "c[dj[X_dWh_W$Yec$Xh")
	*/
	strcpy(buffer, "c[dj[X_dWh_W$Yec$Xh");
     // nova adição
	escrever = DecriptStr(buffer); // Simplificamos os detalhes, então a call é efetuada o retorno volta em EAX e volta a "referência" para um endereço.
}

4º Nosso código está ficando bacana, vamos prestar mais atenção nos próximos detalhes abaixo disso:
 

call    _puts          ; Chama a função puts para escrever nosso retorno
mov     eax, 0      ; Lembra da dica de retorno da função ? sim, nosso main também é uma subcall e ele vai retornar 0 


Pseudocode:
puts(escrever);

return 0;

 

5º Vamos reescrever isso agora no código que já entendemos.

 

int main(int argc, char** argv) {
	const char* escrever;
	char buffer[20];
	
	/*
	mov     dword ptr [esp+1Ch], 6A645B63h       ; Char 1   
	mov     dword ptr [esp+20h], 645F585Bh       ; Char 2
	mov     dword ptr [esp+24h], 575F6857h       ; Char 3
	mov     dword ptr [esp+28h], 63655924h       ; Char 4
	mov     dword ptr [esp+2Ch], 685824h         ; Char 5
	
	
	ESP[0] =  6A645B63h;
	ESP[1] = 645F585Bh;
	ESP[2] = 575F6857h;
	ESP[3] = 63655924h;
	ESP[4] = 685824h;
	
	Simplificando:
	strcpy(ESP, "c[dj[X_dWh_W$Yec$Xh")
	*/
	strcpy(buffer, "c[dj[X_dWh_W$Yec$Xh");
	
	escrever = DecriptStr(buffer);
	puts(escrever);
	
	return 0;
}


Dando continuidade com nossa reconstrução e agilizando o processo, analisei a função de DecriptStr e ficou da seguinte forma:
 

char* DecriptStr(char *stringOfuscada){
	char *armazenaLocal;
	armazenaLocal = stringOfuscada;
	while(*stringOfuscada)
		*stringOfuscada++ += 10;
	return armazenaLocal;
}

Esse é o algoritmo usado para ocultar as strings do Debugger, basicamente, é relativamente simples esconder alguma string do debbuger, por esse fato devemos também ficar atento no que acontece no algoritmo do mesmo verificar se temos argumentos suspeitos sendo passados.

Conclusão:
Esse tipo de algoritmo é muito comum em malwares como o bom e velho XOR, porem são muito utilizados em diversos software que você encontrar durante sua analise, fique atento a isso !

E para o fim temos o resultado:

Obrigado pela sua Leitura, e espero que isso tenha sido útil para alguém !

Saudações a todos. 

Sem Título.png

CADE_Algoritimo.zip 547 B · 3 downloads

Muito bom!

Uma ressalva (nada demais)...

Ao invés de usar o termo char1 , char2 e etc nos comentário dessas variáveis locais, não seria mais ideal um dos seguintes termos:

1 - Array de char (ou *char)

2 - String

3 - Buffer

Dá pra ver que é um Array de char (string) passado em "pedaços". Um char tem 1 byte, mas está sendo passado valor literal de  4 em 4 bytes, que é justamente palavras DWORD em cada endereços de destino na stack, para variáveis locais.

NOTE: A menos que você tenha considerado que cada valor passado para variável é sempre um PONTEIRO para a primeira char de cada string passada. Se for isso, então desconsidere meu comentário que acaba sendo só uma explicação desse ponto para quem se perguntar sobre isso. Hehe

Tua análise ficou ótima. Se me equivoquei nessa observação, desconsidere! 🙂

ofuscapng.png

Editado por Michel Pereira
Link para o comentário
Compartilhar em outros sites

Participe da conversa

Você pode postar agora e se cadastrar mais tarde. Se você tem uma conta, faça o login para postar com sua conta.

Visitante
Responder

×   Você colou conteúdo com formatação.   Remover formatação

  Apenas 75 emojis são permitidos.

×   Seu link foi automaticamente incorporado.   Mostrar como link

×   Seu conteúdo anterior foi restaurado.   Limpar o editor

×   Não é possível colar imagens diretamente. Carregar ou inserir imagens do URL.

  • Quem Está Navegando   0 membros estão online

    • Nenhum usuário registrado visualizando esta página.
×
×
  • Criar Novo...