16位汇编语言学习笔记

最近,不知道是心血来潮,还是有点厌倦web,突然想学一学逆向,未知的领域,有点儿神秘,有了一种想探索的欲望,这学期,也挺闲的,就给自己找点事做,学一下逆向,逆向探索之旅,就此开始吧!

基础知识

1.地址总线:cpu是通过地址总线来制定存储单元的。
数据总线:cpu与内存或其他器件之间的数据传送,。
控制总线: cpu对外部器件的控制
2.cpu包括运算器、控制器、寄存器等器件
运算器进行信息处理
寄存器进行信息存储
控制器控制各种器件进行工作
内部总线连接各种器件,进行数据的传送
3.8086cpu的所有寄存器是16位的,存放两个字节。
AX、BX、CX 、DX是通用寄存器,用来存放一般的数据
AX可分为AH和BL,其他同理。
4.字节,记为byte,一个字节有8个bit组成,可以存在8位寄存器中。
字:记为word,一个字由连个字节组成,这两个字节分别称为高位字节和低位字节。
5.cpu在执行指令时,会认为ah和al是两个不相关的寄存器。
在进行数据传送或运算时,要注意指令的两个操作对象的位数应当一致的:

mov ax,2000H 正确的。  
mov al,2000H 错误的。

6.8086cpu有20位地址总线。
物理地址=段地址x16+偏移地址
cpu可以使用不同的段地址和偏移地址形成同一个物理地址
7.8086cpu有4个段寄存器:CS DS SS ES
8.jmp 段地址:偏移地址 功能是用指令中给出的段地址修改CS,偏移地址修改IP.
jmp 某一合法寄存器 功能是用寄存器中的值修改ip.
9.debug的功能
R查看、改变cpu寄存器的内容
D查看内存中的内容
E改写内容内存中的内容
U将内存中的机器指令翻译成汇编指令
T执行一天机器指令
A以汇编指令的格式在内存中写入一条机器指令

寄存器的内存访问

1.字单元,即存放一个字型数据(16位)的内存单元,由两个地址连续的内存单元组成。
2.DS存放要访问数据的段地址,8086cpu不支持将数据直接送入段寄存器。
例子:

mov bx,1000H   
mov ds,bx
mov al,[0]
mov [0], cx

3.栈的push和pop都是以字为单元进行的。
4.栈顶的段地址存放在SS中,偏移地址存放在SP中,任意时刻,SS:SP 指向栈顶元素,push指令和pop指令执行时,cpu从ss和sp中得到栈顶的地址。

push 寄存器 将一个寄存器中的数据入栈 
push 段寄存器
push 内存单元
pop同理

5.8086cpu只记录栈顶,栈空间的大小要我们自己管理

程序

1.汇编指令,有对应的机械码的指令,可以被编译为机器指令,最终为cpu执行
伪指令:没有对应的机器码,最终不被cpu所执行,由编译器执行
(1)一个段的开始和结束

段名 segment


段名 ends

(2)end 是一个汇编程序的结束标记
(3)assume将有特定用途的段和相关的段寄存器关联起来即可。
2.我们在这里定义()来表示一个寄存器或一个内存单元中的内容

mov ax [bx]  bx中存放的数据作为一个偏移地址EA,段地址SA默认在ds中
inc bx,1 是bx中的内容加1

3.loop指令来实现循环功能,cx中存放循环次数。
例子计算2^12:

assume cs:code  
code segment
mov ax,2
mov cx,11
s:add ax,ax
loop s
mov ax,4c00h
int 21h
code ends
end

4.大于9FFF的十六进制数据A000H等,在汇编源程序中,数据不能以字母开头,所以要在前面加0
所以汇编程序中要写0A000H
5.debug和汇编编译器masm对指令的不同处理:
在汇编源程序中,指令mov ax,[0] 被编译器当做指令mov,ax,0 处理
6.汇编源程序中以下指令的含义

mov al,[0] 将常量0送入al中
mov al,ds:[0] 含义:(al)=((ds)*16+0),将内存单元中的数据送入al中
mov al,[bx] (al)=((ds)*16+(bx)),将内存单元中的数据送入al中
mov al,ds:[bx] 与mov al,[bx]相同

