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]
retCom 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
retAo criar uma tabela de pseudocódigo, ela ficaria mais ou menos assim:
| OP | Arg1 | Arg2 | Arg3 | pseudocódigo |
|---|---|---|---|---|
| MOV | EDX | EDI | EDX = EDI | |
| SHR | EDX | 1 | EDX = EDX » 1 | |
| OR | EDX | EDI | EDX = EDI | EDX | |
| MOV | EAX | EDX | EAX = EDX | |
| SHR | EAX | 2 | EAX = EAX » 2 | |
| OR | EAX | EDX | EAX = EDX | EAX | |
| MOV | EDX | EAX | EDX = EAX | |
| SHR | EDX | 4 | EDX = EDX » 4 | |
| OR | EDX | EAX | EDX = EAX | EDX | |
| MOV | EAX | EDX | EAX = EDX | |
| SHR | EAX | 8 | EAX = EAX » 8 | |
| OR | EAX | EDX | EAX = EDX | EAX | |
| MOV | EDX | EAX | EDX = EAX | |
| SHR | EDX | 16 | EDX = EDX » 16 | |
| OR | EDX | EAX | EDX = EAX | EDX | |
| IMUL | EAX | EDX | 79355661 | EAX = EDX * 0x4badf0d |
| SHR | EAX | 26 | EAX = EAX » 26 | |
| MOV | EAX | DWORD PTR v[0+rax*4] | EAX = (int)v[RAX] | |
| RET | return 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:
| input | output | decimal | No Array |
|---|---|---|---|
| 0 | 0x0 | 0 | -1 |
| 1 | 0x1 | 1 | 31 |
| 2-3 | 0x3 | 3 | 30 |
| 4-7 | 0x8 | 8 | 29 |
| 8-15 | 0x11 | 17 | 28 |
| 16-31 | 0x24 | 36 | 27 |
| 32-63 | 0x0a | 10 | 26 |
| 64-127 | 0x16 | 22 | 25 |
| 128-255 | 0x2d | 45 | 24 |
| 256-511 | 0x1c | 28 | 23 |
| 512-1023 | 0x39 | 57 | 22 |
| 1024-2047 | 0x34 | 52 | 21 |
| 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.