Challenges.re - RE 03#

O Challenges.re é um site com uma compilação de exercícios de Reverse Engineering. Estou escrevendo este documento apenas para meu próprio uso, para praticar meus conhecimentos em programação assembly e engenharia reversa. Se você pretende resolver esses exercícios por conta própria algum dia, por favor, não leia minha solução, tente resolver sozinho. Minha resposta pode estar errada, então não confie em mim, confie no processo.

Pergunta#

Reverse Engineering challenge #3. Tags: . O que este código faz? A função possui um array de 64 inteiros de 32 bits. Removi de cada trecho de código assembly para economizar espaço. O array é:

int v[64]=
	{ -1,31, 8,30, -1, 7,-1,-1, 29,-1,26, 6, -1,-1, 2,-1,
	  -1,28,-1,-1, -1,19,25,-1, 5,-1,17,-1, 23,14, 1,-1,
	   9,-1,-1,-1, 27,-1, 3,-1, -1,-1,20,-1, 18,24,15,10,
	  -1,-1, 4,-1, 21,-1,16,11, -1,22,-1,12, 13,-1, 0,-1 };

Código Assembly#

f:
	mov	edx, edi
	shr	edx
	or	edx, edi
	mov	eax, edx
	shr	eax, 2
	or	eax, edx
	mov	edx, eax
	shr	edx, 4
	or	edx, eax
	mov	eax, edx
	shr	eax, 8
	or	eax, edx
	mov	edx, eax
	shr	edx, 16
	or	edx, eax
	imul	eax, edx, 79355661 ; 0x4badf0d
	shr	eax, 26
	mov	eax, DWORD PTR v[0+rax*4]
	ret

Com comentários em cada linha:

f: 									; Método de função                                              
	mov	edx, edi					; Move dados de EDI para EDX                                    
	shr	edx 						; Deslocar EDX para a direita 1 bit (EDX / 2) 0x2               
	or	edx, edi 					; Realiza comparação OR EDX com EDI e armazena no EDX.          
	mov	eax, edx 					; Move dados do EDX para o EAX                                  
	shr	eax, 2 						; Deslocar EAX para a direita 2 bits (EDX / 4) 0x4              
	or	eax, edx 					; Realiza comparação OR entre EAX e EDX e armazena em EAX.      
	mov	edx, eax 					; Move dados do EAX para o EDX                                  
	shr	edx, 4 						; Deslocar EDX para a direita 4 bit (EDX / 16) 0x10             
	or	edx, eax 					; Realiza comparação OR entre EDX e EAX e armazena em EDX.      
	mov	eax, edx 					; Move dados do EDX para o EAX                                  
	shr	eax, 8 						; Deslocar EAX para a direita 8 bits (EDX / 256) 0x100          
	or	eax, edx 					; Realiza comparação OR entre EAX e EDX e armazena em EAX.      
	mov	edx, eax 					; Move dados do EAX para o EDX                                  
	shr	edx, 16 					; Deslocar EDX para a direita 16 bits (EDX / 65536) 0x10000     
	or	edx, eax 					; Realize comparação OR entre EDX e EAX e armazena em EDX.      
	imul	eax, edx, 79355661 		; (0x4badf0d) Multiplique EDX por 79355661 e armazene em EAX    
	shr	eax, 26  					; Deslocar EAX para a direita 26 bits (EAX / 67108864) 0x4000000
	mov	eax, DWORD PTR v[0+rax*4] 	; Move v[0+RAX*4] DWORD para EAX                                
	ret

Ao criar uma tabela de pseudocódigo, ela ficaria mais ou menos assim:

OPArg1Arg2Arg3pseudocódigo
MOVEDXEDIEDX = EDI
SHREDX1EDX = EDX » 1
OREDXEDIEDX = EDI | EDX
MOVEAXEDXEAX = EDX
SHREAX2EAX = EAX » 2
OREAXEDXEAX = EDX | EAX
MOVEDXEAXEDX = EAX
SHREDX4EDX = EDX » 4
OREDXEAXEDX = EAX | EDX
MOVEAXEDXEAX = EDX
SHREAX8EAX = EAX » 8
OREAXEDXEAX = EDX | EAX
MOVEDXEAXEDX = EAX
SHREDX16EDX = EDX » 16
OREDXEAXEDX = EAX | EDX
IMULEAXEDX79355661EAX = EDX * 0x4badf0d
SHREAX26EAX = EAX » 26
MOVEAXDWORD PTR v[0+rax*4]EAX = (int)v[RAX]
RETreturn EAX

E tentando fazer engenharia reversa para código C:

// Type your code here, or load an example.
int v[64]=
	{ -1,31, 8,30, -1, 7,-1,-1, 29,-1,26, 6, -1,-1, 2,-1,
	  -1,28,-1,-1, -1,19,25,-1, 5,-1,17,-1, 23,14, 1,-1,
	   9,-1,-1,-1, 27,-1, 3,-1, -1,-1,20,-1, 18,24,15,10,
	  -1,-1, 4,-1, 21,-1,16,11, -1,22,-1,12, 13,-1, 0,-1 };

int f(int num) {

    num|=(num>>1);
    num|=(num>>2);
    num|=(num>>4);
    num|=(num>>8);
    num|=(num>>16);

    num*=79355661;

    num>>=26;

    return v[num];
}

Com essas informações, podemos criar uma tabela de possíveis resultados:

inputoutputdecimalNo Array
00x00-1
10x1131
2-30x3330
4-70x8829
8-150x111728
16-310x243627
32-630x0a1026
64-1270x162225
128-2550x2d4524
256-5110x1c2823
512-10230x395722
1024-20470x345221
E assim continua…

O que o código faz#

O algoritmo pega o bit mais valioso à esquerda, expande-o até preencher toda a extremidade direita e, em seguida, multiplica o resultado por um número mágico: 79355661.

Esse número gera um resultado que é filtrado deslocando 26 bits para a direita. Fazendo com que ele varie de 0 a 63, os números possíveis para serem reunidos no array v[].

O resultado então retornará um valor dentro do array. Nesse caso, o valor 1 retorna 31, os valores 2 e 3 retornam 30, etc.

Está decrementando um valor em cada bit alcançado à esquerda. (verifique a tabela acima)

Isso poderia ser usado como um tipo de código de ofuscação. O malware seria colocado em uma ordem incomum para ser lido, enganando os padrões conhecidos dos bancos de dados de antivírus.