为什么非图片代码不能完全使用运行时修复程序进行ASLR?

如何解决为什么非图片代码不能完全使用运行时修复程序进行ASLR?

我知道PIC代码可以使ASLR随机化更加有效和容易,因为该代码可以放置在内存中的任何位置而无需更改代码。但是,如果我根据Wikipedia relocation正确理解,则动态链接程序可以在运行时进行“修正”,因此可以定位符号,尽管代码与位置无关。但是根据许多答案,我看到这里非图片代码无法ASLR部分,除了堆栈(因此不能使程序入口点随机化)。如果那是正确的,那么什么是运行时修补程序?为什么我们不能在程序开始运行之前就在程序运行时对代码中的所有位置进行修补,以使程序入口点随机化。

解决方法

首先让我们了解Windows,然后再了解Linux:

Windows的.EXE文件(程序)通常具有所谓的“基本重定位表”,并且具有“图像基本”。

“图像基础”是程序的“所需”起始地址;如果Windows将程序加载到该地址,则无需进行重定位。

“基本重定位表”包含程序中代表地址的所有值的列表。如果程序加载的地址不同于“图像库”的地址,则Windows必须将差异添加到该表中列出的所有值。

如果.EXE文件不包含“基本重定位表”(据我所知,某些32位GCC版本会生成此类文件),则无法将文件加载到另一个地址。 / p>

这是因为,如果变量someVariable位于地址12340000上,则以下C代码语句将产生完全相同的机器代码(二进制代码),并且无法区分它们:>

long myVariable = 12340000;

并且:

int * myVariable = &someVariable;

在第一种情况下,在任何情况下都不得更改值12340000;在第二种情况下,如果将程序加载到另一个地址,则必须将地址(12340000)更改为实际地址。

如果缺少“基本重定位表”,则没有信息12340000是整数值(不得更改)或地址(必须更改)。

因此,必须将程序加载到某个固定地址。

我不确定最新的32位Linux版本,但是至少在较旧的32位Linux版本中,没有什么比“基本重定位表”更重要的了,并且程序没有使用PIC。这意味着必须将这些程序加载到其“收藏夹”地址。

我不了解64位Linux程序,但是如果一个程序与(较旧的)32位程序的编译方式相同,则还必须将它们加载到某个地址,并且无法进行ASLR。 / p>

,

TL:DR:并非所有对绝对地址的使用都会在非PIE可执行文件(ELF类型EXEC,不是DYN)中具有重定位信息。因此,内核的程序加载器找不到它们全部应用修正。

因此,无法为非PIE构建的可执行文件追溯启用ASLR。传统的可执行文件无法将自己标记为每次使用绝对地址都具有重定位元数据,也没有必要添加这种功能,因为如果您要使用文本ASLR,则只需构建一个PIE。

由于可以保证ELF类型的EXEC Linux可执行文件在链接时在链接器选择的固定基地址处加载/映射,因此在可执行文件中为内部符号建立符号表条目将浪费空间。因此,工具链没有做到这一点,也没有理由开始。这就是传统的ELF可执行文件的设计方式。在堆栈ASLR成为现实之前,Linux在90年代中期从a.out转换为ELF,因此它不在人们的关注范围之内。

例如static char buf[100]的绝对地址可能嵌入在使用该地址的机器代码中的某个位置(如果我们正在谈论将地址存储在寄存器中的32位代码或64位代码),但是无法实现知道在哪里或多少次。

此外,特别是对于x86-64,非PIE可执行文件的默认代码模型可确保静态地址(文本/数据/ bss)全部位于虚拟地址空间的低2GiB中,因此32位绝对符号或无符号地址可以使用,rel32移位可以覆盖任何内容。这就是非PIE编译器输出使用mov $symbol,%edi(5字节)而不是lea symbol(%rip),%rdi(7字节)将地址放入寄存器的原因。 https://godbolt.org/z/89PeK1

因此,即使您确实知道每个绝对地址在哪里,也只能在低2GiB中对其进行ASLR,从而限制了您可以引入的熵位数。 (我认为Windows为此提供了一种模式:LargeAddressAware = no。但是Linux没有。32-bit absolute addresses no longer allowed in x86-64 Linux?同样,PIE是允许文本ASLR的更好方法,因此人们(发行人)应该只为此编译它的好处。)

与Windows不同,Linux不会花费大量精力通过重新编译源代码中的二进制文件来更好地处理这些事情。

话虽这么说,GNU / Linux 确实支持 64位绝对地址的修复程序重定位,即使在PIC / PIE ELF共享对象中也是如此。这就是为什么像NASM mov rdi,BUFFER这样的初学者代码甚至可以在共享库中工作的原因:使用objdump -drwC -Mintelmov reg,imm64指令中查看有关符号使用的重定位信息。如果lea rdi,[rel BUFFER]不是全局符号,则BUFFER不需要任何重定位条目。 (相当于C static。)


您可能想知道为什么元数据必不可少:

没有可靠的方法来搜索文本/数据以查找可能的绝对地址。误报是可能的。例如/usr/bin/ld可能包含0x401000作为x86-64可执行文件的默认起始地址。您不希望ld的代码+数据的ASLR也更改其默认值。或者该整数值可能在许多程序中以多种方式出现,例如作为位图。当然,x86-64机器代码的长度是可变的,因此在最一般的情况下,甚至没有可靠的方法甚至可以将操作码与立即操作数区分开。

还有潜在的假阴性。 x86程序用多条指令在寄存器中构造绝对地址的可能性不大,但是这肯定是可能的。但是在非x86代码中,这很常见。

具有固定长度指令的RISC机器不能将32位地址放入32位指令中。没有别的空间了。因此,要从静态存储中加载,绝对地址必须在多个指令之间进行拆分,例如MIPS lui $t0,%hi(0x612300) / lw $t1,%lo(0x612300)($t0),才能从绝对地址0x612300的静态变量加载。 (在asm源代码中通常会有一个符号名,但是除非是.globl,否则它不会出现在最终链接的二进制文件中,因此我使用数字作为提醒。)类似的说明不必成对出现该地址的前半部分可以被其他访问相同数组或结构的地址重用,稍后再进行说明。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?