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

来源:互联网 发布:edius8软件下载 编辑:程序博客网 时间:2024/06/07 23:09

在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:红
程序运行效果如下:



因此,在此只简单介绍一下,首先来看一段简单的窗口程序。注意";"表示注释


  1. ;加上注释和个人理解  
  2.   
  3.   
  4. .386  
  5. .model flat,stdcall  
  6. option casemap:none  
  7.   
  8. ;以下定义INCLUDE文件  
  9. include winows.inc  
  10. include gdi32.inc  
  11. includelib gdi32.lib  
  12. include user32.inc  
  13. includelib user32.lib  
  14. include kernel32.inc  
  15. include kernel32.lib  
  16. ;以下定义数据段  
  17. .data ? ;定义变量  
  18. hinstance dd ?  
  19. hwinmain dd ?  
  20. .const ;定义常量,字符串全部要以0结尾,因为在内存中0是字符串的结束符  
  21. szclassname db 'billclass',0  
  22. szcaptionmain db 'bill's firt program',0  
  23. sztext db 'WIN32汇编,BILL!!!!'0  
  24. ;以下是代码段  
  25. .code  
  26. ;定义窗口过程  
  27. _procwinmain proc uses ebx edi esi,hwnd,umsg,wparam,lparam  
  28. ;定义局部变量用关键字local  
  29. local @stps:PAINTSTRUCT  
  30. local @strect:PAINTSTRUCT  
  31. local @hdc  
  32. mov eax,umsg ;取得传入过程的消息变量值  
  33. ;-----------下面开始根据消息类型的不同作出不同的处理  
  34. .if eax == WM_PAINT ;如果消息是窗口绘制  
  35. invoke BeginPaint,hwnd,addr@stRect;WIN32汇编调用API程序后,API程序将返回值放在EAX中,  
  36. ;客户区准备  
  37. mov @hdc,eax;取得设备句柄  
  38.   
  39. invoke GetclientRect,hwnd,addr @stRect;addr是取变量的地址但只能用在INVOKE语句中且  
  40. ;不能同时使用  
  41. ;EAX寄存器传参数,因为ADDR会用到EAX。  
  42. ;此API的含义是取得描述客户区的结构放在@stRect  
  43. invoke drawText,@hdc,addr sztext,-1,addr @stRect,\  
  44. DT_SINGLELINE or DT_CENTER or DT_VCENTER ;语句换行符是\,显示'WIN32汇编,BIL  
  45. ;L!!!!',并设置其为单行DT_SINGLE  
  46. ;等等LINE  
  47. invoke EndPaint,hwnd,addr @stPs  
  48.   .elseifmeax==WM_CLOSE  
  49. invoke DestroyWindow,hwinmain ;销毁窗口  
  50. invoke PostQuitMessage,Null ;向消息循环中发出退出消息  
  51. .else  
  52. invoke DefWindowProc,hwnd,uMsg,wPara,lParam;如果不是上述消息,则执行WINDOWS标准的默认消息处  
  53. ;理,如键盘等消息  
  54. ret;返回  
  55. .endif  
  56.   
  57. xor eax,eax ;eax清0  
  58. ret  
  59. _ProcWinMain endp  
  60. ;以上这个子程序处理窗口消息的,是窗口的回调函数,该项函数不是我们调用,是由WINDOWS调用用来处理  
  61. ;窗口消息的,我们调用的是DispatchMessage,DispatchMessage再回过头来调用窗口过程。  
  62. _WinMain Proc ;主程序  
  63. local @stWndClass:WNDCLASSEX  
  64. local @stmsg:MSG  
  65.   
  66. invoke GetModuleHandle,Null ;得到应用程序句柄  
  67. mov hInstance,eax ;将应用程序的句柄放入hInstance变量  
  68. invoke RtlZeroMemory,addr @stWndClass,sizeof WndClassEX ;msdn的解释TheRtlZeroMemory routine  
  69. ;fills a block of memory with zeros,即  
  70. 0填充stWndClass结构变量所占的内存,也就是初始化  
  71. ;-----下面注册窗口类  
  72. invoke loadcursor,0,IDC_ARROW ;加载箭头形指针句柄  
  73. mov @stWndClass.hCursor,eax ;鼠标指针赋值  
  74. push hInstance  
  75. pop @stWndClass.hInstance ;窗口句柄赋值  
  76. mov @stWndClass.cbsize,sizeof WNDCLASSEX ;结构大小  
  77. mov @stWndClass.style,CS_HREDRAW or CS_VREDRAW ;设置窗口样式  
  78. mov @stWndClass.lpfnwndproc,offset _procwinmain;设置回调函数,也就是窗口消息处理过程  
  79. mov @stwndclass.hbrbackground,COLOR_WINDOW+1  
  80. mov @stwndclass.lpszclassname,offset szclassname ;设置窗口类的名称  
  81. invoke RegisterClassEx,addr @stwndclass ;传上述设置好的结构以注册窗口类  
  82.  ;建立显示窗口  
  83. invoke CreateWindowEx,WS_EX_CLIENTEDGE,\  
  84. offset szclassname,offsetszcaptionmain,\  
  85. WS_OVERLAPPEDWINDOW,100,100,600,400,NULL,\  
  86. NULL,hinstance,NULL ;建立窗口并返回句柄在EAX中  
  87. mov hwinMain,eax ;刚创建的窗口句柄赋值  
  88. invoke showwindow,hwinmain,SW_SHOWNORMAL ;显示窗口  
  89. invoke updatewindow,hwinmain ;刷新窗口客户区,导致客户区窗口paint  
  90. ;消息循环,win32汇编得自行建立WINDOWS消息循环,不过这样更自由,可以彻底地控制程序  
  91. .while true  
  92.  invoke GetMessage,addr @stMsg,null,0,0 ;WINDOWS在系统内部有个系统消息队列,  
  93.             ;并为每个应用程序还维护了一个消息队列,将这些属于这些程序窗口范围内的  
  94.             ;系统消息发到该应用程序消息队伍中,这个API的作用就是从自己的应用程序  
  95.             ;消息队伍中接收消息。  
  96.   .break .if eax==0 ;If the function retrieves the WM_QUIT message, the return value is zero.  
  97.             ;invoke Translate(msdn),也就是说,当程序退出里,消息队伍里会有WM_QUIT消息,            ;就退出循环,意味着退出程序。  
  98.   invoke translatemessage,addr @stmsg;由应用程序对消息进行预处理,如把基于键盘扫描码的按键消息黑心                   ;换成ASCII码的键盘消息等  
  99.   invoke dispatchmessage,addr@stmsg ;将预处理好的消息发给WINDOWS,WINDOWS将其分派给该程序的相应窗;口处理过程处理,那么WINDOWS怎么知道窗口处理过程在哪呢,刚才不是已经注册过窗口类了,这就是为什么窗口;类要注册的原因了,那么为什么不能由程序自己处理消息,非得发给WINDOWS呢,其一、一个应有程序的窗口很多,如果自己处理的话,得建立一个窗口列表,上面记录每个窗口的窗口处理过程。其二、WINDOWS对于一些实时性很;强的信息采用直接调用窗口处理过程的方法。  
  100. .endw  
  101. ret  
  102. _winmain endp  
  103. ;没有下面的代码程序无法执行,因为START语句指定程序启动的入口点  
  104. start:  
  105. call _winmain  
  106. invoke ExitProcess,NULL;退出  
  107. end start   

 可以看到上面代码和用C编写的WIN SDK程序很相似。我们接着继续看2个例子:

