最新消息:XAMPP默认安装之后是很不安全的,我们只需要点击左方菜单的 "安全"选项,按照向导操作即可完成安全设置。

Linux ELF软件中的各种地址

XAMPP案例 admin 381浏览 0评论

ELF软件动态和静态调试的时候经常看到各种地址,而且,地址的范围还不一样,这篇就分析一下吧。

一.先说下ASLR和PIE的区别

ASLR

ASLR 不负责代码段以及数据段的随机化工作,这项工作由 PIE 负责。但是只有在开启 ASLR 之后,PIE 才会生效。

Linux下的ASLR总共有3个级别,0、1、2:

01linux

0是关闭ASLR,没有随机化,堆栈基地址每次都相同,而且libc.so每次的地址也相同。

1是普通的ASLR。mmap基地址、栈基地址、.so加载基地址都将被随机化,但是堆没用随机化

是增强的ASLR,增加了堆随机化

可以使用cat /proc/sys/kernel/randomize_va_space查看是否开启了aslr

关闭aslr:echo 0 >/proc/sys/kernel/randomize_va_space

PIE

PIE叫做代码部分地址无关,PIE能使程序像共享库一样在主存任何位置装载,这需要将程序编译成位置无关,并链接为ELF共享对象。如果不开启PIE的话,那么每次ELF文件加载的地址都是相同的。如果开启PIE,那么每次都会不同。

示例程序如下:

#include<stdio.h>
#include <string.h>

int main(int argc,char *argv[])
{
        char buffer[50];
        printf("buffer is at %p\n",&buffer);

        if(argc>1)
                strcpy(buffer,argv[1]);
        return 0;
}

然后分别用如下命令执行预处理、编译及链接。

remnux@remnux:~/Documents/addr$ gcc -g -no-pie main.c -o main_no_pie
remnux@remnux:~/Documents/addr$ gcc -g -fPIC main.c -o main_pie

当前开启了ASLR。然后分别用编译成绝对地址和位置无关代码(PIC,编译时可以加参数-fPIC),然后,分别查看地址情况。先看main_no_pie:

remnux@remnux:~/Documents/addr$ ldd ./main_no_pie 
  linux-vdso.so.1 (0x00007ffcb05c8000)
  libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f94b9a52000)
  /lib64/ld-linux-x86-64.so.2 (0x00007f94b9c60000)
remnux@remnux:~/Documents/addr$ ldd ./main_no_pie 
  linux-vdso.so.1 (0x00007ffd352ef000)
  libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb3b8815000)
  /lib64/ld-linux-x86-64.so.2 (0x00007fb3b8a23000)
remnux@remnux:~/Documents/addr$ ldd ./main_no_pie 
  linux-vdso.so.1 (0x00007ffdfe074000)
  libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa1a33f7000)
  /lib64/ld-linux-x86-64.so.2 (0x00007fa1a3605000)
remnux@remnux:~/Documents/addr$ ./main_no_pie 
buffer is at 0x7fff708507a0
remnux@remnux:~/Documents/addr$ ./main_no_pie 
buffer is at 0x7ffc54006d40
remnux@remnux:~/Documents/addr$ ./main_no_pie 
buffer is at 0x7ffc1fdd9680
remnux@remnux:~/Documents/addr$ ./main_no_pie 
buffer is at 0x7ffebe6656a0
remnux@remnux:~/Documents/addr$

可以看出,开启ASLR之后,动态库的地址在变化,buffer的运行地址也在变化。

radare2看一下:

[0x00401176]> pdf
            ; DATA XREF from entry0 @ 0x4010b1
            ;-- main:
