CTF-All-In-One/doc/4.3_gcc_arg.md
2018-08-05 17:43:10 +08:00

250 lines
9.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 4.3 GCC 编译参数解析
- [GCC](#gcc)
- [常用选择](#常用选项)
- [Address sanitizer](#address-sanitizer)
- [mcheck](#mcheck)
- [参考资料](#参考资料)
## GCC
```text
$ wget -c http://www.mirrorservice.org/sites/sourceware.org/pub/gcc/releases/gcc-4.4.0/gcc-4.4.0.tar.bz2
$ tar -xjvf gcc-4.4.0.tar.bz2
$ ./configure
$ make && sudo make install
```
## 常用选项
使用 `gcc -v` 可以查看默认开启的选项:
```text
$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 5.4.0-6ubuntu1~16.04.9' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9)
```
### 控制标准版本的编译选项
- `-ansi`:告诉编译器遵守 C 语言的 ISO C90 标准。
- `-std=`:通过使用一个参数来设置需要的标准。
- `c89`:支持 C89 标准。
- `iso9899:1999`:支持 ISO C90 标准。
- `gnu89`:支持 C89 标准。
### 控制标准版本的常量
这些常量(#define可以通过编译器的命令行选项来设置或者通过源代码总的 `#define` 语句来定义。
- `__STRICT_ANSI__`:强制使用 C 语言的 ISO 标准。这个常量通过命令行选项 `-ansi` 来定义。
- `_POSIX_C_SOURCE=2`:启用由 IEEE Std1003.1 和 1003.2 标准定义的特性。
- `_BSD_SOURCE`:启用 BSD 类型的特性。
- `_GNU_SOURCE`:启用大量特性,其中包括 GNU 扩展。
### 编译器的警告选项
- `-pedantic`:除了启用用于检查代码是否遵守 C 语言标准的选项外,还关闭了一些不被标准允许的传统 C 语言结构,并且禁用所有的 GNU 扩展。
- `-Wformat`:检查 printf 系列函数所使用的参数类型是否正确。
- `Wparentheses`:检查是否总是提供了需要的圆括号。当想要检查一个复杂结构的初始化是否按照预期进行时,这个选项就很有用。
- `Wswitch-default`:检查是否所有的 switch 语句都包含一个 default case。
- `Wunused`:检查诸如声明静态函数但没有定义、未使用的参数和丢弃返回结果等情况。
- `Wall`:启用绝大多数 gcc 的警告选项,包括所有以 -W 为前缀的选项。
## Address sanitizer
Address sanitizer 是一种用于检测内存错误的技术GCC 从 4.8 版本开始支持了这一技术。ASan 在编译时插入额外指令到内存访问操作中,同时通过 Shadow memory 来记录和检测内存的有效性。ASan 其实只是 Sanitizer 一系列工具中的一员,其他工具比如 memory leak 检测在 LeakSanitizer 中uninitialized memory read 检测在 MemorySanitizer 中等等。
举个例子,很明显下面这个程序存在栈溢出:
```C
#include<stdio.h>
void main() {
int a[10] = {0};
int b = a[11];
}
```
编译时加上参数 `-fsanitize=address`,如果使用 Makefile则将参数加入到 CFLAGS 中:
```text
$ gcc -fsanitize=address santest.c
```
然后运行:
```text
$ ./a.out
=================================================================
==9399==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffc03f4d64c at pc 0x565515082ad6 bp 0x7ffc03f4d5e0 sp 0x7ffc03f4d5d0
READ of size 4 at 0x7ffc03f4d64c thread T0
#0 0x565515082ad5 in main (/home/firmy/a.out+0xad5)
#1 0x7fb4c04c0f69 in __libc_start_main (/usr/lib/libc.so.6+0x20f69)
#2 0x565515082899 in _start (/home/firmy/a.out+0x899)
Address 0x7ffc03f4d64c is located in stack of thread T0 at offset 76 in frame
#0 0x565515082989 in main (/home/firmy/a.out+0x989)
This frame has 1 object(s):
[32, 72) 'a' <== Memory access at offset 76 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow (/home/firmy/a.out+0xad5) in main
Shadow bytes around the buggy address:
0x1000007e1a70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000007e1a80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000007e1a90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000007e1aa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000007e1ab0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x1000007e1ac0: f1 f1 f1 f1 00 00 00 00 00[f2]f2 f2 00 00 00 00
0x1000007e1ad0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000007e1ae0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000007e1af0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000007e1b00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000007e1b10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==9399==ABORTING
```
确实检测出了问题。在实战篇中,为了更好地分析软件漏洞,我们可能会经常用到这个选项。
参考:<https://en.wikipedia.org/wiki/AddressSanitizer>
## mcheck
利用 mcheck 可以实现堆内存的一致性状态检查。其定义在 `/usr/include/mcheck.h`,是一个 GNU 扩展函数,原型如下:
```c
#include <mcheck.h>
int mcheck(void (*abortfunc)(enum mcheck_status mstatus));
```
可以看到参数是一个函数指针,但检查到堆内存异常时,通过该指针调用 abortfunc 函数,同时传入一个 mcheck_status 类型的参数。
举个例子,下面的程序存在 double-free 的问题:
```c
#include <stdlib.h>
#include <stdio.h>
void main() {
char *p;
p = malloc(1000);
fprintf(stderr, "About to free\n");
free(p);
fprintf(stderr, "About to free a second time\n");
free(p);
fprintf(stderr, "Finish\n");
}
```
通过设置参数 `-lmcheck` 来链接 mcheck 函数:
```text
$ gcc -lmcheck t_mcheck.c
$ ./a.out
About to free
About to free a second time
block freed twice
Aborted (core dumped)
```
还可以通过设置环境变量 `MALLOC_CHECK_` 来实现,这样就不需要重新编译程序。
```text
$ gcc mcheck.c
$ #检查到错误时不作任何提示
$ MALLOC_CHECK_=0 ./a.out
About to free
About to free a second time
Finish
$ #检查到错误时打印一条信息到标准输出
$ MALLOC_CHECK_=1 ./a.out
About to free
About to free a second time
*** Error in `./a.out': free(): invalid pointer: 0x0000000001fb9010 ***
Finish
$ #检查到错误时直接中止程序
$ MALLOC_CHECK_=2 ./a.out
About to free
About to free a second time
Aborted (core dumped)
```
具体参考 `man 3 mcheck``man 3 mallopt`
glibc 还提供了 `mtrace()``muntrace()` 函数分别在程序中打开和关闭对内存分配调用进行跟踪的功能。这些函数需要与环境变量 `MALLOC_TRACE` 配合使用,该变量定义了写入跟踪信息的文件名。在被调用时,`mtrace()` 会检查是否定义了该文件,又是否可以读写该文件。如果一切正常,那么会在文件里跟踪和记录所有对 malloc 系列函数的调用。由于生成的文件不易于理解,还提供了脚本(`mtrace`)用于分析文件,并生成易于理解的汇总报告。
将上面的例子修改一下:
```c
#include <stdlib.h>
#include <stdio.h>
#include <mcheck.h>
void main() {
char *p;
mtrace();
calloc(16, 16);
fprintf(stderr, "calloc some chunks that will not be freed\n");
p = malloc(1000);
fprintf(stderr, "About to free\n");
free(p);
fprintf(stderr, "About to free a second time\n");
free(p);
fprintf(stderr, "Finish\n");
muntrace();
}
```
```text
$ gcc t_mtrace.c
$ export MALLOC_TRACE=/tmp/t
$ ./a.out
calloc some chunks that will not be freed
About to free
About to free a second time
Finish
$ mtrace /tmp/t
- 0x000055e427cde7b0 Free 5 was never alloc'd 0x55e425da287c
Memory not freed:
-----------------
Address Size Caller
0x000055e427cde6a0 0x100 at 0x55e425da27f6
```
于是 double-free 和内存泄漏被检测出来了。
## 参考资料
- [GCC online documentation](https://gcc.gnu.org/onlinedocs/)