GNU/Linux >> Tutoriels Linux >  >> Linux

Outils d'ingénierie inverse sous Linux - chaînes, nm, ltrace, strace, LD_PRELOAD

Cet article explique les outils et les commandes qui peuvent être utilisés pour désosser un exécutable dans un environnement Linux.

L'ingénierie inverse est l'acte de comprendre ce que fait un logiciel, pour lequel il n'y a pas de code source disponible. L'ingénierie inverse peut ne pas vous donner les détails exacts du logiciel. Mais vous pouvez assez bien comprendre comment un logiciel a été implémenté.

L'ingénierie inverse implique les trois étapes de base suivantes :

  1. Recueillir les informations
  2. Déterminer le comportement du programme
  3. Intercepter les appels de la bibliothèque

Je. Rassembler les informations

La première étape consiste à rassembler les informations sur le programme cible et ce qu'il fait. Pour notre exemple, nous prendrons la commande « who ». La commande ‘who’ imprime la liste des utilisateurs actuellement connectés.

1. Commande de chaînes

Strings est une commande qui imprime les chaînes de caractères imprimables dans les fichiers. Alors maintenant, utilisons cela contre notre commande cible (qui).

# strings /usr/bin/who

Certaines des chaînes importantes sont,

users=%lu
EXIT
COMMENT
IDLE
TIME
LINE
NAME
/dev/
/var/log/wtmp
/var/run/utmp
/usr/share/locale
Michael Stone
David MacKenzie
Joseph Arceneaux

À partir de la sortie about, nous pouvons savoir que « who » utilise 3 fichiers (/var/log/wtmp, /var/log/utmp, /usr/share/locale).

En savoir plus :Exemples de commandes de chaînes Linux (rechercher du texte dans des fichiers binaires UNIX)

2. Commande nm

nm, est utilisée pour lister les symboles du programme cible. En utilisant nm, nous pouvons connaître les fonctions locales et de bibliothèque ainsi que les variables globales utilisées. nm ne peut pas fonctionner sur un programme qui est rayé à l'aide de la commande "strip".

Remarque :Par défaut, la commande « who » est supprimée. Pour cet exemple, j'ai de nouveau compilé la commande "who".

# nm /usr/bin/who

Cela listera les éléments suivants :

08049110 t print_line
08049320 t time_string
08049390 t print_user
08049820 t make_id_equals_comment
080498b0 t who
0804a170 T usage
0804a4e0 T main
0804a900 T set_program_name
08051ddc b need_runlevel
08051ddd b need_users
08051dde b my_line_only
08051de0 b time_format
08051de4 b time_format_width
08051de8 B program_name
08051d24 D Version
08051d28 D exit_failure

Dans la sortie ci-dessus :

  • t|T – Le symbole est présent dans la section de code .text
  • b|B – Le symbole est dans la section .data initialisée UN
  • D|d – Le symbole est dans la section Initialized .data.

La lettre majuscule ou minuscule détermine si le symbole est local ou global.

À partir de la sortie à propos, nous pouvons savoir ce qui suit,

  • Il a la fonction globale (main,set_program_name,usage,etc..)
  • Il a quelques fonctions locales (print_user,time_string etc..)
  • Il a des variables globales initialisées (Version,exit_failure)
  • Il contient les variables initialisées par l'ONU (time_format, time_format_width, etc.)

Parfois, en utilisant les noms des fonctions, nous pouvons deviner ce que les fonctions vont faire.

Lire la suite :10 exemples pratiques de commandes Linux nm

Les autres commandes qui peuvent être utilisées pour obtenir des informations sont

  • commande ldd
  • commande fuser
  • commande lsof
  • /système de fichiers proc

II. Détermination du comportement du programme

3. Commande ltrace

Il trace les appels à la fonction de bibliothèque. Il exécute le programme dans ce processus.

# ltrace /usr/bin/who

La sortie est illustrée ci-dessous.

utmpxname(0x8050c6c, 0xb77068f8, 0, 0xbfc5cdc0, 0xbfc5cd78)          = 0
setutxent(0x8050c6c, 0xb77068f8, 0, 0xbfc5cdc0, 0xbfc5cd78)          = 1
getutxent(0x8050c6c, 0xb77068f8, 0, 0xbfc5cdc0, 0xbfc5cd78)          = 0x9ed5860
realloc(NULL, 384)                                                   = 0x09ed59e8
getutxent(0, 384, 0, 0xbfc5cdc0, 0xbfc5cd78)                         = 0x9ed5860
realloc(0x09ed59e8, 768)                                             = 0x09ed59e8
getutxent(0x9ed59e8, 768, 0, 0xbfc5cdc0, 0xbfc5cd78)                 = 0x9ed5860
realloc(0x09ed59e8, 1152)                                            = 0x09ed59e8
getutxent(0x9ed59e8, 1152, 0, 0xbfc5cdc0, 0xbfc5cd78)                = 0x9ed5860
realloc(0x09ed59e8, 1920)                                            = 0x09ed59e8
getutxent(0x9ed59e8, 1920, 0, 0xbfc5cdc0, 0xbfc5cd78)                = 0x9ed5860
getutxent(0x9ed59e8, 1920, 0, 0xbfc5cdc0, 0xbfc5cd78)                = 0x9ed5860
realloc(0x09ed59e8, 3072)                                            = 0x09ed59e8
getutxent(0x9ed59e8, 3072, 0, 0xbfc5cdc0, 0xbfc5cd78)                = 0x9ed5860
getutxent(0x9ed59e8, 3072, 0, 0xbfc5cdc0, 0xbfc5cd78)                = 0x9ed5860
getutxent(0x9ed59e8, 3072, 0, 0xbfc5cdc0, 0xbfc5cd78)