┌ 117: int dbg.main (signed int64_t argc, char **argv);
│           ; var char **src @ rbp-0x50
│           ; var signed int64_t var_44h @ rbp-0x44
│           ; var char[50] buffer @ rbp-0x40
│           ; var int64_t canary @ rbp-0x8
│           ; arg signed int64_t argc @ rdi
│           ; arg char **argv @ rsi
│           0x00401176      f30f1efa       endbr64                     ; main.c:5 { ; int main(int argc,char ** argv);
│           0x0040117a      55             push rbp
│           0x0040117b      4889e5         mov rbp, rsp
│           0x0040117e      4883ec50       sub rsp, 0x50
│           0x00401182      897dbc         mov dword [var_44h], edi    ; argc
│           0x00401185      488975b0       mov qword [src], rsi        ; argv
│           0x00401189      64488b042528.  mov rax, qword fs:[0x28]
│           0x00401192      488945f8       mov qword [canary], rax
│           0x00401196      31c0           xor eax, eax
(gdb) r
Starting program: /home/remnux/Documents/addr/main_no_pie 

Breakpoint 1, main (argc=32767, argv=0xc2) at main.c:5
5  { 
(gdb) p &main
$1 = (int (*)(int, char **)) 0x401176 <main>
(gdb)

链接后的地址便为程序实际运行地址,默认初始地址为0x400000(见上篇文章)。

再看 main_pie:

emnux@remnux:~/Documents/addr$ ldd ./main_pie 
  linux-vdso.so.1 (0x00007ffe90b33000)
  libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9988fed000)
  /lib64/ld-linux-x86-64.so.2 (0x00007f9989200000)
remnux@remnux:~/Documents/addr$ ldd ./main_pie 
  linux-vdso.so.1 (0x00007ffdaaf1e000)
  libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fdae13ee000)
  /lib64/ld-linux-x86-64.so.2 (0x00007fdae1601000)
remnux@remnux:~/Documents/addr$ ldd ./main_pie 
  linux-vdso.so.1 (0x00007ffe5acb3000)
  libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f87fa0fa000)
  /lib64/ld-linux-x86-64.so.2 (0x00007f87fa30d000)
remnux@remnux:~/Documents/addr$ ldd ./main_pie 
  linux-vdso.so.1 (0x00007ffe80546000)
  libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007faec2287000)
  /lib64/ld-linux-x86-64.so.2 (0x00007faec249a000)

remnux@remnux:~/Documents/addr$ ./main_pie 
buffer is at 0x7ffe92a12300
remnux@remnux:~/Documents/addr$ ./main_pie 
buffer is at 0x7ffdcc71c9d0
remnux@remnux:~/Documents/addr$ ./main_pie 
buffer is at 0x7ffde0932680
remnux@remnux:~/Documents/addr$ ./main_pie 
buffer is at 0x7fff0a8adc10
remnux@remnux:~/Documents/addr$

可以看出,开启ASLR之后,动态库的地址在变化,buffer的运行地址也在变化。

[0x00001189]> aaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for vtables
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information
[x] Integrate dwarf function information.
[x] Use -AA or aaaa to perform additional experimental analysis.
[0x00001189]> s main[0x00001189]>
Reading symbols from ./main_pie...
(gdb) b main
Breakpoint 1 at 0x1189: file main.c, line 5.
(gdb) r
Starting program: /home/remnux/Documents/addr/main_pie 

Breakpoint 1, main (argc=32767, argv=0xc2) at main.c:5
5  { 
(gdb) b main
Note: breakpoint 1 also set at pc 0x555555555189.
Breakpoint 2 at 0x555555555189: file main.c, line 5.
(gdb) p &main
$1 = (int (*)(int, char **)) 0x555555555189 <main>
(gdb) 

可以看出,链接后的地址与程序实际运行地址不同。

也可以看出,no_pie是已经指定了ELF加载位置,所以,逆向时显示0x0040****地址;pie未指定ELF加载位置,所以,逆向时显示0x0000****地址。

 

二、下面关闭ASLR

oot@remnux:/home/remnux/Documents/addr# echo 0 >/proc/sys/kernel/randomize_va_space
remnux@remnux:~/Documents/addr$ ldd ./main_no_pie 
  linux-vdso.so.1 (0x00007ffff7fce000)
  libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffff7dbd000)
  /lib64/ld-linux-x86-64.so.2 (0x00007ffff7fcf000)
remnux@remnux:~/Documents/addr$ ldd ./main_no_pie 
  linux-vdso.so.1 (0x00007ffff7fce000)
  libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffff7dbd000)
  /lib64/ld-linux-x86-64.so.2 (0x00007ffff7fcf000)
remnux@remnux:~/Documents/addr$ ldd ./main_no_pie 
  linux-vdso.so.1 (0x00007ffff7fce000)
  libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffff7dbd000)
  /lib64/ld-linux-x86-64.so.2 (0x00007ffff7fcf000)
