本文已被更新,请看新版文章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 链 接: http://blog.csdn.net/magus_yang/archive/2007/02/26/1514439.aspx 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位汇编语言程序设计》(第二版)第三章