文档章节

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

涂孟超
 涂孟超
发布于 2014/09/26 15:32
字数 1020
阅读 15
收藏 0

记得刚学多线程的时候, 碰到一个结构:
//Delphi 的语法描述
PContext = ^TContext;
_CONTEXT = record
  ContextFlags: DWORD;
  Dr0: DWORD;
  Dr1: DWORD;
  Dr2: DWORD;
  Dr3: DWORD;
  Dr6: DWORD;
  Dr7: DWORD;
  FloatSave: TFloatingSaveArea;
  SegGs: DWORD;
  SegFs: DWORD;
  SegEs: DWORD;
  SegDs: DWORD;
  Edi: DWORD;
  Esi: DWORD;
  Ebx: DWORD;
  Edx: DWORD;
  Ecx: DWORD;
  Eax: DWORD;
  Ebp: DWORD;
  Eip: DWORD;
  SegCs: DWORD;
  EFlags: DWORD;
  Esp: DWORD;
  SegSs: DWORD;
end;

 
 
 
 
 

 

 

  

从这个结构中可以基本洞察多线程的基本原理:
1、在切换到另一个线程之前, 先把当前线程在寄存器中的数据保存在这个结构;
2、重新切回线程时, 再才这个结构中读出相关数据到寄存器, 从而继续运行...

压栈、出栈也是类似的道理.

一个程序包含若干子程序, 子程序中一般会有自己的参数或局部变量.
在执行这个子程序前, 应该先把寄存器中的相关数据暂存一下(子程序也要使用寄存器), 这就是所谓的压栈(PUSH);
等子程序执行完毕, 再把之前压到栈中的数据取回(而让程序继续执行), 这就是所谓的出栈(POP).

什么是 "栈"?

程序把内存划分了若干区域, 其中有 "全局数据区" 和 "局部数据区".

全局数据所在的位置叫 "堆";
局部数据(局部变量、局部常量、子程序参数)所在的位置叫 "栈", 也叫 "堆栈".

对 "堆" 和 "栈", 前人给出了不同的使用规则:
"堆" 中的数据一般是由下到上排列;
"栈" 的数据则完全相反, 是由下到上排列.

验证 "堆" 与 "栈" 不同的数据排列方式:
; Test17_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?
    GlobalVal1 dd ?
    GlobalVal2 dd ?
    GlobalVal3 dd ?
.code

main proc
    LOCAL LocalVal1:dword, LocalVal2:dword, LocalVal3:dword
    
    ;获取全局变量地址(地址是顺序递增的):
    PrintHex offset GlobalVal1  ;00403054
    PrintHex offset GlobalVal2  ;00403058
    PrintHex offset GlobalVal3  ;0040305C
    
    ;获取局部变量地址(地址是顺序递减的):
    lea eax, LocalVal1
    PrintHex eax                ;0012FFBC
    lea eax, LocalVal2
    PrintHex eax                ;0012FFB8
    lea eax, LocalVal3
    PrintHex eax                ;0012FFB4
    ret
main endp
end main

 
 
 
 
 

 

 

  

压栈与出栈的顺序:
.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
    val1 dd 111
    val2 dd 222
    val3 dd 333
.code
main proc
    push val1
    push val2
    push val3
    ;压栈完毕, 接着出栈
    pop val1
    pop val2
    pop val3
    ;查看取回的数据:
    PrintDec val1  ;333
    PrintDec val2  ;222
    PrintDec val3  ;111
    ;怎么反了? 这就是常说的 "栈中的数据是先进后出"! 让后进的先出就好了.
    ret
main endp
end main

 
 
 
 
 

 

 

  

根据 "栈" 先进后出的特点, 写一个变量换值的程序:
; Test17_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

.data
    val1 dd 111
    val2 dd 999
.code

main proc
    push val1
    push val2
    pop val1
    pop val2
    ;现在 val1 和 val2 的值已经交换
    PrintDec val1  ;999
    PrintDec val2  ;111
    ret
main endp
end main

 
 
 
 
 

 

 

  

如果仅是交换变量的值, 可以使用 XCHG 指令:
; Test17_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

.data
    val1 dd 111
    val2 dd 999
.code

main proc
    ;xchg va1, val2 ;指令都不支持对两个变量直接操作, 需要用个寄存器中转下
    mov  eax, val1
    xchg eax, val2
    mov  val1, eax
    PrintDec val1   ;999
    PrintDec val2   ;111
    ret
main endp
end main

 
 
 
 
 

 

 

  

根据上面的原理, 也可以方便写出一个翻转字符串的函数:
; Test17_5.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
    szText db 'Hello World!', 0
.code

main proc
    ;把字符串中的字符逐个压入栈中
    mov ecx, sizeof szText - 1  ;把字符串长度(将要反复的次数)给 ecx, 没包括结束记号
    xor esi, esi                ;清空 esi, 准备用作数组索引
@@: movzx eax, szText[esi]      ;循环读出并压栈
    push eax
    inc esi
    loop @B
    
    ;从栈中逐个取出并写入字符串
    mov ecx, sizeof szText - 1
    xor esi, esi
@@: pop eax
    mov szText[esi], al
    inc esi
    loop @B
    
    PrintString szText  ;!dlroW olleH
    ret
main endp
end main
;做这个程序也有更好的方案, 譬如用 movs

 
 
 
 
 

 

 

  

本文转载自:http://www.cnblogs.com/del/archive/2010/04/10/1708891.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
使用javap分析return和finally的执行字节码

常见指令: 1、putstatic #5; --putstatic当栈顶元素出栈放到常量解析池中的#5位置 2、iconst1 --int常量1放入操作数栈 3、aload0 --将局部变量0位置对象取出来,压入栈push 4、invokespecial...

墙头草
2011/09/16
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

没有更多内容

加载失败,请刷新页面

加载更多

如何有效的背单词

转眼间到了大三快要结束了。英语四级考了三次了,每次都220多分。成绩很稳定,但离四级线还有200多分。学校规定如果过不了四级线,就拿不到学士学位证。没有学位证就找不到好工作,找不到好工...

我是菜鸟我骄傲
35分钟前
1
0
导出表格

https://blog.csdn.net/hhzzcc_/article/details/80419396

Js_Mei
58分钟前
0
0
Ubuntu中安装Elasticsearch

1.安装jre elasticsearch是使用java开发的搜索引擎,因此其运行依赖于java runtime environment,我们在这里不使用Oracel的官方jre,改为使用open-jre。 运行环境: ubuntu:18.04 jre:openj...

cloes
今天
0
0
nginx rails 详细站点配置入门教程

Ruby on Rails 是一个用于开发数据库驱动的网络应用程序的完整框架。Rails基于MVC(模型- 视图- 控制器)设计模式。从视图中的Ajax应用,到控制器中的访问请求和反馈,到封装数据库的模型,R...

xiangyunyan
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部