匯編語言筆記(7—10章)
第七章:
學過C語言的都知道,在C語言中,也有邏輯與運算和邏輯或運算。匯編語言當然也有
and 和or就是
我是這麽理解and和or
and的是邏輯與運算,相當於兩人談感情,0代表假,1代表真,那麽必須兩人同時為
真也就是1的時間,結果才能為真,如果一個人為假就是零,那麽結果也就是0
如:
mov al,11111111b
and al,00000000b
那麽結果就是:00000000
or是邏輯或運算,就是一廂情願,只要有一方願意付出,願意為真,那麽結果就能為真
如:
mov al,11111111b
or al,00000000b
那麽結果就是:11111111b
ASCII碼:
呵呵,這個和摩斯電碼很像,喜歡無線電技術的朋友會有所體感,一個指定的值代表一個英文字母,就想電視中,嘀嘀 嘀 滴滴 滴滴 呵呵!這個是無線電臺相互交流時所采用的一種編碼,電腦也有,我們常用的如ASCLL碼,當然還有其他很多。
ASCII表。百度一下,有很大這種表,不需要大家死記,但最好能記得其中的規律。
當你打開一個win下得文本編輯器時,按下鍵盤的A鍵,屏幕上就會顯示出A,具體是
怎麽實現的呢?如下:
1.電腦開機,打開編輯器,按下鍵盤的A
2.按下以後,鍵盤會把這個信息傳送給計算機,計算機內部通過ASCLL碼進行編碼,將對應值61H存儲在內存空間中,
3.編輯器,從相應的內存空間取出這個值61H,送入顯卡的顯存當中,此事工作在文本模式下得顯卡,就會利用ASCLL碼進行解碼,就會把61H轉換為字符A
4.此時顯卡驅動顯示器,將A的圖像顯示在屏幕上,我們也就能看到
結論:大寫A在ASCLL碼中對應的是61H,註意這是十六進制的61.
關於其他ASCLL碼值,請大家自行下載ASCLL表進行查看
以字符形式給出數據
形式為:‘...’單引號,註意是英文的單引號
如:db‘ABCD’
還不懂存儲單元轉換的,看日誌揭秘系列的單講。
打下這個程序:
assume cs:qq,ds:ww (把qq,ww分別和寄存器CS,DS扯上關系)
ww segment (ww段開始)
db‘abcd‘ (定義數據,把abcd對應的ASCLL碼放入地址空間)
db‘ABCD‘ (同上)
ww ends (ww段結束)
qq segment (qq段開始)
start: mov al,‘a‘ (start定義程序入口,mov...意思把a的ASCLL值值放入寄存器al中)
mov bl,‘b‘ (同上)
mov ax,4c00h(和一面一句,以後解釋)
int 21h
qq ends (qq段結束)
end start (程序結束,這個start和前面的staet對應)
大家可以載入debug中查看寄存器變化和相應地址空間的值。
待續...
[bx+idata]
我們之前學過[bx]這個用法,他得段寄存器是ds,[bx]是他得偏移地址
例如:
mov cx,[bx+100]
意思就是:把段地址為ds,偏移地址為bx+100的這個內存單元的數據送人cx中
邏輯表達就是ds*16+[bx]+100
運用這種方法,處理大小寫轉換的問題:
程序如下:
assume cs:qq,ds:ee
ee segment
db ‘FisHc‘
db ‘LiNuX‘
ee ends
qq segment
start:mov ax,ee
mov ds,ax
mov bx,0
mov cx,5
s:mov al,[bx]
and al,11011111b
mov [bx],al
mov al,[bx+5]
or al,00100000b
mov [bx+5],al
inc bx
loop s
mov ax,4c00h
int 21h
qq ends
end start
si和di這兩個寄存器,和bx寄存器功能相近,作為偏移地址時,他們的段地址都默認在ds中存放
如:mov bx,1
mov al,[bx+100]
mov si,1
mov al,[si+100]
功能一樣!
唯一不同的是,bx可以分為bl和bh兩個8位寄存器,而si和di不能分成兩個8位寄存器
書上7.2,把wlcome to masm 復制到他後面的數據區中,
我們先用[bx+idata]的方法來寫,一次復制1字節
assume cs:qq,ds:ee
ee segment
db ‘welcome to masm!‘
db ‘...............‘
ee ends
qq segment
start:mov ax,ee
mov ds,ax
mov bx,0
mov cx,16
s:mov al,[bx]
mov [bx+16],al
inc bx
loop s
mov ax,4c00h
int 21h
qq ends
end start
一次復制2字節:
assume cs:qq,ds:ee
ee segment
db ‘welcome to masm!‘
db ‘...............‘
ee ends
qq segment
start:mov ax,ee
mov ds,ax
mov bx,0
mov cx,8
s:mov ax,[bx]
mov [bx+16],ax
add bx,2
loop s
mov ax,4c00h
int 21h
qq ends
end start
用si和di一次復制兩個字:
assume cs:qq,ds:ee
ee segment
db ‘welcome to masm!‘
db ‘...............‘
ee ends
qq segment
start:mov ax,ee
mov ds,ax
mov cx,8
mov di,0
mov si,16
s:mov ax,[di]
mov [si],ax
add di,2
add si,2
loop s
mov ax,4c00h
int 21h
qq ends
end start
用si和di一次性復制一個字節:
assume cs:qq,ds:ee
ee segment
db ‘welcome to masm!‘
db ‘...............‘
ee ends
qq segment
start:mov ax,ee
mov ds,ax
mov cx,16
mov di,0
mov si,16
s:mov al,[di]
mov [si],al
inc di
inc si
loop s
mov ax,4c00h
int 21h
qq ends
end start
其實第七章註重一個靈活
如:
[bx]
[idata]
[bx+idata]
[bx+si]
[si+bx+idata]
把
db ‘1.fishc..‘
db ‘2.linux..‘
db ‘3.unix...‘
db ‘4.windows‘
首字母設置為大寫:
代碼如下:
assume cs:qq,ds:ee
ee segment
db ‘1.fishc..‘
db ‘2.linux..‘
db ‘3.unix...‘
db ‘4.windows‘
ee ends
qq segment
start:mov ax,ee
mov ds,ax
mov bx,0
mov cx,4
s:mov al,[bx+2]
and al,11011111b
mov [bx+2],al
add bx,9
loop s
mov ax,4c00h
int 21h
qq ends
end start
把
db ‘1.fishc..‘
db ‘2.linux..‘
db ‘3.unix...‘
db ‘4.windows‘
所有字母設置為大寫:
代碼如下:
assume cs:qq,ds:ee,ss:tt
ee segment
db ‘dll‘
db ‘exe‘
db ‘dos‘
db ‘win‘
ee ends
tt segment
db ‘......‘
tt ends
qq segment
start:mov ax,ee
mov ds,ax
mov ax,tt
mov ss,ax
mov sp,12
mov bx,0
mov cx,4
s1:
push cx
mov si,0
mov cx,3
s:mov al,[bx+si]
and al,11011111b
mov [bx+si],al
inc si
loop s
add bx,4
pop cx
loop s1
mov ax,4c00h
int 21h
qq ends
end start
雙層循環,多想想就明白了
最後一例:
把
db ‘1.fishc..‘
db ‘2.linux..‘
db ‘3.hacker...‘
db ‘4.windows‘
把前四個字母設置為大寫:
代碼如下:
assume cs:qq,ds:ee,ss:tt
ee segment
db ‘1.linux..‘
db ‘2.hacker.‘
db ‘3.windows‘
ee ends
tt segment
db ‘..........‘
tt ends
qq segment
start:
mov ax,tt
mov ss,ax
mov sp,20
mov ax,ee
mov ds,ax
mov bx,0
mov cx,3
s:
push cx
mov si,0
mov cx,4
s1:mov al,[bx+si+2]
and al,11011111b
mov [bx+si+2],al
inc si
loop s1
add bx,9
pop cx
loop s
mov ax,4c00h
int 21h
qq ends
end start
(第七章完)
首先是寄存器了,這裏我們統計下之前所學的寄存器
首先四個:ax,bx,cx,dx,
si,di,sp,ip,(bp),(pws)
cs,ds,ss,(es)
其中:ax,bx,cx,dx,這四個可分為高低8位寄存器
如ah,al....
其中cx 用來計算循環次數
bx一般當做偏移地址來說時,段地址在ds中
其中cs,ds,ss,es這四個為段寄存器
cs和ip為組合, 定義指令指針
ss和sp為組合, 定義棧
ds和bx,si,di組合,定義數據
關於es以後再說
bp下面說
pws以後再說
其中si,di與bx功能相似,作為偏移地址時,段地址都在ds中
不一樣的是不能向bx一樣可以劃分為單獨的兩個8位寄存器
十四個寄存器中,唯一沒有接觸過的是:es段寄存器,pws寄存器
bp寄存器
說了這麽說,就介紹下bp寄存器
bp寄存器,實際上和sp很相似,默認段地址都在ss中,BP可以間
接尋址寄存器而SP不能以外,其余的功能基本相同
如:
mov ax,[bp]
就等於:
ax=ss*16+(bp)
關於,bx,bp,si,di四個寄存器的配合
很多人暈,我是這樣記的:
假設:
bx和bp是兩個男人,像我一樣帥!呵呵,bx是我本人,bp就是你
了,他們名字的姓分別是bx姓ds bp姓ss
si和di是兩個女孩 很漂亮是親姐妹,比如si是丁丁(人名),di是斜陽(人名),等
下封ID了...
idata呢,就是小三了,像誰呢?你猜
一共5個人哈!記好!
根據我多年對易術天圓地方的研究!以及我多年的物理學經驗,終
於得出一個結論:正負相吸,相同排斥的原理!
男女搭配的常規的四種搭配方式:
[bx+si] [bx+di] 分別是:我和丁丁,我和斜陽
[bp+si] [bp+di] 分別是:你和丁丁,你和斜陽
當然,如果想搞小三的話,那可以加上idata,甲魚(人名)常常
[bp+si+idata...]
多少個idata,誰知道呢???
如:
[bx+si+idata] 那這樣就是,我和丁丁,外加小三.... 這種三人關
系還是可以的,我喜歡....
你有錢的話,還可以多找幾個小三,像甲魚一樣:
[bx+si+idata+idata+...]
註意:絕不允許兩個男人娶一個老婆,如:
[bx+bp+si] 如,我和甲魚和丁丁, 這樣甲魚是肯定不會願意的
,所以甲魚只好走了,就剩我和丁丁,如:[bx+di] 當然這個時間
我還可以找個小三 ,如[bx+si+idata]
但是不能找斜陽了,不然就亂倫了...那就是[bx+si+di] 這樣絕對不
行
還有就是連同小三我們五個人都單身一輩子,都單身一輩可以5個
單獨出現,所以說:
親!你是不是經常男女搭配啊!
親!你是不是經常亂倫啊!
親!你是不是經常同性戀啊!
親!你是不是經常包小三啊!
親!你是不是經常單身啊!
有木有???
還有就是si和di不能一起,因為亂倫了!
註意:si可以單獨和idata在一起,哎!這個關系怎麽可以?設計
CPU的人啊!傷不起...
最後討論下生出孩子的問題,主要是孩子姓什麽?
註意一點:
[bp+idata]
[bp+di+idata]
[bp+si]
很多人認為,bp段地址在ss,di或者si段地址在ds
這樣是錯誤的!
只要bp出現,那就是統一段地址在ss中
這樣理解:
bp和bx說了是男人,si和di是女人,
夫唱婦隨,孩子出生肯定跟隨bp的姓了,那就是段地址在ss
idata是小三!
呵呵,肯定也是男的是bp就跟ss,是dx就跟ds
如果小三和si與di在一起了,那就跟si和di的姓,也是ds
說下數據處理和數據有關的方面
首先cpu處理數據,一般不關心數據值是多少,而是關心數據所在
位置,一般有三種操作,讀,寫,運行
一般數據在三個位置:
內存地址 如:mov ax,[bx]或mov ax,[bx+2]
cpu內部,如寄存器中,指令緩沖器... mov ax,bx或者mov ax,1
端口 暫時不說
數據進行運算時,一般不需要指定數據運算的單位,如字還是字節
那是因為很多運算使用了寄存器
如:mov ax,bx 默認就是字運算 如mov ax,1122h
註意:mov ax,12h 也是字運算,不要看只有12h就以為是字節運算
其實編譯器當0012h
mov al,bl 默認是字節運算 如 mov al,22h
push和pop默認是字運算
如果在沒有指定寄存器的情況下,不指定運算單位,就是要出錯的
程序如下:
假設數據:
ds:0 11 22 00 00....
我們修改11為33,就是 33 22 00 00...
原來程序如下:
assume cs:qq,ds:ee
ee segment
db 11h,22h
ee ends
qq segment
start:
mov ax,ee
mov ds,ax
mov bx,0
mov [bx],33h
mov ax,4c00h
int 21h
qq ends
end start
這樣的話,結果是
ds:0 33 00 00 00
我們雖然修改了11,但同時也把22給修改了
因為此時沒有出現寄存器,所以cpu就認為是字運算了
,如果我們要單獨修改11的話,那就用一條新指定
XX ptr
解析:xx可以是word或者byte...
如字運算: mov word ptr[bx],33h
如字節運算:mov byte ptr[bx],33h
這樣的話,我們改進上面的程序,把mov [bx],33h
這一句修改為:mov byte ptr[bx],33h
程序結果如下:
ds:0 33 22 00 00
這樣就做到沒有修改到22這個數據!
關於尋址方式加定義處理數據單位的綜合因為:
我就寫了個最簡單的例子,沒有用到循環和別的,就是讓大家
更明了
assume cs:qq,ds:ee
ee segment
db‘IBM‘
db‘100‘
db‘PDF‘
db‘in‘
ee ends
qq segment
start:
mov ax,ee
mov ds,ax
mov bx,0
mov byte ptr[bx+03h],‘0‘
mov byte ptr[bx+04h],‘1‘
mov byte ptr[bx+05h],‘0‘
mov byte ptr[bx+06h],‘d‘
mov byte ptr[bx+07h],‘o‘
mov byte ptr[bx+08h],‘c‘ 只修改一個字節
mov word ptr[bx+09h],‘hh‘ 同時修改1個字
mov ax,4c00h
int 21h
qq ends
end start
好了,第八章上半部分就說的這裏!
第八章:
DIV命令
除法命令
說下我們之前學過的運算
add 加sub減inc自加1div除
了解基本除法運算
9/2=4...1
9是被除數
2是除數
4是商
1是余數
在8086匯編中,除數可能有8位與16位兩種
除數為8位時,被除數為16位,默認放在ax寄存器中
商則放在al中,余數放在ah中
例:
div byte ptr [bx]
商al=(ax)/(ds*16+bx)
余ah=(ax)/(ds*16+bx)
如果除數為16位時,被除數為32位,默認放在ax和dx中,
其中bx存放高16位,ax存放低16位
商則放在ax中,余數則放在dx中
例:
div word ptr [bx+si+6]
商ax=(dx*10000H+ax)/(ds*16+bx+si+6)
余dx=(dx*10000H+ax)/(ds*16+bx+si+6)
這裏的10000H解析下:
如果32位數據為:
AABBCCDD
那麽AABB放在bx寄存器中
CCDD放在ax寄存器中
那麽AABB*10000H時,也就是等於AABB0000
這個時間加上ax的值,那就是AABBCCDD
註意,一定要拿word ptr或者byte ptr指明是字節
還是字操作,也就是8位還是16位
舉個例子
1000/101=9...101
程序如下:
assume cs:qq,ds:ee
ee segment
db 65h ;65h即等於十進制101
ee ends
qq segment
start:
mov ax,ee
mov ds,ax
mov ax,3E8h ;3E8h即等於十進制1000
mov bx,0
div byte ptr [bx]
mov ax,4c00h
int 21h
qq ends
end start
結果:ax=5B09
其中al=09即是商 ,十進制也是9
ah=5B即是余數 5Bh即十進制101
偽指令dd
其實前面我們說過了,
db定義字節 8位
dw定義字 16位
dd定義雙字 32位
dup這個命令很有用,大家在使用一段幹凈內存空間時
可以用它來定義
dup功能:
XX y dup(a,b,c)
其中XX可以是dd,dw,db...
y即是重復的次數
括號內部的a,b,c即是要重復的內容
如
db 3 dup(11,22)
執行後,相當於
db 11,22,11,22,11,22
關於實驗7,我用了一個笨方法下了出來,雖然笨,但是幾乎用到
了前面的所有的知識
我把代碼寫下來
assume cs:qq,ds:ee
ee segment
db ‘1991‘,‘1992‘,‘1993‘,‘1994‘,‘1995‘,‘1996‘
dd 16,22,382,1356,8000,5937000
dw 3,7,9,13,38,30000,17800
ee ends
;qq段開始
qq segment
start:
mov ax,ee
mov ds,ax
mov ax,1000h
mov ss,ax
;年份計算
mov bp,0
mov bx,0
mov si,0
mov cx,6
s:
mov es,cx
mov cx,4
s1:
mov word ptr ax,[bx]
mov word ptr [bp+si],ax
inc bx
inc bp
loop s1
mov cx,es
add si,12
loop s
;中間空格
mov bp,0
mov cx,6
s2:
mov byte ptr [bp+4],0
add bp,16
loop s2
;收入計算
mov cx,6
mov bx,18h
mov si,0
mov bp,0
s3:
mov es,cx
mov cx,2
s4:
mov word ptr ax,[bx]
mov word ptr [bp+si+5],ax
add bx,2
add bp,2
loop s4
mov cx,es
add si,12
loop s3
;中間空格
mov bp,0
mov cx,6
s5:
mov byte ptr [bp+9],0
add bp,16
loop s5
;雇員數計算
mov cx,6
mov bx,30h
mov si,0
mov bp,0
s6:
mov es,cx
mov cx,1
s7:
mov ax,[bx]
mov [bp+si+10],ax
add bx,2
inc bp
loop s7
mov cx,es
add si,15
loop s6
;中間空格
mov bp,0
mov cx,6
s8:
mov byte ptr [bp+12],0
add bp,16
loop s8
;最後的空格
mov bp,0
mov cx,6
s9:
mov byte ptr [bp+15],0
add bp,16
loop s9
;除法運算,商保存偏移OD處,余保存0E處
mov bp,0
mov cx,6
mov si,0
ps:
mov bx,0
mov ax,[bp+si+5]
mov dx,[bp+si+7]
div word ptr [si+bp+10]
mov [bp+si+13],ax
mov [bp+si+14],dx
add si,16
loop ps
;結束
mov ax,4c00h
int 21h
qq ends
end start
書上原題,答案是這樣的:
assume cs:code,ds:data,es:table
data segment
db ‘1975‘,‘1976‘,‘1977‘,‘1978‘,‘1979‘,‘1980‘,‘1981‘,‘1982‘,‘1983‘
db ‘1984‘,‘1985‘,‘1986‘,‘1987‘,‘1988‘,‘1989‘,‘1990‘,‘1991‘,‘1992‘
db ‘1993‘,‘1994‘,‘1995‘
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
dw 11452,14430,15257,17800
data ends
table segment
db 21 dup (‘year summ ne ?? ‘)
table ends
code segment
start: mov ax,data
mov ds,ax
mov ax,table
mov es,ax
mov bx,0
mov si,0
mov di,0
mov cx,21
s: mov ax,[bx]
mov es:[si],ax
mov ax,[bx].2
mov es:[si].2,ax
mov ax,[bx].84
mov es:[si].5,ax
mov dx,[bx].86
mov es:[si].7,dx
div word ptr ds:[di].168
mov es:[si].13,ax
mov ax,[di].168
mov es:[si].10,ax
add di,2
add bx,4
add si,16
loop s
mov ax,4c00h
int 21h
code ends
end start
OK 第八章算是結束了,下面就是第九種跳轉了
呵呵!破解的必備!
第九章轉移指令
呵呵!轉移相對於逆向軟件還是非常重要的
其實我們之前學過的loop就是一個轉移指令,可以說是一個條件轉移
或者是循環轉移的指令
先說offset操作符
他的作用就是取得標號的偏移地址!
比如:offset sb
如果sb標號所在的偏移地址為6,那麽這個offset就取得6
例如:
assume cs:qq
qq segment
mov ax,5
sb:
mov bx,ax
mov cx,offset sb
s1:
add ax,10
loop s1
mov ax,4c00h
int 21h
qq ends
end
上面的cx,他的值就是3了,因為sb所在的偏移地址是3,mov ax,5這句占了3個地址,即0,1,2
下面說下jmp指令了
jmp為無條件轉移指令,他的功能可以分為很多種,我們一樣一樣說
首先是短轉移
格式是: jmp short 標號
既然短轉移,可以看出肯定非常短,所以他只能修改ip的值,範圍呢
就是-128到127之間,就是往前可以移動128個字節,往後可以移動127個字節
例如:
assume cs:qq
qq segment
mov ax,5
add ax,10
jmp short s
add ax,10
s:mov ax,4c00h
int 21h
qq ends
end
那麽這樣的話,就只執行一次加10的操作,而跳過一個加10的操作
把上面程序,載入debug,用u指令查看
20BC:0000 B80500 MOV AX,0005
20BC:0003 83C00A ADD AX,+0A
20BC:0006 EB03 JMP 000B
20BC:0008 83C00A ADD AX,+0A
20BC:000B B8004C MOV AX,4C00
20BC:000E CD21 INT 21
可以看出,其他立即數都顯示在匯編指令中,如,MOV AX,0005
顯示的是B80500 ,其他也有對應的數值,但jmp命令卻沒有 是:EB03
在觀察可以發現,jmp 000b其實是給出的內存地址的位移數,即我們需要跳轉到
MOV AX,4C00 這裏,而這裏的偏移地址正好是000b,而jmp指令後的的
下一個地址是0008,相減正好是03,即我們得出結論
jmp short 標號,的地址是,標號處的地址減去jmp指令執行後的第一個字節的地址
還有一直和jmp short 指令很像的一條指令,我們稱為近轉移
他的格式是 jmp near ptr標號,他也是只能修改ip的值,範圍是-32768到32767之間
是16位的位移!其他和jmp short一樣!
在就是jmp的段間轉移了
格式是jmp far ptr標號
他是遠轉移,可以修改cs和ip的值,如:
assume cs:qq
qq segment
mov ax,5
add ax,10
jmp far ptr s
add ax,10
s:mov ax,4c00h
int 21h
qq ends
end
載入debug使用U命令查看:
20BC:0000 B80500 MOV AX,0005
20BC:0003 83C00A ADD AX,+0A
20BC:0006 EA0E00BC20 JMP 20BC:000E
20BC:000B 83C00A ADD AX,+0A
20BC:000E B8004C MOV AX,4C00
20BC:0011 CD21 INT 21
可以看出,jmp命令,已經把cs和ip的值都給了出來,而且機器碼也顯示出來
再說下一個jmp命令
就是jmp 寄存器(16位寄存器)
他的作用是修改ip的,範圍和jmp near ptr一樣
最後說一下,地址在內存的轉移
這個有兩種,也是段內轉移,和段間轉移
先說段內轉移:
格式
jmp word ptr 內存單元地址
這個word說明從內存單元開始的一個字,是ip的地址
如:mov ax,1234h
mov [bx],ax
jmp word ptr [bx]
這樣ip的值就是1234h
在就是段間轉移
格式
jmp dword ptr 內存單元地址
這個dword說明從內存單元開始的一個雙字,低字是ip的地址
高字是cs的地址
如:mov bx,0
mov ax,1122h
mov [bx],ax
mov ax,3344h
mov [bx+2],ax
jmp dword ptr ds:[0]
add ax,10
這樣ip的值就是1122h,cs就是3344h
下面就是條件轉移指令了
jcxz指令
他和loop差不多,都是拿cx來判斷的
也是短轉移,所以修改的也只是ip的值,同樣也是8位的位移
所以修改範圍還是—128——127之間
格式:
jcxz XX(如果cx=0就執行跳轉)
這個XX就是標號,這個命令和loop相反
loop是cx不等於就執行跳轉
jcxz的例子:
assume cs:qq
qq segment
start:
mov cx,0
jcxz s
mov ax,10
s: add ax,10
mov ax,4c00h
int 21h
qq ends
end start
這個例子中,ax的結果就是A,也就是十進制的10
而不執行mov ax,10這條指令
OK,我們總結下轉移命令
條件轉移的是:
loop cx不為0,則轉移
jcxz cx為0,則轉移
都是修改ip,都是8位位移
無條件轉移:
jmp short 標號
短轉移,ip,8位位移
jmp near ptr 標號
近轉移,ip,16位位移
jmp far ptr 標號
遠轉移,同時修改cs和ip
jmp 寄存器
16位ip位移
jmp word ptr 內存單元地址
段內轉移,修改ip,16位位移
jmp dword ptr 內存單元地址
段間轉移,修改cs和ip的值
第九章下半部分了
呵呵!其實第九章基本前面已經說的差不多了,這裏就是個總結和補充
說下jmp指令為什麽不是全部直接給出cs和ip的值,比如jmp short 標號
就是給出偏移地址
原因很簡單,就是為了程序的移植新行和,在內存中的浮動配置,以及後期程序的修改
比如:
我要死限定jmp跳轉到某一個地址如2000:100 指令為mov ax,bx
但是如果放到別人的電腦上,就有可能2000:100是別的指令,特別是循環語句loop,如果
循環語句給出的是cs和ip的準確值,那麽修改源碼時,就要重新計算地址...這個大家對內
存地址稍微了解些,就明白了
關於內存地址跳轉時的指令的範圍,大家可以字節實驗一下
比如測試jmp short 標號 這個,大家可以在跳轉命令和符號地址中間填充一些空白數據測
試,如這個命令只能向後移動127個字節,那麽你就中間填充130個或更多的字節,自己編
譯看看!當然編譯器會報錯,這個是由編譯器自己檢測的
我分析下實驗八,其實這個就是有點繞,大家單步走走就明白了
源代碼:
assume cs:codesg
codesg segment
mov ax,4c00h
int 21h
start: mov ax,0
s: nop
nop
mov di,offset s
mov si,offset s2
mov ax,cs:[si]
mov cs:[di],ax
s0: jmp short s
s1: mov ax,0
int 21h
mov ax,0
s2: jmp short s1
nop
codesg ends
end start
加載debug用u指令查看:
20BC:0005 B80000 MOV AX,0000
20BC:0008 90 NOP
20BC:0009 90 NOP
20BC:000A BF0800 MOV DI,0008
20BC:000D BE2000 MOV SI,0020
20BC:0010 2E CS:
20BC:0011 8B04 MOV AX,[SI]
20BC:0013 2E CS:
20BC:0014 8905 MOV [DI],AX
20BC:0016 EBF0 JMP 0008
20BC:0018 B80000 MOV AX,0000
20BC:001B CD21 INT 21
20BC:001D B80000 MOV AX,0000
20BC:0020 EBF6 JMP 0018
20BC:0022 90 NOP
我一步一步說下:
先說地址:
start上面的兩段代碼暫居了0—4地址
首先
根據start找入口,執行mov ax,0 ax=0
然後從s標號開始執行,也就是兩個nop
然後執行mov di,offset s
mov si,offset s2
就是把di設置為8h,應為s標號的地址是8h
把si設置為20h,因為s2標號地址是20h
然後ip為20h的指令放入ax中,即s2的指令放入ax中,s2的指令是jmp short s1 (兩個
字節)
然後又把ax中的值放入ip值是di的地方,前面說過di是8h,也就是把ax的指令放入ip地址
是8h中 正好把兩個nop填充了
然後執行s0: jmp short s 命令,就跳轉到8h處,也就是標號s處
這時標號s的地址指令已經是EBF6 這條指令在地址20h處,他指向了18h,也就是向上編
譯了10個地址可以得知 F6是補碼-10然後在8h地址執行,占兩個字節,也就是8+2-10,得出跳轉到地址0處然後執mov ax,4c00hint 21h....
呵呵!感覺有點饒,關鍵是考驗補碼和jmp short命令的位移問題!大家好好思考就OK了關於實驗九,方法很多,但為了練習大家,還是希望多用jmp指令,這樣簡單些!代碼就不寫了,自己地下寫,很簡單!
第十章上
呵呵!已經第十章了,很快!這幾章呢,都是說的轉移指令,都是和cs:ip打交道所以大家一定要對cs和ip的原理爛透於心
先說下ret指令
ret指令有兩個
一個是ret指令,一個是retf指令
可以這樣說,一個是近轉移,一個是遠轉移
一個修改ip一個同時修改cs和ip
ret指令就是
ip=ss*16+sp
retf就是
ip=ss*16+sp
sp+2
cs=ss*16+sp
拿ret做個無限循環的例子:
assume cs:qq,ds:ee
ee segment
db 16 dup (0)
ee ends
qq segment
mov ax,4c00h
int 21h
start:
mov ax,ee
mov ss,ax
mov sp,16
mov ax,5
push ax
ret
qq ends
end start
上面的程序,我把ax設置為5,然後入棧,這樣的話,
每次執行到ret的時間,ip都是指向5,就是指向了mov ax,ee這句
導致程序不能正常退出!
如果那retf做的話,也是一樣,程序如下:
assume cs:qq,ds:ee
ee segment
db 16 dup (0)
ee ends
qq segment
mov ax,4c00h
int 21h
start:
mov ax,ee
mov ss,ax
mov sp,16
mov ax,5
push cs
push ax
retf
qq ends
end start
無非是,retf指令,是遠轉移,需要指定cs的值,我把cs本身的值入棧,
就是cs=cs,ip還是5,所有效果和上面是一樣的!
在寫給例子吧
例如我們讓程序從2000:1000處執行
程序就是:
assume cs:qq,ds:ee
ee segment
db 16 dup (0)
ee ends
qq segment
mov ax,4c00h
int 21h
start:
mov ax,ee
mov ss,ax
mov sp,16
mov ax,200
push ax
mov ax,2000h
push ax
mov ax,1000h
push ax
retf
qq ends
end start
執行retf後,cs就是2000,ip就是1000
下面就是說下call指令了
首先是位移的call指令 格式:call 標號
範圍呢是十六位的位移
這個剛開始有點繞,光看這一個call很難理解,我說下和ret的合用
舉個例子:
使程序正常退出:
assume cs:code,ss:ee
ee segment
db 16 dup(0)
ee ends
code segment
start:mov ax,ee
mov ss,ax
mov sp,16
call s
mov ax,4c00h
int 21h
s:add ax,1
ret
code ends
end start
上面的程序,載入debug,用u查看機器指令:
20BD:0000 B8BC20 MOV AX,20BC
20BD:0003 8ED0 MOV SS,AX
20BD:0005 BC1000 MOV SP,0010
20BD:0008 E80500 CALL 0010
20BD:000B B8004C MOV AX,4C00
20BD:000E CD21 INT 21
20BD:0010 83C001 ADD AX,+01
20BD:0013 C3 RET
單步走的話,執行到call s這句的時間,進行了如下操作
首先把此時ip的值,也就是下句:mov ax,4c00h 的ip值
入棧,就是000B入棧,然後跳轉到位移是0005的地方,也就是
000B+0005就是0010處,就是add ax,1
現在接著執行下面的ret
ret執行的時間,從棧中獲取到0005當作ip的值,這是就跳轉到
mov ax,4c00h處,從而實現了正常退出
下面就是段間的跳轉,用call指令
格式:call far ptr 標號
他是在進行跳轉的時間,同時把下面指令的cs和ip的值入棧
我們舉個例子:
使程序正常退出:
assume cs:code,ss:ee
ee segment
db 16 dup(0)
ee ends
code segment
start:mov ax,ee
mov ss,ax
mov sp,16
call far ptr s
mov ax,4c00h
int 21h
s:mov ax,0
retf
code ends
end start
上面的程序,載入debug,用u查看機器指令:
20BD:0000 B8BC20 MOV AX,20BC
20BD:0003 8ED0 MOV SS,AX
20BD:0005 BC1000 MOV SP,0010
20BD:0008 9A1200BD20 CALL 20BD:0012
20BD:000D B8004C MOV AX,4C00
20BD:0010 CD21 INT 21
20BD:0012 B80000 MOV AX,0000
20BD:0015 CB RETF
當程序執行到call far ptr s的時間,首先把cs和ip的值入棧
即把20BD 000D 先是cs入棧,後ip入棧
然後跳轉到cs=20bd,ip=0012處,就是mov ax,0000這裏
然後執行retf這句
當執行這句的時間,會用棧頂獲取cs和ip的值,首先是ip=0012
再就是cs=20bd,這個時間就跳轉到mov ax,4c00h這句,從而實現
正常退出
再說下call 寄存器的命令
格式:call 16位寄存器
還是舉個例子說:
同樣是實現正常退出程序:
assume cs:code,ss:ee
ee segment
db 16 dup(0)
ee ends
code segment
start:mov ax,ee
mov ss,ax
mov sp,16
mov ax,12h
call ax
mov ax,4c00h
int 21h
s:mov ax,0
ret
code ends
end start
上面的程序,載入debug,用u查看機器指令:
20BD:0000 B8BC20 MOV AX,20BC
20BD:0003 8ED0 MOV SS,AX
20BD:0005 BC1000 MOV SP,0010
20BD:0008 B81200 MOV AX,0012
20BD:000B FFD0 CALL AX
20BD:000D B8004C MOV AX,4C00
20BD:0010 CD21 INT 21
20BD:0012 B80000 MOV AX,0000
20BD:0015 C3 RET
當程序執行到call ax時,首先OD入棧,然後根據ax=12
所以跳轉到mov ax,0 處,然後執行ret命令,ret從棧中獲取
ip的值,就是ip=od,即跳轉到mov ax,4c00處,從而實現正常
退出程序最後說下call在內存地址中的指令和jmp一樣,分2種
先說 call word ptr 內存單元地址
例子:
正常退出:
assume cs:code,ss:ee
ee segment
db 16 dup(0)
ee ends
code segment
mov ax,4c00h
int 21h
start:mov ax,ee
mov ss,ax
mov sp,16
mov ax,0
mov ds:[0],ax
call word ptr ds:[0]
code ends
end start
載入debug,用U命令查看:
20BD:0000 B8004C MOV AX,4C00
20BD:0003 CD21 INT 21
20BD:0005 B8BC20 MOV AX,20BC
20BD:0008 8ED0 MOV SS,AX
20BD:000A BC1000 MOV SP,0010
20BD:000D B80000 MOV AX,0000
20BD:0010 A30000 MOV [0000],AX
20BD:0013 FF160000 CALL [0000]
執行到call call word ptr ds:[0] 這條時,
首先把下面的地址ip入棧,然後跳轉到ip為0處執行,這個最簡單!
下面再說下,call dword ptr 內存單元地址這個命令
他和上面不同的是,他執行跳轉時,同時把cs和ip的值都入棧了
舉個例子:
還是正常退出:
assume cs:code,ss:ee
ee segment
db 16 dup(0)
ee ends
code segment
retf
start:
mov ax,ee
mov sp,16
mov ax,0
mov ds:[0],ax
mov ds:[2],cs
call dword ptr ds:[0]
mov ax,4c00h
int 21h
code ends
end start
載入debug查看,用U指令:
20BD:0000 CB RETF
20BD:0001 B8BC20 MOV AX,20BC
20BD:0004 BC1000 MOV SP,0010
20BD:0007 B80000 MOV AX,0000
20BD:000A A30000 MOV [0000],AX
20BD:000D 8C0E0200 MOV [0002],CS
20BD:0011 FF1E0000 CALL FAR [0000]
20BD:0015 B8004C MOV AX,4C00
20BD:0018 CD21 INT 21
當程序執行call dword ptr ds:[0] 的時間,首先把
cs是20bd和ip是0015入棧了,然後根據ds:0的數據進行跳轉
就是本段的0處,所以跳轉到retf處,retf又根據棧中的數據
設置了cs=20bd,ip=0015,所有跳轉到mov ax,4c00h處
導致程序正常退出!
關於call和ret的綜合運用,我就不說了,上面全部賽綜合運用了
OK 第十章上半部分就先說到這!
說下乘法指令mul
兩數相乘:
都是八位的話,一個默認放在al中
另一個在內容單元或者8位寄存器中
結果默認是放在ax
格式:
mul 內存單元
mul 8位寄存器
由於是八位,這樣乘數就必須小於255
大於的話,就用16位相乘
寄存器例如:
計算20*30
20的十六進制是14H
30的十六進制是1EH
assume cs:qq
qq segment
mov al,14h
mov ah,1eh
mul ah
mov ax,4c00h
int 21h
qq ends
end
結果AX=0258
內存單元例如:
assume cs:qq
qq segment
mov al,14h
mov ah,1eh
mov bx,0
mov byte ptr[bx],ah
mul byte ptr[bx]
mov ax,4c00h
int 21h
qq ends
end
結果還是AX=0258
都是十六位的話,一個默認讓在ax中一個默認放在另一個在內容單元或者16位寄存器中結果默認是高位放dx中,地位放ax中
mul 內存單元
mul 16位寄存器
內存單元例如:
計算500*400
500的十六進制是1F4H
400的十六進制是190H
assume cs:qq
qq segment
mov ax,1F4h
mov bx,190h
mul bx
mov ax,4c00h
int 21h
qq ends
end
結果:dx=0003
ax=0d40
即結果是30d40 即200000
拿內存單元來說:
assume cs:qq
qq segment
mov ax,1F4h
mov dx,190h
mov word ptr[bx],dx
mul word ptr[bx]
mov ax,4c00h
int 21h
qq ends
end
結果一樣!
註意:word ptr 和byte ptr的使用
下面我再說個,混合運用的例子:
計算:
(8+12-10)*100/20 的結果
思路:先算8+12的和,然後拿和減去10,乘以100,然後除於20
assume cs:qq
qq segment
mov ah,8h
mov al,0ch
mov bh,0ah
add ah,al
sub ah,bh
mov al,ah
mov ah,64h
mul ah
mov bx,14h
div bx
mov ax,4c00h
int 21h
qq ends
end
結果是al=32h 即50
下面就是重要的,關於模塊化程序的設計
這個和C語言很想象,呵呵!思想很類似與api
我寫給例子給大家說明:
計算第一組數據的三次方,然後把結果放到第二組裏面
assume cs:qq,ds:ee
ee segment
dw 2,4,6,8,10,12,15
dd 0,0,0,0,0,0,0,0
ee ends
qq segment
start:
mov ax,ee
mov ds,ax
mov si,0
mov di,16
mov cx,8
s:
mov bx,[si]
call ww
mov [di],ax
mov [di+2],dx
add si,2
add di,4
loop s
mov ax,4c00h
int 21h
ww:
mov ax,bx
mul bx
mul bx
ret
qq ends
end start
程序執行到s表好處,把地址ds:si處的數據給了bx然後call 跳轉到ww標號處,並把ip值入棧
程序執行ww標號處指令,也就算出三次方的值,題目為16位相乘ret根據棧中數據地址,跳轉到mov [di],ax處執行,把積的值依次放入第二組數據表中然後si加2,di加4,然後循環上面的操作!
註意:是十六位相乘,故出現:
mov [di],ax
mov [di+2],dx
理解add di,4
載入debug用U命令查看:
20BF:0000 B8BC20 MOV AX,20BC
20BF:0003 8ED8 MOV DS,AX
20BF:0005 BE0000 MOV SI,0000
20BF:0008 BF1000 MOV DI,0010
20BF:000B B90800 MOV CX,0008
20BF:000E 8B1C MOV BX,[SI]
20BF:0010 E81200 CALL 0025
20BF:0013 8905 MOV [DI],AX
20BF:0015 895502 MOV [DI+02],D
20BF:0018 83C602 ADD SI,+02
20BF:001B 83C704 ADD DI,+04
20BF:001E E2EE LOOP 000E
20BF:0020 B8004C MOV AX,4C00
20BF:0023 CD21 INT 21
20BF:0025 8BC3 MOV AX,BX
20BF:0027 F7E3 MUL BX
20BF:0029 F7E3 MUL BX
20BF:002B C3 RET
批量數據的傳遞,返回值的傳遞大家有這樣的感受,如果再不用內存地址的時間只有寄存器來傳遞,大家會感覺到寄存器不夠用呵呵!從開始的那麽多寄存器不知道用那個到現在的不夠用,代表進步了很多,同時大家要註意對內存地址的使用,因為這個是很大利用空間啊說下,用內存地址來傳遞數據
如書上例題一樣:
把一個字符串轉換為大寫
assume cs:qq,ds:ee
ee segment
db ‘hellofishc‘
ee ends
qq segment
start:
mov ax,ee
mov ds,ax
mov di,0
mov cx,10
call ww
mov ax,4c00h
int 21h
ww: and byte ptr [di],11011111b
inc di
loop ww
ret
qq ends
end start
這個思路說下:
call到ww標號處,and命令轉換大寫,然後inc di轉換下面的字母,cx=10,就是轉換10次,所以字母轉換完後,ret跳轉到mov ax,4c00h處,正常退出程序
這個問題,用jcxz和jmp來做,很有意思
assume cs:qq,ds:ee
ee segment
db ‘hellofishc‘
ee ends
qq segment
start:
mov ax,ee
mov ds,ax
mov di,0
call ww
mov ax,4c00h
int 21h
ww:
mov cl,0
mov ch,[di]
jcxz ok
and byte ptr [di],11011111b
inc di
jmp ww
ok:ret
qq ends
end start
呵呵!先把cl設置為0,然後把di的值放入ch中,如果cx=0則不運算,直接返回!
關於多個值的傳遞,大家下面可以測試下,同時計算幾個數的積,然後把所有的積相加
如:(2*4)+(4*5)+(3*6)+(7*8)把積用地址存放
關於寄存器的沖突問題,相信大家之前也遇到過,往往循環中就出現這種的沖突
解決辦法:大家可以利用棧或者是內存地址空間,把某個寄存器的值先放進去
然後在重新定義這個寄存器的值,用到的時間,在出棧,或者是調用內存地址的數據,註意棧的出棧順序!實驗十不多講!都是做過的
轉自 https://fishc.com.cn/thread-12687-1-1.html
作者:小生我怕怕
匯編語言筆記(7—10章)