为什么同一程序的INST_PTR(指令指针)值会因不同的运行而发生变化?

时间:2015-03-27 02:23:46

标签: c linux intel instrumentation isa

在英特尔的PinTool中,您可以打印出"指令地址"使用IARG_INST_PTRINS_Address编写程序中的每条指令。我观察到在不同的时间点运行相同的程序会为完全相同的指令产生不同的指令地址。但是,我希望运行中的地址保持不变。这种变化的根本原因是什么?我附加了两个示例输出,下面显示了OPCODE和执行的前三条指令的指令地址。

我如何找到每条指令的PC?或者OBJDUMPPinTool

中显示的地址

- RUN1 -

op:       MOV addr:0x00007fac87a8d2d0

op: CALL_NEAR addr:0x00007fac87a8d2d3

op:      PUSH addr:0x00007fac87a90a70

- RUN2 -

op:       MOV addr:0x00007fc529f402d0

op: CALL_NEAR addr:0x00007fc529f402d3

op:      PUSH addr:0x00007fc529f43a70

2 个答案:

答案 0 :(得分:3)

(tl; dr版本,最后可能有解决方案。)

几乎可以肯定,由于address space randomization已应用于共享库。运行以下命令几次将让您了解它是如何工作的:

$ cat /proc/self/maps

/ proc / self / 指当前进程(打开文件的进程)。还有针对特定PID的 / proc /< pid> / 目录。 maps 文件列出了流程的映射 - 在本例中为cat流程本身。

这是我系统上一次运行的输出:

00400000-0040c000 r-xp 00000000 08:01 3409248            /bin/cat
0060b000-0060c000 r--p 0000b000 08:01 3409248            /bin/cat
0060c000-0060d000 rw-p 0000c000 08:01 3409248            /bin/cat
0063a000-0065b000 rw-p 00000000 00:00 0                  [heap]
7f017ef95000-7f017f761000 r--p 00000000 08:01 8126750    /usr/lib/locale/locale-archive
7f017f761000-7f017f91b000 r-xp 00000000 08:01 11155466   /lib/x86_64-linux-gnu/libc-2.19.so
7f017f91b000-7f017fb1a000 ---p 001ba000 08:01 11155466   /lib/x86_64-linux-gnu/libc-2.19.so
7f017fb1a000-7f017fb1e000 r--p 001b9000 08:01 11155466   /lib/x86_64-linux-gnu/libc-2.19.so
7f017fb1e000-7f017fb20000 rw-p 001bd000 08:01 11155466   /lib/x86_64-linux-gnu/libc-2.19.so
7f017fb20000-7f017fb25000 rw-p 00000000 00:00 0 
7f017fb25000-7f017fb48000 r-xp 00000000 08:01 11155454   /lib/x86_64-linux-gnu/ld-2.19.so
7f017fd1c000-7f017fd1f000 rw-p 00000000 00:00 0 
7f017fd23000-7f017fd47000 rw-p 00000000 00:00 0 
7f017fd47000-7f017fd48000 r--p 00022000 08:01 11155454   /lib/x86_64-linux-gnu/ld-2.19.so
7f017fd48000-7f017fd49000 rw-p 00023000 08:01 11155454   /lib/x86_64-linux-gnu/ld-2.19.so
7f017fd49000-7f017fd4a000 rw-p 00000000 00:00 0 
7fffacef5000-7fffacf16000 rw-p 00000000 00:00 0          [stack]
7fffacf5a000-7fffacf5c000 r-xp 00000000 00:00 0          [vdso]
7fffacf5c000-7fffacf5e000 r--p 00000000 00:00 0          [vvar]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0  [vsyscall]

前三行是可执行文件中的代码段,只读数据段和读写数据段。剩下的行是堆栈,堆,共享库的各个段,内存映射文件(作为旁注,库也只是内存映射文件),以及一些与实现某些系统调用有关的内部内容。

如果您重复执行该命令几次,您可能会看到除可执行文件中的代码和数据段之外的所有映射随机移动。这是一项安全措施。不知道事物在内存中的位置使得某些漏洞利用难以实现,因为你不能直接跳转到一些你知道会有一些有用例程的地址。

地址空间随机化未应用于可执行文件本身的代码和数据段的主要原因可能是效率。未加载到固定地址的代码必须是与位置无关的,这会增加一些开销。这就是需要使用-fPIC显式编译共享库的原因。

(除了安全性之外,共享库还需要与位置无关的其他原因。如果两个库碰巧得到重叠的加载地址,则为每个库使用固定地址会导致问题。)

不幸的是我不熟悉PinTool。我相信GDB只是禁用地址空间随机化(使用personality(2)系统调用)来获取共享库的可预测地址。

地址空间随机化可以转为off for a single shell session(这似乎也在引擎盖下使用personality()),或者通过echo 0 > /proc/sys/kernel/randomize_va_space全局转换(参见 / proc / sys / documentation)。

我在this page上找到了以下内容。可能是相关的。

  

Pin是否会更改应用程序代码和数据地址?

     

...

     

注意:即使您没有使用pin,最近的Linux内核也会故意移动堆栈的位置并从运行中动态分配数据。在基于RedHat的系统上,您可以通过运行Pin来解决此问题,如下所示:

     

$ setarch i386 pin -t pintool - app

tl;博士回答

如果您需要做的只是将PinTool中的地址从库中关联到objdump反汇编地址,并且您不介意每次都做一些手动工作,那么以下内容应该有效: / p>

  1. 从您的流程中打印 / proc / maps 。 (您也可以在后台运行它并从shell中打印 / proc /< pid> / maps ,使用例如$!来获取PID。)

  2. 检查地址所属的映射。在库案例中,它可能是某个库的文本段(在 / proc / maps 中标记为r-xp)。

  3. 从您在PinTool中看到的地址中减去映射的起始地址。

  4. 这将为您提供在objdump反汇编中看到的地址(当您在同一个库上运行它时)。如果库具有调试信息,您也可以使用addr2line(1)来获取源代码行。

    当然可能有更好的工作流程。至少在与dlopen(3)dlsym(3)玩游戏时,这对我有用。核心转储应该包含库加载地址,所以也许可以以某种方式使用...

答案 1 :(得分:0)

因为程序被加载到不同的内存地址。

编译程序(对于某些部分)使用固定的内存布局;然而,他们不知道他们将使用哪些地址,因为他们无法在计算机运行时知道哪些内存地址是免费的。

所以他们的内部地址实际上是“程序开始”内存地址的“偏移”。当程序被复制到ram中时,“起始”内存地址将被添加到所有偏移地址。

这也解释了为什么低阶位是一致的,即使高阶位不是。您正在查看相同的代码块两次,加载到不同的起始内存地址。这意味着偏移量是相同的,但地址不是。