Suite (et fin) des épisode précédents…
Alors que le malware a récupéré des adresses de fonctions Windows dont il vient de décoder les noms, il entre dans une courte fonction que j’ai baptisé time_and_beep
qui :
- Prend une référence de temps avec
GetTickCount
- Attend 51 secondes en utilisant les fonctions d’événements comme on a pu déjà le voir
- Prend une seconde référence de temps
- Fait la soustraction de ces deux références
Si le résultat vaut 0 (ce qui est impossible à moins par exemple de hooker GetTickCount
), le malware émet un Beep
(quasi inaudible car d’une fréquence de 10 hertz sur 10 millisecondes) puis quitte.
Dans le cas contraire il rappelle GetTickCount
, effectue des opérations dessus et stocke le résultat dans magic_int
:
1
2
3
4
5
6
7
8
9
10
11
12
13
402151 ! GetTickCount_magic: ;xref c4023d5
...... ! push ebp
402152 ! mov ebp, esp
402154 ! call dword ptr [GetTickCount]
40215a ! and eax, 17fffh
40215f ! shr eax, 1
402161 ! add eax, 523h
402166 ! mov [magic_int], eax
40216b ! mov eax, [magic_int]
402170 ! and eax, 0ffffh
402175 ! mov [magic_int], eax
40217a ! pop ebp
40217b ! ret
Le rôle de cette fonction peut sembler étrange au premier coup d’œil : on récupère un timestamp et on lui applique un masque le réduisant à une valeur de l’ordre de la minute (entre 1 et deux minutes).
En réalité, cette fonction agit comme un générateur de nombres pseudo-aléatoire (pour l’utilisation que l’auteur du malware en a, ça suffit amplement)
Il entre ensuite dans une fonction qui itère sur les noms de processus avec CreateToolhelp32Snapshot
/ Process32First
/ Process32Next
. Il les passe en minuscule et les compare à teatimer.exe
. Si un exécutable de ce nom est en cours d’exécution, il le termine avec la fonction Windows TerminateProcess
.
Si on prend la calculatrice windows (calc.exe
) et si on l’exécute après l’avoir renommée en teatimer.exe
, on la voit effectivement se fermer lors de l’exécution du malware.
Décodage
On entre alors dans le vrai du malware avec la fonction que j’ai baptisé decrypt_dll_and_load_it
qui effectue les opérations suivantes :
- réserve de l’espace pour des chaines de caractères
- appelle une autre fonction que j’ai nommé
decrypt_dll_file_content
- récupère le répertoire système de Windows avec
GetSystemDirectoryA
- décode les chaines de caractères
\???*.dll
et.dll
selon la routine vue dans un précédent article - appelle une fonction
trouve_un_nom_de_fichier_dll
- détruit le fichier obtenu avec cette fonction avec
DeleteFileA
- lance une fonction
cree_un_fichier
- charge un fichier dll avec
LoadLibraryA
Vous avez déjà une vision globale de l’objectif du malware. Etudions plus en détails le fonctionnement des fonctions précédemment citées.
decrypt_dll_file_content
prend pour argument les mêmes arguments que ceux que decrypt_dll_and_load_it
a reçu, soit :
408030h
= une très longue chaine de caractères encodée située dans la section.data
15A00h
= ce qui s’avère être la longueur de cette mystérieuse chaine8220h
= una variable servant comme vecteur d’initialisation pour déchiffrer la chaine de caractères.
Je n’entrerais pas dans les détails du code assembleur. Pour faire bref, on trouve une boucle qui traite octet par octet la chaine jusqu’à arriver à 15A00h
.
L’opération de décodage se fait par un xor avec une variable qui subit différentes opérations mathématiques à chaque passage de la boucle (valeur initiale = 8220h
)
J’ai préféré écrire un programme en C (plus simple à comprendre) qui reproduit le décryptage. Il prend comme argument un fichier avec les données codées et un nom de fichier ou écrire la version décodée :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int data_42009c, data_420098;
int main(int argc,char *argv[])
{
unsigned int i, x = 0x8220, len = 0x15a00;
int fd_in, fd_out;
char *str;
if(argc!=3)
{
printf("Usage: %s <input_file> <output_file>\n", argv[0]);
return 1;
}
fd_in=open(argv[1],O_RDONLY);
if(fd_in<0)
{
perror("open");
return 1;
}
fd_out = open(argv[2], O_WRONLY|O_TRUNC|O_CREAT,S_IRUSR|S_IWUSR);
if(fd_out<0)
{
perror("open");
return 1;
}
str = (char*)malloc(len+1);
read(fd_in, str, len);
data_42009c = 0x1d;
data_420098 = 0x7d02;
data_420098 += 0x15;
for(i=0;i<len;i++)
{
x *= data_420098;
x += data_42009c;
x = (char)(x & 0x000000ff);
str[i] = str[i] ^ x;
}
write(fd_out, str, len);
free(str);
close(fd_out);
close(fd_in);
return 0;
}
Il ne reste plus qu’à extraire la chaine de l’exécutable. J’ai d’abord cherché un plugin OllyDgb mais je me suis dit que c’était aussi simple avec dd.
1
dd if=malware.exe of=plop bs=1 count=88576 skip=32816
Une fois le fichier décrypté, on obtient une dll de 87Ko (md5: 1154378d77d4dd1eb83d40a3a0b6982f
) que AVG détecté comme “Trojan horse BackDoor.Generic9.AYAH”.
Un petit passage dans HTEditor pour voir les sections et on remarque aussitôt que la dll est compressée avec UPX.
Une fois décompressé, le fichier fait 202Ko (md5: 70e2780a0ecce1ec7755397381bb5606
) et est détecté (toujours par AVG) comme “Trojan horse Agent.XMD” (étrange qu’ils ne soient pas connus sous le même nom…)
Recherche d’un nom de fichier
Mais reprenons le cours du programme (car à ce moment de l’exécution le fichier n’a normalement pas encore touché le disque).
Le malware récupère le répertoire système (sous XP, c’est C:\WINDOWS\System32
) et le passe comme argument à trouve_un_nom_de_fichier_dll
avec les chaines de caractères décodées (.dll
et \???*.dll
) ainsi que différentes zones mémoire (buffer de sortie).
trouve_un_nom_de_fichier_dll
effectue une recherche sur les fichiers correspondant au masque c:\windows\system32\???*.dll
.
Il génère aussi deux valeurs d’un octet avec des appels à calcul_sur_magic_int
(voir articles précédents) dont la valeur initiale a été préalablement créée par le générateur pseudo-aléatoire.
Il itère ainsi sur tous les noms de fichier dll qu’il trouve dans le répertoire system32
et s’arrête uniquement lorsque le compteur de boucle modulo (reste d’une division) l’une de ces deux valeurs est nul.
Quand la condition est rencontrée, il récupère le début du nom du fichier dll en cours (les caractères avant le premier point) et calcule la longueur de chaine obtenue.
Il recopie cette chaine en mémoire avec lstrcpyn
en prenant soin de retirer le dernier caractère puis rajoute finalement l’extension .dll
.
Pour conclure, si trouve_un_nom_de_fichier_dll
s’arrête sur kernel32.dll
, le résultat obtenu sera kernel3.dll
. Il vérifie tout de même que le fichier n’existe pas (pour ne pas faire de bétises) mais appellera quand même DeleteFileA
en sortant de la fonction.
Fonctionnalité de dropper
La fonction suivante, cree_un_fichier
, va générer un fichier temporaire avec GetTempPathA
/ GetTempFileNameA
. Le chemin du fichier sera de la forme :
C:\Documents and Settings\<username>\Local Settings\Temp\datXXXX.tmp
Le fichier est mappé en mémoire avec CreateFileMappingA
/ MapViewOfFile
et le contenu du fichier dll décrypté (actuellement en mémoire) est recopiée à l’aide de la fonction de recopie obfusquée vu dans un précédent article.
Le fichier écrit dans le fichier temporaire est finalement déplacé avec MoveFileA
dans system32
sous le nom dll qui a été calculé.
Quand on sort de cette fonction pour retourner à decrypt_dll_and_load_it
, le fichier dll est chargé en mémoire avec LoadLibraryA
.
Il est intéressant de noter que cette fonction contient une portion de code alternative par laquelle on ne passe pas mais dont l’objectif semble être de créer un fichier exe et d’appeller CreateProcess
.
Le dropper a donc été créé pour déposer aussi bien des dll que des exécutables bien que la présente version ne contient qu’un fichier dll (il n’y a pas d’autres chaines de grande taille dans la section .data
).
Fin de l’exécution
Pour terminer, le malware commande sa propre suppression en appelant MoveFileEx avec le flag MOVEFILE_DELAY_UNTIL_REBOOT
. Ainsi Windows supprime lui-même le fichier au redémarrage du système.
Il appelle ensuite une fonction qui retourne invariablement la valeur 5018 (présence de tests qui donneront toujours le même résultat !?) et dormira (encore) ce délai en millisecondes. En cumulant tous ces délais d’attente on se rapprocherait presque d’une minute d’exécution.
Le malware quitte alors, il a atteint son objectif : déposer un fichier dll et le charger pour l’exécuter.
Je n’ai pas encore commencé l’analyse du dll qui doit receler plein de surprises (je ne vous promets rien).
Conclusion
Bien que le malware ne fasse pas grand-chose et ne contenait pas de techniques anti-analyse avancées (il n’était pas packé, utilisait peu de junk-code…) il était néanmoins intéressant d’analyser son fonctionnement (contenu dissimulé, réécriture de code pour appeler un code de détection de débogueur, kill de logiciel de sécurité…)
Ça m’a aussi permis de découvrir quelques astuces assembleur comme la suite neg / sbb / inc.
En gros :
1
2
3
neg reg
sbb reg,reg
inc reg
correspond à
1
(reg==0)?reg=1;reg=0
et
1
2
3
neg reg
sbb reg,reg
neg reg
correspond à
1
(reg==0)?reg=0;reg=1
Un petit dropper sympathique à analyser :)
Published January 11 2011 at 16:10