1.基本概念

[入库:2005年8月19日] [更新:2007年3月24日]

本文简介:选择自 goddragon 的 blog

第一课 基本概念


我们先假设您已知道了如何使用masm。如果您还不知道的话,请下载 win32asm.exe ,并请仔细研读其中所附带的文档资料。好,如果您已准备就绪,我们这就开始吧!

理论:

win32 程序运行在保护模式下的,保护模式的历史可以追溯到 80286。而今 80286 已成为了历史。所以我们将只把精力集中于 80386 及后续的x86 系列 cpu。windows 把每一个 win32 应用程序放到分开的虚拟地址空间中去运行,也就是说每一个应用程序都拥有其相互独立的 4gb 地址空间,当然这倒不是说它们都拥有 4gb 的物理地址空间,而只是说能够在 4gb 的范围内寻址。操作系统将会在应用程序运行时完成 4gb 的虚拟地址和物理内存地址间的转换。这就要求编写应用程序时必须格守 windows 的规范,否则极易引起内存的保护模式错误。而过去的 win16 内存模式下,所有的应用程序都运行于同一个 4gb 地址空间,它们可以彼此"看"到别的程序的内容,这极易导致一个应用程序破坏另一个应用程序甚至是操作系统的数据或代码。
和 16 位 windows 下的把代码分成 data,code 等段的内存模式不同,win32 只有一种内存模式,即 flat 模式,意思是"平坦"的内存模式,再没有 64k 的段大小限制,所有的 win32 的应用程序运行在一个连续、平坦、巨大的 4gb 的空间中。这同时也意味着您无须和段寄存器打交道,您可以用任意的段寄存器寻址任意的地址空间,这对于程序员来说是非常方便的,这也使得用32位汇编语言和用c语言一样方便。 在win32下编程,有许多重要的规则需要遵守。有一条很重要的是:windows 在内部频繁使用 esi,edi,ebp,ebx 寄存器,而且并不去检测这些寄存器的值是否被更改,这样当您要使用这些寄存器时必须先保存它们的值,待用完后再恢复它们,一个最显著的应用例子就是 windows 的 callback 函数中。

内容:

下面的程序段是一个框架, 若您现在还不知道这些指令的确切意义的话,没关系, 随后我就会给大家详细解释。

.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 约定规定参数传递顺序是从右到左,即最右边的参数最先压栈,由调用者恢复堆栈指针。

例如:为调用函数 foo ( int first_param, int second_param, int third_param ); 按c约定的汇编代码应该是这样的:

push [third_param]
push [second_param]
push [first_param]
call foo
add esp, 3 * 4 ;调用者自己恢复堆栈指针

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>

是用来唯一标识您的代码范围的标签, 两个标签必须相同,应用程序的所有可执行代码必修在两个标签之间。

本文关键:asm
 

本站最佳浏览方式为 分辨率 1024x768 IE 6.0(或更高版本的 IE浏览器)

go top