win32asm程序框架
.386
.MODEL Flat, STDCALL
.DATA
<Your initialized data>
......
.DATA?
<Your uninitialized data>
......
.CONST
<Your constants>
......
.CODE
<label>
<Your code>
.....
end <label>
.386
一个汇编语言伪指令,告诉编译器程序是使用80386指令集编写的。还可以使用 .486、.586, 但最安全的还是使用.386。对于每一种CPU有两
套几乎功能相同伪指令: .386/.386P、 486/.486P、 586/.586P。 带P的指令标明程序中可以用特权级指令。特权级指令是保留给操作系统的
,如虚拟设备驱动程序。在大多数时间,程序都无须运行在RING0层(编写病毒的话另当别论^_^),故用不带后缀P的伪指令已足够了。
.MODEL FLAT,STDCALL
.MODEL 是用来指定内存模式的伪指令,在Win32下,只有一种内存模型,那就是FLAT。 STDCALL 告诉编译器参数的传递约定。参数的传递约定
是指参数传达时的顺序(从左到右或从右到左)和由谁恢复堆栈指针(调用者或被调用者)。在Win16下有两种约定:C 和 PASCAL。C 约定规定参
数传递顺序是从右到左,即最右边的参数最先压栈,由调用者恢复堆栈指针。PASCAL约定和C约定正好相反,它规定参数是从左向右传递,由被
调用者恢复堆栈。Win16采用了PASCAL约定, 因为PASCAL约定产生的代码量要小。当不知道参数的个数时,C约定特别有用。如在函数wsprintf
() 中, wsprintf预先并不知道要传递几个参数,所以它不知道如何恢复堆栈。STDCALL是C约定和PASCAL约定的混合体,它规定参数的传递是
从右到左,恢复堆栈的工作交由被调用者。Win32只用STDCALL约定,但除了一个特例,即:wsprintf。
.DATA .DATA? .CONST .CODE
上面的四个伪指令是"分段"(SECTION)伪指令。Win32下没有"段"(SEGMENT)的概念,但是可以把程序分成不同的"分段",一个"分段"的开始即是
上一个"分段"的结束。WIN32中只有两种性质的"分段":DATA和CODE。
其中DATA"分段"又分为三种:
.DATA 其中包括已初始化的数据。
.DATA? 其中包括未初始化的数据。比如有时仅想预先分配一些内存但并不想指定初始值。使用未初始化的数据的优点是它不占据可执行文件的
大小,如:若您要在 .DATA? 段中分配10,000字节的空间,您的可执行文件的大小无须增加10,000字节,而仅仅是要告诉编译器在装载可执
行文件时分配所需字节。
.CONST 其中包括常量定义。这些常量在程序运行过程中是不能更改的。 应用程序并不需要以上所有的三个"分段", 可以根据需要进行定义。
.CODE 这是代码"分段"。
注:实际上,分段并不是象在 Dos 下一样,为不同的段分别指出不同的段寄存器,因为 Windows 下只有一个 4GB 的段,Windows 程序中的分
段表现在当程序装载时,赋予不同的分段不同的属性,比如说当程序加载时,对于 Ring3 程序来说,.code 段是不可写的,而 .data 段是可
写的,如果尝试象在 Dos 下一样写自己的代码部分,会得到一个蓝屏错误。
<label>
end <label>
是用来唯一标识您的代码范围的标签, 两个标签必须相同,应用程序的所有可执行代码必修在两个标签之间。
Windows API 简介
Windows API是一大组驻扎在 Windows 中供人们随时调用的功能强大的函数。这些函数的大部分被包含在DLL中,譬如:kernel32.dll(程调度
)、 user32.dll(主要控制用户界面) 和 gdi32.dll(图形方面的操作)。
当应用程序被加载时 Windows 会检查这些信息,这些信息包括动态链接库的名字和其中被调用的函数的名字。若检查到这样的信息,Windows
就会加载相应的动态链接库,并且重定位调用的函数语句的入口地址,以便在调用函数时控制权能转移到函数内部。
从和字符集的相关性来分,API 共有两类:一类是处理 ANSI 字符集的,另一类是处理 UNICODE 字符集的。前一类函数名字的尾部带一个"A"
字符,处理UNICODE的则带一个"W"字符。我们比较熟悉的ANSI字符串是以 NULL 结尾的一串字符数组,每一个ANSI字符是一个 BYTE 宽。对于
欧洲语言体系,ANSI 字符集已足够了,但对于有成千上万个唯一字符的几种东方语言体系来说就只有用 UNICODE 字符集了。每一个 UNICODE
字符占有两个 BYTE 宽,这样一来就可以在一个字符串中使用 65336 个不同字符了。
这也是为什么引进 UNICODE 的原因。在大多数情况下我们都可以用一个包含头文件,在其中定义一个宏,然后在实际调用函数时,函数名后不
需要加后缀"A"或"W"。
INVOKE与CALL的区别
INVOKE 是一个有参数类型检查的CALL调用语句,一般使用函数原型定义。INVOKE 的语法如下:
INVOKE expression [,arguments]
expression 既可以是一个函数名也可以是一个函数指针。参数由逗号隔开。大多数API函数的原型放在头文件中。hutch 的 MASM32,这些头文
件在文件夹MASM32/include 下。
offset 和 addr的区别
1.addr不可以处理向前引用,offset则能。
2.addr可以处理局部变量而 offset 则不能。