这应该是一个非常简单、非常快速的问题。这是我用C编写的程序的前3行:
Dump of assembler code for function main:
0x0804844d <+0>: push ebp
0x0804844e <+1>: mov ebp,esp
0x08048450 <+3>: and esp,0xfffffff0
... ... ... ... ... ... ...
什么是0x0804844d
、0x0804844e
和0x08048450
?它不受ASLR的影响。它仍然是内存地址,还是文件的相对点?
如果查看《英特尔开发人员手册》指令集参考,您可以看到0x0804846d <+32>: eb 15 jmp 0x8048484
对相对地址进行编码。即它是CCD_ 5短编码。这甚至在与位置无关的代码中也能工作,即在任何地址映射/加载时都能运行的代码。
ASLR意味着每次将文件加载到内存中时,可执行文件中的堆栈地址(以及可选的代码+数据)都会发生变化显然,一旦加载程序,地址就不会再更改,直到再次加载。因此,如果你在运行时知道地址,你可以针对它,但你不能假设一个固定的地址来编写攻击。
GDB在任何ASLR之后向您显示进程的虚拟内存空间中的代码地址。(BTW,GDB默认禁用ASLR:set disable-randomization on|off
切换。)
对于可执行文件,通常只有堆栈指针是ASLRed,而代码依赖于位置并加载在固定地址,因此代码和静态数据地址是链接时间常数,因此像push OFFSET .LC0
/call puts
这样的代码可以工作,将字符串常数的地址硬编码为push imm32
。
库通常需要独立于位置,所以ASLR可以在随机地址加载它们。
但是,通过制作独立于位置的可执行文件(Linux),或者让操作系统在将可执行文件加载到与编译时不同的地址时修复每个硬编码地址(Windows),可执行文件的ASLR是可能的,并且变得越来越普遍。
地址与文件中的位置只有1:1的关系,只是在同一段中的相对意义上。即代码的下一字节是文件的下一个字节。可执行文件的标题描述了文件的哪些区域是什么(以及操作系统的程序加载程序应该将它们映射到哪里)。
显示的地址在三种情况下的含义不同:
- 对于可执行文件
- 对于DLL(Windows)或共享对象(.so、Linux和Un*x-like)
- 对于对象文件
对于可执行文件:
可执行文件通常无法加载到内存中的任何地址。在Windows中,可以将"重新定位表"添加到可执行文件中(对于非常旧的Windows版本是必需的);如果不存在这种情况(通常是使用GCC时的情况),则无法将文件加载到另一个内存位置。在Linux中,永远不可能将可执行文件加载到另一个位置。
你可以试试这样的东西:
static int a;
printf("%Xn", &a);
当您执行程序100次时,您会发现a的地址始终相同,因此不会对可执行文件本身执行ASLR。
objdump转储的地址是绝对地址。
对于DLL/.so文件:
这些地址是相对于DLL的基地址(在Linux下),或者是绝对地址(在Windows下),当DLL加载到另一个内存区时,这些地址会发生变化。
对于对象文件:
转储对象文件时,地址是相对于当前显示的节的。如果一个文件中有多个".text"节,则每个节的地址都将从0开始。