【问题标题】:How is the address of the text section of a PIE executable determined in Linux?在 Linux 中如何确定 PIE 可执行文件的文本部分的地址?
【发布时间】:2018-12-22 22:12:27
【问题描述】:

首先我尝试对其进行逆向工程:

printf '
#include <stdio.h>
int main() {
    puts("hello world");
}
' > main.c
gcc -std=c99 -pie -fpie -ggdb3 -o pie main.c
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
readelf -s ./pie | grep -E 'main$'
gdb -batch -nh \
  -ex 'set disable-randomization off' \
  -ex 'start' -ex 'info line' \
  -ex 'start' -ex 'info line' \
  -ex 'set disable-randomization on' \
  -ex 'start' -ex 'info line' \
  -ex 'start' -ex 'info line' \
  ./pie \
;

输出:

64: 000000000000063a    23 FUNC    GLOBAL DEFAULT   14 main
Temporary breakpoint 1, main () at main.c:4
4           puts("hello world");
Line 4 of "main.c" starts at address 0x5575f5fd263e <main+4> and ends at 0x5575f5fd264f <main+21>.
Temporary breakpoint 2 at 0x5575f5fd263e: file main.c, line 4.

Temporary breakpoint 2, main () at main.c:4
4           puts("hello world");
Line 4 of "main.c" starts at address 0x55e3fbc9363e <main+4> and ends at 0x55e3fbc9364f <main+21>.
Temporary breakpoint 3 at 0x55e3fbc9363e: file main.c, line 4.

Temporary breakpoint 3, main () at main.c:4
4           puts("hello world");
Line 4 of "main.c" starts at address 0x55555555463e <main+4> and ends at 0x55555555464f <main+21>.
Temporary breakpoint 4 at 0x55555555463e: file main.c, line 4.

Temporary breakpoint 4, main () at main.c:4
4           puts("hello world");
Line 4 of "main.c" starts at address 0x55555555463e <main+4> and ends at 0x55555555464f <main+21>.

表示它是0x555555554000 + 随机偏移量 + 63e

但后来我尝试用 grep 查找555555554 的 Linux 内核和 glibc 源代码,但没有成功。

哪个代码的哪一部分计算了那个地址?

我在回答时遇到了这个问题:What is the -fPIE option for position-independent executables in gcc and ld?

【问题讨论】:

  • Basile 在 2015 年也有同样的猜测:stackoverflow.com/a/29856596/196561因为 0x55555555 似乎出现在内核源代码中”,但现在我在 linux 内核中找不到 55555555

标签: linux linux-kernel glibc aslr position-independent-code


【解决方案1】:

一些 Internet 搜索 的 0x555555554000 给出了提示:ThreadSanitizer https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual 存在问题

问:当我运行程序时,它说:致命:ThreadSanitizer 无法映射影子内存(有些东西映射在 0x555555554000

 $ echo 2 >/proc/sys/kernel/randomize_va_space

这可能会在未来的内核中修复,请参阅https://bugzilla.kernel.org/show_bug.cgi?id=66721 ...

 $ gdb -ex 'set disable-randomization off' --args ./a.out

https://lwn.net/Articles/730120/“稳定的内核更新。”发布于 2017 年 8 月 7 日 20:40 UTC(星期一)由 hmh(订阅者)https://marc.info/?t=150213704600001&r=1&w=2 (https://patchwork.kernel.org/patch/9886105/, commit c715b72c1ba4)

将 x86_64 和 arm64 PIE 基础从 0x555555554000 移动到 0x000100000000 破坏了 AddressSanitizer。这是部分还原:

还原的代码是:

b/arch/arm64/include/asm/elf.h
 /*
  * This is the base location for PIE (ET_DYN with INTERP) loads. On
- * 64-bit, this is raised to 4GB to leave the entire 32-bit address
+ * 64-bit, this is above 4GB to leave the entire 32-bit address   * space open for things that want to use the area for 32-bit pointers.   */
-#define ELF_ET_DYN_BASE      0x100000000UL
+#define ELF_ET_DYN_BASE      (2 * TASK_SIZE_64 / 3)


+++ b/arch/x86/include/asm/elf.h
 /*
  * This is the base location for PIE (ET_DYN with INTERP) loads. On
- * 64-bit, this is raised to 4GB to leave the entire 32-bit address
+ * 64-bit, this is above 4GB to leave the entire 32-bit address
  * space open for things that want to use the area for 32-bit pointers.
  */
 #define ELF_ET_DYN_BASE      (mmap_is_ia32() ? 0x000400000UL : \
-                       0x100000000UL)
+                       (TASK_SIZE / 3 * 2))

因此,0x55​​5555554000 与 ELF_ET_DYN_BASE macro(在 fs/binfmt_elf.c for ET_DYN as not randomized load_bias 中引用)相关,对于 x86_64 和 arm64,它就像 TASK_SIZE 的 2/3。当没有CONFIG_X86_32时,x86_64的TASK_SIZE为2^47 - one page in arch/x86/include/asm/processor.h

/*
 * User space process size. 47bits minus one guard page.  The guard
 * page is necessary on Intel CPUs: if a SYSCALL instruction is at
 * the highest possible canonical userspace address, then that
 * syscall will enter the kernel with a non-canonical return
 * address, and SYSRET will explode dangerously.  We avoid this
 * particular problem by preventing anything from being mapped
 * at the maximum canonical address.
 */
#define TASK_SIZE_MAX   ((1UL << 47) - PAGE_SIZE)

旧版本:

/*
 * User space process size. 47bits minus one guard page.
 */
#define TASK_SIZE_MAX   ((1UL << 47) - PAGE_SIZE)

较新的版本还支持5level__VIRTUAL_MASK_SHIFT of 56 bit - v4.17/source/arch/x86/include/asm/processor.h(但don't want to use it before enabled by user + commit b569bab78d8d ".. Not all user space is ready to handle wide addresses"))。

因此,0x55​​5555554000 从公式 (2^47-1page)*(2/3) 向下舍入(load_bias = ELF_PAGESTART(load_bias - vaddr);,vaddr 为零)(或 @987654341 为 2^56 @):

$ echo 'obase=16; (2^47-4096)/3*2'| bc -q
555555554AAA
$ echo 'obase=16; (2^56-4096)/3*2'| bc -q
AAAAAAAAAAA000

2/3 * TASK_SIZE 的一些历史记录:

几乎所有的拱门都将 ELF_ET_DYN_BASE 定义为 TASK_SIZE 的 2/3。尽管 似乎某些架构以错误的方式执行此操作。问题 是 2*TASK_SIZE 可能会溢出 32 位,所以真正的 ELF_ET_DYN_BASE 变得错误。通过在之前划分 TASK_SIZE 来修复此溢出 乘法:(TASK_SIZE / 3 * 2)

diff --git a/include/asm-i386/elf.h b/include/asm-i386/elf.h
+/* This is the location that an ET_DYN program is loaded if exec'ed.  Typical
+   use of this is to invoke "./ld.so someprog" to test out a new version of
+   the loader.  We need to make sure that it is out of the way of the program
+   that it will "exec", and that there is sufficient room for the brk.  */
+
+#define ELF_ET_DYN_BASE         (2 * TASK_SIZE / 3)

【讨论】:

    猜你喜欢
    • 2010-12-03
    • 2011-11-14
    • 1970-01-01
    • 1970-01-01
    • 2020-05-06
    • 2011-05-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多