7.

inc ax   在ax中加1
dw 0123h,0456h,…… dw定义的数据处于代码段的开始,所以偏移地址为0。
db 'unIX' 相当于 db 66H,6EH,49H,58H

8.

[bx+idata]  表示一个内存单元,他的偏移地址为(bx)+idata 
也可以写成如下形式:
200[bx]
[bx].200

9.si和di与bx功能相近的寄存器,si和di不能够分成两个8位寄存器来使用
10.[bx+si]或[bx+di] 表示一个内存单元,它的偏移笛子为(bx)+(si)

数据处理

1.bx,si,di,bp:
1)在8086cpu中只有bx、si di bp4个寄存器可以用来[]中来进行内存单元的寻址。
例子:

mov ax,[bx]  正确 
mov ax,[cx] 错误

2)在[]中,这4个寄存器可以单个出现,或只能以4中组合出现:bx和si、bx和di、bp和si、bp和di
3)只要在[]中使用寄存器bp,而只李宁中没有显性地给出段地址,段地址就默认在ss中
2.寄存器寻址方式:直接寻址 寄存器间接寻址 寄存器相对寻址 基址变址寻址 相对基址变址寻址
3.指令处理数据的长度
1)通过寄存器名指明指令进行的是字操作或者字节操作
2)在没有寄存器名存在的情况下,用X ptr 指令内存的长度
例子:

mov word ptr ds:[0],1 
inc word ptr [bx]
mov byte ptr ds:[0],1
inc byte ptr [bx]

4.div
1)除数有8,16位
2)被除数:默认放在AX或DX和AX中,
如果除数为8位,被除数则为16位,默认在AX中存放
如除数为16位,被除数则为32位,默认放在DX和AX中
3)结果:如果除数为8位,则AL存储除法操作的商,AH存储除法操作的余数
如果除数为16位,则AX存储除法操作的商,DX存储除法操作的余数
例子:

div byte ptr ds:[0] 
含义:(al)=(ax)/((ds)*16+0)的商
(ah)=(ax)/((ds)*16+0)的余数
div word ptr es:[0]
含义:(ax)=[(dx)*10000H+(ax)]/((es)*16+0)的商
(dx)=[(dx)*10000H+(ax)]/((es)*16+0)的余数

5

db 定义字节型数据 ,占用一个字节
dw 定义字型数据 ,占用一个字
dd 定义双字型数据 ,占用两个字

6.dup用来数据的重复
例子:

db 3 dup (0) 相当于db 0,0,0

转移指令的原理

1.offset 取得标号的偏移地址
2.

jmp short 标号  转到标号处执行指令 
jmp near ptr 标号 实现段内转移
jmp far ptr 标号 实现的是段间转移,又称为远转移
jmp word ptr 内存单元 从内存单元地址处开始存放一个字,是转移的目的地址的偏移地址。
jmmp dword ptr 内存单元 从内存单元地址处开始存放两个字,高地址处的字是转移目的段地址,低地址处是转移的目的偏移地址
jcxz 标号 如果(cx)=0 转移到标号处执行

3.ret指令用栈中的数据,修改ip的内容,从而实现 近转移

(ip)=((ss)*16+(sp))
(sp)=(sp)+2

retf指令用栈中的数据,修改cs和ip的内容,从而实现远转移

(ip)=((ss)*16+(sp))
(sp)=(sp)+2
(cs)=((ss)*16+(sp))
(sp)=(sp)+2

4.
1)call 标号 将当前的ip压栈后,转到标号处执行指令

(sp)=(sp)-2
((ss)*16+(sp))=(ip)

(ip)=(ip)+16 位位移

相当于进行

push ip
jmp near ptr 标号

2)call far ptr 标号 实现的的段间转移

(sp)=(sp)-2
((ss)*16+(sp))=(cs)
(sp)=(sp)-2
((ss)*16+(sp))=(ip)


(cs)=标号所在段的短地址
(ip)=标号在段中的段地址

相当于进行

push cs 
push ip
jmp near ptr 标号

3) call 16位reg

