Ch 00 在 Win32 系統下的組合語言準備工作


有關 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 作業系統為平台的程式設計網站『午後的程式設計』,堶惜]有提及組合語言程式設計,並且也有範例,可惜也已消失了。現在在繁體中文的網站堙A有關在 Win32 組合語言程式設計的網頁似乎只剩下零星的記錄而已,如 Free Tech 等。

但是在簡體中文的網站堙A有許多網站可供參考,小木偶最常去的是『羅云彬的編程樂園』,堶惘陶\多高手發表程式範例,羅先生本身就是很高段的程式設計師,他也有許多精彩的文章發表,西元 2002 年,他寫了一本名為『Windows 環境下 32 位元組合語言程式設計』的書,至今仍是中文世界堙A有關 Win32 組合語言的唯一書籍。此書有正體中文與簡體中文兩種版本,正體版由全華公司代理販售。

在英文的網站堙A那就多如牛毛了,最是有名的,應該屬於『 Iczelion's Win32 Assembly Homepage』這個網站,堶惘陰q初學者到高手必經的教材,有志於此的人當然不可放過,可惜它是個英文網站,對我們來說總有一些隔閡,但也不用氣餒,在羅云彬的編程樂園網站堣w經有翻譯好的文章可供參考,再加上一些正簡體轉換軟體,輕而易舉就可變成您熟悉的文字。

其實,這堣]是一塊臥虎藏龍之地,程式高手不乏其人,但想來他們大概是太忙了,無暇為初學者寫初級的入門文章。小木偶不自量力,想為 Win32 組合語言撰寫正體中文的網頁,這個網頁參考上述網頁及 Win32 C++ 相關書籍,希望諸位前輩先進能不吝指教,為組合語言程式設計投注心力,方能使敝網頁能更增可讀性,或許未來能有人因敝網頁受益,進而更上一層樓,寫出中文的殺手級軟體,則敝人亦與有榮焉。


所需工具及軟體

在 Win32 平台撰寫 32 位元的組合語言程式至少要有三種工具:組譯器與連結器、除錯器、參考資料。

組譯器與連結器:MASM32 v7.0 或 TASM v5.0

在 Win32 平台上撰寫組合語言,至少有兩種組譯器可以選擇:一是 Borland 的 TASM 5.0,一是微軟的 MASM 6.11 以後的版本。微軟的 MASM 並沒有附帶定義程式庫的包含檔,也沒有其他支援工具,必須到微軟所發佈的各個工具包去收集,因此早期要使用 MASM 去撰寫 Win32 程式是件很麻煩的事。幸好後來微軟開放免費下載 MASM 6.11d ( 參考 DOS 的組合語言準備工作:註一,但這堨u能得到 ML.EXE 與 ML.ERR 兩個檔案而已,版本是 6.11d ),又有一位前輩,Steve Hutchesson,為 MASM 定義程式庫與包含檔,並且再包含了許多文件、範例、工具程式等,再把這些檔案包裝成 MASM32 v7.0,使得用組合語言寫出 Win32 程式並不再是件難如登天的事。由於他無私的付出及努力,使得以組合語言撰寫 Win32 程式變得相當方便了,我輩就不須不斷的去查各種常數,我們應該要感謝這種為大眾付出的人。因為小木偶本身的習慣再加上網路上所能找到的資料較其他組譯器來得多,所以小木偶將以 MASM32 7.0 為主介紹在 Win32 平台上撰寫組合語言,對於使用 TASM 的人,只好說聲抱歉了。

MASM32 v7.0 並不是微軟出品的,而是 Steve Hutchesson 集合了微軟 MASM v6.11d 與所需工具包裝而成,專門用來撰寫 Win32 程式的開發工具。現在 ( 民國 100 年之前,即西元 2011 年之前 ) 市面上大部分講述組合語言的書籍,仍使用微軟的 MASM,請讀者不要與 MASM32 搞混了。MASM32 7.0 可以到 hutch's home page 下載取得。下載後,將它解壓縮並執行其中的 INSTALL.EXE 就可以了 ( 其實 MASM32V7.ZIP 只有一個檔案,INSTALL.EXE ),INSTALL.EXE 會要您選擇安裝在那一台硬碟機,如下圖:

MASM32 v7.0 安裝畫面

然後它自己會進行安裝作業,過一陣之後會自動出現 DOS 模式的視窗,待 INSTALL.EXE 自己執行好再關閉這個 DOS 模式視窗即可,如下圖:

重建程式庫畫面

安裝到最後,INSTALL.EXE 還會告訴您可以參考您剛才安裝磁碟機下的『masm32』子目錄內,有個『README.HTM』檔的說明,如下左圖。最後安裝完畫面是像下面右圖。

