La base de registre de Windows contient énormément d'informations. Dans un programme il est parfois nécessaire de passer par la base de registre pour écrire ou lire des informations, comme le nom de l'utilisateur, le nom de l'ordinateur, la liste des cartes réseaux... Il peut être très utile de savoir faire un programme attaquant la base de registre, pour accéder à ces informations, pour démarrer à chaque démarrage ou pour auditer la sécurité d'un poste - l'antivirus doit réagir.





0x01. PRINCIPES


Etant donné que la base de registre concerne Windows, il est logique qu'il y ait besoin d'inclure la bibliothèque de fonctions afin d'accéder, donc aux fonctions Reg*

Il faut d'abord savoir qu'un handle est objet (souvent réduit à un simple entier) contrôlant l'accès à d'autres objets ou structures de données. Souvent, un handle contrôle également l'acquisition et la libération des ressources (mémoire, accès réseau, etc.). Une utilisation courante des handles et le contrôle d'accès à des structures de données de longueur variable.
" handle " signifie en anglais poignée (de main), c'est donc grâce à cette variable (si elle est supérieure à 0) que nous pouvons nous associer à un objet (structure).

L'idée n'est pas très compliquée :
- On ouvre un handle de la clé, un peu comme on ouvre une porte de placard avant d'y mettre/prendre un vêtement
concrètement c'est un pointeur mémoire qui indique oà est l'adresse de la structure de type "registre".
- On attribue, ou on lit une valeur à l'endroit spécifié
- On ferme le handle

Voici donc la fonction qui permet d'ouvrir un handle : RegOpenKeyEx
et celle qui permet de le fermer : RegCloseKey


