现在的位置: 首页 > 综合 > 正文

函数调用-汇编分析

2012年01月25日 ⁄ 综合 ⁄ 共 5173字 ⁄ 字号 评论关闭

0.寄存器

4个数据寄存器EAXEBXECXEDX

在用途方面,它们有各自默认的用途:
EAX - 用来保存函数的返回值;
ECX - 用来存放this指针。

2个指针寄存器EBPESP

EBP - 基址指针寄存器,作为堆栈数据存取操作的基本地址指针寄存器;
ESP - 栈指针寄存器,指示堆栈的当前偏移地址(堆栈顶部到堆栈起始位置的距离)。

2个变址寄存器ESIEDI

变址寄存器主要用于存放存储单元在段内的偏移量,用它们可实现多种存储器操作数的寻址方式,
为以不同的地址形式访问存储单元提供方便。 ESI - 源变址寄存器;  EDI - 目标变址寄存器

6个段寄存器ESCSSSDSFSGS

CS - 代码段寄存器(Code Segment Register),存放当前正在执行代码段的起始地址;
DS - 数据段寄存器(Data Segment Register),存放当前正在执行程序所用数据段的起始地址;
SS - 堆栈段寄存器(Stack Segment Register),存放当前正在执行程序暂时保留信息段的起始地址,用来指示堆栈起始位置的指针;
ES/FS/GS - 附加段寄存器(Extra Segment Register),存放程序的数据段的起始地址,为程序设计使用多个数据段带来方便。

-- 16位汇编,示意图如下(段寄存器中的值×16[2^4],得到段基址的值):

1个指令指针寄存器EIP/IP

指令指针EIP/IP(Instruction Pointer) - 存放下次将要执行的指令在代码段的偏移量。
当取出一条指令后,EIP/IP自动加上该指令的长度或者形成转移地址,又指向下一条指令的地址,
从而可以控制有序的执行程序。

1个标志寄存器EFlags/Flags

1. 堆栈

堆栈是由程序开辟的一片内存区域;先进后出;总是向下增长的(位于高地址,向低地址进行分配)。示意图如下:

EBP指向函数活动记录的一个固定位置,又称为栈帧指针(Frame Pointer)。
进入一个函数后,其EBP的值为:上层调用点的ESP-8(Call + Push上层函数的EBP)
可通过EBP + 8 访问第一个参数,EBP + 8 + sizeof(第一个参数)来访问第二个参数,... ...

PUSH(压栈):ESP -= 4
Call函数调用:ESP -= 4
POP(弹栈):ESP += 4
函数返回(ret):ESP += 4
函数返回(ret 8):ESP += 8

2. 寻址方式

(1) 立即数寻址
mov eax,44332211h
(2) 寄存器寻址
mov eax,ebx
(3) 直接寻址
mov eax,[1234h] ;[]表示取地址1234h处的DWORD值
(4) 寄存器间接寻址
mov eax,[ebx]
(5) 寄存器相对寻址(带位移量的寄存器间接寻址)
mov eax,[ebx+80h]
(6) 基址变址寻址(基址的变址寻址)
mov eax,[ebx+esi]
(7) 相对基址变址寻址(基址的带位移量的变址寻址)
mov eax,[ebx+esi+80h]
(8) 带比例的变址寻址
mov eax,[esi*2]
(9) 基址的带比例的变址寻址
mov eax,[ebx+esi*4]
(10) 基址的带位移量的带比例的变址寻址
mov eax,[ebx+esi*8+80h]
(11) I/O端口的直接寻址
in eax,80h
(12) I/O端口的寄存器间接寻址
in eax,dx

16位和32位寻址时的4元素规定:

有效地址元素 16位寻址 32位寻址
基址寄存器 BX,BP 任何32位通用寄存器
变址寄存器 SI,DI 除ESP外的任何32位通用寄存器
比例因子 无(或1) 1,2,4,8
位移量 0,8,16位 0,8,32位

32位存储器寻址方式的组成公式为

32位有效地址 = 基址寄存器+(变址寄存器 × 比例)+位移量

其中的4个组成部分是:
· 基址寄存器——任何8个32位通用寄存器之一;
· 变址寄存器——除ESP之外的任何32位通用寄存器之一;
· 比例——可以是1∕2∕4∕8(因为操作数的长度可以是1∕2∕4∕8字节);
· 位移量——可以是8∕32位值。

16位存储器分段寻址计算方法:

存储段内的每个单元的物理地址PA(Physical Address),用“段基址 : 段内偏移地址”来表达。
段基址即段地址SA(Segment Address)是相应段的起始地址。
段内偏移地址即偏移地址或称有效地址EA(Effective Address),
它是该单元的物理地址PA到段地址SA的距离(字节数),即:EA = PA - SA。
由于实方式下段寄存器只有16位,EA由寄存器BX、BP、SI或DI的内容来计算,也只有16位,
因此,需要将段寄存器的内容左移4位后与EA相加来达到PA需要的20位地址(1MB空间)。
即:PA = SA + EA = 段寄存器的内容左移4位(×16) + EA

3. 常用汇编指令

参见:http://www.cnblogs.com/caoyawei/archive/2009/04/24/1442569.html

4. 示例分析(VC6反汇编)   注:汇编代码的注释符为 ;

