helloworld对应的汇编

记得n年前汇编考试我在上面写作文,最后老师还是放过去了,语言嘛,用不上自然没动力学。x86的汇编跟blackfin的汇编比起来似乎要零碎一点,不过都是寄存器的操作。

最简单的hello和main函数

int hello(int i)
{
    return i;
}
int main(void)

   hello(1);
    return 42;
}

vc2008的反汇编如下:(本机cpu:Intel Celeron CPU 2.66GHz)

main()函数

int main(void)

004113E0 push        ebp 
004113E1 mov         ebp,esp 
004113E3 sub         esp,0C0h 
004113E9 push        ebx 
004113EA push        esi 
004113EB push        edi 
004113EC lea         edi,[ebp-0C0h] //把偏移地址存入edi
004113F2 mov         ecx,30h 
004113F7 mov         eax,0CCCCCCCCh 
004113FC rep stos    dword ptr es:[edi]//重复ecx次按一次4个字节保存eax到es:[edi]地址,每重复一次,edi地址按4个字节递增。dword ptr 为没有寄存器参与的内存单元访问指令指明访问的长度。
hello(1);
004113FE push        1    
00411400 call        @ILT+10(_hello) (41100Fh) 
00411405 add         esp,4 
return 42;
00411408 mov         eax,2Ah //返回值放入EAX
}
0041140D pop         edi 
0041140E pop         esi 
0041140F pop         ebx 
00411410 add         esp,0C0h 
00411416 cmp         ebp,esp 
00411418 call        @ILT+315(__RTC_CheckEsp) (411140h) 
0041141D mov         esp,ebp 
0041141F pop         ebp 
00411420 ret

跳转

@ILT+10(_hello):
0041100F jmp         hello (4113A0h)

hello()函数

int hello(int i)
{
004113A0 push        ebp 
004113A1 mov         ebp,esp 
004113A3 sub         esp,0C0h 
004113A9 push        ebx 
004113AA push        esi 
004113AB push        edi 
004113AC lea         edi,[ebp-0C0h] 
004113B2 mov         ecx,30h 
004113B7 mov         eax,0CCCCCCCCh 
004113BC rep stos    dword ptr es:[edi] 
return i;
004113BE mov         eax,dword ptr [i] //返回值放入EAX
}
004113C1 pop         edi 
004113C2 pop         esi 
004113C3 pop         ebx 
004113C4 mov         esp,ebp 
004113C6 pop         ebp 
004113C7 ret

寄存器组

数据寄存器:保存操作数和运算结果等信息。
    EAX:Accumulator。函数返回值。取低16位为AX,分割为8位寄存器AH-AL。指令ret返回用到。
    EBX:Base Register。
    ECX:Count Register。
    EDX:Data Register。

指针寄存器:EBP,ESP可作为通用寄存器,即可存储算术逻辑运算的操作数和运算结果。
    EBP:Base Pointer,基指针寄存器,直接访问栈中的数据。
    ESP:Stack Pointer,栈指针寄存器,只可访问栈顶。指令pop/push时自动变。
    EIP:Instruction Pointer,指令指针寄存器,存放下次将要执行的指令在代码段中的偏移量。每走一条指令自动变一次,如果希望跳转后能返回继续就需要跳转前把它放入栈中,返回时出栈。

变址寄存器:主要用于存放存储单元在段内的偏移量。
    ESI: Source Index,源变址寄存器。EDS:ESI即源串段寄存器:源串变址,ESI在串操作中自动增减。
    EDI:Destination Index,目标变址寄存器。EES:EDI即目标串段寄存器:目标串变址,EDI在串操作中自动增减。

段寄存器:内存分段。这里最多为6个内存段,不同的内存段放入不同的东西。
    ECS:Code Segment Register,代码段寄存器。
    EDS:Data Segment Register,数据段寄存器。
    EES:Extra Segment Register,附加段寄存器。
    ESS:Stack Segment Register,栈段寄存器。
    EFS:Extra Segment Register,附加段寄存器。
    EGS:Extra Segment Register,附加段寄存器。

函数调用过程call 和ret

函数调用如何定位指令地址,利用和保护栈空间的呢?主要关注EIP,EBP,ESP的变化就可以看出来了。

int main(void)

... 
hello(1);
004113FE push        1   
//EIP = 00411400 ESP = 0013FE98 EBP = 0013FF68
00411400 call        @ILT+10(_hello) (41100Fh) //跳转到hello()

