再探elf格式

本篇记录一些elf格式里面的利用小技巧~

elf结构

网上很多,例如:Keith Makan

ld.so

连接器,有些好用的参数有助于调试

LD_LIBRARY_PATH

指定so,优先级最高,例如:LD_LIBRARY_PATH=/root/libc.so.2 ./shell,需要注意,若指定libc则须保证系统默认的ld版本要与之匹配,否则就不能正常运行。

LD_SHOW_AUXV

很有用的一个环境变量,可以输出一些关键的信息,首先先来看看栈布局:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//参考[1]
...
local variables of main
saved registers of main
return address of main
argc
argv
envp
stack from startup code
argc
argv pointers
NULL that ends argv[]
environment pointers
NULL that ends envp[]
ELF Auxiliary Table <=====auxvec
argv strings
environment strings
program name
NULL

看到它其实是位于栈上,env之后,要是可以泄露出这个的话,那么就可以拿到下面这些东西了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
➜  ~ LD_SHOW_AUXV=1 ls
AT_SYSINFO: 0x7fffb7dcfc80 #__kernel_vsyscall 32位
AT_SYSINFO_EHDR: 0x7fffb7dcf000 #vdso
AT_HWCAP: 1fabfbff #arch dependent hints at CPU capabilities
AT_PAGESZ: 4096 #system page size
AT_CLKTCK: 100 #frequency at which times() increments
AT_PHDR: 0x55a7f773b040 #program headers for program
AT_PHENT: 56 #size of program header entry
AT_PHNUM: 9 #number of program headers
AT_BASE: 0x7f3140848000 #libcbase
AT_FLAGS: 0x0 #flags
AT_ENTRY: 0x55a7f7740430 #entry point of program
AT_UID: 0 #real uid
AT_EUID: 0 #effective uid
AT_GID: 0 #real gid
AT_EGID: 0 #effective gid
AT_SECURE: 0 #secure mode boolean
AT_RANDOM: 0x7fffb7ca7ee9 #stackcannry
AT_EXECFN: /bin/ls #filename of program
AT_PLATFORM: x86_64

LD_PRELOAD

它指定的动态库最先被加载,于是可以用来hook动态库函数,首先编译hook函数库:

1
2
3
4
5
#include<stdio.h>
unsigned int alarm(unsigned int seconds){
printf("%d\n",seconds);
}
// gcc hook.c -o hook.so -shared -fPIC [-m32]

接着hook
LD_PRELOAD=./hook.so ./shellcode

__libc_stack_end

这是ld.so中的符号,指向了栈的起始位置,要是泄露出它,就可以通过栈起始位置加偏移泄露出auxv的地址,于是可以得到那里面的几个关键东西(向上看啦)。

瘦身

在学习elf格式时都提到section与segment是对于两种不同的环境而言的,前者是在文件中中,后者是在内存中,elf文件被加载到内存中,会通过文件头解析segment,但并没有用到section部分,于是可以把这部分删掉,程序仍然可以正常运行,但是却不能被readelf等工具分析了[3]:

于是可以对它做手脚来抵抗分析,emmmm,当然现在的一些工具已经能正常分析它了!那看看头里面有些什么:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef struct elfhdr {
unsigned char e_ident[EI_NIDENT];
Elf32_Half e_type;
Elf32_Half e_machine;
Elf32_Word e_version; /* object file version */
Elf32_Addr e_entry;
Elf32_Off e_phoff;
Elf32_Off e_shoff; /* section header table offset */
Elf32_Word e_flags;
Elf32_Half e_ehsize; /* program header entry size */
Elf32_Half e_phentsize;
Elf32_Half e_phnum;
Elf32_Half e_shentsize; /* section header entry size */
Elf32_Half e_shnum; /* number of section header entries */
Elf32_Half e_shstrndx; /* section header table's "section
header string table" entry offset */
} Elf32_Ehdr;

如上标记内容是在加载时未用到的,另外e_shoff指向section header表,于是那些内容也可以删掉:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Section Header */
typedef struct {
Elf32_Word sh_name; /* name - index into section header
string table section */
Elf32_Word sh_type; /* type */
Elf32_Word sh_flags; /* flags */
Elf32_Addr sh_addr; /* address */
Elf32_Off sh_offset; /* file offset */
Elf32_Word sh_size; /* section size */
Elf32_Word sh_link; /* section header table index link */
Elf32_Word sh_info; /* extra information */
Elf32_Word sh_addralign; /* address alignment */
Elf32_Word sh_entsize; /* section entry size */
} Elf32_Shdr;

当然还可以继续深入,Jonathan Salwan[3]大大的程序只删除到了这里,继续删除的效果应该会更好!

参考

[1]http://www.win.tue.nl/~aeb/linux/hh/stack-layout.html
[2]http://www.man7.org/linux/man-pages/man8/ld.so.8.html
[3]http://shell-storm.org/blog/Linux-process-execution-and-the-useless-ELF-header-fields/
[4]https://www.tuicool.com/articles/MNRJVj