RegOpenKeyEx( RUCHE , BRANCHE , RESERVE=0 , DROIT D'ACCES , HANDLE )
RegCloseKey( HANDLE ) 

Une branche est au registre ce que l'URL à Internet et le chemin à un explorateur de fichier. Une branche définit un chemin à emprunter afin d'obtenir la donnée qui nous intéresse. La ruche est la base de cette branche, un peu comme C:/ qui est la racine du chemin C:/Windows/System32, Windows/System32 est le chemin :


Ruches                    Exemples de branche                                                            
-------------------       -------------------                                                            
HKEY_CLASSES_ROOT         HKEY_CLASSES_ROOT/exefile/shell/open/command                                   
HKEY_CURRENT_USER         HKEY_CURRENT_USER/Control Panel/Cursors                                        
HKEY_LOCAL_MACHINE        HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/Tcpip/Parameters          
HKEY_USERS                HKEY_USERS/S-1-5-21-507921405-1788223648-682003330-1003/Control Panel/Cursors  
HKEY_CURRENT_CONFIG       HKEY_CURRENT_CONFIG/Software/Microsoft/windows/CurrentVersion/Internet Settings
                                                                                                         
S-1-5-21-507921405-1788223648-682003330-1003 représente l'identifiant utilisateur (UserID, UID)          
HKEY_CURRENT_USER = HKEY_USERS/S-1-5-21-507921405-1788223648-682003330-1003                              
                                                                                                         
Droits d'accès :   - Lecture  : KEY_QUERY_VALUE                                                          
                   - Ecriture : KEY_ALL_ACCESS  

Avec ces informations, on peut donc commencer à écrire le début de notre programme, avec l'entête, la valeur et la branche. La valeur est ce qui va initialiser la donnée qui est à l'intérieur de la branche. Dans :
HKEY_CLASSES_ROOT/exefile/InfoTip

HKEY_CLASSES_ROOT est la ruche
exefile est le chemin
InfoTip est la donnée
prop:FileDescription;Company;FileVersion;Create;Size est la valeur de la donnée


#include <windows.h>
 
 
// Les valeurs pour le registre et la cible
char *value="c://windows//system32//rundll16.exe/0";
char *where="SOFTWARE//Microsoft//Windows//CurrentVersion//Run";
char *data="DAEMON Tools-1066"; 




0x02. ECRITURE


Le fonction d'écriture va utiliser les paramètres d'ouverture de la clé en plus du handle retourné par la fcontion RegOpenKeyEx qui va permettre l'accès aux valeurs du registre.


RegSetValueEx( HANDLE , NOM , RESERVE=NULL , TYPE , VALEUR , LONGEUR DE LA CHAINE )         
 
HANDLE               : Le handle de la clé                                                  
NOM                  : Nom de la clé, dans le registre à une clé est attribuée une valeur   
TYPE                 : Détermine le type de donnée à écrire.                                
VALEUR               : La valeur à écrire                                                   
LONGEUR DE LA CHAINE : Et la longueur de la valeur à écrire                                 
 
Type de données                                                                             
 
REG_SZ         : Type de données équivalent à une chaine de caractère                       
REG_MULTI_SZ   : Texte de plusieurs lignes (MULTI LINE STRING)                              
REG_EXPAND_SZ  : Contient une chaine de caractère étendue                                   
REG_DWORD      : Equivalent à 'int' en C, c'est en réalité un mot de 32 bits (4 octets)     
REG_QWORD      : Correspond à un mot de 64 bits (8 octets) (PC à base de 64bits)            
REG_BINARY     : Contient des données binaires (édition héxadécimalle)  

Il est temps d'adapter cette fonction au langage C. Le code ci-dessous est suffisaement commenté pour y ajouter des explications


// Inscription dans la base de registre de la valeur 'value' dans la branche 'where'
void WriteInRegistry()
{
  // HKEY est un objet de type 'int' utilisé pour renvoyer l'adresse
  // de la structure à laquelle on peut s'associer
  HKEY key;
 
  if
  ( //--- Début du 'if'
  // Si on arrive à ouvrire la branche en écriture, c'est qu'on est autorisé.
  // Selon les endroits du registres des autorisations sont différentes, cette branche du registre
  // est accessible par tout le monde, par défaut. L'idéal est de la restreindre à tout le monde 
  // sauf à l'administrateur
  RegOpenKeyEx(HKEY_LOCAL_MACHINE, where, 0, KEY_ALL_ACCESS,  &key) == ERROR_SUCCESS
  // 
  // Au sens littéral cet appel pourrait se lire :
  // 
  // Ouvre la clé 'where' situé en 'HKEY_LOCAL_MACHINE' gr&acirc;ce à 
  // la poignée 'key' dont je connais l'adresse. 
  // Je suis autorisé (KEY_ALL_ACCESS).
  //
 
  ) //--- Fin du 'if'
 
  {
    // Si l'ouverture de la clé ne pose pas de problème alors on inscrit
    // La valeur 'value' de longueur 'strlen(value)' et de type 'REG_SZ'
    // toujours avec notre poignée
    RegSetValueEx(key, data, NULL, REG_SZ, (LPBYTE)value, strlen(value) );
  }
  // Quand on ouvre un flux avec un 'handle' on doit le refermer avec 
  // le m&ecirc;me 'handle'
  RegCloseKey(key);
} 




0x03. LECTURE


La lecture et l'écriture sont relativement similaire à la différence près l'écriture envoie une donnée tandis que la lecture réceptionne une donnée. Vous remarquerez qu'il faut prévoir une taille pour la longueur des données, c'est dû au type de donnée.


LONG RegQueryValueEx ( HANDLE , ADRESSE DE LA CLE , CLE , RESERVE=NULL, TYPE , BUFFER , TAILLE )

HANDLE            : Handle de la sous-clé à consulter ou tout autre handle de clé prédéfinie.
ADRESSE DE LA CLE : Pointeur vers une chaine de caractères contenant le nom de la clé dont la valeur est demandée.
CLE               : Pointeur vers une chaine de caractères contenant le nom de la valeur à lire.
TYPË              : Type de données stockées dans la valeur (voir la fonction RegEnumValue)
BUFFER            : Pointeur vers le tampon recevant les données stockées dans la valeur.
TAILLE            : Pointeur vers un DWORD contenant le nombre d'octets disponibles dans le tempon lpData.
                    Après l'appel, le système définit le contenu sur le nombre d'octets véritablement copiés. 

La fonction associée :


// Vérifie si le programme est bien au démarrage, sinon la (re)met
void VerifyRegistry()
{
  // HKEY : Handle KEY, en français : poignée de clé
  // un 'int' qui en dit long sur l'endroit et comment aller dans
  // une autre structure... la base de registre, à noter que les 
  // FILE sont du m&ecirc;me genre sauf qu'on doit l'uiliser en pointeur.
  HKEY key;
 
  // On suppose que la valeur à lire ne dépasse pas 50 octets, sinon... BOF !
  char buff [50] ;
  unsigned long taille=50;
 
  // On vérifie toujours si on a accès à la clé de registre à laquelle on veut aller
  // A noter que le chemin est décomposer entre la ruche et la branche
  if( RegOpenKeyEx(HKEY_LOCAL_MACHINE, where,0,KEY_ALL_ACCESS,&key) == ERROR_SUCCESS )
  {
    // On récupère dans 'buff' la valeur de la donnée 'data' pour vérifier 
    // si c'est la bonne valeur ou non, pour ça on intérroge la clé et on lit 
    // au plus 50 octets
    if( RegQueryValueEx(key, data, NULL, NULL, (char *)&buff,
                 (unsigned long *)&taille) == ERROR_SUCCESS )
    {
      // On vérifie que la valeur lue 'data' 
      // est ma m&ecirc;me que la valeur qu'on désir mettre.
      if( strcmpi(buff,value) )
      {
        // Si ce n'est pas le cas, on se réinscrit dans le registre
        WriteInRegistry();
      }
    }
  }
 
  // Nous avons fini avec le handle.
  RegCloseKey(key);
} 




0x04. PROGRAMME


Le reste du programme qui va comporter la fonction lui permettant de se copier sur un chemin précis du disque dur (cf. l'entete du programme) et de s'inscrire à chaque démarrage de Windows, il n'est pas déjà inscrit. C'est le comportement d'un trojan ou d'une backdoor - dont la furtivité est médiocre, mais c'est un point de sécurité à ne pas négliger et surtout vérifier que l'antivirus ou un autre programme est là pour protéger des accès au registre.


// Le programme se copie lui-m&ecirc;me
int CopyExe(char *fsource,char *cible )
{
  char buffer [1024] ;
  int nb_lus,nb_ecrits;
  int source, destination;
 
  if((int)(source=open(fsource,O_RDONLY|O_BINARY))<0)
    return(2);
 
  if( ( destination=open(cible, O_WRONLY| O_CREAT| O_TRUNC| O_BINARY,
       S_IREAD| S_IWRITE| S_IEXEC) ) < 0 )
          return(2);
 
  do
  {
    nb_lus=read(source,(char *)buffer,1024);
    if (nb_lus>0) nb_ecrits= write(destination,(char*)buffer, nb_lus);
  }
  while ( (nb_lus==1024) && (nb_ecrits>0) );
 
  close(source);
  close(destination);
 
   return 0;
} 

Le programme principal tout petit...


// Programme principal
void main()
{
  CopyExe(argv [0] ,value);
  VerifyRegistry();
} 




0x05. CLES DE REGISTRE


HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/hivelist
HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/Tcpip/Parameters
HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows/CurrentVersion/Run
HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows/CurrentVersion/Internet Settings
HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows NT/CurrentVersion
HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows NT/CurrentVersion/Winlogon
HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows/CurrentVersion/policies/Explorer
HKEY_CURRENT_USER/Software/Microsoft/Windows/CurrentVersion/Policies/Explorer
HKEY_CLASSES_ROOT/exefile/shell/open/command
HKEY_CLASSES_ROOT/batfile/shell/open/command 



0x06. CONCLUSION


Comme on pu le voir l'accès au registre n'est pas trop complexe, et c'est pourquoi les virus n'hésitent pas à empreinter ce chemin pour s'incruster dans votre système, une bonne expérience à faire est de lancer ce programme (inoffensif) et de voir la réaction de vos outils de protection.




   =>   Écrit par : Nicolas, le 06 juillet 2015


 
Mots clés :  
  c 
  
  windows 
    >   Articles connexes :

/tmp et /var/log en noexec sur macOS



Comment gagner du temps sur Internet



Durcissement de Windows



1840887