(sp)=(sp)-2
((ss)*16+(sp))=(ip)
(ip)=(16位reg)

功能:

push IP
jmp 16位reg

4)call word ptr 内存单元地址
相当于:

push Ip
jmp word ptr 内存单元地址

5)call dword ptr 内存单元地址
相当于:

push cs
push Ip
jmp dword ptr 内存单元地址

5.
1)两个相乘的数:两个数相乘,要么8位,要么16位,如果是8位,一个默认放在AL中,另一个放在8位reg或内存字节单元中;如果是16位,一个默认在AX中,另一个放在16位reg或内存字单元中。
2)结果:如果是8位乘法,结果默认放在AX中;如果是16位乘法,结果高位默认在DX中存放,低位在AX中放。
格式
mul reg
mul 内存单元
例子:

mul byte ptr ds:[0]  含义:(ax)=(al)*((ds)*16+0)
mul word ptr [bx+si+8]
含义:(ax)=(ax)*((ds)*16+(bx)+(si)+8)结果的低16位
(dx)=(ax)*((ds)*16+(bx)+(si)+8)结果的高16位

  1. divdw
    功能:进行不会溢出的除法运算,被除数位dword型,除数为word型,结果为dword型。
    参数:
    (ax)=dword型数据的低16位
    (dx)=dword型数据的高16位
    (cx)=除数
    返回:
    (dx)=结果的高16位,(ax)结果的低16位
    (cx)=余数
    7.dtoc
    功能:将word型数据转变为表示十进制数的字符串,字符串以0位结尾符。
    参数:(ax)=word型数据
    ds:si指向字符串的首地址
    返回:无

    标记寄存器

    1.zf标志:flag的第六位,零标志位,记录相关指令执行后,结果是否为0。为0,zf=1,否则zf=0
    pf标志:flag的第二位,奇偶标志位。记录相关指令执行后,结果的所有bit位中1的个数是否为偶数,是pf=1,否pf=0
    sf标志:第七位,符号标志位,记录相关指令执行后,其结果是否为负。负为1,否为0
    cf标志:第零位,进位标志位。一般情况下,在进行无符号运算的时候它记录了运算结果的最高有效位想更高位的进位值,或从更高位的借位值。
    of标志:第11位,溢出标志。发生溢出,of=1,否则of=0
    df标志:第10位,方向标志位.df=0,每次操作后si、di递增;df=1,每次操作后si、di递减。
    2.adc带进位的的加法指令
    3.sbb是借位减法指令,它利用了cf位上记录的借位值。
    4.cmp是比较指令,相当于减法指令,只是不保存结果。
    5.
指令          含义           检测的相关标志位
je 等于则转移 zf=1
jne 不等于 zf=0
jb 低于 cf=1
jnb 不低于 cf=0
ja 高于 cf=0或zf=0
jna 不高于 cf=1或zf=1

6.movsb
相当于

((es)*16+(di))=((ds)*16+(si))
如果 df=0,则:
(si)=(si)+1
(di)=(di)+1
否则:
(si)=(si)-1
(di)=(di)-1

movsw的功能将ds:si指向的内存字单元中的字送入es:di中,然后根据标志寄存器df的值,将si和di递增2或递减2
7.rep movsb
相当于

s:movsb
loop s

cld指令:将df位置0
std指令:将df位置1
8.pushf将标志寄存器的值压栈,
popf是从栈中弹出数据,送入标志寄存器中。

内中断

1.内中断的产生:
1)除法错误
2)单步执行
3)执行into指令
4)执行int指令
2.iret指令的功能用汇编语法描述为:

pop IP
pop CS
popf

3.int n
n为中断类型码,引发中断
执行过程:
1)取中断类型码n
2)标志寄存器入栈,IF=0,TF=0
3)CS、IP入栈
4)(IP)=(n4),(CS)=(n4+2)
4.端口的读写指令只有两条:in和out
5.shl逻辑左移指令
shr逻辑右移指令

外中断

1.可屏蔽中断是cpu可以不响应的外中断。
sti,设置IF=1
cli,设置IF=0
2.键盘的输入
断码=通码+80h


参考书籍:
https://book.douban.com/subject/25726019/

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器