鱼C论坛

 找回密码
 立即注册
查看: 53383|回复: 155

[技术交流] 汇编语言笔记(7—10章)

  [复制链接]
发表于 2011-12-30 03:39:55 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 小生我怕怕 于 2012-4-24 03:27 编辑

      呵呵!2011马上就要过去了,终于赶在年前把10章之前的笔记写完了,为确保不使新手误入迷途,这几章笔记吸取了1—6章笔记的不足,更易懂,简洁,所有例子均为课本外的程序,所有代
码都是经过调试,确保无误!
      7——10章也是本书的重点和难点,难就难在一个思路和思维行,难在灵活性,说是重点,跳转命令,灵活内存地址定位,都是以后逆向或者编写复杂程序的基础!这么说,学汇编就像红军过草地,很多有志之士,热血沸腾的!但都是倒在了7—10章这里没走过去,能迈过这几章的,前面都是一片光明!

鱼C官方汇编讨论一群:158781547   欢迎汇编爱好者加入!
注意:由于笔记字数过多,帖子限制20000字,所以:一楼回沙发也是!
由于制作水平有限,时间仓促,如有错误之处,欢迎回帖纠正!


第七章:
学过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 第八章算是结束了,下面就是第九种跳转了
呵呵!破解的必备!
未完...看一楼......








本帖被以下淘专辑推荐:

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
 楼主| 发表于 2011-12-30 03:40:46 | 显示全部楼层

汇编语言笔记(7——10章)

本帖最后由 小生我怕怕 于 2012-2-14 04:50 编辑

第九章转移指令
呵呵!转移相对于逆向软件还是非常重要的
其实我们之前学过的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)把积用地址存放
关于寄存器的冲突问题,相信大家之前也遇到过,往往循环中就出现这种的冲突
解决办法:大家可以利用栈或者是内存地址空间,把某个寄存器的值先放进去
然后在重新定义这个寄存器的值,用到的时间,在出栈,或者是调用内存地址的数据,注意栈的出栈顺序!实验十不多讲!都是做过的
明年同步更新汇编语言11—17章笔记,另C语言全章笔记,敬请期待!


鱼C论坛——By小生我怕怕

2011年12月30日凌晨

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 5 反对 0

使用道具 举报

发表于 2011-12-30 07:53:52 | 显示全部楼层
:P 全力支持!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2011-12-30 09:27:10 | 显示全部楼层
全力支持小生
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2011-12-30 09:40:06 | 显示全部楼层



good man
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2011-12-30 11:11:52 | 显示全部楼层
感谢大神分享:lol:lol:lol
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2011-12-30 15:48:54 | 显示全部楼层
支持楼主 要向楼主学习
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2011-12-30 18:28:07 | 显示全部楼层
很好,支持楼主~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2011-12-31 01:37:02 | 显示全部楼层
这么快就学完第十章啊,得象楼主学习努力啊
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2011-12-31 16:46:07 | 显示全部楼层
。 慢慢看 支持啊。。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2011-12-31 17:14:11 | 显示全部楼层
好东东 学习
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2011-12-31 19:28:51 | 显示全部楼层
学习了,:'(
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2011-12-31 23:33:12 | 显示全部楼层
佩服,真是人才啊!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2012-1-5 14:53:34 | 显示全部楼层
:loveliness: 支持一下 看着有点类
等搞到word里面 部分关键亮就好了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2012-1-6 13:20:19 | 显示全部楼层
哥们给力啊,辛苦你了~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2012-1-7 23:57:28 | 显示全部楼层
贡献  支持
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2012-1-9 15:23:50 | 显示全部楼层
请问一下这段代码的 [BX+5]是代表什么意思。
  1. assume cs:qq,ds:ee
  2. ee segment
  3. db 'FisHc'
  4. db 'LiNuX'
  5. ee ends
  6. qq segment
  7. start:mov ax,ee
  8. mov ds,ax
  9. mov bx,0
  10. mov cx,5
  11. s:mov al,[bx]
  12. and al,11011111b  
  13. mov [bx],al
  14. mov al,[bx+5]
  15. or al,00100000b
  16. mov [bx+5],al
  17. inc bx
  18. loop s
  19. mov ax,4c00h
  20. int 21h
  21. qq ends
  22. end start
复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2012-1-10 10:19:06 | 显示全部楼层
谢谢楼主分享!!!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2012-1-11 23:59:10 From FishC Mobile | 显示全部楼层
小生我怕怕 发表于 2011-12-30 03:40
第九章转移指令
呵呵!转移相对于逆向软件还是非常重要的
其实我们之前学过的loop就是一个转移指令,可以 ...

好!  写的好啊  汇编我能学到这地步也很好了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2012-1-12 00:08:35 | 显示全部楼层
辛苦楼主帮我们收集答案 谢谢
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-3-29 10:33

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表