本文已被更新,请看新版文章
Jack整理的Win32汇编基础知识
http://blog.csdn.net/magus_yang/archive/2007/04/05/1552930.aspx
标 题: Jack's第一个Win32汇编程序HelloWorld
作 者: Jack Yang
时 间: 2007-02-26 1:02
Hello.asm文件的内容如下:
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 第一部分:模式和源程序格式的定义语句
.386 ; 指令集
.model flat,stdcall ; 工作模式
option casemap:none ; 格式
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include
windows.inc
include
user32.inc
includelib
user32.lib
include
kernel32.inc
includelib
kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
szCaption
db 'A MessageBox !',0
szText
db 'Hello, World !',0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.code
start:
invoke MessageBox,NULL,offset szText,offset szCaption,MB_OK
invoke ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
end start ; 指定程序的入口
1.
第一部分
模式和源程序格式的定义语句
第一行 指定使用的指令集(编译器使用)
Win32环境工作在80386及以上的处理器中,所以必须定义.386。如果程序(VxD等驱动程序)中要用到特权指令,那么必须定义.386p。
第二行 定义程序工作的模式(包括内存模式、语言模式、其它模式)
对Win32程序来说,只有一种内存模式,即flat(平坦)模式。
Win32 API调用使用的是stdcall格式,所以Win32汇编中必须在.model中加上stdcall参数。
第三行 option语句
由于Win32 API中的API名称区分大小写,所以必须定义option casemap:none,来表明程序中的变量和子程序名对大小写敏感。
2.
包含全部段的源程序结构:
.386
.model flat,stdcall
option casemap:none
<一些include语句>
.stack [堆栈段的大小]
.data
<一些初始化过的变量定义>
.data?
<一些没有初始化过的变量定义>
.const
<一些常量定义>
.code
<代码>
<开始标记>
<其他语句>
end 开始标记
3.
段的定义
数据段
.data
已初始化数据段,可读可写的已定义变量;
当程序装入完成时,这些值就已经在内存中;
数据定义在.data段中会增加可执行文件的大小;
.data段一般存放在可执行文件的_DATA节区(Section)内;
.data?
未初始化数据段,可读可写的未定义变量,在可执行文件中不占空间;
这些变量一般作为缓冲区或者在程序执行后才开始使用。
数据定义在.data?数据段中不会增加可执行文件的大小;
.data?段一般存放在可执行文件的_BSS节区内;
.const
常量,可读不可写的变量;
代码段
.code
所有的指令都必须写在代码段中;
Win32中,数据段是不可执行的,只有代码段有可执行的属性;
对于运行在特权级3的应用程序,.code段不可写。除非把可执行文件PE头部中的属性位改成可写;
对于运行在特权级0的程序,所有的段都有读写权限,包括代码段;
.code代码段一般存放在可执行文件的_TEXT节区内;
堆栈段
.stack
与DOS汇编不同,Win32汇编不必考虑堆栈。系统会自动分配堆栈空间;
堆栈段的内存属性是可读写并且可执行;
靠动态修改代码的反跟踪模块可以拷贝到堆栈中去边修改边执行;
缓冲区溢出技术也会用到这个特性;
4.
调用操作系统功能的方法:
DOS下
操作系统的功能通过各种软中断来实现。
应用程序调用操作系统功能将经历如下三个过程:
把相应的参数放在各个寄存器中再调用相应的中断;
程序控制权转到中断中去执行;
完成以后通过iret中断返回指令回到应用程序中;
DOS下调用系统功能方法的缺点:
所有的功能号定义是难以记忆的数字;
80x86系列处理器能处理的中断最多只能有256个;
通过寄存器来传递参数,对于参数较多的函数很不方便;
Win32下
系统功能模块放在Windows的动态链接库(DLL)中
作为Win32 API核心的3个DLL:
KERNEL32.DLL 系统服务功能。
GDI32.DLL 图形设备接口。
USER32.DLL 用户接口服务。
常用API的参数和函数声明,查看文档《Microsoft Win32 Programmer's Reference》
5.
Win32 API
的函数原型声明
函数原型声明的汇编格式如下:
函数名 proto [距离] [语言] [参数1]:数据类型, [参数2]:数据类型,......
proto是函数声明的伪指令
距离可以设置为NEAR、FAR、NEAR16、NEAR32、FAR16或FAR32,由于Win32中只有一个平坦的段,无所谓距离,所以在定义时可以忽略距离。
语言类型可是使用.model所定义的默认值。
以消息对话框函数MessageBox为例
C格式如下:
int MessageBox(
HWND hWnd, // Handle to owner window
LPCTSTR lpText, // text in message box
LPCTSTR lpCaption, // message box title
UINT uType // message box style
);
汇编格式如下:
MessageBox Proto hWnd:dword,lpText:dword,lpCaption:dword,uType:dword
或者写为
MessageBox Proto :dword,:dword,:dword,:dword
编译器只对参数的数量和类型感兴趣,参数的名称只是增加可读性,所以可以省略。
对于汇编语言来说,Win32环境中的参数实际上只有一种类型,就是一个32位的整数(dword,double word),双字,四字节。
6.
调用Win32 API
调用API有如下两种方法:
1) invoke
MASM提供的伪指令;
invoke伪指令的好处就是能够提高代码的可读性,减少错误;
invoke做了下面三件事:
在编译的时候,由编译器把invoke伪指令展开成相应的push指令和call指令;
进行参数数量的检查工作;
如果带的参数数量和声明时的数量不符,编译器会报错;
2) push和call的组合
80386处理器的指令
invoke
MessageBox,NULL,offset szText,offset szCaption,MB_OK
也可写为
push NULL
push offset szText
push offset szCaption
push MB_OK
call MessageBox
7.
Win32 API
函数返回值的处理方法
对于汇编语言来说,Win32 API函数返回值的类型只有dword一种类型,它永远放在eax中。
如果要返回的内容在一个eax中放不下,Win32 API采用如下方法来解决:
a) 一般是eax中返回一个指向返回数据的指针;
b) 在调用参数中提供一个缓冲区地址,数据直接返回到这个缓冲区中去。类似变参的概念;
8.
与字符串相关Win32 API
的分类
在Win32环境中,根据两个不同的字符集(ANSI字符集和Unicode字符集),可以把和字符串相关的API分成两类:
a) 处理ANSI字符集的Win32 API函数
函数名称的尾部带一个“A”字符;
ANSI字符串是以NULL结尾的一串字符数组,每一个ANSI字符占一个字节的宽度;
MessageBoxA Proto hWnd:dword,lpText:dword,lpCaption:dword,uType:dword
b) 处理Unicode字符集的Win32 API函数
函数名称的尾部带一个“W”字符;
每一个Unicode字符占两个字节的宽度,所以可以同时定义65536个不同的字符;
MessageBoxW Proto hWnd:dword,lpText:dword,lpCaption:dword,uType:dword
Windows 9x系列不支持Unicode版本的API,绝大多数的API只有ANSI版本。
只有Windows NT系列才完全支持Unicode版本的API。
为了编写在几个平台中都能通用的程序,一般应用程序都使用ANSI版本的API函数集。
提高程序可移植性的一个方法:
一般在源程序中不直接指明使用Unicode还是ANSI版本,而是使用宏汇编中的条件汇编功能来统一替换。
比如,在头文件中做如下定义:
if UNICODE
MessageBox equ <MessageBoxW>
else
MessageBox equ <MessageBoxA>
endif
然后在源程序的头部指定UNICODE=1或UNICODE=0,重新编译后就能产生不同的版本。
未完,待续。。。
参考资料:
罗云彬的《Windows环境下32位汇编语言程序设计》(第二版)第三章