Accueil Analyse du malware Podnuha.ql : seconde partie
Post
Annuler

Analyse du malware Podnuha.ql : seconde partie

Dans ce second épisode nous allons nous pencher sur le rôle de la fonction antifuck du malware. Cette fonction est l’avant-dernière fonction appelée dans le WinMain, juste avant la fonction dont l’adresse était obfusquée.

D’entrée de jeux, la fonction répète un schéma qui nous est maintenant classique : deux GetTickCount, séparés par un Sleep() de 6 secondes (6001ms pour être exact).

Le délai entre ces deux instructions est calculé par soustraction entre les deux prises de temps et est comparé à différentes possibilités (inférieur à 5s ? supérieur à 18s ? inférieur à 1s ?)
En fonction des résultats de ces tests, une valeur différente est définie pour une variable locale : 17, 5, 47 ou 1. La valeur obtenue normalement étant 5.

Passé ce test, on entre dans une fonction ne prenant aucun argument. Cette fonction commence par effectuer différentes modifications sur une zone mémoire de 9 octets située dans la section .data.

Cette modification n’a rien de régulier : aucune boucle n’est présente, tout se fait au cas par cas.

Ainsi on part de la suite d’octets suivante : 0E 02 0D 00 03 00 00 C2 00

À laquelle on applique différentes modifications (dans l’ordre) :

  1. ajouter 1 au premier octet
  2. retirer 1 au second octet
  3. mettre à zéro le 5ème octet
  4. ajouter 1 au 8ème octet
  5. écraser les octets du 4ème au 7ème inclus par la valeur 0x0042004C (qui correspond à une adresse mémoire située dans .data)

On obtient finalement 9 nouveaux octets :

1
0F 01 0D 4C 00 42 00 C3 00

On entre ensuite dans une fonction particulière que voici :

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
40129b !   push        ebp
40129c !   mov         ebp, esp
40129e !   push        ecx
40129f !   push        ebx
4012a0 !   push        esi
4012a1 !   push        edi
4012a2 !   push        ecx
4012a3 !   pop         ecx
4012a4 !   nop
4012a5 !   nop
4012a6 !   call        sub_401284
4012ab !   mov         [ebp-4], eax
4012ae !   push        ecx
4012af !   push        ebx
4012b0 !   push        edx
4012b1 !   mov         edx, ecx
4012b3 !   nop
4012b4 !   nop
4012b5 !   inc         ecx
4012b6 !   pop         edx
4012b7 !   pop         ebx
4012b8 !   pop         ecx
4012b9 !   nop
4012ba !   mov         eax, [ebp-4]
4012bd !   pop         edi
4012be !   pop         esi
4012bf !   pop         ebx
4012c0 !   mov         esp, ebp
4012c2 !   pop         ebp
4012c3 !   ret

Ce qui la rend si particulière, c’est la présence d’instructions inutiles (nop et modifications de registres qui sont restaurés juste après).

On note à l’adresse 4012a6 l’appel à la fonction suivante :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
401284 !   push        ebp
401285 !   mov         ebp, esp
401287 !   push        ecx
401288 !   push        ebx
401289 !   push        esi
40128a !   push        edi
40128b !   mov         eax, [ebp+4]
40128e !   mov         [ebp-4], eax
401291 !   mov         eax, [ebp-4]
401294 !   pop         edi
401295 !   pop         esi
401296 !   pop         ebx
401297 !   mov         esp, ebp
401299 !   pop         ebp
40129a !   ret

Cette fonction retourne comme valeur le contenu du [ebp+4]. Or les habitués du langage assembleur savent que les arguments placés sur la pile ne commence qu’à [ebp+8]. À quoi correspond [ebp+4] ? Tout simplement à l’adresse de retour (l’adresse où sauter en sortant de la fonction) qui est 4012ab.

Une fois que l’on a “remonté” ces deux fonctions et que l’on se retrouve à nouveau dans la fonction antifuck, cette adresse est sauvegardée dans une variable globale.

Finalement le malware appelle la fonction VirtualProtect avec les arguments suivants :

  • l’adresse 0x004012ab récupérée précédemment.
  • la valeur 9
  • la valeur 0x40
  • l’adresse d’une variable locale.

