Les faux points d’arrêt    Enregistrer au format PDF

Cette technique s’appuie sur les différences d’éxécution entre un programme seul et un programme dans un débuggueur. Une différence essentielle est l’interprétation du caractères int 3, en hexadécimal, 0xCC. Ce caractère constitue les fameux breakpoints.


par S3cur3D

Ces points d’arrêt sont associés au signal SIGTRAP. Quand un débuggueur lit un caractère 0xCC, il mets le programme en pause (ce qui permet de l’analyser à l’instant X). Quand un programme reçoit un SIGTRAP, il effectue l’action associée au signal (par défaut, il quitte). Le principe de cette méthode est simple : on modifie l’action effectuée par un SIGTRAP. Donc, un programme éxécuté normalement va suivre cette action, alors qu’un programme en cours de debug va se mettre en pause, puis continuer (et être piégé dans une partie du programme qui lui sera bien sûr réservée).

Illustration

Voici un exemple d’utilisation des faux breakpoints. Le programme suivant est le même que celui de la partie ptrace, légèrement modifié :

auth.cpp : Exemple de l’utilisation d’un faux breakpoint

  1.     #include <signal.h>
  2.  
  3.     #include <iostream>
  4.         using std::cout;
  5.         using std::cin;
  6.         using std::endl;
  7.     #include <string>
  8.         using std::string;
  9.     #define MDP "exemple_pass"
  10.  
  11.     void authentification(int signo) {
  12.         string pass;
  13.  
  14.         cout << "Authentification requise\nMot de passe :\t";
  15.         cin >> pass;   //Capture clavier
  16.  
  17.         if (pass == MDP)   //Si la chaîne rentrée au clavier est pareil que la chaîne MDP définie plus tôt
  18.         cout << "Authentification réussie, bienvenue dans la suite du programme" << endl;
  19.  
  20.         else cout << "Echec de l'authentification\nAbandon..." << endl;   //Sinon
  21.  
  22.         exit(0);
  23.     }
  24.  
  25.     int main() {
  26.  
  27.         signal(SIGTRAP, authentification);   //réception de SIGTRAP => authentification()
  28.         __asm__("int3");   //On pose un breakpoint qui va envoyer un SIGTRAP
  29.  
  30.         return 1;   //On quitte en état d'erreur
  31.     }

Télécharger

Le return 1 ; va juste nous permettre de prouver que le débugger a terminé dans cette partie du programme, contrairement à ce qui se passe en temps normal. En réalité, on utilise souvent ce genre de piège pour faire un faux clône de la suite réell du programme. Le cracker va essayer de la cracker sans comprendre pourquoi le vrai programme ne se soumet pas à ses ordres. Cette technique s’inscrit plutôt dans une optique de découragement du reverser. Voici un exemple d’utilisation du programme puis ce qui se passe en utilisant gdb :

   $ g++ auth.cpp -o auth && ./auth
   Authentification requise
   Mot de passe : testdepass
   Echec de l'authentification
   Abandon...
   $ ./auth
   Authentification requise
   Mot de passe : exemple_pass
   Authentification réussie, bienvenue dans la suite du programme
   $ gdb -q auth
   Using host libthread_db library "/lib/libthread_db.so.1".
   (gdb) r
   Starting program: /home/SeriousHack/auth
   Failed to read a valid object file image from memory.

   Program received signal SIGTRAP, Trace/breakpoint trap.
   0x0804894c in main ()
   (gdb) c
   Continuing.

   Program exited with code 01.

Comme prévu, bien que tout marche parfaitement en utilisation normale, le programme quitte tout de suite avec un code de sortie 01, ce qui correspond à ce que nous avons codé. Cette protection étant aisément contournable, on prend l’habitude de la protéger par faux désassemblage et par checksum, ce que nous nous proposons d’étudier maintenant.

Documentations publiées dans cette rubrique