/**************************main****************************/
       int main(int argc, char* argv[])
       {
004106B0   push        ebp            // 保存上一层函数栈底指针,esp -= 4
004106B1   mov         ebp,esp        // ebp = esp 设置当前函数栈底指针
004106B3   sub         esp,4Ch        // esp -= 4Ch
004106B6   push        ebx            // 保存ebx到堆栈中,esp -= 4
004106B7   push        esi            // 保存esi到堆栈中,esp -= 4
004106B8   push        edi            // 保存edi到堆栈中,esp -= 4
004106B9   lea         edi,[ebp-4Ch]      // edi = ebp - 4Ch
004106BC   mov         ecx,13h            // ecx = 13h
004106C1   mov         eax,0CCCCCCCCh     // eax = 0CCCCCCCCh
004106C6   rep stos    dword ptr [edi]        // 将edi地址起的(13h*4=4Ch)大小的内存区域初始化为0CCh
            int a = 1;
004106C8   mov         dword ptr [ebp-4],1    // [ebp-4] = 1
            int b = 2;
004106CF   mov         dword ptr [ebp-8],2    // [ebp-8] = 2

            int c = sum(a, b);                  // 参数从右向左,依次压栈
004106D6   mov         eax,dword ptr [ebp-8]    // eax = [ebp-8]
004106D9   push        eax                      // 保存eax到堆栈中,[esp] = eax,esp -= 4
004106DA   mov         ecx,dword ptr [ebp-4]    // ecx = [ebp-4]
004106DD   push        ecx                      // 保存ecx到堆栈中,[esp] = ecx,esp -= 4
004106DE   call        @ILT+5(sum) (0040100a)   // 调用@ILT+5跳转,跳转到sum函数,esp -= 4
004106E3   add         esp,8                    // 调用方进行堆栈平衡  esp += 8(参数a、参数b)
004106E6   mov         dword ptr [ebp-0Ch],eax  // [ebp-0Ch] = eax(eax中存放的是返回值)
    
            return 0;
004106E9   xor         eax,eax        // eax = 0,效率更高
        }
004106EB   pop         edi            // 恢复edi,esp += 4
004106EC   pop         esi            // 恢复esi,esp += 4
004106ED   pop         ebx            // 恢复ebx,esp += 4
004106EE   add         esp,4Ch            // esp += 4Ch
004106F1   cmp         ebp,esp            // 检查栈是否平衡(即:ebp是否等于esp)
004106F3   call        __chkesp (00401060)// 对esp的值进行检查
004106F8   mov         esp,ebp            // esp = ebp
004106FA   pop         ebp                // 恢复上一层函数栈底指针
004106FB   ret                            // 返回,esp += 4
/*************************************************************/

/******************编译器生成的一个jmp**********************/
0040100A   jmp         sum (00401030)        // 跳转到sum函数的第1条指令
/*************************************************************/

/**************************sum函数***************************/
       int sum(int a, int b)
      {
00401030   push        ebp            // esp -= 4
00401031   mov         ebp,esp
00401033   sub         esp,40h
00401036   push        ebx
00401037   push        esi
00401038   push        edi
00401039   lea         edi,[ebp-40h]
0040103C   mov         ecx,10h
00401041   mov         eax,0CCCCCCCCh
00401046   rep stos    dword ptr [edi]
          return a+b;
00401048   mov         eax,dword ptr [ebp+8]    // eax = [ebp+8],参数a
0040104B   add         eax,dword ptr [ebp+0Ch]  // eax += [ebp+0Ch],参数b
       }
0040104E   pop         edi
0040104F   pop         esi
00401050   pop         ebx
00401051   mov         esp,ebp
00401053   pop         ebp
00401054   ret                // 返回,esp += 4
/*************************************************************/

/**************************esp检查***************************/
00401060   jne         __chkesp+3 (00401063)    // 若__chkesp+3不等于00401063,则跳转到00401063
00401062   ret
00401063   push        ebp            // 错误处理代码
00401064   mov         ebp,esp
00401066   sub         esp,0
00401069   push        eax
0040106A   push        edx
0040106B   push        ebx
0040106C   push        esi
0040106D   push        edi
0040106E   push        offset string "The value of ESP was not properl"... (00422030) //等价于push 00422030h
00401073   push        offset string "" (0042202c)    //将字符串""的首地址0042202c值压栈
00401078   push        2Ah
0040107A   push        offset string "i386\\chkesp.c" (0042201c)
0040107F   push        1
00401081   call        _CrtDbgReport (00401390)
00401086   add         esp,14h
00401089   cmp         eax,1
0040108C   jne         __chkesp+2Fh (004010df)
0040108E   int         3            // 软中断
0040108F   pop         edi
00401090   pop         esi
00401091   pop         ebx
00401092   pop         edx
00401093   pop         eax
00401094   mov         esp,ebp
00401096   pop         ebp
00401097   ret
/*************************************************************/

5. 函数调用约定__cdecl/__stdcall/__fastcall/__thiscall)  

参见:http://blog.csdn.net/walkinginthewind/article/details/7580109

    http://www.cnblogs.com/Dah/archive/2006/11/29/576867.html

    http://hi.baidu.com/148332727/item/9e9b38ad9571afad29ce9dc0

6. 参考

http://jpkc.szpt.edu.cn/2006/IA32/ftp/address/frames/content/CHAPT3/index.htm

http://jpkc.zzu.edu.cn/hbyycai/courses/step.asp?id=13

抱歉!评论已关闭.