在WINDOWS系统能用到汇编的机会不多,基本都可以用C或C++代劳,更何况现在MICROSOFT的Visual Studio 系列工具非常强大,WINDOWS下的开发已经在向.NET方面发展,实际调用WINDOWS SDK的机会也不多。 WIN32汇编编写窗口程序需要调用大量的WINDOWS 的API,而且提供了高级语言才有的条件语句和循环语句,难度相对于LINUX下的AT&T汇编要小很多。
本博客所有内容是原创,如果转载请注明来源
http://blog.csdn.net/myhaspl/
汇编可以开发WINDOWS程序?答案是肯定的,用WIN32汇编语言开发出来的WINDOWS程序具有执行效率高、占用空间小等特点。
一、开发工具下载与安装
在众多WIN32汇编开发工具中,MASM32笔者比较偏爱,它具有很多代码示例和丰富的开发资源,在下列下载地址中选择一个地址下载MASM32。
http://website.assemblercode.com/masm32/m32v9r.zip
http://masm32.masmcode.com/masm32/m32v9r.zip
http://webhost.ccp.com/~win32/hutch/m32v9r.zip
二、知识预备
1、寄存器
为了提高运算速度和数据的存取速度,在计算机的CPU内,有一组硬件装置,第一个装置内存放CPU运算需要的数,这些数值可供CPU直接存取,这组装置叫寄存器。寄存器分为通用寄存器、段寄存器、程序指针寄存器、标志寄存器。
编程最常用就是通用寄存器,常用的通用寄存器有eax,ebx,ecx,edx,esi,edi等等,现代计算机的CPU一般是以32位为单位进行运算,因此一个寄存器最大能存放32位的二进制数。每个寄存器通常都有它默认的用法,所谓默认只是一种编程的习惯,你可以不遵守这些用法,但是有些情况下必须遵守,因为我们开发的是WINDOWS程序,要遵守WINDOWS操作系统的规定和汇编语法本身的约束,比如调用一个WINDOWSAPI函数,返回值放在EAX,堆栈的栈顶地址在esp,在汇编语言的循环中,ECX内存放循环的次数。具体寄存器的使用会在以后介绍。
2、堆栈
堆栈是个非常古老的概念,在DOS时代就有了,也是个非常重要的东西,程序没了它就活不了,堆栈就是在内存里分配一个区域,使用这个区域必须遵守一个规定:后进先出,后进来的先出去,可以把它想像成一个空木箱,首先往里面放棉衣,然后往里面放书,最后放上运动服。如果要取出书,必须得把最后放上的运动服取出,放的顺序是棉衣->书->运动服,取的顺序是运动服->书->棉衣,堆栈也是如此。
先解释一下什么是地址,内存价格的便宜和内存容量的扩大,WINDOWS虚拟内存早已出现,内存中放着众多的数据,必须要有方法表示内存某个地点,这个地点就用地址来表示。把内存以字节为单位划分,某个地址表示某个字节的地址,如左下图是一个内有4个成员的堆栈,堆栈里的成员以一个字节为单位(数的右边标明了以十六进制表示的地址,如1001)
+--+
|AF| 地址:AF21
+--+
|03| 地址:AF20
+--+
|30| 地址:AF19
+--+
|F1| 地址:AF18
+--+
堆栈是向下增长的,每增加一个成员,栈顶(堆栈顶部的地址)的地址减1,对于这个堆栈,栈顶地址是AF18,如果再往这个堆栈里增加一个成员的话,它的地址是:AF18-1=AF17。如果往这个堆栈里增加一个32位的数,栈顶的地址是多少呢,32位的数占4个成员的位置,栈顶的地址为:AF18-4=AF14。
在这个堆栈中增加一个32位数DAB0CD90,新的堆栈如下:
+--+
|AF| 地址:AF21
+--+
|03| 地址:AF20
+--+
|30| 地址:AF19
+--+
|F1| 地址:AF18
+--+
|DA| 地址:AF17
+--+
|B0| 地址:AF16
+--+
|CD| 地址:AF15
+--+
|90| 地址:AF14
+--+
如果从堆栈里拿走一个32位数,则栈顶的地址为:AF14+4=AF18
三、WIN32汇编语言的语法
为了方便大家理解和入门,下面尽量使用宏汇编和伪指令地方进行描述,也正因为有了宏汇编和伪指令的帮助,WIN32汇编才具有很多高级语言的特性,很多语法和C差不多。
1、WIN32汇编程序基本结构
.386
.MODEL Flat,STDCALL
.DATA
初始化值的全局变量定义
.DATA?
未初始化值的全局变量定义
.CONST
常量定义
.CODE
..............
...............
程序入口LABEL
............
............
end程序入口LABEL
2、变量定义
(1)定义全局变量
全局变量定义在.data和.data?内,
初始化变量的定义方式如下:
.data
变量名 类型 初始值1,初始值2,.......
变量名 类型 重复次数dup(初始值1,初始值2,....)
注意:如果用?表示初始值的话,则表示0
未初始化变量的定义方式是
.data?
变量名 类型 ?
(2)条件测试语句
(A)基本结构
.IF条件
程序代码
[.ELSEIF]条件比较
程序代码
.......
[.ELSE]
程序代码
.ENDIF
(3)操作符
(A)比较操作符
== 相等
!= 不等于
> 大于
>= 大于或等于
< 小于
<= 小于或等于
& 位测试
! 逻辑非
&& 逻辑与
|| 逻辑或
(B)位操作符
AND按位与
OR 按位或
XOR 异或
SHL逻辑左移
SHR逻辑右移
(C)标志寄存器操作符
CARRY?是否进位
OVERFLOW? 是否溢出
PARITY? 奇偶位是否置位
SIGN? 符号位标志位是否被置位
ZERO? 零位标志位是否置位
(4)循环语句
(A)while语句
while 条件
..........
..........
[.break[.if 退出条件]]
[.contine[.if 退出条件]]]
.end
(B)repeat语句
.repeat
...........
..........
[.break[.if 退出条件]]
[.contine[.if 退出条件]]]
.until 条件(或.untilcxz [条件])
(5)子程序定义
1、定义
子程序名 proc [距离][语言类型][可视区域][USERS 寄存器列表][,参数:类型]...[VARARG]local 局部变量列表
..............
...............
...............
子程序名 endp
2、如果在未定义前使用,要声明、
函数名 proto [距离][语言][参数1]:数据类型,[参数2]:数据类型,...............
(6)数据结构
(A)声明
wndclass struct
....
.....
......
wndclass ends
(B)定义
mystruct wndclass<1,1,...,1>
mystruct wndclass <>
(C)使用
mov eax,mystruct.lpfnwndproc
mov esi,offset mystruct
assume esi: ptr WNDCLASS
mov eax,[esi].lpfnwndproc
.......
assume esi:nothing
四、在WIN32汇编中的使用WINDOWS API
WIN32汇编如果没有API的帮助无法实现很多功能,笔者没见过在WIN32汇编程序不调用API的。
调用API实际上是靠堆栈来完成参数传递的,既然是堆栈,那就要遵守后进先出的原则,这意味API的第一个参数是最后一个放入堆栈的,最后一个参数是第一个放入堆栈的。
调用方式如下:
push 参数n
..........
push 参数2
push 参数1
call API函数名
为了简化代码,也可以使用以下这种方式调用API
invoke API函数名,参数1,参数2,.....,参数n
我们用WIN32汇编构建第一个WINODWS程序,这个程序完成显示一个带问号的对话框,对话框的内容是现在系统时间。
首先,打开MASM32Editor(在桌面上可以找到图标),在里面输入以下代码:
.386
.model flat, stdcall
option casemap :none
;#################################################################
include windows.inc
include user32.inc
include kernel32.inc
include gdi32.inc
include masm32.inc
includelib user32.lib
includelib kernel32.lib
includelib gdi32.lib
includelib masm32.lib
;#################################################################
.data?
szbuffer db 100 dup(?)
.data
szcaptionName db "我的HELLO,WORLD!",0
szbegin db "现在时间:"
sztext db 100 dup(?)
;################################################################# .code
start:
;程序的入口
call _callgetnow
invoke MessageBox,NULL,offset szbegin,offsetszcaptionName,MB_ICONQUESTION or MB_OK
invoke ExitProcess,eax
;#################################################################
_callgetnow proc
pushad
invokeGetDateFormat,LOCALE_USER_DEFAULT,DATE_LONGDATE,NULL,NULL,addrsztext,100
invokeGetTimeFormat,LOCALE_USER_DEFAULT,LOCALE_NOUSEROVERRIDE,NULL,NULL,addrszbuffer,100
invokeszCatStr,addr sztext,addr szbuffer
popad
ret
_callgetnow endp
;#################################################################
end start
将上述代码保存为HELLOWORLD.ASM后,对程序进行编译。
在编译前参照下图设置好系统的环境变量,path变量加上x:\masm32\bin,lib变量加上x:\masm32\lib,include变量加上x:\masm32\include。
确环境变量设置好后,进入DOS窗口开始编译。
首先运行ml,编译成coff文件格式
然后运行LINK,进行链接,生成EXE文件,
大功造成,运行一下试试效果吧!
下面我们接着来做一个有些难度的helloworld,这个程序将系统时间直接显示在桌面上。程序源代码如下:
.386
.model flat, stdcall
option casemap :none
;#########################################################################
include windows.inc
include user32.inc
include kernel32.inc
include gdi32.inc
include masm32.inc
includelib user32.lib
includelib kernel32.lib
includelib gdi32.lib
includelib masm32.lib
;#########################################################################
.data?
szbuffer db 100 dup(?)
.data
szmssucesscap db "HELLO,WORLD!深入",0
szmssucesstext db "在桌面的(300,300)处显示了当前时间",0
szmscap db "错误",0
szmstext1 db "无法在桌面上显示!",0
szmstext2 db "无法得到全屏DC!",0
szbegin db "现在时间:"
sztext db 100 dup(?)
;#########################################################################
.code
start:
;程序的入口
_showtext proto :DWORD
call _callgetnow
invoke _showtext,offset szbegin
invoke ExitProcess,eax
;#########################################################################
_callgetnow proc
pushad
invokeGetDateFormat,LOCALE_USER_DEFAULT,DATE_LONGDATE,NULL,NULL,addrsztext,100
invokeGetTimeFormat,LOCALE_USER_DEFAULT,LOCALE_NOUSEROVERRIDE,NULL,NULL,addrszbuffer,100
invokeszCatStr,addr sztext,addr szbuffer
popad
ret
_callgetnow endp
;#########################################################################
_showtext proc lpsztext:DWORD
LOCAL@Desktopdc:HDC
LOCAL@dwcolor:DWORD
pushad
mov@dwcolor,00FF0000h
invokeGetWindowDC,NULL
cmpeax,0
jne @f
invokeMessageBox,NULL,offset szmstext2,offset szmscap,MB_ICONERROR
@@:
mov@Desktopdc,eax
invokelstrlen,lpsztext
movebx,eax
invokeSetBkMode,@Desktopdc,TRANSPARENT
invokeSetTextColor,@Desktopdc,@dwcolor
invokeTextOut,@Desktopdc,300,300,lpsztext,ebx
invokeMessageBox,NULL,offset szmssucesstext,offsetszmssucesscap,MB_ICONINFORMATION
cmpeax,0
jne @f
invokeMessageBox,NULL,offset szmstext1,offset szmscap,MB_ICONERROR
@@:
popad
ret
_showtext endp
;#########################################################################
end start
以上程序中有几个重要的GDI相关的API,下面简要介绍一下
(1)
invoke SetBkMode,@Desktopdc,TRANSPARENT
设置背景方式为透明
(2)
invoke SetTextColor,@Desktopdc,@dwcolor
设置字体颜色为蓝色
(3)
invoke TextOut,@Desktopdc,300,300,lpsztext,ebx
在300,300处显示文本
(4)
invoke GetWindowDC,NULL
取得桌面DC
(5)
mov @dwcolor,00FF0000h
设置颜色为蓝色。@dwcolor是一个DWORD型的变量,
可以在相关头文件中找到这样的定义:
typedef DWORD COLORREF;
因此COLORREF类型的变量就是DWORD型变量。
COLORREF变量如何表示颜色呢,只有一个双字大小,它的表示格式是(16进制):
0x00bbggrr
bb表示蓝色,gg表示绿色,rr表示红色
00FF0000h:蓝
0000ff00h:绿
000000FFh:红
程序运行效果如下:
因此,在此只简单介绍一下,首先来看一段简单的窗口程序。注意";"表示注释
- ;加上注释和个人理解
-
-
- .386
- .model flat,stdcall
- option casemap:none
-
- ;以下定义INCLUDE文件
- include winows.inc
- include gdi32.inc
- includelib gdi32.lib
- include user32.inc
- includelib user32.lib
- include kernel32.inc
- include kernel32.lib
- ;以下定义数据段
- .data ? ;定义变量
- hinstance dd ?
- hwinmain dd ?
- .const ;定义常量,字符串全部要以0结尾,因为在内存中0是字符串的结束符
- szclassname db 'billclass',0
- szcaptionmain db 'bill's firt program',0
- sztext db 'WIN32汇编,BILL!!!!',0
- ;以下是代码段
- .code
- ;定义窗口过程
- _procwinmain proc uses ebx edi esi,hwnd,umsg,wparam,lparam
- ;定义局部变量用关键字local
- local @stps:PAINTSTRUCT
- local @strect:PAINTSTRUCT
- local @hdc
- mov eax,umsg ;取得传入过程的消息变量值
- ;-----------下面开始根据消息类型的不同作出不同的处理
- .if eax == WM_PAINT ;如果消息是窗口绘制
- invoke BeginPaint,hwnd,addr@stRect;WIN32汇编调用API程序后,API程序将返回值放在EAX中,
- ;客户区准备
- mov @hdc,eax;取得设备句柄
-
- invoke GetclientRect,hwnd,addr @stRect;addr是取变量的地址但只能用在INVOKE语句中且
- ;不能同时使用
- ;EAX寄存器传参数,因为ADDR会用到EAX。
- ;此API的含义是取得描述客户区的结构放在@stRect
- invoke drawText,@hdc,addr sztext,-1,addr @stRect,\
- DT_SINGLELINE or DT_CENTER or DT_VCENTER ;语句换行符是\,显示'WIN32汇编,BIL
- ;L!!!!',并设置其为单行DT_SINGLE
- ;等等LINE
- invoke EndPaint,hwnd,addr @stPs
- .elseifmeax==WM_CLOSE
- invoke DestroyWindow,hwinmain ;销毁窗口
- invoke PostQuitMessage,Null ;向消息循环中发出退出消息
- .else
- invoke DefWindowProc,hwnd,uMsg,wPara,lParam;如果不是上述消息,则执行WINDOWS标准的默认消息处
- ;理,如键盘等消息
- ret;返回
- .endif
-
- xor eax,eax ;eax清0
- ret
- _ProcWinMain endp
- ;以上这个子程序处理窗口消息的,是窗口的回调函数,该项函数不是我们调用,是由WINDOWS调用用来处理
- ;窗口消息的,我们调用的是DispatchMessage,DispatchMessage再回过头来调用窗口过程。
- _WinMain Proc ;主程序
- local @stWndClass:WNDCLASSEX
- local @stmsg:MSG
-
- invoke GetModuleHandle,Null ;得到应用程序句柄
- mov hInstance,eax ;将应用程序的句柄放入hInstance变量
- invoke RtlZeroMemory,addr @stWndClass,sizeof WndClassEX ;msdn的解释TheRtlZeroMemory routine
- ;fills a block of memory with zeros,即
- ;0填充stWndClass结构变量所占的内存,也就是初始化
- ;-----下面注册窗口类
- invoke loadcursor,0,IDC_ARROW ;加载箭头形指针句柄
- mov @stWndClass.hCursor,eax ;鼠标指针赋值
- push hInstance
- pop @stWndClass.hInstance ;窗口句柄赋值
- mov @stWndClass.cbsize,sizeof WNDCLASSEX ;结构大小
- mov @stWndClass.style,CS_HREDRAW or CS_VREDRAW ;设置窗口样式
- mov @stWndClass.lpfnwndproc,offset _procwinmain;设置回调函数,也就是窗口消息处理过程
- mov @stwndclass.hbrbackground,COLOR_WINDOW+1
- mov @stwndclass.lpszclassname,offset szclassname ;设置窗口类的名称
- invoke RegisterClassEx,addr @stwndclass ;传上述设置好的结构以注册窗口类
- ;建立显示窗口
- invoke CreateWindowEx,WS_EX_CLIENTEDGE,\
- offset szclassname,offsetszcaptionmain,\
- WS_OVERLAPPEDWINDOW,100,100,600,400,NULL,\
- NULL,hinstance,NULL ;建立窗口并返回句柄在EAX中
- mov hwinMain,eax ;刚创建的窗口句柄赋值
- invoke showwindow,hwinmain,SW_SHOWNORMAL ;显示窗口
- invoke updatewindow,hwinmain ;刷新窗口客户区,导致客户区窗口paint
- ;消息循环,win32汇编得自行建立WINDOWS消息循环,不过这样更自由,可以彻底地控制程序
- .while true
- invoke GetMessage,addr @stMsg,null,0,0 ;WINDOWS在系统内部有个系统消息队列,
- ;并为每个应用程序还维护了一个消息队列,将这些属于这些程序窗口范围内的
- ;系统消息发到该应用程序消息队伍中,这个API的作用就是从自己的应用程序
- ;消息队伍中接收消息。
- .break .if eax==0 ;If the function retrieves the WM_QUIT message, the return value is zero.
- ;invoke Translate(msdn),也就是说,当程序退出里,消息队伍里会有WM_QUIT消息, ;就退出循环,意味着退出程序。
- invoke translatemessage,addr @stmsg;由应用程序对消息进行预处理,如把基于键盘扫描码的按键消息黑心 ;换成ASCII码的键盘消息等
- invoke dispatchmessage,addr@stmsg ;将预处理好的消息发给WINDOWS,WINDOWS将其分派给该程序的相应窗;口处理过程处理,那么WINDOWS怎么知道窗口处理过程在哪呢,刚才不是已经注册过窗口类了,这就是为什么窗口;类要注册的原因了,那么为什么不能由程序自己处理消息,非得发给WINDOWS呢,其一、一个应有程序的窗口很多,如果自己处理的话,得建立一个窗口列表,上面记录每个窗口的窗口处理过程。其二、WINDOWS对于一些实时性很;强的信息采用直接调用窗口处理过程的方法。
- .endw
- ret
- _winmain endp
- ;没有下面的代码程序无法执行,因为START语句指定程序启动的入口点
- start:
- call _winmain
- invoke ExitProcess,NULL;退出
- end start
可以看到上面代码和用C编写的WIN SDK程序很相似。我们接着继续看2个例子:
例1:用WIN32汇编构建第一个WINODWS程序,这个程序完成显示一个带问号的对话框,对话框的内容是现在系统时间。
首先,打开MASM32Editor(在桌面上可以找到图标),在里面输入以下代码:
- .386
- .model flat, stdcall
- option casemap :none
- ;#################################################################
- include windows.inc
- include user32.inc
- include kernel32.inc
- include gdi32.inc
- include masm32.inc
-
- includelib user32.lib
- includelib kernel32.lib
- includelib gdi32.lib
- includelib masm32.lib
- ;#################################################################
- .data?
- szbuffer db 100 dup(?)
- .data
- szcaptionName db "我的HELLO,WORLD!",0
- szbegin db "现在时间:"
- sztext db 100 dup(?)
- ;################################################################# .code
- start:
- ;程序的入口
- call _callgetnow
- invoke MessageBox,NULL,offset szbegin,offsetszcaptionName,MB_ICONQUESTION or MB_OK
- invoke ExitProcess,eax
- ;#################################################################
- _callgetnow proc
- pushad
- invokeGetDateFormat,LOCALE_USER_DEFAULT,DATE_LONGDATE,NULL,NULL,addrsztext,100
- invokeGetTimeFormat,LOCALE_USER_DEFAULT,LOCALE_NOUSEROVERRIDE,NULL,NULL,addrszbuffer,100
-
- invokeszCatStr,addr sztext,addr szbuffer
- popad
- ret
- _callgetnow endp
- ;#################################################################
- end start
将上述代码保存为HELLOWORLD.ASM后,对程序进行编译。
在编译前参照下图设置好系统的环境变量,path变量加上x:\masm32\bin,lib变量加上x:\masm32\lib,include变量加上x:\masm32\include。
确环境变量设置好后,进入DOS窗口开始编译。
首先运行ml,编译成coff文件格式
然后运行LINK,进行链接,生成EXE文件,
大功造成,运行一下试试效果吧!
例2:系统时间直接显示在桌面上。程序源代码如下:
- .386
- .model flat, stdcall
- option casemap :none
- ;#########################################################################
- include windows.inc
- include user32.inc
- include kernel32.inc
- include gdi32.inc
- include masm32.inc
-
- includelib user32.lib
- includelib kernel32.lib
- includelib gdi32.lib
- includelib masm32.lib
- ;#########################################################################
- .data?
- szbuffer db 100 dup(?)
- .data
- szmssucesscap db "HELLO,WORLD!深入",0
- szmssucesstext db "在桌面的(300,300)处显示了当前时间",0
- szmscap db "错误",0
- szmstext1 db "无法在桌面上显示!",0
- szmstext2 db "无法得到全屏DC!",0
- szbegin db "现在时间:"
- sztext db 100 dup(?)
- ;#########################################################################
- .code
- start:
- ;程序的入口
- _showtext proto :DWORD
- call _callgetnow
- invoke _showtext,offset szbegin
- invoke ExitProcess,eax
- ;#########################################################################
- _callgetnow proc
- pushad
- invokeGetDateFormat,LOCALE_USER_DEFAULT,DATE_LONGDATE,NULL,NULL,addrsztext,100
- invokeGetTimeFormat,LOCALE_USER_DEFAULT,LOCALE_NOUSEROVERRIDE,NULL,NULL,addrszbuffer,100
-
- invokeszCatStr,addr sztext,addr szbuffer
- popad
- ret
- _callgetnow endp
- ;#########################################################################
- _showtext proc lpsztext:DWORD
- LOCAL@Desktopdc:HDC
- LOCAL@dwcolor:DWORD
-
- pushad
- mov@dwcolor,00FF0000h
- invokeGetWindowDC,NULL
- cmpeax,0
- jne @f
- invokeMessageBox,NULL,offset szmstext2,offset szmscap,MB_ICONERROR
- @@:
- mov@Desktopdc,eax
- invokelstrlen,lpsztext
- movebx,eax
- invokeSetBkMode,@Desktopdc,TRANSPARENT
- invokeSetTextColor,@Desktopdc,@dwcolor
- invokeTextOut,@Desktopdc,300,300,lpsztext,ebx
- invokeMessageBox,NULL,offset szmssucesstext,offsetszmssucesscap,MB_ICONINFORMATION
- cmpeax,0
- jne @f
- invokeMessageBox,NULL,offset szmstext1,offset szmscap,MB_ICONERROR
- @@:
- popad
- ret
- _showtext endp
- ;#########################################################################
- end start
以上程序中有几个重要的GDI相关的API,下面简要介绍一下
(1)
invoke SetBkMode,@Desktopdc,TRANSPARENT
设置背景方式为透明
(2)
invoke SetTextColor,@Desktopdc,@dwcolor
设置字体颜色为蓝色
(3)
invoke TextOut,@Desktopdc,300,300,lpsztext,ebx
在300,300处显示文本
(4)
invoke GetWindowDC,NULL
取得桌面DC
(5)
mov @dwcolor,00FF0000h
设置颜色为蓝色。@dwcolor是一个DWORD型的变量,
可以在相关头文件中找到这样的定义:
typedef DWORD COLORREF;
因此COLORREF类型的变量就是DWORD型变量。
COLORREF变量如何表示颜色呢,只有一个双字大小,它的表示格式是(16进制):
0x00bbggrr
bb表示蓝色,gg表示绿色,rr表示红色
00FF0000h:蓝
0000ff00h:绿
000000FFh:红
.NET堆栈原理
1、用调试器调试线程
本博客所有内容是原创,如果转载请注明来源
http://blog.csdn.net/myhaspl/
1)栈调用
以下面代码为例
- Imports System.Threading
-
- Public Class Form1
-
- Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
- Dim main_x As Integer
- main_x = 5
- Call sub1(main_x)
- End Sub
- Private Sub sub1(sub1_x As Integer)
- Dim jg As Integer
- jg = sub1_x * sub1_x
- Call sub2(jg)
- End Sub
- Private Sub sub2(sub2_x As Integer)
- Dim jg As Integer
- jg = sub2_x * 2
- '在下一句设置断点
- jg = jg * jg
-
- End Sub
-
- End Class
我们首先来看调用堆栈,在调试菜单中选择调用堆栈,可看到过程的调用顺序:
然后查看线程
最后查看局部变量
此外,我们还可以研究一下这个线程的调用时堆栈情况,通过反汇编代码,在调用堆栈窗口中选择“转到反汇编”
- Private Sub sub2(sub2_x As Integer)
- 00000000 push ebp
- 00000001 mov ebp,esp
- 00000003 sub esp,14h
- 00000006 mov dword ptr [ebp-10h],ecx
- 00000009 mov dword ptr [ebp-4],edx
- 0000000c cmp dword ptr ds:[0256B1B8h],0
- 00000013 je 0000001A
- 00000015 call 62A16743
- 0000001a xor edx,edx
- 0000001c mov dword ptr [ebp-8],edx
- 0000001f mov eax,dword ptr [ebp-10h]
- 00000022 mov dword ptr [ebp-14h],eax
- 00000025 mov ecx,dword ptr [ebp-10h]
- 00000028 call 628F5C25
- 0000002d mov dword ptr [ebp-0Ch],eax
- 00000030 push 32h
- 00000032 mov edx,dword ptr [ebp-0Ch]
- 00000035 mov ecx,dword ptr [ebp-14h]
- 00000038 call FFDF30D0
- 0000003d mov ecx,dword ptr [ebp-4]
- 00000040 call FFFFFF70
- 00000045 mov ecx,63h
- 0000004a call FFDF2940
- 0000004f nop
- Dim jg As Integer
- jg = sub2_x * 2
- 00000050 mov eax,dword ptr [ebp-4]
- 00000053 mov edx,2
- 00000058 imul eax,eax,2
- 0000005b jno 00000062
- 0000005d call 62A19A30
- 00000062 mov dword ptr [ebp-8],eax
- '在下一句设置断点
- jg = jg * jg
- 00000065 mov eax,dword ptr [ebp-8]
- 00000068 imul eax,dword ptr [ebp-8]
- 0000006c jno 00000073
- 0000006e call 62A19A30
- 00000073 mov dword ptr [ebp-8],eax
-
- End Sub
- 00000076 nop
- 00000077 nop
- 00000078 mov ecx,63h
- 0000007d call FFDF2A60
- 00000082 nop
- 00000083 mov esp,ebp
- 00000085 pop ebp
- 00000086 ret
ESP是栈顶指针
ebp是基址指针
+-----+
+基址 +
+-----+
+栈内容+
+栈内容+
+栈内容+
+栈内容+
+栈内容+
+栈顶 +
如上图所示,基地的地址比栈顶的地址大,就是向下增长。
寄存器ebp和esp保存着当前的基址和栈顶地址
首先,进入函数时
00000000 push ebp
备份基址指针
00000001 mov ebp,esp
然后设置基址指针指向栈顶,相当于为当前栈清空了内容,做好在栈中分配局部变量的准备
00000003 sub esp,14h
完成栈(可以理解为本函数可访问的栈)的空间分配,将栈顶指针向下增长14h(向下增长的意思是栈的空间增长规律是地址递减)。相当于栈中已经容纳了14h的空间
Dim jg As Integer
jg = sub2_x * 2
00000050 mov eax,dword ptr [ebp-4]
00000053 mov edx,2
00000058 imul eax,eax,2
0000005b jno 00000062
0000005d call 617391D0
00000062 mov dword ptr [ebp-8],eax
'在下一句设置断点
jg = jg * jg
00000065 mov eax,dword ptr [ebp-8]
00000068 imul eax,dword ptr [ebp-8]
0000006c jno 00000073
0000006e call 617391D0
00000073 mov dword ptr [ebp-8],eax
从上面这段代码可以看出来
sub2_x分配在了dword ptr [ebp-4] ,而jg分配在了dword ptr [ebp-8]
最后,离开函数时,恢复进入函数前栈的指针,相当于释放了本次在栈中分配的空间
End Sub
00000083 mov esp,ebp
恢复栈顶指针
00000085 pop ebp
恢复基址指针
00000086 ret
2、修改默认栈的大小
Dim 线程变量名 As Thread = New Thread(函数名,以字节为单位的栈大小)
比如
Dim mythread As Thread = New Thread(myfun,1024*512)
分配了512kb字节