学 Win32 汇编[17]: 关于压栈(PUSH)与出栈(POP) 之一
16lz
2021-04-30
学 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, stdcallinclude windows.incinclude kernel32.incinclude masm32.incinclude debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib.data? GlobalVal1 dd ? GlobalVal2 dd ? GlobalVal3 dd ?.codemain 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 retmain endpend main
压栈与出栈的顺序:
.386.model flat, stdcallinclude windows.incinclude kernel32.incinclude masm32.incinclude debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib.data val1 dd 111 val2 dd 222 val3 dd 333.codemain proc push val1 push val2 push val3 ;压栈完毕, 接着出栈 pop val1 pop val2 pop val3 ;查看取回的数据: PrintDec val1 ;333 PrintDec val2 ;222 PrintDec val3 ;111 ;怎么反了? 这就是常说的 "栈中的数据是先进后出"! 让后进的先出就好了. retmain endpend main
根据 "栈" 先进后出的特点, 写一个变量换值的程序:
; Test17_3.asm.386.model flat, stdcallinclude windows.incinclude kernel32.incinclude masm32.incinclude debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib.data val1 dd 111 val2 dd 999.codemain proc push val1 push val2 pop val1 pop val2 ;现在 val1 和 val2 的值已经交换 PrintDec val1 ;999 PrintDec val2 ;111 retmain endpend main
如果仅是交换变量的值, 可以使用 XCHG 指令:
; Test17_4.asm.386.model flat, stdcallinclude windows.incinclude kernel32.incinclude masm32.incinclude debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib.data val1 dd 111 val2 dd 999.codemain proc ;xchg va1, val2 ;指令都不支持对两个变量直接操作, 需要用个寄存器中转下 mov eax, val1 xchg eax, val2 mov val1, eax PrintDec val1 ;999 PrintDec val2 ;111 retmain endpend main
根据上面的原理, 也可以方便写出一个翻转字符串的函数:
; Test17_5.asm.386.model flat, stdcallinclude windows.incinclude kernel32.incinclude masm32.incinclude debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib.data szText db 'Hello World!', 0.codemain 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 retmain endpend main;做这个程序也有更好的方案, 譬如用 movs
posted on 2010-04-10 18:16 万一 阅读(3295) 评论(0) 编辑 收藏©著作权归作者所有:来自51CTO博客作者JLee79的原创作品,如需转载,请注明出处,否则将追究法律责任
更多相关文章
- 学 Win32 汇编[32] - 子程序进阶
- 宝塔强行卸载mysql数据库
- jQuery中的$.ajax方法,以及基本的vue指令操作
- IHS Markit:85%的运营商计划部署智能端局(CO)
- 全息政府行业数据安全解决方案
- Jaeger-实践处理相关计划
- 【PHP连接MySQL】我来手把手教你PHP怎么连接Mysql数据库
- 浅谈 Integer 类
- Pinpoint-技术专区-全流程学习