gcc常用的编译选项对代码的影响
本文出自:http://xfocus.org 作者:alert7
测试环境?redhat?6.2
★?前言
本文讨论gcc的一些常用编译选项对代码的影响。当然代码变了,
它的内存布局也就会变了,随之exploit也就要做相应的变动。
gcc的编译选项实在太多,本文检了几个最常用的选项。
★?演示程序
[alert7@redhat62?alert7]$?cat??test.c
#include?
void?hi(void)
{
printf("hi");
}
int?main(int?argc,?char?*argv[])
{
????????hi();
????????return?0;
}
★?一般情况
[alert7@redhat62?alert7]$?gcc?-o?test?test.c
[alert7@redhat62?alert7]$?wc?-c?test
??11773?test
[alert7@redhat62?alert7]$?gdb?-q?test
(gdb)?disass?main
Dump?of?assembler?code?for?function?main:
0x80483e4?:???????push???%ebp
0x80483e5?:?????mov????%esp,%ebp
0x80483e7?:?????call???0x80483d0?
0x80483ec?:?????xor????%eax,%eax
0x80483ee?:????jmp????0x80483f0?
0x80483f0?:????leave
0x80483f1?:????ret
....
End?of?assembler?dump.
(gdb)?disass?hi
Dump?of?assembler?code?for?function?hi:
0x80483d0?:????????push???%ebp
0x80483d1?:???????mov????%esp,%ebp
0x80483d3?:???????push???$0x8048450
0x80483d8?:???????call???0x8048308?
0x80483dd?:??????add????$0x4,%esp
0x80483e0?:??????leave
0x80483e1?:??????ret
0x80483e2?:??????mov????%esi,%esi
End?of?assembler?dump.
来看看部分的内存映象
???????????????????(内存高址)
??????????????????????????????+--------+
??????????????????????????????|bffffbc4|?argv的地址(即argv[0]的地址)
???????????????????0xbffffb84?+--------+
??????????????????????????????|00000001|?argc的值
???????????????????0xbffffb80?+--------+
??????????????????????????????|400309cb|main的返回地址
???????????????????0xbffffb7c?+--------+?
??????????????????????????????|bffffb98|?调用main函数前的ebp
???????????????????0xbffffb78?+--------+?
??????????????????????????????|080483ec|?hi()的返回地址
???????????????????0xbffffb74?+--------+
??????????????????????????????|bffffb78|?调用hi()前的esp
???????????????????0xbffffb70?+--------+?
??????????????????????????????|08048450|?"hi"的地址
???????????????????0xbffffb6c?+--------+?
??????????????????????????????|?......?|
???????????????????(内存低址)
leave????指令所做的操作相当于MOV?ESP,EBP?然后?POP?EBP?
ret????指令所做的操作相当于POP?EIP
★?-O?编译选项
With?`-O',?the?compiler?tries?to?reduce?code?size?and?execution?time.
When?you?specify?`-O',?the?two?options?`-fthread-jumps'?and?
`-fdefer-pop'?are?turned??on
优化,减少代码大小和执行的时间
[alert7@redhat62?alert7]$?gcc?-O?-o?test?test.c
[alert7@redhat62?alert7]$?wc?-c?test
??11757?test
[alert7@redhat62?alert7]$?gdb?-q?test
(gdb)?disass?main
Dump?of?assembler?code?for?function?main:
0x80483d8?:???????push???%ebp
0x80483d9?:?????mov????%esp,%ebp
0x80483db?:?????call???0x80483c8?
0x80483e0?:?????xor????%eax,%eax
0x80483e2?:????leave
0x80483e3?:????ret
0x80483e4?:????nop
...
End?of?assembler?dump.
(gdb)?disass?hi
Dump?of?assembler?code?for?function?hi:
0x80483c8?:????????push???%ebp
0x80483c9?:???????mov????%esp,%ebp
0x80483cb?:???????push???$0x8048440
0x80483d0?:???????call???0x8048308?
0x80483d5?:??????leave
0x80483d6?:??????ret
0x80483d7?:??????nop
End?of?assembler?dump.
在main()中,把一条jmp指令优化掉了,很显然,这条指令是可以不需要的。
在hi()中,把add????$0x4,%esp优化掉了,这会不会使stack不平衡呢?
来看看部分的内存映象
???????????????????(内存高址)
??????????????????????????????+--------+
??????????????????????????????|bffffbc4|?argv的地址(即argv[0]的地址)
???????????????????0xbffffb84?+--------+
??????????????????????????????|00000001|?argc的值
???????????????????0xbffffb80?+--------+
??????????????????????????????|400309cb|main的返回地址
???????????????????0xbffffb7c?+--------+?
??????????????????????????????|bffffb98|?调用main函数前的ebp
???????????????????0xbffffb78?+--------+?
??????????????????????????????|080483e0|?hi()的返回地址
???????????????????0xbffffb74?+--------+
??????????????????????????????|bffffb78|?调用hi()前的esp
???????????????????0xbffffb70?+--------+?
??????????????????????????????|08048440|?"hi"的地址
???????????????????0xbffffb6c?+--------+?
??????????????????????????????|?......?|
???????????????????(内存低址)
leave????指令所做的操作相当于把MOV?ESP,EBP?然后?POP?EBP??
看到leave指令操作了没有,先把ebp--esp,再pop?ebp,这样即使
在过程内堆栈的esp,ebp是不平衡的,但只要返回时候碰到leave指令
就会平衡了,所以把add????$0x4,%esp优化掉也是没有问题的。
★?-O2?编译选项
-O2????Optimize??even?more.??Nearly?all?supported?optimizations?that?do?
????not?involve?a?space-speed?tradeoff?are?performed.??Loop?unrolling
????and?function?inlining?are?not?done,?for?example.??As?compared?to?-O,
????????this?option?increases?both?compilation?time?and?the?performance?of
????the?generated?code.
[alert7@redhat62?alert7]$?gcc?-O2?-o?test?test.c
[alert7@redhat62?alert7]$?wc?-c?test
??11757?test
[alert7@redhat62?alert7]$?gdb?-q?test
(gdb)?disass?main
Dump?of?assembler?code?for?function?main:
0x80483d8?:???????push???%ebp
0x80483d9?:?????mov????%esp,%ebp
0x80483db?:?????call???0x80483c8?
0x80483e0?:?????xor????%eax,%eax
0x80483e2?:????leave
0x80483e3?:????ret
...
0x80483ef?:????nop
End?of?assembler?dump.
(gdb)?disass?hi
Dump?of?assembler?code?for?function?hi:
0x80483c8?:????????push???%ebp
0x80483c9?:???????mov????%esp,%ebp
0x80483cb?:???????push???$0x8048440
0x80483d0?:???????call???0x8048308?
0x80483d5?:??????leave
0x80483d6?:??????ret
0x80483d7?:??????nop
End?of?assembler?dump.
由于程序比较简单,再优化也没有好优化的了,所以跟-O出来的一样。
★?-fomit-frame-pointer?编译选项
-fomit-frame-pointer
??????????????Don't?keep?the?frame?pointer?in?a?register?for?functions?
??????????that?don't?need?one.??This?avoids?the??instructions?to?save,?
??????????set?up?and?restore?frame?pointers;?it?also?makes?an?extra?
??????????register?available?in?many?functions.??It?also?makes?
??????????debugging?impossible?on?most?machines.
忽略帧指针。这样在程序就不需要保存,安装,和恢复ebp了。这样ebp也就是一个
free的register了,在函数中就可以随便使用了。
[alert7@redhat62?alert7]$?gcc?-fomit-frame-pointer?-o?test?test.c
[alert7@redhat62?alert7]$?wc?-c?test
??11773?test
[alert7@redhat62?alert7]$?gdb?-q?test
(gdb)?disass?main
Dump?of?assembler?code?for?function?main:
0x80483e0?:???????call???0x80483d0?
0x80483e5?:?????xor????%eax,%eax
0x80483e7?:?????jmp????0x80483f0?
0x80483e9?:?????lea????0x0(%esi,1),%esi
0x80483f0?:????ret
....
End?of?assembler?dump.
(gdb)?disass?hi
Dump?of?assembler?code?for?function?hi:
0x80483d0?:????????push???$0x8048450
0x80483d5?:???????call???0x8048308?
0x80483da?:??????add????$0x4,%esp
0x80483dd?:??????ret
0x80483de?:??????mov????%esi,%esi
End?of?assembler?dump.
在main()和hi()中都去掉了以下指令
push???%ebp
mov????%esp,%ebp//这两条指令安装
leave//这条指令恢复
来看看部分的内存映象
???????????????????(内存高址)
??????????????????????????????+--------+
??????????????????????????????|bffffbc4|?argv的地址(即argv[0]的地址)
???????????????????0xbffffb84?+--------+
??????????????????????????????|00000001|?argc的值
???????????????????0xbffffb80?+--------+
??????????????????????????????|400309cb|main的返回地址
???????????????????0xbffffb7c?+--------+?
??????????????????????????????|080483e5|?hi()的返回地址
???????????????????0xbffffb78?+--------+
??????????????????????????????|08048450|??"hi"字符串的地址
???????????????????0xbffffb74?+--------+?
??????????????????????????????|?......?|
???????????????????(内存低址)
没有保存上层执行环境的ebp.
★?-fomit-frame-pointer?&&?-O2?
-fomit-frame-pointer编译选项去掉了
push???%ebp
mov????%esp,%ebp//这两条指令安装
leave//这条指令恢复
-O2编译选项去掉了
add????$0x4,%esp
两个加起来会不会这四条指令一起去掉,从而使stack不平衡呢?
[alert7@redhat62?alert7]$?gcc?-fomit-frame-pointer?-O2?-o?test?test.c
[alert7@redhat62?alert7]$?wc?-c?test
??11741?test
[alert7@redhat62?alert7]$?gdb?-q?test
(gdb)?disass?main
Dump?of?assembler?code?for?function?main:
0x80483d8?:???????call???0x80483c8?
0x80483dd?:?????xor????%eax,%eax
0x80483df?:?????ret
End?of?assembler?dump.
(gdb)?disass?hi
Dump?of?assembler?code?for?function?hi:
0x80483c8?:????????push???$0x8048430
0x80483cd?:???????call???0x8048308?
0x80483d2?:??????add????$0x4,%esp
0x80483d5?:??????ret
0x80483d6?:??????mov????%esi,%esi
End?of?assembler?dump.
来看看部分的内存映象
???????????????????(内存高址)
??????????????????????????????+--------+
??????????????????????????????|bffffbc4|?argv的地址(即argv[0]的地址)
???????????????????0xbffffb84?+--------+
??????????????????????????????|00000001|?argc的值
???????????????????0xbffffb80?+--------+
??????????????????????????????|400309cb|main的返回地址
???????????????????0xbffffb7c?+--------+?
??????????????????????????????|080483dd|?hi()的返回地址
???????????????????0xbffffb78?+--------+
??????????????????????????????|08048430|??"hi"字符串的地址
???????????????????0xbffffb74?+--------+?
??????????????????????????????|?......?|
???????????????????(内存低址)
此时就没有把add????$0x4,%esp优化掉,如果优化掉的话,整个stack就
会变的不平衡,从而会导致程序出错。
★?-fPIC?编译选项
-fPIC????If??supported?for?the?target?machine,?emit?position-independent
????code,?suitable?for?dynamic?linking,even?if?branches?need?large?
????displacements.
产生位置无关代码(PIC),一般创建共享库时用到。
在x86上,PIC的代码的符号引用都是通过ebx进行操作的。
[alert7@redhat62?alert7]$?gcc?-fPIC?-o?test?test.c
[alert7@redhat62?alert7]$?wc?-c?test
??11805?test
[alert7@redhat62?alert7]$?gdb?-q?test
(gdb)?disass?main
Dump?of?assembler?code?for?function?main:
0x80483f8?:???????push???%ebp
0x80483f9?:?????mov????%esp,%ebp
0x80483fb?:?????push???%ebx
0x80483fc?:?????call???0x8048401?
0x8048401?:?????pop????%ebx//取得该指令的地址
0x8048402?:????add????$0x1093,%ebx//此时ebx里面存放着是GOT表的地址
0x8048408?:????call???0x80483d0?
0x804840d?:????xor????%eax,%eax
0x804840f?:????jmp????0x8048411?
0x8048411?:????mov????0xfffffffc(%ebp),%ebx
0x8048414?:????leave
0x8048415?:????ret
...
End?of?assembler?dump.
(gdb)?disass?hi
Dump?of?assembler?code?for?function?hi:
0x80483d0?:????????push???%ebp
0x80483d1?:???????mov????%esp,%ebp
0x80483d3?:???????push???%ebx
0x80483d4?:???????call???0x80483d9?
0x80483d9?:???????pop????%ebx
0x80483da?:??????add????$0x10bb,%ebx
0x80483e0?:??????lea????0xffffefdc(%ebx),%edx
0x80483e6?:??????mov????%edx,%eax
0x80483e8?:??????push???%eax
0x80483e9?:??????call???0x8048308?
0x80483ee?:??????add????$0x4,%esp
0x80483f1?:??????mov????0xfffffffc(%ebp),%ebx
0x80483f4?:??????leave
0x80483f5?:??????ret
0x80483f6?:??????mov????%esi,%esi
End?of?assembler?dump.
来看看部分的内存映象
??
????(内存高址)
??????????????+--------+
??????????????|bffffbc4|?argv的地址(即argv[0]的地址)
???0xbffffb84?+--------+
??????????????|00000001|?argc的值
???0xbffffb80?+--------+
??????????????|400309cb|main的返回地址
???0xbffffb7c?+--------+?
??????????????|bffffb98|?调用main函数前的ebp
???0xbffffb78?+--------+?
??????????????|401081ec|?保存的ebx
???0xbffffb74?+--------+
??????????????|0804840d|?(存放过call?0x8048401的下一条指令地址)
???0xbffffb70?+--------+?
??????????????|bffffb78|?调用hi()前的esp
???0xbffffb6c?+--------+?
??????????????|08049494|?GOT表地址
???0xbffffb68?+--------+?
??????????????|08048470|(存放过call?0x80483d9的下一条指令地址)?
???0xbffffb64?+--------+?
??????????????|?......?|
?????(内存低址)
★?-static?编译选项
-static
????On?systems?that?support?dynamic?linking,?this?prevents?
????linking?with?the?shared?libraries.??On?other??systems,?
????this?option?has?no?effect.
把一些函数都静态的编译到程序中,而无需动态链接了。
[alert7@redhat62?alert7]$?gcc?-o?test?-static?test.c
[alert7@redhat62?alert7]$?wc?-c?test
962808?test
[alert7@redhat62?alert7]$?gdb?-q?test
(gdb)?disass?main
Dump?of?assembler?code?for?function?main:
0x80481b4?:???????push???%ebp
0x80481b5?:?????mov????%esp,%ebp
0x80481b7?:?????call???0x80481a0?
0x80481bc?:?????xor????%eax,%eax
0x80481be?:????jmp????0x80481c0?
0x80481c0?:????leave
0x80481c1?:????ret
...
End?of?assembler?dump.
(gdb)?disass?hi
Dump?of?assembler?code?for?function?hi:
0x80481a0?:????????push???%ebp
0x80481a1?:???????mov????%esp,%ebp
0x80481a3?:???????push???$0x8071528
0x80481a8?:???????call???0x804865c?
0x80481ad?:??????add????$0x4,%esp
0x80481b0?:??????leave
0x80481b1?:??????ret
0x80481b2?:??????mov????%esi,%esi
End?of?assembler?dump.
[alert7@redhat62?alert7]$?ldd?test
????????not?a?dynamic?executable
-static出来的代码已经没有PLT了,GOT虽然有,已经全部为0了。