彙編程序的基礎與一些例子

源地址:http://www.cnblogs.com/mfm11111/archive/2009/03/27/1423099.html

一.             [bx]和內存單元

  [bx]是什麼呢?

       和[0]有些類似,[0]表示內存單元,它的偏移地址是0。

      我們要完整地描述一個內存單元,需要兩種信息:

       (1)內存單元的地址;

      (2)內存單元的長度(類型)

      我們用[0]表示一個內存單元時,0 表示單元的偏移地址,段地址默認在ds中,單元的長度(類型)可以由具體指令中的其他操作對象(比如說寄存器)指出

   所以,按照這樣的原理,我們對如下指令可以這樣理解:

   Mov ax,[0]:表示將一個內存單元的內容送入ax寄存器。其中內存單元的長度爲2字節,偏移地址爲0,段地址在ds中。

   Mov al,[0]:表示將一個內存單元的內容送入al寄存器。其中內存單元的長度爲1字節,偏移地址爲0,段地址在ds中。

   Mov ax,[bx]:表示將一個內存單元的內容送入ax寄存器。其中內存單元的長度爲2字節,偏移地址在bx中,段地址在ds中。

例子:比如AX=1234,BX=1000, DS:1000內容爲1111

      mov [bx],ax 把AX的值賦予BX所指向的內存單元  
 那麼執行後AX=1234,BX=1000,DS:1000的內容爲1234

      這種做法就叫做:寄存器間接尋址

 

二. 描述性符號():

     爲了描述上的簡潔,我們將使用一個描述性的符號 “() ”來表示一個寄存器或一個內存單元中的內容。

     例如:

     (ax):表示寄存器ax中的內容

     (20000h)表示內存單元20000h中的內容,(?)括號中的表示物理地址。

     ((ds)*16+(bx)),如果ds中的內容爲1230,bx中的內容爲4567,則(1230*16+4567)可以理解爲:

                    Ds中的內容作爲段地址,bx中的內容作爲偏移地址,這樣形成的物理地址的內容。

     爲了加強理解,我們看看下面的如何描述:

       我們看一下(X)的應用,比如:

        (1)ax中的內容爲0010H,我們可以這樣來描述:(ax)=0010H;

        (2)2000:1000 處的內容爲0010H,我們可以這樣來描述:(21000H)=0010H;

        (3)對於mov ax,[2]的功能,我們可以這樣來描述:(ax)=((ds)*16+2);

        (4)對於mov [2],ax 的功能,我們可以這樣來描述:((ds)*16+2)=(ax);

        (5)對於 add ax,2 的功能,我們可以這樣來描述:(ax)=(ax)+2;

        (6)對於add ax,bx的功能,我們可以這樣來描述:(ax)=(ax)+(bx);

        (7)對於push ax的功能,我們可以這樣來描述:
      (sp) = (sp)-2

          ((ss)*16+(sp))=(ax)

 

三. mov ax,2 那其機器代碼是 B8 02 00,這個設計計算機指令,彙編語言教材第一章一般就講這個,B8表示把後面的一個WORD存放到AX裏面,比如MOV AL,1的機器指令是B0 01,B0就表示把後面的字節保存到AL裏面,而MOV AH,1的機器指令是B4 01。

 

四.總結:現在我們通過一個實例,將上述的指令綜合一下:

題目:從offffh:0006內存單元中取一個值,並相加三次

分析:(1)運算後的結果是否會超出dx所能存儲的範圍?

       Ffff:0006 單元中的數是一個字節型的數據,範圍在0~255之間,則用它和3相乘結果不會大於65535,可以在dx 中存放下

(2)我們用循環累加來實現乘法,用哪個寄存器進行累加?
我們將ffff:0006單元中的數賦值給ax,用dx進行累加。先設(dx)=0,然後做3次(dx)=(dx)+(ax)。

(3) ffff:0006單元是一個字節單元,ax是一個 16 位寄存器,數據長度不一樣,如何賦值?

我們說的是“賦值”,就是說,讓 ax 中的數據的值(數據的大小)和ffff:0006 單元中的數據的值(數據的大小)相等。

8 位數據01H和16位數據0001H的數據長度不一樣,但它們的值是相等的。

        那麼我們如何賦值?

        設ffff:0006單元中的數據是XXH,若要ax中的值和ffff:0006單元中的相等,ax中的數據應爲00XXH。

   所以,若實現ffff:0006單元向ax 賦值,我們應該令(ah)=0,(al)=(ffff6H)。

        實現計算ffff:0006單元中的數乘以3,結果存儲在dx中的程序代碼。

 

 

代碼:

assume cs:codesg

