操作系统实践与原理
目的是收集CSAPP之外的一些x86汇编指令。
csapp之外的x86汇编
IBM PC下的所有地址都是由“段:偏移”构成的
CS:IP寻址
CS:IP 两个寄存器指示了 CPU 当前将要读取的指令的地址,其中 CS 为代码段寄存器,而 IP 为指令指针寄存器 。
CSAPP中没有提及似乎是为了简便将开始的程序计数器从0开始,然后就直接找到了要执行程序的第一条指令。但是实际上肯定是存在其他地方的。
但是 CPU 怎么知道这些指令存放在内存的那个位置呢?这就需要CS:IP。
1000H:0000H 指向的是 MOV AX,1234H 的首地址,
如果 CPU 要读取到我的指令的话,很显然,必须要知道地址 1000H:0000H ,
然后 CPU 就可以根据这个首地址,将汇编指令 MOV AX,1234H 所对应的机器码读入到 CPU 的指令寄存器中,
Shell 将可执行文件加载到内存中以后,就会设置 CPU 中的两个寄存器,即设置 CS:IP 两个寄存器指向可执行文件的起始地址,此后 CPU 便从这个起始地址开始读取内存中的指令,并且执行。
比如我们在写汇编程序时,通常会使用 START 标记,其实这个标记就是用来标记起始地址的,
当将一个汇编程序编译,连接成可执行文件以后,再通过操作系统的 Shell 程序将可执行文件加载到内存中以后,
这个 START 所标记处的地址就是整个可执行文件的起始地址了 。
也就是说,当一个可执行文件加载到内存中以后,CS:IP 两个寄存器便指向了这个可执行文件的起始地址,
当读取完指令后,CS:IP 将会自动的改变,基本上是改变 IP ,从而指向下一条要读取的指令,这样就可以执行这个可执行文件了 。
总的来说
- shell会将可执行文件加载到内存的时候设置CS:IP , 然后就可以跳转到第一条指令了
- 指令计算为:CS<<4+IP,
- CS<<4在十六进制下就是空出一位来
- 读取后基本上是改变IP
- 段寄存器有
- ES 附加段寄存器
CS 代码段寄存器
SS 堆栈段寄存器
DS 数据段寄存器
FS 附加段寄存器
GS 附加段寄存器
- ES 附加段寄存器
连续存放的数据串的指令——串操作指令
(1)串传送指令MOVS
MOVSB ;字节串传送:ES:[DI]←DS:[SI],SI←SI+/-1,DI←DI+/-1
MOVSW ;字串传送:ES:[DI]←DS:[SI],SI←SI+/-2,DI←DI+/-2
MOVS 目的串名,源串名 ;这种格式需要使用前缀WORD PTR或BYTE PTR指明
例:将数据段SOURCE指示的100个字节数据传送到附加段DESTINATION指示的主存区
MOV SI,OFFSET SOURCE
MOV DI,OFFSET DESTINATION
MOV CX,100
CLD
AGAIN: MOVSB
DEC CX
JNZ AGAIN
例如:rep movw
rep: 重复执行该语句直至寄存器cx为0
movw: 将DS:SI的内容送至ES:DI,note! 是复制过去,原来的代码还在。
(2)串存储指令STOS
STOSB ;字节串存储:ES:[DI]←AL,DI←DI+/-1
STOSW ;字串存储:ES:[DI]←AX,DI←DI+/-2
串存储指令将AL或AX寄存器的内容存入由DI指定的附加段主存单元中,并根据DF和传送单位修改DI寄存器。STOS不影响标志。
(3)串读取指令LODS
LODSB ;字节串读取:AL←DS:[SI],SI←SI+/-1
LODSW ;字串读取:AX←DS:[SI],SI←SI+/=2
LODS指令和STOS指令功能互逆,它将SI寄存器指向的主存单元的内容送至AL或AX寄存器,并相应修改SI使其指向下一个元素。不影响标志。
(4)串比较指令CMPS
CMPSB ;字节串比较:DS:[SI]-ES:[DI],SI←SI+/-1,DI←DI+/-1
CMPSW ;字串比较:DS:[SI]-ES:[DI],SI←SI+/-2,DI←DI+/-2
船比较指令的功能是比较源串与目的串是否相同,并根据其减法结果设置标志位;指令在每次比较后修改SI和DI寄存器的值,使之指向下一个元素。
(5)串扫描指令SCAS
SCASB ;字节串扫描:AL-ES:[DI],DI←DI+/-1
SCASW ;字串扫描:AX-ES:[DI],DI←DI+/-2
串扫描指令SCAS将附加段中的字节或字内容与AL/AX寄存器内容进行比较,根据比较的结果设置标志,每次比较后修改DI寄存器的值,使之指向下一个元素。
(6)重复前缀指令rep
任何一个串操作指令,都可以在前面加一个重复前缀,以实现串操作的重复执行,重复次数隐含在CX寄存器中
REP ;REP前缀用在MOVS、STOS、LODS指令前,每次执行一次指令,CX减1;直到CX=0,重复执行结束
REPZ ;也可以表把为REPE,用在CMPS、SCAS指令前,每执行一次串指令CX减1,并判断ZF标志是否为0
;只要CX=0或ZF=0,则重复执行结束
REPNZ ;也可以表达为REPNE,用在CMPS、SCAS指令前,每执行一次串操作指令CX减1,并判断ZF标志是否为1,只要CX=0或ZF=1,则重复执行结束。
●REP前缀可以理解为“当数据串没有结束(CX≠0),则继续传送”
●REPZ/REPE前缀可以理解为“当数据串没有结束(CX≠0),并且串相等(ZF=1)则继续比较”
●REPNZ/REPNE前缀可以理解为“当数据串没有结束(CX≠0),并且串不相等(ZF=0)则继续比较”
(7)CLD&STD
CLD与STD是用来操作方向标志位DF(Direction Flag)。CLD使DF复位,即DF=0,STD使DF置位,即DF=1.用于串操作指令中.
例如:
MOVS ( MOVE String) 串传送指令
MOVSB //字节串传送 DF=0, SI = SI + 1 , DI = DI + 1 ;DF = 1 , SI = SI - 1 , DI = DI - 1
MOVSW //字串传送 DF=0, SI = SI + 2 , DI = DI + 2 ;DF = 1 , SI = SI - 2 , DI = DI - 2
执行操作:[DI] = [SI] ,将位于DS段的由SI所指出的存储单元的字节或字传送到位于ES段的由DI 所指出的存储单元,再修改SI和DI, 从而指向下一个元素.
在执行该指令之前,必须预置SI和DI的初值,用STD或CLD设置DF值.
MOVS DST , SRC //同上,不常用,DST和SRC只是用来用类型检查,并不允许使用其它寻址方式来确定操作数.
1.目的串必须在附加段中,即必须是ES:[DI]
2.源串允许使用段跨越前缀来修饰,但偏移地址必须是[SI].
(8)loop指令
每次执行loop指令,cx减1.为0即到下一条指令,不然继续
loop print_digit
ret
mov [num],dx
[num]默认的是对ds寄存器
lds si,[num]
从内存指定位置处读取一个长指针值并放入ds和si寄存器中。ds中放段地址,si是段内偏移地址。
eg.在PC机中BIOS设定的中断向量表中 int 0x41 的中断向量位置(4*0x41 = 0x0000:0x0104)存放的并不是中断程序的地址,而是第一个硬盘的基本参数表
lds si,[4*0x41]
这里是把内存地址4 * 0x41 (= 0x104)处保存的4个字节(段和偏移值)读出。
跳转指令
jmpi
段间跳转指令-可以跳到指定的段地址
jmpi go, INITSEG
///先设置CS段寄存器=INITSEG
///再设置IP寄存器=go
//然后进行jmp CS:IP 跳转地址这样的操作.
jnc
当进位标记C为0时跳转,为1时执行后面的指令。产生进位则C为1
中断信号
控制中断
CLI :禁止中断发生
STI : 允许中断发生
int 0x13
13号中断
-
读写磁盘的中断调用
-
AH=0X02----要读磁盘内容到内存
-
AL=0X04----要读4个扇区
-
CL=0X2----要读的磁盘扇区从2号扇区开始.
-
CH=0—要读的磁盘扇区所在的柱面号为0
-
DH=0–要读的磁盘扇区所在的磁头号为0
-
DL=0x00—要读扇区所在的驱动号为0
-
AH=0x08 ----可以获得每个磁道的扇区个数
-
CL的低6位—存放每个磁道的扇区个数
int 0x10
int 0x10 主要调用了BIOS中断int 0x10 , 该中断的作用是在屏幕上输出信息.
AH(AH中的值通常被称为BIOS调用功能号).
- AH=0X03 会取出当前光标的位置
- DH寄存器会存放光标所在的行
- DL存放光标所在的列
- AH=0x13 + int 0x10 就会在屏幕上输出信息
- ES:BP用来说明输出字符串所在的内存地址
- CX表示要输出的字符个数
- BL设置显示字符的属性
- 7表示黑底白字
循环移位
rol
循环左移指令:ROL DEST,COUNT
ROL(循环左移)指令把所有位都向左移。最高位复制到进位标志位和最低位。该指令格式与 SHL 指令相同。