vendredi 23 février 2018

La pie et les pingouins

 

 La Pie et les Pingouins

Merci à Alice, Arthur et Yurug  pour le titre.

Ne vous méprenez pas, il n'est pas question que je me prenne pour "un éditeur affable comme il y en a depuis Esope" (Bobby Lapointe, évidemment).

Tout espoir de trouver là des histoires de volatiles, s'est envolé.

Ma plume, j'en ai peur, n'est point duveteuse.


Programme inutile

J'adore les programmes inutiles.

fa$ cat unepie.c
#include


int main(int argc, char **argv)

    printf("Y'a une pie dans le poirier,\n"); 
    printf("J'entends la pie qui chante...\n");
    return 0; 

fa$

Prenons, 2 distributions Linux

Ubuntu 16.4Debian 9.1
fa$ lsb_release -a
Distributor ID:Ubuntu
Description:   Ubuntu 16.04.2 LTS
Release:       16.04
Codename:      xenial

fa$
fa$ lsb_release -a
Distributor ID: Debian
Description:    Debian GNU/Linux 9.1 (stretch)
Release:        9.1
Codename:       stretch

fa$

Compilons, exécutons notre programme.

Ubuntu 16.4Debian 9.1
fa$ gcc unepie.c -o unepie
fa$ ./unepie
Y'a une pie dans le poirier,
J'entends la pie qui chante...
fa$
fa$ gcc unepie.c -o unepie
fa$ ./unepie
Y'a une pie dans le poirier,
J'entends la pie qui chante...
fa$

Pas de quoi s'émouvoir, je suis bien d'accord. Cependant..

Des formats différents!

Ubuntu 16.4
fa$ file -b ./unepie
ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=f528ec2c46a51d998c04d42b86d6d1a50331d378, not stripped

fa$
Debian 9.1
fa$ file -b ./unepie
ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=587bb830a101539c950ea7999b43442ebf7bd037, not stripped

fa$

Rire sardonique emprunté à DjiBee.

Sous Ubuntu 16.04, le binaire exécutable est conformément à nos attentes un binaire exécutable. Mais sous Debian 9.1 (et je crois Ubuntu 17), le binaire exécutable n'est pas, n'est plus un binaire exécutable. Et pourtant il s'exécute!

readelf à la rescousse ?

Ubuntu 16.4
fa$ readelf -l unepie

Elf file type is EXEC (Executable file)
Entry point 0x400430
There are 9 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                 0x00000000000001f8 0x00000000000001f8  R E    8
  INTERP         0x0000000000000238 0x0000000000400238 0x0000000000400238
                 0x000000000000001c 0x000000000000001c  R      1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x0000000000000744 0x0000000000000744  R E    200000
  LOAD           0x0000000000000e10 0x0000000000600e10 0x0000000000600e10
                 0x0000000000000228 0x0000000000000230  RW     200000

fa$
Debian 9.1
fa$ readelf -l unepie

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

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040
                 0x00000000000001f8 0x00000000000001f8  R E    0x8
  INTERP         0x0000000000000238 0x0000000000000238 0x0000000000000238
                 0x000000000000001c 0x000000000000001c  R      0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x00000000000008f4 0x00000000000008f4  R E    0x200000
  LOAD           0x0000000000000dd8 0x0000000000200dd8 0x0000000000200dd8
                 0x0000000000000258 0x0000000000000260  RW     0x200000


fa$

Sous Ubuntu, le binaire exécutable a les caractéristiques habituelles.  Sous Debian 9.1, le fichier ELF généré est différent: son type est DYN au lieu de EXEC mais il a un point d'entrée, et l'adresse virtuelle du texte est à 0 en lieu et place des 0x...0400000 proposés par Ubuntu.

Pourrait-on trouver d'autres exemples de fichiers de type "Shared object file" ?

Les librairies


Si on regarde la librairie C sous Debian 9.1:

Debian 9.1
fa$ readelf -l /lib/x86_64-linux-gnu/libc-2.24.so

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

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040
                 0x0000000000000230 0x0000000000000230  R E    0x8
  INTERP         0x000000000016aee0 0x000000000016aee0 0x000000000016aee0
                 0x000000000000001c 0x000000000000001c  R      0x10
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x00000000001948e8 0x00000000001948e8  R E    0x200000
  LOAD           0x00000000001957c8 0x00000000003957c8 0x00000000003957c8
                 0x0000000000004f38 0x00000000000091d8  RW     0x200000
 
fa$

C'est très similaire à notre binaire "unepie" sur Debian 9.1.
Notre binaire serait-il une librairie ?

Pas tout à fait.

On nous cache tout, on nous dit rien...

(Jacques Dutronc)

En fait, Debian 9.1 génère automatiquement les binaires exécutables sous forme
"PIE". Ça consiste à générer le texte et les données du binaire comme on le fait pour des librairies dynamiques partagées. Pour les librairies, on utilise l'option -fPIC. Ici, on utilise l'option -fPIE (pour le compilateur) et -pie (pour l'éditeur de liens).

Oui, mais ! Je n'ai rien demandé à gcc.

En fait, gcc ne demande rien, mais est pré-configuré pour compiler avec un certain nombres d'options. Ce que l'on peut observer avec l'option -v de gcc, comme ci-dessous:

Debian 9.1
fa$ gcc -v unepie.c -o unepie
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/6/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 6.3.0-18' ... --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ ...

 --enable-default-pie .... --target=x86_64-linux-gnu
Thread model: posix
gcc version 6.3.0 20170516 (Debian 6.3.0-18)
COLLECT_GCC_OPTIONS='-v' '-o' 'unepie' '-mtune=generic' '-march=x86-64'
 /usr/lib/gcc/x86_64-linux-gnu/6/cc1 -quiet -v -imultiarch x86_64-linux-gnu unepie.c -quiet -dumpbase unepie.c -mtune=generic -march=x86-64 -auxbase unepie -version -o /tmp/ccQvdgZ6.s
fa$

Le résultat ci-dessus a été considérablement expurgé.
Si on effectue la même commande dans le monde Ubuntu 16.4, on s'aperçoit que l'option PIE n'est pas configurée par défaut.

Si on veut se débarrasser de la chose, il faut le dire au compilateur :


Debian 9.1
fa$ gcc unepie.c -o unepie -fno-PIE -no-pie
fa$ file -b unepie
ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=4b5d9a8c53c61f07d857612460a2cd2d7742a82d, not stripped

fa$

Pourquoi ?

La raison principale pour générer des binaires en mode PIE, est de pouvoir étendre la protection fournie par le mécanisme d'ASLR aux régions de code et de données. Sans le mode PIE, la protection est limitée aux régions de pile, de librairies partagées et aux régions créées par mmap.

Une curiosité pour finir


Debian 9.1 ou Ubuntu 16.4
fa$  /lib/x86_64-linux-gnu/libc.so.6
GNU C Library (Ubuntu GLIBC 2.23-0ubuntu10) stable release version 2.23, by Roland McGrath et al.
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 5.4.0 20160609.
Available extensions:
    crypt add-on version 2.1 by Michael Glad and others
    GNU Libidn by Simon Josefsson
    Native POSIX Threads Library by Ulrich Drepper et al
    BIND-8.2.3-T5B
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
.
fa$

La librairie C est exécutable! (Merci à Xavier R.)
Elle donne la version!

Ce n'est pas vrai de toutes les bibliothèques partagées.  Seules quelques unes ont des droits rwx permettant une exécution.

Pour compléter

Et sinon