为了账号安全,请及时绑定邮箱和手机立即绑定

C指针原理(23)-win32汇编及.NET调试

标签:
C++


在WINDOWS系统能用到汇编的机会不多,基本都可以用C或C++代劳,更何况现在MICROSOFT的Visual Studio 系列工具非常强大,WINDOWS下的开发已经在向.NET方面发展,实际调用WINDOWS SDK的机会也不多。 WIN32汇编编写窗口程序需要调用大量的WINDOWS 的API,而且提供了高级语言才有的条件语句和循环语句,难度相对于LINUX下的AT&T汇编要小很多。

汇编可以开发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:系统时间直接显示在桌面上。程序源代码如下:

    

Asm代码  

.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、用调试器调试线程

 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字节

©著作权归作者所有:来自51CTO博客作者myhaspl的原创作品,如需转载,请注明出处,否则将追究法律责任


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消