Ce qui peut se traduire en “modifier les paramètres d’accès des 9 octets se trouvant à l’adresse 0x004012ab pour qu’ils soient readable, writable et executable (flag 0x40)”.

On saute ensuite à l’adresse 0x00402d40 que je ne prendrais pas la peine de détailler mais qui est pourrie de junk-code et de sauts sur des adresses relatives (du genre jmp dword ptr [edx*4+mem]) destinés à abuser le désassembleur.

Cette fonction volontairement obscurcie se charge de recopier les neuf octets modifiés tout à l’heure à l’adresse 0x004012ab qui a été déprotégée par VirtualProtect.

En fin de compte, la suite de push et de nop que l’on a croisé plus haut prend tout son sens : ils servaient à “garder” de la place pour placer des instructions cachées.

Et à quoi correspond la suite d’octets 0F 01 0D 4C 00 42 00 C3 00 en assembleur ? Voici la réponse :

1
2
sidt fword ptr ds:[0042004C]
retn

Alors que ces instructions ont été placées en mémoire, on saute dessus puis on retourne aux instructions suivantes avant de revenir à antifuck :

1
2
3
4
5
6
7
40136b !   xor         ecx, ecx
40136d !   mov         cl, [data_420051]
401373 !   xor         eax, eax
401375 !   cmp         ecx, 0d0h
40137b !   setg        al
...
401384 !   ret

À quoi tout ça rime ? Et bien le malware récupère l’adresse de la table de description des interruptions (IDT) par le biais de l’instruction sidt. Il stocke le résultat obtenu (un fword, soit 6 octets, à l’adresse mémoire 0x0042004C)

Les 6 octets retournés par sidt correspondant à la taille de la table de description des interruptions (sur deux octets) et son adresse (sur 4 octets).

Le dernier octet correspondant à l’octet de poids fort de l’adresse de l’IDT est placé dans ecx et est ensuite comparé à la valeur hexadécimale 0xD0. Si c’est supérieur on retourne 1, sinon on retourne 0.

Afin de mettre les choses au point, j’ai écrit le programme suivant (à compiler avec FASM) qui affiche l’adresse de l’IDT :

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
format PE console
entry start

include 'macro\import32.inc'

section '.text' code readable executable

  start:

  sidt fword [val]
  lea eax, [val+2]
  mov eax, [eax]
  push eax
  push fmt_str
  call [printf]
  add esp, 8

  push  0
  call  [ExitProcess]

section '.data' data readable writeable
  val df ?
  fmt_str db "%p", 10, 0

section '.idata' import data readable writeable

  library kernel, 'KERNEL32.DLL',\
    msvcrt, 'MSVCRT.DLL'

  import kernel,\
   ExitProcess,'ExitProcess'

  import msvcrt, \
   printf, 'printf'

Quand je l’exécute directement depuis mon Win7 j’obtiens par exemple 807CC020 ou 80B95400. En revanche depuis le Windows XP virtualisé dans VirtualBox, l’adresse obtenue est F8C06BD0.

Si on recherche sur Google, on trouve quelques références sur des forums chinois à cette comparaison destinée à détecter la présence d’un environnement virtualisé (VMWare, VirtualBox…)

Si le malware détecte l’entourloupe, il quitte rapidement le programme. Sinon il tente d’allouer avec VirtualAlloc une quarantaine de Mo (48 000 320 octets pour être précis). S’il échoue, le programme quitte sinon il libère cette mémoire, ajoute 1 à la variable locale vue au tout début de l’article (qui passe donc à 6) et le programme suit son cours (on quitte proprement antifuck avec la valeur de retour 6 pour retourner au WinMain).

Je n’ai pas encore déterminé à quoi correspondait ce test mémoire… peut-être le programme va-t-il faire une utilisation importante de la mémoire à venir et préfère vérifier tout de suite les capacités du système sur lequel il se trouve…

Pour conclure cette fonction antifuck a pour unique but de détecter la présence d’une machine virtuelle mais le créateur du malware a pris soin de dissimuler cet objectif par différents moyens (réécriture de code, junk-code etc).

Dans le prochain article, nous verrons comment le malware déchiffre des chaines de caractères et importe des fonctions qui ne sont pas présentes dans la table des imports.

Published January 11 2011 at 15:45

Cet article est sous licence CC BY 4.0 par l'auteur.