文档章节

学 Win32 汇编[18]: 关于压栈(PUSH)与出栈(POP) 之二

涂孟超
 涂孟超
发布于 2014/09/26 15:35
字数 991
阅读 18
收藏 0

由于 "栈" 是由高到低使用的, 所以新压入的数据的位置更低.
ESP 中的指针将一直指向这个新位置, 所以 ESP 中的地址数据是动态的.

每次 PUSH, ESP = ESP - x; 每次 POP, ESP = ESP + x;
其中的 x 只能是 4 或 2, 因为 Win32 的 PUSH 只可以压入 32 位(默认)或 16 位的数据.

ESP 有个名字叫 "栈顶", 其实它指向的是栈中最低位置的数据.

实例查看 ESP 的变化:
; Test18_1.asm
.386
.model flat, stdcall

include    windows.inc
include    kernel32.inc
include    masm32.inc
include    debug.inc
includelib kernel32.lib
includelib masm32.lib
includelib debug.lib

.data
    ddVal1 dd 1
    ddVal2 dd 2
    dwVal1 dw 3
    dwVal2 dw 4
.code
main proc
    PrintHex esp  ;0012FFA4
    
    push ddVal1
    PrintHex esp  ;0012FFA0
    push ddVal2
    PrintHex esp  ;0012FF9C
    push dwVal1
    PrintHex esp  ;0012FF9A
    push dwVal2
    PrintHex esp  ;0012FF98
    
    pop dwVal2
    PrintHex esp  ;0012FF9A
    pop dwVal1
    PrintHex esp  ;0012FF9C
    pop ddVal2
    PrintHex esp  ;0012FFA0
    pop ddVal1
    PrintHex esp  ;0012FFA4
    ret
main endp
end main

 
 
 
 
 

 

 

  

使用参数压栈的方式调用函数, 同时揭示 invoke 的本质:
; Test18_2.asm
.386
.model flat, stdcall

include    windows.inc
include    kernel32.inc
;include    masm32.inc
;include    debug.inc
includelib kernel32.lib
;includelib masm32.lib
;includelib debug.lib

include    user32.inc
includelib user32.lib

.data
    szMsg     db 'Hello World!', 0
    szCaption db 'Hi', 0
.code
main proc
    ;invoke MessageBox, NULL, addr szMsg, addr szCaption, MB_OK
    ;用压栈的方式调用 MessageBox 函数; 本来就是如此, invoke 只是简化了这个步骤
    push MB_OK ;C 函数和系统函数读取参数的顺序是: 从右到左; 最左边的参数最后使用, 要先压入
    push offset szCaption
    push offset szMsg
    push NULL  ;一个常数会默认当作 32 位数据压入
    call MessageBox
    pop edx    ;随便出栈到一个地方, 已经没用了, 相当于进回收站
    pop edx    ;尽管没用, 不出是不行的, 因为 push 和 pop 要成对出现
    pop edx
    pop edx
    
    ;invoke ExitProcess, NULL
    ;用压栈的方式调用 ExitProcess 函数
    push NULL
    call ExitProcess
    pop edx
main endp
end main

 
 
 
 
 

 

 

  

从上面的例子看出, 函数调用是需要先压栈(PUSH)参数的;

PUSH 另一重要作用是保护数据, 调用函数前, 最先需要保护的就是 EIP, 这是执行完函数后的下一条指令的地址.
call 指令会先把 EIP 传给 ESP; ret 指令最后把 ESP 恢复给 EIP. 所以, 压栈出栈保护的是 ESP.
但因 ESP 是动态的, 所以一般先 mov ebp, esp, 然后 push ebp ... 像这样:
mov ebp, esp
push ebp
;...函数或子过程
pop ebp
mov esp, ebp
;leave ;可以使用 leave 指令代替上面两行, 它是对上面两行的简化

 
 
 
 
 

 

 

  

从调试器中查看编译器添加的保护 ESP 的代码:
; Test18_3.asm; 这是用于调试的例子
.386
.model flat, stdcall

include    windows.inc
include    kernel32.inc
include    masm32.inc
include    debug.inc
includelib kernel32.lib
includelib masm32.lib
includelib debug.lib

.code
;求和函数
sumProc proc v1:dword, v2:dword, v3:dword
    mov eax, v1
    add eax, v2
    add eax, v3
    ret
sumProc endp
;
main proc
    invoke sumProc, 11, 22, 33
    PrintDec eax ;66
    ret
main endp
end main
;--------------------------

;Ctrl + T 是设置或取消断点
;Ctrl + D 是调试运行
;从调试器中看到 sumProc 函数的代码变成了:

PUSH EBP
MOV EBP,ESP
MOV EAX,DWORD PTR SS:[EBP+8]
ADD EAX,DWORD PTR SS:[EBP+C]
ADD EAX,DWORD PTR SS:[EBP+10]
LEAVE

;看来保护 ESP 的工作是由编译器做的
;从这里也看出了 EBP 寄存器的主要用途就是中转 ESP 中的数据

 
 
 
 
 

 

 

  