codesg segment

     mov ax,0ffffh

     mov ds,ax

       mov bx,6

       mov al,[bx]

       mov ah,0

       mov dx,0

       mov cx,3

      s:add dx,ax

       loop s

 

       mov ax,4c00h

       int 21h

codesg ends

end

 

現在我們用debug來跟蹤這個程序:

 

91.jpg

 

我們來分析一下:

1.       圖中ds=165h,所以程序cs=166f,ss的值也爲166f,爲什麼?如果還不清楚的可以看看我們前面的學習彙編系列(大略提一下:不明白cs的值的,請仔細回頭去看.exe文件裝載的那張圖,ss的值不明白的請回頭看棧的那張圖),實際上程序運行是,先要找到一塊起始地址的內存,此處起始地址爲:ds:0,但是在.exe加載時,因爲dos需要與.exe文件通訊,所以會有一塊psp區用來存放通訊的內容。所以此處cs的值實際上是cs=ds+10h=166F,我們說cpu取得內存單元,就是通過cs:ip,所以cs:0實際上就是指程序的第一條指令。

2.       同樣的道理,此時指針也是指向程序的第一條指令,所以指針的段地址也爲166f,不明白的回頭去看棧的原理,這裏就不多說了。

3.       破解軟件時,我們要先學會用g命令,爲了使大家看得更清楚一點,我們將循環次數改爲10:

我們可以使用g 0012,則會自動執行到偏移地址爲0012處。

當我們遇到loop時,因爲後面的循環次數太多,我們可以用p命令。也可以用g 0016

4.       同一種寫法,不同的處理方式:

對於這種寫mov al,[0],debug表示:把ds:0內存單元處的內容放入al.

                      源程序中表示:就是把數據0放入al中。

解決方法:1.用過渡寄存器,比如:bx,如下:

            Mov bx,0

            Mov ax,[bx]

          2.Mov ax,ds:[0]

 

五.活用:loop和[bx]的聯合應用

例子:計算ffff:0~ffff:b單元中的數據的和,結果存儲在dx中。

        我們還是先分析一下

1.運算後的結果是否會超出 dx 所能存儲的範圍?

 ffff:0~ffff:b內存單元中的數據是字節型數據,範圍在0~255之間,12個這樣的數據相加,結果不會大於 65535 ,可以在dx中存放下。

2. 我們是否將 ffff:0~ffff:b中的數據直接累加到dx中?

 當然不行,因爲ffff:0~ffff:b中的數據是8位的,不能直接加到16位寄存器dx中。

