Wie bekommt der Kernel eine ausführbare Binärdatei, die unter Linux läuft?

Lesezeit: 6 Minuten

Benutzer-Avatar
Daniel

Wie bekommt der Kernel eine ausführbare Binärdatei, die unter Linux läuft?

Es scheint eine einfache Frage zu sein, aber jemand kann mir helfen, tief zu graben? Wie wird die Datei in den Speicher geladen und wie wird der Ausführungscode gestartet?

Kann mir jemand helfen und Schritt für Schritt sagen, was passiert?

Benutzer-Avatar
Ciro Santilli OurBigBook.com

Die besten Momente der exec Systemaufruf unter Linux 4.0

Der beste Weg, all das herauszufinden, ist das GDB-Step-Debuggen des Kernels mit QEMU: Wie debuggt man den Linux-Kernel mit GDB und QEMU?

  • fs/exec.c definiert den Systemaufruf at SYSCALL_DEFINE3(execve

    Einfach weiterleiten an do_execve.

  • do_execve

    Weiterleiten an do_execveat_common.

  • do_execveat_common

    Um die nächste Hauptfunktion zu finden, verfolgen Sie den Rückgabewert retval ist zuletzt geändert.

    Beginnt mit dem Bau von a struct linux_binprm *bprm um das Programm zu beschreiben, und übergibt es an exec_binprm ausführen.

  • exec_binprm

    Folgen Sie erneut dem Rückgabewert, um den nächsten größeren Aufruf zu finden.

  • search_binary_handler

    • Handler werden durch die ersten magischen Bytes der ausführbaren Datei bestimmt.

      Die beiden häufigsten Handler sind die für interpretierte Dateien (#! Magie) und für ELF (\x7fELF magic), aber es gibt noch andere in den Kernel eingebaute, zB a.out. Und Benutzer können sich aber auch selbst registrieren /proc/sys/fs/binfmt_misc

      Der ELF-Handler ist unter definiert fs/binfmt_elf.c.

      Siehe auch: Warum schreiben die Leute den #!/usr/bin/env python shebang in die erste Zeile eines Python-Skripts?

    • Das formats Liste enthält alle Handler.

      Jede Handler-Datei enthält etwas wie:

      static int __init init_elf_binfmt(void)
      {
          register_binfmt(&elf_format);
          return 0;
      }
      

      und elf_format ist ein struct linux_binfmt in dieser Datei definiert.

      __init ist magisch und fügt diesen Code in einen magischen Abschnitt ein, der aufgerufen wird, wenn der Kernel startet: Was bedeutet __init im Linux-Kernel-Code?

      Abhängigkeitsinjektion auf Linker-Ebene!

    • Es gibt auch einen Rekursionszähler, falls sich ein Interpreter unendlich selbst ausführt.

      Versuche dies:

      echo '#!/tmp/a' > /tmp/a
      chmod +x /tmp/a
      /tmp/a
      
    • Wieder einmal folgen wir dem Rückgabewert, um zu sehen, was als nächstes kommt, und sehen, dass es kommt von:

      retval = fmt->load_binary(bprm);
      

      wo load_binary ist für jeden Handler in der Struktur definiert: Polymorphismus im C-Stil.

  • fs/binfmt_elf.c:load_binary

    Funktioniert eigentlich:

    • parsen der ELF-Datei gemäß der ELF-Spezifikation, hier ist eine Übersicht über das ELF-Dateiformat: How to make an executable ELF file in Linux using a hex editor?
    • Richten Sie den anfänglichen Programmstatus des Prozesses basierend auf der geparsten ELF-Datei ein, insbesondere:
      • anfängliche Registereinrichtung in a struct pt_regs
      • Bei der anfänglichen Einrichtung des virtuellen Speichers wird der Speicher in den ELF-Segmenten angegeben: Was ist der Unterschied zwischen Abschnitt und Segment im ELF-Dateiformat
      • Anruf start_threadwodurch der Prozess als verfügbar markiert wird, um vom Planer geplant zu werden
  • Schließlich entscheidet der Scheduler, den Prozess auszuführen, und muss dann zu der in gespeicherten PC-Adresse springen struct pt_regs und gleichzeitig in einen weniger privilegierten CPU-Zustand wie Ring 3 / EL0 wechseln: Was sind Ring 0 und Ring 3 im Kontext von Betriebssystemen?

    Der Scheduler wird regelmäßig durch eine Uhrenhardware geweckt, die periodisch Interrupts generiert, wie sie beispielsweise zuvor vom Kernel konfiguriert wurden das alte x86 PIT oder der ARM-Timer. Der Kernel registriert auch Handler, die den Scheduler-Code ausführen, wenn die Timer-Interrupts ausgelöst werden.

TODO: Quellenanalyse weiter fortsetzen. Was ich als nächstes erwarte:

  • Der Kernel analysiert den INTERP-Header des ELF, um den dynamischen Loader zu finden (normalerweise auf /lib64/ld-linux-x86-64.so.2).
  • falls vorhanden:
    • Der Kernel bildet den dynamischen Lader und die auszuführende ELF in den Speicher ab
    • Der dynamische Lader wird gestartet und nimmt einen Zeiger auf die ELF im Speicher.
    • Jetzt im Userland analysiert der Loader irgendwie elf Header und tut es dlopen auf sie
    • dlopen verwendet einen konfigurierbaren Suchpfad, um diese Bibliotheken zu finden (ldd und Freunde), speichere sie und informiere die ELF irgendwie, wo sie ihre fehlenden Symbole finden kann
    • Loader ruft die _start der ELF
  • andernfalls lädt der Kernel die ausführbare Datei direkt ohne den dynamischen Lader in den Arbeitsspeicher.

    Es muss daher insbesondere prüfen, ob die ausführbare Datei PIE ist oder nicht, und ob sie an einer zufälligen Stelle im Speicher abgelegt wird: Was ist die Option -fPIE für positionsunabhängige ausführbare Dateien in gcc und ld?

  • “Die Formatliste enthält alle Handler”, welche Datei enthält diese Formatliste??

    – Zzz0_o

    24. Februar 2017 um 14:01 Uhr


  • @mysticTot die Liste ist eine Variable: github.com/torvalds/linux/blob/v4.9/fs/exec.c#L72 dann jede Datei binfmt_ in github.com/torvalds/linux/tree/v4.9/fs fügt sich mithilfe der Magie zu dieser Liste hinzu __init Ding. Versuchen Sie auch, die Kernel-Quellen zu erhalten, und grep Sie sie 😉

    – Ciro Santilli OurBigBook.com

    24. Februar 2017 um 15:41 Uhr

  • Verstanden. Vielen Dank.

    – Zzz0_o

    25. Februar 2017 um 1:15 Uhr

Benutzer-Avatar
Basile Starynkevitch

Zwei Systemaufrufe von dem Linux Kernel sind relevant. Das Gabel Systemaufruf (oder evtl vfork oder clone) wird verwendet, um einen neuen Prozess zu erstellen, der dem aufrufenden ähnlich ist (jeder Linux-Benutzerlandprozess außer init wird erstellt von fork oder Freunde). Das ausführen Systemaufruf den Adressraum des Prozesses durch einen neuen ersetzen (im Wesentlichen durch sort-of mmmap-ing von Segmenten aus der ausführbaren ELF-Datei und von anonymen Segmenten, dann Initialisierung der Register, einschließlich des Stapelzeigers). Das x86-64 ABI-Ergänzung und die Anleitung zum Linux-Assembler Details angeben.

Die dynamische Verknüpfung erfolgt danach execve und beinhaltet die /lib/x86_64-linux-gnu/ld-2.13.so Datei, die für ELF als “Interpreter” angesehen wird.

  • Es scheint etwas mit dem Elf-Format und der Unterroutine Fork zu tun zu haben. Und es könnte auch an /lib/ld-linux.so.2 beteiligt sein, um diese dynamischen Bibliotheken zu laden.

    – Daniel

    5. Dezember 2011 um 2:28 Uhr

  • fork ist keine Subroutine, sondern ein Systemaufruf, und wie alle Systemaufrufe ist es aus Sicht der Anwendung eine atomare Operation. Das ELF-Format ist mit dem verwandt execve Systemaufruf (nicht an die fork eines). Und die ld-linuc.so.2 Der dynamische Loader wird innerhalb der ausführbaren ELF-Datei referenziert, ist also verwandt mit execve und ELF.

    – Basile Starynkevitch

    5. Dezember 2011 um 5:57 Uhr


Nach dem Lesen der ELF-Dokumente bereits verwiesen, sollten Sie nur Lesen Sie den Kernel-Code das geht eigentlich.

Wenn Sie Probleme haben, diesen Code zu verstehen, erstellen Sie eine UML-Linuxund Sie könnten diesen Code im Debugger schrittweise durchlaufen.

  • Tatsächlich ist der Kernel-Code dafür sehr einfach, im Gegensatz zu vielen anderen Dingen im Kernel.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    2. Dezember 2011 um 7:29 Uhr

  • @R. einverstanden, sogar ich kann es mehr oder weniger verstehen! Enthält eine Zusammenfassung des Codepfads: stackoverflow.com/a/31394861/895245

    – Ciro Santilli OurBigBook.com

    13. Juli 2015 um 22:58 Uhr

Sie können damit beginnen, ausführbare Dateiformate wie ELF zu verstehen. http://en.wikipedia.org/wiki/Executable_and_Linkable_Format

Die ELF-Datei enthält mehrere Abschnitte mit Überschriften, die beschreiben, wie und wo Teile der Binärdatei in den Speicher geladen werden sollen.

Dann schlage ich vor, sich über den Teil von Linux zu informieren, der Binärdateien lädt und dynamische Verknüpfungen handhabt. ld-linux. Dies ist auch eine gute Beschreibung von ld-linux: http://www.cs.virginia.edu/~dww4s/articles/ld_linux.html

1383050cookie-checkWie bekommt der Kernel eine ausführbare Binärdatei, die unter Linux läuft?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy