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 能正确返回
顺序使用局部变量 第1个 EBP-4 第2个 EBP-8 第3个EBP-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
天天