2016-11-16 1 views
1

Ich habe eine sehr einfache ELF ausführbare:Warum bildet der Linux-Kernel mein RW-Segment als RWX ab?

$ readelf -l ./plt.out 

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

Program Headers: 
    Type   Offset    VirtAddr   PhysAddr 
       FileSiz   MemSiz    Flags Align 
    PHDR   0x0000000000000040 0x00000000003ff040 0x00000000003ff040 
       0x0000000000000188 0x0000000000000188 R E 8 
    LOAD   0x0000000000000000 0x00000000003ff000 0x00000000003ff000 
       0x0000000000001000 0x0000000000001000 RW  1000 
    INTERP   0x00000000000001c8 0x00000000003ff1c8 0x00000000003ff1c8 
       0x0000000000000032 0x0000000000000032 R  1 
     [Requesting program interpreter: /data/keno/new_glibc/usr/lib/ld-linux-x86-64.so.2] 
    LOAD   0x0000000000001000 0x0000000000400000 0x0000000000400000 
       0x00000000000003b0 0x00000000000003b0 R E 1000 
    LOAD   0x0000000000001ea0 0x0000000000600ea0 0x0000000000600ea0 
       0x0000000000000180 0x0000000000000180 RW  1000 
    DYNAMIC  0x0000000000001ea0 0x0000000000600ea0 0x0000000000600ea0 
       0x0000000000000150 0x0000000000000150 RW  8 
    GNU_RELRO  0x0000000000001ea0 0x0000000000600ea0 0x0000000000600ea0 
       0x0000000000000160 0x0000000000000160 R  1 

nun von meinem Verständnis davon, wie ELF funktioniert, würde ich drei Segmente erwarten:

  • One RW von 0x3ff000-0x400000
  • One RX von 0x400000-0x401000
  • One RW von 0x600000-0x602000 (0xea0+0x180 > 0x1000)
Jedoch

, wenn ich mir eigentlich, was ich bekommen, während die ausführbare Datei ausgeführt wird, verwendet /proc/pid/maps, sehe ich folgendes:

003ff000-00400000 rwxp 00000000 00:28 1456774       plt.out 
00400000-00401000 r-xp 00001000 00:28 1456774       plt.out 
00600000-00601000 r-xp 00001000 00:28 1456774       plt.out 
00601000-00602000 rwxp 00002000 00:28 1456774       plt.out 

, die überhaupt nicht, was ich erwartet hatte. Was ist denn hier los?

Antwort

5

Die Antwort hier ist zweifach, wobei ein Teil vom dynamischen Linker, der andere vom Kernel beigetragen wird. Um dies zu sehen, lassen Sie uns die Speicherzuordnung direkt nach dem Eingeben des dynamischen Linkers betrachten (z. B. indem Sie einen Haltepunkt in _dl_start setzen). Wir sehen:

003ff000-00400000 rwxp 00000000 00:28 1456774       plt.out 
00400000-00401000 r-xp 00001000 00:28 1456774       plt.out 
00600000-00602000 rwxp 00001000 00:28 1456774       plt.out 

, die zumindest näher an dem, was wir wollten (es hat die richtigen Segmente, an den richtigen Stellen). Der Grund, warum das letzte Segment aufgeteilt wird, ist der GNU_RELRO-Programmheader, der besagt, dass der dynamische Linker "Hey, ich werde das nicht mehr schreiben müssen, nachdem du mit deinen ersten Verlagerungen fertig bist", so the dynamic linker faithfully tries to set that region of memory to PROT_READ (Beachten Sie, dass es die tatsächlichen Berechtigungsflags ignoriert, die im Programmheader festgelegt wurden, obwohl sie konventionell auf PF_R festgelegt sind).

Das ist nur das halbe Geheimnis. Wir haben immer noch diese lästigen PROT_EXEC Bits übrig, die wir nicht bestellt haben. Es stellt sich heraus, dass diese auf eine Eigenschaft des Linux-Kernels namens READ_IMPLIES_EXEC Persönlichkeit zurückzuführen sind, die bewirkt, dass alle Maps mit der Berechtigung PROT_READ auch über die Berechtigung PROT_EXEC verfügen (siehe man page for personality(2)). Es stellt sich heraus, dass aus Kompatibilitätsgründen, Linux automatically sets this personality, es sei denn, ein PT_GNU_STACK Programmkopf sagt es nicht. Der Linker erstellt diesen Programmkopf automatisch, wenn alle Eingabeobjekte einen (leeren) Abschnitt .note.GNU-stack haben. Weitere Informationen zu diesem Mechanismus finden Sie unter here.

Verwandte Themen