例1:用WIN32汇编构建第一个WINODWS程序,这个程序完成显示一个带问号的对话框,对话框的内容是现在系统时间。

 

 

   首先,打开MASM32Editor(在桌面上可以找到图标),在里面输入以下代码:

  1.      .386  
  2.      .model flat, stdcall  
  3.      option casemap :none    
  4. ;#################################################################  
  5.      include windows.inc  
  6.      include user32.inc  
  7.      include kernel32.inc  
  8.      include gdi32.inc  
  9.      include masm32.inc  
  10.        
  11.      includelib user32.lib  
  12.      includelib kernel32.lib  
  13.      includelib gdi32.lib  
  14.      includelib masm32.lib  
  15. ;#################################################################  
  16.    .data?  
  17.       szbuffer db 100 dup(?)  
  18.    .data  
  19.       szcaptionName db "我的HELLO,WORLD!",0  
  20.       szbegin db "现在时间:"  
  21.       sztext db 100 dup(?)  
  22. ;#################################################################  .code  
  23. start:  
  24. ;程序的入口  
  25.        call _callgetnow  
  26.        invoke MessageBox,NULL,offset szbegin,offsetszcaptionName,MB_ICONQUESTION or MB_OK  
  27.        invoke ExitProcess,eax  
  28. ;#################################################################  
  29. _callgetnow proc  
  30.     pushad  
  31.   invokeGetDateFormat,LOCALE_USER_DEFAULT,DATE_LONGDATE,NULL,NULL,addrsztext,100  
  32.     invokeGetTimeFormat,LOCALE_USER_DEFAULT,LOCALE_NOUSEROVERRIDE,NULL,NULL,addrszbuffer,100  
  33.      
  34.     invokeszCatStr,addr sztext,addr szbuffer  
  35.     popad  
  36.     ret  
  37. _callgetnow endp  
  38. ;#################################################################  
  39. end start  

 

  将上述代码保存为HELLOWORLD.ASM后,对程序进行编译。
  在编译前参照下图设置好系统的环境变量,path变量加上x:\masm32\bin,lib变量加上x:\masm32\lib,include变量加上x:\masm32\include。

  确环境变量设置好后,进入DOS窗口开始编译。

   首先运行ml,编译成coff文件格式
  然后运行LINK,进行链接,生成EXE文件,


  大功造成,运行一下试试效果吧!

 



