GNU/Linux >> Tutoriels Linux >  >> Linux

vérifier si un binaire compilé avec -static

Vous pouvez également utiliser le file commande (et objdump pourrait également être utile).


Vérifiez s'il a un en-tête de programme de type INTERP

Au niveau inférieur, un exécutable est statique s'il n'a pas d'en-tête de programme de type :

Elf32_Phd.p_type == PT_INTERP

Ceci est mentionné dans la spécification System V ABI.

Rappelez-vous que les en-têtes de programme déterminent les segments ELF, y compris ceux de type PT_LOAD qui sera chargé en mémoire et exécuté.

Si cet en-tête est présent, son contenu correspond exactement au chemin du chargeur dynamique.

readelf vérifier

Nous pouvons observer cela avec readelf . Commencez par compiler dynamiquement un monde C hello :

gcc -o main.out main.c

puis :

readelf --program-headers --wide main.out

sorties :

Elf file type is DYN (Shared object file)
Entry point 0x1050
There are 11 program headers, starting at offset 64

Program Headers:
  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
  PHDR           0x000040 0x0000000000000040 0x0000000000000040 0x000268 0x000268 R   0x8
  INTERP         0x0002a8 0x00000000000002a8 0x00000000000002a8 0x00001c 0x00001c R   0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x000000 0x0000000000000000 0x0000000000000000 0x000560 0x000560 R   0x1000
  LOAD           0x001000 0x0000000000001000 0x0000000000001000 0x0001bd 0x0001bd R E 0x1000
  LOAD           0x002000 0x0000000000002000 0x0000000000002000 0x000150 0x000150 R   0x1000
  LOAD           0x002db8 0x0000000000003db8 0x0000000000003db8 0x000258 0x000260 RW  0x1000
  DYNAMIC        0x002dc8 0x0000000000003dc8 0x0000000000003dc8 0x0001f0 0x0001f0 RW  0x8
  NOTE           0x0002c4 0x00000000000002c4 0x00000000000002c4 0x000044 0x000044 R   0x4
  GNU_EH_FRAME   0x00200c 0x000000000000200c 0x000000000000200c 0x00003c 0x00003c R   0x4
  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x10
  GNU_RELRO      0x002db8 0x0000000000003db8 0x0000000000003db8 0x000248 0x000248 R   0x1

 Section to Segment mapping:
  Segment Sections...
   00
   01     .interp
   02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
   03     .init .plt .plt.got .text .fini
   04     .rodata .eh_frame_hdr .eh_frame
   05     .init_array .fini_array .dynamic .got .data .bss
   06     .dynamic
   07     .note.ABI-tag .note.gnu.build-id
   08     .eh_frame_hdr
   09
   10     .init_array .fini_array .dynamic .got

alors notez le INTERP header est là, et il est si important que readelf a même donné un aperçu rapide de son court contenu de 28 octets (0x1c) :/lib64/ld-linux-x86-64.so.2 , qui est le chemin vers le chargeur dynamique (27 octets de long + 1 pour \0 ).

Notez comment cela réside côte à côte avec les autres segments, y compris par ex. ceux qui sont réellement chargés en mémoire tels que :.text .

Nous pouvons ensuite extraire plus directement ces octets sans l'aperçu avec :

readelf -x .interp main.out

ce qui donne :

Hex dump of section '.interp':
  0x000002a8 2f6c6962 36342f6c 642d6c69 6e75782d /lib64/ld-linux-
  0x000002b8 7838362d 36342e73 6f2e3200          x86-64.so.2.

comme expliqué à :Comment puis-je examiner le contenu d'une section de données d'un fichier ELF sous Linux ?

file code source

file 5.36 Les commentaires du code source sur src/readelf.c affirment qu'il vérifie également PT_INTERP :

/*
 * Look through the program headers of an executable image, searching
 * for a PT_INTERP section; if one is found, it's dynamically linked,
 * otherwise it's statically linked.
 */
private int
dophn_exec(struct magic_set *ms, int clazz, int swap, int fd, off_t off,
    int num, size_t size, off_t fsize, int sh_num, int *flags,
    uint16_t *notecount)
{
    Elf32_Phdr ph32;
    Elf64_Phdr ph64;
    const char *linking_style = "statically";

trouvé avec git grep statically du message main.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped .

Cependant, ce commentaire semble être obsolète par rapport au code, qui vérifie à la place PT_DYNAMIC :

    case PT_DYNAMIC:
        linking_style = "dynamically";
        doread = 1;
        break;

Je ne sais pas pourquoi cela est fait, et j'ai la flemme de creuser dans git log à présent. En particulier, cela m'a un peu dérouté lorsque j'ai essayé de créer un exécutable PIE lié statiquement avec --no-dynamic-linker comme indiqué sur:Comment créer un ELF exécutable indépendant de la position lié statiquement sous Linux? qui n'a pas PT_INTERP mais a PT_DYNAMIC , et que je ne m'attends pas à utiliser le chargeur dynamique.

J'ai fini par faire une analyse de source plus approfondie pour -fPIE at :Pourquoi GCC crée-t-il un objet partagé au lieu d'un binaire exécutable en fonction du fichier ? la réponse s'y trouve probablement aussi.

Code source du noyau Linux

Le noyau Linux 5.0 lit le fichier ELF lors de l'appel système exec à fs/binfmt_elf.c comme expliqué à :Comment le noyau obtient-il un fichier binaire exécutable fonctionnant sous linux ?

Le noyau boucle sur les en-têtes de programme à load_elf_binary

    for (i = 0; i < loc->elf_ex.e_phnum; i++) {
        if (elf_ppnt->p_type == PT_INTERP) {
            /* This is the program interpreter used for
             * shared libraries - for now assume that this
             * is an a.out format binary
             */

Je n'ai pas entièrement lu le code, mais je m'attendrais alors à ce qu'il n'utilise le chargeur dynamique que si INTERP est trouvé, sinon quel chemin doit-il utiliser ?

PT_DYNAMIC n'est pas utilisé dans ce fichier.

Bonus :vérifiez si -pie a été utilisé

J'ai expliqué cela en détail sur :Pourquoi GCC crée-t-il un objet partagé au lieu d'un binaire exécutable en fonction du fichier ?


ldd /path/to/binary ne doit pas répertorier les bibliothèques partagées si le binaire est compilé statiquement.


Linux
  1. Patcher un binaire avec Dd ?

  2. Exécuter un programme avec divers paramètres (boucle) ?

  3. Le programme de thread C++ simple ne peut pas être compilé ?

  4. Comment créer des threads sous Linux (avec un exemple de programme C)

  5. Mon chargeur de démarrage ne peut pas être compilé avec gcc 4.6 et 4.7 ... seulement 4.5

Bash For Loop avec des exemples pratiques

Gérer les machines virtuelles KVM avec le programme Virsh

Puis-je changer 'rpath' dans un binaire déjà compilé ?

ouvrir un fichier avec le programme par défaut dans node-webkit

Exécution d'un script bash ou d'un binaire c sur un système de fichiers avec l'option noexec

Comment configurer monit pour démarrer un processus avec un utilisateur spécifique ?