Vous pouvez observer qu'il existe un ensemble d'appels à getutxent et sa famille de fonctions de bibliothèque. Vous pouvez également noter que ltrace donne les résultats dans l'ordre dans lequel les fonctions sont appelées dans le programme.

Nous savons maintenant que la commande « who » fonctionne en appelant le getutxent et sa famille de fonctions pour obtenir les utilisateurs connectés.

4. Commande strace

La commande strace est utilisée pour tracer les appels système effectués par le programme. Si un programme n'utilise aucune fonction de bibliothèque et qu'il n'utilise que des appels système, alors en utilisant plain ltrace, nous ne pouvons pas suivre l'exécution du programme.

# strace /usr/bin/who
[b76e7424] brk(0x887d000)               = 0x887d000
[b76e7424] access("/var/run/utmpx", F_OK) = -1 ENOENT (No such file or directory)
[b76e7424] open("/var/run/utmp", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 3
.
.
.
[b76e7424] fcntl64(3, F_SETLKW, {type=F_RDLCK, whence=SEEK_SET, start=0, len=0}) = 0
[b76e7424] read(3, "\10\325"..., 384) = 384
[b76e7424] fcntl64(3, F_SETLKW, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0

Vous pouvez observer que chaque fois que la fonction malloc est appelée, elle appelle l'appel système brk(). La fonction de la bibliothèque getutxent appelle en fait l'appel système 'open' pour ouvrir '/var/run/utmp' et il met un verrou en lecture et lit le contenu puis libère les verrous.

Maintenant, nous avons confirmé que la commande who a lu le fichier utmp pour afficher la sortie.

'strace' et 'ltrace' ont tous deux un ensemble de bonnes options qui peuvent être utilisées.

  • -p pid – Attache au pid spécifié. Utile si le programme est déjà en cours d'exécution et que vous souhaitez connaître son comportement.
  • -n 2 – Indenter chaque appel imbriqué de 2 espaces.
  • -f - Suivre la fourche

Lire la suite :7 exemples Strace pour déboguer l'exécution d'un programme sous Linux

III. Intercepter les appels de la bibliothèque

5. LD_PRELOAD &LD_LIBRARY_PATH

LD_PRELOAD nous permet d'ajouter une bibliothèque à une exécution particulière du programme. La fonction de cette bibliothèque écrasera la fonction de bibliothèque actuelle.

Remarque :Nous ne pouvons pas l'utiliser avec des programmes définis avec le bit "suid".

Prenons le programme suivant.

#include <stdio.h>
int main() {
  char str1[]="TGS";
  char str2[]="tgs";
  if(strcmp(str1,str2)) {
    printf("String are not matched\n");
  }
  else {
    printf("Strings are matched\n");
  }
}

Compilez et exécutez le programme.

# cc -o my_prg my_prg.c
# ./my_prg

Il affichera "Les chaînes ne correspondent pas".

Nous allons maintenant écrire notre propre bibliothèque et nous verrons comment nous pouvons intercepter la fonction de bibliothèque.

#include <stdio.h>
int strcmp(const char *s1, const char *s2) {
  // Always return 0.
  return 0;
}

Compilez et définissez la variable LD_LIBRARY_PATH sur le répertoire courant.

# cc -o mylibrary.so -shared library.c -ldl
# LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH

Maintenant, un fichier nommé "library.so" sera créé.
Définissez la variable LD_PRELOAD sur ce fichier et exécutez le programme de comparaison de chaînes.

# LD_PRELOAD=mylibrary.so ./my_prg

Maintenant, il affichera "Les chaînes correspondent" car il utilise notre version de la fonction strcmp.

Remarque :Si vous souhaitez intercepter une fonction de bibliothèque, votre propre fonction de bibliothèque doit avoir le même prototype que la fonction de bibliothèque d'origine.

Nous venons de couvrir les éléments de base nécessaires à la rétro-ingénierie d'un programme.

Pour ceux qui souhaitent passer à l'étape suivante de l'ingénierie inverse, la compréhension du format de fichier ELF et du programme de langage d'assemblage aidera dans une plus grande mesure.


Linux
  1. 5 outils Rust à essayer sur la ligne de commande Linux

  2. 5 outils de ligne de commande pour trouver rapidement des fichiers sous Linux

  3. Commande Linux mv

  4. Linux du command

  5. Commande Linux pour rechercher des chaînes dans un fichier binaire ou non ascii

Tutoriel de commande de chaînes Linux pour les débutants (5 exemples)

Tutoriel de commande Linux strace pour les débutants (8 exemples)

Comment suivre l'exécution d'un programme à l'aide de la commande Linux Strace

11 Commande Strace avec exemple sous Linux

Outils Sysadmin :11 façons d'utiliser la commande ls sous Linux

Comment utiliser la commande Linux Strace