Betriebssysteme verwenden normalerweise dynamische Verknüpfungen, um die Effizienz des Programmbetriebs zu verbessern. Bei der dynamischen Verknüpfung werden nicht alle Funktionen in der Linkbibliothek beim Laden des Programms geladen, sondern bei Bedarf, wenn das Programm ausgeführt wird Das Programm wird ins Leben geladen. Dieses Design kann den reibungslosen Programmbetrieb verbessern und den Speicherplatz reduzieren. Darüber hinaus erlauben moderne Betriebssysteme keine Änderung von Codesegmenten, sondern nur von Datensegmenten, sodass die GOT-Tabelle und die PLT-Tabelle entstanden sind.
Werfen wir einen kurzen Blick auf ein Beispiel
Wir folgen scanf@plt
und werden feststellen, dass es drei Codezeilen gibt
jmp 一个地址 push 一个值到栈里面 jmp 一个地址
Schauen Sie sich einfach den Namen der Funktion an. Da wir wissen, dass dies die PLT-Tabelle der Scanf-Funktion ist, wollen wir nicht überstürzen, wofür PLT verwendet wird. Schauen wir uns zunächst an, wohin der erste JMP springt .
Tatsächlich ist dies die got-Tabelle der Funktion, die der plt-Tabelle entspricht. Wir werden feststellen, dass der Wert 0x201020 die Adresse des Stapelbefehls ist und die anderen Stellen 0 sind. Zu diesem Zeitpunkt haben wir Ich möchte fragen:
1. Habe einen Tisch und einen Plt-Tisch. Was ist der Sinn, warum das ganze Herumspringen?
2. Welche Verbindung besteht zwischen der got-Tabelle und der plt-Tabelle?
Wenn Sie also Fragen haben, lesen Sie zuerst die Antwort und bestätigen Sie sie dann. Wir müssen verstehen, dass Betriebssysteme normalerweise dynamische Verknüpfungen verwenden, um die Effizienz des Programmbetriebs zu verbessern, und nicht in das Codesegment zurückgeschrieben werden können.
Im obigen Beispiel können wir sehen, dass scanf -> scanf's plt table -> scanf's got table Der Wert der got-Tabelle ist uns im Moment egal ein Gedanke, der aus der got-Tabelle abgerufen werden kann. Finden Sie die echte Scanf-Funktion, die das Programm laden und ausführen kann.
Nachdem wir darüber nachgedacht haben, wird dies zu einem indirekten Adressierungsprozess
Wir rufen den kleinen Codeabschnitt auf, der das Datensegment erhält, um die Funktionsadresse namens PLT (Procedure Linkage Table) zu speichern. Prozessverknüpfungstabellenspeicherung Das Datensegment der Funktionsadresse wird als globale Offset-Tabelle GOT (Global Offset Table) bezeichnet. Nachdem wir einen solchen Gedanken formuliert haben, können wir die darin enthaltenen Details sorgfältig verstehen.
push got[1] jmp **got[2]
Es ist immer noch die Adresse von Push 0x1 und fahren Sie mit dem Debuggen fort wurde geändert
Jetzt möchte ich fragen, wo ist das?
Dann rufen Sie<_dl_fixup> in got[2] auf, um die Adresse in got[3];
zu ändern那么问题就来了,刚才got[2]处不是0吗,怎么现在又是这个(_dl_runtime_resolve)?这就是运行时重定位。
其实got表的前三项是:
got[0]:address of .dynamic section 也就是本ELF动态段(.dynamic段)的装载地址 got[1]:address of link_map object( 编译时填充0)也就是本ELF的link_map数据结构描述符地址,作用:link_map结构,结合.rel.plt段的偏移量,才能真正找到该elf的.rel.plt got[2]:address of _dl_runtime_resolve function (编译时填充为0) 也就是_dl_runtime_resolve函数的地址,来得到真正的函数地址,回写到对应的got表位置中。
那么此刻,got表怎么知道scanf函数的真实地址?
这个问题已经解决了。我们可以看一下其中的装载过程:
说到这个,可以看到在_dl_runtimw_resolve之前和之后,会将真正的函数地址,也就是glibc运行库中的函数的地址,回写到代码段,就是got[n](n>=3)中。也就是说在函数第一次调用时,才通过连接器动态解析并加载到.got.plt中,而这个过程称之为延时加载或者惰性加载。
到这里,也要接近尾声了,当第二次调用同一个函数的时候,就不会与第一次一样那么麻烦了,因为got[n]中已经有了真实地址,直接jmp该地址即可。
Das obige ist der detaillierte Inhalt vonWie man die GOT-Tabelle und die PLT-Tabelle genau versteht. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!