linux反汇编
(2014-02-16 13:03:00)
方式一:使用gcc
gcc编译有四步走,预编译,编译,汇编,连接
使用-S编译选项
gcc -S
test.c
会在当前目录下生成test.s的文件,该文件即是相应的汇编程序
方式二:使用gdb
首先编译时要是用-g编译选项
gcc -g
./test.c -o ./test
接着运行gdb
gdb
./test--(可执行文件)
在gdb中使用 disassemble + frame(帧),即可查看相应代码段的汇编代码
frame通常为一个函数名。
方式三:使用objdump
命令为 objdump -d test.o--(目标文件)
或者 objdump -d
test--(可执行文件)
一个反汇编代码解释:
(本例使用的GCC的汇编格式,这种格式叫做GAS(GNU ASsembler ,GNU汇编器) )
c语言代码如下:
------test.c-------
#include
#include
int static_var = 5;
int
fun_ret_int(int a, int b, register int c)
{
int
d=1;
return
a+b+c+d;
}
void fun()
{
int i1, i2,
i3, i4,i5, i6,i7,i8,i9,i10;
i1=1;i2=3;i3=5; i4=7;i5=9;
i6=11;i7=13;i8=15;i9=17;i10=19;
int
i;
for(i=11;
i< 20;++i);
i2 =
fun_ret_int(1, 2, i1);
}
int main(int argc ,char *argv[])
{
int i
=1;
int b
=2;
argc =
3;
char
**a=argv;
fun();
return
0;
}
现在编译:
gcc test.c -o
test
反汇编 :
objdump -d
test
我截取部分代码如下:
08048394 :
8048394:
55
push
�p
8048395:
89
e5
mov
%esp,�p
8048397:
83 ec
10
sub
$0x10,%esp
804839a:
8b 4d
10
mov
0x10(�p),�x
804839d:
c7 45 fc 01 00 00 00
movl
$0x1,-0x4(�p)
80483a4:
8b 45
0c
mov
0xc(�p),�x
80483a7:
8b 55
08
mov
0x8(�p),�x
80483aa:
8d 04
02
lea
(�x,�x,1),�x
80483ad:
01
c8
add
�x,�x
80483af:
03 45
fc
add
-0x4(�p),�x
80483b2:
c9
leave
80483b3:
c3
ret
080483b4 :
80483b4:
55
push
�p
80483b5:
89
e5
mov
%esp,�p
80483b7:
83 ec
3c
sub
$0x3c,%esp
80483ba:
c7 45 fc 01 00 00 00
movl
$0x1,-0x4(�p)
80483c1:
c7 45 f8 03 00 00 00
movl
$0x3,-0x8(�p)
80483c8:
c7 45 f4 05 00 00 00
movl
$0x5,-0xc(�p)
80483cf:
c7 45 f0 07 00 00 00
movl
$0x7,-0x10(�p)
80483d6:
c7 45 ec 09 00 00 00
movl
$0x9,-0x14(�p)
80483dd:
c7 45 e8 0b 00 00 00
movl
$0xb,-0x18(�p)
80483e4:
c7 45 e4 0d 00 00 00
movl
$0xd,-0x1c(�p)
80483eb:
c7 45 e0 0f 00 00 00
movl
$0xf,-0x20(�p)
80483f2:
c7 45 dc 11 00 00 00
movl
$0x11,-0x24(�p)
80483f9:
c7 45 d8 13 00 00 00
movl
$0x13,-0x28(�p)
8048400:
c7 45 d4 01 00 00 00
movl
$0x1,-0x2c(�p)
8048407:
c7 45 d0 0b 00 00 00
movl
$0xb,-0x30(�p)
804840e:
eb
04
jmp
8048414
8048410:
83 45 d0
01
addl
$0x1,-0x30(�p)
8048414:
83 7d d0
13
cmpl
$0x13,-0x30(�p)
8048418:
7e
f6
jle
8048410
804841a:
8b 45
fc
mov
-0x4(�p),�x
804841d:
89 44 24
08
mov
�x,0x8(%esp)
8048421:
c7 44 24 04 02 00 00
movl
$0x2,0x4(%esp)
8048428:
00
8048429:
c7 04 24 01 00 00 00
movl
$0x1,(%esp)
8048430:
e8 5f ff ff
ff
call
8048394
8048435:
89 45
f8
mov
�x,-0x8(�p)
8048438:
c9
leave
8048439:
c3
ret
0804843a :
804843a:
55
push
�p
804843b:
89
e5
mov
%esp,�p
804843d:
83 ec
10
sub
$0x10,%esp
8048440:
c7 45 fc 01 00 00 00
movl
$0x1,-0x4(�p)
8048447:
c7 45 f8 02 00 00 00
movl
$0x2,-0x8(�p)
804844e:
c7 45 08 03 00 00 00
movl
$0x3,0x8(�p)
8048455:
8b 45
0c
mov
0xc(�p),�x
8048458:
89 45
f4
mov
�x,-0xc(�p)
804845b:
e8 54 ff ff
ff
call
80483b4
8048460:
b8 00 00 00
00
mov
$0x0,�x
8048465:
c9
leave
8048466:
c3
ret
8048467:
90
nop
8048468:
90
nop
8048469:
90
nop
804846a:
90
nop
804846b:
90
nop
804846c:
90
nop
804846d:
90
nop
804846e:
90
nop
804846f:
90
nop
现在尝试对其中的一些语句进行分析:
在每个函数的开始部分都是:
push �p
mov
%esp,�p
sub
$***,%esp
�p---是帧寄存器,在函数中就是函数的基址寄存器,指向一个函数的栈底(帧底)。
%esp---是栈寄存器,相当于是整个程序的基址寄存器,始终指向栈顶。
push---入栈操作。
mov ---移动
sub ---减法
第一句话 push �p
的意思是�p入栈,此时的�p保存的是上一个函数的帧起始地址,也即调用该函数的地址。
把�p压栈,保存起来,以便返回。
第二句
mov %esp,�p
的意思是
把%esp赋值给�p,%esp保存的是当前程序的栈顶,也即该函数所占用内存的起始地址。
把%esp赋值给�p,也就把�p设置成了当前函数的帧起始地址。
第三句话sub
$***,%esp,并不会在每个程序中都会出现。可以尝试一下,如果一个函数没有任何局部变量,那么反汇编这句话也就
不会出来。这句话的意思是,把%esp减去一个数。我们知道栈空间是由高到底发展的,所以%esp++,相当于%esp=%esp-1。因为调用了新函数,而且该函数有局部变量,那么栈空间就变大了,所以要扩展栈空间,也即是修改%esp,让其指向更低的地址。而让%esp减去多少呢?这要看函数占用多少空间,于其中的局部变量有关,以及他将调用的函数参数有关。其并不计算其参数所占的空间,其参数所占的空间要算在调用它的函数中。
下面来看看参数的压栈顺序;
函数fun中语句:
i2 =
fun_ret_int(1, 2, i1);
所对应的汇编如下:
804841a:
8b 45
fc
mov
-0x4(�p),�x
804841d:
89 44 24
08
mov
�x,0x8(%esp)
8048421:
c7 44 24 04 02 00 00
movl
$0x2,0x4(%esp)
8048428:
00
8048429:
c7 04 24 01 00 00 00
movl
$0x1,(%esp)
8048430:
e8 5f ff ff
ff
call
8048394
从这个汇编中可以看出函数参数的入栈顺序是自左往右的。
mov
-0x4(�p),�x 是把i1 放到�x 寄存器中
mov
�x,0x8(%esp)
是把�x压栈,所以这句话是把i1放在距离%esp为8byte的地方,即8~12byte存放的是一个i1(int)
movl
$0x2,0x4(%esp) 距%esp
4~8byte存放的是2(int)
movl
$0x1,(%esp)
距%esp 0~4byte存放的是1(int)
因为栈是由高到低的,且%esp始终指向栈顶。所以看出入栈顺序是i1,2,1。正好与c文件中fun函数的参数顺序相反。
自右向左压栈有什么好处呢?第一,由于栈是FILO,所以反方向入栈,那么第一个参数也就距离%esp越近。每次取参数时也就很方便,不用把所有参数占用的空间都计算出来,然后在取。第二,当传递的参数过多时,每次都从栈顶计算,取适当位置的参数,其他便可忽略。
gcc编译有四步走,预编译,编译,汇编,连接
使用-S编译选项
会在当前目录下生成test.s的文件,该文件即是相应的汇编程序
方式二:使用gdb
首先编译时要是用-g编译选项
接着运行gdb
在gdb中使用 disassemble + frame(帧),即可查看相应代码段的汇编代码
frame通常为一个函数名。
方式三:使用objdump
命令为 objdump -d test.o--(目标文件)
或者
一个反汇编代码解释:
(本例使用的GCC的汇编格式,这种格式叫做GAS(GNU ASsembler ,GNU汇编器) )
c语言代码如下:
------test.c-------
#include
#include
int static_var = 5;
int
fun_ret_int(int a, int b, register int c)
{
}
void fun()
{
}
int main(int argc ,char *argv[])
{
}
现在编译:
反汇编 :
我截取部分代码如下:
08048394 :
080483b4 :
0804843a :
现在尝试对其中的一些语句进行分析:
在每个函数的开始部分都是:
push
mov
sub
�p---是帧寄存器,在函数中就是函数的基址寄存器,指向一个函数的栈底(帧底)。
%esp---是栈寄存器,相当于是整个程序的基址寄存器,始终指向栈顶。
push---入栈操作。
mov ---移动
sub ---减法
第一句话 push
把�p压栈,保存起来,以便返回。
第二句
把%esp赋值给�p,也就把�p设置成了当前函数的帧起始地址。
第三句话sub
不会出来。这句话的意思是,把%esp减去一个数。我们知道栈空间是由高到底发展的,所以%esp++,相当于%esp=%esp-1。因为调用了新函数,而且该函数有局部变量,那么栈空间就变大了,所以要扩展栈空间,也即是修改%esp,让其指向更低的地址。而让%esp减去多少呢?这要看函数占用多少空间,于其中的局部变量有关,以及他将调用的函数参数有关。其并不计算其参数所占的空间,其参数所占的空间要算在调用它的函数中。
下面来看看参数的压栈顺序;
函数fun中语句:
所对应的汇编如下:
从这个汇编中可以看出函数参数的入栈顺序是自左往右的。
mov
mov
movl
movl
因为栈是由高到低的,且%esp始终指向栈顶。所以看出入栈顺序是i1,2,1。正好与c文件中fun函数的参数顺序相反。
自右向左压栈有什么好处呢?第一,由于栈是FILO,所以反方向入栈,那么第一个参数也就距离%esp越近。每次取参数时也就很方便,不用把所有参数占用的空间都计算出来,然后在取。第二,当传递的参数过多时,每次都从栈顶计算,取适当位置的参数,其他便可忽略。