在 Win32 系统下的组合语言准备工作

来源:互联网 发布:php和java的区别2016 编辑:程序博客网 时间:2024/05/17 03:05
导读:
  有关 Win32 平台上组合语言的资料
  虽然 DOS 已成过去,但小木偶仍然很怀念那段在 DOS 的时光,尤其是用组合语言去驾驭电脑,在 32 位元作业系统 ( 指 OS/2、Windows 9x/2K/XP、Linux 等作业系统 ) 当道的今天,似乎很少人提及『写程式』这档事,即使有,也只是 C/C++、Delphi 或是 Basic,难道仍然能使用组合语言吗?答案绝对是肯定的。早在 1999 年 7~8 月智冠科技所出版的『游戏设计大师』内有一篇傅新灯先生所写的文章,是我所见过最早在 Win32 作业系统 ( 指 Windows 9x/Me/2K/XP 等作业系统,以後简称为『Win32』) 使用组合语言写程式,此文章有一系列,最先介绍 Win16 写组合语言程式,之後再介绍 Win32,傅先生也有网页,可惜现在已经消失了。2002年初,出现了一个以 OS/2 作业系统为平台的程式设计网站『午後的程式设计』,里面也有提及组合语言程式设计,并且也有范例,可惜也已消失了。现在在繁体中文的网站里,有关在 Win32 组合语言程式设计的网页似乎只剩下零星的记录而已,如 Free Tech 等。
  但是在简体中文的网站里,有许多网站可供参考,小木偶最常去的是『罗云彬的编程乐园』,里面有许多高手发表程式范例,罗先生本身就是很高段的程式设计师,他也有许多精彩的文章发表,西元 2002 年,他写了一本名为『Windows 环境下 32 位元组合语言程式设计』的书,至今仍是中文世界里,有关 Win32 组合语言的唯一书籍。此书有正体中文与简体中文两种版本,正体版由全华公司代理贩售。
  在英文的网站里,那就多如牛毛了,最是有名的,应该属於『Iczelion's Win32 Assembly Homepage』这个网站,里面有从初学者到高手必经的教材,有志於此的人当然不可放过,可惜它是个英文网站,对我们来说总有一些隔阂,但也不用气馁,在罗云彬的编程乐园网站里已经有翻译好的文章可供参考,再加上一些正简体转换软体,轻而易举就可变成您熟悉的文字。
  其实,这里也是一块卧虎藏龙之地,程式高手不乏其人,但想来他们大概是太忙了,无暇为初学者写初级的入门文章。小木偶不自量力,想为 Win32组合语言撰写繁体中文的网页,这个网页将参考上述网页及 Win32 C++ 相关书籍,希望诸位前辈能不吝指教,为组合语言程式设计投注心力,方能使敝网页能更增可读性,或许未来能有人因敝网页受益,进而更上一层楼,写出中文的杀手级软体,则敝人亦与有荣焉。
  所需工具及软体
  在 Win32 平台撰写 32 位元的组合语言程式至少要有三种工具:组译器与连结器、除错器、参考资料。
  组译器与连结器:MASM32 v7.0 或 TASM v5.0
  在 Win32 平台上撰写组合语言,至少有两种组译器可以选择:一是 Borland 的 TASM 5.0,一是微软的 MASM 6.11 以後的版本。微软的 MASM 并没有附带定义程式库的包含档,也没有其他支援工具,必须到微软所发布的各个工具包去收集,因此早期要使用 MASM 是件很麻烦的事,幸好後来微软把 MASM 6.11d 开放下载 ( 参考 DOS 的组合语言准备工作:注一,但这里只能得到 ML.EXE 与 ML.ERR 两个档案而已,版本是 6.11d ),又有一位前辈,Steve Hutchesson (让我们对他致上崇高的敬意,因为没有他的努力,全世界所有的人要撰写 Win32 组合语言要跨过比现在还困难的门槛),为 MASM 定义程式库,并且包含了许多文件、工具等必要档案包装成 MASM32 v7.0,使得用组合语言写出 Win32 程式并不再是件难如登天的事,再加上网路上所能找到的资料较多以及小木偶的习惯,因此小木偶将以 MASM 为主介绍在 Win32 平台上撰写组合语言,对於使用 TASM 的人,只好说声抱歉了。
  MASM32 7.0 可以到 hutch's home page下载取得。下载後,将它解压缩并执行其中的 INSTALL.EXE 就可以了 ( 其实 MASM32V7.ZIP 只有一个档案,INSTALL.EXE ),INSTALL.EXE 会要您选择安装在那一台硬碟机,如下图:
  
  
  然後它自己会进行安装作业,过一阵之後会自动出现 DOS 模式的视窗,待 INSTALL.EXE 自己执行好再关闭这个 DOS 模式视窗即可,如下图:
  
  
  安装到最後,INSTALL.EXE 还会告诉您可以参考您刚才安装磁碟机下的『masm32』子目录内,有个『README.HTM』档的说明,如下左图。最後安装完画面是像下面右图。
  
  
  
  
  除错器:Soft-ICE 或 OllyDbg
  安装好组译器以及安排好环境後,还要有除错器以找出错误。最有名的除错器是 NuMega 公司出品的 Soft-ICE。这套除错器功能强大,可以到笨冬瓜的网页去抓取。
  安装 Soft-ICE 的过程很简单,过程中会要您输入序号 ( 已包含在压缩档内 )、安装路径、选择显示卡 ( 如下图 )、滑鼠种类,并不困难就不赘述了。
  
  
  安装到最後,Soft-ICE 还会自动在 C:/AUTOEXEC.BAT 上加入一行 C:/PROGRA~1/NUMEGA/SOFTIC~1/WINICE.EXE,表示每次启动 Win 9x 都会自动载入 Soft-ICE。重新启动 Win9x 後,Soft-ICE 自行载入,您可以按 Ctrl-D 把 Soft-ICE 视窗叫出来,您会发现,所有在桌面上的程式全都停止了,连滑鼠都动弹不得 ( 注一),这是正常的,因为 Soft-ICE 已经控制了所有执行权。再按一次 Ctrl-D,Soft-ICE 又把控制权还给 Win 9x,Soft-ICE 视窗消失,Win 9x 恢复正常。
  但是 Soft-ICE 预设的字形太小,视窗也不太大,您可以稍稍修改。修改方法是编辑『C:/Program Files/NuMega/SoftICE95/』子目录内的 winice.dat 档案,这个档案是一个纯文字档,可以用任何的文字编辑软体,如小作家、PE2 编辑,找到该档案内有一行 INIT=" 的字样,把引号内的文字依自己需求稍加修改,再存档,重新启动 Win 9x 後就会依您修改的方式显示了。底下是 INIT 字串所代表的意义:
  LINES:LINES 後面所接的是十进位数,这个数字表示 Soft-ICE 视窗总共显示的行数。
  WC:WC 後面也是接一个十进位数,它表示程式码视窗的行数。Soft-ICE 的视窗分成暂存器视窗、资料区视窗窗、程式码视窗及交谈视窗。
  WD:设定资料视窗大小。
  SET FONT:SET FONT 後面接设定字形,用阿拉伯数字 1、2、3 表示字形种类。
  SET CODE:可以接 ON 或 OFF 两种选项,分别表示於程式码视窗显示或不显示机械码。
  X:离开 Soft-ICE 视窗,作用相当於在 Soft-ICE 视窗里按下 Ctrl-D 键。假如最後没有 X 的话,启动 Win 9x 後,Soft-ICE 视窗会自动跳出来。
  每个 Soft-ICE 命令之後用『;』连接,在小木偶的电脑里是
  INIT="LINES 43; SET FONT 2; WC 20; WD 8; SET CODE ON; X;"
  此外,winice.dat 还有一个地方要修改,那就是 PHYSMB,这个字串表示您电脑的记忆体有多大,单位是 MB。现在的电脑大部份是 128MB 或 256MB,您就依据您电脑上装了多少 RAM 填上即可。然後重新启动,修改後的设定才能生效。
  Soft-ICE 虽然功能强大,但是与有些机器的相容性差,在 Windows XP 系统上似乎也有点儿问题,小木偶一直无法解决,如果有前辈知道还请来信告知,在此先谢谢了。为了解决在 Windows XP 下除错或其他相容性的问题,小木偶另外推荐一个可以在 Windows 9x/Me/XP 里除错,并且也算是很有名的除错器,OllyDebug,它是 Oleh Yuschuk 所撰写的共享软体,可以到 Ollydbg网站中下载。下载完成後只需要解压缩到一个子目录里,不须安装就可以使用了,您只要在命令模式下切换到 OllyDebug 所解压缩的子目录下,执行 OLLYDBG.EXE 即可。更方便的方法是在桌面上建立 OllyDebug 的捷径。
  参考资料:Win32 程式设计师参考手册、MSDN
  回想以前在 DOS 下撰写组合语言,常用 DOS/BIOS 服务程式来做低阶功能,省去许多麻烦。同样的,在 Win32 系统也提供了许多的服务程式供程式呼叫,这些系统提供的服务程式,称为 Win32 API (Application Programming Interface),可以到 Iczelion's Win32 Assembly Homepage 下载网页下载 win32api.zip。
  解压缩後,可以得到一个 Windows 的 HLP 格式档,它不仅包含了 Win32 API,还包含了一些在 Win 32 环境底下撰写程式的资料,所以它的标题栏是『Win32 Programmer's Reference』。当我们撰写 Win32 程式时,它是一份重要的参考文献,其地位相当於 Ralf Brown's 中断列表一样。
  设定组译环境
  完成安装 MASM32 之後,必须设定组译环境。MASM32 组译时,并不像 Borland C++ Builder 或是 Visual C++ 一样整合开发环境,MASM32 必须进入命令提示列下以文字输入命令组译,就像以前的 DOS 时代执行指令一样。组译及连结时要用到许多包含档与程式库,所以为了组译方便,应该事先设定好这些环境。
  假如您使用 Windows 95/98/Me,可以在 AUTOEXEC.BAT 档里加入以下四行 ( AUTOEXEC.BAT 是一进入 MS-DOS 模式就会自动执行的批次档,所以只要您加入了下面这四行以後,只要一进入 MS-DOS 模式,系统就会自动将组译环境设好。如果您 Win 9x 的 C:/ 没有 AUTOEXEC.BAT 那麽就得自行开启文书处理程式,建立新的 AUTOEXEC.BAT;如果您已经有 AUTOEXEC.BAT,那就加上以下四行 ):
  SET INCLUDE=X:/masm32/INCLUDE
  SET LIB=X:/masm32/LIB
  SET PATH=X:/masm32/BIN;%path%
  SET ML=/coff /link /SUBSYSTEM:WINDOWS
  这四行,上面的 X: 是表示您安装 MASM32 v7.0 的磁碟机名,要注意的是 ML 的参数是大小写有别,所以必须依照上面的大小写,否则会产生错误。前两行是告诉电脑到那里去找包含档以及程式库,第三行是告诉电脑到那里去找组译器 ( ML.EXE ) 以及连结器 ( LINK.EXE ),您可以找一找刚刚您安装的磁碟机的 /masm32/BIN 子目录里应该有这两个可执行档 ( ML.EXE 和 LINK.EXE )。
  在 MASM 6.0 以後的编译程式不再是 MASM.EXE 了,而改为 ML.EXE,它会在组译完成後自动呼叫 LINK.EXE 执行连结,这样就不须使用者於 DOS 提示号後再执行 LINK.EXE。这是一项不错的设计,可惜在 DOS 的执行档格式与 Win32 的不同,所以必须在 LINK 之後下 /SUBSYSTEM:WINDOWS 指定 LINK.EXE 要制作成 Win32 可执行档格式。因此 ML.EXE 得把「制作成 Win32 可执行档的参数」传递给 LINK.EXE,就是使用 /link 这个参数,/link 之後所接的『/SUBSYSTEM:WINDOWS』就是 LINK.EXE 的其中一个参数,它表示产生 Win32 的可执行档。而 /coff 是使 ML.EXE 建立 COFF 格式的目的档,我想您现在只要知道,在 Win32 的可执行档必须由 COFF 格式目的档制作出来就可以了。
  假如您在 Windows XP ( NT 我没试过,不过猜想应该和 XP 一样 ) 组译,也和上述一样,最好也先制作或修改一个批次档以自动设好组译环境,例如在 C: 的根目录下的 AUTOEXEC.BAT 档,其内容应包含
  SET INCLUDE=X:/masm32/INCLUDE;%INCLUDE%
  SET LIB=X:/masm32/LIB;%LIB%
  SET PATH=X:/masm32/BIN;%PATH%
  SET ML=/coff /link /SUBSYSTEM:WINDOWS
  这四行,其中的意义与上面相同,您注意到前三行都有一对『%』包含着环境变数,这是为了不改变他们。存档之後,将滑鼠移到『开始』→『所有程式』→『附属应用程式』→『命令提示字元』上,按下滑鼠右键後会蹦现一个选单,选择其中的『内容』之後再选择『捷径』标签,出现下图:
  
  
  ,然後修改『目标』,在後面加上『 /KC:/AUTOEXEC.BAT』( 即上图中蓝色标示的文字 )。这样一旦您执行『命令提示字元』,就会把适当的环境设定好。
  Win32 程式设计简介
  Win 32 程式是在保护模式下执行的,这表示您不能像在 DOS 作业系统一样,可以任意存取任意位址的资料 ( DOS 系统及其大部分的应用程式都是在真实模式下执行的 ),在 Win 32 作业系统里的程式那些可以被读取而不能写入,那些不能读取也不能写入,那些能读取也能写入都由作业系统指定,所有程式都要遵守,否则 Windows 就给你一个蓝色画面终止程式执行。
  Section
  在 Win 32 作业系统里,记忆体的定址方式不再用『段暂存器:偏移位址』来表示了,因为在 Win32 作业系统里改用 32 位元的暂存器来定址,32 位元可以定址 4GB ( 也就是 4,294,967,296 个位元组 )。在 4GB 这麽大的记忆空间,Windows 作业系统是把它看成由 00 到 4GB 连续的空间,而应用程式要存取某一位址之资料,作业系统能将之转换成实际记忆体所在位址,这种记忆体模式称为平坦模式 ( flat mode ),而 Win 32 所能用的模式也只有这一种。当然为了管理记忆体 ( 实现刚才所说的那一块区域只可读不可写,那一块能读能写 ),Windows 作业系统还是把记忆体分成许多『段』(section),此处的段与 DOS 的段 ( segment ) 不同,此处的段可以超过 64KB,而且不需要为它指定段暂存器,系统会自动分辨。当一个段的结束就是另一个段的开始。
  Win 32 作业系统的段 (section) 只有两种,一是程式码段,另一种是资料段。程式码段是用『.code』来定义的,在程式码段的程式码是不可改写的,这点和 DOS 不同,如果有程式想改写自己的程式码,将会得到错误讯息。资料段还可再分为三种:
  .data:这是定义已经有初始值的变数所在的资料区段。
  .data?:这是定义尚未有初始值的变数所在的资料区段。
  .const:这是定义常数资料段。这个区段内的资料无法再被更改。
  视窗各部名称
  以小作家为例,一个视窗的模样长得像下图:
  最上面是标题栏,标题栏上有图示 (icon )、最大化按钮、最小化按钮、关闭按钮,当然也有些视窗没有按钮。标题栏下面是选单 (menu ),上面有许多选项。再下面是工具栏,它是由许多按钮组成的。再下面是工作区 ( client area,也有人翻成客户区 ),是使用者输入或文件显示的主要区域。最下面是状态列,显示工作时的情况。
  保存暂存器
  在 Win32 作业系统里,还有一条很重要的规则必须遵守,那就是:Windows 在内部频繁使用 ESI,EDI,EBP,EBX 暂存器,而不去检查这些暂存器的值是否被更改,所以当您要使用这些暂存器时,必须先保存它们,待用完後再恢复,否则会引起当机。
  资料型态与变数命名
  在 DOS 时代写组合语言的资料型态很简单,常用的就只有位元组 ( byte )、字组 ( word )、双字组 ( double word )、十个位元组 ( ten bytes ) 等不到十种,都在资料段定义,定义方式分别是 DB、DW、DD、DT 等。但是在 Win 32 环境下的资料型态似乎很复杂,除了刚刚所提到的,还有 HINSTANCE、HWND、LPSTR……等数十种,其实这些资料型态只不过是为了可读性的关系,当看到没看过的资料型态不用紧张,只要知道『喔,有这种资料型态』就好了,至於它所代表的长度也可以不必太担心也不太重要,但假如您想知道的话,可以开启您所安装的 MASM32 资料夹里的 INCLUDE 子目录,其中有个档案,WINDOWS.INC,其内容应该有。例如其中有两行
  HINSTANCE TYPEDEF DWORD
  HWND TYPEDEF DWORD
  您就可以知道 HINSTANCE 和 HWND 的长度均为一个双字组 ( Double Word ) 的大小。而其意义您也可以很容易由字面上解读出来,例如 HINSTANCE 应该把它看成 handle of instance,instance 是执行实例的意思,至於其意义在第二章说明。handle 是代码的意思,代码在 Win 32 环境底下是很常见的东西。在 Win 32 环境下,可能有许多程式同时执行,每个程式都有一个独一无二的编号,此编号就称为代码,执行实例的编号就称执行实例代码。第二行的 HWND 就是 handle of windows,表示视窗代码之意,Win 32 环境的桌面上,也有许多视窗,每一视窗也有一个独一无二的编号,称为视窗代码。也就是说,HINSTANCE 表示这是一个执行实例代码的资料型态,HWND 表示这是一个视窗代码的资料型态,这两种资料型态其实都是双字组。
  除了资料型态采用这种有系统的表示方法外,在 Win32 环境里的程式写作中,变数与常数名称也常常采用一种有系统的命名方式,称为匈牙利命名法 ( Hungarian convention ),这套方法是以前微软的工程师 Charles Simonyi 发明的,这个命名方法把变数名称分成三部份,字首、资料型态与修饰语。字首是用来表示变数的使用方法,资料型态在组合语言中并不重要,只要在意长度即可,真正表示变数名称是最後面的修饰语,如何把修饰语取得短但又不失其意是不太容易的。常用的字首与资料型态如下表:
  字首 意 义   资料型态 意 义
  c 计数器 b Boolean
  h 代码 by 位元组(byte)
  p 指标 c 字元(character)
  lp 长指标 n integer
  g 全域变数 s string
  sz 以零结尾的字串
  u 无号数
  dw 双字组(DWORD)
  举个例子来说,你看到 lpText,就可以连想到此变数表示 Text 之指标,即表示 Text 字串之位址。
  常数
  在 Win32 环境下撰写组合语言,常常为了可读性,所以用常数名称来代替数值。例如您在删除档案时,系统常会弹出一个视窗问您是否真的删除,视窗上有两个按钮『是』和『否』,在撰写程式时,常用常数名,MB_YESNO,表示,而不用数值表示。这些常数和数值之间的关系也都记录在 WINDOWS.INC 里。
  事件驱动与讯息驱动
  不管在 DOS 或 Win32 系统的执行的程式,都会接收到使用者是否按下键、是否移动滑鼠或者按下滑鼠上的按钮等等动作,这些动作称之为事件。在 DOS 环境下的程式,必须由程式不断的检查这些事件,而程式必须针对这些事件作出处理来。
  而 Win 32 是多工系统,每次使用者触发这些事件时,由系统判断使用者做出这些事件时是在那一个程式或那一个视窗里,然後再由系统把该事件发生时的一些资料变成一个结构体,称为讯息传递给该程式或视窗,通知该程式有事情必须处理了。所以我们撰写的程式必须有一个接收来自系统讯息的回圈,也必须有一段程式来处理这些事件。这就是所谓的事件驱动或讯息驱动。
  不管是 Win32 也好或是 DOS 程式也好,相同的地方是都必须针对使用者的动作事先设计好如何处理。不同的地方是 Win32 的程式是被动地通知要处理的讯息,而 DOS 程式则是主动地检查讯息。两者在观念上不太相同,是刚踏入 Win32 程式设计者难以接受的。
  好了,底下我们就进入第一章先看看一个简单的程式,但是实际真正牵涉到讯息的 Win32 程式是在第二章。

本文转自
http://home.educities.edu.tw/wanker742126/win32asm/w32asm_ch00.html