文档章节

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

涂孟超
 涂孟超
发布于 2014/09/26 15:35
字数 991
阅读 17
收藏 0
点赞 0
评论 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
博文 2004
码字总数 14107
作品 0
深圳
程序员
献给汇编初学者-函数调用堆栈变化分析

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

失足处男的倒霉孩子 ⋅ 2014/09/04 ⋅ 2

【汇编】C++ 函数调用之——有参无返回调用(传值)

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

Micooz ⋅ 2013/07/26 ⋅ 0

【转】通过一段汇编,加深对寄存器ESP和EBP的理解

一直对寄存器ESP和EBP的概念总是有些混淆,查看定义ESP是栈顶指针,EBP是存取堆栈指针。还是不能很透彻理解。之后借于一段汇编代码,总算是对两者有个比较清晰的理解。 下面是按调用约定std...

Orion ⋅ 2011/04/22 ⋅ 0

通过内存布局完成局部变量访问解析

首先看一段代码: void a(){int arr[10];int i;for(i=0;i<10;i++){arr[i]=i; // 初始化数组arr[]}} void b(){int arr2[10];int i;for(i=0;i<10;i++){printf("%d", arr2[i]); //未初始化arr2[......

长平狐 ⋅ 2012/11/01 ⋅ 0

一些基本概念

1,什么是逆向工程,什么是反汇编? 逆向工程:产品开发都是从设计到产品的一个过程;而逆向工程,顾名思义就是从产品到设计的一个分析过程。在计算机软件领域,所谓的逆向工程,指的是逆向人...

enimey ⋅ 2013/12/09 ⋅ 0

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

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

长平狐 ⋅ 2012/06/11 ⋅ 0

为栈实现高效的max操作

最近一个美国回来的学长给我出的一道题目,他经常拿来考核去他们公司应聘的程序员,你能想出最完美的答案么? 题目: 请设计一个栈,除了提供push(压栈),pop(出栈),peak(取栈顶元素)...

乐搏学院 ⋅ 2016/12/02 ⋅ 0

栈是一种先进后出的线性数据结构,规定只允许在一端进行插入和删除元素的操作。其中进栈操作又叫做压栈(Push),出栈操作又叫做弹出(Pop)。允许进行操作的一端叫做栈顶(top),另一端叫做...

SkyHive ⋅ 2017/10/14 ⋅ 0

C/C++ — 函数栈帧的简单见解

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

Dawn_sf ⋅ 2016/12/18 ⋅ 0

详解C++代码反汇编后的堆栈寄存器EBP和ESP

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

长平狐 ⋅ 2012/06/08 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

MySQL 数据库设计总结

规则1:一般情况可以选择MyISAM存储引擎,如果需要事务支持必须使用InnoDB存储引擎。 注意:MyISAM存储引擎 B-tree索引有一个很大的限制:参与一个索引的所有字段的长度之和不能超过1000字节...

OSC_cnhwTY ⋅ 59分钟前 ⋅ 0

多线程(四)

线程池和Exector框架 什么是线程池? 降低资源的消耗 提高响应速度,任务:T1创建线程时间,T2任务执行时间,T3线程销毁时间,线程池没有或者减少T1和T3 提高线程的可管理性。 线程池要做些什...

这很耳东先生 ⋅ 今天 ⋅ 0

使用SpringMVC的@Validated注解验证

1、SpringMVC验证@Validated的使用 第一步:编写国际化消息资源文件 编写国际化消息资源ValidatedMessage.properties文件主要是用来显示错误的消息定制 [java] view plain copy edit.userna...

瑟青豆 ⋅ 今天 ⋅ 0

19.压缩工具gzip bzip2 xz

6月22日任务 6.1 压缩打包介绍 6.2 gzip压缩工具 6.3 bzip2压缩工具 6.4 xz压缩工具 6.1 压缩打包介绍: linux中常见的一些压缩文件 .zip .gz .bz2 .xz .tar .gz .tar .bz2 .tar.xz 建立一些文...

王鑫linux ⋅ 今天 ⋅ 0

6. Shell 函数 和 定向输出

Shell 常用函数 简洁:目前没怎么在Shell 脚本中使用过函数,哈哈,不过,以后可能会用。就像java8的函数式编程,以后获取会用吧,行吧,那咱们简单的看一下具体的使用 Shell函数格式 linux ...

AHUSKY ⋅ 今天 ⋅ 0

单片机软件定时器

之前写了一个软件定时器,发现不够优化,和友好,现在重写了 soft_timer.h #ifndef _SOFT_TIMER_H_#define _SOFT_TIMER_H_#include "sys.h"typedef void (*timer_callback_function)(vo...

猎人嘻嘻哈哈的 ⋅ 今天 ⋅ 0

好的资料搜说引擎

鸠摩搜书 简介:鸠摩搜书是一个电子书搜索引擎。它汇集了多个网盘和电子书平台的资源,真所谓大而全。而且它还支持筛选txt,pdf,mobi,epub、azw3格式文件。还显示来自不同网站的资源。对了,...

乔三爷 ⋅ 今天 ⋅ 0

Debian下安装PostgreSQL的表分区插件pg_pathman

先安装基础的编译环境 apt-get install build-essential libssl1.0-dev libkrb5-dev 将pg的bin目录加入环境变量,主要是要使用 pg_config export PATH=$PATH:/usr/lib/postgresql/10/bin 进......

玛雅牛 ⋅ 今天 ⋅ 0

inno安装

#define MyAppName "HoldChipEngin" #define MyAppVersion "1.0" #define MyAppPublisher "Hold Chip, Inc." #define MyAppURL "http://www.holdchip.com/" #define MyAppExeName "HoldChipE......

backtrackx ⋅ 今天 ⋅ 0

Linux(CentOS)下配置php运行环境及nginx解析php

【part1:搭建php环境】 1.选在自己需要安装的安装包版本,wget命令下载到服务器响应目录 http://php.net/releases/ 2.解压安装包 tar zxf php-x.x.x 3.cd到解压目录执行如下操作 cd ../php-...

硅谷课堂 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部