Was ist ein Linux-Bild?

青灯夜游
Freigeben: 2022-07-15 19:14:30
Original
2888 Leute haben es durchsucht

Unter Linux bedeutet die chinesische Bedeutung von pic „positionsunabhängiger Code“, was bedeutet, dass der Code unabhängig von der Adresse, auf die er geladen wird, normal ausgeführt werden kann. PIC wird verwendet, um ortsunabhängige gemeinsam genutzte Bibliotheken zu generieren. Das sogenannte ortsunabhängige bedeutet, dass die Codesegmente der gemeinsam genutzten Bibliotheken schreibgeschützt sind und im Codesegment gespeichert werden können Kopieren.

Was ist ein Linux-Bild?

Die Betriebsumgebung dieses Tutorials: Linux7.3-System, Dell G3-Computer.

Unter Linux lautet der vollständige Name von pic „Position Independent Code“, was auf Chinesisch „positionsunabhängiger Code“ bedeutet.

1. Übersicht über den Code im Zusammenhang mit dem virtuellen Adressraum und Speicherort des Programms

Wenn ein Linux-Prozess von der Festplatte in den Speicher geladen und ausgeführt wird, weist der Kernel dem Prozess einen virtuellen Adressraum und die virtuelle Adresse zu Der Raum ist in Blöcke unterteilt. Die wichtigsten Bereiche sind wie folgt:

Abbildung 1 – Beschreibung des virtuellen Adressraums der Anwendung

Der Kernel-Adressraum ist für alle Anwendungen und Anwendungen in diesem Teil gleich Der Zugriff auf den Adressraum ist nicht möglich. Der Kernel-Adressraum steht nicht im Mittelpunkt dieses Artikels. Wir konzentrieren uns auf einige wichtige SEGMENTE der Anwendung.

Tabelle 1 – Beschreibung wichtiger Segmente der Anwendung

Wenn das System die Adress-Randomisierung (ASLR – Address Space Layout Randomization, Adress-Randomisierung, die später eingeführt wird) nicht aktiviert, verwendet Linux jedes Segment in In der obigen Tabelle wird der Adressraum an einer festen Adresse platziert.

Schreiben wir ein tatsächliches Programm, um zu sehen, wie die Adressen der einzelnen Segmente auf einem Linux X86_64-Computer angeordnet sind. Das Programm sieht wie folgt aus und deckt die Segmente ab, die uns wichtig sind. Abbildung 2: Demonstrationsprogramm für den virtuellen Adressraum sind ein fester Wert. Dies liegt daran, dass das System den virtuellen Adressraum des Programms nicht zufällig zuweist, wenn die ASLR-Funktion nicht aktiviert ist, und alle Adressen des Programms nach festen Regeln generiert werden.

Abbildung 3 – Feste Segmentadressenverteilung

Nach der Disassemblierung mit dem objdump-Befehl können Sie sehen, dass für den Zugriff auf globale Variablen und Funktionsaufrufe die Adressen der Assembleranweisungen festgelegt sind. Wir nennen diesen Code ortsabhängig .

Abbildung 4 – Beispiel für eine positionsbezogene Code-Assembly-Anweisung

Da die Adresse fest codiert ist, kann sie nur dann an die angegebene Adresse geladen und ausgeführt werden, wenn sich die Ladeadresse ändert Die Variablen und Funktionen, auf die im Code zugegriffen wird. Die Adresse ist fest und das Programm kann nicht normal ausgeführt werden, nachdem sich die Ladeadresse geändert hat.

Obwohl die Methode mit fester Adresse einfach ist, kann sie einige erweiterte Funktionen wie die Unterstützung dynamischer Bibliotheken nicht implementieren. Der Code der dynamischen Bibliothek wird über den Systemaufruf mmap() dem virtuellen Adressraum des Prozesses zugeordnet. In verschiedenen Prozessen ist die von derselben dynamischen Bibliothek zugeordnete virtuelle Adresse unsicher. Wenn bei der Implementierung einer dynamischen Bibliothek positionsbezogener Code verwendet wird, kann der Zweck der Ausführung an keiner Adresse erreicht werden. In diesem Fall müssen wir das Konzept des positionsunabhängigen Codes PIC einführen.

