Maison >Opération et maintenance >Sécurité >Comment comprendre en profondeur les tables GOT et PLT

Comment comprendre en profondeur les tables GOT et PLT

WBOY
WBOYavant
2023-05-14 12:13:141447parcourir

0x01 Préface

Les systèmes d'exploitation utilisent généralement des liaisons dynamiques pour améliorer l'efficacité du fonctionnement des programmes. Dans le cas d'une liaison dynamique, toutes les fonctions de la bibliothèque de liens ne sont pas chargées ensemble lorsque le programme est chargé, mais à la demande lorsque le programme est exécuté. Si une fonction n'est pas appelée, elle ne sera pas chargée. le programme. Être chargé dans la vie. Cette conception peut améliorer la fluidité du fonctionnement du programme et réduire l'espace mémoire. De plus, les systèmes d'exploitation modernes ne permettent pas de modifier les segments de code, mais uniquement les segments de données, c'est pourquoi les tables GOT et PLT ont vu le jour.

0x02 Une exploration préliminaire de la table GOT et de la table PLT

Jetons un bref coup d'œil à un exemple

Comment comprendre en profondeur les tables GOT et PLT

Nous suivons scanf@plt

Comment comprendre en profondeur les tables GOT et PLT

et nous constaterons qu'il y a trois lignes de code

jmp 一个地址
push 一个值到栈里面
jmp 一个地址

Regardez simplement le nom de la fonction Sachant qu'il s'agit de la table plt de la fonction scanf, ne nous précipitons pas pour comprendre à quoi sert plt. Continuons la lecture. Voyons d'abord ce qu'est le premier jmp et. où ça saute.

Comment comprendre en profondeur les tables GOT et PLT

En fait, c'est la table got de la fonction correspondant à la table plt, et nous constaterons que la valeur de 0x201020 est l'adresse de la commande stack, et les autres endroits sont 0. A ce moment, nous je veux demander :

1. J'ai une table et une table plt. A quoi ça sert, pourquoi tous ces sauts ?

2. Quel est le lien entre la table got et la table plt ?

Donc, si vous avez des questions, lisez d'abord la réponse, puis confirmez-la. Nous devons comprendre que les systèmes d'exploitation utilisent généralement des liaisons dynamiques pour améliorer l'efficacité du fonctionnement du programme et ne peuvent pas être réécrits dans le segment de code.

Dans l'exemple ci-dessus, nous pouvons voir cet appel à scanf —> scanf's plt table —> scanf's got table Quant à la valeur de la got table, nous ne nous en soucions pas pour le moment, nous pouvons former. en pensant qu'il peut être obtenu à partir de la table got. Trouvez la vraie fonction scanf pour que le programme se charge et s'exécute.

Après que nous le pensons, cela devient un processus d'adressage indirect

Comment comprendre en profondeur les tables GOT et PLT

Nous appelons le petit morceau de code qui obtient le segment de données pour stocker l'adresse de fonction appelée stockage de table de liaison de processus PLT (Procedure Linkage Table) Le segment de données de l'adresse de la fonction est appelée table de décalage globale GOT (Global Offset Table). Après avoir formé une telle pensée, nous pouvons soigneusement comprendre les détails à l’intérieur.

0x03 Explorons à nouveau la table GOT et la table PLT

Après avoir compris un processus aussi général, examinons comment on l'appelle étape par étape. Il y a plusieurs doutes qui doivent être résolus :

1. à propos de la table got ? Connaissez-vous la véritable adresse de la fonction scanf ?

2. Quelle est la structure de la table got et de la table plt ? Regardons d'abord la table plt. Nous venons de découvrir que la troisième ligne de code dans la table scanf@plt est une adresse de jmp. Continuons et voyons de quoi il s'agit. En fait, c'est le début d'une table PLT de programme. (plt[0]). C'est le cas. Le problème est que :

push got[1]
jmp **got[2]

est suivi du tableau plt pour chaque fonction. A ce moment, revenons sur cette mystérieuse table GOTComment comprendre en profondeur les tables GOT et PLT

À l'exception de ces deux-là (l'adresse du push 0xn des fonctions printf et scanf, qui est l'adresse du deuxième code de la table plt correspondante), l'autre got[ 1], got[2] est 0, alors qu'est-ce que la table plt pointe vers la table got qui est 0 ? Parce que nous avons pris du retard sur une condition, les systèmes d'exploitation modernes n'autorisent pas la modification des segments de code, mais uniquement des segments de données, ce qui est une réécriture. Un nom plus professionnel devrait être la relocalisation de l'exécution. Lorsque nous exécutons le programme, notre adresse précédente et le contenu enregistré ont changé. Avant cela, sauvegardons le contenu pendant le lien et faisons une comparaison

Comment comprendre en profondeur les tables GOT et PLT

② 寻找printf的plt表
③ jmp到plt[0]
④ jmp got[2] -> 0x00000
⑤⑥ printf和scanf的got[3] got[4] -> plt[1] plt[2]的第二条代码的地址
⑦⑧ 证实上面一点

Exécutons le programme et définissons un point d'arrêt à scanf

Comment comprendre en profondeur les tables GOT et PLT

It. On constate que la table scanf@plt a changé à ce moment-là. Vérifiez le contenu dans got[4]

Comment comprendre en profondeur les tables GOT et PLT

C'est toujours l'adresse du push 0x1 et continuez le débogage. Jusqu'ici, l'adresse got[4] a. été modifié

Comment comprendre en profondeur les tables GOT et PLT

Maintenant, je veux demander, où est-ce ?

Comment comprendre en profondeur les tables GOT et PLT

Comment comprendre en profondeur les tables GOT et PLTPuis appelez<_dl_fixup> dans got[2] pour modifier l'adresse dans got[3];

那么问题就来了,刚才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函数的真实地址?

这个问题已经解决了。我们可以看一下其中的装载过程:

Comment comprendre en profondeur les tables GOT et PLT

Comment comprendre en profondeur les tables GOT et PLT

说到这个,可以看到在_dl_runtimw_resolve之前和之后,会将真正的函数地址,也就是glibc运行库中的函数的地址,回写到代码段,就是got[n](n>=3)中。也就是说在函数第一次调用时,才通过连接器动态解析并加载到.got.plt中,而这个过程称之为延时加载或者惰性加载。

到这里,也要接近尾声了,当第二次调用同一个函数的时候,就不会与第一次一样那么麻烦了,因为got[n]中已经有了真实地址,直接jmp该地址即可。

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer