assembly DLL上的Windows加载器操作

jbose2ul  于 2023-06-30  发布在  Windows
关注(0)|答案(2)|浏览(169)

我正在阅读John Levine的“Linkers and Loaders”一书中的这一段,描述了在Windows中加载dll的过程:
对于每个导入的DLL,都有一个导入地址数组,通常在程序的文本段中,程序加载器将解析的地址放入其中。
这在两个方面让我感到惊讶:
1.文本段不是只读/执行的吗?(也许加载器只在写入后才更改其权限?)
1.这不会使文本段在进程之间不可共享吗?Linux不遗余力地保持库的所有示例的文本段相同(“位置独立代码”)。这难道不是Windows的目标吗?

2skhul33

2skhul331#

对于每个导入的DLL,都有一个导入地址数组,通常在程序的文本段中,程序加载器将解析的地址放入其中。
过去流行将导入放在读/写.idata部分,然后改为只读.idata,现在(对于64位的ex和dll)导入通常是(只读). rdata的一部分。你也可以把导入的内容放在.text中,但我不记得这是流行的,也许在我之前。
顺便说一句,你可以创建一些任意的节名,并把导入放在那里,这没关系,节的实际名称并不重要。
也许加载器只在写入后才更改其权限?
是的,它可以很容易地做到这一点,但它实际上更糟:延迟加载的DLL呢?因此,如果存在延迟加载的DLL,则无论导入在哪个页面中,都可以在执行期间的各个点处,无论何时加载延迟加载的DLL,被短暂地更改为可写、修改,然后返回到只读。
作为一个有趣的事实,在x86内核代码可以直接写入只读页面,除非它专门启用CR0.WP。但是加载程序运行在用户模式下,所以这在这里并不重要。
这不会使文本段在进程之间不可共享吗?
如果.text中有 are 导入,那么这只会影响实际上有导入的页面,而不是整个部分。导入不是通过在整个代码中更改各种call指令的目标地址来处理的,而是通过填充一个中央地址数组来处理的,这些地址随后被间接调用指令使用。所以代码本身并没有改变,只是在某处增加了一个密集的数组。如果该数组在.text中,那么.text的 that 部分将不可共享,但其余部分仍然可以共享。
但是,撇开.text中的 imports 的情况不谈,影响text部分的重定位会使text部分不可共享吗?32位代码通常不会在.text中导入,但通常会对.text进行重定位,这似乎更糟:这会影响大多数页面。
但即便如此,代码页最终还是大部分是可共享的。Some trickery is necessary:尝试在多个进程中以一致的地址加载DLL,以便即使重新定位代码页也可以共享它们。但是,由于页面被修改了,因此不能简单地对它们进行内存Map。
由于RIP相对寻址,x64代码在大多数情况下自然是位置无关的。
顺便说一句,它允许(虽然不推荐)有一个不可重定位的exe或dll。不可重定位意味着它们只能在其“首选”(在本例中是“必需”)基址上加载,并且与ASLR不兼容。exe首先被加载到它的地址空间中,这样就可以工作了。一个不可重定位的dll要么得到它的首选地址,要么没有,如果没有,那么它将无法加载。

fjaof16o

fjaof16o2#

导入地址数组由PE文件可选头中的16个数据目录之一标识,即 * 导入地址表 (IAT)。它通常位于自己的部分*.idata中,但一些链接器可能会将IAT与 IMPORT Table.text部分捆绑在一起。
1.是的,
.text**代码段仅可读可执行,但只有在Windows加载器解析了导入函数的所有地址并将其存储到IAT后,才能建立访问权限。

  1. Windows动态库(DLL)的每个示例只加载到内存一次。当同一PE程序的多个示例运行时,所有必需的DLL都Map到相同的虚拟地址,因此它们的IAT的内容在运行时是相同的。然而,大多数DLL都有不同的部分**.text.idata**,因此导入函数的Map地址可以随机化(ASLR)。
    微软似乎不太关心PIC,每个DLL可以加载到任意虚拟地址,并在需要时重新定位,使用它们的**.reloc**节。

相关问题