Darüber hinaus können wir sehen, dass es auf Systemen, auf denen die Adress-Randomisierungsfunktion nicht aktiviert ist, für Hacker einfacher ist, anzugreifen, da die Adressen jedes Segments des Programms festgelegt sind (interessierte Studenten können nach Ret2shellcode oder Ret2libc suchen). Angriffe) , zu diesem Zeitpunkt muss das Konzept von PIE zusammen mit ASLR zum Schutz eingeführt werden.

2. Implementierung von positionsunabhängigem Code PIC und dynamischer Bibliothek

PIC-positionsunabhängiger Code bedeutet, dass der Code normal ausgeführt werden kann, unabhängig von der Adresse, in die er geladen wird. Durch Hinzufügen von -fPIC zur gcc-Option wird relevanter Code generiert.

PIC wird verwendet, um ortsunabhängige gemeinsam genutzte Bibliotheken zu generieren. Das sogenannte ortsunabhängige bedeutet, dass die Codesegmente der gemeinsam genutzten Bibliotheken schreibgeschützt sind und im Codesegment gespeichert werden können Zeit ohne Kopieren. Auf Variablen (globale Variablen und statische Variablen) in der Bibliothek wird über die GOT-Tabelle zugegriffen, und auf Funktionen in der Bibliothek wird über die Position PLT->GOT->Funktion zugegriffen. Beim Kompilieren einer gemeinsam genutzten Bibliothek unter Linux müssen Sie den Parameter -fPIC hinzufügen, da sonst beim Verknüpfen eine Fehlermeldung angezeigt wird (einige Informationen besagen, dass dieser Fehler nur auf AMD64-Maschinen auftritt, aber auch auf meiner Inter-Maschine).

Schlüsselpunkt Nr. 1 - Versatz von Codesegment und Datensegment Der Versatz zwischen Codesegment und Datensegment wird beim Verknüpfen vom Linker angegeben, was für PIC sehr wichtig ist. Wenn der Linker alle ps jeder Objektdatei zusammenfasst, kennt der Linker die Größe jedes ps und die relative Position zwischen ihnen vollständig.

Abbildung 5 – Beispiel für den Versatz von Codesegment und Datensegment

Wie in der obigen Abbildung gezeigt, liegen TEXT und DATA im Beispiel nahe beieinander. Tatsächlich kann der Linker den Versatz dieser beiden Segmente kennen. Basierend auf diesem Offset kann der relative Offset jedes Befehls im TEXT-Segment relativ zur Startadresse des DATA-Segments berechnet werden. Wie in der Abbildung oben gezeigt, können wir unabhängig von der virtuellen Adresse, auf der das TEXT-Segment platziert ist, unter der Annahme, dass sich ein Mov-Befehl am 0xe0-Offset innerhalb von TEXT befindet, wissen, dass die relative Offset-Position des DATA-Segments ist: die Größe des TEXT-Segment – ​​die mov-Anweisung befindet sich am TEXT. Interner Offset = 0xXXXXE000 – 0xXXXX00E0 = 0xDF20. Schlüsselpunkt Nr. 2: Berechnung des relativen Offsets von Anweisungen auf X86. Wenn Sie die relative Position für die Verarbeitung verwenden, können Sie sehen, dass die Code kann positionsunabhängig sein. Aber auf der X86-Plattform erfordert der Mov-Befehl eine absolute Adresse als Datenreferenz. Was sollen wir also tun?

Wenn wir anhand der Beschreibung in „Schlüsselpunkt 1“ die Adresse des aktuellen Befehls kennen, können wir die Adresse des Datensegments berechnen. Es gibt keine Anweisung, den Wert der aktuellen Befehlszeigerregister-IP auf der X86-Plattform abzurufen (auf X64 kann direkt auf RIP zugegriffen werden), aber er kann durch einen kleinen Trick ermittelt werden. Schauen wir uns einen Teil des Pseudocodes an:

Abbildung 6 – X86-Plattform-Akquisitionsanweisung-Adressassembly

Wenn dieser Code tatsächlich ausgeführt wird, passieren die folgenden Dinge:

Wenn die CPU den Aufruf STUB ausführt, wird er speichert die Adresse des nächsten Befehls im Stapel und springt dann zur Ausführung zum Label STUB.

    Die Anweisung bei STUB ist pop ebx, daher wird die Adresse der Anweisung „pop ebx“ vom Stapel in das ebx-Register verschoben und erhält so den Wert des IP-Registers.
  • 1. Globale Offset-Tabelle GOT

  • Nachdem wir die vorherigen Punkte verstanden haben, schauen wir uns an, wie positionsunabhängige Datenreferenzen auf X86 implementiert werden. Diese Funktion erfolgt über die globale Offset-Tabelle (GOT). zu erreichen.

GOT ist eine in Daten p gespeicherte Tabelle, die viele Adressfelder (Einträge) aufzeichnet. Angenommen, eine Anweisung möchte auf eine Variable verweisen. Sie verwendet nicht direkt die absolute Adresse, sondern verweist auf einen Eintrag im GOT. Die Adresse der GOT-Tabelle in Daten p ist klar und der Eintrag des GOT enthält die absolute Adresse der Variablen.

Abbildung 7 – Die Beziehung zwischen der Codeadresse und dem GOT-Tabelleneintrag

Wie in der Abbildung oben gezeigt, können wir anhand von „Schlüsselpunkt 1“ und „Schlüsselpunkt 2“ zunächst den Wert von erhalten aktuelle IP, und berechnen Sie dann den Absolutwert der GOT-Tabellenadresse. Da auch der Offset des Adresseintrags der Variablen in der GOT-Tabelle bekannt ist, kann ein ortsunabhängiger Datenzugriff erreicht werden.

Nehmen Sie den Pseudocode eines Mov-Befehls mit absoluter Adresse als Beispiel (X86-Plattform):

Abbildung 8 – Beispiel eines positionsbezogenen Mov-Befehls

Wenn Sie ihn in einen positionsunabhängigen Code umwandeln möchten, Sie benötigen noch ein paar Schritte

Abbildung 9 – Beispiel für eine positionsunabhängige mov-Anweisung kombiniert mit GOT

Durch die oben genannten Schritte können Sie einen adressunabhängigen Codezugriff auf Variablen erreichen. Es stellt sich jedoch immer noch die Frage, wie der in der GOT-Tabelle gespeicherte VAR_ADDR-Wert zur tatsächlichen absoluten Adresse wird.

Angenommen, es gibt eine libtest.so und eine globale Variable g_var. Nachdem wir readelf -r libtest.so übergeben haben, sehen wir die folgende Ausgabe:

Abbildung 10 – Beschreibungsfeld für die globale Variablenumleitung des rel.dyn-Segments

Der dynamische Loader analysiert das rel.dyn-Segment. Wenn er erkennt, dass der Umleitungstyp R_386_GLOB_DAT ist, wird er Folgendes tun: Ersetzen Sie den tatsächlichen Adresswert des Symbols g_var durch den Offset 0x1fe4 (d. h. ersetzen Sie den Wert von Sym. Wert (Ersetzt durch den tatsächlichen Adresswert)

2. Positionsunabhängige Implementierung von Funktionsaufrufen

Theoretisch kann die PIC-Implementierung der Funktion genauso positionsunabhängig sein wie die Datenreferenz-GOT-Tabelle. Anstatt die Adresse der Funktion direkt zu verwenden, wird die tatsächliche absolute Adresse der Funktion durch Nachschlagen im GOT ermittelt. Tatsächlich ist dies jedoch aufgrund der PIC-Eigenschaften der Funktion nicht der Fall, und die tatsächliche Situation ist komplizierter. Warum nicht die gleiche Methode wie bei Datenreferenzen anwenden und sich zunächst ein Konzept ansehen: verzögerte Bindung?

Bei dynamischen Bibliotheksfunktionen ist die tatsächliche Adresse der Funktion unbekannt, bevor sie in den Adressraum des Programms geladen wird. Der dynamische Loader behandelt diese Probleme und analysiert die tatsächliche Adresse. Der Bindungsvorgang wird einige Zeit in Anspruch nehmen, da der Lader spezielle Tabellensuch- und Ersetzungsvorgänge durchführen muss.

如果动态库有成百上千个函数接口,而实际的进程只用到了其中的几十个接口,如果全部都在加载的时候进行绑定操作,没有意义并且非常耗时。因此提出了延迟绑定的概念,程序只有在使用到对应接口时才实时地绑定接口地址。

因为有了延迟绑定的需求,所以函数的PIC实现和数据访问的PIC有所区别。为了实现延迟绑定,就额外增加了一个间接表PLT(过程链接表)。

PLT搭配GOT实现延迟绑定的过程如下:

第一次调用函数

图11 - 首次调用PIC函数时PLT,GOT关系

首先跳到PLT表对应函数地址PLT[n],然后取出GOT中对应的entry。GOT[n]里保存了实际要跳转的函数的地址,首次执行时此值为PLT[n]的prepare resolver的地址,这里准备了要解析的函数的相关参数,然后到PLT[0]处调用resolver进行解析。

resolver函数会做几件事情:

(1)解析出代码想要调用的func函数的实际地址A

(2)用实际地址A覆盖GOT[n]保存的plt_resolve_addr的值

(3)调用func函数

首次调用后,上图的链接关系会变成下图所示:

图12 - 首次调用PIC函数后PLT,GOT关系

随后的调用函数过程,就不需要再走resolver过程了

三、位置无关可执行程序PIE

PIE,全称Position Independent Executable。2000年早期及以前,PIC用于动态库。对于可执行程序来讲,仍然是使用绝对地址链接,它可以使用动态库,但程序本身的各个segment地址仍然是固定的。随着ASLR的出现,可执行程序运行时各个segment的虚拟地址能够随机分布,这样就让攻击者难以预测程序运行地址,让缓存溢出攻击变得更困难。OS在使能ASLR的时候,会检查可执行程序是否是PIE的可执行程序。gcc选项中添加-fPIE会产生相关代码。

四、Linux ASLR机制和PIE的关系

ASLR的全称为 Address Space Layout Randomization。在Linux 2.6.12 中被引入到 Linux 系统,它将进程的某些虚拟地址进行随机化,增大了入侵者预测目的地址的难度,降低应用程序被攻击成功的风险。

在Linux系统上,ASLR有三个级别

表2 - ASLR级别描述

ASLR的级别通过两种方式配置:

echo level > /proc/sys/kernel/randomize_va_space
Nach dem Login kopieren

sysctl -w kernel.randomize_va_space=level
Nach dem Login kopieren

例子:

echo 0 > /proc/sys/kernel/randomize_va_space 关闭地址随机化
Nach dem Login kopieren

sysctl -w kernel.randomize_va_space=2 最大级别的地址随机化
Nach dem Login kopieren

我们还是以文章开头的那个程序来说明ASLR在不同级别下时如何表现的,首先在ASLR关闭的情况下,相关地址不变,输出如下:

图13 - ASLR=0时虚拟地址空间分配情况

我们把ASLR级别设置为1,运行两次,看看结果:

图14 - ASLR=1时虚拟地址空间分配情况

可以看到STACK和MMAP的地址发生了变化。堆、数据段、代码段仍然是固定地址。

接下来我们把ASLR级别设置为2,运行两次,看看结果:

图15 - ASLR=2,PIE不启用时虚拟地址空间分配情况

可以看到此时堆的地址也发生了变化,但是我们发现BSS,DATA,TEXT段的地址仍然是固定的,不是说ASLR=2的时候,是完全随机化吗?

这里就引出了PIE和ASLR的关系了。从上面的实验可以看出,如果不对可执行文件做一些特殊处理,ASLR即使在设置为完全随机化的时候,也仅能对STACK,HEAP,MMAP等运行时才分配的地址空间进行随机化,而可执行文件本身的BSS,DATA,TEXT等没有办法随机化。结合文章前面讲到的PIE相关知识,我们也很容易理解这一点,因为编译和链接过程中,如果没有PIE的选项,生成的可执行文件里都是位置相关的代码。如果OS不管这一点,ASLR=2时也将BSS,DATA,TEXT等随意排布,可想而知程序根本不能正常运行起来。

明白了原因,我们在编译时加入PIE选项,然后在ASLR=2时重新运行一下看看结果如何

图16 - ASLR=2,PIE启用时虚拟地址空间分配情况

Sie können sehen, dass bei aktiviertem PIE und ASLR=2 die virtuellen Adressen jedes Segments vollständig randomisiert werden können.

Verwandte Empfehlungen: „Linux-Video-Tutorial

Das obige ist der detaillierte Inhalt vonWas ist ein Linux-Bild?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage