`
zzc1684
  • 浏览: 1192775 次
  • 性别: Icon_minigender_1
  • 来自: 广州
文章分类
社区版块
存档分类
最新评论

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

阅读更多

记得刚学多线程的时候, 碰到一个结构:


//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
分享到:
评论

相关推荐

    微型计算机原理和汇编语言 8086/8088 汇编语言指令表

    附录 B 8086/8088 汇编语言...POP POP dst 出栈一个字数据,送到操作数dst确定的位置- - - - - - - - - XCHG XCHG 把dst、src两个操作数中的内容互换- - - - - - - - - XLAT XLAT 以BX+AL的和作为偏移地址,从数据段

    单片机课程设计汇编语言程序.doc

    出栈 LJMP MAIN FX1:JNB P1.1 ,FX2 MOV A,#4 ADD A,R2 PUSH Acc LCALL XS POP Acc LJMP MAIN FX2: JNB P1.2 , FX3 MOV A,#8 ADD A,R2 PUSH Acc LCALL XS POP Acc LJMP MAIN FX3: MOV A,#12 ADD A,R2 PUSH Acc LCALL ...

    万年历源码 汇编版(带源码)

    一个很好的汇编学习例程。 ;;;;;;;;;;;;;;;;;;;;;;;;;;将数值转换为字符型数据,进入函数之前ax保存年份,结果转换完了将字符放到year表示的数据段中;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; itoa: mov si,10 ;将ax传进来的...

    c语言 Day1 入门知识,程序

    语言发展史: 机器语言→汇编语言→高级语言 ...push压栈 pop 出栈 call 调用 eax 32位寄存器 简单加法计算 { __asm mov a, 10 mov b, 20 mov eax,a add eax,b mov c, eax } 断点和单步调试:

    汇编常用命令

     PUSH 入栈指令及POP出栈指令: 堆栈操作是以"后进先出"的方式进行数据操作.  PUSH SRC //Word  入栈的操作数除不允许用立即数外,可以为通用寄存器,段寄存器(全部)和存储器.  入栈时高位字节先入栈,低位字节后...

    单片机计时器(汇编)

    置光标位置 ,AH=2,BH=0,DH跟DL分别为行号与列号,并入栈保护BX mov ah,2 mov bh,0 mov dh,3 mov dl,8 int 10h pop bx lea dx,t_buff ;送t_buff偏移地址到DX,并调用DOS显示功能,功能号为9 mov ah,9 int 21h ...

    exe文件脱壳步骤txt下载

    pop xx:xx 出栈 栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。 我们搜索 Possible Reference to String Resource ID=00122 因为对E...

    汇编指令

     PUSH 入栈指令及POP出栈指令: 堆栈操作是以"后进先出"的方式进行数据操作.  PUSH SRC //Word  入栈的操作数除不允许用立即数外,可以为通用寄存器,段寄存器(全部)和存储器.  入栈时高位字节先入栈,低位...

    汇编速查手册

    POPD 32位标志出栈. 二、算术运算指令 ─────────────────────────────────────── ADD 加法. ADC 带进位加法. INC 加 1. AAA 加法的ASCII码调整. DAA 加法的十进制调整. SUB...

    8086 汇编指令 微型计算机原理

    MOV(Move)传送 PUSH(Push onto the stack)进栈...POP(Pop from the stack)出栈 XCHG(Exchange)交换 .MOV指令 格式为: MOV DST,SRC 执行的操作:(DST)(SRC) .PUSH进栈指令 格式为:PUSH SRC 执行的操作:(SP)(SP)-2

    汇编指令(chm格式)

    8088 汇编速查手册 一、数据传输指令 ─────────────────────────────────────── 它们在存贮器和寄存器、寄存器和输入输出端口之间传送数据. 1. 通用数据传送指令. ...

    字母排序与输入输出显示

    loop1:push cx;保存外循环次数 mov bx,0 loop2: mov al,data+2[bx];备份 cmp al,data+2[bx+1];相邻两个数比较 jl next xchg al,data+2[bx+1];相邻两个数交换 mov data+2[bx],al next:inc bx loop loop2 pop ...

    od脱壳软件破解工具

    但愿大家互相进修进修,大家对于破解都不是很了解,人们想学破解,可是去无从入手,所以决议为大家写1个破解初级读物的教程,但愿能大家了解破解有一些帮忙,但愿能有更多的人踏入破解的大门  1.低级,修改步伐,用...

    单片机期末考试题目及答案详解

    调用返回时再进行出栈操作,把保护的断点送回 。 3.某程序初始化时使(SP)=40H,则此堆栈地址范围为 ,若使(SP)=50H,则此堆栈深度为 。 4.在相对寻址方式中,“相对”两字是指相对于 ,寻址得到的结果是 。...

Global site tag (gtag.js) - Google Analytics