例2:系统时间直接显示在桌面上。程序源代码如下:
    
Asm代码  收藏代码
  1. .386  
  2.      .model flat, stdcall  
  3.      option casemap :none    
  4. ;#########################################################################  
  5.      include windows.inc  
  6.      include user32.inc  
  7.      include kernel32.inc  
  8.      include gdi32.inc  
  9.      include masm32.inc  
  10.        
  11.      includelib user32.lib  
  12.      includelib kernel32.lib  
  13.      includelib gdi32.lib  
  14.      includelib masm32.lib  
  15. ;#########################################################################  
  16.    .data?  
  17.       szbuffer db 100 dup(?)  
  18.    .data  
  19.       szmssucesscap db "HELLO,WORLD!深入",0  
  20.       szmssucesstext db "在桌面的(300,300)处显示了当前时间",0  
  21.       szmscap db "错误",0  
  22.       szmstext1 db "无法在桌面上显示!",0  
  23.       szmstext2 db "无法得到全屏DC!",0  
  24.       szbegin db "现在时间:"  
  25.       sztext db 100 dup(?)  
  26. ;#########################################################################  
  27.     .code  
  28. start:  
  29. ;程序的入口  
  30.        _showtext  proto :DWORD  
  31.        call _callgetnow  
  32.        invoke _showtext,offset szbegin  
  33.        invoke ExitProcess,eax  
  34. ;#########################################################################  
  35. _callgetnow proc  
  36.     pushad  
  37.   invokeGetDateFormat,LOCALE_USER_DEFAULT,DATE_LONGDATE,NULL,NULL,addrsztext,100  
  38.     invokeGetTimeFormat,LOCALE_USER_DEFAULT,LOCALE_NOUSEROVERRIDE,NULL,NULL,addrszbuffer,100  
  39.      
  40.     invokeszCatStr,addr sztext,addr szbuffer  
  41.     popad  
  42.     ret  
  43. _callgetnow endp  
  44. ;#########################################################################  
  45. _showtext proc  lpsztext:DWORD  
  46.     LOCAL@Desktopdc:HDC  
  47.     LOCAL@dwcolor:DWORD  
  48.      
  49.     pushad  
  50.     mov@dwcolor,00FF0000h  
  51.     invokeGetWindowDC,NULL  
  52.     cmpeax,0  
  53.     jne @f  
  54.     invokeMessageBox,NULL,offset szmstext2,offset szmscap,MB_ICONERROR  
  55.     @@:  
  56.     mov@Desktopdc,eax  
  57.     invokelstrlen,lpsztext  
  58.     movebx,eax  
  59.     invokeSetBkMode,@Desktopdc,TRANSPARENT  
  60.     invokeSetTextColor,@Desktopdc,@dwcolor  
  61.     invokeTextOut,@Desktopdc,300,300,lpsztext,ebx  
  62.     invokeMessageBox,NULL,offset szmssucesstext,offsetszmssucesscap,MB_ICONINFORMATION  
  63.     cmpeax,0  
  64.     jne @f  
  65.     invokeMessageBox,NULL,offset szmstext1,offset szmscap,MB_ICONERROR  
  66.     @@:  
  67.     popad  
  68.     ret  
  69. _showtext endp  
  70. ;#########################################################################  
  71. 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)栈调用

以下面代码为例



  1. Imports System.Threading  
  2.   
  3. Public Class Form1  
  4.   
  5.     Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click  
  6.         Dim main_x As Integer  
  7.         main_x = 5  
  8.         Call sub1(main_x)  
  9.     End Sub  
  10.     Private Sub sub1(sub1_x As Integer)  
  11.         Dim jg As Integer  
  12.         jg = sub1_x * sub1_x  
  13.         Call sub2(jg)  
  14.     End Sub  
  15.     Private Sub sub2(sub2_x As Integer)  
  16.         Dim jg As Integer  
  17.         jg = sub2_x * 2  
  18.         '在下一句设置断点  
  19.         jg = jg * jg  
  20.   
  21.     End Sub  
  22.   
  23. End Class  

 

 

 
 

 

 我们首先来看调用堆栈,在调试菜单中选择调用堆栈,可看到过程的调用顺序:



 然后查看线程



 最后查看局部变量



 此外,我们还可以研究一下这个线程的调用时堆栈情况,通过反汇编代码,在调用堆栈窗口中选择“转到反汇编”

 


  1.    Private Sub sub2(sub2_x As Integer)  
  2. 00000000  push        ebp   
  3. 00000001  mov         ebp,esp   
  4. 00000003  sub         esp,14h   
  5. 00000006  mov         dword ptr [ebp-10h],ecx   
  6. 00000009  mov         dword ptr [ebp-4],edx   
  7. 0000000c  cmp         dword ptr ds:[0256B1B8h],0   
  8. 00000013  je          0000001A   
  9. 00000015  call        62A16743   
  10. 0000001a  xor         edx,edx   
  11. 0000001c  mov         dword ptr [ebp-8],edx   
  12. 0000001f  mov         eax,dword ptr [ebp-10h]   
  13. 00000022  mov         dword ptr [ebp-14h],eax   
  14. 00000025  mov         ecx,dword ptr [ebp-10h]   
  15. 00000028  call        628F5C25   
  16. 0000002d  mov         dword ptr [ebp-0Ch],eax   
  17. 00000030  push        32h   
  18. 00000032  mov         edx,dword ptr [ebp-0Ch]   
  19. 00000035  mov         ecx,dword ptr [ebp-14h]   
  20. 00000038  call        FFDF30D0   
  21. 0000003d  mov         ecx,dword ptr [ebp-4]   
  22. 00000040  call        FFFFFF70   
  23. 00000045  mov         ecx,63h   
  24. 0000004a  call        FFDF2940   
  25. 0000004f  nop   
  26.         Dim jg As Integer  
  27.         jg = sub2_x * 2  
  28. 00000050  mov         eax,dword ptr [ebp-4]   
  29. 00000053  mov         edx,2   
  30. 00000058  imul        eax,eax,2   
  31. 0000005b  jno         00000062   
  32. 0000005d  call        62A19A30   
  33. 00000062  mov         dword ptr [ebp-8],eax   
  34.         '在下一句设置断点  
  35.         jg = jg * jg  
  36. 00000065  mov         eax,dword ptr [ebp-8]   
  37. 00000068  imul        eax,dword ptr [ebp-8]   
  38. 0000006c  jno         00000073   
  39. 0000006e  call        62A19A30   
  40. 00000073  mov         dword ptr [ebp-8],eax   
  41.   
  42.     End Sub  
  43. 00000076  nop   
  44. 00000077  nop   
  45. 00000078  mov         ecx,63h   
  46. 0000007d  call        FFDF2A60   
  47. 00000082  nop   
  48. 00000083  mov         esp,ebp   
  49. 00000085  pop         ebp   
  50. 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字节

原创粉丝点击