《Intel+汇编语言程序设计(第五版》——第8章 高级过程

STDCALL 看到 EBP+几  就得 RET 4*N  除非 伪指令定义好的  PROC USES 参数,参数

C            在 调用者 CALL ADDTEWO    后 ADD ESP,8 

 

1 为什么返回RET 有时候会出错误:STDCALL约定 子程序 ADDTWO 要清理参数

   RET 8的含义(MOV ESP,EBP   RET    之后 ADD ESP,8清除 参数)

   C约定 是调用者 直接ADD ESP,8  直接清理然后找到了返回地址

      C 约定 先(CALL已经返回)清理 在返回 别的调用者
         STDCALL  先返回返回调用者后清理 

-------------------------------------------------------------------------------

  局顺-   反+参

关键在于参数赋值(过程调用前 PUSH 和MOV)和参数的访问 

   参数赋值 PUSH  5,push 6 这样的情况 要注意RET  8

  参数访问 mov eax,[ebp+12]  mov eax,[ebp+8]  ;此时能正常返回 不用看RET

参数的顺序:

反向  有4个参数的话 1 [EBP+20] 2 [EBP+16] 3 [EBP+12] 4 [EBP+8]

   C++(4,3,2,1)

-------------------

 2 调用子过程里有局部变量  push ebp    mov ebp,esp   sub esp,8  ;俩变量 此时 ESP 有变动

   结束之前要销毁局部变量           mov esp,ebp       ret 能正确返回

顺序使用局部变量 第1EBP-4      第2EBP-8        第3EBP-12   ESP在最后的局部变量位置上   所以要MOV ESP,EBP

 --------------------------------------------------------------------------------------------------------------------------------

3:种过程形参数()

STDCALL:  RET 4*N

C   :            ADD ESP,4*N 

完整PROC 带参数 就不考虑 RET +几

 

 

 

4 局部变量 :
(1)  用LEA返回运行时变量地址 方便双字对齐

(2)ENTER 和LEAVE为局部变量服务

(3)LOCAL声明多变量 (自动开辟变量空间,自动leave)

 

 

一 .过程参数:2种基本类型的 寄存器参数(MOV)和堆栈参数(PUSH)。

Irvine32和Irine16库使用寄存器参数(用的MOV 不是PUSH);缺点代码混乱,堆栈的参数必须由调用过程压栈。

(1)寄存器参数 速度快 乱

 

 

 (2)堆栈参数  灵活(参数顺序相反)

 

 

 堆栈两类参数: 值参数(变和常值),引用参数(变量地址)##传递数组也是地址##

(1)值传递 (变量和常量)

 

CALL前 堆栈

 

 

 

 

(2)传递引用

调用Swap前 堆栈

 

 

 

 传递数组:

 

二.堆栈参数的访问(C++)

1准备:

 访问子过程参数 要准备2个步骤:

    AddTwo PROC

            push ebp          ; 保存EBP

 

         

            mov ebp,esp     ;用EBP代替ESP 访问参数 ;ESP好做别的事情

 

 

2:访问:

     

  

 

3:堆栈的清理:

 

 

 

 

         

3种解决:

      (1)add esp,8

               ret

     

      (2)   如果有 push    ebp

                  mov     ebp,esp

         

            mov  esp,ebp

              ret

       (3)   STDCALL 调用约定 : 

                   pop ebp

                   ret     8              ;1个参数是 ret  4   ,2个是8  3个是 12  ,。。。。

 


 


 1) (PUSH的原因)MOVZX扩充8位和16位参数:   如果参数 是8位或者16位的 就不能 PUSH  ‘X’    push  word1 (字)

要用MOVZX变量来和1个寄存器间接 扩充

  .data

           charVal BYTE 'x'

  .code 

            push charVal          ;错误

            call   Uppercase

 

改:

      movzx  eax,charVal

      push eax

      call  Uppercase

-----------------------------------------------------------------------------------------------------

2)16位

.data

word1 WORD 1234h

word2 WORD 4111h

.code   

          push  word1

push word2

  call AddTwo           ;错误

改:

     movzx      eax,word1

      push        eax

      movzx      eax,word2

       push       eax

call AddTwo

  --------------------------------------------------------------------------------------------------

 3)长整数64位:

  .data

logval  DQ  1234567890ABCDEFh

.code

      push DWORD PTR logval+4     ;高双字 12345678

      push DWORD PTR logval          ;低双字 90ABCDEF

call WriteHex 64

 

 

 

 