利用 ESP 的地址偏移读取栈中的数据:
; Test18_4.asm
.386
.model flat, stdcall

include    windows.inc
include    kernel32.inc
include    masm32.inc
include    debug.inc
includelib kernel32.lib
includelib masm32.lib
includelib debug.lib

.code
main proc
    push 111
    push 222
    push 333
    push 444
    
    mov eax, [esp]
    PrintDec eax     ;444
    mov eax, [esp+4]
    PrintDec eax     ;333
    mov eax, [esp+12]
    PrintDec eax     ;111
    
    pop edx
    pop edx
    pop edx
    pop edx
    ret
main endp
end main

 
 
 
 
 

 

 

  

总结 PUSH 和 POP 的主要用途: 1、暂存与恢复数据; 2、处理函数参数.

压栈、出栈指令汇总:
PUSH(PUSHW、PUSHD)  / POP   ;进出 16 位或 32 位操作数, 默认 32 位

PUSHAD              / POPAD ;进出 EAX、ECX、EDX、EBX、ESP、EBP、ESI、EDI
PUSHA               / POPA  ;进出  AX、 CX、 DX、 BX、 SP、 BP、 SI、 DI

PUSHFD              / POPFD ;进出 EFLAGS
PUSHF               / POPF  ;进出 EFLAGS 的低 16 位

 
 
 
 
 

 

 

  

本文转载自:http://www.cnblogs.com/del/archive/2010/04/11/1709788.html

共有 人打赏支持
涂孟超
粉丝 12
博文 2011
码字总数 14107
作品 0
深圳
程序员
私信 提问
献给汇编初学者-函数调用堆栈变化分析

标 题: 献给汇编初学者-函数调用堆栈变化分析 作 者: 堕落天才 时 间: 2007-01-19,19:20:09 链 接: http://bbs.pediy.com/showthread.php?t=38234 跟一个朋友谈堆栈的时候 就写下了这段文字...

失足处男的倒霉孩子
2014/09/04
765
2
【汇编】C++ 函数调用之——有参无返回调用(传值)

C++函数有参调用有几种传参方式: 一.传值 二.传指针(地址) 三.传引用 其中参数可被const修饰,也可以有默认值。下面分情况讨论: 为了简洁,省略main函数的汇编码而直接给出func函数的汇编...

Micooz
2013/07/26
0
0
逆向分析一个完整的C++程序包含寄存器与参数传递详解

最近在分析C++ dump 文件的时候觉得有必要将一些必要的反汇编东西总结一下以备别人参考,自己有时间的时候也可以进行更多的改进。下面通过一个简单的C++代码转成汇编代码后的详细解释说明一下...

长平狐
2012/06/11
148
0
详解C++代码反汇编后的堆栈寄存器EBP和ESP

详解C++代码反汇编后的堆栈寄存器EBP和ESP 最近在分析一个进程崩溃的严重问题,其中有些过程分析需要对ebp, esp 有清晰的理解,对于ebp 和esp 相信大家都很熟悉了,但是为了使本文自成体系,...

长平狐
2012/06/11
225
0
C/C++ — 函数栈帧的简单见解

函数的栈帧 我们在进行编写程序的时候无论是定义变量,还是调用函数,甚至输出一句话都要用到内存空间,如果说程序是鱼的话,那么 内存空间就是大海,没有内存空间什么都没有办法实现,但是程...

Dawn_sf
2016/12/18
0
0

没有更多内容

加载失败,请刷新页面

加载更多

多表查询

第1章 多表关系实战 1.1 实战1:省和市  方案1:多张表,一对多  方案2:一张表,自关联一对多 1.2 实战2:用户和角色 (比如演员和扮演人物)  多对多关系 1.3 实战3:角色和权限 (比如...

stars永恒
今天
7
0
求推广,德邦快递坑人!!!!

完全没想好怎么来吐槽自己这次苦逼的德邦物流过程了,只好来记一个流水账。 从寄快递开始: 2019年1月15日从 德邦物流 微信小app上下单,截图如下: 可笑的是什么,我预约的是17号上门收件,...

o0无忧亦无怖
昨天
7
0
Mac Vim配置

1.升级 vim   我自己 MacBook Pro 的系统还是 10.11 ,其自带的 vim 版本为 7.3 ,我们将其升至最新版: 使用 homebrew : brew install vim --with-lua --with-override-system-vim 这将下...

Pasenger
昨天
8
0
vmware安装Ubuntu上不了网?上网了安装不了net-tools,无法执行ifconfig?

1.重新设置网络适配器还是不行,如下指定nat 2.还需要指定共享网络,我是在无线环境下 3.无法执行ifconfig https://packages.ubuntu.com/bionic/net-tools到这个网站下载net-tools的deb文件...

noob_chr
昨天
6
0
解决SVN:E210007无法协商认证机制

svn:E210007 svn: Cannot negotiate authentication mechanism 执行下面代码即可 sudo yum install cyrus-sasl cyrus-sasl-plain cyrus-sasl-ldap...

临江仙卜算子
昨天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部