3.我們能否將ffff:0~ffff:b中的數據累加到dl中,並設置(dh=0,從而實現累加到dx中的目標?


 這也不行,因爲dl是8位寄存器,能容納的數據的範圍在小 255 之間,ffff : 0~ffff:b中的數據也都是 8 位,如果僅向dl中累加12個 8 位數據,很有可能造成進位丟失。

4. 我們到底怎樣將用ffff:0~ffff:b中的8位數據,累加到16位寄存器dx中? 


我們將內存單元中的 8 位數據賦值到一個16位寄存器ax中,再將ax中的數據加到dx上,從而使兩個運算對象的類型匹配並且結果不會超界。

 

分析後,我們現在來寫出代碼:

Mov ax,0ffffh

Mov ds,ax

Mov bx,0

Mov dx,0

Mov cx,0

 

S:Mov al,[bx]

Mov ah,0

Add dx,ax

Inc bx

Loop s

 

請大家自行加上僞指令運行。

通過前面的介紹,我們知道了[寄存器]的含義以及各個寄存器之間如何靈活計算的方式,這是學習破解的基礎,那麼破解時入口地址的確認以及如何查看反彙編的數據段,代碼段,棧段呢?看下面的幾個基礎點。

六.在一個段中存放數據、代碼、棧,我們先來體會一下不使用多個段時的情況;

1. 考慮這樣一個問題,編程計算以下8個數據的和,結果存在ax 寄存器中:

   0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H

看下面的程序:

assume cs:codesg

codesg segment

       dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h

      mov bx,0

       mov ax,0

       mov cx,8

 s: add ax,cs:[bx]

       add bx,2

       loop s

       mov ax,4c00h

       int 21h

codesg ends

end

        解釋一下,程序第一行中的 “dw”的含義是定義字型數據。dw即define word。

        在這裏,我們使用dw定義了8個字型數據(數據之間以逗號分隔),它們所佔的內存空間的大小爲16個字節

      這8個數據的偏移地址是多少呢?

   因爲用dw定義的數據處於代碼段的最開始,所以偏移地址爲0,這8 個數據就在代碼段的偏移0、2、4、6、8、A、C、E處。

   程序運行時,它們的地址就是CS:0、CS:2、CS:4、CS:6、CS:8、CS:A、CS:C、CS:E。

2.如何讓這個程序在編譯後可以存系統中直接運行呢?我們可以在源程序中指明界序的入口所在.

          用 Debug加載後,我們可以將 IP 設置爲10h,從而使CS:IP指向程序中的第一條指令。然後再用T命令、P命令、或者是G 命令執行。

        可是這樣一來,我們就必須用Debug 來執行程序。

   程序 6.1 編譯成可執行文件後,在系統中直接運行可能會出現問題,因爲程序的入口處不是我們所希望執行的指令

        我們在程序的第一條指令的前面加上了一個標號start,而這個標號在僞指令end的後面出現。end 除了通知編譯器程序結束外,還可以通知編譯器程序的入口在什麼地方。

 

2.通過上面的分析,我們明白了在代碼段中使用數據的情況,現在在來看看在代碼段中使用棧的情況。

        完成下面的程序,利用棧,將程序中定義的數據逆序存放。

   assume cs:codesg

   codesg segment

      dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h

 

      ?

   code ends

   end

分析:

(1).程序運行時,定義的數據存放在cs:0~cs:15單元中,共8個字單元。依次將這8個字單元中的數據入棧,然後再依次出棧到這 8 個字單元中,從而實現數據的逆序存放。

(2).問題是,我們首先要有一段可當作棧的內存空間。如前所述,這段空間應該由系統來分配。我們可以在程序中通過定義數據來取得一段空間,然後將這段空間當作棧空間來用。

程序如下所示:

assume cs:codesg

codesg segment

     dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h

     dw 0,0,0,0,0,0,0,0

  start:mov ax,cs

  mov ss,ax

  mov sp,32

  mov bx,0

  mov cx,8

 

  s: push cs:[bx]

     add bx,2

     loop s

 

     mov bx,0

     mov cx,8

   s0:pop cs:[bx]

      add bx,2

      loop s0

 

      mov ax,4c00h

      int 21h

codesg ends

end start

 

解釋:(1).其中mov ax,cs

           mov ss,ax

           mov sp,32

   我們要講 cs:16 ~ cs:31 的內存空間當作棧來用,初始狀態下棧爲空,所以 ss:sp要指向棧底,則設置ss:sp指向cs:32


七。看下面的代碼,實現了兩個字符串變大寫和變小寫的方法。


assume cs:codesg,ds:data
data segment
db 
'BaSiC'
db 'iNfOrMaTiOn'
data ends
codesg segment
  start:mov ax,data
        mov ds,ax
 mov bx,0
        mov cx,5
    s:mov al,[bx]
 and al,11011111B
 mov [bx],al
 inc bx
 loop s
 mov bx,5
 mov cx,11
     s0:mov al,[bx]
 or al,00100000B
 mov [bx],al
 inc bx
 loop s0
        mov ax,4c00h
        int 21h
codesg ends
end start


 

 

1.  數據段是放在程序開頭的,程序指令執行段是在數據段之後,所以數據段的首地址爲:ds+10h.

2.  注意data,and,or ,[bx]的用法。

3.  [bx]與[bx+idata]的用法。後者只是取出bx中的數值(即偏移地址)後+一個數=新的偏移地址而已。例如:

2000:1000 00 be 00 06 00 05

Mov ax,2000h

Mov ds,ax

Mov bx,1000h

Mov cx,[bx] 那麼此時cx=00

Mov cx,[bx+1],那麼此時cx=be

 4.si與di,與bx相似,只是不能分成兩個8位寄存器。所以在傳輸時,一般按字傳。(比如將一個字符串複製到另外的空間地址)。

5.mov ax,[bx+si],[bx+si]此種方式與數組類似。一般si計數,然後通過si找新偏移地址的數據。例如:

  Mov ax,2000h

  Mov ds,ax

  Mov bx,1000h

  Mov si,0

 

  Mov ax,[bx+si]

  Inc si

 

6.[bx+si+idata]與[bx+si]類似。

 

最後總結一下:

n          如果我們比較一下前而用到的幾種定位內存地址的方法(可稱爲尋址方式),就可以發現有以下幾種方式:

n              (1)[iata] 用一個常量來表示地址,可用於直接定位一個內存單元;

n              (2)[bx]用一個變量來表示內存地址,可用於間接定位一個內存單元;

n              (3)[bx+idata] 用一個變量和常量表示地址,可在一個起始地址的基礎上用變量間接定位一個內存單元;

n              (4)[bx+si]用兩個變量表示地址;

n              (5)[bx+si+idata] 用兩個變量和一個常量表示地址。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章