<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Decode360's Blog

    業(yè)精于勤而荒于嬉 QQ:150355677 MSN:decode360@hotmail.com

      BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 ::  :: 管理 ::
      302 隨筆 :: 26 文章 :: 82 評(píng)論 :: 0 Trackbacks
    ??? 最近想初步了解一下匯編的內(nèi)容,在網(wǎng)上搜了搜,發(fā)現(xiàn)一篇寫(xiě)得很不錯(cuò)的文章,特地轉(zhuǎn)過(guò)來(lái)留存。寫(xiě)得淺顯易懂,而且加入了很多個(gè)人的見(jiàn)解,比書(shū)上寫(xiě)的好懂多了。比較欽佩作者,可惜找了半天沒(méi)有找到這篇文章的原作者是誰(shuí)。轉(zhuǎn)載地址:http://www.zxbc.cn/html/20070611/22772.html
    ?
    ?
    ?
    ?
    學(xué)習(xí)匯編前你應(yīng)該知道的知識(shí)
    ?

    1、匯編需要什么工具和程序,到哪里下載?

    ??? 目前階段,匯編程序僅需要兩個(gè)程序就夠了。masm.exe,link.exe。 前者是編譯程序,后者是鏈接程序。另外,為了驗(yàn)證和調(diào)試程序,還需要一個(gè)程序debug.exe,該程序由windows本身就提供。
    ??? 將二者下載后,放到某一個(gè)目錄中(任意目錄都可以),考慮到很多命令需要通過(guò)鍵盤(pán)敲入,所以建議你不要把文件放入到長(zhǎng)文件名目錄、中文目錄或很深的目錄中。比如你可以建一個(gè)“D:\Masm”目錄,并建議此后的程序都放這個(gè)目錄,此后稱(chēng)這個(gè)目錄為匯編目錄。
    ?
    2、學(xué)習(xí)匯編需要有哪些編程方面的知識(shí)?
    ?
    ??? 沒(méi)有任何編程方面的知識(shí),學(xué)習(xí)此語(yǔ)言等于緣木求魚(yú),所以請(qǐng)放棄學(xué)習(xí)的想法。一般來(lái)說(shuō)至少要知道如下幾點(diǎn):
    ??? *)程序的運(yùn)行邏輯結(jié)構(gòu)有順序(按語(yǔ)句依次執(zhí)行)、分支結(jié)構(gòu)(IF...THEN...ELSE...),循環(huán)結(jié)構(gòu)(FOR...NEXT)三種結(jié)構(gòu)。
    ??? *)知道什么是子程序,什么是調(diào)用。
    ??? *)匯編程序員的視角。不同編程視角編程要求是不一樣的。比如刪除文件:
    ??????? >>用戶的視角是找到“刪除”按鈕或菜單,然后單擊一下即可。
    ??????? >>高級(jí)程序員的視角是知道刪除的文件,并發(fā)出刪除命令。這些通過(guò)API實(shí)現(xiàn)。
    ??????? >>匯編程員的視角是得到要?jiǎng)h除的文件名,找到該文件所在位置,通過(guò)調(diào)用刪除“中斷命令”進(jìn)行刪除。
    ??????? >>操作系統(tǒng)開(kāi)發(fā)人員的視角則是接到刪除命令后,先找到系統(tǒng)根目錄區(qū),由根目錄區(qū)的鏈接依次找到子目錄區(qū),直到找到要?jiǎng)h除的文件,然后按照操作系統(tǒng)刪除文件的規(guī)則對(duì)該文件名進(jìn)行修改。比如DOS,只把第一個(gè)字符改成"?"。
    ?
    ??? 按程序語(yǔ)句等價(jià)的角度看,一行VB的打印語(yǔ)句,用匯編實(shí)現(xiàn)大約需要一百二十多行。知道匯編語(yǔ)言的視角后就要知道,前面的道路是坎坷的,沒(méi)有耐心是不行的。想通過(guò)幾分鐘幾行程序就完成很復(fù)雜的操作不是件容易的事。
    ?
    3、學(xué)匯編有什么用?
    ?
    ??? 匯編產(chǎn)生于DOS時(shí)代或更早,而現(xiàn)在是Windows時(shí)代,所以可能 遺憾地說(shuō):盡管還有批牛人在用匯編開(kāi)發(fā)核心級(jí)程序,但我們幾乎沒(méi)什么用,除了必要時(shí)間能拿來(lái)分析一兩個(gè)程序的部分代碼之外,別的也就沒(méi)干什么用了。并且并不是所有的匯編命令都能在windows下使用。而泛泛地追求“時(shí)髦”而學(xué)本語(yǔ)言,最后的結(jié)果是損了夫人又折兵。所以學(xué)之前你要考慮好。我勸那些為了當(dāng)“黑客”而學(xué)匯編的人就此止步。
    ?
    ?
    第零講 預(yù)備知識(shí)
    ?
    ?
    1、一個(gè)匯編程序的編譯過(guò)程是怎么樣的?

    ??? 1)首先你需要找一個(gè)編輯器,編輯器用任何“純文本”編輯器都可以。比如記事本。編好以后保存到匯編目錄中。擴(kuò)展名為asm,比如myfirst.asm。但這里建議你找一個(gè)能顯示出當(dāng)前行的編譯器。這樣出錯(cuò)后排錯(cuò)很容易。
    ??? 2)然后在DOS下進(jìn)入D:\Masm目錄中,輸入“masm myfirst.asm",如果有錯(cuò)系統(tǒng)會(huì)提示出錯(cuò)的行位置和出錯(cuò)原因。
    ??? 3)然后再輸入“l(fā)ink myfirst.obj”,即可看到當(dāng)前目錄下有一個(gè)myfirst.exe程序。
    ?
    2、宏匯編和匯編有什么區(qū)別嗎?
    ?
    ??? 二者的區(qū)別在于前者提供宏,后者不提供。后者已找不到了,所以你可以認(rèn)為二者沒(méi)有區(qū)別。
    ?
    3、機(jī)器語(yǔ)言、匯編語(yǔ)言、高級(jí)語(yǔ)言的關(guān)系

    ??? 最早的計(jì)算機(jī)采用機(jī)器語(yǔ)言,這種語(yǔ)言直接用二進(jìn)制數(shù)表示,通過(guò)直接輸入二進(jìn)制數(shù),插拔電路板等實(shí)現(xiàn),這種“編程”很容易出錯(cuò),每個(gè)命令都是通過(guò)查命令表實(shí)現(xiàn),既然是通過(guò)“查表”實(shí)現(xiàn)的,那當(dāng)然也可以讓計(jì)算機(jī)來(lái)代替人查表實(shí)現(xiàn)了。于是就產(chǎn)生了匯編語(yǔ)言,所以不管別人怎么定義機(jī)、匯語(yǔ)言,我就認(rèn)為,二者是等價(jià)。后來(lái)人們發(fā)現(xiàn),用匯編語(yǔ)言編某一功能的時(shí)候,連續(xù)一段代碼都是相同或相似,于是就考慮用一句語(yǔ)言來(lái)代替這一段匯編語(yǔ)言,于是就產(chǎn)生了高級(jí)語(yǔ)言。因此,所有高級(jí)語(yǔ)言都能轉(zhuǎn)化成匯編語(yǔ)言,而所以匯編語(yǔ)言又可轉(zhuǎn)化成機(jī)器語(yǔ)言。反之,所有機(jī)器語(yǔ)言可以轉(zhuǎn)成匯編語(yǔ)言(因?yàn)槎叩葍r(jià))。但并不是所以匯編語(yǔ)言都能轉(zhuǎn)成高級(jí)語(yǔ)言。
    ?
    4、計(jì)算機(jī)的組成

    ??? 通常都把計(jì)算機(jī)定義成五部分:運(yùn)算器、控制器、存儲(chǔ)器、輸入系統(tǒng)、輸出系統(tǒng)。
    ??? 為了簡(jiǎn)單起見(jiàn),我們?nèi)绱死斫猓哼\(yùn)算器+控制器=CPU。存儲(chǔ)器=內(nèi)存(暫不包括外存,也不包括CACHE)。輸入系統(tǒng)=鍵盤(pán)(不包括鼠標(biāo)),輸出系統(tǒng)=顯示器(不包括打印機(jī),繪圖儀)。
    ?
    5、寄存器和內(nèi)存的區(qū)別

    ??? 寄存器在CPU中。內(nèi)存在內(nèi)存條中。前者的速度比后者快100倍左右。后面的程序要求每條指定要么沒(méi)有內(nèi)存數(shù)據(jù),要么在有一個(gè)寄存器的參與下有一個(gè)內(nèi)存數(shù)據(jù)。(也就是說(shuō),不存在只訪問(wèn)內(nèi)存的指令)。
    ?
    6、匯編語(yǔ)言的計(jì)數(shù)

    ??? 與生活中的計(jì)數(shù)不一樣,匯編中的計(jì)數(shù)是從0開(kāi)始的。比如16個(gè)計(jì)數(shù),則是從0~15,而不是生活中的1~16。這一點(diǎn)看起來(lái)簡(jiǎn)單,真運(yùn)算起來(lái)就不是件容易的事了,不信等著瞧。
    ?
    7、進(jìn)制問(wèn)題
    ?
    ??? 又與生活中不一樣的地方是進(jìn)制。切記下面的常識(shí):
    ??? *)計(jì)算機(jī)內(nèi)部存儲(chǔ)都用二進(jìn)制。
    ??? *)我們的匯編源程序默認(rèn)都用十進(jìn)制。(除非你指明類(lèi)型)
    ??? *)我們用的調(diào)試程序debug默認(rèn)的都是十六進(jìn)制。(無(wú)法指明其他類(lèi)型)
    ??? 其中十六進(jìn)制的十六個(gè)個(gè)位數(shù)依次是:0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F。
    ?
    8、進(jìn)制轉(zhuǎn)換

    ??? 一個(gè)比較簡(jiǎn)單的方法是查表法。
    ??? 十進(jìn)制?十六進(jìn)制?二進(jìn)制
    ????? 0????? 0????? 0000
    ????? 1????? 1????? 0001
    ????? 2????? 2????? 0010
    ????? 3????? 3????? 0011
    ????? 4????? 4????? 0100
    ????? 5????? 5????? 0101
    ????? 6????? 6????? 0110
    ????? 7????? 7????? 0111
    ????? 8????? 8????? 1000
    ????? 9????? 9????? 1001
    ????? 10????A????? 1010
    ????? 11????B????? 1011
    ????? 12????C????? 1100
    ????? 13????D????? 1101
    ????? 14????E????? 1110
    ????? 15????F????? 1111
    ??
    ??? 好了,結(jié)合6,7,8三條。大家來(lái)算一個(gè)“題”。某一組數(shù)據(jù)顯示時(shí),每個(gè)數(shù)據(jù)占了四個(gè)位置,每行共十六個(gè)。問(wèn):十六進(jìn)制的13位置在哪里(第幾行,第幾列)。
    ??? 格式如下:m m m m n n n n o o o o p p p p '注:之所以沒(méi)用ABC是怕與上面十六進(jìn)制弄混。
    ??????????? r r r r s s s s t t t t u u u u
    ?
    ?
    第一講 基礎(chǔ)知識(shí)
    ?

    1、訪問(wèn)內(nèi)存

    ??? 程序在內(nèi)存中,訪問(wèn)內(nèi)存是幾乎每一程序都要進(jìn)行的操作,計(jì)算機(jī)對(duì)內(nèi)存編址是線性的,也就是說(shuō)是一維的,比如256M的內(nèi)存,地址就應(yīng)該是從0~(256M-1),這個(gè)地址稱(chēng)為物理地址或絕對(duì)地址。

    ? 1.1 地址表示
    ?
    ??? 但從匯編程序員的角度看,內(nèi)存卻是二維的,要說(shuō)明一個(gè)地址,需要給出兩個(gè)值,就象你在平面上指定一點(diǎn)需要說(shuō)出(X,Y)坐標(biāo)一樣,匯編程序員的內(nèi)存視角也需要兩個(gè)“坐標(biāo)”,前一個(gè)稱(chēng)為段地址(Segment),后一個(gè)稱(chēng)為偏移地址(Offset),該地址稱(chēng)為邏輯地址。
    ??? 比如“1234:3DF5”就是一個(gè)地址?!?F3F:”不是一個(gè)地址,因?yàn)樗挥卸蔚刂罚瑳](méi)有偏移地址。注意此后的地址都用十六進(jìn)制表示。
    ?
    ? 1.2 地址計(jì)算
    ?
    ??? 前面提到,計(jì)算機(jī)編址是一維的,匯編程序員是二維的,那么二者怎么換算呢?由后者到前者的換算方法是,“段地址串”后面加個(gè)“0”,然后再加上偏移地址。
    ??? 比如“1234:3DF5”(十六進(jìn)制的加減運(yùn)算參見(jiàn)相關(guān)資料)
    ??? 12340? --串后加了一個(gè)0
    ???? 3DF5
    ??? -----
    ??? 16135??--注意此串仍然是十六進(jìn)制。
    ??? 所以,匯編程序員眼中的地址“1234:3DF5”就是物理地址(計(jì)算機(jī)編址):16135。
    ??? 知道了由后者向前者的轉(zhuǎn)換,那么由前者向后者的轉(zhuǎn)換呢?
    ??? “不知道”,為什么不知道,繼續(xù)往下看。
    ?
    ? 1.3 到底哪個(gè)地址對(duì)
    ?
    ??? 知道了1.2的地址算法后,我又發(fā)現(xiàn)一個(gè)問(wèn)題:
    ??? “1000:6135”的物理地址是多少呢? 10000+6135=16135。
    ??? “1001:6125”的物理地址呢? 10010+6125=16135。
    ??? ......
    ??? 那么到底哪個(gè)對(duì)呢?問(wèn)題的回答是這樣的:假設(shè)我現(xiàn)在讓你按一下“L”鍵,我可以告訴你如下幾種方法中的一種或幾種。1 請(qǐng)按一下“L”鍵; 2請(qǐng)按一下鍵盤(pán)上第四行第十個(gè)鍵;3 請(qǐng)按一下第十列中的第四個(gè)鍵;4 請(qǐng)按一下“K”右邊的鍵;5 按標(biāo)準(zhǔn)指法單擊一下右手無(wú)名指。
    ?
    ??? 舉上面的例子也就是說(shuō),同一個(gè)地址有很多種表示方式,具體用哪一種,要看實(shí)際使用時(shí)的情況。但無(wú)論用哪種方式,只要能達(dá)到目的即可。(實(shí)際中該問(wèn)題一般不會(huì)受此問(wèn)題困擾,但初學(xué)時(shí)突然想不通)。
    ?
    ? 1.4 有多少內(nèi)存可以訪問(wèn)
    ?
    ??? 無(wú)論是段地址還是偏移地址都是四位十六進(jìn)制(如果不夠四位,前面補(bǔ)0)。也就是說(shuō):總共可以訪問(wèn)的地址說(shuō)是:0000:0000~FFFF:FFFF。 總共FFFF0+FFFF+1=10FFF0個(gè)地址。也就是不到1M的空間。
    ??? 記住如下結(jié)論:
    ??? *) 不管你實(shí)際內(nèi)存有多少,目前我們只能訪問(wèn)不到1M的空間。
    ??? *) 而實(shí)際上連這1M也用不完。其中上端的384K的址只能讀不能寫(xiě),只能讀,一般稱(chēng)為ROM。
    ??? *) 低端的640K可以讀寫(xiě)。但這640K的低端100多K也不能隨便寫(xiě),因此DOS系統(tǒng)使用該區(qū)。
    ??? *) 原來(lái)1024M的內(nèi)存,匯編程序只能使用其中400多K。這段內(nèi)存的容易相當(dāng)于一個(gè)普通文檔的大小。不過(guò)這就足夠了。

    2、DEBUG的使用
    ?
    ??? 先記住以下兩個(gè)命令:D命令和Q命令。前者是顯示內(nèi)存內(nèi)容,后者是退出DEBUG命令。

    ------------------------以下為抄別的人內(nèi)容----------------------
    ??? DEBUG.EXE程序是專(zhuān)門(mén)為分析、研制和開(kāi)發(fā)匯編語(yǔ)言程序而設(shè)計(jì)的一種調(diào)試工具,具有跟蹤程序執(zhí)行、觀察中間運(yùn)行結(jié)果、顯示和修改寄存器或存儲(chǔ)單元內(nèi)容等多種功能。它能使程序設(shè)計(jì)人員或用戶觸及到機(jī)器內(nèi)部,因此可以說(shuō)它是80X86CPU的心靈窗口,也是我們學(xué)習(xí)匯編語(yǔ)言必須掌握的調(diào)試工具。
    ?
    ??? 1) DEBUG程序使用
    ?
    ??? 在DOS提示符下鍵入命令:
    ??? C>DEBUG [盤(pán)符:][路徑][文件名.EXE][參數(shù)1][參數(shù)2]
    ?
    ??? 這時(shí)屏幕上出現(xiàn)DEBUG的提示符“-”,表示系統(tǒng)在DEBUG管理之下,此時(shí)可以用DEBUG進(jìn)行程序調(diào)試。若所有選項(xiàng)省略,僅把DEBUG裝入內(nèi)存,可對(duì)當(dāng)前內(nèi)存中的內(nèi)容進(jìn)行調(diào)試,或者再用N和L命令,從指定盤(pán)上裝入要調(diào)試的程序;若命令行中有文件名,則DOS把DEBUG程序調(diào)入內(nèi)存后,再由DEBUG將指定的文件名裝入內(nèi)存。
    ?
    ??? 2) DEBUG的常用命令
    ?
    ???????? (1)退出命令 Q
    ??????? 格式:Q
    ??????? 功能:退出DEBUG,返回到操作系統(tǒng)。
    ????????(2)顯示存儲(chǔ)單元命令 D
    ??????? 格式1:D[起始地址]
    ??????? 格式2:D[起始地址][結(jié)束地址|字節(jié)數(shù)]
    ??????? 功能:格式1從起始地址開(kāi)始按十六進(jìn)制顯示80H個(gè)單元的內(nèi)容,每行16個(gè)單元,共8行,每行右邊顯示16個(gè)單元的ASCII碼,不可顯示的ASCII碼則顯示“.”。格式2顯示指定范圍內(nèi)存儲(chǔ)單元的內(nèi)容,其他顯示方式與格式1一樣。如果缺省起始地址或地址范圍,則從當(dāng)前的地址開(kāi)始按格式1顯示。
    ??????? 例如:-D 200???????--表示從DS:0200H開(kāi)始顯示128個(gè)單元內(nèi)容
    ?????????????-D 100 120???--表示顯示DS:0100-DS:0120單元的內(nèi)容
    ??????? 說(shuō)明:在DEBUG中,地址表示方式有如下形式:
    ??????? 段寄存器名:相對(duì)地址,如:DS:100
    ??????? 段基值:偏移地址(相對(duì)地址),如:23A0:1500
    --------------------------小抄結(jié)束--------------------------------
    ?
    3、驗(yàn)證第一節(jié)里的內(nèi)容
    ?
    ??? 運(yùn)行“開(kāi)始/程序/附件/MS-DOS命令提示符”(這是win2000,win98下自己找吧)
    ??? 在“-”下輸入D,顯示
    -d
    1398:0100? 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00? ................
    1398:0110? 00 00 00 00 00 00 00 00-00 00 00 00 34 00 87 13? ............4...
    1398:0120? 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00? ................
    1398:0130? 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00? ................
    1398:0140? 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00? ................
    1398:0150? 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00? ................
    1398:0160? 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00? ................
    1398:0170? 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00? ................
    -
    ??? 我們記下:1398:011C的值是個(gè)34。1389:011C的物理地址應(yīng)該是:13A9C。
    ??? 那么1000:3A9C的物理地址也應(yīng)該是13A9C,他的內(nèi)存也應(yīng)該是34,(因?yàn)楸緛?lái)就是一個(gè)地址嗎,就象第三行第十列和第十列第三行當(dāng)然應(yīng)該是同一個(gè)位置)。
    -d 1000:3A9C
    1000:3A90????????????????????????????????????? 34 00 87 13????????????? 4...
    1000:3AA0? 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00? ................
    1000:3AB0? 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00? ................
    1000:3AC0? 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00? ................
    1000:3AD0? 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00? ................
    1000:3AE0? 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00? ................
    1000:3AF0? 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00? ................
    1000:3B00? 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00? ................
    1000:3B10? 00 00 00 00 00 00 00 00-00 00 00 00????????????? ............
    -
    ??? 果然如此,同樣你可以驗(yàn)證:13A9:000C也肯定是指這一個(gè)地址,不信試試。
    ?
    4、DEBUG命令

    ------------------------繼續(xù)小抄----------------------------
    ??? 前面已學(xué)過(guò):顯示存儲(chǔ)單元命令 D
    ??? 再學(xué)一個(gè)命令
    ????(1)修改存儲(chǔ)單元命令 E
    ?
    ??? 格式1:E[起始地址] [內(nèi)容表]
    ??? 格式2:E[地址]
    ??? 功能:格式1按內(nèi)容表的內(nèi)容修改從起始地址開(kāi)始的多個(gè)存儲(chǔ)單元內(nèi)容,即用內(nèi)容表指定的內(nèi)容來(lái)代替存儲(chǔ)單元當(dāng)前內(nèi)容。
    ?
    ??? 例如:-E DS:0100 'VAR' 12 34
    ??? 表示從DS:0100 為起始單元的連續(xù)五個(gè)字節(jié)單元內(nèi)容依次被修改為
    ??? 'V'、'A'、'R'、12H、34H。
    ??? 格式2是逐個(gè)修改指定地址單元的當(dāng)前內(nèi)容。
    ??? 如:-E DS:0010
    ??? 156F:0010 41.5F
    ??? 其中156F:0010單元原來(lái)的值是41H,5FH為輸入的修改值。若只修改一個(gè)單元的內(nèi)容,這時(shí)按回車(chē)鍵即可;若還想繼續(xù)修改下一個(gè)單元內(nèi)容,此時(shí)應(yīng)按空格鍵,就顯示下一個(gè)單元的內(nèi)容,需修改就鍵入新的內(nèi)容,不修改再按空格跳過(guò),如此重復(fù)直到修改完畢,按回車(chē)鍵返回DEBUG“-”提示符。如果在修改過(guò)程中,將空格鍵換成按“-”鍵,則表示可以修改前一個(gè)單元的內(nèi)容。
    ----------------------小抄結(jié)束------------------------------
    ?
    5、使用DOS時(shí),匯編用戶可以從DOS操作系統(tǒng)中得到什么?

    ??? 現(xiàn)在編程,通常很多功能都是通過(guò)調(diào)用系統(tǒng)API。很多高級(jí)語(yǔ)言都直接把這些API包裝起來(lái),以系統(tǒng)接口或函數(shù)的方式提供給用戶,那么匯編函數(shù)都能得到什么呢?
    ??? 首先,匯編用戶有很多東西可以調(diào)用。他們主要是:
    ?
    ??? 5.1 BIOS提供的接口?,F(xiàn)在硬件與軟件的區(qū)分已越來(lái)越不明顯,很多硬件不僅僅是電路,而還要提供一些固化寫(xiě)入硬件的一部分“程序”,這些程序以ROM的方式出現(xiàn),匯編用戶最大的好處就是可以直接使用這些“程序”,這些使用不僅功能強(qiáng)大,而且效率非常高。
    ?
    ??? 5.2 DOS功能調(diào)用,作為操作系統(tǒng)也象BIOS一樣向用戶提供了相應(yīng)的“程序”。這些程序在很大程序上擴(kuò)充了BIOS。與BIOS不同的是,這部分程序放在內(nèi)存中,它可以被修改。而B(niǎo)IOS中不能再修改。
    ??? ==========================================================
    ??? 以上兩種接口都通過(guò)一種相同的格式調(diào)用,這些程序統(tǒng)稱(chēng)為“中斷”,現(xiàn)在先不要理解中斷的本意,你現(xiàn)在可以認(rèn)為是系統(tǒng)提供給你的函數(shù)。
    ??? ============================================================
    ?
    ??? 5.3 系統(tǒng)共享數(shù)據(jù)區(qū)。編過(guò)程序的人都知道全局變量的好處,全局變量方便之外在于任何函數(shù)、過(guò)程都可以調(diào)用、讀取、修改。全局變量不足之處是危險(xiǎn)性,有一個(gè)過(guò)程改了這個(gè)變量值,其它的也得跟著改變了。DOS操作系統(tǒng)同樣也提供了這樣的共享數(shù)據(jù)區(qū),該區(qū)是整個(gè)系統(tǒng)的共享區(qū),任何程序都可以查找、修改。當(dāng)然,修改某處必然會(huì)對(duì)其它程序造成影響。
    ?
    6、再談中斷

    ??? 前面5.2已提到中斷了,現(xiàn)在問(wèn)題是不同硬件不一樣,即使相同硬件的ROM,不同版本,各個(gè)BIOS中斷程序所處的位置也不一樣。DOS中斷也一樣,不同版本、不同配置,在內(nèi)存位置也不一樣。那么你使用某一個(gè)中斷,系統(tǒng)怎么知道你使用的那個(gè)中斷程序在哪呢?
    ??? 為了解決這一問(wèn)題,DOS會(huì)在啟動(dòng)的時(shí)候,把所有這些(BIOS和DOS)中斷的首地址保存到一個(gè)地址。這個(gè)地址很容易記,這段地址是內(nèi)存的絕對(duì)零地址(0000:0000)。前面已講過(guò),每個(gè)地址在匯編程序員角度來(lái)看是二維的,也就是分為段地址和偏移地址。每個(gè)地址各占兩個(gè)字節(jié),所以要表示這個(gè)二維地址需要4個(gè)字節(jié)。所以每個(gè)中斷首地址由4個(gè)字節(jié)表示。一共256個(gè)中斷,占用了1024個(gè)字節(jié)的位置。
    ??? 另外需要注意的是,這4個(gè)表示地址的字節(jié),數(shù)據(jù)是由低向高的。比如12 34 56 78所表示的地址是:7856:3412。
    ?
    ??? 一般用INT M表示中斷M,如果M是十六進(jìn)制,則在后面加上一個(gè)H。比如19號(hào)中斷,十六進(jìn)制應(yīng)該是13H。所以該中斷就是INT 13H。

    7、再談系統(tǒng)共享數(shù)據(jù)區(qū)

    ??? 該共享數(shù)據(jù)區(qū)在絕對(duì)地址:0040:0000開(kāi)始。
    ?
    8、驗(yàn)證我上面說(shuō)的內(nèi)容
    ?
    ??? 8.1 找中斷
    ?
    ??? 運(yùn)行DEBUG后。輸入D 0000:0000。顯示絕對(duì)零地址的內(nèi)容。
    C:\>debug
    -d 0:0
    0000:0000? 68 10 A7 00 8B 01 70 00-16 00 9B 03 8B 01 70 00? h.....p.......p.
    0000:0010? 8B 01 70 00 B9 06 0E 02-40 07 0E 02 FF 03 0E 02? ..p.....@.......
    0000:0020? 46 07 0E 02 0A 04 0E 02-3A 00 9B 03 54 00 9B 03? F.......:...T...
    0000:0030? 6E 00 9B 03 88 00 9B 03-A2 00 9B 03 FF 03 0E 02? n...............
    0000:0040? A9 08 0E 02 99 09 0E 02-9F 09 0E 02 5D 04 0E 02? ............]...
    0000:0050? A5 09 0E 02 0D 02 DC 02-B8 09 0E 02 8B 05 0E 02? ................
    0000:0060? 02 0C 0E 02 08 0C 0E 02-13 0C 0E 02 AD 06 0E 02? ................
    0000:0070? AD 06 0E 02 A4 F0 00 F0-37 05 0E 02 71 84 00 C0? ........7...q...
    -u 0070:018B
    0070:018B 1E??????????? PUSH??? DS
    0070:018C 50??????????? PUSH??? AX
    0070:018D B84000??????? MOV????AX,0040
    0070:0190 8ED8????????? MOV????DS,AX
    0070:0192 F70614030024? TEST??? WORD PTR [0314],2400
    0070:0198 754F????????? JNZ????01E9
    0070:019A 55??????????? PUSH??? BP
    0070:019B 8BEC????????? MOV????BP,SP
    0070:019D 8B460A??????? MOV????AX,[BP+0A]
    0070:01A0 5D??????????? POP????BP
    0070:01A1 A90001??????? TEST??? AX,0100
    0070:01A4 7543????????? JNZ????01E9
    0070:01A6 A90002??????? TEST??? AX,0200
    0070:01A9 7422????????? JZ????? 01CD
    ??? 首先,D命令把中斷首地址顯示出來(lái)。每4個(gè)表示一個(gè)地址。其中INT 0的中斷首地址為:00A7:1068,INT 1的中斷地址為:0070:018B.......0070:018B是中斷3的首地址。后面那個(gè)U命令就表示顯示該地址的“中斷程序”的內(nèi)存。
    ?
    ??? 你們可以試著找找INT 13H的位置在哪。
    ?
    ??? 8.2 驗(yàn)證系統(tǒng)共享數(shù)據(jù)區(qū)
    ?
    ??? 系統(tǒng)共享數(shù)據(jù)區(qū)內(nèi)容極為豐富,我實(shí)在記不住哪么多了。我曾記在一個(gè)本上,可惜那個(gè)本早在N年前(3<N<6)就丟了。兄弟們誰(shuí)找到這個(gè)地址的內(nèi)容,一定要貼上來(lái),這里有東西可以讓大家眼界大開(kāi)。
    ??? 前幾年,我用的286計(jì)算機(jī)是黑白顯示器(555555~~~~~~~~~,別嫌我老、舊、慢呀),可當(dāng)時(shí)有個(gè)游戲非要彩顯,不是彩顯不讓運(yùn)行。我就是改了這個(gè)區(qū)的某一個(gè)位,讓哪游戲“以為”我用的是彩顯,于是游戲能用了。雖然不好看,但總能用。
    ??? 在DOS下,你每按一個(gè)鍵,系統(tǒng)都會(huì)記下來(lái),下面我們一起找找這個(gè)鍵盤(pán)緩沖區(qū)的地址。知道這個(gè)地址,你就可以作一個(gè)“虛擬”鍵盤(pán),通過(guò)發(fā)命令來(lái)模擬某個(gè)人在按鍵。這個(gè)地址位于:0040:001E。 其中每個(gè)鍵有兩個(gè)字節(jié),一個(gè)字節(jié)是ASCII碼,一個(gè)是掃描碼。共16個(gè)。
    ?
    C:\>debug
    -d 40:0
    0040:0000? F8 03 F8 02 E8 03 E8 02-BC 03 78 03 78 02 80 9F? ..........x.x...
    0040:0010? 22 C8 00 80 02 28 00 00-00 00 2A 00 2A 00 20 39? "....(....*.*. 9
    0040:0020? 34 05 30 0B 3A 27 30 0B-0D 1C 64 20 20 39 34 05? 4.0.:'0...d? 94.
    0040:0030? 30 0B 3A 27 30 0B 0D 1C-71 10 0D 1C 64 20 00 00? 0.:'0...q...d ..
    0040:0040? A2 00 C3 00 A2 AF 09 E1-C8 03 50 00 00 10 00 00? ..........P.....
    0040:0050? 00 18 00 00 00 00 00 00-00 00 00 00 00 00 00 00? ................
    0040:0060? 0F 0C 00 D4 03 29 30 7F-03 00 C0 00 A1 B7 11 00? .....)0.........
    0040:0070? 00 00 00 00 00 00 00 00-14 14 14 00 01 01 01 01? ................
    -d 0040:0000
    0040:0000? F8 03 F8 02 E8 03 E8 02-BC 03 78 03 78 02 80 9F? ..........x.x...
    0040:0010? 22 C8 00 80 02 28 00 00-00 00 2A 00 2A 00 3A 27? "....(....*.*.:'
    0040:0020? 30 0B 30 0B 30 0B 30 0B-0D 1C 64 20 20 39 30 0B? 0.0.0.0...d? 90.
    0040:0030? 30 0B 30 0B 30 0B 08 0E-08 0E 34 05 30 0B 00 00? 0.0.0.....4.0...
    0040:0040? 1F 00 C3 00 A2 AF 09 E1-C8 03 50 00 00 10 00 00? ..........P.....
    0040:0050? 00 18 00 00 00 00 00 00-00 00 00 00 00 00 00 00? ................
    0040:0060? 0F 0C 00 D4 03 29 30 7F-03 00 C0 00 24 B8 11 00? .....)0.....$...
    0040:0070? 00 00 00 00 00 00 00 00-14 14 14 00 01 01 01 01? ................
    -
    ??? 既然是鍵盤(pán)緩沖區(qū),每個(gè)輸入的鍵都會(huì)顯示在該區(qū)中,第一次我只輸入了“d 40:0”,所以你可以在此后顯示數(shù)據(jù)右邊字符中找到這些字符,注意是間隔開(kāi)的。
    ??? 第二次我輸入“d 0040:0000”,則右邊顯示的是“d 0040:0000”的內(nèi)容。你可以找找。
    ?
    ?
    第二講 內(nèi)存映象
    ?

    ??? 之所以把這個(gè)內(nèi)存單獨(dú)放一章,是為了說(shuō)明它的重要性,后面的幾乎很多程序都需要你對(duì)這一章的理解。這里的內(nèi)存映象就是指當(dāng)你把一個(gè)可執(zhí)行文件(EXE或COM文件)放到內(nèi)存后,整個(gè)內(nèi)存“看”起來(lái)是什么樣子的。
    ?
    ??? 前面講過(guò),這里匯編程序只能訪問(wèn)1M的內(nèi)存空間,所以下面就以1M內(nèi)存為例。并且以DOS操作系統(tǒng)作為講解對(duì)象,所以所編出來(lái)的程序也僅是DOS程序。事實(shí)上,通過(guò)winasm可以訪問(wèn)遠(yuǎn)遠(yuǎn)超過(guò)1M的空間,并且可以編出FOR windows的程序。但那是另外的話題。我們暫且不說(shuō)那些。

    2.1內(nèi)存映象

    ??? 首先,這1M內(nèi)存如果我們不再以二維的方式看,而是一維的,線性地看(二維和一維的轉(zhuǎn)化方式參見(jiàn)前面章節(jié))。但描述還是以二維的方式描述,從最底端到最高端依次是:
    ??? 1) 中斷向量區(qū):該區(qū)由0000:0000~0000:03FF。這里存著系統(tǒng)的所有中斷的中斷向量表。對(duì)于中斷向量表,你現(xiàn)在先理解為一些程序的首地址。由這個(gè)地址你就能找到該程序。
    ??? 2) 系統(tǒng)數(shù)據(jù)區(qū):該區(qū)由0040:0000~0040:XXXX (不好意思,忘了),這里存著整個(gè)系統(tǒng)中,DOS操作系統(tǒng)要用的數(shù)據(jù),由于這個(gè)區(qū)的數(shù)據(jù)對(duì)用戶是開(kāi)放的,所以用戶當(dāng)然也可以從這里讀出來(lái)用。
    ??? 3) DOS操作系統(tǒng)區(qū):操作系統(tǒng)常駐內(nèi)存,你向計(jì)算機(jī)發(fā)的每個(gè)命令其實(shí)都是操作系統(tǒng)執(zhí)行的。這個(gè)區(qū)的大小主要是由操作系統(tǒng)的版本和用戶的配置大小決定,如果是驅(qū)動(dòng)程序配置,就放到根目錄下的config.sys里,如果是程序,就放到autoexec.bat里。這里設(shè)置在現(xiàn)在的windows 95/98/nt/me/2000/xp/2003中仍然有,所以我就不多說(shuō)了。
    ??? 4) 用戶程序:這個(gè)當(dāng)然就是你執(zhí)行的程序了,這種程序分兩種,一種是擴(kuò)展名為com文件,一種是exe文件。從程序內(nèi)部看,前者程序的四個(gè)段重合(后面要講這四個(gè)段),所以最大長(zhǎng)度只等于一個(gè)段,用前面段地址的理解就是com文件最大只能是64K,所以com文件只適合小的程序。而exe,四個(gè)段可任何分配,并可擴(kuò)充段,而且每個(gè)段的段地址可以任何改動(dòng),因此exe的訪問(wèn)內(nèi)存能力大多了。這種格式訪問(wèn)能力只受地址結(jié)構(gòu)的限制了。
    ??? 用戶程序所占的內(nèi)存大小完全由程序本身決定,但最大,只能到640K。這一點(diǎn),怪不得別人,只能怪當(dāng)前計(jì)算機(jī)軟硬件設(shè)置高手高手高高手們(包括比爾蓋茨)們的失誤了,60年代的超級(jí)計(jì)算機(jī)只有36K的內(nèi)存,所以他們就在80年代得到一個(gè)結(jié)論:640K的內(nèi)存足夠了。
    ??? 如果用戶程序大于由操作系統(tǒng)所占內(nèi)存的頂?shù)椎?40K之間的內(nèi)存量,就會(huì)顯示:內(nèi)存不夠,因而程序不能執(zhí)行。這種現(xiàn)象對(duì)于一開(kāi)始就用windows的人來(lái)說(shuō),幾乎沒(méi)見(jiàn)過(guò),但對(duì)于一開(kāi)始用DOS并打漢字的人來(lái)說(shuō),再正常不過(guò)。如果小于這段內(nèi)存,多余部分就空著。
    ??? 5) 從640K到1M-64K,這段內(nèi)存就很難說(shuō)清了。這段內(nèi)存中有一部分被硬件占有,有一部分是顯示緩沖區(qū)點(diǎn)有,還有一部分是系統(tǒng)ROM占有。
    ??? 6) 從1M-64K到1M之間的這段64K的內(nèi)存叫作HMA。這段內(nèi)存是小孩沒(méi)娘,說(shuō)來(lái)話長(zhǎng),我們先不說(shuō)他。
    ???
    2.2 驗(yàn)證上面的理論
    ?
    ??? 2.2.1 中斷向量表
    ?
    ??? 中斷向量表就是所有中斷向量首地址表,這里保存著每個(gè)中斷程序的首地址,幾乎所有的匯編書(shū)都把中斷放到后面的章節(jié)中,并且對(duì)中斷的解釋也僅從字面意思解釋?zhuān)詫?dǎo)致大學(xué)對(duì)中斷的不重要和誤解。沒(méi)耐心的沒(méi)到這個(gè)章節(jié)就不學(xué)匯編了,有耐心的到這里才豁然開(kāi)朗。我現(xiàn)在不講中斷的原意。我直接告訴你,你把中斷當(dāng)成API也許更合適。也就是說(shuō),別人把很多已作好的功能放到了內(nèi)存中。并且把調(diào)用這一功能的號(hào)告訴了你,你只要調(diào)用這些功能號(hào),系統(tǒng)就自動(dòng)從這個(gè)中斷向量表中找到對(duì)應(yīng)的中斷,然后執(zhí)行你的功能。
    ?
    ??? 首先讓你感受一下中斷的魅力一下吧。比如中斷21H的2A功能調(diào)用是讀取系統(tǒng)的日期,這個(gè)調(diào)用的規(guī)則是,調(diào)用前AH寄存器置為2A。調(diào)用后年在CX中,月在DH中,DL在日中,星期在AL中。
    -a
    139D:0100 mov ah,2a
    139D:0102 int 21
    139D:0104 int 3
    139D:0105
    -g=100
    ?

    AX=2A05? BX=0000? CX=07D4? DX=0C18? SP=FFEE? BP=0000? SI=0000? DI=0000
    DS=139D? ES=139D? SS=139D? CS=139D? IP=0104? NV UP EI PL NZ NA PO NC
    139D:0104 CC??????????? INT??? 3
    -

    ??? 可能上面的程序你目前還看不懂。不過(guò)沒(méi)關(guān)系,“mov ah,2a”表示調(diào)用功能號(hào)是2a的API?!癷nt 21”表示調(diào)用十六進(jìn)制21號(hào)中斷,“int 3”表示3號(hào)中斷,表示程序運(yùn)行到這一句時(shí)停一下。“g=100”表示從“139D:0100 ”開(kāi)始執(zhí)行。
    ??? AX=2A05? BX=0000? CX=07D4? DX=0C18? SP=FFEE? BP=0000? SI=0000? DI=0000
    ??? DS=139D? ES=139D? SS=139D? CS=139D? IP=0104? NV UP EI PL NZ NA PO NC
    ??? 表示執(zhí)行的結(jié)果。其中CX是年,這個(gè)年是由CX中存。07D4十進(jìn)制就是2004年。DH+DL=DX,所以DH=0C,DL=18。二者轉(zhuǎn)化為十進(jìn)制就是DH=12,DL=24,也就是今天了。AX=AH+AL=2A05,所以AL=05。那就是今天是星期五。
    ??? 上面可能你們現(xiàn)在還看不懂,不過(guò)通過(guò)解說(shuō)你應(yīng)該可以知道,僅僅兩行命令,就讀到了現(xiàn)在的值?,F(xiàn)在需要作的就是把這些值提取出來(lái)用作他用了。
    ?
    ??? 從中斷的作來(lái)與中斷向量表又有什么關(guān)系呢?原來(lái)你在匯編里運(yùn)行int 21時(shí),系統(tǒng)就在上面的中斷向量表中找到int 21的中斷地址,該中斷的地址應(yīng)該位于: 0000:0084~0000:0087,具體算法前面已說(shuō)明了。
    -d 0000:0084 0087
    0000:0080????????????? 7C 10 A7 00????????????????????????????? |...
    -

    ??? 找到內(nèi)容是:00A7:107C。然后系統(tǒng)就轉(zhuǎn)到這個(gè)地址執(zhí)行int 21。
    ?
    ??? 2.2.2 系統(tǒng)數(shù)據(jù)區(qū)
    ?
    ??? 前面都已說(shuō)明過(guò)。不再多說(shuō)。系統(tǒng)區(qū),很多DOS中斷程序?qū)崿F(xiàn)部分就在這個(gè)區(qū)。程序運(yùn)行區(qū)依不同的程序而不用。
    ?
    ??? 2.2.3 640K~1M之間
    ?
    ??? 這期間有些地方是ROM,有些地方是硬件的BIOS區(qū)。我僅以?xún)蓚€(gè)例子說(shuō)明這一區(qū)。
    ?
    ??? ROM區(qū) :ROM區(qū)就是只讀內(nèi)存,也就是說(shuō)這個(gè)區(qū)的數(shù)據(jù)只能讀不能寫(xiě)。比如F000:0000開(kāi)始的內(nèi)存是ROM。我們來(lái)寫(xiě)一下,然后再看看效果。
    ?
    -d f000:0000 0005??--顯示由F000:0000到F000:0005的六個(gè)字節(jié)值
    F000:0000? 04 E8 A2 FF F9 C3??????????????????????????????? ......
    -e f000:0000????--修改命令
    F000:0000? 04.00? E8.00? A2.00? FF.00? F9.00? C3.00?? --注意,.后面的是我改的,把這幾個(gè)值都改成0了。
    ?
    -d f000:0000 0005?? --再次顯示這個(gè)區(qū)的數(shù)據(jù)。
    F000:0000? 04 E8 A2 FF F9 C3??????????????????????????????? ......
    -
    ??? 通過(guò)上面測(cè)試,發(fā)現(xiàn)該區(qū)數(shù)據(jù)仍然未改變。但你要是試別的RAM區(qū)的,肯定會(huì)變。如果想試你自己試試吧。
    ?
    ??? 顯示緩沖區(qū) :在文本方式下,B800:0000開(kāi)始的地址保存著屏幕上每個(gè)字符位置的值。在文本方式下,屏幕被分為80×25。每個(gè)位置有兩個(gè)值,一個(gè)值是ASCII字符,一個(gè)值是該ASCII的屬性值(主要是顏色)。所以一個(gè)屏幕共有80×25×2=400個(gè)字符。
    ??? 我們來(lái)改:
    -d b800:0000 0010?? --顯示屏幕緩沖區(qū)的內(nèi)容,注意此時(shí)本行最左邊的“-”是屏幕左上角。
    B800:0000? 2D 07 64 07 20 07 62 07-38 07 30 07 30 07 3A 07? -.d. .b.8.0.0.:.
    B800:0010? 30??????????????????????????????????????????????? 0
    -
    ??? 看上面的命令,屏幕最上邊一行是“-d b800:0000 0010”,所以他的內(nèi)容就是“2D 07 64 07 20 07 62 07-38 07 30 07 30 07 3A 07”其中,2D是“-”的ASCII值,07是“-”的屬性值。64是“d”的ASCII值,07是“d”的屬性值。。。。。
    ??? 現(xiàn)在修改這些值。我把左上角的字改成黃顏色的“-”,那當(dāng)然是改b800:0001的屬性值了。
    -e b800:0001? 0e
    ??? 是不是左上角的顏色變成黃色了嗎?
    ??? 好了,把第二個(gè)字符變成綠色的“-”吧?
    -e b800:0002 2d 0b
    ??? 變了嗎?
    ?
    2.3 可執(zhí)行文件內(nèi)存映象
    ?
    ??? DOS下可執(zhí)行文件有兩種(BAT是批處理文件,他只是簡(jiǎn)單調(diào)用DOS內(nèi)部命令或其它程序,所以此處不認(rèn)為它是可執(zhí)行文件):一種是COM文件,一種是EXE文件,前面提到,COM文件一般小于64K。EXE文件則可以任意大。為什么呢?
    ??? 說(shuō)到這里,還要提到段。每個(gè)段64K。段的作用就是數(shù)據(jù)組織單位。段的類(lèi)型有三種:代碼段(Code Segment,簡(jiǎn)稱(chēng)CS)、數(shù)據(jù)段(Data Segment,簡(jiǎn)稱(chēng)DS)、棧段(Stack Segment,簡(jiǎn)稱(chēng)SS),另外還有一個(gè)附加數(shù)據(jù)段(Extra Segment,簡(jiǎn)稱(chēng)ES),它的用與數(shù)據(jù)段DS可以認(rèn)為完全一樣,當(dāng)數(shù)據(jù)段的64K不夠用,或你就需要把數(shù)據(jù)放到兩個(gè)段中以便移動(dòng)、復(fù)制、比較時(shí),才用到附加數(shù)據(jù)段ES。(當(dāng)然,移動(dòng)、復(fù)制、比較操作在一個(gè)段中也可以完成)。
    ?
    ??? 1.段的作用
    ?
    ??? 1.1 代碼段(CS):程序裝入內(nèi)存中,DOS怎么知道是從哪里執(zhí)行呢?答案就是系統(tǒng)自動(dòng)從代碼段指定位置開(kāi)始執(zhí)行,并且始終在代碼段中執(zhí)行。因此代碼段CS的作用就是保存所有的指令。這里所說(shuō)的代碼也就是匯編指令了。所以編寫(xiě)匯編程序也就主要是編寫(xiě)代碼段中的代碼。
    ?
    ??? 1.2 數(shù)據(jù)段(DS)、附加段(ES):顧名思義,數(shù)據(jù)段中存的就是數(shù)據(jù),這些數(shù)據(jù)供代碼段的程序調(diào)用。附加段就是附加數(shù)據(jù)段。作用與數(shù)據(jù)段相同。
    ?
    ??? 1.3 棧段(SS):這個(gè)段非常重要,但實(shí)際上,你在使用中,似乎用不著這個(gè)段,但實(shí)際上,這是黑客編程中最重要的一部分,而且系統(tǒng)會(huì)不停地“偷偷地”使用這個(gè)段,正是這個(gè)偷偷地用,使得系統(tǒng)的很多動(dòng)作被記錄到這個(gè)段中。還有兩點(diǎn),你必須記?。阂皇侨绻闶褂昧诉@個(gè)棧,比如你把數(shù)據(jù)存到這個(gè)棧中,則必須有相應(yīng)的出棧命令,并且入幾個(gè)數(shù)據(jù),就得出幾個(gè)數(shù)據(jù),多一個(gè)或少一個(gè),你的程序就可能導(dǎo)致死機(jī)或異常;二是你要把握操作時(shí)機(jī),比如你不能在系統(tǒng)使用棧的前后使用棧,比如你在調(diào)用子程序之前入棧,而在子程序中出棧,而在系統(tǒng)調(diào)用子程序時(shí),系統(tǒng)也要使用棧,這種也將導(dǎo)致出錯(cuò)。
    ??? 棧就是一種先入后出(也有稱(chēng)為后入先出)的結(jié)構(gòu),有地址由小到大的增加棧,有地址由大到小的逆向減棧。
    ?
    ??? 2.段重疊
    ?
    ??? 從上面,我們可以看到,CS,DS,SS三者作用各不相同,內(nèi)存就是象錄音磁帶,錄新歌,則舊歌被刪,帶子上存的始終是最后錄的那段音樂(lè)。因此,如果重疊則必然相互沖突。那還能重疊嗎?
    ??? 這里所說(shuō)的重疊不是指內(nèi)容重疊,而是指概念上的重疊,即數(shù)據(jù)相互放到一個(gè)段中,但相互可以區(qū)分開(kāi)。比如某一段既有數(shù)據(jù)也有代碼,則代碼在每要執(zhí)行到數(shù)據(jù)之前加一個(gè)跳轉(zhuǎn)指令跳過(guò)這段代碼。這個(gè)跳轉(zhuǎn)指令要求用戶在編程的時(shí)候加上。
    ??? 而棧段呢?棧段有自己的特殊性,特殊就在于系統(tǒng)也會(huì)自動(dòng)地使用,而用戶則又在不知道系統(tǒng)在使用的情況下使用。避免這種沖突的方法就是采用逆向的棧段。
    ?
    ???? 3 .COM文件內(nèi)存映象

    ??? COM文件被讀到內(nèi)存中后,該文件的前100H個(gè)字節(jié)被操作系統(tǒng)使用,操作系統(tǒng)使用這256個(gè)字節(jié)保存一些系統(tǒng)要使用的數(shù)據(jù),匯編語(yǔ)言編程者不能在這里存自己的數(shù)據(jù),但在知道這此數(shù)據(jù)的作用后可以使用其中的數(shù)據(jù)。從100H開(kāi)始,就是程序的開(kāi)始了。COM文件之所以最大只能有64K,其原因是COM文件的四個(gè)段是相互重疊的。也就是說(shuō),CS、DS、SS、ES四個(gè)段的地址都指向這個(gè)COM文件的100H處。程序代碼、數(shù)據(jù)、棧都在由100H到64K的區(qū)域內(nèi)。如何把三者分開(kāi)呢?棧段采用逆向棧,這個(gè)棧由64K開(kāi)始,隨著數(shù)據(jù)入棧,則地址就減小。這樣作的好處是,棧段由高端向低端進(jìn)展,可以詳細(xì)與數(shù)據(jù)、代碼分開(kāi);壞處也不言而喻,假如一個(gè)COM程序大量用到棧(比如是個(gè)遞歸程序)因此棧就不停地降低,而程序代碼本身也很多,甚至不停地申請(qǐng)新空間,這樣數(shù)據(jù)和棧就會(huì)在中間碰頭,導(dǎo)致程序被破壞。
    ??? 區(qū)分開(kāi)數(shù)據(jù)代碼段與棧段后,下面討論把數(shù)據(jù)段和代碼段也分開(kāi)。這個(gè)簡(jiǎn)單的多,只要邏輯上分開(kāi)就可以。不過(guò)一般的方法就是:在100H處放一個(gè)跳轉(zhuǎn)指令,隨后放數(shù)據(jù),然后再放置其它的代碼。而100H處的跳轉(zhuǎn)指令就跳到這里。
    ??? 因此,COM文件內(nèi)存映象就是:
    ??? CS:0000??? (由于COM的CS,DS,SS,ES三段重疊,因此此行前CS,寫(xiě)成DS,SS,ES都一樣)??
    ??? CS:0100??? 一個(gè)跳轉(zhuǎn)到Y(jié)YYY地址的跳轉(zhuǎn)指令。
    ??? CS:0101??? 本程序所需要用到的數(shù)據(jù)
    ??? CS:XXXX??? 數(shù)據(jù)結(jié)束處。
    ??? CS:YYYY??? 程序代碼保存處。
    ??? CS:ZZZZ??? 程序代碼結(jié)束處。
    ??? CS:FFFF??? 棧段開(kāi)始處(注意棧是地址越來(lái)越小,所以這里是開(kāi)始而不是結(jié)束處),也是程序的結(jié)束處。另外,此處FFFF與前面XXXX,YYYY,ZZZZ不一樣,這里是十六進(jìn)制的64K。
    ?
    ??? 4.EXE文件

    ??? 比起COM文件,EXE文件要復(fù)雜一些,他的復(fù)雜就在于COM文件前面規(guī)定了100H個(gè)字節(jié)用于系統(tǒng)使用,而EXE文件則有個(gè)文件頭,文件頭的大小看具體內(nèi)容多少。文件頭的內(nèi)容使得EXE看起來(lái)復(fù)雜了,但也更靈活了。更重要的是,對(duì)于病毒設(shè)計(jì)者,這個(gè)文件頭使他們?nèi)玺~(yú)得水。因?yàn)槲募^處
    ??? EXE文件的內(nèi)存映象為:
    ??? XXXX:0000 文件頭
    ??? XXXX:YYYY 文件頭結(jié)束處
    ?
    ??? CS:0000 代碼段開(kāi)始處
    ??? CS:ZZZZ 代碼段結(jié)束處
    ?
    ??? DS:0000 數(shù)據(jù)碼段開(kāi)始處
    ??? DS:WWWW 數(shù)據(jù)碼段結(jié)束處
    ?
    ??? SS:0000 棧段開(kāi)始處
    ??? SS:UUUU 棧段結(jié)束處
    ?
    ??? ES:0000 附加段開(kāi)始處
    ??? ES:VVVV 附加段結(jié)束處
    ?
    ??? 說(shuō)明:
    ??? 1) 上述ES可以沒(méi)有,要看實(shí)際需要
    ??? 2) CS,DS,ES,SS的順序也是看編程者是怎么安排的,好在用戶不必關(guān)心他的具體位置。
    ??? 3) 由上可見(jiàn),CS,DS,ES,SS的段地址肯定保存到了文件頭中。
    ??? 4) 由上可見(jiàn),實(shí)際執(zhí)行的只是CS,因此DS,ES,SS的首地址,CS肯定要想辦法知道。:)
    ?
    ?
    第三講 匯編指令
    ?

    3.1 什么是機(jī)器語(yǔ)言
    ?
    ??? 前面提到“最早的計(jì)算機(jī)采用機(jī)器語(yǔ)言,這種語(yǔ)言直接用二進(jìn)制數(shù)表示,通過(guò)直接輸入二進(jìn)制數(shù),插拔電路板等實(shí)現(xiàn),這種‘編程’很容易出錯(cuò),每個(gè)命令都是通過(guò)查命令表實(shí)現(xiàn)”。
    ??? 比如要執(zhí)行21號(hào)中斷,需要查表,得到21號(hào)中斷的指令就是CD 21。這樣不管你通過(guò)什么方式,在內(nèi)存指令位置,寫(xiě)入兩個(gè)字節(jié),一個(gè)是CD(這可不是音樂(lè)光盤(pán),而是二進(jìn)制數(shù),轉(zhuǎn)成十進(jìn)制就是205),另一個(gè)是21(同樣是十六進(jìn)制,十進(jìn)制是33)。
    ??? 上面就是機(jī)器語(yǔ)言。
    ?
    3.2 什么是匯編語(yǔ)言

    ??? 前面也提到“既然是通過(guò)‘查表’實(shí)現(xiàn)的,那當(dāng)然也可以讓計(jì)算機(jī)來(lái)代替人查表實(shí)現(xiàn)了。于是就產(chǎn)生了匯編語(yǔ)言”,匯編語(yǔ)言產(chǎn)生的重要目的就是用容易記的符號(hào)來(lái)代替容易出錯(cuò)的二進(jìn)制數(shù)(或十六進(jìn)制數(shù))。
    ??? 比如前面的21號(hào)中斷,機(jī)器語(yǔ)言是CD 21。而匯編語(yǔ)言就規(guī)定中斷用int表示(interrupt的前三個(gè)字母),21號(hào)中斷就成了int 21h。其中21后面的h表示是表示這個(gè)21是十六進(jìn)制。由于大小寫(xiě)不敏感,所以int 21h寫(xiě)成下列方式都等價(jià):
    ??? int 33
    ??? Int 21h
    ??? INT 21H
    ?
    3.3 匯編指令集

    一、數(shù)據(jù)傳輸指令?
    -----------------------------------------------------------------
    它們?cè)诖尜A器和寄存器、寄存器和輸入輸出端口之間傳送數(shù)據(jù).
    1. 通用數(shù)據(jù)傳送指令.
    MOV???? 傳送字或字節(jié).
    MOVSX?? 先符號(hào)擴(kuò)展,再傳送.
    MOVZX?? 先零擴(kuò)展,再傳送.
    PUSH??? 把字壓入堆棧.
    POP???? 把字彈出堆棧.
    PUSHA?? 把AX,CX,DX,BX,SP,BP,SI,DI依次壓入堆棧.
    POPA??? 把DI,SI,BP,SP,BX,DX,CX,AX依次彈出堆棧.
    PUSHAD?把EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI依次壓入堆棧.
    POPAD?? 把EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX依次彈出堆棧.
    BSWAP?? 交換32位寄存器里字節(jié)的順序
    XCHG??? 交換字或字節(jié).( 至少有一個(gè)操作數(shù)為寄存器,段寄存器不可作為操作數(shù))
    CMPXCHG 比較并交換操作數(shù).( 第二個(gè)操作數(shù)必須為累加器AL/AX/EAX )
    XADD??? 先交換再累加.( 結(jié)果在第一個(gè)操作數(shù)里 )
    XLAT??? 字節(jié)查表轉(zhuǎn)換.
    BX????? 指向一張 256 字節(jié)的表的起點(diǎn), AL 為表的索引值 (0-255,即0-FFH); 返回 AL 為查表結(jié)果. ( [BX+AL]->AL )
    2. 輸入輸出端口傳送指令.
    IN?I/O端口輸入. ( 語(yǔ)法: IN 累加器, {端口號(hào)│DX} )
    OUT I/O端口輸出. ( 語(yǔ)法: OUT {端口號(hào)│DX},累加器 )
    輸入輸出端口由立即方式指定時(shí), 其范圍是 0-255; 由寄存器 DX 指定時(shí),
    其范圍是 0-65535.
    3. 目的地址傳送指令.
    LEA 裝入有效地址.
    例: LEA DX,string ;把偏移地址存到DX.
    LDS 傳送目標(biāo)指針,把指針內(nèi)容裝入DS.
    例: LDS SI,string ;把段地址:偏移地址存到DS:SI.
    LES 傳送目標(biāo)指針,把指針內(nèi)容裝入ES.
    例: LES DI,string ;把段地址:偏移地址存到ES:DI.
    LFS 傳送目標(biāo)指針,把指針內(nèi)容裝入FS.
    例: LFS DI,string ;把段地址:偏移地址存到FS:DI.
    LGS 傳送目標(biāo)指針,把指針內(nèi)容裝入GS.
    例: LGS DI,string ;把段地址:偏移地址存到GS:DI.
    LSS 傳送目標(biāo)指針,把指針內(nèi)容裝入SS.
    例: LSS DI,string ;把段地址:偏移地址存到SS:DI.
    4. 標(biāo)志傳送指令.
    LAHF 標(biāo)志寄存器傳送,把標(biāo)志裝入AH.
    SAHF 標(biāo)志寄存器傳送,把AH內(nèi)容裝入標(biāo)志寄存器.
    PUSHF 標(biāo)志入棧.
    POPF 標(biāo)志出棧.
    PUSHD 32位標(biāo)志入棧.
    POPD 32位標(biāo)志出棧.

    二、算術(shù)運(yùn)算指令
    -----------------------------------------------------------------
    ADD?加法.
    ADC?帶進(jìn)位加法.
    INC?加 1.
    AAA?加法的ASCII碼調(diào)整.
    DAA?加法的十進(jìn)制調(diào)整.
    SUB?減法.
    SBB?帶借位減法.
    DEC?減 1.
    NEC?求反(以 0 減之).
    CMP?比較.(兩操作數(shù)作減法,僅修改標(biāo)志位,不回送結(jié)果).
    AAS?減法的ASCII碼調(diào)整.
    DAS?減法的十進(jìn)制調(diào)整.
    MUL?無(wú)符號(hào)乘法.
    IMUL 整數(shù)乘法.?
    ???? --以上兩條,結(jié)果回送AH和AL(字節(jié)運(yùn)算),或DX和AX(字運(yùn)算),

    AAM?乘法的ASCII碼調(diào)整.
    DIV?無(wú)符號(hào)除法.
    IDIV 整數(shù)除法.?
    ???? --以上兩條,結(jié)果回送: 商回送AL,余數(shù)回送AH, (字節(jié)運(yùn)算);
    ???????????????????? 或 商回送AX,余數(shù)回送DX, (字運(yùn)算).
    AAD?除法的ASCII碼調(diào)整.
    CBW?字節(jié)轉(zhuǎn)換為字. (把AL中字節(jié)的符號(hào)擴(kuò)展到AH中去)
    CWD?字轉(zhuǎn)換為雙字. (把AX中的字的符號(hào)擴(kuò)展到DX中去)
    CWDE 字轉(zhuǎn)換為雙字. (把AX中的字符號(hào)擴(kuò)展到EAX中去)
    CDQ?雙字?jǐn)U展. (把EAX中的字的符號(hào)擴(kuò)展到EDX中去)
    ?
    三、邏輯運(yùn)算指令
    -----------------------------------------------------------------
    AND?與運(yùn)算.
    OR?? 或運(yùn)算.
    XOR?異或運(yùn)算.
    NOT?取反.
    TEST 測(cè)試.(兩操作數(shù)作與運(yùn)算,僅修改標(biāo)志位,不回送結(jié)果).
    SHL?邏輯左移.
    SAL?算術(shù)左移.(=SHL)
    SHR?邏輯右移.
    SAR?算術(shù)右移.(=SHR)
    ROL?循環(huán)左移.
    ROR?循環(huán)右移.
    RCL?通過(guò)進(jìn)位的循環(huán)左移.
    RCR?通過(guò)進(jìn)位的循環(huán)右移.?
    ???? --以上八種移位指令,其移位次數(shù)可達(dá)255次.
    移位一次時(shí), 可直接用操作碼. 如 SHL AX,1.
    移位>1次時(shí), 則由寄存器CL給出移位次數(shù).
    如 MOV CL,04
    SHL AX,CL
    ?
    四、串指令
    -----------------------------------------------------------------
    DS:SI 源串段寄存器 :源串變址.
    ES:DI 目標(biāo)串段寄存器:目標(biāo)串變址.
    CX??? 重復(fù)次數(shù)計(jì)數(shù)器.
    AL/AX 掃描值.
    D標(biāo)志?0表示重復(fù)操作中SI和DI應(yīng)自動(dòng)增量; 1表示應(yīng)自動(dòng)減量.
    Z標(biāo)志?用來(lái)控制掃描或比較操作的結(jié)束.
    MOVS?串傳送.
    ( MOVSB 傳送字符. MOVSW 傳送字. MOVSD 傳送雙字. )
    CMPS?串比較.
    ( CMPSB 比較字符. CMPSW 比較字. )
    SCAS?串掃描.
    把AL或AX的內(nèi)容與目標(biāo)串作比較,比較結(jié)果反映在標(biāo)志位.
    LODS?裝入串.?
    ( 把源串中的元素(字或字節(jié))逐一裝入AL或AX中.?)
    ( LODSB 傳送字符. LODSW 傳送字. LODSD 傳送雙字. )
    STOS?保存串.
    是LODS的逆過(guò)程.
    REP???????? 當(dāng)CX/ECX<>0時(shí)重復(fù).
    REPE/REPZ?? 當(dāng)ZF=1或比較結(jié)果相等,且CX/ECX<>0時(shí)重復(fù).
    REPNE/REPNZ 當(dāng)ZF=0或比較結(jié)果不相等,且CX/ECX<>0時(shí)重復(fù).
    REPC????????當(dāng)CF=1且CX/ECX<>0時(shí)重復(fù).
    REPNC?????? 當(dāng)CF=0且CX/ECX<>0時(shí)重復(fù).
    ?
    五、程序轉(zhuǎn)移指令
    -----------------------------------------------------------------
    1>無(wú)條件轉(zhuǎn)移指令 (長(zhǎng)轉(zhuǎn)移)
    JMP 無(wú)條件轉(zhuǎn)移指令
    CALL 過(guò)程調(diào)用
    RET/RETF過(guò)程返回.

    2>條件轉(zhuǎn)移指令 (短轉(zhuǎn)移,-128到+127的距離內(nèi))
    ( 當(dāng)且僅當(dāng)(SF XOR OF)=1時(shí),OP1<OP2 )
    JA/JNBE 不小于或不等于時(shí)轉(zhuǎn)移.
    JAE/JNB 大于或等于轉(zhuǎn)移.
    JB/JNAE 小于轉(zhuǎn)移.
    JBE/JNA 小于或等于轉(zhuǎn)移.
    以上四條,測(cè)試無(wú)符號(hào)整數(shù)運(yùn)算的結(jié)果(標(biāo)志C和Z).
    JG/JNLE 大于轉(zhuǎn)移.
    JGE/JNL 大于或等于轉(zhuǎn)移.
    JL/JNGE 小于轉(zhuǎn)移.
    JLE/JNG 小于或等于轉(zhuǎn)移.
    以上四條,測(cè)試帶符號(hào)整數(shù)運(yùn)算的結(jié)果(標(biāo)志S,O和Z).
    JE/JZ 等于轉(zhuǎn)移.
    JNE/JNZ 不等于時(shí)轉(zhuǎn)移.
    JC 有進(jìn)位時(shí)轉(zhuǎn)移.
    JNC 無(wú)進(jìn)位時(shí)轉(zhuǎn)移.
    JNO 不溢出時(shí)轉(zhuǎn)移.
    JNP/JPO 奇偶性為奇數(shù)時(shí)轉(zhuǎn)移.
    JNS 符號(hào)位為 "0" 時(shí)轉(zhuǎn)移.
    JO 溢出轉(zhuǎn)移.
    JP/JPE 奇偶性為偶數(shù)時(shí)轉(zhuǎn)移.
    JS 符號(hào)位為 "1" 時(shí)轉(zhuǎn)移.

    3>循環(huán)控制指令(短轉(zhuǎn)移)
    LOOP CX不為零時(shí)循環(huán).
    LOOPE/LOOPZ CX不為零且標(biāo)志Z=1時(shí)循環(huán).
    LOOPNE/LOOPNZ CX不為零且標(biāo)志Z=0時(shí)循環(huán).
    JCXZ CX為零時(shí)轉(zhuǎn)移.
    JECXZ ECX為零時(shí)轉(zhuǎn)移.

    4>中斷指令
    INT 中斷指令
    INTO 溢出中斷
    IRET 中斷返回

    5>處理器控制指令
    HLT 處理器暫停, 直到出現(xiàn)中斷或復(fù)位信號(hào)才繼續(xù).
    WAIT 當(dāng)芯片引線TEST為高電平時(shí)使CPU進(jìn)入等待狀態(tài).
    ESC 轉(zhuǎn)換到外處理器.
    LOCK 封鎖總線.
    NOP 空操作.
    STC 置進(jìn)位標(biāo)志位.
    CLC 清進(jìn)位標(biāo)志位.
    CMC 進(jìn)位標(biāo)志取反.
    STD 置方向標(biāo)志位.
    CLD 清方向標(biāo)志位.
    STI 置中斷允許位.
    CLI 清中斷允許位.

    六、偽指令
    -----------------------------------------------------------------
    DW????? 定義字(2字節(jié)).
    PROC??? 定義過(guò)程.
    ENDP??? 過(guò)程結(jié)束.
    SEGMENT 定義段.
    ASSUME?建立段寄存器尋址.
    ENDS??? 段結(jié)束.
    END???? 程序結(jié)束.
    ?

    3.4 再談寄存器和內(nèi)存的區(qū)別
    ?
    ??? 第零講說(shuō)到“寄存器在CPU中。內(nèi)存在內(nèi)存條中。前者的速度比后者快100倍左右。后面的程序要求每條指定要么沒(méi)有內(nèi)存數(shù)據(jù),要么在有一個(gè)寄存器的參與下有一個(gè)內(nèi)存數(shù)據(jù)。(也就是說(shuō),不存在只訪問(wèn)內(nèi)存的指令)
    ??? 寄存器是在CPU中的存儲(chǔ)器,而內(nèi)存是在內(nèi)存條中的存儲(chǔ)器。CPU訪問(wèn)寄存器,只需要通過(guò)微指令直接就可以訪問(wèn),而訪問(wèn)內(nèi)存則要先經(jīng)過(guò)總線,再由總線到達(dá)內(nèi)存控制器,讀到某單元的內(nèi)存數(shù)據(jù)后放上總線,再傳到CPU中,CPU才能使用。
    ??? 8086系列計(jì)算機(jī)的寄存器,共有14個(gè),每個(gè)都是十六位的。
    ??? AX,BX,CX,DX,SP,BP,SI,DI,CS,DS,SS,ES,IP,F(xiàn)LAGS。
    ??? 其中前四位,每個(gè)可以單位再分成兩個(gè),AX=AH+AL,BX=BH+BL,CX=CH+CL,DX=DH+DL。這些分開(kāi)的每個(gè)都是8位的。
    ??? 這個(gè)分開(kāi)不要理解成平時(shí)語(yǔ)言中的分開(kāi),你可以理解為AX是由AH和AL組合成的,你給AL付值,就意味著同時(shí)給AX的低半部付值。你給AX付值,就意味著同時(shí)改變AH和AL。這樣作的好處是你可以更靈活地控制這個(gè)寄存器。
    ?
    3.5 指令說(shuō)明

    ??? 看了3.3的指令集和3.4的寄存器,是不是已經(jīng)暈了,或者了迷糊?不要急,上面的東西雖然多,我也沒(méi)讓你一下學(xué)會(huì),(其實(shí)有些永遠(yuǎn)也不會(huì)似乎也不是什么大不了的事)。為了應(yīng)付看的懂我后面所說(shuō)的,我把其中的指令挑幾個(gè)重點(diǎn)的,你必須要記住,其它的慢慢學(xué)吧。
    ?
    1.數(shù)據(jù)傳輸指令
    ?
    ??? mov A,B?
    ??? 注意不是move,這個(gè)指令是把B中的數(shù)據(jù)復(fù)制給A,(B中仍保存原狀)。這里的A和B可以是寄存器,可以是內(nèi)存。但可以同時(shí)是寄存器,不能同時(shí)是內(nèi)存。比如
    ?
    ??? mov ax,100; 這是對(duì)的,注意100在這里叫立即數(shù),但這個(gè)數(shù)在編譯系統(tǒng)編譯成exe的時(shí)候保存在內(nèi)存中。如果學(xué)過(guò)別的高級(jí)語(yǔ)言,你就可以理解為這就是賦值語(yǔ)句 Let ax=100/ax:=100;/ax=100。

    2.偽指令

    ??? 偽指令就是不是真的指令,但他同時(shí)又是指令。之所以說(shuō)這樣矛盾的話,是因?yàn)閭沃噶畈皇菣C(jī)器語(yǔ)言的一部分,而是匯編語(yǔ)言的一部分,是你告訴匯編的編譯系統(tǒng)如何去作。
    ??? string DB '這是我的第一個(gè)匯編語(yǔ)言程序$'
    ??? 上面一行指令中,DB就是偽指令,他的作用就是告訴編譯程序,把后面一些數(shù)據(jù)或字符串放到內(nèi)存中。當(dāng)然對(duì)于exe來(lái)說(shuō),已在內(nèi)存中了,就不用“告訴”了。(這就是為什么叫偽指令)。string是你給這段內(nèi)存起的名字,如果你不需要這段內(nèi)存,不起名字也可以,但如果后面要用,當(dāng)然要加上這個(gè)名字。'這是我的第一個(gè)匯編語(yǔ)言程序$'這個(gè)就是要處理的數(shù)據(jù),當(dāng)然你也可以換成別的內(nèi)容,但需要注意的是,要以'$'結(jié)尾,這是匯編的約寫(xiě),即:只是到了$,就認(rèn)為字符串結(jié)束,否則就一直向下找,直到找到一個(gè)$為止。所以這就要求你的字符串中不能有'$',如果必須有,再換別的處理方式,后面再說(shuō)。
    ?
    3.地址傳送指令
    ?
    ??? Lea A,string
    ??? 前面已經(jīng)定義了string,后面要把地址找到,就要用到lea指令。lea是把字符串的地址給A這個(gè)寄存器中,A當(dāng)然可以上前面提到的任意寄存器。注意地址和內(nèi)容的區(qū)別。如果是內(nèi)容就是把string的字符串給A了。(當(dāng)然這也不成立,一個(gè)字符串有很多字節(jié),而一個(gè)寄存器只有兩個(gè)字節(jié))
    ??? 那么從上面也看到了,string代表一個(gè)地址,lea把這個(gè)地址給了A,那這個(gè)地址到底在哪里呢?事實(shí)上這不重要,就象你要把某書(shū)店買(mǎi)書(shū),這個(gè)書(shū)店在哪并不是最重要的,有沒(méi)有你要的書(shū)才是最重要的。所以你前面標(biāo)出string,后面引用就行了,至于這個(gè)地址到底在哪是編譯程序的事,不是你的事。

    4.運(yùn)算指令

    ??? ADD A,N
    ??? 這個(gè)很容易理解吧,寄存器A加上N,把和仍存在A中。類(lèi)似于高級(jí)語(yǔ)言中的let a=a+n/a:=a+n/a+=n。
    ?
    5.串操作指令

    ??? 記住串操作指令表面很復(fù)雜,其實(shí)很簡(jiǎn)單。
    ??? 因?yàn)樗拖笠粋€(gè)復(fù)雜的數(shù)學(xué)公式一樣簡(jiǎn)單,你所要記住的就是公式的格式,使用時(shí)具體套用即可。
    ??? 從一個(gè)地址到另一個(gè)地址的復(fù)制需要注意的是:
    ??? *)把源串段地址給DS。
    ??? *)把源串編址給SI。
    ??? *)把目的串段址給ES。
    ??? *)把目的串偏址給DI。
    ??? *)把要復(fù)制的個(gè)數(shù)給CX,這里可不考慮$了。
    ??? *)把FLAG中的方向標(biāo)志標(biāo)志你要的方向,一個(gè)是順向,另一個(gè)是逆向。
    ??? *)發(fā)送loop movs,scans等命令。

    6.轉(zhuǎn)移指令

    ??? 記住:無(wú)條件轉(zhuǎn)移指令 jmp。等于轉(zhuǎn) jz,不等于時(shí)轉(zhuǎn)jnz
    ?
    7.中斷指令
    ?
    ??? int 中斷號(hào),注意進(jìn)制,默認(rèn)是十進(jìn)制,所以十六進(jìn)制就加h。
    ?
    ??? 好了,上面的指令變成七八個(gè)了,這你不能嫌多了吧,如果再嫌多就不要繼續(xù)向下看了。
    ?
    ?
    第四講 匯編程序
    ?
    ?
    4.1 匯編程序框架
    ?
    ??? data SEGMENT?'數(shù)據(jù)段,編程者可以把數(shù)據(jù)都放到這個(gè)段里
    ??? ....數(shù)據(jù)部分....? '數(shù)據(jù)格式是: 標(biāo)識(shí)符 db/dw 數(shù)據(jù)。
    ??? data ENDS? '數(shù)據(jù)段結(jié)束處。
    ?
    ??? edata SEGMENT?'附加數(shù)據(jù)段,編程者可以把數(shù)據(jù)都放到這個(gè)段里
    ??? ....附加數(shù)據(jù)部分....
    ??? edata ENDS? '附加數(shù)據(jù)段結(jié)束處。
    ?
    ??? code SEGMENT? '代碼段,實(shí)際的程序都是放這個(gè)段里。
    ??????? ASSUME CS:code,DS:data,ES:edata?'告訴編譯程序,data段是數(shù)據(jù)段DS,code段是代碼段CS
    ?
    ??? start:MOV AX,data?'前面的start表示一個(gè)標(biāo)識(shí)位,后面用到該位,如果用不到,就可以不加
    ??????? MOV DS,AX?'這一句與上一行共同組成把data賦值給DS。段寄存器.
    ??????? MOV AX,edata?
    ??????? MOV ES,AX?'與前一句共同組成edata->ES
    ??????? ....程序部分....
    ??????? MOV AX,4C00h? '程序退出,該句內(nèi)存由下一行決定。退出時(shí),要求ah必須是4c。
    ??????? INT 21h
    ??? code ENDS? '代碼段結(jié)束。
    ??? END start? '整個(gè)程序結(jié)束,并且程序執(zhí)行時(shí)由start那個(gè)位置開(kāi)始執(zhí)行。
    ?
    ??? 上面就是一個(gè)程序的框架結(jié)構(gòu)。在這個(gè)結(jié)構(gòu)中,有三個(gè)段,DS、ES、CS。這三個(gè)段分別存數(shù)據(jù)、附加數(shù)據(jù)、代碼段。
    ?
    4.2 編寫(xiě)我們的Hello,world思路

    ??? 開(kāi)始編寫(xiě)我們的第一個(gè)程序。
    ??? 程序要求:顯示一個(gè)“Hello,Mr.286.”怎么樣?
    ??? 思路:
    ??? 1 要顯示一個(gè)字符串,根據(jù)前面我讓你們記的七八個(gè)指令夠嗎?答案是:不僅夠,而且還用不完。
    ??? 首先定義一下總可以吧。
    ?
    ??? hellostr db 'Hello,Mr.286.$'
    ??? 最后的$不要忘了。
    ?
    ??? 2 首先要考慮的問(wèn)題就是找中斷,找到合適的中斷,該中斷就能幫我們完成這個(gè)顯示任務(wù)。我找到(在哪找到的,怎么找到的,別問(wèn)我,到網(wǎng)上或書(shū)上都能找到)
    ??? -------------------------------------------
    ??? 中斷INT 21H功能09H
    ?
    ??? 功能描述: 輸出一個(gè)字符串到標(biāo)準(zhǔn)輸出設(shè)備上。如果輸出操作被重定向,那么,將無(wú)法判斷磁盤(pán)已滿?
    ??? 入口參數(shù): AH=09H
    ??? DS:DX=待輸出字符的地址
    ??? 說(shuō)明:待顯示的字符串以'$'作為其結(jié)束標(biāo)志?
    ??? 出口參數(shù): 無(wú)?
    ??? -------------------------------------------?
    ??? 由上面看到,我們所需要作的就是把DS指向數(shù)據(jù)段,DX指向字符串的地址,AH等于9H,調(diào)用21h中斷。
    ??? mov ds,數(shù)據(jù)段地址
    ??? lea dx,hellostr?'hellostr已在前面1中定義了。
    ??? mov ah,9h
    ??? int 21h
    ??? 由于只要在調(diào)用int 21h之前把準(zhǔn)備的東西準(zhǔn)備齊就行了,所以int 21h前面三行的順序并不重要。
    ?
    ??? 3 退出程序,運(yùn)行完總要退出呀。再查中斷手冊(cè)
    ??? --------------------------------------------
    ??? 中斷INT 21H功能4CH
    ?
    ??? 功能描述: 終止程序的執(zhí)行,并可返回一個(gè)代碼?
    ??? 入口參數(shù): AH=4CH
    ??? AL=返回的代碼?
    ??? 出口參數(shù): 無(wú)
    ??? --------------------------------------------
    ??? mov ah,4Ch
    ??? mov al,0
    ??? int 21h
    ???
    ??? mov ax,4c00h
    ??? int 21h
    ??? 這里需要說(shuō)明的是返回代碼有什么用,返回給誰(shuí)?返回給操作系統(tǒng),因?yàn)槭遣僮飨到y(tǒng)DOS調(diào)用的這個(gè)程序,這個(gè)返回值可以通過(guò)批處理中的errorlevel得到,這里不多說(shuō)明,實(shí)際上操作系統(tǒng)很少處理這一值,因此al你隨便寫(xiě)什么值影響都不大。
    ?
    4.3 程序?qū)崿F(xiàn)

    ??? data SEGMENT
    ??? msg DB 'Hello, Mr.286.$'
    ??? data ENDS
    ?
    ??? code SEGMENT
    ??????? ASSUME CS:code,DS:data
    ??? start:MOV AX,data
    ????????? MOV DS,AX
    ????????? lea dx,msg?
    ????????? mov ah,9h
    ????????? int 21h
    ????????? MOV AX,4C00h
    ????????? INT 21h
    ?
    ??? code ENDS
    ??? END start

    4.4 編譯運(yùn)行

    ??? 把上面程序保存成hello286.asm后,就可以編譯運(yùn)行了。進(jìn)入DOS,進(jìn)入?yún)R編目錄,如果還沒(méi)下載,到前面找下載地址。
    ?
    ??? =================================================
    ??? E:\Download\Masm>masm hello286.asm
    ??? Microsoft (R) Macro Assembler Version 5.00
    ??? Copyright (C) Microsoft Corp 1981-1985, 1987.? All rights reserved.
    ?
    ??? Object filename [hello286.OBJ]:
    ??? Source listing? [NUL.LST]:
    ??? Cross-reference [NUL.CRF]:
    ?
    ????? 50408 + 415320 Bytes symbol space free
    ?
    ????????? 0 Warning Errors
    ????????? 0 Severe? Errors
    ??? 說(shuō)明:上面連續(xù)三個(gè)回車(chē),表示我要的都是默認(rèn)值。下面是零個(gè)警告,零個(gè)嚴(yán)重錯(cuò)誤,(當(dāng)然了,我的程序還敢錯(cuò)嗎?)
    ?
    ??? E:\Download\Masm>link hello286
    ?
    ??? Microsoft (R) Overlay Linker? Version 3.60
    ??? Copyright (C) Microsoft Corp 1983-1987.? All rights reserved.
    ?
    ??? Run File [HELLO286.EXE]:
    ??? List File [NUL.MAP]:
    ??? Libraries [.LIB]:
    ??? LINK : warning L4021: no stack segment
    ?
    ??? 說(shuō)明:三個(gè)回車(chē)仍要默認(rèn),后面有個(gè)警告,沒(méi)有棧段,這個(gè)沒(méi)關(guān)系,沒(méi)有的話系統(tǒng)會(huì)自動(dòng)給一個(gè)。
    ?
    ??? E:\Download\Masm>hello286
    ??? Hello, Mr.286.
    ??? 說(shuō)明:運(yùn)行成功。
    ??? E:\Download\Masm>
    4.4 深度思考
    ??? 4.4.1 是不是數(shù)據(jù)必須放數(shù)據(jù)段,代碼必段放代碼段呢?
    ??? 答,代碼必段放代碼段,否則你怎么執(zhí)行呀?但數(shù)據(jù)也可以放到代碼段,只是程序要作修改。
    ??? code SEGMENT
    ???????? ASSUME CS:code,DS:data
    ???????? msg DB 'Hello, Mr.286.$'
    ??? start:MOV AX,data
    ????????? MOV DS,AX
    ????????? lea dx,msg?
    ????????? mov ah,9h
    ????????? int 21h
    ????????? MOV AX,4C00h
    ????????? INT 21h
    ??? code ENDS
    ??? END start
    ??? 編譯后仍然可以。
    ??? 4.4.2 我編的程序在內(nèi)存中是什么樣子的呢?
    ??? ------------------------------------------------------------------------
    ??? E:\Download\Masm>debug hello286.exe
    ??? -u
    ??? 1420:0000 B81F14??????? MOV??? AX,141F
    ??? 1420:0003 8ED8????????? MOV??? DS,AX
    ??? 1420:0005 8D160000????? LEA??? DX,[0000]
    ??? 1420:0009 B409????????? MOV??? AH,09
    ??? 1420:000B CD21????????? INT??? 21
    ??? 1420:000D B8004C??????? MOV??? AX,4C00
    ??? 1420:0010 CD21????????? INT??? 21
    ??? 1420:0012 FF362421????? PUSH??? [2124]
    ?
    ??? 1420:0016 E87763??????? CALL??? 6390
    ??? 1420:0019 83C406??????? ADD??? SP,+06
    ??? 1420:001C FF362421????? PUSH??? [2124]
    ??? -d 141f:0000 L20
    ??? 141F:0000? 48 65 6C 6C 6F 2C 20 4D-72 2E 32 38 36 2E 24 00? Hello, Mr.286.$.
    ??? 141F:0010? B8 1F 14 8E D8 8D 16 00-00 B4 09 CD 21 B8 00 4C? ............!..L
    ??? -q
    ?
    ??? E:\Download\Masm>
    ??? ------------------------------------------------------------------------------
    ??? 上面是什么呀?還記得前面說(shuō)的嗎?
    ??? 1420:0000 B81F14??????? MOV??? AX,141F
    ????? |? |????? |??????????? |???????|
    ??? 段址:偏址?機(jī)器語(yǔ)言????? mov指令 把段地址的地址(141f)賦值給AX寄存器。
    ?
    ??? 1420:0012后面的是垃圾數(shù)據(jù),不用管它,把上面程序與源程序作一個(gè)比較,看有什么不用,差別在于把標(biāo)號(hào)語(yǔ)言轉(zhuǎn)成實(shí)際地址了。
    ??? 程序前兩行一執(zhí)行,數(shù)據(jù)段地址就變成了141f,而那個(gè)字符串偏移地址在0000,由(LEA??? DX,[0000]看出),所以我用-d 141f:0000 L20(后面L20表示只顯示20個(gè)字節(jié)),就能把段地址顯示出來(lái)了。
    ??? 所以剛才的程序在內(nèi)存中就變成了:
    ??? 141f:0000 Hello, Mr.286.$? ----->這是段地址里的內(nèi)存
    ??? 1420:0000 B81F14??????? MOV??? AX,141F? ------>這是代碼段里的內(nèi)存。data變成了實(shí)際地址
    ??? 1420:0003 8ED8????????? MOV??? DS,AX
    ??? 1420:0005 8D160000????? LEA??? DX,[0000] ------>偏址變成了0000,因?yàn)閷?shí)際上msg也就是從頭開(kāi)始的。當(dāng)然是0了。
    ??? 1420:0009 B409????????? MOV??? AH,09??? ------->注意Debug里,默認(rèn)的是十六進(jìn)制
    ??? 1420:000B CD21????????? INT??? 21
    ??? 1420:000D B8004C??????? MOV??? AX,4C00
    ??? 1420:0010 CD21????????? INT??? 21
    ?

    ?
    ?




    -The End-

    posted on 2009-05-17 23:18 decode360-3 閱讀(11484) 評(píng)論(2)  編輯  收藏 所屬分類(lèi): The Others

    評(píng)論

    # re: 匯編基礎(chǔ)入門(mén)知識(shí) 2009-09-17 23:56 Jassize
    太感謝了 回去慢慢啃了……  回復(fù)  更多評(píng)論
      

    # re: 匯編基礎(chǔ)入門(mén)知識(shí) 2014-05-23 11:00 Crystal_C++
    win7的f000:0000 0005 可以修改、  回復(fù)  更多評(píng)論
      

    主站蜘蛛池模板: 国产自国产自愉自愉免费24区| 亚洲欧洲日产国码二区首页 | 理论亚洲区美一区二区三区 | 精品亚洲综合在线第一区| 亚洲国产精品国产自在在线 | 成人性生活免费视频| 亚洲欧洲免费无码| 成人免费777777| 永久免费看mv网站入口| 国产精品国产午夜免费福利看| 又大又硬又爽免费视频| 久久久久国产亚洲AV麻豆 | 免费一级毛片正在播放| xvideos亚洲永久网址| 久久精品国产精品亚洲人人| 亚洲一区AV无码少妇电影☆| 亚洲成a人片在线观看无码| 亚洲视频中文字幕在线| 亚洲三级在线观看| 国内成人精品亚洲日本语音 | 国产精品亚洲精品观看不卡| 中文字幕精品三区无码亚洲| 亚洲av永久无码| h在线看免费视频网站男男| a级毛片100部免费观看| 37pao成人国产永久免费视频| 成熟女人特级毛片www免费| 国产伦精品一区二区三区免费迷| 亚洲一区二区三区乱码A| 亚洲第一福利视频| 亚洲一卡2卡三卡4卡无卡下载| 在线观看亚洲精品专区| 三级黄色免费观看| 亚洲精品在线免费看| 国产一级大片免费看| 国产亚洲无线码一区二区| 亚洲国产日韩在线成人蜜芽| 国产亚洲精品精品精品| 免费a级毛片无码a∨免费软件| 国产免费毛不卡片| 亚洲高清免费视频|