Jack's第一个Win32汇编程序HelloWorld

本文已被更新,请看新版文章
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位汇编语言程序设计》(第二版)第三章
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值