remnux@remnux:~/Documents/addr$ ./main_pie 
buffer is at 0x7fffffffdf70
remnux@remnux:~/Documents/addr$ ./main_pie 
buffer is at 0x7fffffffdf70
remnux@remnux:~/Documents/addr$ ./main_pie 
buffer is at 0x7fffffffdf70
remnux@remnux:~/Documents/addr$ ./main_pie 
buffer is at 0x7fffffffdf70
remnux@remnux:~/Documents/addr$

可以看出,每次运行的地址都一样了。

remnux@remnux:~/Documents/addr$ r2 ./main_no_pie 
 -- Sharing your latest session to Facebook ...
[0x00401090]> s main
[0x00401176]>

Reading symbols from ./main_no_pie...
(gdb) b main
Breakpoint 1 at 0x401176: file main.c, line 5.
(gdb) r
Starting program: /home/remnux/Documents/addr/main_no_pie 

Breakpoint 1, main (argc=32767, argv=0xc2) at main.c:5
5  { 
(gdb) p &main
$1 = (int (*)(int, char **)) 0x401176 <main>
(gdb) 

remnux@remnux:~/Documents/addr$ r2 ./main_pie 
Warning: run r2 with -e bin.cache=true to fix relocations in disassembly
 -- Mind the tab
[0x000010a0]> s main
[0x00001189]>

Reading symbols from ./main_pie...
(gdb) b main
Breakpoint 1 at 0x1189: file main.c, line 5.
(gdb) r
Starting program: /home/remnux/Documents/addr/main_pie 

Breakpoint 1, main (argc=32767, argv=0xc2) at main.c:5
5  { 
(gdb) p &main
$1 = (int (*)(int, char **)) 0x555555555189 <main>
(gdb)

可见,关闭ASLR对pie和no_pie没影响。

三、编译时改变地址

首先,gcc编译时,默认使用main函数作为执行入口点。我们可以在编译时改变,使其指向别的函数。为了实验,改变代码如下:

#include<stdio.h>
#include <string.h>
#include <stdlib.h>

void test(){
        printf("Entry is test function!\n");
        exit(0);
}

int main(int argc,char *argv[])
{
        printf("Entry is the default main!\n");
        return 0;
}

remnux@remnux:~/Documents/addr$ gcc -g -no-pie -nostartfiles -e test main.c -o main_test
remnux@remnux:~/Documents/addr$ gcc -g -no-pie main.c -o main_default
remnux@remnux:~/Documents/addr$ readelf -h main_default | grep Entry
  Entry point address:               0x401070
remnux@remnux:~/Documents/addr$ readelf -h main_test | grep Entry
  Entry point address:               0x401050
remnux@remnux:~/Documents/addr$
remnux@remnux:~/Documents/addr$ ./main_default 
Entry is the default main!
remnux@remnux:~/Documents/addr$ ./main_test 
Entry is test function!
remnux@remnux:~/Documents/addr$

实验证明了这一点。

其次,我们还可以在编译时改变程序默认加载地址。

remnux@remnux:~/Documents/addr$ gcc -g -no-pie -Wl,-Ttext-segment,0x4200000 main.c -o main_addr
remnux@remnux:~/Documents/addr$ r2 ./main_addr 
 -- All your base are belong to r2
[0x04201070]> aaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for vtables
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information
[x] Integrate dwarf function information.
[x] Use -AA or aaaa to perform additional experimental analysis.
[0x04201070]> s main
[0x04201174]> 

可以看到,我们编译时改成了4200000,而不是默认的4000000了,实验证明了这一点。

此外,-Ttext-segment指定的必需是一个页对齐的地址;它并不是指定.text段的加载位置,而是指定整个elf的加载位置。

附:

位置无关码需用到PLT和GOT,其中,GOT(Global Offset Table):全局偏移表用于记录在ELF文档中所用到的共享库中符号的绝对地址。在进程刚开始运行时GOT表项是空的,当符号第一次被调用时会动态解析符号的绝对地址然后转去执行,并将被解析符号的绝对地址记录在GOT中,第二次调用同一符号时,由于GOT中已经记录了其绝对地址,直接转去执行即可,不用重新解析。

PLT(Procedure Linkage Table):过程链接表的作用是将位置无关的符号转移到绝对地址。当一个外部符号被调用时,PLT去引用GOT中的其符号对应的绝对地址,然后转入并执行。

GOT位于.got.plt section中,PLT位于.plt section中。

转载请注明:XAMPP中文组官网 » Linux ELF软件中的各种地址

您必须 登录 才能发表评论!