除錯器:Soft-ICE 或 OllyDbg

安裝好組譯器以及安排好環境後,還要有除錯器以找出錯誤。最有名的除錯器是 NuMega 公司出品的 Soft-ICE。這套除錯器功能強大,可以到笨冬瓜的網頁去抓取。

安裝 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 字串所代表的意義:

  1. LINES:LINES 後面所接的是十進位數,這個數字表示 Soft-ICE 視窗總共顯示的行數。
  2. WC:WC 後面也是接一個十進位數,它表示程式碼視窗的行數。Soft-ICE 的視窗分成暫存器視窗、資料區視窗窗、程式碼視窗及交談視窗。
  3. WD:設定資料視窗大小。
  4. SET FONT:SET FONT 後面接設定字形,用阿拉伯數字 1、2、3 表示字形種類。
  5. SET CODE:可以接 ON 或 OFF 兩種選項,分別表示於程式碼視窗顯示或不顯示機械碼。
  6. X:離開 Soft-ICE 視窗,作用相當於在 Soft-ICE 視窗堳鬗U Ctrl-D 鍵。假如最後沒有 X 的話,啟動 Win 9x 後,Soft-ICE 視窗會自動跳出來。

每個 Soft-ICE 命令之後用『;』連接,在小木偶的電腦堿O

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 堸鶡瑼滌ˋ躨飽A它也是很有名的,OllyDebug,是 Oleh Yuschuk 所撰寫的共享軟體,可以到 Ollydbg 網站中下載。下載完成後只需要解壓縮到一個子目錄堙A不須安裝就可以使用了,您只要在命令模式下切換到 OllyDebug 所解壓縮的子目錄下,執行 OLLYDBG.EXE 即可。更方便的做法是在桌面上建立 OllyDebug 的捷徑,以後要執行 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 的參數是大小寫有別,所以必須依照上面的大小寫,否則會產生錯誤。前兩行是告訴電腦到那堨h找包含檔以及程式庫,第三行是告訴電腦到那堨h找組譯器 ( 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

這四行,其中的意義與上面相同,您注意到前三行都有一對『%』包含著環境變數,這是為了不改變他們。存檔之後,將滑鼠移到『開始』→『所有程式』→『附屬應用程式』→『命令提示字元』上,按下滑鼠右鍵後會蹦現一個選單,選擇其中的『內容』之後再選擇『捷徑』標籤,出現下圖:

CMD.EXE 的內容
,然後修改『目標』,在後面加上『 /KC:\AUTOEXEC.BAT』( 即上圖中藍色標示的文字 )。這樣一旦您執行『命令提示字元』,就會把適當的環境設定好。


Win32 程式設計簡介

Win 32 程式是在保護模式下執行的,這表示您不能像在 DOS 作業系統一樣,可以任意存取任意位址的資料 ( DOS 系統及其大部分的應用程式都是在真實模式下執行的 ),在 Win 32 作業系統堛熊{式那些可以被讀取而不能寫入,那些不能讀取也不能寫入,那些能讀取也能寫入都由作業系統指定,所有程式都要遵守,否則 Windows 就給你一個藍色畫面終止程式執行。

Section

在 Win 32 作業系統堙A記憶體的定址方式不再用『段暫存器:偏移位址』來表示了,因為在 Win32 作業系統塈鴷 32 位元的暫存器來定址,32 位元可以定址 4GB 的記憶體空間 ( 也就是 232=4,294,967,296 個位元組 )。在 4GB 這麼大的記憶空間,Windows 作業系統是把它看成由 00 到 4GB 連續的空間,而應用程式要存取某一位址之資料,作業系統能將之轉換成實際記憶體所在位址,這種記憶體模式稱為平坦模式 ( flat mode ),而 Win 32 所能用的模式也只有這一種。當然為了管理記憶體 ( 實現剛才所說的那一塊區域只可讀不可寫,那一塊能讀能寫 ),Windows 作業系統還是把記憶體分成許多『段』(section),此處的段與 DOS 的段 ( segment ) 不同,此處的段可以超過 64KB,而且不需要為它指定段暫存器,系統會自動分辨。當一個段的結束就是另一個段的開始。

Win 32 作業系統的段 (section) 只有兩種,一是程式碼段,另一種是資料段。程式碼段是用『.code』來定義的,在程式碼段的程式碼是不可改寫的,這點和 DOS 不同,如果有程式想改寫自己的程式碼,將會得到錯誤訊息。如果您使用『.code』來定義一個區段,組譯器會自行假設區段名為『_TEXT』。如果您想更改區段名稱,可以用下面的方式:

區段名  SEGMENT 'CODE'
區段名  ENDS

資料段還可再分為三種:

  1. .data:這是定義已經有初始值的變數所在的資料區段,在這種資料段堛瘍僂えO可以被讀取,也可以被改寫的。如果您用『.data』定義一個區段,組譯器會自行假設區段名稱為『_DATA』。
  2. .data?:這是定義尚未有初始值的變數所在的資料區段,在這種資料段堛瘍僂えO可以被讀取,也可以被改寫的。如果您用『.data?』定義一個區段,組譯器會自行假設區段名稱為『_BSS』。
  3. .const:這是定義常數資料段,在這種資料段內的變數只能被讀取,無法被更改。

如果您想更改資料區段名稱,可以用下面的方式:

區段名  SEGMENT
區段名  ENDS

視窗各部名稱

以小作家為例,一個視窗的模樣長得像下圖:

一個視窗大致可分為非工作區 ( Non-Client Area ) 與工作區 ( client area,也有人翻成客戶區 )。非工作區是大部分視窗常見的部份,包含視窗最上面的標題欄,標題欄上的圖示 (icon ),標題欄右邊的最大化、最小化、還原、關閉按鈕,標題欄左邊的系統功能表以及邊框。工作區則包含許多彈出選單的主選單 ( menu ),由許多按鈕組成的工具欄,下面輸出或輸入訊息區域和最下面是狀態列 ( 顯示工作時的情況 )。

保存暫存器

在 Win32 作業系統堙A還有一條很重要的規則必須遵守,那就是: Windows 在內部頻繁使用 ESI,EDI,EBP,EBX 暫存器,而不去檢查這些暫存器的值是否被更改,所以當您要使用這些暫存器時,必須先保存它們,待用完後再恢復,否則會引起當機。

資料型態與變數命名

在 DOS 時代寫組合語言的資料型態很簡單,常用的就只有位元組 ( byte )、字組 ( word )、雙字組 ( double word )、十個位元組 ( ten bytes ) 等不到十種,都在資料段定義,定義方式分別是 DB、DW、DD、DT 等 ( 在 MASM 6.0 版以後,可以用 BYTE、WORD、DWORD、TBYTE 來定義,其意義和 DB、DW、DD、DT 等相同)。但是在 Win 32 環境下的資料型態似乎很複雜,除了剛剛所提到的之外,還有 HINSTANCE、HWND、LPSTR……等數百種 ( 也可能數千種 )。其實這些資料型態只不過是為了可讀性的關係,當看到沒看過的資料型態不用緊張,只要知道『喔,有這種資料型態』就好了,至於它所代表的長度幾乎都是雙字組,假如您想確定的話,可以開啟您所安裝的 MASM32 資料夾堛 INCLUDE 子目錄的 WINDOWS.INC 檔案。例如有兩個資料形態,HINSTANCE 和 HWND,您想看看到底是什麼?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 表示這是一個視窗代碼的資料型態,這兩種資料型態其實都是雙字組。

資料形態很容易可以由字面上猜出來是什麼?例如底下四個資料形態:LPSTR、LPCSTR、LPWSTR、LPCWSTR,其中的「LP」代表長程指標 ( long pointer ),「STR」代表字串 ( string ),「C」代表常數 ( constant ),「W」代表寬字元 ( wide character ),所以 LPSTR 就表示字串變數的位址,LPCSTR 表示字串常數位址。

除了資料型態採用這種有系統的表示方法外,在 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 堙C

事件驅動與訊息驅動

不管在 DOS 或 Win32 系統的執行的程式,都會接收到使用者是否按下鍵、是否移動滑鼠或者按下滑鼠上的按鈕等等動作,這些動作稱之為事件。在 DOS 環境下的程式,必須由程式不斷的檢查這些事件,而程式必須針對這些事件作出處理來。

而 Win 32 是多工系統,每次使用者觸發這些事件時,由系統判斷使用者做出這些事件時是在那一個程式或那一個視窗堙A然後再由系統把該事件發生時的一些資料變成一個結構體,稱為訊息傳遞給該程式或視窗,通知該程式有事情必須處理了。所以我們撰寫的程式必須有一個接收來自系統訊息的迴圈,也必須有一段程式來處理這些事件。這就是所謂的事件驅動或訊息驅動。

不管是 Win32 也好或是 DOS 程式也好,相同的地方是都必須針對使用者的動作事先設計好如何處理。不同的地方是 Win32 的程式是被動地通知要處理的訊息,而 DOS 程式則是主動地檢查訊息。兩者在觀念上不太相同,是剛踏入 Win32 程式設計者難以接受的。

好了,底下我們就進入第一章先看看一個簡單的程式,但是實際真正牽涉到訊息的 Win32 程式是在第二章。


回到首頁到第一章