-----------------------------------------------------------------------

 

 ------------------------------------------------------------------------

 三.局部变量

1。局部变量是运行时栈上创建的,在内存中起位置在基址指针(EBP)之下,汇编是不给定默认值,运行时候初始化。

 

 

 C++ 局部变量X和局部变量Y:

    void MySub()

{

     int   x=10;

     int   y =20;

 

}

    2个变量8个字节的空间

 反汇编 C约定 :创建,赋值以及堆栈上移除变量

 

 

 

 

 如果没有 mov esp,ebp   

ESP 在20位置上    书上有错误 (是10的位置是错的)图上都写了是ESP 在EBP-8第2个变量

 

 2  lea指令 动态运行时候 返偏移地址  (ADDR和OFFSET在保护模式下返回32位偏移值),因为Irvine32.inc  中的.MODEL伪指令指定了平坦内存模式

(1)   全局变量 获取变量地址:  mov 寄存器,offset 变量名(老罗的书73页)

(2)局部变量  EBP操作的  ebp    40100h

                                 变量1    ebp-4=400FCH

     EBP随着程序的执行环境不同而不同  在编译的时候就不确定 所以offset获取不到

                      获取变量地址:  lea,[ebp-4]

(3)实参赋值:invoke的参数用到一个局部变量地址: (invoke用 addr一起  但是不能和外EAX一起 invoke TEST eax,addr szH);就不能用LEA 和OFFSET 并且 如果是数组的话 指向第2个元素就要+4  INVOKE sWAP,ADDR [R+1],ADDR [R+4]  239页

   addr  全局变量名 (此时按照OFFSET方法用)

           和局部变量名(自动用LEA指令把地址给EAX,EAX代替变量地址)

但是ADDR 里不能是[ebp]也不能和MOV一起用

 

 

错误:

 

LEA的另一个用法

 

(4)ptr:以不同类型 访问参数:(老罗71页)

12

1234 5678h

装入 内存 78 56 34 12

取:         12 34 56 78

1:12h

2:3412h    34

3:7812 3412h

和放到内存相反

-----------------------------------------------------

 

 (5)ENTER和LEAVE指令 局部变量的用法

 (6)LOCAL声明变量(可以与PTR一起表示位数 和[]数组)

后面自动生成leava删除局部变量空间 不用管RET

注意 字节变量声明会分配多余空间

注意 字节变量声明会分配多余空间

 (6).stack  4096  保留额外的堆栈空间

总结


INVOKE ,ADDR ,PROC和PROTO 指令

 

PROC

 

参数名:类型 也可以是TYPEDEF和STRUCT 变量 和以下的指针

 

有引用参数就不能传递立即数;  sub PROC da:ptr word      mov esi,da   INVOKE sub,1000H出现一个保护错误内存1000H不在数据段内

 

PROTO

调用 注意前后顺序

(1)把PROC改为PROTO

(2)如果有USES伪指令,去掉USES后面的寄存器

(3)类型与实现要匹配

 

 

 

 


创建多个模块OBJ    2种方法:

(1)EXTERN(移植有问题)

(2)INVOKE 和PROTO

 

          1在一个过程 中变成私有 为了是避免变量名与其他模块使用发生冲突

          2多过程   OPTION PROC:PRIVATE 所有过程都成私有

         PUBLEC 导出  PUBLIC SUB1,SUB2,SUB3

MIAN入口必须是PUBLIC否则找不到入口

 

 

   调用 ;

EXTRN sub@0:PROC

 

call    sub@0   0代表参数  如果2个参数 就是8

跨界使用变量和符号

  变量和符号默认是私有的  要用PUBLIC 导出指定名字

PUBLIC count ,SYM1

SYM1=10  ;符号

.DATA

count DWORD 0

  

访问外部:    EXTERN  name :type

    符号: 以EQU和=定义 type就是ABS

    变量: type 是 BYTE,DOWRD     PTR BYTE

ENTERN: one:WORD ,two:SDWORD, three:PTR BYTE, four:ABS

EXTERNDEF代替PUBLIC和EXTERN

 

;vars.inc  一个模块

EXTERNDEF count:DWORD,SYM:ABS

 

1 调用 :在sub.asm 调用 用到INCLUDE vars.inc

 

 

TITILE sub1.asm

.386

.model flat,STDCALL

INCLUDE vars.inc

SYM1=10

.data

count DWORD 0

END

  END 后面省略程序入口标号 也无须声明 运行时栈

 

 

在MIAN  必须 END main

2

天天     

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值