//从hello()返回
//EIP = 00411405 ESP = 0013FE98 EBP = 0013FF68
00411405 add         esp,4 
...
}

//EIP = 0041100F ESP = 0013FE94 EBP = 0013FF68 
@ILT+10(_hello):
0041100F jmp         hello (4113A0h)

int hello(int i)
{
//EIP = 004113A0 ESP = 0013FE94 EBP = 0013FF68
004113A0 push        ebp 
//EIP = 004113A1 ESP = 0013FE90 EBP = 0013FF68
004113A1 mov         ebp,esp 
//EIP = 004113A3 ESP = 0013FE90 EBP = 0013FE90
... 
return i;
004113BE mov         eax,dword ptr [i] 
}
...
//EIP = 004113C4 ESP = 0013FDD0 EBP = 0013FE90
004113C4 mov         esp,ebp 
//EIP = 004113C6 ESP = 0013FE90 EBP = 0013FE90
004113C6 pop         ebp 
//EIP = 004113C7 ESP = 0013FE94 EBP = 0013FF68
004113C7 ret

call指令:

观察
//EIP = 00411400 ESP = 0013FE98 EBP = 0013FF68
00411400 call        @ILT+10(_hello) (41100Fh) //跳转到hello()
00411405 add         esp,4 

//EIP = 0041100F ESP = 0013FE94 EBP = 0013FF68 
0041100F jmp         hello (4113A0h)
再到
int hello(int i)
{
//EIP = 004113A0 ESP = 0013FE94 EBP = 0013FF68
004113A0 push        ebp

    EIP始终指向下一个要执行的指令,call指令使得ESP往上走了4个字节,观察对应地址的内容,发现是call后一条指令的地址入栈了。这是call指令隐含了跳转前先计算下一条指令的地址放入EIP,然后入栈相当于push eip,再让EIP指向跳转的指令地址。另外看到因为jmp不需要返回它的下一条指令,所以从jmp到hello()时EIP没有入栈,ESP也没变。

ret指令:

观察
//EIP = 004113C7 ESP = 0013FE94 EBP = 0013FF68
004113C7 ret 

//从hello()返回
//EIP = 00411405 ESP = 0013FE98 EBP = 0013FF68
00411405 add         esp,4

   ret指令使得ESP往下走了4个字节,EIP刚好是从栈顶取出来的。这是ret指令隐含了返回前相当于先出栈pop eip。

栈空间转换和恢复:

int hello(int i)
{
004113A0 push        ebp 
004113A1 mov         ebp,esp 
... 
004113C4 mov         esp,ebp 
004113C6 pop         ebp 
004113C7 ret
    进入函数时ebp入栈,返回前ebp出栈,相当于恢复前一函数的ebp。
    进入函数时ebp=esp,ebp在此函数中一直保持不变,返回前esp=ebp,相当于恢复前一函数的esp。

     进入函数时ebp=esp,即把上一函数的栈顶位置作为当前子函数的基指针地址。(ebp相当于blackfin里的FP寄存器)
    至于此函数如何用栈空间esp,就是它的自由啦。

 

函数参数传递和栈数据变化:

观察:函数hello传递参数i

//EIP = 004113FE ESP = 0013FE9C EBP = 0013FF68
//memory 0x0013fe94: 1027039f 0013feb8
004113FE push        1
//EIP = 00411400 ESP = 0013FE98 EBP = 0013FF68
//memory 0x0013fe94: 1027039f 00000001
00411400 call        @ILT+10(_hello) (41100Fh) //跳转到hello()
//EIP = 00411405 ESP = 0013FE98 EBP = 0013FF68
00411405 add         esp,4  //和参数传递的push 1对应,esp恢复
//EIP = 00411408 ESP = 0013FE9C EBP = 0013FF68

//EIP = 0041100F ESP = 0013FE94 EBP = 0013FF68
//memory 0x0013fe94: 00411405 00000001
0041100F jmp         hello (4113A0h)
再到
int hello(int i)
{
//EIP = 004113A0 ESP = 0013FE94 EBP = 0013FF68
//memory 0x0013fe94: 00411405 00000001
//&i : 0x0013fe98, i=1
004113A0 push        ebp
...
 return i;
004113BE  mov         eax,dword ptr [i]
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值